fixes + generator work
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2024-11-13 15:49:52 -05:00
parent d06d5d4506
commit a32f6200cc
31 changed files with 780 additions and 275 deletions

View File

@ -125,6 +125,12 @@
"path" : "Models/items/weapons/katana1alt.glb" "path" : "Models/items/weapons/katana1alt.glb"
} }
}, },
"boneGroups" : [
{
"id" : "torso",
"boneNamesThirdPerson" : []
}
],
"collidable": { "collidable": {
"type" : "CUBE", "type" : "CUBE",
"dimension1" : 0.04, "dimension1" : 0.04,

View File

@ -102,16 +102,13 @@ export const TestGen: ChunkGenerator = {
engine: Engine engine: Engine
): VoxelFunction => { ): VoxelFunction => {
const rVal = ( const rVal = (
voxel: GeneratedVoxel,
worldX: number, worldY: number, worldZ: number, worldX: number, worldY: number, worldZ: number,
chunkX: number, chunkY: number, chunkZ: number, chunkX: number, chunkY: number, chunkZ: number,
stride: number, stride: number,
surfaceHeight: number, surfaceHeight: number,
surfaceBiome: BiomeData surfaceBiome: BiomeData
): GeneratedVoxel => { ): void => {
let rVal: GeneratedVoxel = {
type: 0,
weight: -1,
}
const realX = voxelToReal(chunkX,worldX) const realX = voxelToReal(chunkX,worldX)
const realY = voxelToReal(chunkY,worldY) const realY = voxelToReal(chunkY,worldY)
@ -122,8 +119,8 @@ export const TestGen: ChunkGenerator = {
const surfacePercent = (surfaceHeight - realY) / strideMultiplier const surfacePercent = (surfaceHeight - realY) / strideMultiplier
if(heightDiff < -strideMultiplier){ if(heightDiff < -strideMultiplier){
return getSubsurfaceVoxel( getSubsurfaceVoxel(
rVal, voxel,
worldX, worldY, worldZ, worldX, worldY, worldZ,
chunkX, chunkY, chunkZ, chunkX, chunkY, chunkZ,
realX, realY, realZ, realX, realY, realZ,
@ -132,8 +129,8 @@ export const TestGen: ChunkGenerator = {
surfaceBiome surfaceBiome
); );
} else if(heightDiff > 0) { } else if(heightDiff > 0) {
return getOverSurfaceVoxel( getOverSurfaceVoxel(
rVal, voxel,
worldX, worldY, worldZ, worldX, worldY, worldZ,
chunkX, chunkY, chunkZ, chunkX, chunkY, chunkZ,
realX, realY, realZ,- realX, realY, realZ,-
@ -142,8 +139,8 @@ export const TestGen: ChunkGenerator = {
surfaceBiome surfaceBiome
); );
} else { } else {
return getSurfaceVoxel( getSurfaceVoxel(
rVal, voxel,
worldX, worldY, worldZ, worldX, worldY, worldZ,
chunkX, chunkY, chunkZ, chunkX, chunkY, chunkZ,
realX, realY, realZ, realX, realY, realZ,

View File

@ -25,6 +25,7 @@ export interface GeneratedVoxel {
/** /**
* Gets a voxel for a given position * Gets a voxel for a given position
* @param voxel The voxel to fill with values
* @param worldX The world x coordinate of the chunk * @param worldX The world x coordinate of the chunk
* @param worldY The world y coordinate of the chunk * @param worldY The world y coordinate of the chunk
* @param worldZ The world z coordinate of the chunk * @param worldZ The world z coordinate of the chunk
@ -38,12 +39,13 @@ export interface GeneratedVoxel {
* @returns The voxel at the provided position * @returns The voxel at the provided position
*/ */
export type VoxelFunction = ( export type VoxelFunction = (
voxel: GeneratedVoxel,
worldX: number, worldY: number, worldZ: number, worldX: number, worldY: number, worldZ: number,
chunkX: number, chunkY: number, chunkZ: number, chunkX: number, chunkY: number, chunkZ: number,
stride: number, stride: number,
surfaceHeight: number, surfaceHeight: number,
surfaceBiome: BiomeData surfaceBiome: BiomeData
) => GeneratedVoxel; ) => void;

View File

@ -130,7 +130,7 @@ void main(){
vec3 textureColor = getColor(texPlane1, texPlane2, texPlane3, norm, samplerIndexVec, samplerRatioVec, material); vec3 textureColor = getColor(texPlane1, texPlane2, texPlane3, norm, samplerIndexVec, samplerRatioVec, material);
//shadow //shadow
float shadow = ShadowCalculation(FragPosLightSpace, normalize(-directLight.direction), norm); float shadow = ShadowCalculation(FragPosLightSpace, normalize(-directLight.direction), -norm);
// //
//point light calculations //point light calculations
@ -350,5 +350,5 @@ float ShadowCalculation(vec4 fragPosLightSpace, vec3 lightDir, vec3 normal){
// shadow = currentDepth; // shadow = currentDepth;
return shadow; return clamp(1.0 - shadow, 0.0, 0.7);
} }

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file #maven.buildNumber.plugin properties file
#Sun Nov 10 17:05:45 EST 2024 #Wed Nov 13 13:33:32 EST 2024
buildNumber=378 buildNumber=381

View File

@ -10,4 +10,13 @@ Different cases to consider when working on a biome generation system
- Superstructure biomes (World tree, massive mountains, etc) - Superstructure biomes (World tree, massive mountains, etc)
- Oceans - Oceans
- Lakes - Lakes
- Rivers - Rivers
- Large scale overhangs
Operations we might want to do
- Placing things on the surface
- Solve navgation at the macro scale

View File

@ -60,3 +60,14 @@ store fire pixels as "good for fire civilization"
when a pixel changes biome, must evict it from all these structures and recalculate which ones it should be within (except maybe zone) when a pixel changes biome, must evict it from all these structures and recalculate which ones it should be within (except maybe zone)
Solve for the heightmap at a given x-z for the surface
Then, have "cells" placed above the surface based on the base height offset of the biome
These cells can be populated with larger structures

View File

@ -1001,6 +1001,18 @@ Passing chunk data between nodes
Fix homogenous value propagating from chunk gen to client cache Fix homogenous value propagating from chunk gen to client cache
Fix homogenous value joining on client Fix homogenous value joining on client
(11/12/2024)
Work to optimize javascript chunk generators
Solve curve SDF
(11/13/2024)
Fix shadows on terrain
Work on an anime-style mountain generator
Work on making chunk reloading less obvious
Fix default chunk generator
Fix unit test
Fix missing data on katana item
# TODO # TODO

View File

@ -51,6 +51,11 @@
"data" : [ "data" : [
"data" "data"
] ]
},
{
"messageName" : "EditorSwap",
"description" : "Swaps between the editor entity and the non-editor entity",
"data" : []
} }
] ]
} }

View File

@ -305,7 +305,7 @@
<dependency> <dependency>
<groupId>io.github.studiorailgun</groupId> <groupId>io.github.studiorailgun</groupId>
<artifactId>MathUtils</artifactId> <artifactId>MathUtils</artifactId>
<version>1.1.0</version> <version>1.3.0</version>
</dependency> </dependency>
<!--DataStructures--> <!--DataStructures-->

View File

@ -184,31 +184,6 @@ public class ClientDrawCellManager {
if(!updatedLastFrame && !this.initialized){ if(!updatedLastFrame && !this.initialized){
this.initialized = true; this.initialized = true;
} }
// if(!updatedLastFrame){
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells");
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, HALF_RES_LOD, distCache);
// Globals.profiler.endCpuSample();
// }
// if(!updatedLastFrame){
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells");
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, QUARTER_RES_LOD, distCache);
// Globals.profiler.endCpuSample();
// }
// if(!updatedLastFrame){
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - quarter res cells");
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, EIGHTH_RES_LOD, distCache);
// Globals.profiler.endCpuSample();
// }
// if(!updatedLastFrame){
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - eighth res cells");
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerWorldPos, evaluationMap, SIXTEENTH_RES_LOD, distCache);
// Globals.profiler.endCpuSample();
// }
// if(!updatedLastFrame){
// Globals.profiler.beginCpuSample("ClientDrawCellManager.update - all res cells");
// updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, ALL_RES_LOD);
// Globals.profiler.endCpuSample();
// }
} }
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
} }

View File

@ -11,6 +11,7 @@ import electrosphere.collision.CollisionEngine;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.types.terrain.TerrainChunk; import electrosphere.entity.types.terrain.TerrainChunk;
import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration;
import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData;
@ -23,6 +24,11 @@ import electrosphere.util.math.GeomUtils;
*/ */
public class DrawCell { public class DrawCell {
/**
* Number of frames to wait before destroying the chunk entity
*/
public static final int FRAMES_TO_WAIT_BEFORE_DESTRUCTION = 15;
/** /**
* Enum for the different faces of a draw cell -- used when filling in data for higher LOD faces * Enum for the different faces of a draw cell -- used when filling in data for higher LOD faces
*/ */
@ -188,9 +194,19 @@ public class DrawCell {
*/ */
public void destroy(){ public void destroy(){
if(modelEntity != null){ if(modelEntity != null){
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); Globals.clientScene.registerBehaviorTree(new BehaviorTree(){
collisionEngine.destroyPhysics(modelEntity); int framesSimulated = 0;
ClientEntityUtils.destroyEntity(modelEntity); public void simulate(float deltaTime) {
if(framesSimulated < FRAMES_TO_WAIT_BEFORE_DESTRUCTION){
framesSimulated++;
} else {
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
collisionEngine.destroyPhysics(modelEntity);
ClientEntityUtils.destroyEntity(modelEntity);
Globals.clientScene.deregisterBehaviorTree(this);
}
}
});
} }
} }

View File

@ -57,6 +57,9 @@ public class ClientEntityUtils {
if(Globals.clientSceneWrapper != null){ if(Globals.clientSceneWrapper != null){
Globals.clientSceneWrapper.getScene().deregisterEntity(entity); Globals.clientSceneWrapper.getScene().deregisterEntity(entity);
Globals.clientSceneWrapper.deregisterTranslationMapping(entity); Globals.clientSceneWrapper.deregisterTranslationMapping(entity);
if(Globals.clientSceneWrapper.getCollisionEngine() != null){
Globals.clientSceneWrapper.getCollisionEngine().destroyPhysics(entity);
}
} }
if(Globals.clientScene != null){ if(Globals.clientScene != null){
Globals.clientScene.deregisterEntity(entity); Globals.clientScene.deregisterEntity(entity);

View File

@ -11,7 +11,6 @@ import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.btree.BehaviorTree;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.server.datacell.utils.DataCellSearchUtils; import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils; import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
@ -46,7 +45,6 @@ public class ServerPhysicsSyncTree implements BehaviorTree {
Quaterniond rotation = EntityUtils.getRotation(parent); Quaterniond rotation = EntityUtils.getRotation(parent);
DBody body = PhysicsEntityUtils.getDBody(parent); DBody body = PhysicsEntityUtils.getDBody(parent);
if(body == null){ if(body == null){
LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Running physics sync tree on entity that does not have a physics body!"));
} else { } else {
//velocities //velocities
Vector3d linearVel = PhysicsUtils.odeVecToJomlVec(body.getLinearVel()); Vector3d linearVel = PhysicsUtils.odeVecToJomlVec(body.getLinearVel());

View File

@ -63,7 +63,7 @@ public class TerrainChunk {
} }
rVal.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true); rVal.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true);
} else { } else {
LoggerInterface.loggerEngine.WARNING("Finished generating terrain polygons; however, entity has already been deleted."); LoggerInterface.loggerEngine.DEBUG("Finished generating terrain polygons; however, entity has already been deleted.");
} }
} catch (Error e){ } catch (Error e){
LoggerInterface.loggerEngine.ERROR(e); LoggerInterface.loggerEngine.ERROR(e);

View File

@ -251,7 +251,11 @@ public class Actor {
} }
} }
} else if(this.boneGroups == null){ } else if(this.boneGroups == null){
LoggerInterface.loggerRenderer.WARNING("Trying to play animation on pose actor that uses bone groups, but the actor's bone group isn't defined!"); LoggerInterface.loggerRenderer.WARNING(
"Trying to play animation on Actor that uses bone groups, but the Actor's bone group isn't defined!\n" +
"Model path: " + modelPath + "\n" +
"Animation name: " + animationName + "\n"
);
} }

View File

@ -31,17 +31,22 @@ public class ShadowMapPipeline implements RenderPipeline {
/** /**
* The eye of the camera that is used to render the shadow map * The eye of the camera that is used to render the shadow map
*/ */
Vector3d cameraEye = new Vector3d(-1,10,-5.5); Vector3d cameraEye = new Vector3d(-1,20,-5.5);
/** /**
* The near plane distance * The near plane distance
*/ */
float nearPlane = 0.1f; float nearPlane = 1f;
/** /**
* The far plane * The far plane
*/ */
float farPlane = 25f; float farPlane = 40f;
/**
* Sides of the orthagonal box
*/
float sideLength = 50f;
/** /**
* Sets whether the far plane should update based on camera location or not * Sets whether the far plane should update based on camera location or not
@ -68,9 +73,10 @@ public class ShadowMapPipeline implements RenderPipeline {
float eyeX = (float)cameraEye.x; float eyeX = (float)cameraEye.x;
float eyeY = (float)cameraEye.y; float eyeY = (float)cameraEye.y;
float eyeZ = (float)cameraEye.z; float eyeZ = (float)cameraEye.z;
float eyeDist = (float)cameraEye.length(); // float eyeDist = (float)cameraEye.length();
// float farPlane = eyeDist + 10.0f; // float farPlane = eyeDist + 10.0f;
float sidesMagnitude = (float)Math.sqrt(eyeDist); // float sidesMagnitude = (float)Math.sqrt(eyeDist);
float sidesMagnitude = sideLength;
//set matrices for light render //set matrices for light render
Matrix4f lightProjection = new Matrix4f().setOrtho(-sidesMagnitude, sidesMagnitude, -sidesMagnitude, sidesMagnitude, nearPlane, farPlane);//glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); Matrix4f lightProjection = new Matrix4f().setOrtho(-sidesMagnitude, sidesMagnitude, -sidesMagnitude, sidesMagnitude, nearPlane, farPlane);//glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
Matrix4f lightView = new Matrix4f().setLookAt( Matrix4f lightView = new Matrix4f().setLookAt(

View File

@ -48,11 +48,11 @@ public class ServerContentGenerator {
); );
} }
//setup // //setup
Random random = new Random(randomizer); // Random random = new Random(randomizer);
//generate foliage // //generate foliage
// BiomeData biome = null; // BiomeData biome = null;
// if(realm.getServerWorldData() != null && realm.getServerWorldData().getServerTerrainManager() != null && realm.getServerWorldData().getServerTerrainManager().getModel() != null){ // if(realm.getServerWorldData() != null && realm.getServerWorldData().getServerTerrainManager() != null && realm.getServerWorldData().getServerTerrainManager().getModel() != null){
// biome = realm.getServerWorldData().getServerTerrainManager().getModel().getSurfaceBiome(worldPos.x, worldPos.z); // biome = realm.getServerWorldData().getServerTerrainManager().getModel().getSurfaceBiome(worldPos.x, worldPos.z);

View File

@ -224,7 +224,11 @@ public class PoseActor {
} }
} }
} else if(this.boneGroups == null){ } else if(this.boneGroups == null){
LoggerInterface.loggerRenderer.WARNING("Trying to play animation on pose actor that uses bone groups, but the actor's bone group isn't defined!"); LoggerInterface.loggerRenderer.WARNING(
"Trying to play animation on PoseActor that uses bone groups, but the PoseActor's bone group isn't defined!\n" +
"Model path: " + modelPath + "\n" +
"Animation name: " + animationName + "\n"
);
} }

View File

@ -26,19 +26,19 @@ public class DefaultChunkGenerator implements ChunkGenerator {
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) { public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) {
//Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap. //Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap.
//Hence, width should actually be chunk dimension + 1 //Hence, width should actually be chunk dimension + 1
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; 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_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
for(int inc = 0; inc < ServerTerrainChunk.CHUNK_DIMENSION; inc++){ for(int inc = 0; inc < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; inc++){
for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DIMENSION; weightX++){ for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){
for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DIMENSION; weightZ++){ for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){
weights[weightX][inc][weightZ] = -1; weights[weightX][inc][weightZ] = -1;
values[weightX][inc][weightZ] = 0; values[weightX][inc][weightZ] = 0;
} }
} }
} }
if(worldY < 1){ if(worldY < 1){
for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DIMENSION; weightX++){ for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){
for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DIMENSION; weightZ++){ for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){
weights[weightX][0][weightZ] = 0.1f; weights[weightX][0][weightZ] = 0.1f;
values[weightX][0][weightZ] = baseVoxelId; values[weightX][0][weightZ] = baseVoxelId;
} }

View File

@ -5,7 +5,7 @@ import java.util.Map;
import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cache.ChunkData;
// import org.graalvm.polyglot.Value; import org.graalvm.polyglot.Value;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.game.data.biome.BiomeData; import electrosphere.game.data.biome.BiomeData;
@ -17,6 +17,10 @@ import electrosphere.server.terrain.generation.heightmap.HillsGen;
import electrosphere.server.terrain.generation.heightmap.PlainsGen; import electrosphere.server.terrain.generation.heightmap.PlainsGen;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel; import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel;
import electrosphere.server.terrain.generation.interfaces.GenerationContext;
import electrosphere.server.terrain.generation.voxelphase.AnimeMountainsGen;
import electrosphere.server.terrain.generation.voxelphase.HillsVoxelGen;
import electrosphere.server.terrain.generation.voxelphase.VoxelGenerator;
import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.models.TerrainModel; import electrosphere.server.terrain.models.TerrainModel;
@ -61,9 +65,14 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
ServerWorldData serverWorldData; ServerWorldData serverWorldData;
/** /**
* Map of generator tag to the generator * The map of generator tag to the heightmap generator
*/ */
Map<String,HeightmapGenerator> tagGeneratorMap = new HashMap<String,HeightmapGenerator>(); 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 * Tracks whether to use javascript generation or not
@ -75,9 +84,11 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
*/ */
public TestGenerationChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){ public TestGenerationChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){
this.serverWorldData = serverWorldData; this.serverWorldData = serverWorldData;
registerGenerator(new EmptySkyGen()); registerHeightmapGenerator(new EmptySkyGen());
registerGenerator(new HillsGen()); registerHeightmapGenerator(new HillsGen());
registerGenerator(new PlainsGen()); registerHeightmapGenerator(new PlainsGen());
registerVoxelGenerator(new HillsVoxelGen());
registerVoxelGenerator(new AnimeMountainsGen());
this.useJavascript = useJavascript; this.useJavascript = useJavascript;
} }
@ -85,27 +96,31 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
* Registers a heightmap generator * Registers a heightmap generator
* @param generator The heightmap generator * @param generator The heightmap generator
*/ */
private void registerGenerator(HeightmapGenerator generator){ private void registerHeightmapGenerator(HeightmapGenerator generator){
tagGeneratorMap.put(generator.getTag(),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 @Override
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) { public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride) {
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.generateChunk"); Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.generateChunk");
ServerTerrainChunk rVal = null; ServerTerrainChunk rVal = new ServerTerrainChunk(worldX, worldY, worldZ);
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];; float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];;
int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
int firstType = -2;
boolean homogenous = true;
try { try {
//actual generation algo
//biome of the current chunk //biome of the current chunk
BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ); BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ);
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
if(heightmapGen == null){ if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
} }
@ -129,39 +144,58 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
} }
} }
VoxelGenerator voxelGenerator = this.tagVoxelMap.get("hills");
if(this.useJavascript){ if(this.useJavascript){
// Globals.scriptEngine.executeSynchronously(() -> { Globals.scriptEngine.executeSynchronously(() -> {
// Value getVoxelFunc = Globals.scriptEngine.invokeEngineMember("chunkGeneratorManager", "getVoxelFunction", SCRIPT_GEN_TEST_TAG); int firstType = -2;
// for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ boolean homogenous = true;
// Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice"); GeneratedVoxel voxel = new GeneratedVoxel();
// for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ Value getVoxelFunc = Globals.scriptEngine.invokeEngineMember("chunkGeneratorManager", "getVoxelFunction", SCRIPT_GEN_TEST_TAG);
// for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ for(int x = 0; x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; x++){
// int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice");
// int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); for(int y = 0; y < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; y++){
// int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){
// int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
// int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
// int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
// Value result = getVoxelFunc.execute( int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
// finalWorldX, finalWorldY, finalWorldZ, int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
// finalChunkX, finalChunkY, finalChunkZ, int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
// stride, getVoxelFunc.execute(
// heightfield[x][z], voxel,
// surfaceBiome finalWorldX, finalWorldY, finalWorldZ,
// ); finalChunkX, finalChunkY, finalChunkZ,
// if(result != null){ stride,
// weights[x][y][z] = result.getMember("weight").asFloat(); heightfield[x][z],
// values[x][y][z] = result.getMember("type").asInt(); surfaceBiome
// } );
// } weights[x][y][z] = voxel.weight;
// } values[x][y][z] = voxel.type;
// Globals.profiler.endCpuSample(); 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 { } else {
int firstType = -2;
boolean homogenous = true;
GeneratedVoxel voxel = new GeneratedVoxel(); GeneratedVoxel voxel = new GeneratedVoxel();
GenerationContext generationContext = new GenerationContext();
generationContext.setServerWorldData(serverWorldData);
for(int x = 0; x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; x++){ 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 y = 0; y < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; y++){
for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){
int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION); int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
@ -170,13 +204,14 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION; int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
this.getVoxel( voxelGenerator.getVoxel(
voxel, voxel,
finalWorldX, finalWorldY, finalWorldZ, finalWorldX, finalWorldY, finalWorldZ,
finalChunkX, finalChunkY, finalChunkZ, finalChunkX, finalChunkY, finalChunkZ,
stride, stride,
heightfield[x][z], heightfield[x][z],
surfaceBiome surfaceBiome,
generationContext
); );
if(voxel != null){ if(voxel != null){
weights[x][y][z] = voxel.weight; weights[x][y][z] = voxel.weight;
@ -189,13 +224,18 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
} }
} }
} }
Globals.profiler.endCpuSample();
} }
if(homogenous){
rVal.setHomogenousValue(firstType);
} else {
rVal.setHomogenousValue(ChunkData.NOT_HOMOGENOUS);
}
rVal.setWeights(weights);
rVal.setValues(values);
} }
} catch(Exception ex){ } catch(Exception ex){
ex.printStackTrace(); ex.printStackTrace();
} }
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, homogenous ? firstType : ChunkData.NOT_HOMOGENOUS, weights, values);
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
return rVal; return rVal;
} }
@ -205,7 +245,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ); BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ);
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
if(heightmapGen == null){ if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
} }
@ -222,140 +262,4 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
this.terrainModel = model; this.terrainModel = model;
} }
/**
* Gets the value for a chunk
* @param voxel The voxel to fill
* @param worldX The world x pos
* @param worldY The world y pos
* @param worldZ The world z pos
* @param chunkX The chunk x pos
* @param chunkY The chunk y pos
* @param chunkZ The chunk z pos
* @param stride The stride of the data
* @param surfaceHeight The height of the surface at x,z
* @param surfaceBiome The surface biome of the chunk
*/
private void getVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
int stride,
float surfaceHeight,
BiomeData surfaceBiome
){
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.getChunkValue");
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag());
if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
}
double realX = this.serverWorldData.convertVoxelToRealSpace(chunkX,worldX);
double realY = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldY);
double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkZ,worldZ);
double strideMultiplier = Math.pow(2,stride);
double heightDiff = realY - surfaceHeight;
double surfacePercent = TestGenerationChunkGenerator.getSurfaceWeight(surfaceHeight,realY,strideMultiplier);
Globals.profiler.endCpuSample();
if(heightDiff < -strideMultiplier * SURFACE_VOXEL_WIDTH){
getSubsurfaceVoxel(
voxel,
worldX, worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,
surfacePercent,
surfaceHeight,
surfaceBiome
);
} else if(heightDiff > 0) {
getOverSurfaceVoxel(
voxel,
worldX, worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,-
surfacePercent,
surfaceHeight,
surfaceBiome
);
} else {
getSurfaceVoxel(
voxel,
worldX, worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,
surfacePercent,
surfaceHeight,
surfaceBiome
);
}
}
/**
* Gets the voxel on the surface
* @return The voxel
*/
private void getSurfaceVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
BiomeData surfaceBiome
){
voxel.weight = (float)surfacePercent * 2 - 1;
voxel.type = 2;
}
/**
* Gets the voxel below the surface
* @return The voxel
*/
private void getSubsurfaceVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
BiomeData surfaceBiome
){
if(realY < surfaceHeight - 5){
voxel.weight = 1;
voxel.type = 6;
} else {
voxel.weight = 1;
voxel.type = 1;
}
}
/**
* Gets the voxel above the service
* @return The voxel
*/
private void getOverSurfaceVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
BiomeData surfaceBiome
){
voxel.weight = -1;
voxel.type = 0;
}
/**
* Calculates the weight of a voxel on the surface based on the surface height, the position of the voxel, and the stride multiplier
* @param surfaceHeight The surface height
* @param realPosY The position of the voxel
* @param strideMultiplier The stride multiplier
* @return The weight of the voxel
*/
protected static double getSurfaceWeight(double surfaceHeight, double realPosY, double strideMultiplier){
return ((surfaceHeight - realPosY) / strideMultiplier);
}
} }

View File

@ -52,7 +52,7 @@ public class HillsGen implements HeightmapGenerator {
Globals.profiler.beginAggregateCpuSample("HillsGen.getHeight"); Globals.profiler.beginAggregateCpuSample("HillsGen.getHeight");
double scaledX = x * POSITION_SCALE; double scaledX = x * POSITION_SCALE;
double scaledY = y * POSITION_SCALE; double scaledY = y * POSITION_SCALE;
float rVal = gradientHeight(SEED, scaledX, scaledY) * VERTICAL_SCALE + HEIGHT_OFFSET; float rVal = HillsGen.gradientHeight(SEED, scaledX, scaledY) * VERTICAL_SCALE + HEIGHT_OFFSET;
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
return rVal; return rVal;
} }

View File

@ -0,0 +1,26 @@
package electrosphere.server.terrain.generation.heightmap;
import electrosphere.util.noise.OpenSimplex2S;
/**
* Generates a mild plain that would look like a seafloor
*/
public class SeaFloorGen implements HeightmapGenerator {
/**
* The scale of the noise
*/
float noiseScale = 0.01f;
@Override
public float getHeight(long SEED, double x, double y) {
float noise = (float)OpenSimplex2S.noise2_ImproveX(SEED, x * noiseScale, y * noiseScale);
return noise;
}
@Override
public String getTag() {
return "seafloor";
}
}

View File

@ -0,0 +1,37 @@
package electrosphere.server.terrain.generation.interfaces;
import electrosphere.game.server.world.ServerWorldData;
/**
* The context the generation is happening in. Stores things like biomes to interpolate between.
*/
public class GenerationContext {
/**
* The world data for the realm we're generating for
*/
ServerWorldData serverWorldData;
/**
* Constructor
*/
public GenerationContext(){
}
/**
* Gets the world data for the server
* @return The world data
*/
public ServerWorldData getServerWorldData(){
return serverWorldData;
}
/**
* Sets the world data for the context
* @param serverWorldData The world data
*/
public void setServerWorldData(ServerWorldData serverWorldData){
this.serverWorldData = serverWorldData;
}
}

View File

@ -0,0 +1,259 @@
package electrosphere.server.terrain.generation.voxelphase;
import electrosphere.engine.Globals;
import electrosphere.game.data.biome.BiomeData;
import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel;
import electrosphere.server.terrain.generation.interfaces.GenerationContext;
import io.github.studiorailgun.MathUtils;
import io.github.studiorailgun.RandUtils;
/**
* Generates anime-style mountains
*/
public class AnimeMountainsGen implements VoxelGenerator {
/**
* The width of the surface in number of voxels
*/
public static final int SURFACE_VOXEL_WIDTH = 2;
/**
* Size of the large cells that generate mountains
*/
public static final int LARGE_CELL_SIZE = 1024;
/**
* How much to sink the cell into the surface
*/
public static final int CELL_VERTICAL_OFFSET = - (int)((LARGE_CELL_SIZE) * 2.5 / 5.0);
/**
* The variance in scale of mountains
*/
public static final double MOUNTAIN_SCALE_VARIANCE = 0.2;
/**
* The width of the mountain
*/
public static final double MOUNTAIN_WIDTH = 0.4;
/**
* The center x point of the cell
*/
public static final double CELL_CENTER_X = 0.5;
/**
* The center y point of the cell
*/
public static final double CELL_CENTER_Y = 0.5;
/**
* The center z point of the cell
*/
public static final double CELL_CENTER_Z = 0.5;
/**
* Amount the vertical rotation offset can vary
*/
public static final double VERTICAL_ROTATION_OFFSET_VARIANCE = 0.2;
@Override
public String getTag() {
return "animeMountain";
}
@Override
public void getVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
int stride, float surfaceHeight,
BiomeData surfaceBiome,
GenerationContext generationContext
) {
Globals.profiler.beginAggregateCpuSample("AnimeMountainsGen.getVoxel");
double realX = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkX,worldX);
double realY = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkY,worldY);
double realZ = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkZ,worldZ);
double strideMultiplier = Math.pow(2,stride);
double heightDiff = realY - surfaceHeight;
double surfacePercent = AnimeMountainsGen.getSurfaceWeight(surfaceHeight,realY,strideMultiplier);
Globals.profiler.endCpuSample();
if(heightDiff < -strideMultiplier * SURFACE_VOXEL_WIDTH){
this.getSubsurfaceVoxel(
voxel,
worldX, worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,
surfacePercent,
surfaceHeight,
surfaceBiome
);
} else if(heightDiff > 0) {
this.getOverSurfaceVoxel(
voxel,
worldX, worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,-
surfacePercent,
surfaceHeight,
surfaceBiome
);
} else {
this.getSurfaceVoxel(
voxel,
worldX, worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,
surfacePercent,
surfaceHeight,
surfaceBiome
);
}
}
/**
* Gets the voxel on the surface
* @return The voxel
*/
private void getSurfaceVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
BiomeData surfaceBiome
){
voxel.weight = (float)surfacePercent * 2 - 1;
voxel.type = 2;
}
/**
* Gets the voxel below the surface
* @return The voxel
*/
private void getSubsurfaceVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
BiomeData surfaceBiome
){
if(realY < surfaceHeight - 5){
voxel.weight = 1;
voxel.type = 6;
} else {
voxel.weight = 1;
voxel.type = 1;
}
}
/**
* Gets the voxel above the service
* @return The voxel
*/
private void getOverSurfaceVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
BiomeData surfaceBiome
){
//default voxel value
voxel.weight = -1;
voxel.type = 0;
//get the height above the surface
double heightAboveBaseSurface = realY - surfaceBiome.getSurfaceGenerationParams().getHeightOffset();
double offsetHeight = heightAboveBaseSurface - CELL_VERTICAL_OFFSET;
//calculated floored values
double x_i = Math.floor(realX / LARGE_CELL_SIZE);
double y_i = Math.floor(offsetHeight / LARGE_CELL_SIZE);
double z_i = Math.floor(realZ / LARGE_CELL_SIZE);
//only generate if you're on the first cell from the surface
if(y_i < 1){
//calculate values from which cell we're in
double rotationTheta = RandUtils.rand(x_i, z_i, 0) * Math.PI * 2;
// double mountainScale = MathUtils.rand(x_i, z_i, 1) * MOUNTAIN_SCALE_VARIANCE + (1.0 - (MOUNTAIN_SCALE_VARIANCE / 2.0));
// double verticalRotationOffset = MathUtils.rand(x_i, z_i, 2) * VERTICAL_ROTATION_OFFSET_VARIANCE;
//remainders of the point coordinates
double x_r = (realX / LARGE_CELL_SIZE) - x_i;
double y_r = (offsetHeight / LARGE_CELL_SIZE) - y_i;
double z_r = (realZ / LARGE_CELL_SIZE) - z_i;
//get the center of the cell
double cellCenterX = CELL_CENTER_X;
double cellCenterY = CELL_CENTER_Y;
double cellCenterZ = CELL_CENTER_Z;
//delta positions
double deltaX = (x_r - cellCenterX);
double deltaY = (y_r - cellCenterY);
double deltaZ = (z_r - cellCenterZ);
//rotate around the center
double rotX = deltaX * Math.cos(rotationTheta) + deltaZ * Math.sin(rotationTheta);
double rotY = deltaY;
double rotZ = - deltaX * Math.sin(rotationTheta) + deltaZ * Math.cos(rotationTheta);
//roation along the arctre
//ranged [0,2PI]
double rotationAlongArc = Math.atan2(rotY,rotX);
//ranges [0,1]
// double rotationPercent = rotationAlongArc / (Math.PI * 2.0);
//calculate values from where we are WITHIN the cell
// double voxelGamma = Math.atan2(
// x_r - cellCenterX,
// z_r - cellCenterZ
// ) + Math.PI * 2.0;
// double voxelTheta = Math.atan2(y_r - cellCenterY, MathUtils.dist(x_r,z_r,cellCenterX,cellCenterZ));
double distanceFromCenter = MathUtils.dist(rotX,rotY,0,0);
double distanceFromArcCenter = Math.sqrt(rotZ*rotZ);
//target distance from center
double targetDistanceFromArcCenter = Math.sin(rotationAlongArc) * 0.3;
double targetArcRadius = 0.6;
double targetArcRadiusWidth = Math.sin(rotationAlongArc) * 0.03;
if(
// voxelTheta > 0 &&
// (voxelGamma - Math.PI/2) < Math.PI &&
// voxelTheta > Math.max(Math.sin(voxelGamma),0) &&
// voxelTheta > 0.4 * Math.max(Math.sin(voxelGamma),0) &&
distanceFromCenter > targetArcRadius - targetArcRadiusWidth &&
distanceFromCenter < targetArcRadius + targetArcRadiusWidth &&
distanceFromArcCenter < targetDistanceFromArcCenter
){
voxel.weight = 1.0f;
voxel.type = 1;
}
}
}
/**
* Calculates the weight of a voxel on the surface based on the surface height, the position of the voxel, and the stride multiplier
* @param surfaceHeight The surface height
* @param realPosY The position of the voxel
* @param strideMultiplier The stride multiplier
* @return The weight of the voxel
*/
private static double getSurfaceWeight(double surfaceHeight, double realPosY, double strideMultiplier){
return ((surfaceHeight - realPosY) / strideMultiplier);
}
}

View File

@ -0,0 +1,146 @@
package electrosphere.server.terrain.generation.voxelphase;
import electrosphere.engine.Globals;
import electrosphere.game.data.biome.BiomeData;
import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel;
import electrosphere.server.terrain.generation.interfaces.GenerationContext;
/**
* Generates voxels for a hill
*/
public class HillsVoxelGen implements VoxelGenerator {
/**
* The width of the surface in number of voxels
*/
public static final int SURFACE_VOXEL_WIDTH = 2;
@Override
public String getTag(){
return "hills";
}
@Override
public void getVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
int stride,
float surfaceHeight,
BiomeData surfaceBiome,
GenerationContext generationContext
){
Globals.profiler.beginAggregateCpuSample("HillsVoxelGen.getVoxel");
double realX = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkX,worldX);
double realY = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkY,worldY);
double realZ = generationContext.getServerWorldData().convertVoxelToRealSpace(chunkZ,worldZ);
double strideMultiplier = Math.pow(2,stride);
double heightDiff = realY - surfaceHeight;
double surfacePercent = HillsVoxelGen.getSurfaceWeight(surfaceHeight,realY,strideMultiplier);
Globals.profiler.endCpuSample();
if(heightDiff < -strideMultiplier * SURFACE_VOXEL_WIDTH){
this.getSubsurfaceVoxel(
voxel,
worldX, worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,
surfacePercent,
surfaceHeight,
surfaceBiome
);
} else if(heightDiff > 0) {
this.getOverSurfaceVoxel(
voxel,
worldX, worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,-
surfacePercent,
surfaceHeight,
surfaceBiome
);
} else {
this.getSurfaceVoxel(
voxel,
worldX, worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,
surfacePercent,
surfaceHeight,
surfaceBiome
);
}
}
/**
* Gets the voxel on the surface
* @return The voxel
*/
private void getSurfaceVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
BiomeData surfaceBiome
){
voxel.weight = (float)surfacePercent * 2 - 1;
voxel.type = 2;
}
/**
* Gets the voxel below the surface
* @return The voxel
*/
private void getSubsurfaceVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
BiomeData surfaceBiome
){
if(realY < surfaceHeight - 5){
voxel.weight = 1;
voxel.type = 6;
} else {
voxel.weight = 1;
voxel.type = 1;
}
}
/**
* Gets the voxel above the service
* @return The voxel
*/
private void getOverSurfaceVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
BiomeData surfaceBiome
){
voxel.weight = -1;
voxel.type = 0;
}
/**
* Calculates the weight of a voxel on the surface based on the surface height, the position of the voxel, and the stride multiplier
* @param surfaceHeight The surface height
* @param realPosY The position of the voxel
* @param strideMultiplier The stride multiplier
* @return The weight of the voxel
*/
protected static double getSurfaceWeight(double surfaceHeight, double realPosY, double strideMultiplier){
return ((surfaceHeight - realPosY) / strideMultiplier);
}
}

View File

@ -0,0 +1,45 @@
package electrosphere.server.terrain.generation.voxelphase;
import electrosphere.game.data.biome.BiomeData;
import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel;
import electrosphere.server.terrain.generation.interfaces.GenerationContext;
/**
* Used for generating voxels
*/
public interface VoxelGenerator {
/**
* Gets the tag of the generator
* @return The tag
*/
public String getTag();
/**
* Gets the value for a chunk
* @param voxel The voxel to fill
* @param worldX The world x pos
* @param worldY The world y pos
* @param worldZ The world z pos
* @param chunkX The chunk x pos
* @param chunkY The chunk y pos
* @param chunkZ The chunk z pos
* @param stride The stride of the data
* @param surfaceHeight The height of the surface at x,z
* @param surfaceBiome The surface biome of the chunk
* @param generationContext The generation context
*/
public void getVoxel(
GeneratedVoxel voxel,
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
int stride,
float surfaceHeight,
BiomeData surfaceBiome,
GenerationContext generationContext
);
}

View File

@ -80,6 +80,18 @@ public class ServerTerrainChunk {
this.values = values; this.values = values;
} }
/**
* Constructor
* @param worldX The world position x coordinate
* @param worldY The world position y coordinate
* @param worldZ The world position z coordinate
*/
public ServerTerrainChunk(int worldX, int worldY, int worldZ){
this.worldX = worldX;
this.worldY = worldY;
this.worldZ = worldZ;
}
/** /**
* Gets the world position x coordinate * Gets the world position x coordinate
* @return The x coordinate * @return The x coordinate
@ -201,5 +213,31 @@ public class ServerTerrainChunk {
public int getHomogenousValue(){ public int getHomogenousValue(){
return homogenousValue; return homogenousValue;
} }
/**
* Sets the weights of the chunk
* @param weights The weights
*/
public void setWeights(float[][][] weights) {
this.weights = weights;
}
/**
* Sets the values of the chunk
* @param values The values
*/
public void setValues(int[][][] values) {
this.values = values;
}
/**
* Sets the homogenous value
* @param homogenousValue The homogenous value
*/
public void setHomogenousValue(int homogenousValue) {
this.homogenousValue = homogenousValue;
}
} }

View File

@ -40,9 +40,11 @@ public class ClientDrawCellManagerTests {
Vector3i playerPos = new Vector3i(0,0,0); Vector3i playerPos = new Vector3i(0,0,0);
WorldOctTreeNode<DrawCell> node = WorldOctTreeNode.constructorForTests(manager.chunkTree, 1, new Vector3i(16,0,0), new Vector3i(32,16,16)); WorldOctTreeNode<DrawCell> node = WorldOctTreeNode.constructorForTests(manager.chunkTree, 1, new Vector3i(16,0,0), new Vector3i(32,16,16));
node.setLeaf(true); node.setLeaf(true);
node.setData(DrawCell.generateTerrainCell(new Vector3i(0,0,0),3)); node.setData(DrawCell.generateTerrainCell(new Vector3i(16,0,0),3));
assertTrue(manager.shouldSplit(playerPos, node, 0)); boolean shouldBeTrue = node.getParent() != null;
assertEquals(shouldBeTrue,manager.shouldSplit(playerPos, node, 0));
//cleanup //cleanup
Main.shutdown(); Main.shutdown();

View File

@ -1,22 +0,0 @@
package electrosphere.server.terrain.generation;
import static org.junit.jupiter.api.Assertions.assertEquals;
import electrosphere.test.annotations.FastTest;
import electrosphere.test.annotations.UnitTest;
/**
* Unit tests for the test generation chunk generator
*/
public class TestGenerationChunkGeneratorTests {
@UnitTest
@FastTest
public void getSurfaceWeight_ValueTests(){
assertEquals(0.5,TestGenerationChunkGenerator.getSurfaceWeight(0.5, 0, 1));
assertEquals(0.1,TestGenerationChunkGenerator.getSurfaceWeight(0.1, 0, 1));
assertEquals(0.9,TestGenerationChunkGenerator.getSurfaceWeight(0.9, 0, 1));
assertEquals(0.95,TestGenerationChunkGenerator.getSurfaceWeight(1.9, 0, 2));
}
}

View File

@ -0,0 +1,22 @@
package electrosphere.server.terrain.generation.voxelphase;
import static org.junit.jupiter.api.Assertions.assertEquals;
import electrosphere.test.annotations.FastTest;
import electrosphere.test.annotations.UnitTest;
/**
* Unit tests for the test generation chunk generator
*/
public class HillsVoxelGenTests {
@UnitTest
@FastTest
public void getSurfaceWeight_ValueTests(){
assertEquals(0.5,HillsVoxelGen.getSurfaceWeight(0.5, 0, 1));
assertEquals(0.1,HillsVoxelGen.getSurfaceWeight(0.1, 0, 1));
assertEquals(0.9,HillsVoxelGen.getSurfaceWeight(0.9, 0, 1));
assertEquals(0.95,HillsVoxelGen.getSurfaceWeight(1.9, 0, 2));
}
}