diff --git a/assets/Data/game/voxel/test1.json b/assets/Data/game/voxel/test1.json
new file mode 100644
index 00000000..92161717
--- /dev/null
+++ b/assets/Data/game/voxel/test1.json
@@ -0,0 +1,7 @@
+{
+ "name" : "test1",
+ "sampler" : {
+ "name" : "Invoke",
+ "target" : "test2"
+ }
+}
\ No newline at end of file
diff --git a/assets/Data/game/voxel/test2.json b/assets/Data/game/voxel/test2.json
new file mode 100644
index 00000000..b3af64ac
--- /dev/null
+++ b/assets/Data/game/voxel/test2.json
@@ -0,0 +1,36 @@
+{
+ "name" : "test2",
+ "sampler" :{
+ "name" : "DomainWarp",
+ "x" : {
+ "name" : "Mul",
+ "first" : {
+ "name" : "Const",
+ "value" : 0.02
+ },
+ "second" : {
+ "name" : "OpenSimplex"
+ }
+ },
+ "z" : {
+ "name" : "Mul",
+ "first" : {
+ "name" : "Const",
+ "value" : 0.02
+ },
+ "second" : {
+ "name" : "OpenSimplex"
+ }
+ },
+ "source" : {
+ "name" : "Add",
+ "first" : {
+ "name" : "Const",
+ "value" : 1.0
+ },
+ "second" : {
+ "name" : "OpenSimplex"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md
index ce96db24..df8fe157 100644
--- a/docs/src/progress/renderertodo.md
+++ b/docs/src/progress/renderertodo.md
@@ -1065,6 +1065,16 @@ Fix flickering chunks on unload
Fix draw cell distance cache busting on far distances
Fix skybox not updating position with player entity
Add Scene shorthand for registering runnables as behavior trees
+Re-integrate frame time tracking
+Solve y last in chunkgen to optimize height calc
+
+(11/18/2024)
+Defining noise functions in config files
+Invoking noise functions from noise functions
+Voxel and Heightmap generators based on noise functions in files
+Set all client terrain rigid bodies are kinematic
+Add caves
+Tweaking test2 noise definition
diff --git a/pom.xml b/pom.xml
index d5d223f6..716ff5ec 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,8 +7,8 @@
jar
UTF-8
- 17
- 17
+ 21
+ 21
3.3.3
1.9.19
1.5.7
diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java
index 081dbf50..246f463f 100644
--- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java
+++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java
@@ -133,8 +133,8 @@ public class ClientTerrainManager {
ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData());
FloatBuffer floatBuffer = buffer.asFloatBuffer();
for(int x = 0; x < ChunkData.CHUNK_DATA_SIZE; x++){
- for(int y = 0; y < ChunkData.CHUNK_DATA_SIZE; y++){
- for(int z = 0; z < ChunkData.CHUNK_DATA_SIZE; z++){
+ for(int z = 0; z < ChunkData.CHUNK_DATA_SIZE; z++){
+ for(int y = 0; y < ChunkData.CHUNK_DATA_SIZE; y++){
weights[x][y][z] = floatBuffer.get();
}
}
@@ -144,8 +144,8 @@ public class ClientTerrainManager {
int firstType = -1;
boolean homogenous = true;
for(int x = 0; x < ChunkData.CHUNK_DATA_SIZE; x++){
- for(int y = 0; y < ChunkData.CHUNK_DATA_SIZE; y++){
- for(int z = 0; z < ChunkData.CHUNK_DATA_SIZE; z++){
+ for(int z = 0; z < ChunkData.CHUNK_DATA_SIZE; z++){
+ for(int y = 0; y < ChunkData.CHUNK_DATA_SIZE; y++){
values[x][y][z] = intView.get();
if(firstType == -1){
firstType = values[x][y][z];
diff --git a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java
index d09d3b4f..332c4150 100644
--- a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java
+++ b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java
@@ -481,14 +481,12 @@ public class PhysicsEntityUtils {
* @param data The terrain description
* @return The rigid body created (note, attachment has already been performed)
*/
- public static DBody clientAttachTerrainChunkRigidBody(Entity terrain, TerrainChunkData data){
+ public static void clientAttachTerrainChunkRigidBody(Entity terrain, TerrainChunkData data){
DBody terrainBody = CollisionBodyCreation.generateBodyFromTerrainData(Globals.clientSceneWrapper.getCollisionEngine(), data, Collidable.TYPE_STATIC_BIT);
-
-
- Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(terrainBody, new Collidable(terrain,Collidable.TYPE_TERRAIN, false));
- PhysicsEntityUtils.setDBody(terrain,terrainBody);
-
- return terrainBody;
+ if(terrainBody != null){
+ Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(terrainBody, new Collidable(terrain,Collidable.TYPE_TERRAIN, false));
+ PhysicsEntityUtils.setDBody(terrain,terrainBody);
+ }
}
@@ -514,6 +512,9 @@ public class PhysicsEntityUtils {
*/
public static void serverRepositionEntities(CollisionEngine collisionEngine){
List toReposition = new LinkedList();
+ if(collisionEngine.getCollidables() == null){
+ throw new Error("Collision engine collidables are null!");
+ }
for(Collidable collidable : collisionEngine.getCollidables()){
Entity entity = collidable.getParent();
DBody body = PhysicsEntityUtils.getDBody(entity);
diff --git a/src/main/java/electrosphere/engine/Main.java b/src/main/java/electrosphere/engine/Main.java
index 5066dd37..1dfa99af 100644
--- a/src/main/java/electrosphere/engine/Main.java
+++ b/src/main/java/electrosphere/engine/Main.java
@@ -428,6 +428,12 @@ public class Main {
}
+ ///
+ /// F R A M E T I M E T R A C K I NG
+ ///
+ ImGuiWindowMacros.addGlobalFramerateDatapoint("totalframerate", Globals.timekeeper.getMostRecentRawFrametime());
+
+
///
/// E N D M A I N L O O P
///
diff --git a/src/main/java/electrosphere/game/data/Config.java b/src/main/java/electrosphere/game/data/Config.java
index 7e634703..6a4a4ba6 100644
--- a/src/main/java/electrosphere/game/data/Config.java
+++ b/src/main/java/electrosphere/game/data/Config.java
@@ -20,6 +20,7 @@ import electrosphere.game.data.tutorial.HintDefinition;
import electrosphere.game.data.units.UnitDefinitionFile;
import electrosphere.game.data.units.UnitLoader;
import electrosphere.game.data.voxel.VoxelData;
+import electrosphere.game.data.voxel.sampler.SamplerFile;
import electrosphere.game.server.race.model.RaceMap;
import electrosphere.game.server.symbolism.model.SymbolMap;
import electrosphere.util.FileUtils;
@@ -66,6 +67,11 @@ public class Config {
* The biome map
*/
BiomeTypeMap biomeMap;
+
+ /**
+ * The list of sampler definitions
+ */
+ List samplerDefinitions;
/**
* Loads the default data
@@ -87,6 +93,7 @@ public class Config {
config.unitLoader = UnitLoader.create(FileUtils.loadObjectFromAssetPath("Data/game/units/units.json", UnitDefinitionFile.class));
config.recipeMap = RecipeDataMap.loadRecipeFiles("Data/game/recipes.json");
config.biomeMap = BiomeTypeMap.loadBiomeFile("Data/game/biomes.json");
+ config.samplerDefinitions = SamplerFile.readSamplerDefinitionFiles("Data/game/voxel");
//create furniture items
ItemDataMap.loadSpawnItems(config.itemMap, config.objectTypeLoader);
@@ -282,5 +289,13 @@ public class Config {
public BiomeTypeMap getBiomeMap(){
return biomeMap;
}
+
+ /**
+ * Gets the list of all sampler files
+ * @return The list of all sampler files
+ */
+ public List getSamplerFiles(){
+ return this.samplerDefinitions;
+ }
}
diff --git a/src/main/java/electrosphere/game/data/voxel/sampler/SamplerFile.java b/src/main/java/electrosphere/game/data/voxel/sampler/SamplerFile.java
new file mode 100644
index 00000000..4438fb18
--- /dev/null
+++ b/src/main/java/electrosphere/game/data/voxel/sampler/SamplerFile.java
@@ -0,0 +1,112 @@
+package electrosphere.game.data.voxel.sampler;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+import electrosphere.server.terrain.generation.noise.NoiseContainer;
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
+import electrosphere.server.terrain.generation.noise.operators.NoiseOperoatorInvoke;
+import electrosphere.util.FileUtils;
+
+/**
+ * A sampler definition file
+ */
+public class SamplerFile {
+
+ /**
+ * The name of this sampler type
+ */
+ String name;
+
+ /**
+ * The sample to pull from
+ */
+ NoiseSampler sampler;
+
+ /**
+ * Gets the name of the file
+ * @return The name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets the sampler for this file
+ * @return The sampler
+ */
+ public NoiseSampler getSampler() {
+ return sampler;
+ }
+
+ /**
+ * Gets the sampler definition files
+ * @return The list of sampler definition files
+ */
+ public static List readSamplerDefinitionFiles(String initialPath){
+ File initialDirectory = FileUtils.getAssetFile(initialPath);
+ List rVal = new LinkedList();
+
+ //read the files in
+ SamplerFile.recursivelyReadSamplerDefinitionFiles(initialDirectory, rVal);
+
+ //link invoke targets
+ for(SamplerFile file : rVal){
+ SamplerFile.linkInvokeTargets(rVal, file.getSampler());
+ }
+
+ return rVal;
+ }
+
+ /**
+ * Recursively reads from the root all sampler files
+ * @param rootDir The root dir
+ * @param appendTarget The list to add sampler files to
+ */
+ private static void recursivelyReadSamplerDefinitionFiles(File rootDir, List appendTarget){
+ if(rootDir == null || !rootDir.isDirectory()){
+ throw new Error("Invalid path provided! " + rootDir.getAbsolutePath());
+ }
+ if(rootDir == null || !rootDir.exists() || rootDir.listFiles() == null){
+ return;
+ }
+ for(File child : rootDir.listFiles()){
+ if(child.isDirectory()){
+ SamplerFile.recursivelyReadSamplerDefinitionFiles(child, appendTarget);
+ } else {
+ SamplerFile file = FileUtils.loadObjectFromFile(child, SamplerFile.class);
+ appendTarget.add(file);
+ }
+ }
+ }
+
+ /**
+ * Links all invoke targets to their respective samplers
+ * @param files The list of all sampler files
+ * @param current The current sampler function to evaluate
+ */
+ private static void linkInvokeTargets(List files, NoiseSampler current){
+ if(current instanceof NoiseOperoatorInvoke){
+ NoiseOperoatorInvoke invoker = (NoiseOperoatorInvoke)current;
+ String target = invoker.getTarget();
+ if(target.equals(current.getName())){
+ throw new Error("Invoke module pointing at itself!");
+ }
+ for(SamplerFile file : files){
+ if(file.getName().equals(target)){
+ invoker.setInvokeSampler(file.getSampler());
+ break;
+ }
+ }
+ if(invoker.getInvokeSampler() == null){
+ throw new Error("Failed to link " + target);
+ }
+ } else if(current instanceof NoiseContainer){
+ for(NoiseSampler child : ((NoiseContainer)current).getChildren()){
+ SamplerFile.linkInvokeTargets(files, child);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java
index 622ae5ac..5ca2430e 100644
--- a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java
+++ b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java
@@ -229,8 +229,8 @@ public class TerrainProtocol implements ServerProtocolTemplate {
FloatBuffer floatView = buffer.asFloatBuffer();
for(int x = 0; x < xWidth; x++){
- for(int y = 0; y < yWidth; y++){
- for(int z = 0; z < zWidth; z++){
+ for(int z = 0; z < zWidth; z++){
+ for(int y = 0; y < yWidth; y++){
floatView.put(chunk.getWeights()[x][y][z]);
}
}
@@ -240,8 +240,8 @@ public class TerrainProtocol implements ServerProtocolTemplate {
intView.position(floatView.position());
for(int x = 0; x < xWidth; x++){
- for(int y = 0; y < yWidth; y++){
- for(int z = 0; z < zWidth; z++){
+ for(int z = 0; z < zWidth; z++){
+ for(int y = 0; y < yWidth; y++){
intView.put(chunk.getValues()[x][y][z]);
}
}
diff --git a/src/main/java/electrosphere/script/ScriptContext.java b/src/main/java/electrosphere/script/ScriptContext.java
index 791e9c1b..72cce355 100644
--- a/src/main/java/electrosphere/script/ScriptContext.java
+++ b/src/main/java/electrosphere/script/ScriptContext.java
@@ -3,6 +3,7 @@ package electrosphere.script;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import org.graalvm.polyglot.Context;
@@ -376,7 +377,15 @@ public class ScriptContext {
* @param function The function
*/
public void executeSynchronously(Runnable function){
- lock.lock();
+ boolean success = false;
+ try {
+ success = lock.tryLock(1, TimeUnit.MICROSECONDS);
+ } catch (InterruptedException e) {
+ LoggerInterface.loggerScripts.ERROR(e);
+ }
+ if(!success){
+ throw new Error("Failed to acquire lock!");
+ }
function.run();
lock.unlock();
}
diff --git a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java
index 1b7b9ffe..e39fae3a 100644
--- a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java
+++ b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java
@@ -54,7 +54,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
/**
* The number of frames without players that must pass before a server data cell is unloaded
*/
- static final int UNLOAD_FRAME_THRESHOLD = 100;
+ static final int UNLOAD_FRAME_THRESHOLD = 500;
/**
* Tracks whether this manager has been flagged to unload cells or not
diff --git a/src/main/java/electrosphere/server/terrain/generation/JSChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/JSChunkGenerator.java
new file mode 100644
index 00000000..b6126b1d
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/JSChunkGenerator.java
@@ -0,0 +1,241 @@
+package electrosphere.server.terrain.generation;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import electrosphere.client.terrain.cache.ChunkData;
+
+import org.graalvm.polyglot.Value;
+
+import electrosphere.engine.Globals;
+import electrosphere.game.data.biome.BiomeData;
+import electrosphere.game.data.biome.BiomeSurfaceGenerationParams;
+import electrosphere.game.server.world.ServerWorldData;
+import electrosphere.server.terrain.generation.heightmap.EmptySkyGen;
+import electrosphere.server.terrain.generation.heightmap.HeightmapGenerator;
+import electrosphere.server.terrain.generation.heightmap.HillsGen;
+import electrosphere.server.terrain.generation.heightmap.MountainGen;
+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.voxelphase.AnimeMountainsGen;
+import electrosphere.server.terrain.generation.voxelphase.HillsVoxelGen;
+import electrosphere.server.terrain.generation.voxelphase.MountainVoxelGen;
+import electrosphere.server.terrain.generation.voxelphase.VoxelGenerator;
+import electrosphere.server.terrain.manager.ServerTerrainChunk;
+import electrosphere.server.terrain.models.TerrainModel;
+
+/**
+ * Dedicated script-based chunk generator
+ */
+public class JSChunkGenerator implements ChunkGenerator {
+
+ /**
+ * The size of the realm for testing generation
+ */
+ public static final int GENERATOR_REALM_SIZE = 512;
+
+ /**
+ * The default biome index
+ */
+ public static final int DEFAULT_BIOME_INDEX = 1;
+
+ /**
+ * The width of the surface in number of voxels
+ */
+ public static final int SURFACE_VOXEL_WIDTH = 2;
+
+ /**
+ * Tag for the test generator
+ */
+ public static final String SCRIPT_GEN_TEST_TAG = "test";
+
+ /**
+ * Controls the default setting for whether to use javascript or not
+ */
+ public static final boolean DEFAULT_USE_JAVASCRIPT = false;
+
+ /**
+ * The terreain model for the generator
+ */
+ TerrainModel terrainModel;
+
+ /**
+ * The server world data
+ */
+ ServerWorldData serverWorldData;
+
+ /**
+ * The map of generator tag to the heightmap generator
+ */
+ Map tagHeightmapMap = new HashMap();
+
+ /**
+ * The map of generator tag to voxel generator
+ */
+ Map tagVoxelMap = new HashMap();
+
+ /**
+ * Tracks whether to use javascript generation or not
+ */
+ boolean useJavascript = false;
+
+ /**
+ * Constructor
+ */
+ public JSChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){
+ this.serverWorldData = serverWorldData;
+ this.registerHeightmapGenerator(new EmptySkyGen());
+ this.registerHeightmapGenerator(new HillsGen());
+ this.registerHeightmapGenerator(new PlainsGen());
+ this.registerHeightmapGenerator(new MountainGen());
+ this.registerVoxelGenerator(new HillsVoxelGen());
+ this.registerVoxelGenerator(new AnimeMountainsGen());
+ this.registerVoxelGenerator(new MountainVoxelGen());
+ this.useJavascript = useJavascript;
+ }
+
+ /**
+ * Registers a heightmap generator
+ * @param generator The heightmap 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 = 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];
+
+ try {
+ //biome of the current chunk
+ BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ);
+
+ BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
+ HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
+ heightmapGen = this.tagHeightmapMap.get("hills");
+ if(heightmapGen == null){
+ throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
+ }
+
+ //stride value
+ int strideValue = (int)Math.pow(2,stride);
+
+ //presolve heightfield
+ float[][] heightfield = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
+ for(int x = 0; x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; x++){
+ for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){
+ int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
+ int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
+ int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
+ int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
+ heightfield[x][z] = heightmapGen.getHeight(
+ this.terrainModel.getSeed(),
+ this.serverWorldData.convertVoxelToRealSpace(finalChunkX, finalWorldX),
+ this.serverWorldData.convertVoxelToRealSpace(finalChunkZ, finalWorldZ)
+ );
+ }
+ }
+
+ float[][] gradientField = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
+ for(int x = 0; x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; x++){
+ for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){
+ float deltaX = 0;
+ float deltaZ = 0;
+ if(x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 1){
+ deltaX = Math.abs(heightfield[x][z] - heightfield[x+1][z]);
+ } else {
+ deltaX = Math.abs(heightfield[x][z] - heightfield[x-1][z]);
+ }
+ if(z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 1){
+ deltaX = Math.abs(heightfield[x][z] - heightfield[x][z+1]);
+ } else {
+ deltaX = Math.abs(heightfield[x][z] - heightfield[x][z-1]);
+ }
+ gradientField[x][z] = deltaX * deltaX + deltaZ * deltaZ;
+ }
+ }
+
+ Globals.scriptEngine.getScriptContext().executeSynchronously(() -> {
+ int firstType = -2;
+ boolean homogenous = true;
+ GeneratedVoxel voxel = new GeneratedVoxel();
+ Value getVoxelFunc = Globals.scriptEngine.getScriptContext().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);
+ });
+ } catch(Exception ex){
+ ex.printStackTrace();
+ }
+ Globals.profiler.endCpuSample();
+ return rVal;
+ }
+
+ @Override
+ public double getElevation(int worldX, int worldZ, int chunkX, int chunkZ){
+ BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ);
+
+ BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
+ HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
+ if(heightmapGen == null){
+ throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
+ }
+ double rVal = heightmapGen.getHeight(
+ this.terrainModel.getSeed(),
+ this.serverWorldData.convertVoxelToRealSpace(chunkX, worldX),
+ this.serverWorldData.convertVoxelToRealSpace(chunkZ, worldZ)
+ );
+ return rVal;
+ }
+
+ @Override
+ public void setModel(TerrainModel model) {
+ this.terrainModel = model;
+ }
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java
index 0149d06f..0f93ff86 100644
--- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java
+++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java
@@ -5,14 +5,14 @@ import java.util.Map;
import electrosphere.client.terrain.cache.ChunkData;
-import org.graalvm.polyglot.Value;
-
import electrosphere.engine.Globals;
import electrosphere.game.data.biome.BiomeData;
import electrosphere.game.data.biome.BiomeSurfaceGenerationParams;
+import electrosphere.game.data.voxel.sampler.SamplerFile;
import electrosphere.game.server.world.ServerWorldData;
import electrosphere.server.terrain.generation.heightmap.EmptySkyGen;
import electrosphere.server.terrain.generation.heightmap.HeightmapGenerator;
+import electrosphere.server.terrain.generation.heightmap.HeightmapNoiseGen;
import electrosphere.server.terrain.generation.heightmap.HillsGen;
import electrosphere.server.terrain.generation.heightmap.MountainGen;
import electrosphere.server.terrain.generation.heightmap.PlainsGen;
@@ -22,6 +22,7 @@ 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.MountainVoxelGen;
+import electrosphere.server.terrain.generation.voxelphase.NoiseVoxelGen;
import electrosphere.server.terrain.generation.voxelphase.VoxelGenerator;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.models.TerrainModel;
@@ -86,6 +87,16 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
*/
public TestGenerationChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){
this.serverWorldData = serverWorldData;
+ this.registerAllGenerators();
+ this.useJavascript = useJavascript;
+ }
+
+ /**
+ * Registers all generators
+ */
+ public void registerAllGenerators(){
+ tagHeightmapMap.clear();
+ tagVoxelMap.clear();
this.registerHeightmapGenerator(new EmptySkyGen());
this.registerHeightmapGenerator(new HillsGen());
this.registerHeightmapGenerator(new PlainsGen());
@@ -93,7 +104,10 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
this.registerVoxelGenerator(new HillsVoxelGen());
this.registerVoxelGenerator(new AnimeMountainsGen());
this.registerVoxelGenerator(new MountainVoxelGen());
- this.useJavascript = useJavascript;
+ for(SamplerFile samplerFile : Globals.gameConfigCurrent.getSamplerFiles()){
+ this.registerHeightmapGenerator(new HeightmapNoiseGen(samplerFile));
+ this.registerVoxelGenerator(new NoiseVoxelGen(samplerFile));
+ }
}
/**
@@ -168,95 +182,60 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
}
}
- VoxelGenerator voxelGenerator = this.tagVoxelMap.get("hills");
+ VoxelGenerator voxelGenerator = this.tagVoxelMap.get("test1");
+
+ 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++){
+ int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
+ int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
+ double realX = generationContext.getServerWorldData().convertVoxelToRealSpace(finalChunkX,finalWorldX);
+
+ for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){
+ int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
+ int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
+ double realZ = generationContext.getServerWorldData().convertVoxelToRealSpace(finalChunkZ,finalWorldZ);
+ float surfaceHeight = heightfield[x][z];
+ float gradient = gradientField[x][z];
- if(this.useJavascript){
- Globals.scriptEngine.getScriptContext().executeSynchronously(() -> {
- int firstType = -2;
- boolean homogenous = true;
- GeneratedVoxel voxel = new GeneratedVoxel();
- Value getVoxelFunc = Globals.scriptEngine.getScriptContext().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++){
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;
- voxelGenerator.getVoxel(
- voxel,
- finalWorldX, finalWorldY, finalWorldZ,
- finalChunkX, finalChunkY, finalChunkZ,
- stride,
- heightfield[x][z], gradientField[x][z],
- surfaceBiome,
- generationContext
- );
- if(voxel != null){
- 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;
- }
+ int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
+ int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
+ double realY = generationContext.getServerWorldData().convertVoxelToRealSpace(finalChunkY,finalWorldY);
+
+ voxelGenerator.getVoxel(
+ voxel,
+ finalWorldX, finalWorldY, finalWorldZ,
+ finalChunkX, finalChunkY, finalChunkZ,
+ realX, realY, realZ,
+ stride,
+ surfaceHeight, gradient,
+ surfaceBiome,
+ generationContext
+ );
+ if(voxel != null){
+ 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;
}
}
}
- if(homogenous){
- rVal.setHomogenousValue(firstType);
- } else {
- rVal.setHomogenousValue(ChunkData.NOT_HOMOGENOUS);
- }
- rVal.setWeights(weights);
- rVal.setValues(values);
}
+ if(homogenous){
+ rVal.setHomogenousValue(firstType);
+ } else {
+ rVal.setHomogenousValue(ChunkData.NOT_HOMOGENOUS);
+ }
+ rVal.setWeights(weights);
+ rVal.setValues(values);
} catch(Exception ex){
ex.printStackTrace();
}
diff --git a/src/main/java/electrosphere/server/terrain/generation/heightmap/HeightmapNoiseGen.java b/src/main/java/electrosphere/server/terrain/generation/heightmap/HeightmapNoiseGen.java
new file mode 100644
index 00000000..efd0ddc7
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/heightmap/HeightmapNoiseGen.java
@@ -0,0 +1,40 @@
+package electrosphere.server.terrain.generation.heightmap;
+
+import electrosphere.game.data.voxel.sampler.SamplerFile;
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
+
+/**
+ * Generates a heightmap using a noise definition
+ */
+public class HeightmapNoiseGen implements HeightmapGenerator {
+
+ /**
+ * The tag for the noise function that generates
+ */
+ String tag;
+
+ /**
+ * The sampler to pull from when allocating voxels
+ */
+ NoiseSampler sampler;
+
+ /**
+ * Constructor
+ * @param sampler The sampler to pull from
+ */
+ public HeightmapNoiseGen(SamplerFile samplerDefinitionFile){
+ this.sampler = samplerDefinitionFile.getSampler();
+ this.tag = samplerDefinitionFile.getName();
+ }
+
+ @Override
+ public float getHeight(long SEED, double x, double y) {
+ return (float)sampler.getValue(0, x, y, 0);
+ }
+
+ @Override
+ public String getTag() {
+ return tag;
+ }
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/NoiseContainer.java b/src/main/java/electrosphere/server/terrain/generation/noise/NoiseContainer.java
new file mode 100644
index 00000000..e59b9fe5
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/NoiseContainer.java
@@ -0,0 +1,16 @@
+package electrosphere.server.terrain.generation.noise;
+
+import java.util.Collection;
+
+/**
+ * A module that contains other modules
+ */
+public interface NoiseContainer extends NoiseSampler {
+
+ /**
+ * Gets all child modules of this one
+ * @return All child modules
+ */
+ public Collection getChildren();
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/NoiseModuleSerializer.java b/src/main/java/electrosphere/server/terrain/generation/noise/NoiseModuleSerializer.java
new file mode 100644
index 00000000..82d89088
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/NoiseModuleSerializer.java
@@ -0,0 +1,63 @@
+package electrosphere.server.terrain.generation.noise;
+
+import java.lang.reflect.Type;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+
+import electrosphere.server.terrain.generation.noise.operators.NoiseOperatorAdd;
+import electrosphere.server.terrain.generation.noise.operators.NoiseOperatorClamp;
+import electrosphere.server.terrain.generation.noise.operators.NoiseOperatorConst;
+import electrosphere.server.terrain.generation.noise.operators.NoiseOperatorDomainWarp;
+import electrosphere.server.terrain.generation.noise.operators.NoiseOperatorMul;
+import electrosphere.server.terrain.generation.noise.operators.NoiseOperatorOpenSimplex;
+import electrosphere.server.terrain.generation.noise.operators.NoiseOperatorVoronoi;
+import electrosphere.server.terrain.generation.noise.operators.NoiseOperoatorInvoke;
+
+/**
+ * Deserializes noise modules
+ */
+public class NoiseModuleSerializer implements JsonDeserializer {
+
+ @Override
+ public NoiseSampler deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ switch(json.getAsJsonObject().get("name").getAsString()){
+
+ //meta
+ case NoiseOperoatorInvoke.NAME: {
+ return context.deserialize(json, NoiseOperoatorInvoke.class);
+ }
+
+ //warps
+ case NoiseOperatorDomainWarp.NAME: {
+ return context.deserialize(json, NoiseOperatorDomainWarp.class);
+ }
+
+ //actual noise sampling
+ case NoiseOperatorOpenSimplex.NAME: {
+ return context.deserialize(json, NoiseOperatorOpenSimplex.class);
+ }
+ case NoiseOperatorVoronoi.NAME: {
+ return context.deserialize(json, NoiseOperatorVoronoi.class);
+ }
+
+ //basic ops
+ case NoiseOperatorConst.NAME: {
+ return context.deserialize(json, NoiseOperatorConst.class);
+ }
+ case NoiseOperatorAdd.NAME: {
+ return context.deserialize(json, NoiseOperatorAdd.class);
+ }
+ case NoiseOperatorMul.NAME: {
+ return context.deserialize(json, NoiseOperatorMul.class);
+ }
+ case NoiseOperatorClamp.NAME: {
+ return context.deserialize(json, NoiseOperatorClamp.class);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/NoiseOctaveSampler.java b/src/main/java/electrosphere/server/terrain/generation/noise/NoiseOctaveSampler.java
new file mode 100644
index 00000000..f27c8559
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/NoiseOctaveSampler.java
@@ -0,0 +1,44 @@
+package electrosphere.server.terrain.generation.noise;
+
+/**
+ * A noce function that can be sampled in octaves
+ */
+public interface NoiseOctaveSampler extends NoiseSampler {
+
+ /**
+ * Sets the lacunarity of the sampler
+ * @param lacunarity The lacunarity
+ */
+ public void setLacunarity(double lacunarity);
+
+ /**
+ * Gets the lacunarity of the sampler
+ * @return The lacunarity
+ */
+ public double getLacunarity();
+
+ /**
+ * Sets the number of octaves to sample
+ * @param octaveCount The number of octaves to sample
+ */
+ public void setOctaveCount(int octaveCount);
+
+ /**
+ * Gets the number of octaves to sample
+ * @return The number of octaves to sample
+ */
+ public int getOctaveCount();
+
+ /**
+ * Sets the frequency to sample at
+ * @param frequency The frequency
+ */
+ public void setFrequency(double frequency);
+
+ /**
+ * Gets the frequency of the function
+ * @return The frequency
+ */
+ public double getFrequency();
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/NoiseSampler.java b/src/main/java/electrosphere/server/terrain/generation/noise/NoiseSampler.java
new file mode 100644
index 00000000..fa6adba7
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/NoiseSampler.java
@@ -0,0 +1,24 @@
+package electrosphere.server.terrain.generation.noise;
+
+/**
+ * A noise module that can sample values
+ */
+public interface NoiseSampler {
+
+ /**
+ * Gets the name of this noise module
+ * @return The name of the module
+ */
+ public String getName();
+
+ /**
+ * Samples the noise at a given position with a given seed
+ * @param SEED The seed
+ * @param x The x coordinate
+ * @param y The y coordinate
+ * @param z The z coordinate
+ * @return The value of the noise at that position for the given seed
+ */
+ public double getValue(double SEED, double x, double y, double z);
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorAdd.java b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorAdd.java
new file mode 100644
index 00000000..0d30660e
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorAdd.java
@@ -0,0 +1,44 @@
+package electrosphere.server.terrain.generation.noise.operators;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import electrosphere.server.terrain.generation.noise.NoiseContainer;
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
+
+public class NoiseOperatorAdd implements NoiseContainer {
+
+ /**
+ * The name of this module
+ */
+ public static final String NAME = "Add";
+
+ /**
+ * The first value to pull from
+ */
+ protected NoiseSampler first;
+
+ /**
+ * The second value to pull from
+ */
+ protected NoiseSampler second;
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public double getValue(double SEED, double x, double y, double z) {
+ return first.getValue(SEED, x, y, z) + second.getValue(SEED, x, y, z);
+ }
+
+ @Override
+ public Collection getChildren(){
+ return Arrays.asList(new NoiseSampler[]{
+ first,
+ second,
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorClamp.java b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorClamp.java
new file mode 100644
index 00000000..d560a6e7
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorClamp.java
@@ -0,0 +1,56 @@
+package electrosphere.server.terrain.generation.noise.operators;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import electrosphere.server.terrain.generation.noise.NoiseContainer;
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
+import io.github.studiorailgun.MathUtils;
+
+/**
+ * Clamps a noise value
+ */
+public class NoiseOperatorClamp implements NoiseContainer {
+
+ /**
+ * The name of this module
+ */
+ public static final String NAME = "Clamp";
+
+ /**
+ * The minimum value to clamp to
+ */
+ protected NoiseSampler min;
+
+ /**
+ * The maximum value to clamp to
+ */
+ protected NoiseSampler max;
+
+ /**
+ * The source to clamp
+ */
+ protected NoiseSampler source;
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public double getValue(double SEED, double x, double y, double z) {
+ return MathUtils.clamp(source.getValue(SEED, x, y, z), min.getValue(SEED, x, y, z), max.getValue(SEED, x, y, z));
+ }
+
+ @Override
+ public Collection getChildren(){
+ return Arrays.asList(new NoiseSampler[]{
+ min,
+ max,
+ source,
+ });
+ }
+
+
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorConst.java b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorConst.java
new file mode 100644
index 00000000..8323ba87
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorConst.java
@@ -0,0 +1,30 @@
+package electrosphere.server.terrain.generation.noise.operators;
+
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
+
+/**
+ * Gets a constant value
+ */
+public class NoiseOperatorConst implements NoiseSampler {
+
+ /**
+ * The name of this module
+ */
+ public static final String NAME = "Const";
+
+ /**
+ * The value
+ */
+ double value;
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public double getValue(double SEED, double x, double y, double z) {
+ return value;
+ }
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorDomainWarp.java b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorDomainWarp.java
new file mode 100644
index 00000000..2fbe2763
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorDomainWarp.java
@@ -0,0 +1,76 @@
+package electrosphere.server.terrain.generation.noise.operators;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import electrosphere.server.terrain.generation.noise.NoiseContainer;
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
+
+/**
+ * The warp operator
+ */
+public class NoiseOperatorDomainWarp implements NoiseContainer {
+
+ /**
+ * The name of this module
+ */
+ public static final String NAME = "DomainWarp";
+
+ /**
+ * The sampler to pull the x value from
+ */
+ protected NoiseSampler x;
+
+ /**
+ * The sampler to pull the y value from
+ */
+ protected NoiseSampler y;
+
+ /**
+ * The sampler to pull the z value from
+ */
+ protected NoiseSampler z;
+
+ /**
+ * The amplitude of the warp to apply
+ */
+ protected double amplitude = 1.0f;
+
+ /**
+ * The sampler to pull from for the final emitted value
+ */
+ protected NoiseSampler source;
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public double getValue(double SEED, double x, double y, double z) {
+ double sampleX = x;
+ if(this.x != null){
+ sampleX = sampleX + this.x.getValue(SEED, x, y, z) * amplitude;
+ }
+ double sampleY = y;
+ if(this.y != null){
+ sampleY = sampleY + this.y.getValue(SEED, x, y, z) * amplitude;
+ }
+ double sampleZ = z;
+ if(this.z != null){
+ sampleZ = sampleZ + this.z.getValue(SEED, x, y, z) * amplitude;
+ }
+ return this.source.getValue(SEED, sampleX, sampleY, sampleZ);
+ }
+
+ @Override
+ public Collection getChildren(){
+ return Arrays.asList(new NoiseSampler[]{
+ x,
+ y,
+ z,
+ source,
+ });
+ }
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorMul.java b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorMul.java
new file mode 100644
index 00000000..773a4173
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorMul.java
@@ -0,0 +1,47 @@
+package electrosphere.server.terrain.generation.noise.operators;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import electrosphere.server.terrain.generation.noise.NoiseContainer;
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
+
+/**
+ * Multiplies one noise source by another
+ */
+public class NoiseOperatorMul implements NoiseContainer {
+
+ /**
+ * The name of this module
+ */
+ public static final String NAME = "Mul";
+
+ /**
+ * First sample
+ */
+ protected NoiseSampler first;
+
+ /**
+ * Second sample
+ */
+ protected NoiseSampler second;
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public double getValue(double SEED, double x, double y, double z) {
+ return first.getValue(SEED, x, y, z) * second.getValue(SEED, x, y, z);
+ }
+
+ @Override
+ public Collection getChildren(){
+ return Arrays.asList(new NoiseSampler[]{
+ first,
+ second,
+ });
+ }
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorOpenSimplex.java b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorOpenSimplex.java
new file mode 100644
index 00000000..077eb6b7
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorOpenSimplex.java
@@ -0,0 +1,77 @@
+package electrosphere.server.terrain.generation.noise.operators;
+
+import electrosphere.server.terrain.generation.noise.NoiseOctaveSampler;
+import electrosphere.util.noise.OpenSimplex2S;
+
+/**
+ * Samples open simplex noise
+ */
+public class NoiseOperatorOpenSimplex implements NoiseOctaveSampler {
+
+ /**
+ * The name of this module
+ */
+ public static final String NAME = "OpenSimplex";
+
+ /**
+ * The frequency to sample at
+ */
+ double frequency = 0.02;
+
+ /**
+ * The lacunarity (the amount to change the frequency each octave)
+ */
+ double lacunarity = 0.02;
+
+ /**
+ * The number of octaves to sample
+ */
+ int octaveCount = 1;
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public double getValue(double SEED, double x, double y, double z) {
+ double rVal = 0;
+ double sampleFreq = frequency;
+ for(int i = 0; i < octaveCount; i++){
+ rVal = rVal + OpenSimplex2S.noise3_ImproveXZ((long)SEED, x * sampleFreq, y * sampleFreq, z * sampleFreq);
+ sampleFreq = sampleFreq + lacunarity;
+ }
+ return rVal;
+ }
+
+ @Override
+ public void setLacunarity(double lacunarity) {
+ this.lacunarity = lacunarity;
+ }
+
+ @Override
+ public double getLacunarity() {
+ return this.lacunarity;
+ }
+
+ @Override
+ public void setOctaveCount(int octaveCount) {
+ this.octaveCount = octaveCount;
+ }
+
+ @Override
+ public int getOctaveCount() {
+ return this.octaveCount;
+ }
+
+ @Override
+ public void setFrequency(double frequency) {
+ this.frequency = frequency;
+ }
+
+ @Override
+ public double getFrequency() {
+ return this.frequency;
+ }
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorVoronoi.java b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorVoronoi.java
new file mode 100644
index 00000000..51bb949c
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperatorVoronoi.java
@@ -0,0 +1,77 @@
+package electrosphere.server.terrain.generation.noise.operators;
+
+import electrosphere.server.terrain.generation.noise.NoiseOctaveSampler;
+import io.github.studiorailgun.NoiseUtils;
+
+/**
+ * Samples voronoi noise
+ */
+public class NoiseOperatorVoronoi implements NoiseOctaveSampler {
+
+ /**
+ * The name of this module
+ */
+ public static final String NAME = "Voronoi";
+
+ /**
+ * The frequency to sample at
+ */
+ double frequency = 0.02;
+
+ /**
+ * The lacunarity (the amount to change the frequency each octave)
+ */
+ double lacunarity = 0.02;
+
+ /**
+ * The number of octaves to sample
+ */
+ int octaveCount = 1;
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public double getValue(double SEED, double x, double y, double z) {
+ double rVal = 0;
+ double sampleFreq = frequency;
+ for(int i = 0; i < octaveCount; i++){
+ rVal = rVal + NoiseUtils.voronoi(x * sampleFreq, y * sampleFreq, z * sampleFreq);
+ sampleFreq = sampleFreq + lacunarity;
+ }
+ return rVal;
+ }
+
+ @Override
+ public void setLacunarity(double lacunarity) {
+ this.lacunarity = lacunarity;
+ }
+
+ @Override
+ public double getLacunarity() {
+ return this.lacunarity;
+ }
+
+ @Override
+ public void setOctaveCount(int octaveCount) {
+ this.octaveCount = octaveCount;
+ }
+
+ @Override
+ public int getOctaveCount() {
+ return this.octaveCount;
+ }
+
+ @Override
+ public void setFrequency(double frequency) {
+ this.frequency = frequency;
+ }
+
+ @Override
+ public double getFrequency() {
+ return this.frequency;
+ }
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperoatorInvoke.java b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperoatorInvoke.java
new file mode 100644
index 00000000..528615ba
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/noise/operators/NoiseOperoatorInvoke.java
@@ -0,0 +1,74 @@
+package electrosphere.server.terrain.generation.noise.operators;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import electrosphere.server.terrain.generation.noise.NoiseContainer;
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
+
+/**
+ * Invokes another sample definition file
+ */
+public class NoiseOperoatorInvoke implements NoiseContainer {
+
+ /**
+ * The name of this module
+ */
+ public static final String NAME = "Invoke";
+
+ /**
+ * The sampler to invoke
+ */
+ NoiseSampler invokeSampler;
+
+ /**
+ * The name of the sampler definition file to invoke
+ */
+ String target;
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public double getValue(double SEED, double x, double y, double z) {
+ if(invokeSampler != null){
+ return invokeSampler.getValue(SEED, x, y, z);
+ } else {
+ throw new Error("Invoke sampler undefined for " + target);
+ }
+ }
+
+ /**
+ * Sets the sampler to invoke
+ * @param invokeSampler The sampler
+ */
+ public void setInvokeSampler(NoiseSampler invokeSampler){
+ this.invokeSampler = invokeSampler;
+ }
+
+ /**
+ * Gets the invoke sampler
+ * @return The sampler
+ */
+ public NoiseSampler getInvokeSampler(){
+ return invokeSampler;
+ }
+
+ /**
+ * Gets the target this operator wants to invoke
+ * @return The target
+ */
+ public String getTarget(){
+ return target;
+ }
+
+ @Override
+ public Collection getChildren(){
+ return Arrays.asList(new NoiseSampler[]{
+ invokeSampler
+ });
+ }
+
+}
diff --git a/src/main/java/electrosphere/server/terrain/generation/voxelphase/AnimeMountainsGen.java b/src/main/java/electrosphere/server/terrain/generation/voxelphase/AnimeMountainsGen.java
index a6f306a2..3dd6ce8d 100644
--- a/src/main/java/electrosphere/server/terrain/generation/voxelphase/AnimeMountainsGen.java
+++ b/src/main/java/electrosphere/server/terrain/generation/voxelphase/AnimeMountainsGen.java
@@ -67,16 +67,13 @@ public class AnimeMountainsGen implements VoxelGenerator {
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
+ double realX, double realY, double realZ,
int stride, float surfaceHeight, float surfaceGradient,
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);
diff --git a/src/main/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGen.java b/src/main/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGen.java
index 59e73d54..9b321782 100644
--- a/src/main/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGen.java
+++ b/src/main/java/electrosphere/server/terrain/generation/voxelphase/HillsVoxelGen.java
@@ -26,6 +26,7 @@ public class HillsVoxelGen implements VoxelGenerator {
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
+ double realX, double realY, double realZ,
int stride,
float surfaceHeight, float surfaceGradient,
BiomeData surfaceBiome,
@@ -33,10 +34,6 @@ public class HillsVoxelGen implements VoxelGenerator {
){
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);
diff --git a/src/main/java/electrosphere/server/terrain/generation/voxelphase/MountainVoxelGen.java b/src/main/java/electrosphere/server/terrain/generation/voxelphase/MountainVoxelGen.java
index d79d0d82..93cbf1cf 100644
--- a/src/main/java/electrosphere/server/terrain/generation/voxelphase/MountainVoxelGen.java
+++ b/src/main/java/electrosphere/server/terrain/generation/voxelphase/MountainVoxelGen.java
@@ -33,6 +33,7 @@ public class MountainVoxelGen implements VoxelGenerator {
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
+ double realX, double realY, double realZ,
int stride,
float surfaceHeight, float surfaceGradient,
BiomeData surfaceBiome,
@@ -40,10 +41,6 @@ public class MountainVoxelGen implements VoxelGenerator {
){
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);
diff --git a/src/main/java/electrosphere/server/terrain/generation/voxelphase/NoiseVoxelGen.java b/src/main/java/electrosphere/server/terrain/generation/voxelphase/NoiseVoxelGen.java
new file mode 100644
index 00000000..3a0914a7
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/voxelphase/NoiseVoxelGen.java
@@ -0,0 +1,109 @@
+package electrosphere.server.terrain.generation.voxelphase;
+
+import electrosphere.engine.Globals;
+import electrosphere.game.data.biome.BiomeData;
+import electrosphere.game.data.voxel.sampler.SamplerFile;
+import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel;
+import electrosphere.server.terrain.generation.interfaces.GenerationContext;
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
+import io.github.studiorailgun.MathUtils;
+
+/**
+ * Generates voxels based on a noise config
+ */
+public class NoiseVoxelGen implements VoxelGenerator {
+
+ /**
+ * The width of the surface in number of voxels
+ */
+ public static final int SURFACE_VOXEL_WIDTH = 2;
+
+ /**
+ * The tag for the noise function that generates
+ */
+ String tag;
+
+ /**
+ * The sampler to pull from when allocating voxels
+ */
+ NoiseSampler sampler;
+
+ /**
+ * Constructor
+ * @param samplerDefinitionFile The file to model this generator off of
+ */
+ public NoiseVoxelGen(SamplerFile samplerDefinitionFile){
+ this.sampler = samplerDefinitionFile.getSampler();
+ this.tag = samplerDefinitionFile.getName();
+ }
+
+ @Override
+ public String getTag() {
+ return tag;
+ }
+
+ @Override
+ public void getVoxel(
+ GeneratedVoxel voxel,
+ int worldX, int worldY, int worldZ,
+ int chunkX, int chunkY, int chunkZ,
+ double realX, double realY, double realZ,
+ int stride, float surfaceHeight, float surfaceGradient,
+ BiomeData surfaceBiome, GenerationContext generationContext
+ ) {
+ double strideMultiplier = Math.pow(2,stride);
+ double heightDiff = realY - surfaceHeight;
+ Globals.profiler.endCpuSample();
+ if(heightDiff < -strideMultiplier * SURFACE_VOXEL_WIDTH){
+ //below surface
+ double sample = this.sampler.getValue(0, realX, realY, realZ);
+ if(sample > 0){
+ double finalSurface = MathUtils.clamp(sample,0,1);
+ voxel.weight = (float)finalSurface * 2 - 1;
+ voxel.type = 1;
+ } else {
+ voxel.weight = -1.0f;
+ voxel.type = 0;
+ }
+ } else if(heightDiff > 0) {
+ //above surface
+ voxel.weight = -1.0f;
+ voxel.type = 0;
+ } else if(heightDiff < -strideMultiplier){
+ //generate full-size surface-type voxel
+ double sample = this.sampler.getValue(0, realX, realY, realZ);
+ if(sample > 0){
+ double finalHeight = MathUtils.clamp(sample,0,1);
+ voxel.weight = (float)finalHeight * 2 - 1;
+ voxel.type = 2;
+ } else {
+ voxel.weight = -1.0f;
+ voxel.type = 0;
+ }
+ } else {
+ //surface
+ double sample = this.sampler.getValue(0, realX, realY, realZ);
+ if(sample > 0){
+ double surfacePercent = heightDiff / -strideMultiplier;
+ double finalHeight = MathUtils.clamp(sample,0,1) * MathUtils.clamp(surfacePercent,0,1);
+ voxel.weight = (float)finalHeight * 2 - 1;
+ voxel.type = 2;
+ } else {
+ voxel.weight = -1.0f;
+ 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
index af17ede9..8af2f905 100644
--- a/src/main/java/electrosphere/server/terrain/generation/voxelphase/VoxelGenerator.java
+++ b/src/main/java/electrosphere/server/terrain/generation/voxelphase/VoxelGenerator.java
@@ -37,6 +37,7 @@ public interface VoxelGenerator {
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
+ double realX, double realY, double realZ,
int stride,
float surfaceHeight, float surfaceGradient,
BiomeData surfaceBiome,
diff --git a/src/main/java/electrosphere/util/FileUtils.java b/src/main/java/electrosphere/util/FileUtils.java
index c39c62f4..d1bf60e0 100644
--- a/src/main/java/electrosphere/util/FileUtils.java
+++ b/src/main/java/electrosphere/util/FileUtils.java
@@ -8,6 +8,8 @@ import electrosphere.game.data.creature.type.ai.AITreeDataSerializer;
import electrosphere.game.data.creature.type.movement.MovementSystem;
import electrosphere.game.data.creature.type.movement.MovementSystemSerializer;
import electrosphere.logger.LoggerInterface;
+import electrosphere.server.terrain.generation.noise.NoiseModuleSerializer;
+import electrosphere.server.terrain.generation.noise.NoiseSampler;
import electrosphere.util.annotation.AnnotationExclusionStrategy;
import java.awt.image.BufferedImage;
@@ -43,6 +45,7 @@ public class FileUtils {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MovementSystem.class, new MovementSystemSerializer());
gsonBuilder.registerTypeAdapter(AITreeData.class, new AITreeDataSerializer());
+ gsonBuilder.registerTypeAdapter(NoiseSampler.class, new NoiseModuleSerializer());
gsonBuilder.addDeserializationExclusionStrategy(new AnnotationExclusionStrategy());
gsonBuilder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy());
gson = gsonBuilder.create();
@@ -248,6 +251,23 @@ public class FileUtils {
}
return rVal;
}
+
+ /**
+ * Loads an object from the assets folder
+ * @param The type of object
+ * @param file The file to load from
+ * @param className The class of the object inside the file
+ * @return The file
+ */
+ public static T loadObjectFromFile(File file, Class className){
+ T rVal = null;
+ try {
+ rVal = gson.fromJson(Files.newBufferedReader(file.toPath()), className);
+ } catch (IOException ex) {
+ LoggerInterface.loggerFileIO.ERROR(ex);
+ }
+ return rVal;
+ }
/**
* Gets an sql script file as a string