Added a cubic gravity model and fixed some stuff
- Added TestPlanetGenerator and a corresponding gravity model - Fixed gravity-triggered camera rotation
This commit is contained in:
parent
d438d2aa14
commit
2d55d4db51
@ -122,46 +122,51 @@ class AABBoidCollider {
|
||||
return output;
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
/*
|
||||
* Here we determine whether a collision has actually happened, and if it
|
||||
* did, at what moment.
|
||||
* The basic idea is to compute the moment of collision and impact
|
||||
* coordinates in wall coordinate space.
|
||||
* Then, we can check impact coordinates to determine if we actually hit the
|
||||
* wall or flew by and then
|
||||
* check time to make sure the collision is not too far in the future and
|
||||
* not in the past.
|
||||
* Here we determine whether a collision has actually happened, and if it did, at what moment.
|
||||
*
|
||||
* The basic idea is to compute the moment of collision and impact coordinates in wall coordinate space.
|
||||
* Then, we can check impact coordinates to determine if we actually hit the wall or flew by and then
|
||||
* check time to make sure the collision is not too far in the future and not in the past.
|
||||
*
|
||||
* DETAILED EXPLANATION:
|
||||
* Consider a surface defined by an origin r_wall and two noncollinear
|
||||
* nonzero vectors w and h.
|
||||
*
|
||||
* Consider a surface defined by an origin r_wall and two noncollinear nonzero vectors w and h.
|
||||
* Consider a line defined by an origin r_line and a nonzero vector v.
|
||||
*
|
||||
* Then, a collision occurs if there exist x, y and t such that
|
||||
* ______ _
|
||||
* r_line + v * t
|
||||
*
|
||||
* and
|
||||
* ______ _ _
|
||||
* r_wall + w * x + h * y
|
||||
* describe the same location (indeed, this corresponds to a collision at
|
||||
* moment t0 + t
|
||||
* with a point on the wall with coordinates (x; y) in (w; h) coordinate
|
||||
* system).
|
||||
*
|
||||
* describe the same location (indeed, this corresponds to a collision at moment t0 + t
|
||||
* with a point on the wall with coordinates (x; y) in (w; h) coordinate system).
|
||||
*
|
||||
* Therefore,
|
||||
* ______ _ ______ _ _
|
||||
* r_line + v*t = r_wall + w*x + h*y;
|
||||
*
|
||||
* _ ⎡w_x h_x -v_x⎤ ⎡x⎤ _ ______ ______
|
||||
* r = ⎢w_y h_y -v_y⎥ * ⎢y⎥, where r = r_line - r_wall;
|
||||
* ⎣w_z h_z -v_z⎦ ⎣t⎦
|
||||
*
|
||||
* ⎡x⎤ ⎡w_x h_x -v_x⎤ -1 _
|
||||
* ⎢y⎥ = ⎢w_y h_y -v_y⎥ * r, if the matrix is invertible.
|
||||
* ⎣t⎦ ⎣w_z h_z -v_z⎦
|
||||
*
|
||||
* Then, one only needs to ensure that:
|
||||
* 0 < x < 1,
|
||||
* 0 < y < 1, and
|
||||
* 0 < t < T, where T is remaining tick time.
|
||||
* If the matrix is not invertible or any of the conditions are not met, no
|
||||
* collision happened.
|
||||
*
|
||||
* If the matrix is not invertible or any of the conditions are not met, no collision happened.
|
||||
* If all conditions are satisfied, then the moment of impact is t0 + t.
|
||||
*/
|
||||
// @formatter:on
|
||||
private static Collision computeWallCollision(
|
||||
Wall obstacleWall,
|
||||
AABBoid colliderModel,
|
||||
|
@ -212,8 +212,10 @@ public class Collider {
|
||||
handlePhysics(collision);
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
/*
|
||||
* Here we compute the change in body velocities due to a collision.
|
||||
*
|
||||
* We make the following simplifications:
|
||||
* 1) The bodies are perfectly rigid;
|
||||
* 2) The collision is perfectly inelastic
|
||||
@ -222,12 +224,13 @@ public class Collider {
|
||||
* 4) No tangential friction exists
|
||||
* (bodies do not experience friction when sliding against each other);
|
||||
* 5) Velocities are not relativistic.
|
||||
*
|
||||
* Angular momentum is ignored per 3) and 4),
|
||||
* e.g. when something pushes an end of a long stick, the stick does not
|
||||
* rotate.
|
||||
* e.g. when something pushes an end of a long stick, the stick does not rotate.
|
||||
*
|
||||
* DETAILED EXPLANATION:
|
||||
* Two spherical (sic) bodies, a and b, experience a perfectly inelastic
|
||||
* collision
|
||||
*
|
||||
* Two spherical (sic) bodies, a and b, experience a perfectly inelastic collision
|
||||
* along a unit vector
|
||||
* _ _ _ _ _
|
||||
* n = (w ⨯ h) / (|w ⨯ h|),
|
||||
@ -238,40 +241,43 @@ public class Collider {
|
||||
* ___ ___
|
||||
* After the collision desired velocities are u_a and u_b, respectively.
|
||||
* _
|
||||
* (Notation convention: suffix 'n' denotes a vector projection onto vector
|
||||
* n,
|
||||
* (Notation convention: suffix 'n' denotes a vector projection onto vector n,
|
||||
* and suffix 't' denotes a vector projection onto the dividing plane.)
|
||||
* Consider the law of conservation of momentum for axis n and the dividing
|
||||
* plane:
|
||||
*
|
||||
* Consider the law of conservation of momentum for axis n and the dividing plane:
|
||||
* ____________ ____________ ________________
|
||||
* n: ⎧ p_a_before_n + p_b_before_n = p_common_after_n;
|
||||
* ⎨ ___________ ____________
|
||||
* t: ⎩ p_i_after_t = p_i_before_t for any i in {a, b}.
|
||||
*
|
||||
* Expressing all p_* in given terms:
|
||||
* ___ _ ___ _ ___ ___ ____ ____
|
||||
* n: ⎧ M_a * (v_a ⋅ n) + M_b * (v_b ⋅ n) = (M_a + M_b) * u_n, where u_n ≡
|
||||
* u_an = u_bn;
|
||||
* n: ⎧ M_a * (v_a ⋅ n) + M_b * (v_b ⋅ n) = (M_a + M_b) * u_n, where u_n ≡ u_an = u_bn;
|
||||
* ⎨ ____ ___ _ ___ _
|
||||
* t: ⎩ u_it = v_i - n * (v_i ⋅ n) for any i in {a, b}.
|
||||
*
|
||||
* Therefore:
|
||||
* ___ _ ___ _ ___ _
|
||||
* u_n = n * ( M_a/(M_a + M_b) * v_a ⋅ n + M_b/(M_a + M_b) * v_b ⋅ n );
|
||||
*
|
||||
* or, equivalently,
|
||||
* ___ _ ___ _ ___ _
|
||||
* u_n = n * ( m_a * v_a ⋅ n + m_b * v_b ⋅ n ),
|
||||
*
|
||||
* where m_a and m_b are relative masses (see below).
|
||||
*
|
||||
* Finally,
|
||||
* ___ ____ ___
|
||||
* u_i = u_it + u_n for any i in {a, b}.
|
||||
* The usage of relative masses m_i permits a convenient generalization of
|
||||
* the algorithm
|
||||
* for infinite masses, signifying masses "significantly greater" than
|
||||
* finite masses:
|
||||
* 1) If both M_a and M_b are finite, let m_i = M_i / (M_a + M_b) for any i
|
||||
* in {a, b}.
|
||||
*
|
||||
* The usage of relative masses m_i permits a convenient generalization of the algorithm
|
||||
* for infinite masses, signifying masses "significantly greater" than finite masses:
|
||||
*
|
||||
* 1) If both M_a and M_b are finite, let m_i = M_i / (M_a + M_b) for any i in {a, b}.
|
||||
* 2) If M_i is finite but M_j is infinite, let m_i = 0 and m_j = 1.
|
||||
* 3) If both M_a and M_b are infinite, let m_i = 1/2 for any i in {a, b}.
|
||||
*/
|
||||
// @formatter:on
|
||||
private static void handlePhysics(Collision collision) {
|
||||
// Fuck JGLM
|
||||
Vec3 n = Vectors.grab3();
|
||||
|
@ -30,6 +30,7 @@ import java.util.function.Consumer;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||
import ru.windcorp.progressia.common.util.Vectors;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.generic.GenericChunk;
|
||||
import ru.windcorp.progressia.common.world.rels.AbsFace;
|
||||
@ -44,14 +45,16 @@ public class ChunkData
|
||||
implements GenericChunk<ChunkData, BlockData, TileData, TileDataStack> {
|
||||
|
||||
public static final int BLOCKS_PER_CHUNK = Coordinates.CHUNK_SIZE;
|
||||
public static final int CHUNK_RADIUS = BLOCKS_PER_CHUNK / 2;
|
||||
|
||||
private final Vec3i position = new Vec3i();
|
||||
private final WorldData world;
|
||||
|
||||
private final BlockData[] blocks = new BlockData[BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK];
|
||||
|
||||
private final TileDataStack[] tiles = new TileDataStack[BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK *
|
||||
BLOCK_FACE_COUNT];
|
||||
private final TileDataStack[] tiles = new TileDataStack[
|
||||
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCK_FACE_COUNT
|
||||
];
|
||||
|
||||
private final AbsFace up;
|
||||
|
||||
@ -92,6 +95,13 @@ public class ChunkData
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlockRel(Vec3i relativeBlockInChunk, BlockData block, boolean notify) {
|
||||
Vec3i absoluteBlockInChunk = Vectors.grab3i();
|
||||
resolve(relativeBlockInChunk, absoluteBlockInChunk);
|
||||
setBlock(absoluteBlockInChunk, block, notify);
|
||||
Vectors.release(absoluteBlockInChunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileDataStack getTilesOrNull(Vec3i blockInChunk, BlockFace face) {
|
||||
return tiles[getTileIndex(blockInChunk, face)];
|
||||
|
@ -282,9 +282,9 @@ public class EntityData extends StatefulObject implements Collideable, GenericEn
|
||||
|
||||
// Don't format. @formatter:off
|
||||
matrix.set(
|
||||
cos + (1 - cos)*x*x, (1 - cos)*x*y - sin*z, (1 - cos)*x*z + sin*y,
|
||||
(1 - cos)*y*x + sin*z, cos + (1 - cos)*y*y, (1 - cos)*y*z - sin*x,
|
||||
(1 - cos)*z*x - sin*y, (1 - cos)*z*y + sin*x, cos + (1 - cos)*z*z
|
||||
cos + (1 - cos)*x*x, (1 - cos)*y*x + sin*z, (1 - cos)*z*x - sin*y,
|
||||
(1 - cos)*x*y - sin*z, cos + (1 - cos)*y*y, (1 - cos)*z*y + sin*x,
|
||||
(1 - cos)*x*z + sin*y, (1 - cos)*y*z - sin*x, cos + (1 - cos)*z*z
|
||||
);
|
||||
// @formatter:on
|
||||
|
||||
|
@ -63,7 +63,7 @@ public class Player extends PlayerData implements ChunkLoader {
|
||||
for (cursor.x = -iRadius; cursor.x <= +iRadius; ++cursor.x) {
|
||||
for (cursor.y = -iRadius; cursor.y <= +iRadius; ++cursor.y) {
|
||||
for (cursor.z = -iRadius; cursor.z <= +iRadius; ++cursor.z) {
|
||||
if (cursor.x * cursor.x + cursor.y * cursor.y + (cursor.z * 2) * (cursor.z * 2) <= radiusSq) {
|
||||
if (cursor.x * cursor.x + cursor.y * cursor.y + (cursor.z/* * 2*/) * (cursor.z/* * 2*/) <= radiusSq) {
|
||||
|
||||
cursor.add(start);
|
||||
chunkConsumer.accept(cursor);
|
||||
|
@ -31,7 +31,7 @@ import ru.windcorp.progressia.server.world.WorldLogic;
|
||||
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
|
||||
import ru.windcorp.progressia.server.world.ticking.Change;
|
||||
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
||||
import ru.windcorp.progressia.test.gen.TestWorldGenerator;
|
||||
import ru.windcorp.progressia.test.gen.TestPlanetGenerator;
|
||||
|
||||
public class Server {
|
||||
|
||||
@ -60,7 +60,7 @@ public class Server {
|
||||
private final TickingSettings tickingSettings = new TickingSettings();
|
||||
|
||||
public Server(WorldData world) {
|
||||
this.world = new WorldLogic(world, this, TestWorldGenerator::new);
|
||||
this.world = new WorldLogic(world, this, w -> new TestPlanetGenerator("Test:PlanetGenerator", Units.get("48 m"), w));
|
||||
this.serverThread = new ServerThread(this);
|
||||
|
||||
this.clientManager = new ClientManager(this);
|
||||
@ -206,7 +206,7 @@ public class Server {
|
||||
}
|
||||
|
||||
public float getLoadDistance(Player player) {
|
||||
return Units.get(150.0f, "m");
|
||||
return Units.get(100.5f, "m");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,13 +59,15 @@ import ru.windcorp.progressia.server.world.block.*;
|
||||
import ru.windcorp.progressia.server.world.entity.*;
|
||||
import ru.windcorp.progressia.server.world.tile.*;
|
||||
import ru.windcorp.progressia.test.gen.TestGravityModel;
|
||||
import ru.windcorp.progressia.test.gen.TestPlanetGravityModel;
|
||||
|
||||
public class TestContent {
|
||||
|
||||
public static final String PLAYER_LOGIN = "Sasha";
|
||||
public static final long PLAYER_ENTITY_ID = 0x42;
|
||||
public static final long STATIE_ENTITY_ID = 0xDEADBEEF;
|
||||
public static final Vec3 SPAWN = new Vec3(8, 8, 880);
|
||||
// public static final Vec3 SPAWN = new Vec3(8, 8, 880);
|
||||
public static final Vec3 SPAWN = new Vec3(0, 0, 66);
|
||||
|
||||
public static final List<BlockData> PLACEABLE_BLOCKS = new ArrayList<>();
|
||||
public static final List<TileData> PLACEABLE_TILES = new ArrayList<>();
|
||||
@ -424,6 +426,7 @@ public class TestContent {
|
||||
ChunkIO.registerCodec(new TestChunkCodec());
|
||||
ChunkRenderOptimizerRegistry.getInstance().register("Core:SurfaceOptimizer", ChunkRenderOptimizerSurface::new);
|
||||
GravityModelRegistry.getInstance().register(new TestGravityModel());
|
||||
GravityModelRegistry.getInstance().register(new TestPlanetGravityModel());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.test.gen;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.DecodingException;
|
||||
import ru.windcorp.progressia.common.world.WorldData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
|
||||
import ru.windcorp.progressia.common.world.rels.RelFace;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
|
||||
import ru.windcorp.progressia.server.world.WorldLogic;
|
||||
import ru.windcorp.progressia.server.world.generation.AbstractWorldGenerator;
|
||||
|
||||
public class TestPlanetGenerator extends AbstractWorldGenerator<Boolean> {
|
||||
|
||||
private final int surfaceLevel;
|
||||
|
||||
public TestPlanetGenerator(String id, float planetRadius, WorldLogic world) {
|
||||
super(id, Boolean.class, "Test:PlanetGravityModel");
|
||||
|
||||
this.surfaceLevel = (int) (planetRadius / ChunkData.BLOCKS_PER_CHUNK);
|
||||
if (surfaceLevel < 2) {
|
||||
throw new IllegalArgumentException("planetRadius too small, must be at least 32 m");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doReadGenerationHint(DataInputStream input) throws IOException, DecodingException {
|
||||
return input.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWriteGenerationHint(DataOutputStream output, Boolean hint) throws IOException {
|
||||
output.writeBoolean(hint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkIsChunkReady(Boolean hint) {
|
||||
return hint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkData generate(Vec3i chunkPos, WorldData world) {
|
||||
ChunkData chunk = new ChunkData(chunkPos, world);
|
||||
|
||||
generate(chunk);
|
||||
chunk.setGenerationHint(true);
|
||||
|
||||
world.addChunk(chunk);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private enum ChunkType {
|
||||
SURFACE, UNDERGROUND, EDGE_SURFACE, EDGE_UNDERGROUND, CORE, AIR;
|
||||
}
|
||||
|
||||
private void generate(ChunkData chunk) {
|
||||
switch (getChunkType(chunk.getPosition())) {
|
||||
case SURFACE:
|
||||
fillSurface(chunk);
|
||||
break;
|
||||
case UNDERGROUND:
|
||||
fillUndeground(chunk);
|
||||
break;
|
||||
case EDGE_SURFACE:
|
||||
fillEdgeSurface(chunk);
|
||||
break;
|
||||
case EDGE_UNDERGROUND:
|
||||
fillEdgeUnderground(chunk);
|
||||
break;
|
||||
case CORE:
|
||||
fillCore(chunk);
|
||||
break;
|
||||
case AIR:
|
||||
fillAir(chunk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void fillSurface(ChunkData chunk) {
|
||||
final int bpc = ChunkData.BLOCKS_PER_CHUNK;
|
||||
|
||||
BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt");
|
||||
BlockData granite = BlockDataRegistry.getInstance().get("Test:GraniteMonolith");
|
||||
|
||||
TileData grass = TileDataRegistry.getInstance().get("Test:Grass");
|
||||
|
||||
chunk.forEachBiC(bic -> {
|
||||
|
||||
BlockData block;
|
||||
|
||||
if (bic.z > bpc - 4) {
|
||||
block = dirt;
|
||||
} else {
|
||||
block = granite;
|
||||
}
|
||||
|
||||
chunk.setBlockRel(bic, block, false);
|
||||
|
||||
});
|
||||
|
||||
VectorUtil.iterateCuboid(0, 0, bpc - 1, bpc, bpc, bpc, bic -> {
|
||||
chunk.getTilesRel(bic, RelFace.UP).add(grass);
|
||||
});
|
||||
}
|
||||
|
||||
private void fillUndeground(ChunkData chunk) {
|
||||
fill(chunk, BlockDataRegistry.getInstance().get("Test:GraniteMonolith"));
|
||||
}
|
||||
|
||||
private void fillEdgeSurface(ChunkData chunk) {
|
||||
fill(chunk, BlockDataRegistry.getInstance().get("Test:Stone"));
|
||||
}
|
||||
|
||||
private void fillEdgeUnderground(ChunkData chunk) {
|
||||
fill(chunk, BlockDataRegistry.getInstance().get("Test:Stone"));
|
||||
}
|
||||
|
||||
private void fillCore(ChunkData chunk) {
|
||||
fill(chunk, BlockDataRegistry.getInstance().get("Test:Stone"));
|
||||
}
|
||||
|
||||
private void fillAir(ChunkData chunk) {
|
||||
fill(chunk, BlockDataRegistry.getInstance().get("Test:Air"));
|
||||
}
|
||||
|
||||
private void fill(ChunkData chunk, BlockData block) {
|
||||
chunk.forEachBiC(bic -> chunk.setBlock(bic, block, false));
|
||||
}
|
||||
|
||||
private ChunkType getChunkType(Vec3i pos) {
|
||||
int[] abs = pos.abs_().toIA_();
|
||||
Arrays.sort(abs);
|
||||
|
||||
int medium = abs[1];
|
||||
int largest = abs[2];
|
||||
|
||||
int level = largest;
|
||||
|
||||
if (level == 0) {
|
||||
return ChunkType.CORE;
|
||||
}
|
||||
|
||||
if (largest > surfaceLevel) {
|
||||
return ChunkType.AIR;
|
||||
}
|
||||
|
||||
boolean isSurface = largest == surfaceLevel;
|
||||
|
||||
if (medium == largest) {
|
||||
return isSurface ? ChunkType.EDGE_SURFACE : ChunkType.EDGE_UNDERGROUND;
|
||||
} else {
|
||||
return isSurface ? ChunkType.SURFACE : ChunkType.UNDERGROUND;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.test.gen;
|
||||
|
||||
import glm.vec._3.Vec3;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.common.Units;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.GravityModel;
|
||||
import ru.windcorp.progressia.common.world.rels.AbsFace;
|
||||
|
||||
import static java.lang.Math.*;
|
||||
|
||||
public class TestPlanetGravityModel extends GravityModel {
|
||||
|
||||
private static final float GRAVITATIONAL_ACCELERATION = Units.get("9.8 m/s^2");
|
||||
private static final float ROUNDNESS = Units.get("16 m");
|
||||
private static final float INNER_RADIUS = Units.get("16 m");
|
||||
|
||||
public TestPlanetGravityModel() {
|
||||
this("Test:PlanetGravityModel");
|
||||
}
|
||||
|
||||
protected TestPlanetGravityModel(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGetGravity(Vec3 pos, Vec3 output) {
|
||||
// Change to a CS where (0;0;0) is the center of the center chunk
|
||||
float px = pos.x - ChunkData.CHUNK_RADIUS;
|
||||
float py = pos.y - ChunkData.CHUNK_RADIUS;
|
||||
float pz = pos.z - ChunkData.CHUNK_RADIUS;
|
||||
|
||||
// Assume weightlessness when too close to center
|
||||
if ((px*px + py*py + pz*pz) < INNER_RADIUS*INNER_RADIUS) {
|
||||
output.set(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache absolute coordinates
|
||||
float ax = abs(px);
|
||||
float ay = abs(py);
|
||||
float az = abs(pz);
|
||||
|
||||
// Determine maximum and middle coordinates by absolute value
|
||||
final float maxAbs;
|
||||
final float midAbs;
|
||||
|
||||
// herptyderp
|
||||
if (ax > ay) {
|
||||
if (ax > az) {
|
||||
maxAbs = ax;
|
||||
midAbs = ay > az ? ay : az;
|
||||
} else {
|
||||
maxAbs = az;
|
||||
midAbs = ax;
|
||||
}
|
||||
} else {
|
||||
if (ay > az) {
|
||||
maxAbs = ay;
|
||||
midAbs = ax > az ? ax : az;
|
||||
} else {
|
||||
maxAbs = az;
|
||||
midAbs = ay;
|
||||
}
|
||||
}
|
||||
|
||||
output.x = maxAbs - ax < ROUNDNESS ? (px > 0 ? +1 : -1) : 0;
|
||||
output.y = maxAbs - ay < ROUNDNESS ? (py > 0 ? +1 : -1) : 0;
|
||||
output.z = maxAbs - az < ROUNDNESS ? (pz > 0 ? +1 : -1) : 0;
|
||||
|
||||
if (maxAbs - midAbs < ROUNDNESS) {
|
||||
output.normalize();
|
||||
computeEdgeGravity(output.x, output.y, output.z, px, py, pz, output);
|
||||
} else {
|
||||
assert output.dot(output) == 1 : "maxAbs - midAbs = " + maxAbs + " - " + midAbs + " > " + ROUNDNESS + " yet l*l != 1";
|
||||
}
|
||||
|
||||
output.mul(-GRAVITATIONAL_ACCELERATION);
|
||||
}
|
||||
|
||||
private void computeEdgeGravity(float lx, float ly, float lz, float rx, float ry, float rz, Vec3 output) {
|
||||
// da math is gud, no worry
|
||||
// - Javapony
|
||||
|
||||
if (lx == 0) rx = 0;
|
||||
if (ly == 0) ry = 0;
|
||||
if (lz == 0) rz = 0;
|
||||
|
||||
float scalarProduct = rx*lx + ry*ly + rz*lz;
|
||||
float rSquared = rx*rx + ry*ry + rz*rz;
|
||||
|
||||
float distanceAlongEdge = scalarProduct - (float) sqrt(
|
||||
scalarProduct*scalarProduct - rSquared + ROUNDNESS*ROUNDNESS
|
||||
);
|
||||
|
||||
output.set(lx, ly, lz).mul(-distanceAlongEdge).add(rx, ry, rz).div(ROUNDNESS);
|
||||
|
||||
final float f = (float) sqrt(3.0/2);
|
||||
|
||||
if (signum(lx) != signum(output.x)) {
|
||||
computeEdgeGravity(0, ly*f, lz*f, rx, ry, rz, output);
|
||||
} else if (signum(ly) != signum(output.y)) {
|
||||
computeEdgeGravity(lx*f, 0, lz*f, rx, ry, rz, output);
|
||||
} else if (signum(lz) != signum(output.z)) {
|
||||
computeEdgeGravity(lx*f, ly*f, 0, rx, ry, rz, output);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbsFace doGetDiscreteUp(Vec3i chunkPos) {
|
||||
AbsFace rounded = AbsFace.roundToFace(chunkPos.x, chunkPos.y, chunkPos.z);
|
||||
return rounded == null ? AbsFace.POS_Z : rounded;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user