diff --git a/assets/Data/game/biomes.json b/assets/Data/game/biomes.json index 3cc5e25a..5215af5f 100644 --- a/assets/Data/game/biomes.json +++ b/assets/Data/game/biomes.json @@ -49,6 +49,37 @@ ] } ], + "surfaceGenerationParams": { + "surfaceGenTag": "hills", + "heightOffset": 10, + "floorVariants": [ + ], + "foliageDescriptions": [ + ] + } + }, + { + "id": "hills_forested", + "displayName": "Hills (Forested)", + "isAerial": false, + "isSurface": true, + "isSubterranean": false, + "regions": [ + { + "frequency": 1.0, + "baseFloorVoxel": 1, + "floorVariants": [ + { + "voxelId": 2, + "frequency": 1.0, + "dispersion": 1.0, + "priority": 1.0 + } + ], + "foliageDescription": [ + ] + } + ], "surfaceGenerationParams": { "surfaceGenTag": "hills", "heightOffset": 10, @@ -60,7 +91,7 @@ "pine2" ], "regularity": 0.6, - "threshold": 0.05, + "threshold": 0.2, "scale": 0.5, "priority": 1.0 } diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index c840f8fc..cc172925 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1395,6 +1395,8 @@ Simplify WorldOctTree to reduce lag with large node counts Increase memory allowance, mostly fixed latency while walking around AssetManager streaming budget Better chunk position hashing algo +Hills generator work +Fix foliage manager and cells confusing worldpos and absolutevoxelpos diff --git a/src/main/java/electrosphere/client/terrain/foliage/FoliageCell.java b/src/main/java/electrosphere/client/terrain/foliage/FoliageCell.java index bada1e21..1f915576 100644 --- a/src/main/java/electrosphere/client/terrain/foliage/FoliageCell.java +++ b/src/main/java/electrosphere/client/terrain/foliage/FoliageCell.java @@ -17,7 +17,6 @@ import electrosphere.entity.Entity; import electrosphere.entity.btree.BehaviorTree; import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; import electrosphere.renderer.buffer.ShaderAttribute; -import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.util.ds.octree.WorldOctTree.WorldOctTreeNode; import electrosphere.util.math.GeomUtils; @@ -134,7 +133,7 @@ public class FoliageCell { /** * The data for generating the visuals */ - TransvoxelChunkData chunkData; + ChunkData chunkData; /** * Tracks whether the foliage cell has requested its chunk data or not @@ -226,7 +225,7 @@ public class FoliageCell { worldPos.x, worldPos.y, worldPos.z, - 0 + ChunkData.NO_STRIDE ); if(currentChunk == null){ success = false; @@ -238,9 +237,11 @@ public class FoliageCell { this.setFailedGenerationAttempts(this.getFailedGenerationAttempts() + 1); return; } - this.chunkData = new TransvoxelChunkData(currentChunk.getVoxelWeight(), currentChunk.getVoxelType(), 0); + this.chunkData = currentChunk; + } + if(success){ + this.generate(); } - this.generate(); } @@ -248,40 +249,41 @@ public class FoliageCell { * Generates the foliage cell */ protected void generate(){ + int airID = 0; boolean shouldGenerate = false; - ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos,ChunkData.NO_STRIDE); - if(data == null){ - return; - } //get foliage types supported List foliageTypesSupported = new LinkedList(); + Map handledTypes = new HashMap(); + handledTypes.put(airID,true); boolean airAbove = true; int scale = (int)Math.pow(2,lod); for(int x = 0; x < scale; x++){ for(int y = 0; y < scale; y++){ for(int z = 0; z < scale; z++){ + int voxelType = chunkData.getType(new Vector3i(this.voxelPos).add(x,y,z)); + if(handledTypes.containsKey(voxelType)){ + continue; + } if(voxelPos.y + y >= ServerTerrainChunk.CHUNK_DIMENSION){ continue; } - List currentList = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(new Vector3i(voxelPos).add(x,y,z))).getAmbientFoliage(); + List currentList = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(voxelType).getAmbientFoliage(); if(currentList == null){ + handledTypes.put(voxelType,true); continue; } foliageTypesSupported.addAll(currentList); - airAbove = data.getType(voxelPos.x + x,voxelPos.y + y + 1,voxelPos.z + z) == 0; + airAbove = chunkData.getType(voxelPos.x + x,voxelPos.y + y + 1,voxelPos.z + z) == airID; if(foliageTypesSupported != null && foliageTypesSupported.size() > 0 && airAbove){ shouldGenerate = true; - break; + handledTypes.put(voxelType,true); } } - if(shouldGenerate){ - break; - } - } - if(shouldGenerate){ - break; } } + // if(Math.abs(worldPos.x - 32767) < 5 && Math.abs(worldPos.y - 5) < 3 && Math.abs(worldPos.z - 32767) < 3 && this.chunkData.getHomogenousValue() != 0){ + // System.out.println(worldPos.x + " " + worldPos.y + " " + worldPos.z + " - " + shouldGenerate); + // } if(shouldGenerate){ Entity oldEntity = this.modelEntity; //create entity @@ -447,16 +449,16 @@ public class FoliageCell { /** * Gets the minimum distance from a node to a point - * @param pos the position to check against + * @param absVoxelPos the position to check against * @param node the node * @param distCache the lod value under which distance caches are invalidated * @return the distance */ - public long getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ + public long getMinDistance(Vector3i absVoxelPos, WorldOctTreeNode node, int distCache){ if(cachedMinDistance != INVALID_DIST_CACHE && distCache < lod){ return cachedMinDistance; } else { - double dist = GeomUtils.approxMinDistanceAABB(worldPos, node.getMinBound(), node.getMaxBound()); + double dist = GeomUtils.approxMinDistanceAABB(absVoxelPos, node.getMinBound(), node.getMaxBound()); if(Double.isFinite(dist)){ this.cachedMinDistance = (long)dist; } else { diff --git a/src/main/java/electrosphere/client/terrain/foliage/FoliageCellManager.java b/src/main/java/electrosphere/client/terrain/foliage/FoliageCellManager.java index ce9e6d46..9896b25c 100644 --- a/src/main/java/electrosphere/client/terrain/foliage/FoliageCellManager.java +++ b/src/main/java/electrosphere/client/terrain/foliage/FoliageCellManager.java @@ -8,6 +8,7 @@ import java.util.Map; import org.joml.Vector3d; import org.joml.Vector3i; +import electrosphere.client.terrain.cache.ChunkData; import electrosphere.engine.Globals; import electrosphere.entity.EntityUtils; import electrosphere.game.data.foliage.type.FoliageType; @@ -22,6 +23,11 @@ import electrosphere.util.math.GeomUtils; */ public class FoliageCellManager { + /** + * If moved this many cells in 1 frame, completely bust meta cells + */ + public static final int TELEPORT_DISTANCE = 5000; + /** * Number of times to try updating per frame. Lower this to reduce lag but slow down terrain mesh generation. */ @@ -87,6 +93,11 @@ public class FoliageCellManager { */ public static final int ALL_RES_LOD = 5; + /** + * Lod value for busting up meta cells + */ + public static final int BUST_META_CELLS = 40; + /** * The octree holding all the chunks to evaluate */ @@ -142,6 +153,11 @@ public class FoliageCellManager { */ boolean initialized = false; + /** + * The list of points to break at next evaluation + */ + List breakPoints = new LinkedList(); + /** * Constructor * @param worldDim The size of the world in chunks @@ -177,6 +193,9 @@ public class FoliageCellManager { Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); Vector3i absVoxelPos = Globals.clientWorldData.convertRealToAbsoluteVoxelSpace(playerPos); int distCache = this.getDistCache(this.lastPlayerPos, absVoxelPos); + if(absVoxelPos.distance(this.lastPlayerPos) > TELEPORT_DISTANCE){ + distCache = BUST_META_CELLS; + } this.lastPlayerPos.set(absVoxelPos); //the sets to iterate through updatedLastFrame = true; @@ -190,6 +209,9 @@ public class FoliageCellManager { if(!updatedLastFrame && !this.initialized){ this.initialized = true; } + if(this.breakPoints.size() > 0){ + this.breakPoints.clear(); + } } Globals.profiler.endCpuSample(); } @@ -204,19 +226,37 @@ public class FoliageCellManager { */ private boolean recursivelyUpdateCells(WorldOctTreeNode node, Vector3i absVoxelPos, Map,Boolean> evaluationMap, int minLeafLod, int distCache){ boolean updated = false; + //breakpoint handling + if(this.breakPoints.size() > 0){ + for(Vector3i breakpoint : breakPoints){ + if(GeomUtils.approxMinDistanceAABB(breakpoint, node.getMinBound(), node.getMaxBound()) == 0){ + System.out.println("Break at " + breakpoint + " " + node.getLevel()); + System.out.println(" " + node.getMinBound() + " " + node.getMaxBound()); + System.out.println(" Generated: " + node.getData().hasGenerated()); + System.out.println(" Homogenous: " + node.getData().isHomogenous()); + System.out.println(" Leaf: " + node.isLeaf()); + System.out.println(" Cached min dist: " + node.getData().cachedMinDistance); + System.out.println(" Actual min dist: " + GeomUtils.approxMinDistanceAABB(breakpoint, node.getMinBound(), node.getMaxBound())); + } + } + } if(evaluationMap.containsKey(node)){ return false; } - if(node.getData().hasGenerated() && - ( - node.getData().isHomogenous() || - this.getMinDistance(absVoxelPos, node, distCache) > SIXTEENTH_RES_DIST - ) + if( + node.getData().hasGenerated() && + ( + node.getData().isHomogenous() || + this.getMinDistance(absVoxelPos, node, distCache) > SIXTEENTH_RES_DIST + ) && + distCache != BUST_META_CELLS ){ return false; } if(node.isLeaf()){ - if(this.isMeta(absVoxelPos, node, distCache)){ + if(distCache == BUST_META_CELLS){ + node.getData().setHasGenerated(false); + } if(this.isMeta(absVoxelPos, node, distCache)){ this.flagAsMeta(node); } else if(this.shouldSplit(absVoxelPos, node, distCache)){ Globals.profiler.beginCpuSample("FoliageCellManager.split"); @@ -321,12 +361,12 @@ public class FoliageCellManager { /** * Gets the minimum distance from a node to a point - * @param pos the position to check against + * @param absVoxelPos the position to check against * @param node the node * @return the distance */ - public long getMinDistance(Vector3i worldPos, WorldOctTreeNode node, int distCache){ - return node.getData().getMinDistance(worldPos, node, distCache); + public long getMinDistance(Vector3i absVoxelPos, WorldOctTreeNode node, int distCache){ + return node.getData().getMinDistance(absVoxelPos, node, distCache); } /** @@ -734,7 +774,8 @@ public class FoliageCellManager { * @return true if all cells were successfully requested, false otherwise */ private boolean requestChunks(WorldOctTree.WorldOctTreeNode node){ - Vector3i worldPos = node.getMinBound(); + //min bound is in absolute voxel coordinates, need to convert to world coordinates + Vector3i worldPos = Globals.clientWorldData.convertAbsoluteVoxelToWorldSpace(node.getMinBound()); if( worldPos.x >= 0 && worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() && @@ -742,11 +783,11 @@ public class FoliageCellManager { worldPos.y < Globals.clientWorldData.getWorldDiscreteSize() && worldPos.z >= 0 && worldPos.z < Globals.clientWorldData.getWorldDiscreteSize() && - !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, 0) + !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE) ){ //client should request chunk data from server for each chunk necessary to create the model LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + worldPos); - if(!Globals.clientTerrainManager.requestChunk(worldPos.x, worldPos.y, worldPos.z, 0)){ + if(!Globals.clientTerrainManager.requestChunk(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE)){ return false; } } @@ -756,16 +797,12 @@ public class FoliageCellManager { /** * Checks if all chunk data required to generate this foliage cell is present * @param node The node - * @param highResFace The higher resolution face of a not-full-resolution chunk. Null if the chunk is max resolution or there is no higher resolution face for the current chunk * @return true if all data is available, false otherwise */ private boolean containsDataToGenerate(WorldOctTree.WorldOctTreeNode node){ FoliageCell cell = node.getData(); Vector3i worldPos = cell.getWorldPos(); - if(!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, 0)){ - return false; - } - return true; + return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE); } /** @@ -878,6 +915,14 @@ public class FoliageCellManager { public int getNodeCount(){ return this.chunkTree.getNodeCount(); } + + /** + * Logs when the manager next evaluates the supplied absolute voxel position. Should be used to break at that point. + * @param absVoxelPos The absolute voxel position to break at + */ + public void addBreakPoint(Vector3i absVoxelPos){ + this.breakPoints.add(absVoxelPos); + } } 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 1d7b3d58..92d56b8a 100644 --- a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java +++ b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiChunkMonitor.java @@ -1,6 +1,9 @@ package electrosphere.client.ui.menu.debug; +import org.joml.Vector3i; + import electrosphere.engine.Globals; +import electrosphere.entity.EntityUtils; import electrosphere.renderer.ui.imgui.ImGuiWindow; import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback; import imgui.ImGui; @@ -47,6 +50,12 @@ public class ImGuiChunkMonitor { if(Globals.foliageCellManager != null){ ImGui.text("Foliage node count: " + Globals.foliageCellManager.getNodeCount()); } + if(ImGui.button("Break at chunk")){ + if(Globals.foliageCellManager != null){ + Vector3i absVoxelPos = Globals.clientWorldData.convertRealToAbsoluteVoxelSpace(EntityUtils.getPosition(Globals.playerEntity)); + Globals.foliageCellManager.addBreakPoint(absVoxelPos); + } + } } }); chunkMonitorWindow.setOpen(false); diff --git a/src/main/java/electrosphere/entity/state/movement/fall/ServerFallTree.java b/src/main/java/electrosphere/entity/state/movement/fall/ServerFallTree.java index 6f91ddeb..58a26fc1 100644 --- a/src/main/java/electrosphere/entity/state/movement/fall/ServerFallTree.java +++ b/src/main/java/electrosphere/entity/state/movement/fall/ServerFallTree.java @@ -39,7 +39,7 @@ public class ServerFallTree implements BehaviorTree { /** * The minimum frames to wait before scanning if it should activate due to gravity */ - public static final int MIN_FRAMES_BEFORE_ACTIVATION_SCAN = 10; + public static final int MIN_FRAMES_BEFORE_ACTIVATION_SCAN = 60; /** * The minimum frames to wait before playing landing animation on fall diff --git a/src/main/java/electrosphere/server/terrain/generation/heightmap/HillsGen.java b/src/main/java/electrosphere/server/terrain/generation/heightmap/HillsGen.java index 941676e5..e168b67d 100644 --- a/src/main/java/electrosphere/server/terrain/generation/heightmap/HillsGen.java +++ b/src/main/java/electrosphere/server/terrain/generation/heightmap/HillsGen.java @@ -11,7 +11,7 @@ public class HillsGen implements HeightmapGenerator { /** * Offset from baseline to place the noisemap at */ - static final float HEIGHT_OFFSET = 10; + static final float HEIGHT_OFFSET = 100; /** * Scales the input positions @@ -21,7 +21,7 @@ public class HillsGen implements HeightmapGenerator { /** * Scales the output height */ - static final float VERTICAL_SCALE = 200.0f; + static final float VERTICAL_SCALE = 100.0f; /** * The different scales of noise to sample from diff --git a/src/main/java/electrosphere/util/math/GeomUtils.java b/src/main/java/electrosphere/util/math/GeomUtils.java index 34a516b6..098e7aef 100644 --- a/src/main/java/electrosphere/util/math/GeomUtils.java +++ b/src/main/java/electrosphere/util/math/GeomUtils.java @@ -127,9 +127,9 @@ public class GeomUtils { 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){ + 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); @@ -137,8 +137,8 @@ public class GeomUtils { 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(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,minY,pos.z); @@ -148,8 +148,8 @@ public class GeomUtils { return pos.distance(pos.x,minY,maxZ); } } else { - if(Math.abs(pos.y - minY) > SIMPLIFICATION_CUTOFF){ - return Math.abs(pos.y - minY); + 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,maxY,pos.z); @@ -160,8 +160,8 @@ public class GeomUtils { } } } 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(Math.abs(pos.x - minX) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.x - minX); } if(pos.y > minY && pos.y < maxY){ if(pos.z > minZ && pos.z < maxZ){ @@ -172,8 +172,8 @@ public class GeomUtils { 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(Math.abs(pos.y - minY) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.y - minY); } if(pos.z > minZ && pos.z < maxZ){ return pos.distance(minX,minY,pos.z); @@ -183,8 +183,8 @@ public class GeomUtils { return pos.distance(minX,minY,maxZ); } } else { - if(Math.abs(pos.y - minY) > SIMPLIFICATION_CUTOFF){ - return Math.abs(pos.y - minY); + 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,maxY,pos.z); @@ -195,7 +195,7 @@ public class GeomUtils { } } } else { - if(Math.abs(pos.x - minX) > SIMPLIFICATION_CUTOFF){ + if(Math.abs(pos.x - maxX) > SIMPLIFICATION_CUTOFF){ return Math.abs(pos.x - maxX); } if(pos.y > minY && pos.y < maxY){ @@ -207,8 +207,8 @@ public class GeomUtils { 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(Math.abs(pos.y - minY) > SIMPLIFICATION_CUTOFF){ + return Math.abs(pos.y - minY); } if(pos.z > minZ && pos.z < maxZ){ return pos.distance(maxX,minY,pos.z); @@ -218,8 +218,8 @@ public class GeomUtils { return pos.distance(maxX,minY,maxZ); } } else { - if(Math.abs(pos.y - minY) > SIMPLIFICATION_CUTOFF){ - return Math.abs(pos.y - minY); + 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,maxY,pos.z);