diff --git a/assets/Data/entity/items.json b/assets/Data/entity/items.json index 9f8e7d86..7df0503a 100644 --- a/assets/Data/entity/items.json +++ b/assets/Data/entity/items.json @@ -125,6 +125,12 @@ "path" : "Models/items/weapons/katana1alt.glb" } }, + "boneGroups" : [ + { + "id" : "torso", + "boneNamesThirdPerson" : [] + } + ], "collidable": { "type" : "CUBE", "dimension1" : 0.04, diff --git a/assets/Scripts/server/chunk/generators/testgen.ts b/assets/Scripts/server/chunk/generators/testgen.ts index 89bbac55..f489323a 100644 --- a/assets/Scripts/server/chunk/generators/testgen.ts +++ b/assets/Scripts/server/chunk/generators/testgen.ts @@ -102,16 +102,13 @@ export const TestGen: ChunkGenerator = { engine: Engine ): VoxelFunction => { const rVal = ( + voxel: GeneratedVoxel, worldX: number, worldY: number, worldZ: number, chunkX: number, chunkY: number, chunkZ: number, stride: number, surfaceHeight: number, surfaceBiome: BiomeData - ): GeneratedVoxel => { - let rVal: GeneratedVoxel = { - type: 0, - weight: -1, - } + ): void => { const realX = voxelToReal(chunkX,worldX) const realY = voxelToReal(chunkY,worldY) @@ -122,8 +119,8 @@ export const TestGen: ChunkGenerator = { const surfacePercent = (surfaceHeight - realY) / strideMultiplier if(heightDiff < -strideMultiplier){ - return getSubsurfaceVoxel( - rVal, + getSubsurfaceVoxel( + voxel, worldX, worldY, worldZ, chunkX, chunkY, chunkZ, realX, realY, realZ, @@ -132,8 +129,8 @@ export const TestGen: ChunkGenerator = { surfaceBiome ); } else if(heightDiff > 0) { - return getOverSurfaceVoxel( - rVal, + getOverSurfaceVoxel( + voxel, worldX, worldY, worldZ, chunkX, chunkY, chunkZ, realX, realY, realZ,- @@ -142,8 +139,8 @@ export const TestGen: ChunkGenerator = { surfaceBiome ); } else { - return getSurfaceVoxel( - rVal, + getSurfaceVoxel( + voxel, worldX, worldY, worldZ, chunkX, chunkY, chunkZ, realX, realY, realZ, diff --git a/assets/Scripts/types/host/server/chunk/chunkgenerator.ts b/assets/Scripts/types/host/server/chunk/chunkgenerator.ts index 81753229..8d807db7 100644 --- a/assets/Scripts/types/host/server/chunk/chunkgenerator.ts +++ b/assets/Scripts/types/host/server/chunk/chunkgenerator.ts @@ -25,6 +25,7 @@ export interface GeneratedVoxel { /** * Gets a voxel for a given position +* @param voxel The voxel to fill with values * @param worldX The world x coordinate of the chunk * @param worldY The world y coordinate of the chunk * @param worldZ The world z coordinate of the chunk @@ -38,12 +39,13 @@ export interface GeneratedVoxel { * @returns The voxel at the provided position */ export type VoxelFunction = ( + voxel: GeneratedVoxel, worldX: number, worldY: number, worldZ: number, chunkX: number, chunkY: number, chunkZ: number, stride: number, surfaceHeight: number, surfaceBiome: BiomeData -) => GeneratedVoxel; +) => void; diff --git a/assets/Shaders/entities/terrain2/terrain2.fs b/assets/Shaders/entities/terrain2/terrain2.fs index fc499180..1a6af4c8 100644 --- a/assets/Shaders/entities/terrain2/terrain2.fs +++ b/assets/Shaders/entities/terrain2/terrain2.fs @@ -130,7 +130,7 @@ void main(){ vec3 textureColor = getColor(texPlane1, texPlane2, texPlane3, norm, samplerIndexVec, samplerRatioVec, material); //shadow - float shadow = ShadowCalculation(FragPosLightSpace, normalize(-directLight.direction), norm); + float shadow = ShadowCalculation(FragPosLightSpace, normalize(-directLight.direction), -norm); // //point light calculations @@ -350,5 +350,5 @@ float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal){ // shadow = currentDepth; - return shadow; + return clamp(1.0 - shadow, 0.0, 0.7); } \ No newline at end of file diff --git a/buildNumber.properties b/buildNumber.properties index 38e2d8f5..f5e3fc8f 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Sun Nov 10 17:05:45 EST 2024 -buildNumber=378 +#Wed Nov 13 13:33:32 EST 2024 +buildNumber=381 diff --git a/docs/src/architecture/generation/biomegenerationproblems.md b/docs/src/architecture/generation/biomegenerationproblems.md index 4428118f..f58982a5 100644 --- a/docs/src/architecture/generation/biomegenerationproblems.md +++ b/docs/src/architecture/generation/biomegenerationproblems.md @@ -10,4 +10,13 @@ Different cases to consider when working on a biome generation system - Superstructure biomes (World tree, massive mountains, etc) - Oceans - Lakes - - Rivers \ No newline at end of file + - Rivers + - Large scale overhangs + + +Operations we might want to do + - Placing things on the surface + - Solve navgation at the macro scale + + + diff --git a/docs/src/architecture/generation/terraingenerationprocess.md b/docs/src/architecture/generation/terraingenerationprocess.md index fc7551dc..f4e7ce7c 100644 --- a/docs/src/architecture/generation/terraingenerationprocess.md +++ b/docs/src/architecture/generation/terraingenerationprocess.md @@ -60,3 +60,14 @@ store fire pixels as "good for fire civilization" when a pixel changes biome, must evict it from all these structures and recalculate which ones it should be within (except maybe zone) + + + + + +Solve for the heightmap at a given x-z for the surface +Then, have "cells" placed above the surface based on the base height offset of the biome +These cells can be populated with larger structures + + + diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 29c0374e..9237f530 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1001,6 +1001,18 @@ Passing chunk data between nodes Fix homogenous value propagating from chunk gen to client cache Fix homogenous value joining on client +(11/12/2024) +Work to optimize javascript chunk generators +Solve curve SDF + +(11/13/2024) +Fix shadows on terrain +Work on an anime-style mountain generator +Work on making chunk reloading less obvious +Fix default chunk generator +Fix unit test +Fix missing data on katana item + # TODO diff --git a/net/character.json b/net/character.json index 8c2262eb..96c9395c 100644 --- a/net/character.json +++ b/net/character.json @@ -51,6 +51,11 @@ "data" : [ "data" ] + }, + { + "messageName" : "EditorSwap", + "description" : "Swaps between the editor entity and the non-editor entity", + "data" : [] } ] } diff --git a/pom.xml b/pom.xml index e0081515..ff55397a 100644 --- a/pom.xml +++ b/pom.xml @@ -305,7 +305,7 @@ io.github.studiorailgun MathUtils - 1.1.0 + 1.3.0 diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index be764f03..6187e5c7 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -184,31 +184,6 @@ public class ClientDrawCellManager { if(!updatedLastFrame && !this.initialized){ this.initialized = true; } - // if(!updatedLastFrame){ - // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells"); - // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, HALF_RES_LOD, distCache); - // Globals.profiler.endCpuSample(); - // } - // if(!updatedLastFrame){ - // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells"); - // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, QUARTER_RES_LOD, distCache); - // Globals.profiler.endCpuSample(); - // } - // if(!updatedLastFrame){ - // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - quarter res cells"); - // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, EIGHTH_RES_LOD, distCache); - // Globals.profiler.endCpuSample(); - // } - // if(!updatedLastFrame){ - // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - eighth res cells"); - // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, SIXTEENTH_RES_LOD, distCache); - // Globals.profiler.endCpuSample(); - // } - // if(!updatedLastFrame){ - // Globals.profiler.beginCpuSample("ClientDrawCellManager.update - all res cells"); - // updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, ALL_RES_LOD); - // Globals.profiler.endCpuSample(); - // } } Globals.profiler.endCpuSample(); } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 15ec72fb..d248dc21 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -11,6 +11,7 @@ import electrosphere.collision.CollisionEngine; import electrosphere.engine.Globals; import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.Entity; +import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.types.terrain.TerrainChunk; import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; @@ -23,6 +24,11 @@ import electrosphere.util.math.GeomUtils; */ public class DrawCell { + /** + * Number of frames to wait before destroying the chunk entity + */ + public static final int FRAMES_TO_WAIT_BEFORE_DESTRUCTION = 15; + /** * Enum for the different faces of a draw cell -- used when filling in data for higher LOD faces */ @@ -188,9 +194,19 @@ public class DrawCell { */ public void destroy(){ if(modelEntity != null){ - CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); - collisionEngine.destroyPhysics(modelEntity); - ClientEntityUtils.destroyEntity(modelEntity); + Globals.clientScene.registerBehaviorTree(new BehaviorTree(){ + int framesSimulated = 0; + public void simulate(float deltaTime) { + if(framesSimulated < FRAMES_TO_WAIT_BEFORE_DESTRUCTION){ + framesSimulated++; + } else { + CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); + collisionEngine.destroyPhysics(modelEntity); + ClientEntityUtils.destroyEntity(modelEntity); + Globals.clientScene.deregisterBehaviorTree(this); + } + } + }); } } diff --git a/src/main/java/electrosphere/entity/ClientEntityUtils.java b/src/main/java/electrosphere/entity/ClientEntityUtils.java index 54fc1c38..f1ebf60f 100644 --- a/src/main/java/electrosphere/entity/ClientEntityUtils.java +++ b/src/main/java/electrosphere/entity/ClientEntityUtils.java @@ -57,6 +57,9 @@ public class ClientEntityUtils { if(Globals.clientSceneWrapper != null){ Globals.clientSceneWrapper.getScene().deregisterEntity(entity); Globals.clientSceneWrapper.deregisterTranslationMapping(entity); + if(Globals.clientSceneWrapper.getCollisionEngine() != null){ + Globals.clientSceneWrapper.getCollisionEngine().destroyPhysics(entity); + } } if(Globals.clientScene != null){ Globals.clientScene.deregisterEntity(entity); diff --git a/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java b/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java index ccbfbde0..b2f5e48b 100644 --- a/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java +++ b/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java @@ -11,7 +11,6 @@ import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.btree.BehaviorTree; -import electrosphere.logger.LoggerInterface; import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.server.datacell.utils.DataCellSearchUtils; import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils; @@ -46,7 +45,6 @@ public class ServerPhysicsSyncTree implements BehaviorTree { Quaterniond rotation = EntityUtils.getRotation(parent); DBody body = PhysicsEntityUtils.getDBody(parent); if(body == null){ - LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Running physics sync tree on entity that does not have a physics body!")); } else { //velocities Vector3d linearVel = PhysicsUtils.odeVecToJomlVec(body.getLinearVel()); diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java index e40b0520..8839a2e2 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java @@ -63,7 +63,7 @@ public class TerrainChunk { } rVal.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true); } else { - LoggerInterface.loggerEngine.WARNING("Finished generating terrain polygons; however, entity has already been deleted."); + LoggerInterface.loggerEngine.DEBUG("Finished generating terrain polygons; however, entity has already been deleted."); } } catch (Error e){ LoggerInterface.loggerEngine.ERROR(e); diff --git a/src/main/java/electrosphere/renderer/actor/Actor.java b/src/main/java/electrosphere/renderer/actor/Actor.java index 87140163..93ff80e7 100644 --- a/src/main/java/electrosphere/renderer/actor/Actor.java +++ b/src/main/java/electrosphere/renderer/actor/Actor.java @@ -251,7 +251,11 @@ public class Actor { } } } else if(this.boneGroups == null){ - LoggerInterface.loggerRenderer.WARNING("Trying to play animation on pose actor that uses bone groups, but the actor's bone group isn't defined!"); + LoggerInterface.loggerRenderer.WARNING( + "Trying to play animation on Actor that uses bone groups, but the Actor's bone group isn't defined!\n" + + "Model path: " + modelPath + "\n" + + "Animation name: " + animationName + "\n" + ); } diff --git a/src/main/java/electrosphere/renderer/pipelines/ShadowMapPipeline.java b/src/main/java/electrosphere/renderer/pipelines/ShadowMapPipeline.java index faab8a98..6653d49a 100644 --- a/src/main/java/electrosphere/renderer/pipelines/ShadowMapPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/ShadowMapPipeline.java @@ -31,17 +31,22 @@ public class ShadowMapPipeline implements RenderPipeline { /** * The eye of the camera that is used to render the shadow map */ - Vector3d cameraEye = new Vector3d(-1,10,-5.5); + Vector3d cameraEye = new Vector3d(-1,20,-5.5); /** * The near plane distance */ - float nearPlane = 0.1f; + float nearPlane = 1f; /** * The far plane */ - float farPlane = 25f; + float farPlane = 40f; + + /** + * Sides of the orthagonal box + */ + float sideLength = 50f; /** * Sets whether the far plane should update based on camera location or not @@ -68,9 +73,10 @@ public class ShadowMapPipeline implements RenderPipeline { float eyeX = (float)cameraEye.x; float eyeY = (float)cameraEye.y; float eyeZ = (float)cameraEye.z; - float eyeDist = (float)cameraEye.length(); + // float eyeDist = (float)cameraEye.length(); // float farPlane = eyeDist + 10.0f; - float sidesMagnitude = (float)Math.sqrt(eyeDist); + // float sidesMagnitude = (float)Math.sqrt(eyeDist); + float sidesMagnitude = sideLength; //set matrices for light render Matrix4f lightProjection = new Matrix4f().setOrtho(-sidesMagnitude, sidesMagnitude, -sidesMagnitude, sidesMagnitude, nearPlane, farPlane);//glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); Matrix4f lightView = new Matrix4f().setLookAt( diff --git a/src/main/java/electrosphere/server/content/ServerContentGenerator.java b/src/main/java/electrosphere/server/content/ServerContentGenerator.java index df7bdbcf..934d8bff 100644 --- a/src/main/java/electrosphere/server/content/ServerContentGenerator.java +++ b/src/main/java/electrosphere/server/content/ServerContentGenerator.java @@ -48,11 +48,11 @@ public class ServerContentGenerator { ); } - //setup - Random random = new Random(randomizer); + // //setup + // Random random = new Random(randomizer); - //generate foliage + // //generate foliage // BiomeData biome = null; // if(realm.getServerWorldData() != null && realm.getServerWorldData().getServerTerrainManager() != null && realm.getServerWorldData().getServerTerrainManager().getModel() != null){ // biome = realm.getServerWorldData().getServerTerrainManager().getModel().getSurfaceBiome(worldPos.x, worldPos.z); diff --git a/src/main/java/electrosphere/server/poseactor/PoseActor.java b/src/main/java/electrosphere/server/poseactor/PoseActor.java index 24a8c9ae..fc85a8b0 100644 --- a/src/main/java/electrosphere/server/poseactor/PoseActor.java +++ b/src/main/java/electrosphere/server/poseactor/PoseActor.java @@ -224,7 +224,11 @@ public class PoseActor { } } } else if(this.boneGroups == null){ - LoggerInterface.loggerRenderer.WARNING("Trying to play animation on pose actor that uses bone groups, but the actor's bone group isn't defined!"); + LoggerInterface.loggerRenderer.WARNING( + "Trying to play animation on PoseActor that uses bone groups, but the PoseActor's bone group isn't defined!\n" + + "Model path: " + modelPath + "\n" + + "Animation name: " + animationName + "\n" + ); } diff --git a/src/main/java/electrosphere/server/terrain/generation/DefaultChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/DefaultChunkGenerator.java index 84f1abbd..fc821acf 100644 --- a/src/main/java/electrosphere/server/terrain/generation/DefaultChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/DefaultChunkGenerator.java @@ -26,19 +26,19 @@ public class DefaultChunkGenerator implements ChunkGenerator { public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) { //Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap. //Hence, width should actually be chunk dimension + 1 - float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - int[][][] values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; - for(int inc = 0; inc < ServerTerrainChunk.CHUNK_DIMENSION; inc++){ - for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DIMENSION; weightX++){ - for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DIMENSION; weightZ++){ + float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + for(int inc = 0; inc < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; inc++){ + for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){ + for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){ weights[weightX][inc][weightZ] = -1; values[weightX][inc][weightZ] = 0; } } } if(worldY < 1){ - for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DIMENSION; weightX++){ - for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DIMENSION; weightZ++){ + for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){ + for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){ weights[weightX][0][weightZ] = 0.1f; values[weightX][0][weightZ] = baseVoxelId; } diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index a781391f..76834c39 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -5,7 +5,7 @@ import java.util.Map; import electrosphere.client.terrain.cache.ChunkData; -// import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.Value; import electrosphere.engine.Globals; import electrosphere.game.data.biome.BiomeData; @@ -17,6 +17,10 @@ import electrosphere.server.terrain.generation.heightmap.HillsGen; import electrosphere.server.terrain.generation.heightmap.PlainsGen; import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel; +import electrosphere.server.terrain.generation.interfaces.GenerationContext; +import electrosphere.server.terrain.generation.voxelphase.AnimeMountainsGen; +import electrosphere.server.terrain.generation.voxelphase.HillsVoxelGen; +import electrosphere.server.terrain.generation.voxelphase.VoxelGenerator; import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.models.TerrainModel; @@ -61,9 +65,14 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { ServerWorldData serverWorldData; /** - * Map of generator tag to the generator + * The map of generator tag to the heightmap generator */ - Map tagGeneratorMap = new HashMap(); + Map tagHeightmapMap = new HashMap(); + + /** + * The map of generator tag to voxel generator + */ + Map tagVoxelMap = new HashMap(); /** * Tracks whether to use javascript generation or not @@ -75,9 +84,11 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { */ public TestGenerationChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){ this.serverWorldData = serverWorldData; - registerGenerator(new EmptySkyGen()); - registerGenerator(new HillsGen()); - registerGenerator(new PlainsGen()); + registerHeightmapGenerator(new EmptySkyGen()); + registerHeightmapGenerator(new HillsGen()); + registerHeightmapGenerator(new PlainsGen()); + registerVoxelGenerator(new HillsVoxelGen()); + registerVoxelGenerator(new AnimeMountainsGen()); this.useJavascript = useJavascript; } @@ -85,27 +96,31 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { * Registers a heightmap generator * @param generator The heightmap generator */ - private void registerGenerator(HeightmapGenerator generator){ - tagGeneratorMap.put(generator.getTag(),generator); + private void registerHeightmapGenerator(HeightmapGenerator generator){ + tagHeightmapMap.put(generator.getTag(),generator); + } + + /** + * Registers a voxel generator + * @param generator The voxel generator + */ + private void registerVoxelGenerator(VoxelGenerator generator){ + tagVoxelMap.put(generator.getTag(),generator); } @Override public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) { Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.generateChunk"); - ServerTerrainChunk rVal = null; + ServerTerrainChunk rVal = new ServerTerrainChunk(worldX, worldY, worldZ); float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];; int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; - int firstType = -2; - boolean homogenous = true; try { - //actual generation algo - //biome of the current chunk BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ); BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); - HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); + HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag()); if(heightmapGen == null){ throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); } @@ -129,39 +144,58 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { } } + VoxelGenerator voxelGenerator = this.tagVoxelMap.get("hills"); + if(this.useJavascript){ - // Globals.scriptEngine.executeSynchronously(() -> { - // Value getVoxelFunc = Globals.scriptEngine.invokeEngineMember("chunkGeneratorManager", "getVoxelFunction", SCRIPT_GEN_TEST_TAG); - // for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ - // Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); - // for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ - // for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - // int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - // int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - // int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); - // int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - // int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - // int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - // Value result = getVoxelFunc.execute( - // finalWorldX, finalWorldY, finalWorldZ, - // finalChunkX, finalChunkY, finalChunkZ, - // stride, - // heightfield[x][z], - // surfaceBiome - // ); - // if(result != null){ - // weights[x][y][z] = result.getMember("weight").asFloat(); - // values[x][y][z] = result.getMember("type").asInt(); - // } - // } - // } - // Globals.profiler.endCpuSample(); - // } - // }); + Globals.scriptEngine.executeSynchronously(() -> { + int firstType = -2; + boolean homogenous = true; + GeneratedVoxel voxel = new GeneratedVoxel(); + Value getVoxelFunc = Globals.scriptEngine.invokeEngineMember("chunkGeneratorManager", "getVoxelFunction", SCRIPT_GEN_TEST_TAG); + for(int x = 0; x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; x++){ + Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); + for(int y = 0; y < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; y++){ + for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){ + int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); + int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; + getVoxelFunc.execute( + voxel, + finalWorldX, finalWorldY, finalWorldZ, + finalChunkX, finalChunkY, finalChunkZ, + stride, + heightfield[x][z], + surfaceBiome + ); + weights[x][y][z] = voxel.weight; + values[x][y][z] = voxel.type; + if(firstType == -2){ + firstType = values[x][y][z]; + } else if(homogenous && firstType != values[x][y][z]){ + homogenous = false; + } + } + } + Globals.profiler.endCpuSample(); + } + if(homogenous){ + rVal.setHomogenousValue(firstType); + } else { + rVal.setHomogenousValue(ChunkData.NOT_HOMOGENOUS); + } + rVal.setWeights(weights); + rVal.setValues(values); + }); } else { + int firstType = -2; + boolean homogenous = true; GeneratedVoxel voxel = new GeneratedVoxel(); + GenerationContext generationContext = new GenerationContext(); + generationContext.setServerWorldData(serverWorldData); for(int x = 0; x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; x++){ - Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); for(int y = 0; y < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; y++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){ int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); @@ -170,13 +204,14 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; - this.getVoxel( + voxelGenerator.getVoxel( voxel, finalWorldX, finalWorldY, finalWorldZ, finalChunkX, finalChunkY, finalChunkZ, stride, heightfield[x][z], - surfaceBiome + surfaceBiome, + generationContext ); if(voxel != null){ weights[x][y][z] = voxel.weight; @@ -189,13 +224,18 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { } } } - Globals.profiler.endCpuSample(); } + if(homogenous){ + rVal.setHomogenousValue(firstType); + } else { + rVal.setHomogenousValue(ChunkData.NOT_HOMOGENOUS); + } + rVal.setWeights(weights); + rVal.setValues(values); } } catch(Exception ex){ ex.printStackTrace(); } - rVal = new ServerTerrainChunk(worldX, worldY, worldZ, homogenous ? firstType : ChunkData.NOT_HOMOGENOUS, weights, values); Globals.profiler.endCpuSample(); return rVal; } @@ -205,7 +245,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ); BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); - HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); + HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag()); if(heightmapGen == null){ throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); } @@ -222,140 +262,4 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { this.terrainModel = model; } - /** - * Gets the value for a chunk - * @param voxel The voxel to fill - * @param worldX The world x pos - * @param worldY The world y pos - * @param worldZ The world z pos - * @param chunkX The chunk x pos - * @param chunkY The chunk y pos - * @param chunkZ The chunk z pos - * @param stride The stride of the data - * @param surfaceHeight The height of the surface at x,z - * @param surfaceBiome The surface biome of the chunk - */ - private void getVoxel( - GeneratedVoxel voxel, - int worldX, int worldY, int worldZ, - int chunkX, int chunkY, int chunkZ, - int stride, - float surfaceHeight, - BiomeData surfaceBiome - ){ - Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.getChunkValue"); - BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); - HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); - if(heightmapGen == null){ - throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); - } - - double realX = this.serverWorldData.convertVoxelToRealSpace(chunkX,worldX); - double realY = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldY); - double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkZ,worldZ); - - double strideMultiplier = Math.pow(2,stride); - double heightDiff = realY - surfaceHeight; - double surfacePercent = TestGenerationChunkGenerator.getSurfaceWeight(surfaceHeight,realY,strideMultiplier); - Globals.profiler.endCpuSample(); - if(heightDiff < -strideMultiplier * SURFACE_VOXEL_WIDTH){ - getSubsurfaceVoxel( - voxel, - worldX, worldY, worldZ, - chunkX, chunkY, chunkZ, - realX, realY, realZ, - surfacePercent, - surfaceHeight, - surfaceBiome - ); - } else if(heightDiff > 0) { - getOverSurfaceVoxel( - voxel, - worldX, worldY, worldZ, - chunkX, chunkY, chunkZ, - realX, realY, realZ,- - surfacePercent, - surfaceHeight, - surfaceBiome - ); - } else { - getSurfaceVoxel( - voxel, - worldX, worldY, worldZ, - chunkX, chunkY, chunkZ, - realX, realY, realZ, - surfacePercent, - surfaceHeight, - surfaceBiome - ); - } - } - - /** - * Gets the voxel on the surface - * @return The voxel - */ - private void getSurfaceVoxel( - GeneratedVoxel voxel, - int worldX, int worldY, int worldZ, - int chunkX, int chunkY, int chunkZ, - double realX, double realY, double realZ, - double surfacePercent, - float surfaceHeight, - BiomeData surfaceBiome - ){ - voxel.weight = (float)surfacePercent * 2 - 1; - voxel.type = 2; - } - - /** - * Gets the voxel below the surface - * @return The voxel - */ - private void getSubsurfaceVoxel( - GeneratedVoxel voxel, - int worldX, int worldY, int worldZ, - int chunkX, int chunkY, int chunkZ, - double realX, double realY, double realZ, - double surfacePercent, - float surfaceHeight, - BiomeData surfaceBiome - ){ - if(realY < surfaceHeight - 5){ - voxel.weight = 1; - voxel.type = 6; - } else { - voxel.weight = 1; - voxel.type = 1; - } - } - - /** - * Gets the voxel above the service - * @return The voxel - */ - private void getOverSurfaceVoxel( - GeneratedVoxel voxel, - int worldX, int worldY, int worldZ, - int chunkX, int chunkY, int chunkZ, - double realX, double realY, double realZ, - double surfacePercent, - float surfaceHeight, - BiomeData surfaceBiome - ){ - voxel.weight = -1; - voxel.type = 0; - } - - /** - * Calculates the weight of a voxel on the surface based on the surface height, the position of the voxel, and the stride multiplier - * @param surfaceHeight The surface height - * @param realPosY The position of the voxel - * @param strideMultiplier The stride multiplier - * @return The weight of the voxel - */ - protected static double getSurfaceWeight(double surfaceHeight, double realPosY, double strideMultiplier){ - return ((surfaceHeight - realPosY) / strideMultiplier); - } - } 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 3cb97bf8..941676e5 100644 --- a/src/main/java/electrosphere/server/terrain/generation/heightmap/HillsGen.java +++ b/src/main/java/electrosphere/server/terrain/generation/heightmap/HillsGen.java @@ -52,7 +52,7 @@ public class HillsGen implements HeightmapGenerator { Globals.profiler.beginAggregateCpuSample("HillsGen.getHeight"); double scaledX = x * POSITION_SCALE; double scaledY = y * POSITION_SCALE; - float rVal = gradientHeight(SEED, scaledX, scaledY) * VERTICAL_SCALE + HEIGHT_OFFSET; + float rVal = HillsGen.gradientHeight(SEED, scaledX, scaledY) * VERTICAL_SCALE + HEIGHT_OFFSET; Globals.profiler.endCpuSample(); return rVal; } diff --git a/src/main/java/electrosphere/server/terrain/generation/heightmap/SeaFloorGen.java b/src/main/java/electrosphere/server/terrain/generation/heightmap/SeaFloorGen.java new file mode 100644 index 00000000..0e9e863e --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/heightmap/SeaFloorGen.java @@ -0,0 +1,26 @@ +package electrosphere.server.terrain.generation.heightmap; + +import electrosphere.util.noise.OpenSimplex2S; + +/** + * Generates a mild plain that would look like a seafloor + */ +public class SeaFloorGen implements HeightmapGenerator { + + /** + * The scale of the noise + */ + float noiseScale = 0.01f; + + @Override + public float getHeight(long SEED, double x, double y) { + float noise = (float)OpenSimplex2S.noise2_ImproveX(SEED, x * noiseScale, y * noiseScale); + return noise; + } + + @Override + public String getTag() { + return "seafloor"; + } + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/interfaces/GenerationContext.java b/src/main/java/electrosphere/server/terrain/generation/interfaces/GenerationContext.java new file mode 100644 index 00000000..7492193c --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/interfaces/GenerationContext.java @@ -0,0 +1,37 @@ +package electrosphere.server.terrain.generation.interfaces; + +import electrosphere.game.server.world.ServerWorldData; + +/** + * The context the generation is happening in. Stores things like biomes to interpolate between. + */ +public class GenerationContext { + + /** + * The world data for the realm we're generating for + */ + ServerWorldData serverWorldData; + + /** + * Constructor + */ + public GenerationContext(){ + } + + /** + * Gets the world data for the server + * @return The world data + */ + public ServerWorldData getServerWorldData(){ + return serverWorldData; + } + + /** + * Sets the world data for the context + * @param serverWorldData The world data + */ + public void setServerWorldData(ServerWorldData serverWorldData){ + this.serverWorldData = serverWorldData; + } + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/voxelphase/AnimeMountainsGen.java b/src/main/java/electrosphere/server/terrain/generation/voxelphase/AnimeMountainsGen.java new file mode 100644 index 00000000..fb29ddd2 --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/voxelphase/AnimeMountainsGen.java @@ -0,0 +1,259 @@ +package electrosphere.server.terrain.generation.voxelphase; + +import electrosphere.engine.Globals; +import electrosphere.game.data.biome.BiomeData; +import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel; +import electrosphere.server.terrain.generation.interfaces.GenerationContext; +import io.github.studiorailgun.MathUtils; +import io.github.studiorailgun.RandUtils; + +/** + * Generates anime-style mountains + */ +public class AnimeMountainsGen implements VoxelGenerator { + + /** + * The width of the surface in number of voxels + */ + public static final int SURFACE_VOXEL_WIDTH = 2; + + /** + * Size of the large cells that generate mountains + */ + public static final int LARGE_CELL_SIZE = 1024; + + /** + * How much to sink the cell into the surface + */ + public static final int CELL_VERTICAL_OFFSET = - (int)((LARGE_CELL_SIZE) * 2.5 / 5.0); + + /** + * The variance in scale of mountains + */ + public static final double MOUNTAIN_SCALE_VARIANCE = 0.2; + + /** + * The width of the mountain + */ + public static final double MOUNTAIN_WIDTH = 0.4; + + /** + * The center x point of the cell + */ + public static final double CELL_CENTER_X = 0.5; + + /** + * The center y point of the cell + */ + public static final double CELL_CENTER_Y = 0.5; + + /** + * The center z point of the cell + */ + public static final double CELL_CENTER_Z = 0.5; + + /** + * Amount the vertical rotation offset can vary + */ + public static final double VERTICAL_ROTATION_OFFSET_VARIANCE = 0.2; + + @Override + public String getTag() { + return "animeMountain"; + } + + @Override + public void getVoxel( + GeneratedVoxel voxel, + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + int stride, float surfaceHeight, + BiomeData surfaceBiome, + GenerationContext generationContext + ) { + Globals.profiler.beginAggregateCpuSample("AnimeMountainsGen.getVoxel"); + + double realX = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkX,worldX); + double realY = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkY,worldY); + double realZ = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkZ,worldZ); + + double strideMultiplier = Math.pow(2,stride); + double heightDiff = realY - surfaceHeight; + double surfacePercent = AnimeMountainsGen.getSurfaceWeight(surfaceHeight,realY,strideMultiplier); + Globals.profiler.endCpuSample(); + if(heightDiff < -strideMultiplier * SURFACE_VOXEL_WIDTH){ + this.getSubsurfaceVoxel( + voxel, + worldX, worldY, worldZ, + chunkX, chunkY, chunkZ, + realX, realY, realZ, + surfacePercent, + surfaceHeight, + surfaceBiome + ); + } else if(heightDiff > 0) { + this.getOverSurfaceVoxel( + voxel, + worldX, worldY, worldZ, + chunkX, chunkY, chunkZ, + realX, realY, realZ,- + surfacePercent, + surfaceHeight, + surfaceBiome + ); + } else { + this.getSurfaceVoxel( + voxel, + worldX, worldY, worldZ, + chunkX, chunkY, chunkZ, + realX, realY, realZ, + surfacePercent, + surfaceHeight, + surfaceBiome + ); + } + } + + + /** + * Gets the voxel on the surface + * @return The voxel + */ + private void getSurfaceVoxel( + GeneratedVoxel voxel, + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + double realX, double realY, double realZ, + double surfacePercent, + float surfaceHeight, + BiomeData surfaceBiome + ){ + voxel.weight = (float)surfacePercent * 2 - 1; + voxel.type = 2; + } + + /** + * Gets the voxel below the surface + * @return The voxel + */ + private void getSubsurfaceVoxel( + GeneratedVoxel voxel, + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + double realX, double realY, double realZ, + double surfacePercent, + float surfaceHeight, + BiomeData surfaceBiome + ){ + if(realY < surfaceHeight - 5){ + voxel.weight = 1; + voxel.type = 6; + } else { + voxel.weight = 1; + voxel.type = 1; + } + } + + /** + * Gets the voxel above the service + * @return The voxel + */ + private void getOverSurfaceVoxel( + GeneratedVoxel voxel, + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + double realX, double realY, double realZ, + double surfacePercent, + float surfaceHeight, + BiomeData surfaceBiome + ){ + //default voxel value + voxel.weight = -1; + voxel.type = 0; + + //get the height above the surface + double heightAboveBaseSurface = realY - surfaceBiome.getSurfaceGenerationParams().getHeightOffset(); + double offsetHeight = heightAboveBaseSurface - CELL_VERTICAL_OFFSET; + + //calculated floored values + double x_i = Math.floor(realX / LARGE_CELL_SIZE); + double y_i = Math.floor(offsetHeight / LARGE_CELL_SIZE); + double z_i = Math.floor(realZ / LARGE_CELL_SIZE); + + //only generate if you're on the first cell from the surface + if(y_i < 1){ + //calculate values from which cell we're in + double rotationTheta = RandUtils.rand(x_i, z_i, 0) * Math.PI * 2; + // double mountainScale = MathUtils.rand(x_i, z_i, 1) * MOUNTAIN_SCALE_VARIANCE + (1.0 - (MOUNTAIN_SCALE_VARIANCE / 2.0)); + // double verticalRotationOffset = MathUtils.rand(x_i, z_i, 2) * VERTICAL_ROTATION_OFFSET_VARIANCE; + + //remainders of the point coordinates + double x_r = (realX / LARGE_CELL_SIZE) - x_i; + double y_r = (offsetHeight / LARGE_CELL_SIZE) - y_i; + double z_r = (realZ / LARGE_CELL_SIZE) - z_i; + + //get the center of the cell + double cellCenterX = CELL_CENTER_X; + double cellCenterY = CELL_CENTER_Y; + double cellCenterZ = CELL_CENTER_Z; + + //delta positions + double deltaX = (x_r - cellCenterX); + double deltaY = (y_r - cellCenterY); + double deltaZ = (z_r - cellCenterZ); + + //rotate around the center + double rotX = deltaX * Math.cos(rotationTheta) + deltaZ * Math.sin(rotationTheta); + double rotY = deltaY; + double rotZ = - deltaX * Math.sin(rotationTheta) + deltaZ * Math.cos(rotationTheta); + + //roation along the arctre + //ranged [0,2PI] + double rotationAlongArc = Math.atan2(rotY,rotX); + //ranges [0,1] + // double rotationPercent = rotationAlongArc / (Math.PI * 2.0); + + + //calculate values from where we are WITHIN the cell + // double voxelGamma = Math.atan2( + // x_r - cellCenterX, + // z_r - cellCenterZ + // ) + Math.PI * 2.0; + // double voxelTheta = Math.atan2(y_r - cellCenterY, MathUtils.dist(x_r,z_r,cellCenterX,cellCenterZ)); + double distanceFromCenter = MathUtils.dist(rotX,rotY,0,0); + double distanceFromArcCenter = Math.sqrt(rotZ*rotZ); + + //target distance from center + double targetDistanceFromArcCenter = Math.sin(rotationAlongArc) * 0.3; + double targetArcRadius = 0.6; + double targetArcRadiusWidth = Math.sin(rotationAlongArc) * 0.03; + + + if( + // voxelTheta > 0 && + // (voxelGamma - Math.PI/2) < Math.PI && + // voxelTheta > Math.max(Math.sin(voxelGamma),0) && + // voxelTheta > 0.4 * Math.max(Math.sin(voxelGamma),0) && + distanceFromCenter > targetArcRadius - targetArcRadiusWidth && + distanceFromCenter < targetArcRadius + targetArcRadiusWidth && + distanceFromArcCenter < targetDistanceFromArcCenter + ){ + voxel.weight = 1.0f; + voxel.type = 1; + } + } + + } + + /** + * Calculates the weight of a voxel on the surface based on the surface height, the position of the voxel, and the stride multiplier + * @param surfaceHeight The surface height + * @param realPosY The position of the voxel + * @param strideMultiplier The stride multiplier + * @return The weight of the voxel + */ + private static double getSurfaceWeight(double surfaceHeight, double realPosY, double strideMultiplier){ + return ((surfaceHeight - realPosY) / strideMultiplier); + } + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGen.java b/src/main/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGen.java new file mode 100644 index 00000000..c6d9b0a3 --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGen.java @@ -0,0 +1,146 @@ +package electrosphere.server.terrain.generation.voxelphase; + +import electrosphere.engine.Globals; +import electrosphere.game.data.biome.BiomeData; +import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel; +import electrosphere.server.terrain.generation.interfaces.GenerationContext; + +/** + * Generates voxels for a hill + */ +public class HillsVoxelGen implements VoxelGenerator { + + /** + * The width of the surface in number of voxels + */ + public static final int SURFACE_VOXEL_WIDTH = 2; + + + @Override + public String getTag(){ + return "hills"; + } + + @Override + public void getVoxel( + GeneratedVoxel voxel, + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + int stride, + float surfaceHeight, + BiomeData surfaceBiome, + GenerationContext generationContext + ){ + Globals.profiler.beginAggregateCpuSample("HillsVoxelGen.getVoxel"); + + double realX = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkX,worldX); + double realY = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkY,worldY); + double realZ = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkZ,worldZ); + + double strideMultiplier = Math.pow(2,stride); + double heightDiff = realY - surfaceHeight; + double surfacePercent = HillsVoxelGen.getSurfaceWeight(surfaceHeight,realY,strideMultiplier); + Globals.profiler.endCpuSample(); + if(heightDiff < -strideMultiplier * SURFACE_VOXEL_WIDTH){ + this.getSubsurfaceVoxel( + voxel, + worldX, worldY, worldZ, + chunkX, chunkY, chunkZ, + realX, realY, realZ, + surfacePercent, + surfaceHeight, + surfaceBiome + ); + } else if(heightDiff > 0) { + this.getOverSurfaceVoxel( + voxel, + worldX, worldY, worldZ, + chunkX, chunkY, chunkZ, + realX, realY, realZ,- + surfacePercent, + surfaceHeight, + surfaceBiome + ); + } else { + this.getSurfaceVoxel( + voxel, + worldX, worldY, worldZ, + chunkX, chunkY, chunkZ, + realX, realY, realZ, + surfacePercent, + surfaceHeight, + surfaceBiome + ); + } + } + + /** + * Gets the voxel on the surface + * @return The voxel + */ + private void getSurfaceVoxel( + GeneratedVoxel voxel, + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + double realX, double realY, double realZ, + double surfacePercent, + float surfaceHeight, + BiomeData surfaceBiome + ){ + voxel.weight = (float)surfacePercent * 2 - 1; + voxel.type = 2; + } + + /** + * Gets the voxel below the surface + * @return The voxel + */ + private void getSubsurfaceVoxel( + GeneratedVoxel voxel, + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + double realX, double realY, double realZ, + double surfacePercent, + float surfaceHeight, + BiomeData surfaceBiome + ){ + if(realY < surfaceHeight - 5){ + voxel.weight = 1; + voxel.type = 6; + } else { + voxel.weight = 1; + voxel.type = 1; + } + } + + /** + * Gets the voxel above the service + * @return The voxel + */ + private void getOverSurfaceVoxel( + GeneratedVoxel voxel, + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + double realX, double realY, double realZ, + double surfacePercent, + float surfaceHeight, + BiomeData surfaceBiome + ){ + voxel.weight = -1; + voxel.type = 0; + } + + /** + * Calculates the weight of a voxel on the surface based on the surface height, the position of the voxel, and the stride multiplier + * @param surfaceHeight The surface height + * @param realPosY The position of the voxel + * @param strideMultiplier The stride multiplier + * @return The weight of the voxel + */ + protected static double getSurfaceWeight(double surfaceHeight, double realPosY, double strideMultiplier){ + return ((surfaceHeight - realPosY) / strideMultiplier); + } + + + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/voxelphase/VoxelGenerator.java b/src/main/java/electrosphere/server/terrain/generation/voxelphase/VoxelGenerator.java new file mode 100644 index 00000000..c0030c5b --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/voxelphase/VoxelGenerator.java @@ -0,0 +1,45 @@ +package electrosphere.server.terrain.generation.voxelphase; + +import electrosphere.game.data.biome.BiomeData; +import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel; +import electrosphere.server.terrain.generation.interfaces.GenerationContext; + + +/** + * Used for generating voxels + */ +public interface VoxelGenerator { + + + /** + * Gets the tag of the generator + * @return The tag + */ + public String getTag(); + + + /** + * Gets the value for a chunk + * @param voxel The voxel to fill + * @param worldX The world x pos + * @param worldY The world y pos + * @param worldZ The world z pos + * @param chunkX The chunk x pos + * @param chunkY The chunk y pos + * @param chunkZ The chunk z pos + * @param stride The stride of the data + * @param surfaceHeight The height of the surface at x,z + * @param surfaceBiome The surface biome of the chunk + * @param generationContext The generation context + */ + public void getVoxel( + GeneratedVoxel voxel, + int worldX, int worldY, int worldZ, + int chunkX, int chunkY, int chunkZ, + int stride, + float surfaceHeight, + BiomeData surfaceBiome, + GenerationContext generationContext + ); + +} diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java index d64d0ce0..88ca4a94 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java @@ -80,6 +80,18 @@ public class ServerTerrainChunk { this.values = values; } + /** + * Constructor + * @param worldX The world position x coordinate + * @param worldY The world position y coordinate + * @param worldZ The world position z coordinate + */ + public ServerTerrainChunk(int worldX, int worldY, int worldZ){ + this.worldX = worldX; + this.worldY = worldY; + this.worldZ = worldZ; + } + /** * Gets the world position x coordinate * @return The x coordinate @@ -201,5 +213,31 @@ public class ServerTerrainChunk { public int getHomogenousValue(){ return homogenousValue; } + + /** + * Sets the weights of the chunk + * @param weights The weights + */ + public void setWeights(float[][][] weights) { + this.weights = weights; + } + + /** + * Sets the values of the chunk + * @param values The values + */ + public void setValues(int[][][] values) { + this.values = values; + } + + /** + * Sets the homogenous value + * @param homogenousValue The homogenous value + */ + public void setHomogenousValue(int homogenousValue) { + this.homogenousValue = homogenousValue; + } + + } diff --git a/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java b/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java index edc68deb..942ba843 100644 --- a/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java +++ b/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java @@ -40,9 +40,11 @@ public class ClientDrawCellManagerTests { Vector3i playerPos = new Vector3i(0,0,0); WorldOctTreeNode node = WorldOctTreeNode.constructorForTests(manager.chunkTree, 1, new Vector3i(16,0,0), new Vector3i(32,16,16)); node.setLeaf(true); - node.setData(DrawCell.generateTerrainCell(new Vector3i(0,0,0),3)); + node.setData(DrawCell.generateTerrainCell(new Vector3i(16,0,0),3)); - assertTrue(manager.shouldSplit(playerPos, node, 0)); + boolean shouldBeTrue = node.getParent() != null; + + assertEquals(shouldBeTrue,manager.shouldSplit(playerPos, node, 0)); //cleanup Main.shutdown(); diff --git a/src/test/java/electrosphere/server/terrain/generation/TestGenerationChunkGeneratorTests.java b/src/test/java/electrosphere/server/terrain/generation/TestGenerationChunkGeneratorTests.java deleted file mode 100644 index e0e5e542..00000000 --- a/src/test/java/electrosphere/server/terrain/generation/TestGenerationChunkGeneratorTests.java +++ /dev/null @@ -1,22 +0,0 @@ -package electrosphere.server.terrain.generation; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import electrosphere.test.annotations.FastTest; -import electrosphere.test.annotations.UnitTest; - -/** - * Unit tests for the test generation chunk generator - */ -public class TestGenerationChunkGeneratorTests { - - @UnitTest - @FastTest - public void getSurfaceWeight_ValueTests(){ - assertEquals(0.5,TestGenerationChunkGenerator.getSurfaceWeight(0.5, 0, 1)); - assertEquals(0.1,TestGenerationChunkGenerator.getSurfaceWeight(0.1, 0, 1)); - assertEquals(0.9,TestGenerationChunkGenerator.getSurfaceWeight(0.9, 0, 1)); - assertEquals(0.95,TestGenerationChunkGenerator.getSurfaceWeight(1.9, 0, 2)); - } - -} diff --git a/src/test/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGenTests.java b/src/test/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGenTests.java new file mode 100644 index 00000000..62ec489a --- /dev/null +++ b/src/test/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGenTests.java @@ -0,0 +1,22 @@ +package electrosphere.server.terrain.generation.voxelphase; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import electrosphere.test.annotations.FastTest; +import electrosphere.test.annotations.UnitTest; + +/** + * Unit tests for the test generation chunk generator + */ +public class HillsVoxelGenTests { + + @UnitTest + @FastTest + public void getSurfaceWeight_ValueTests(){ + assertEquals(0.5,HillsVoxelGen.getSurfaceWeight(0.5, 0, 1)); + assertEquals(0.1,HillsVoxelGen.getSurfaceWeight(0.1, 0, 1)); + assertEquals(0.9,HillsVoxelGen.getSurfaceWeight(0.9, 0, 1)); + assertEquals(0.95,HillsVoxelGen.getSurfaceWeight(1.9, 0, 2)); + } + +}