Noise definitions in data files
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2024-11-18 18:25:29 -05:00
parent a44255060d
commit 3a9eb0591d
33 changed files with 1322 additions and 117 deletions

View File

@ -0,0 +1,7 @@
{
"name" : "test1",
"sampler" : {
"name" : "Invoke",
"target" : "test2"
}
}

View File

@ -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"
}
}
}
}

View File

@ -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

View File

@ -7,8 +7,8 @@
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<lwjgl.version>3.3.3</lwjgl.version>
<joml.version>1.9.19</joml.version>
<recast.version>1.5.7</recast.version>

View File

@ -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];

View File

@ -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<Entity> toReposition = new LinkedList<Entity>();
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);

View File

@ -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
///

View File

@ -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<SamplerFile> 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<SamplerFile> getSamplerFiles(){
return this.samplerDefinitions;
}
}

View File

@ -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<SamplerFile> readSamplerDefinitionFiles(String initialPath){
File initialDirectory = FileUtils.getAssetFile(initialPath);
List<SamplerFile> rVal = new LinkedList<SamplerFile>();
//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<SamplerFile> 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<SamplerFile> 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);
}
}
}
}

View File

@ -229,8 +229,8 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
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<TerrainMessage> {
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]);
}
}

View File

@ -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();
}

View File

@ -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

View File

@ -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<String,HeightmapGenerator> tagHeightmapMap = new HashMap<String,HeightmapGenerator>();
/**
* The map of generator tag to voxel generator
*/
Map<String,VoxelGenerator> tagVoxelMap = new HashMap<String,VoxelGenerator>();
/**
* 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;
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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<NoiseSampler> getChildren();
}

View File

@ -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<NoiseSampler> {
@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;
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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<NoiseSampler> getChildren(){
return Arrays.asList(new NoiseSampler[]{
first,
second,
});
}
}

View File

@ -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<NoiseSampler> getChildren(){
return Arrays.asList(new NoiseSampler[]{
min,
max,
source,
});
}
}

View File

@ -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;
}
}

View File

@ -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<NoiseSampler> getChildren(){
return Arrays.asList(new NoiseSampler[]{
x,
y,
z,
source,
});
}
}

View File

@ -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<NoiseSampler> getChildren(){
return Arrays.asList(new NoiseSampler[]{
first,
second,
});
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<NoiseSampler> getChildren(){
return Arrays.asList(new NoiseSampler[]{
invokeSampler
});
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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 <T> 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>T loadObjectFromFile(File file, Class<T> 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