diff --git a/.gitignore b/.gitignore index 123f6a4c..0ff7484e 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ /saves/random_sp_world /saves/defaultLevel* /saves/generation_testing +/saves/World name* #screenshots /assets/Screenshots diff --git a/buildNumber.properties b/buildNumber.properties index 44a8dae2..2c5af09d 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Tue Dec 03 12:29:34 EST 2024 -buildNumber=516 +#Tue Dec 03 15:14:56 EST 2024 +buildNumber=519 diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 1b72885a..c2152d5b 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1229,6 +1229,7 @@ Native code building correctly in jenkins pipeline Refactoring native code Fix gravity tree not deactivating when body is disabled Refactoring world menu generators into dedicated class +Fix single player loading diff --git a/src/main/java/electrosphere/client/block/cells/BlockDrawCell.java b/src/main/java/electrosphere/client/block/cells/BlockDrawCell.java index b8c4879a..6405f820 100644 --- a/src/main/java/electrosphere/client/block/cells/BlockDrawCell.java +++ b/src/main/java/electrosphere/client/block/cells/BlockDrawCell.java @@ -74,7 +74,7 @@ public class BlockDrawCell { /** * The cached minimum distance */ - double cachedMinDistance = -1; + long cachedMinDistance = -1; /** * Target to notify on generation completion @@ -302,11 +302,16 @@ public class BlockDrawCell { * @param distCache the lod value under which distance caches are invalidated * @return the distance */ - public double getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ + public long getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ if(cachedMinDistance != INVALID_DIST_CACHE && distCache < lod){ return cachedMinDistance; } else { - this.cachedMinDistance = GeomUtils.getMinSquaredDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); + double dist = GeomUtils.approxMinDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); + if(Double.isFinite(dist)){ + this.cachedMinDistance = (long)dist; + } else { + this.cachedMinDistance = GeomUtils.REALLY_BIG_NUMBER; + } return cachedMinDistance; } } diff --git a/src/main/java/electrosphere/client/block/cells/ClientBlockCellManager.java b/src/main/java/electrosphere/client/block/cells/ClientBlockCellManager.java index e014799d..45be49e2 100644 --- a/src/main/java/electrosphere/client/block/cells/ClientBlockCellManager.java +++ b/src/main/java/electrosphere/client/block/cells/ClientBlockCellManager.java @@ -35,27 +35,27 @@ public class ClientBlockCellManager { /** * The distance to draw at full resolution */ - public static final double FULL_RES_DIST = 8 * 8; + public static final double FULL_RES_DIST = 8; /** * The distance for half resolution */ - public static final double HALF_RES_DIST = 16 * 16; + public static final double HALF_RES_DIST = 16; /** * The distance for quarter resolution */ - public static final double QUARTER_RES_DIST = 20 * 20; + public static final double QUARTER_RES_DIST = 20; /** * The distance for eighth resolution */ - public static final double EIGHTH_RES_DIST = 32 * 32; + public static final double EIGHTH_RES_DIST = 32; /** * The distance for sixteenth resolution */ - public static final double SIXTEENTH_RES_DIST = 64 * 64; + public static final double SIXTEENTH_RES_DIST = 64; /** * The octree holding all the chunks to evaluate @@ -175,7 +175,9 @@ public class ClientBlockCellManager { return false; } if(node.isLeaf()){ - if(this.shouldSplit(playerPos, node, distCache)){ + if(this.isMeta(playerPos, node, distCache)){ + this.flagAsMeta(node); + } else if(this.shouldSplit(playerPos, node, distCache)){ Globals.profiler.beginCpuSample("ClientDrawCellManager.split"); //perform op WorldOctTreeNode container = chunkTree.split(node); @@ -281,12 +283,8 @@ public class ClientBlockCellManager { * @param node the node * @return the distance */ - public double getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ - if(node.getData() == null){ - return GeomUtils.getMinSquaredDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); - } else { - return node.getData().getMinDistance(worldPos, node, distCache); - } + public long getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ + return node.getData().getMinDistance(worldPos, node, distCache); } /** @@ -429,6 +427,29 @@ public class ClientBlockCellManager { } } + /** + * Checks if this is a meta node + * @param pos The position of the player + * @param node The node + * @param distCache The distance cache + * @return true if it is a meta node, false otherwise + */ + private boolean isMeta(Vector3i pos, WorldOctTreeNode node, int distCache){ + return + node.getLevel() < this.chunkTree.getMaxLevel() - BlockChunkData.LOD_SIXTEENTH_RES && + this.getMinDistance(pos, node, distCache) > SIXTEENTH_RES_DIST + ; + } + + /** + * Sets this node to be a meta node + * @param node The node + */ + private void flagAsMeta(WorldOctTreeNode node){ + node.getData().setHasGenerated(true); + node.getData().setHomogenous(true); + } + /** * Gets whether this should be joined or not * @param pos the player position @@ -818,6 +839,15 @@ public class ClientBlockCellManager { return false; } + + /** + * Gets the number of nodes in the tree + * @return The number of nodes + */ + public int getNodeCount(){ + return this.chunkTree.getNodeCount(); + } + } diff --git a/src/main/java/electrosphere/client/scene/ClientWorldData.java b/src/main/java/electrosphere/client/scene/ClientWorldData.java index 0fb3d8dd..a38ca7dd 100644 --- a/src/main/java/electrosphere/client/scene/ClientWorldData.java +++ b/src/main/java/electrosphere/client/scene/ClientWorldData.java @@ -33,8 +33,6 @@ public class ClientWorldData { Vector3f worldMinPoint; Vector3f worldMaxPoint; - float randomDampener; - int worldDiscreteSize; @@ -42,12 +40,10 @@ public class ClientWorldData { public ClientWorldData( Vector3f worldMinPoint, Vector3f worldMaxPoint, - float randomDampener, int worldDiscreteSize ) { this.worldMinPoint = worldMinPoint; this.worldMaxPoint = worldMaxPoint; - this.randomDampener = randomDampener; this.worldDiscreteSize = worldDiscreteSize; } @@ -62,10 +58,6 @@ public class ClientWorldData { return worldMaxPoint; } - public float getRandomDampener() { - return randomDampener; - } - public int getWorldDiscreteSize() { return worldDiscreteSize; } diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index cc143191..f36bc2dd 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -35,27 +35,27 @@ public class ClientDrawCellManager { /** * The distance to draw at full resolution */ - public static final double FULL_RES_DIST = 8 * 8; + public static final double FULL_RES_DIST = 8; /** * The distance for half resolution */ - public static final double HALF_RES_DIST = 16 * 16; + public static final double HALF_RES_DIST = 16; /** * The distance for quarter resolution */ - public static final double QUARTER_RES_DIST = 20 * 20; + public static final double QUARTER_RES_DIST = 20; /** * The distance for eighth resolution */ - public static final double EIGHTH_RES_DIST = 32 * 32; + public static final double EIGHTH_RES_DIST = 32; /** * The distance for sixteenth resolution */ - public static final double SIXTEENTH_RES_DIST = 128 * 128; + public static final double SIXTEENTH_RES_DIST = 128; /** * Lod value for a full res chunk @@ -205,7 +205,9 @@ public class ClientDrawCellManager { return false; } if(node.isLeaf()){ - if(this.shouldSplit(playerPos, node, distCache)){ + if(this.isMeta(playerPos, node, distCache)){ + this.flagAsMeta(node); + } else if(this.shouldSplit(playerPos, node, distCache)){ Globals.profiler.beginCpuSample("ClientDrawCellManager.split"); //perform op WorldOctTreeNode container = chunkTree.split(node); @@ -325,12 +327,8 @@ public class ClientDrawCellManager { * @param node the node * @return the distance */ - public double getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ - if(node.getData() == null){ - return GeomUtils.getMinSquaredDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); - } else { - return node.getData().getMinDistance(worldPos, node, distCache); - } + public long getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ + return node.getData().getMinDistance(worldPos, node, distCache); } /** @@ -564,6 +562,29 @@ public class ClientDrawCellManager { } } + /** + * Checks if this is a meta node + * @param pos The position of the player + * @param node The node + * @param distCache The distance cache + * @return true if it is a meta node, false otherwise + */ + private boolean isMeta(Vector3i pos, WorldOctTreeNode node, int distCache){ + return + node.getLevel() < this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD && + this.getMinDistance(pos, node, distCache) > SIXTEENTH_RES_DIST + ; + } + + /** + * Sets this node to be a meta node + * @param node The node + */ + private void flagAsMeta(WorldOctTreeNode node){ + node.getData().setHasGenerated(true); + node.getData().setHomogenous(true); + } + /** * Gets whether this should be joined or not * @param pos the player position @@ -1001,6 +1022,14 @@ public class ClientDrawCellManager { return halfResCount; } + /** + * Gets the number of nodes in the tree + * @return The number of nodes + */ + public int getNodeCount(){ + return this.chunkTree.getNodeCount(); + } + /** * Gets The number of generated chunks * @return diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index ac9d3b41..0ea79087 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -91,7 +91,7 @@ public class DrawCell { /** * The cached minimum distance */ - double cachedMinDistance = -1; + long cachedMinDistance = -1; /** * Target to notify on generation completion @@ -579,11 +579,16 @@ public class DrawCell { * @param distCache the lod value under which distance caches are invalidated * @return the distance */ - public double getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ + public long getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ if(cachedMinDistance != INVALID_DIST_CACHE && distCache < lod){ return cachedMinDistance; } else { - this.cachedMinDistance = GeomUtils.getMinSquaredDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); + double dist = GeomUtils.approxMinDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); + if(Double.isFinite(dist)){ + this.cachedMinDistance = (long)dist; + } else { + this.cachedMinDistance = GeomUtils.REALLY_BIG_NUMBER; + } return cachedMinDistance; } } diff --git a/src/main/java/electrosphere/client/terrain/foliage/FoliageCell.java b/src/main/java/electrosphere/client/terrain/foliage/FoliageCell.java index 9a22cea0..80d6d3c8 100644 --- a/src/main/java/electrosphere/client/terrain/foliage/FoliageCell.java +++ b/src/main/java/electrosphere/client/terrain/foliage/FoliageCell.java @@ -190,7 +190,7 @@ public class FoliageCell { /** * The cached minimum distance */ - double cachedMinDistance = -1; + long cachedMinDistance = -1; /** * Target to notify on generation completion @@ -670,11 +670,16 @@ public class FoliageCell { * @param distCache the lod value under which distance caches are invalidated * @return the distance */ - public double getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ + public long getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ if(cachedMinDistance != INVALID_DIST_CACHE && distCache < lod){ return cachedMinDistance; } else { - this.cachedMinDistance = GeomUtils.getMinSquaredDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); + double dist = GeomUtils.approxMinDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); + if(Double.isFinite(dist)){ + this.cachedMinDistance = (long)dist; + } else { + this.cachedMinDistance = GeomUtils.REALLY_BIG_NUMBER; + } return cachedMinDistance; } } diff --git a/src/main/java/electrosphere/client/terrain/foliage/FoliageCellManager.java b/src/main/java/electrosphere/client/terrain/foliage/FoliageCellManager.java index 0b4a82d8..a2dca0c9 100644 --- a/src/main/java/electrosphere/client/terrain/foliage/FoliageCellManager.java +++ b/src/main/java/electrosphere/client/terrain/foliage/FoliageCellManager.java @@ -35,27 +35,27 @@ public class FoliageCellManager { /** * The distance to foliage at full resolution */ - public static final double FULL_RES_DIST = 16 * 16; + public static final double FULL_RES_DIST = 16; /** * The distance for half resolution */ - public static final double HALF_RES_DIST = 20 * 20; + public static final double HALF_RES_DIST = 20; /** * The distance for quarter resolution */ - public static final double QUARTER_RES_DIST = 16 * 16; + public static final double QUARTER_RES_DIST = 16; /** * The distance for eighth resolution */ - public static final double EIGHTH_RES_DIST = 24 * 24; + public static final double EIGHTH_RES_DIST = 24; /** * The distance for sixteenth resolution */ - public static final double SIXTEENTH_RES_DIST = 64 * 64; + public static final double SIXTEENTH_RES_DIST = 64; /** * Lod value for a full res chunk @@ -211,7 +211,9 @@ public class FoliageCellManager { return false; } if(node.isLeaf()){ - if(this.shouldSplit(absVoxelPos, node, distCache)){ + if(this.isMeta(absVoxelPos, node, distCache)){ + this.flagAsMeta(node); + } else if(this.shouldSplit(absVoxelPos, node, distCache)){ Globals.profiler.beginCpuSample("FoliageCellManager.split"); //perform op WorldOctTreeNode container = chunkTree.split(node); @@ -338,12 +340,8 @@ public class FoliageCellManager { * @param node the node * @return the distance */ - public double getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ - if(node.getData() == null){ - return GeomUtils.getMinSquaredDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); - } else { - return node.getData().getMinDistance(worldPos, node, distCache); - } + public long getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ + return node.getData().getMinDistance(worldPos, node, distCache); } /** @@ -486,6 +484,29 @@ public class FoliageCellManager { } } + /** + * Checks if this is a meta node + * @param pos The position of the player + * @param node The node + * @param distCache The distance cache + * @return true if it is a meta node, false otherwise + */ + private boolean isMeta(Vector3i pos, WorldOctTreeNode node, int distCache){ + return + node.getLevel() < this.chunkTree.getMaxLevel() - SIXTEENTH_RES_LOD && + this.getMinDistance(pos, node, distCache) > SIXTEENTH_RES_DIST + ; + } + + /** + * Sets this node to be a meta node + * @param node The node + */ + private void flagAsMeta(WorldOctTreeNode node){ + node.getData().setHasGenerated(true); + node.getData().setHomogenous(true); + } + /** * Gets whether this should be joined or not * @param pos the player position @@ -865,6 +886,13 @@ public class FoliageCellManager { return null; } + /** + * Gets the number of nodes in the tree + * @return The number of nodes + */ + public int getNodeCount(){ + return this.chunkTree.getNodeCount(); + } } diff --git a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java index c4c9fd52..1d7b3d58 100644 --- a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java +++ b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java @@ -36,8 +36,17 @@ public class ImGuiChunkMonitor { //ui framework text ImGui.text("Chunk Monitor"); - Globals.clientDrawCellManager.updateStatus(); - ImGui.text("Full res chunks: " + Globals.clientDrawCellManager.getMaxResCount()); + if(Globals.clientDrawCellManager != null){ + Globals.clientDrawCellManager.updateStatus(); + ImGui.text("Full res chunks: " + Globals.clientDrawCellManager.getMaxResCount()); + ImGui.text("Terrain node count: " + Globals.clientDrawCellManager.getNodeCount()); + } + if(Globals.clientBlockCellManager != null){ + ImGui.text("Block node count: " + Globals.clientBlockCellManager.getNodeCount()); + } + if(Globals.foliageCellManager != null){ + ImGui.text("Foliage node count: " + Globals.foliageCellManager.getNodeCount()); + } } }); chunkMonitorWindow.setOpen(false); diff --git a/src/main/java/electrosphere/game/server/world/ServerWorldData.java b/src/main/java/electrosphere/game/server/world/ServerWorldData.java index 023bafe9..dd08f820 100644 --- a/src/main/java/electrosphere/game/server/world/ServerWorldData.java +++ b/src/main/java/electrosphere/game/server/world/ServerWorldData.java @@ -7,6 +7,7 @@ import electrosphere.server.terrain.generation.DefaultChunkGenerator; import electrosphere.server.terrain.generation.TestGenerationChunkGenerator; import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainManager; +import electrosphere.server.terrain.models.TerrainModel; import electrosphere.util.FileUtils; import org.joml.Vector3d; @@ -21,7 +22,7 @@ public class ServerWorldData { /** * The size of the procedural world */ - public static final int PROCEDURAL_WORLD_SIZE = 2000; + public static final int PROCEDURAL_WORLD_SIZE = TerrainModel.MAX_WORLD_SIZE_DISCRETE; public static enum WorldType { GAME_WORLD, diff --git a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java index 7997797d..9cd32f5f 100644 --- a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java @@ -14,7 +14,6 @@ import electrosphere.engine.Globals; import electrosphere.logger.LoggerInterface; import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.template.ClientProtocolTemplate; -import electrosphere.server.terrain.manager.ServerTerrainChunk; /** * The client protocol for handling terrain messages @@ -35,15 +34,14 @@ public class TerrainProtocol implements ClientProtocolTemplate { //Vector3f worldMinPoint, Vector3f worldMaxPoint, int dynamicInterpolationRatio, float randomDampener, int worldDiscreteSize new Vector3f( message.getworldMinX(), - 0, - message.getworldMinY() + message.getworldMinY(), + message.getworldMinZ() ), new Vector3f( message.getworldMaxX(), - (int)(message.getworldSizeDiscrete() * ServerTerrainChunk.CHUNK_DIMENSION), - message.getworldMaxY() + message.getworldMaxY(), + message.getworldMaxZ() ), - message.getrandomDampener(), message.getworldSizeDiscrete() ); Globals.clientSceneWrapper.getCollisionEngine().setCollisionWorldData(new CollisionWorldData(Globals.clientWorldData)); diff --git a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java index d7837fb0..05bb96cd 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java @@ -37,8 +37,10 @@ public class TerrainMessage extends NetworkMessage { float randomDampener; int worldMinX; int worldMinY; + int worldMinZ; int worldMaxX; int worldMaxY; + int worldMaxZ; float value; int worldX; int worldY; @@ -138,6 +140,20 @@ public class TerrainMessage extends NetworkMessage { this.worldMinY = worldMinY; } + /** + * Gets worldMinZ + */ + public int getworldMinZ() { + return worldMinZ; + } + + /** + * Sets worldMinZ + */ + public void setworldMinZ(int worldMinZ) { + this.worldMinZ = worldMinZ; + } + /** * Gets worldMaxX */ @@ -166,6 +182,20 @@ public class TerrainMessage extends NetworkMessage { this.worldMaxY = worldMaxY; } + /** + * Gets worldMaxZ + */ + public int getworldMaxZ() { + return worldMaxZ; + } + + /** + * Sets worldMaxZ + */ + public void setworldMaxZ(int worldMaxZ) { + this.worldMaxZ = worldMaxZ; + } + /** * Gets value */ @@ -491,27 +521,27 @@ public class TerrainMessage extends NetworkMessage { TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA); stripPacketHeader(byteBuffer); rVal.setworldSizeDiscrete(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); - rVal.setdynamicInterpolationRatio(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); - rVal.setrandomDampener(ByteStreamUtils.popFloatFromByteQueue(byteBuffer)); rVal.setworldMinX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldMinY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldMinZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldMaxX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldMaxY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldMaxZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); return rVal; } /** * Constructs a message of type ResponseMetadata */ - public static TerrainMessage constructResponseMetadataMessage(int worldSizeDiscrete,int dynamicInterpolationRatio,float randomDampener,int worldMinX,int worldMinY,int worldMaxX,int worldMaxY){ + public static TerrainMessage constructResponseMetadataMessage(int worldSizeDiscrete,int worldMinX,int worldMinY,int worldMinZ,int worldMaxX,int worldMaxY,int worldMaxZ){ TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA); rVal.setworldSizeDiscrete(worldSizeDiscrete); - rVal.setdynamicInterpolationRatio(dynamicInterpolationRatio); - rVal.setrandomDampener(randomDampener); rVal.setworldMinX(worldMinX); rVal.setworldMinY(worldMinY); + rVal.setworldMinZ(worldMinZ); rVal.setworldMaxX(worldMaxX); rVal.setworldMaxY(worldMaxY); + rVal.setworldMaxZ(worldMaxZ); rVal.serialize(); return rVal; } @@ -1064,26 +1094,27 @@ public class TerrainMessage extends NetworkMessage { for(int i = 0; i < 4; i++){ rawBytes[2+i] = intValues[i]; } - intValues = ByteStreamUtils.serializeIntToBytes(dynamicInterpolationRatio); + intValues = ByteStreamUtils.serializeIntToBytes(worldMinX); for(int i = 0; i < 4; i++){ rawBytes[6+i] = intValues[i]; } - intValues = ByteStreamUtils.serializeFloatToBytes(randomDampener); + intValues = ByteStreamUtils.serializeIntToBytes(worldMinY); for(int i = 0; i < 4; i++){ rawBytes[10+i] = intValues[i]; - } intValues = ByteStreamUtils.serializeIntToBytes(worldMinX); + } + intValues = ByteStreamUtils.serializeIntToBytes(worldMinZ); for(int i = 0; i < 4; i++){ rawBytes[14+i] = intValues[i]; } - intValues = ByteStreamUtils.serializeIntToBytes(worldMinY); + intValues = ByteStreamUtils.serializeIntToBytes(worldMaxX); for(int i = 0; i < 4; i++){ rawBytes[18+i] = intValues[i]; } - intValues = ByteStreamUtils.serializeIntToBytes(worldMaxX); + intValues = ByteStreamUtils.serializeIntToBytes(worldMaxY); for(int i = 0; i < 4; i++){ rawBytes[22+i] = intValues[i]; } - intValues = ByteStreamUtils.serializeIntToBytes(worldMaxY); + intValues = ByteStreamUtils.serializeIntToBytes(worldMaxZ); for(int i = 0; i < 4; i++){ rawBytes[26+i] = intValues[i]; } diff --git a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java index de1fb7e0..915c1d4e 100644 --- a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java @@ -359,11 +359,11 @@ public class TerrainProtocol implements ServerProtocolTemplate { connectionHandler.addMessagetoOutgoingQueue( TerrainMessage.constructResponseMetadataMessage( realm.getServerWorldData().getWorldSizeDiscrete(), - realm.getServerWorldData().getDynamicInterpolationRatio(), - 0, (int)realm.getServerWorldData().getWorldBoundMin().x, + (int)realm.getServerWorldData().getWorldBoundMin().y, (int)realm.getServerWorldData().getWorldBoundMin().z, (int)realm.getServerWorldData().getWorldBoundMax().x, + (int)realm.getServerWorldData().getWorldBoundMax().y, (int)realm.getServerWorldData().getWorldBoundMax().z ) ); diff --git a/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java index 91ac4d17..c434d306 100644 --- a/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java @@ -78,18 +78,19 @@ public class OverworldChunkGenerator implements ChunkGenerator { * @return The heightmap array */ private float[][] getHeightmap(int worldX, int worldZ){ - String key = worldX + "_" + worldZ; - if(heightmapCache.containsKey(key)){ - return heightmapCache.get(key); - } else { - float[][] macroValues = model.getRad5MacroValuesAtPosition(worldX, worldZ); - float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk( - macroValues, - model.getDynamicInterpolationRatio() - ); - heightmapCache.put(key,heightmap); - return heightmap; - } + // String key = worldX + "_" + worldZ; + // if(heightmapCache.containsKey(key)){ + // return heightmapCache.get(key); + // } else { + // float[][] macroValues = model.getRad5MacroValuesAtPosition(worldX, worldZ); + // float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk( + // macroValues, + // model.getDynamicInterpolationRatio() + // ); + // heightmapCache.put(key,heightmap); + // return heightmap; + // } + return null; } } diff --git a/src/main/java/electrosphere/server/terrain/generation/continentphase/TerrainGenerator.java b/src/main/java/electrosphere/server/terrain/generation/continentphase/TerrainGenerator.java index fd0f0172..eb52d08a 100644 --- a/src/main/java/electrosphere/server/terrain/generation/continentphase/TerrainGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/continentphase/TerrainGenerator.java @@ -162,13 +162,14 @@ public class TerrainGenerator { erosionSimulation.simulate(); erosionHeightmap = erosionSimulation.getData(); - rVal = new TerrainModel( - interpolationPhaseDimension, - modelInput, - OCEAN_THRESHOLD * verticalInterpolationRatio, - MOUNTAIN_THRESHOLD * verticalInterpolationRatio, - dynamicInterpRatio - ); + // rVal = new TerrainModel( + // interpolationPhaseDimension, + // modelInput, + // OCEAN_THRESHOLD * verticalInterpolationRatio, + // MOUNTAIN_THRESHOLD * verticalInterpolationRatio, + // dynamicInterpRatio + // ); + rVal = TerrainModel.create(); //create internal renderer diff --git a/src/main/java/electrosphere/server/terrain/generation/macro/DefaultMacroGenerator.java b/src/main/java/electrosphere/server/terrain/generation/macro/DefaultMacroGenerator.java new file mode 100644 index 00000000..fd6fb2a3 --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/macro/DefaultMacroGenerator.java @@ -0,0 +1,32 @@ +package electrosphere.server.terrain.generation.macro; + +import electrosphere.server.terrain.models.TerrainModel; + +/** + * The default approach to generating macro data + */ +public class DefaultMacroGenerator implements MacroGenerator { + + /** + * The default surface biome + */ + static final short DEFAULT_SURFACE_BIOME = 1; + + @Override + public void generate(TerrainModel model) { + int DIM = model.getDiscreteArrayDimension(); + float[][] elevation = model.getElevation(); + short[][] biome = model.getBiome(); + + for(int x = 0; x < DIM; x++){ + for(int y = 0; y < DIM; y++){ + elevation[x][y] = 1; + biome[x][y] = DEFAULT_SURFACE_BIOME; + } + } + + model.setElevationArray(elevation); + model.setBiome(biome); + } + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/macro/MacroGenerator.java b/src/main/java/electrosphere/server/terrain/generation/macro/MacroGenerator.java new file mode 100644 index 00000000..9cf549a4 --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/macro/MacroGenerator.java @@ -0,0 +1,16 @@ +package electrosphere.server.terrain.generation.macro; + +import electrosphere.server.terrain.models.TerrainModel; + +/** + * Populates a terrain model's macro data + */ +public interface MacroGenerator { + + /** + * Generates the macro data in a terrain model + * @param model The terrain model + */ + public void generate(TerrainModel model); + +} diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java index 40b7ebdd..70805d82 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java @@ -5,8 +5,8 @@ import electrosphere.engine.Globals; import electrosphere.game.server.world.ServerWorldData; import electrosphere.server.terrain.diskmap.ChunkDiskMap; import electrosphere.server.terrain.generation.TestGenerationChunkGenerator; -import electrosphere.server.terrain.generation.continentphase.TerrainGenerator; import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; +import electrosphere.server.terrain.generation.macro.DefaultMacroGenerator; import electrosphere.server.terrain.models.TerrainModel; import electrosphere.server.terrain.models.TerrainModification; import electrosphere.util.FileUtils; @@ -14,6 +14,7 @@ import electrosphere.util.annotation.Exclude; import java.nio.ByteBuffer; import java.nio.FloatBuffer; +import java.nio.ShortBuffer; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; @@ -99,13 +100,10 @@ public class ServerTerrainManager { * Generates a terrain model for the manager */ public void generate(){ - TerrainGenerator terrainGen = new TerrainGenerator(); - terrainGen.setInterpolationRatio(parent.getWorldSizeDiscrete()/200); - terrainGen.setVerticalInterpolationRatio(parent.getWorldSizeDiscrete()); - terrainGen.setRandomSeed(seed); - model = terrainGen.generateModel(); - this.chunkGenerator.setModel(model); - model.setInterpolationRandomDampener(SERVER_TERRAIN_MANAGER_DAMPENER); + this.model = TerrainModel.create(); + DefaultMacroGenerator generator = new DefaultMacroGenerator(); + generator.generate(this.model); + this.chunkGenerator.setModel(this.model); this.chunkDiskMap = ChunkDiskMap.init(); } @@ -115,16 +113,38 @@ public class ServerTerrainManager { */ public void save(String saveName){ if(model != null){ - ByteBuffer buffer = ByteBuffer.allocate(model.getElevation().length * model.getElevation()[0].length * 4); - FloatBuffer floatView = buffer.asFloatBuffer(); - for(int x = 0; x < model.getElevation().length; x++){ - floatView.put(model.getElevation()[x]); + if(model.getDiscreteArrayDimension() < 1){ + throw new Error("Invalid terrain macro data size!"); + } + + //save the model itself + FileUtils.serializeObjectToSavePath(saveName, "./terrain.json", model); + + //save the elevation array + ByteBuffer floatBuff = ByteBuffer.allocate(model.getDiscreteArrayDimension() * model.getDiscreteArrayDimension() * 4); + FloatBuffer floatView = floatBuff.asFloatBuffer(); + for(int x = 0; x < model.getDiscreteArrayDimension(); x++){ + for(int y = 0; y < model.getDiscreteArrayDimension(); y++){ + floatView.put(model.getElevation()[x][y]); + } } if(floatView.position() > 0){ floatView.flip(); } - FileUtils.serializeObjectToSavePath(saveName, "./terrain.json", model); - FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array()); + FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", floatBuff.array()); + + //save the biome array + ByteBuffer shortBuff = ByteBuffer.allocate(model.getDiscreteArrayDimension() * model.getDiscreteArrayDimension() * 2); + ShortBuffer shortView = shortBuff.asShortBuffer(); + for(int x = 0; x < model.getDiscreteArrayDimension(); x++){ + for(int y = 0; y < model.getDiscreteArrayDimension(); y++){ + shortView.put(model.getBiome()[x][y]); + } + } + if(shortView.position() > 0){ + shortView.flip(); + } + FileUtils.saveBinaryToSavePath(saveName, "./biome.dat", shortBuff.array()); } //for each chunk, save via disk map for(ServerTerrainChunk chunk : this.chunkCache.getContents()){ @@ -143,18 +163,41 @@ public class ServerTerrainManager { public void load(String saveName){ //load terrain model if(FileUtils.getSaveFile(saveName, "./terrain.json").exists()){ + + //read the model itself model = FileUtils.loadObjectFromSavePath(saveName, "./terrain.json", TerrainModel.class); chunkGenerator.setModel(model); - byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./terrain.dat"); - ByteBuffer buffer = ByteBuffer.wrap(data); - FloatBuffer floatView = buffer.asFloatBuffer(); - float[][] elevation = new float[parent.getWorldSizeDiscrete()][parent.getWorldSizeDiscrete()]; - for(int x = 0; x < parent.getWorldSizeDiscrete(); x++){ - for(int y = 0; y < parent.getWorldSizeDiscrete(); y++){ - elevation[x][y] = floatView.get(); + if(model.getDiscreteArrayDimension() < 1){ + throw new Error("Invalid terrain macro data size!"); + } + + //read the elevation data + float[][] elevation = new float[model.getDiscreteArrayDimension()][model.getDiscreteArrayDimension()]; + if(FileUtils.checkSavePathExists(saveName, "./terrain.dat")){ + byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./terrain.dat"); + ByteBuffer buffer = ByteBuffer.wrap(data); + FloatBuffer floatView = buffer.asFloatBuffer(); + for(int x = 0; x < model.getDiscreteArrayDimension(); x++){ + for(int y = 0; y < model.getDiscreteArrayDimension(); y++){ + elevation[x][y] = floatView.get(); + } } } model.setElevationArray(elevation); + + //read the biome data + short[][] biome = new short[model.getDiscreteArrayDimension()][model.getDiscreteArrayDimension()]; + if(FileUtils.checkSavePathExists(saveName, "./biome.dat")){ + byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./biome.dat"); + ByteBuffer buffer = ByteBuffer.wrap(data); + ShortBuffer shortView = buffer.asShortBuffer(); + for(int x = 0; x < model.getDiscreteArrayDimension(); x++){ + for(int y = 0; y < model.getDiscreteArrayDimension(); y++){ + biome[x][y] = shortView.get(); + } + } + } + model.setBiome(biome); } //load chunk disk map chunkDiskMap = ChunkDiskMap.init(saveName); @@ -176,10 +219,6 @@ public class ServerTerrainManager { this.chunkCache.clear(); } - public float[][] getTerrainAtChunk(int x, int y){ - return model.getElevationForChunk(x, y); - } - public float getDiscreteValue(int x, int y){ if(model != null){ return model.getElevation()[x][y]; @@ -187,26 +226,6 @@ public class ServerTerrainManager { return 0; } } - - public int getDynamicInterpolationRatio(){ - //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING - if(model != null){ - return model.getDynamicInterpolationRatio(); - } else { - //THIS FIRES IF THERE IS AN ARENA WORLD RUNNING - return 0; - } - } - - public float getRandomDampener(){ - //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING - if(model != null){ - return model.getRandomDampener(); - } else { - //THIS FIRES IF THERE IS AN ARENA WORLD RUNNING - return 0.0f; - } - } /** * Gets the terrain model backing this terrain manager diff --git a/src/main/java/electrosphere/server/terrain/models/TerrainModel.java b/src/main/java/electrosphere/server/terrain/models/TerrainModel.java index 865964bd..31a33638 100644 --- a/src/main/java/electrosphere/server/terrain/models/TerrainModel.java +++ b/src/main/java/electrosphere/server/terrain/models/TerrainModel.java @@ -14,29 +14,24 @@ public class TerrainModel { /** * Maximum size of the macro data */ - public static final int MAX_MACRO_DATA_SIZE = 2000; + public static final int MAX_MACRO_DATA_SIZE = 2048; /** * The scale of the macro data */ public static final int DEFAULT_MACRO_DATA_SCALE = 32; - /** - * The dynamic interpolation ratio + * The maximum discrete world size */ - int dynamicInterpolationRatio; - - /** - * The interpolation dampener - */ - float interpolationRandomDampener = 0.4f; + public static final int MAX_WORLD_SIZE_DISCRETE = MAX_MACRO_DATA_SIZE * DEFAULT_MACRO_DATA_SCALE; + /** * The discrete array dimension of the model */ - int discreteArrayDimension; + int discreteArrayDimension = MAX_MACRO_DATA_SIZE; /** * The macro level elevation data @@ -78,28 +73,16 @@ public class TerrainModel { */ TerrainModel() { } - + /** - * Constructor - * @param dimension The dimension of the model - * @param elevation The macro elevation data - * @param realOceanThreshold The real coordinate ocean threshold - * @param realMountainThreshold The real coordinate mountain threshold - * @param dynamicInterpolationRatio The dynamic interpolation ratio + * Creates the default terrain model + * @return The default terrain model */ - public TerrainModel( - int dimension, - float[][] elevation, - float realOceanThreshold, - float realMountainThreshold, - int dynamicInterpolationRatio - ){ - - this.dynamicInterpolationRatio = dynamicInterpolationRatio; - this.discreteArrayDimension = dimension; - this.elevation = elevation; - this.realMountainThreshold = realMountainThreshold; - this.realOceanThreshold = realOceanThreshold; + public static TerrainModel create(){ + TerrainModel rVal = new TerrainModel(); + rVal.elevation = new float[rVal.discreteArrayDimension][rVal.discreteArrayDimension]; + rVal.biome = new short[rVal.discreteArrayDimension][rVal.discreteArrayDimension]; + return rVal; } /** @@ -108,10 +91,9 @@ public class TerrainModel { * @param dynamicInterpolationRatio The dynamic interpolation ratio * @return The terrain model */ - public static TerrainModel constructTerrainModel(int dimension, int dynamicInterpolationRatio){ + public static TerrainModel constructTerrainModel(int dimension){ TerrainModel rVal = new TerrainModel(); rVal.discreteArrayDimension = dimension; - rVal.dynamicInterpolationRatio = dynamicInterpolationRatio; return rVal; } @@ -122,7 +104,6 @@ public class TerrainModel { public static TerrainModel generateTestModel(){ TerrainModel rVal = new TerrainModel(); rVal.discreteArrayDimension = TestGenerationChunkGenerator.GENERATOR_REALM_SIZE; - rVal.dynamicInterpolationRatio = 1; int macroDataImageScale = TestGenerationChunkGenerator.GENERATOR_REALM_SIZE / DEFAULT_MACRO_DATA_SCALE + 1; rVal.biome = new short[macroDataImageScale][macroDataImageScale]; for(int x = 0; x < macroDataImageScale; x++){ @@ -149,290 +130,13 @@ public class TerrainModel { public short[][] getBiome(){ return biome; } - + /** - * Gets the interpolation random dampener - * @param f The interpolation random dampener + * Sets the biome array + * @param biome The biome array */ - public void setInterpolationRandomDampener(float f){ - interpolationRandomDampener = f; - } - - /** - * Dynamically interpolates a chunk of a specific size from the pre-existing elevation map - * @param x The x position on the elevation map to get a chunk from - * @param y The y position on the elevation map to get a chunk from - * @return Dynamically interpolated float array of elevations of chunk - */ - public float[][] getElevationForChunk(int x, int y){ - - //this is what we intend to return from the function - float[][] rVal = new float[dynamicInterpolationRatio][dynamicInterpolationRatio]; - - /* - So we're looking at chunk x,y - - if this is our grid: - - 4 0.1 0.2 0.3 0.4 - ^ - | 3 0.1 0.2 0.3 0.4 - | - | 2 0.1 0.2 0.3 0.4 - - x 1 0.1 0.2 0.3 0.4 - - 0 1 2 3 - - y ---- > - - say we're looking at x=2,y=1 - - "macroValues" should contain the values for bounds x = [1,3] and y = [0,2] - - the goal is to have the "center" of the output chunk have the value the - elevation grid at x=2,y=1 - - */ - - - //set macroValues - float[][] macroValues = getMacroValuesAtPosition(x,y); - - - - - int halfLength = dynamicInterpolationRatio/2; - - /* - - Four quadrants we're generating - - _____________________ - |1 |2 | - | | | - | | | - | | | - |__________|__________| - |3 |4 | - | | | - | | | - |__________|__________| - - First set of loops is quadrant 1 - then quadrant 2 - then quadrant 3 - then quadrant 4 - - */ - - int outXOffset = 0; - int outYOffset = 0; - - for(int i = 0; i < halfLength; i++){ - for(int j = 0; j < halfLength; j++){ - rVal[i+outXOffset][j+outYOffset] = - (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][0] + - (1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] + - (1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[0][1] + - (1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][1] - ; - } - } - - outXOffset = halfLength; - for(int i = 0; i < halfLength; i++){ - for(int j = 0; j < halfLength; j++){ - rVal[i+outXOffset][j+outYOffset] = - (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] + - (1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][0] + - (1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][1] + - (1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[2][1] - ; - } - } - - outXOffset = 0; - outYOffset = halfLength; - for(int i = 0; i < halfLength; i++){ - for(int j = 0; j < halfLength; j++){ - rVal[i+outXOffset][j+outYOffset] = - (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][1] + - (1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] + - (1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[0][2] + - (1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][2] - ; - } - } - - outXOffset = halfLength; - for(int i = 0; i < halfLength; i++){ - for(int j = 0; j < halfLength; j++){ - rVal[i+outXOffset][j+outYOffset] = - (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] + - (1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][1] + - (1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][2] + - (1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[2][2] - ; - } - } - - - return rVal; - } - - - /* - So we're looking at chunk x,y - - if this is our grid: - - 4 0.1 0.2 0.3 0.4 - ^ - | 3 0.1 0.2 0.3 0.4 - | - | 2 0.1 0.2 0.3 0.4 - - x 1 0.1 0.2 0.3 0.4 - - 0 1 2 3 - - y ---- > - - say we're looking at x=2,y=1 - - "macroValues" should contain the values for x = [1,3] and y = [0,2] - - the goal is to have the "center" of the output chunk have the value the - elevation grid at x=2,y=1 - - */ - - /** - * Gets the macro values at a position - * @param x The world x position - * @param y The world y position - * @return The macro values - */ - public float[][] getMacroValuesAtPosition(int x, int y){ - - float[][] rVal = new float[3][3]; - rVal[1][1] = elevation[x][y]; - if(x - 1 >= 0){ - rVal[0][1] = elevation[x-1][y]; - if(y - 1 >= 0){ - rVal[0][0] = elevation[x-1][y-1]; - } else { - rVal[0][0] = 0; - } - if(y + 1 < discreteArrayDimension){ - rVal[0][2] = elevation[x-1][y+1]; - } else { - rVal[0][2] = 0; - } - } else { - rVal[0][0] = 0; - rVal[0][1] = 0; - rVal[0][2] = 0; - } - if(x + 1 < discreteArrayDimension){ - rVal[2][1] = elevation[x+1][y]; - if(y - 1 >= 0){ - rVal[2][0] = elevation[x+1][y-1]; - } else { - rVal[2][0] = 0; - } - if(y + 1 < discreteArrayDimension){ - rVal[2][2] = elevation[x+1][y+1]; - } else { - rVal[2][2] = 0; - } - } else { - rVal[2][0] = 0; - rVal[2][1] = 0; - rVal[2][2] = 0; - } - if(y - 1 >= 0){ - rVal[1][0] = elevation[x][y-1]; - } else { - rVal[1][0] = 0; - } - if(y + 1 < discreteArrayDimension){ - rVal[1][2] = elevation[x][y+1]; - } else { - rVal[1][2] = 0; - } - - return rVal; - } - - - - - /** - * Gets the 5-radius macro values at a position - * @param x The x position - * @param y The y position - * @return The macro values - */ - public float[][] getRad5MacroValuesAtPosition(int x, int y){ - - float[][] rVal = new float[5][5]; - for(int i = -2; i < 3; i++){ - for(int j = -2; j < 3; j++){ - if(x + i >= 0 && x + i < discreteArrayDimension && y + j >= 0 && y + j < discreteArrayDimension){ - rVal[i+2][j+2] = elevation[x+i][y+j]; - } else { - rVal[i+2][j+2] = 0; - } - } - } - - return rVal; - } - - - /* - So we're looking at chunk x,y - - if this is our grid: - - 4 0.1 0.2 0.3 0.4 - ^ - | 3 0.1 0.2 0.3 0.4 - | - | 2 0.1 0.2 0.3 0.4 - - x 1 0.1 0.2 0.3 0.4 - - 0 1 2 3 - - y ---- > - - say we're looking at x=2,y=1 - - "macroValues" should contain the values for x = [1,3] and y = [0,2] - - the goal is to have the "center" of the output chunk have the value the - elevation grid at x=2,y=1 - - */ - - - /** - * Gets the random dampener for the model - * @return The random dampener - */ - public float getRandomDampener(){ - return interpolationRandomDampener; - } - - /** - * Gets the dynamic interpolation ratio - * @return The ratio - */ - public int getDynamicInterpolationRatio(){ - return dynamicInterpolationRatio; + public void setBiome(short[][] biome){ + this.biome = biome; } /** @@ -532,6 +236,13 @@ public class TerrainModel { public double getMacroWidthInRealTerms(){ return this.macroDataScale * ServerTerrainChunk.CHUNK_DIMENSION; } - + + /** + * Gets the size of the discrete arrays + * @return The size of the discrete arrays + */ + public int getDiscreteArrayDimension(){ + return discreteArrayDimension; + } } diff --git a/src/main/java/electrosphere/util/ds/octree/WorldOctTree.java b/src/main/java/electrosphere/util/ds/octree/WorldOctTree.java index df755564..5a3b3e91 100644 --- a/src/main/java/electrosphere/util/ds/octree/WorldOctTree.java +++ b/src/main/java/electrosphere/util/ds/octree/WorldOctTree.java @@ -270,6 +270,14 @@ public class WorldOctTree { } } + /** + * The number of nodes in the tree + * @return The number of nodes + */ + public int getNodeCount(){ + return nodes.size(); + } + /** * A node in a chunk tree diff --git a/src/main/java/electrosphere/util/math/GeomUtils.java b/src/main/java/electrosphere/util/math/GeomUtils.java index a6f218f5..34a516b6 100644 --- a/src/main/java/electrosphere/util/math/GeomUtils.java +++ b/src/main/java/electrosphere/util/math/GeomUtils.java @@ -8,6 +8,16 @@ import org.joml.Vector3i; */ public class GeomUtils { + /** + * A really big number + */ + public static final int REALLY_BIG_NUMBER = 1000000; + + /** + * Cutoff after which we just take the distance between the current axis + */ + public static final int SIMPLIFICATION_CUTOFF = 100000; + /** * Gets the minimum distance from a point to an axis aligned cube * @param pos the position to check against @@ -103,6 +113,125 @@ public class GeomUtils { } } + /** + * Gets the minimum squared distance from a point to an axis aligned cube + * @param pos the position to check against + * @param cubeMin The min position of the cube + * @param cubeMax The max position of the cube + * @return the distance + */ + public static double approxMinDistanceAABB(Vector3i pos, Vector3i cubeMin, Vector3i cubeMax){ + int minX = cubeMin.x; + int minY = cubeMin.y; + int minZ = cubeMin.z; + int maxX = cubeMax.x; + int maxY = cubeMax.y; + int maxZ = cubeMax.z; + if(pos.x > minX && pos.x < maxX){ + if(pos.y > minY && pos.y < maxY){ + if(pos.z > minZ && pos.z < maxZ){ + return 0; + } else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){ + return pos.distance(pos.x,pos.y,minZ); + } else { + return pos.distance(pos.x,pos.y,maxZ); + } + } else if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){ + if(Math.abs(pos.y - maxY) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.y - maxY); + } + if(pos.z > minZ && pos.z < maxZ){ + return pos.distance(pos.x,minY,pos.z); + } else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){ + return pos.distance(pos.x,minY,minZ); + } else { + return pos.distance(pos.x,minY,maxZ); + } + } else { + if(Math.abs(pos.y - minY) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.y - minY); + } + if(pos.z > minZ && pos.z < maxZ){ + return pos.distance(pos.x,maxY,pos.z); + } else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){ + return pos.distance(pos.x,maxY,minZ); + } else { + return pos.distance(pos.x,maxY,maxZ); + } + } + } else if(Math.abs(pos.x - minX) < Math.abs(pos.x - maxX)){ + if(Math.abs(pos.x - maxX) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.x - maxX); + } + if(pos.y > minY && pos.y < maxY){ + if(pos.z > minZ && pos.z < maxZ){ + return pos.distance(minX,pos.y,pos.z); + } else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){ + return pos.distance(minX,pos.y,minZ); + } else { + return pos.distance(minX,pos.y,maxZ); + } + } else if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){ + if(Math.abs(pos.y - maxY) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.y - maxY); + } + if(pos.z > minZ && pos.z < maxZ){ + return pos.distance(minX,minY,pos.z); + } else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){ + return pos.distance(minX,minY,minZ); + } else { + return pos.distance(minX,minY,maxZ); + } + } else { + if(Math.abs(pos.y - minY) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.y - minY); + } + if(pos.z > minZ && pos.z < maxZ){ + return pos.distance(minX,maxY,pos.z); + } else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){ + return pos.distance(minX,maxY,minZ); + } else { + return pos.distance(minX,maxY,maxZ); + } + } + } else { + if(Math.abs(pos.x - minX) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.x - maxX); + } + if(pos.y > minY && pos.y < maxY){ + if(pos.z > minZ && pos.z < maxZ){ + return pos.distance(maxX,pos.y,pos.z); + } else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){ + return pos.distance(maxX,pos.y,minZ); + } else { + return pos.distance(maxX,pos.y,maxZ); + } + } else if(Math.abs(pos.y - minY) < Math.abs(pos.y - maxY)){ + if(Math.abs(pos.y - maxY) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.y - maxY); + } + if(pos.z > minZ && pos.z < maxZ){ + return pos.distance(maxX,minY,pos.z); + } else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){ + return pos.distance(maxX,minY,minZ); + } else { + return pos.distance(maxX,minY,maxZ); + } + } else { + if(Math.abs(pos.y - minY) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.y - minY); + } + if(pos.z > minZ && pos.z < maxZ){ + return pos.distance(maxX,maxY,pos.z); + } else if(Math.abs(pos.z - minZ) < Math.abs(pos.z - maxZ)){ + return pos.distance(maxX,maxY,minZ); + } else { + return pos.distance(maxX,maxY,maxZ); + } + } + } + } + /** * Gets the minimum squared distance from a point to an axis aligned cube * @param pos the position to check against @@ -205,7 +334,7 @@ public class GeomUtils { * @param cubeMax The max position of the cube * @return the distance */ - public static double getMinSquaredDistanceAABB(Vector3i pos, Vector3i cubeMin, Vector3i cubeMax){ + public static long getMinSquaredDistanceAABB(Vector3i pos, Vector3i cubeMin, Vector3i cubeMax){ int minX = cubeMin.x; int minY = cubeMin.y; int minZ = cubeMin.z; diff --git a/src/net/terrain.json b/src/net/terrain.json index 1de957f8..8b5c8cd2 100644 --- a/src/net/terrain.json +++ b/src/net/terrain.json @@ -25,6 +25,10 @@ "name" : "worldMinY", "type" : "FIXED_INT" }, + { + "name" : "worldMinZ", + "type" : "FIXED_INT" + }, { "name" : "worldMaxX", "type" : "FIXED_INT" @@ -33,6 +37,10 @@ "name" : "worldMaxY", "type" : "FIXED_INT" }, + { + "name" : "worldMaxZ", + "type" : "FIXED_INT" + }, { @@ -120,12 +128,12 @@ "description" : "Tell the client the terrain metadata", "data" : [ "worldSizeDiscrete", - "dynamicInterpolationRatio", - "randomDampener", "worldMinX", "worldMinY", + "worldMinZ", "worldMaxX", - "worldMaxY" + "worldMaxY", + "worldMaxZ" ] }, { diff --git a/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java b/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java index 942ba843..1a42904c 100644 --- a/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java +++ b/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java @@ -35,7 +35,7 @@ public class ClientDrawCellManagerTests { public void testJoinCase(){ int worldDiscreteSize = 64; - Globals.clientWorldData = new ClientWorldData(new Vector3f(0), new Vector3f(worldDiscreteSize * ServerTerrainChunk.CHUNK_DIMENSION), 0, worldDiscreteSize); + Globals.clientWorldData = new ClientWorldData(new Vector3f(0), new Vector3f(worldDiscreteSize * ServerTerrainChunk.CHUNK_DIMENSION), worldDiscreteSize); ClientDrawCellManager manager = new ClientDrawCellManager(null, 64); Vector3i playerPos = new Vector3i(0,0,0); WorldOctTreeNode node = WorldOctTreeNode.constructorForTests(manager.chunkTree, 1, new Vector3i(16,0,0), new Vector3i(32,16,16));