biome surface blending
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-11-29 16:19:39 -05:00
parent 471df1c6b1
commit aa34ea4db6
12 changed files with 251 additions and 62 deletions

View File

@ -1173,6 +1173,9 @@ Allow variable collision bits for collidable entities
Autodisable terrain colliders
Lower grass radius
Work on diagnosing data cell misalignment with entities
Fix gridded data cell manager loops iterating incorrectly
Implement multi-biome sampling for surface heightmap
Nearest biome sampling for content generation
# TODO

View File

@ -270,6 +270,37 @@ public class ServerWorldData {
convertRealToChunkSpace(position.z)
);
}
/**
* Converts a world coordinate to a macro scale coordinate
* @param worldPos The world position
* @return The macro scale position
*/
public Vector3i convertWorldToMacroScale(Vector3i worldPos){
return new Vector3i(
worldPos.x / this.serverTerrainManager.getModel().getMacroDataScale(),
worldPos.y / this.serverTerrainManager.getModel().getMacroDataScale(),
worldPos.z / this.serverTerrainManager.getModel().getMacroDataScale()
);
}
/**
* Converts a world coordinate to a macro scale coordinate
* @param worldPos The world position
* @return The macro scale position
*/
public int convertWorldToMacroScale(int worldPos){
return worldPos / this.serverTerrainManager.getModel().getMacroDataScale();
}
/**
* Clamps the world position to the floored macro value in world pos
* @param worldPos The world position
* @return The floor macro value in world pos
*/
public int clampWorldToMacro(int worldPos){
return (worldPos / this.serverTerrainManager.getModel().getMacroDataScale()) * this.serverTerrainManager.getModel().getMacroDataScale();
}
/**
* Gets the terrain manager for this world

View File

@ -55,7 +55,7 @@ public class ServerContentGenerator {
//generate foliage
BiomeData biome = null;
if(realm.getServerWorldData() != null && realm.getServerWorldData().getServerTerrainManager() != null && realm.getServerWorldData().getServerTerrainManager().getModel() != null){
biome = realm.getServerWorldData().getServerTerrainManager().getModel().getSurfaceBiome(worldPos.x, worldPos.z);
biome = realm.getServerWorldData().getServerTerrainManager().getModel().getClosestSurfaceBiome(worldPos.x, worldPos.z);
}
List<BiomeFoliageDescription> foliageDescriptions = biome.getSurfaceGenerationParams().getFoliageDescriptions();
if(foliageDescriptions != null){

View File

@ -332,7 +332,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//Add to cells that are in range
for(int x = newPosition.x - playerSimulationRadius + 1; x < newPosition.x + playerSimulationRadius; x++){
for(int y = newPosition.y - playerSimulationRadius + 1; y < newPosition.y + playerSimulationRadius; y++){
for(int z = newPosition.x - playerSimulationRadius + 1; z < newPosition.z + playerSimulationRadius; z++){
for(int z = newPosition.z - playerSimulationRadius + 1; z < newPosition.z + playerSimulationRadius; z++){
if(this.canCreateCell(x,y,z) && this.shouldContainPlayer(new Vector3i(x,y,z), newPosition, playerSimulationRadius)){
Vector3i targetPos = new Vector3i(x,y,z);
if(groundDataCells.get(this.getServerDataCellKey(targetPos)) != null){

View File

@ -120,7 +120,7 @@ public class JSChunkGenerator implements ChunkGenerator {
try {
//biome of the current chunk
BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ);
BiomeData surfaceBiome = this.terrainModel.getClosestSurfaceBiome(worldX, worldZ);
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
@ -218,7 +218,7 @@ public class JSChunkGenerator implements ChunkGenerator {
@Override
public double getElevation(int worldX, int worldZ, int chunkX, int chunkZ){
BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ);
BiomeData surfaceBiome = this.terrainModel.getClosestSurfaceBiome(worldX, worldZ);
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());

View File

@ -134,40 +134,21 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
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)
);
}
}
double[][] heightfield = new double[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
BiomeData[][] surfaceBiomeMap = new BiomeData[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
this.populateElevation(heightfield,surfaceBiomeMap,worldX,worldZ,strideValue);
float[][] gradientField = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
double[][] gradientField = new double[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;
double deltaX = 0;
double deltaZ = 0;
if(x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 1){
deltaX = Math.abs(heightfield[x][z] - heightfield[x+1][z]);
} else {
@ -199,8 +180,9 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
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];
double surfaceHeight = heightfield[x][z];
double gradient = gradientField[x][z];
BiomeData surfaceBiome = surfaceBiomeMap[x][z];
for(int y = 0; y < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; y++){
int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
@ -249,21 +231,158 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
return rVal;
}
/**
* Populates the heightfield
* @param heightfield The heightfield to populate
* @param surfaceBiomeMap The surface biome map
* @param worldX The world x position
* @param worldZ The world z position
* @param strideValue The stride value
*/
private void populateElevation(double[][] heightfield, BiomeData[][] surfaceBiomeMap, int worldX, int worldZ, int strideValue){
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] = this.getMultisampleElevation(finalWorldX, finalWorldZ, finalChunkX, finalChunkZ);
//calculate real pos
double realX = serverWorldData.convertVoxelToRealSpace(finalChunkX, finalWorldX);
double realZ = serverWorldData.convertVoxelToRealSpace(finalChunkZ, finalWorldZ);
//clamped macro pos
int macroDataScale = terrainModel.getMacroDataScale();
double macroWorldPosX = serverWorldData.convertWorldToReal(serverWorldData.clampWorldToMacro(finalWorldX));
double macroWorldPosZ = serverWorldData.convertWorldToReal(serverWorldData.clampWorldToMacro(finalWorldZ));
double macroWidth = this.terrainModel.getMacroWidthInRealTerms();
double percent1 = (realX - macroWorldPosX) / macroWidth;
double percent2 = (realZ - macroWorldPosZ) / macroWidth;
//solve dominant surface biome
if(percent1 > 0.5){
if(percent2 > 0.5){
surfaceBiomeMap[x][z] = this.terrainModel.getMacroData(finalWorldX / macroDataScale + 1, finalWorldZ / macroDataScale + 1);
} else {
surfaceBiomeMap[x][z] = this.terrainModel.getMacroData(finalWorldX / macroDataScale + 1, finalWorldZ / macroDataScale);
}
} else {
if(percent2 > 0.5){
surfaceBiomeMap[x][z] = this.terrainModel.getMacroData(finalWorldX / macroDataScale, finalWorldZ / macroDataScale + 1);
} else {
surfaceBiomeMap[x][z] = this.terrainModel.getMacroData(finalWorldX / macroDataScale, finalWorldZ / macroDataScale);
}
}
}
}
}
/**
* Gets the elevation of a given position by sampling all four surrounding biome generators
* @param finalWorldX The world x coordinate
* @param finalWorldZ The world z coordinate
* @param finalChunkX The chunk x coordinate
* @param finalChunkZ The chunk z coordinate
* @return The elevation of the world at that position
*/
private double getMultisampleElevation(int finalWorldX, int finalWorldZ, int finalChunkX, int finalChunkZ){
double rVal = 0;
//biome of the current chunk
double weight = 0;
BiomeData surfaceBiome = null;
BiomeSurfaceGenerationParams surfaceParams = null;
HeightmapGenerator heightmapGen = null;
//calculate real pos
double realX = serverWorldData.convertVoxelToRealSpace(finalChunkX, finalWorldX);
double realZ = serverWorldData.convertVoxelToRealSpace(finalChunkZ, finalWorldZ);
//clamped macro pos
int macroDataScale = terrainModel.getMacroDataScale();
double macroWorldPosX = serverWorldData.convertWorldToReal(serverWorldData.clampWorldToMacro(finalWorldX));
double macroWorldPosZ = serverWorldData.convertWorldToReal(serverWorldData.clampWorldToMacro(finalWorldZ));
double macroWidth = this.terrainModel.getMacroWidthInRealTerms();
double percent1 = (realX - macroWorldPosX) / macroWidth;
double percent2 = (realZ - macroWorldPosZ) / macroWidth;
//sample 1
{
weight = (1.0 - percent1) * (1.0 - percent2);
surfaceBiome = this.terrainModel.getMacroData(finalWorldX / macroDataScale, finalWorldZ / macroDataScale);
surfaceParams = surfaceBiome.getSurfaceGenerationParams();
heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
}
rVal = rVal + heightmapGen.getHeight(
this.terrainModel.getSeed(),
realX,
realZ
) * weight;
}
//sample 2
{
weight = percent1 * (1.0 - percent2);
surfaceBiome = this.terrainModel.getMacroData(finalWorldX / macroDataScale + 1, finalWorldZ / macroDataScale);
surfaceParams = surfaceBiome.getSurfaceGenerationParams();
heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
}
rVal = rVal + heightmapGen.getHeight(
this.terrainModel.getSeed(),
realX,
realZ
) * weight;
}
//sample 3
{
weight = (1.0 - percent1) * percent2;
surfaceBiome = this.terrainModel.getMacroData(finalWorldX / macroDataScale, finalWorldZ / macroDataScale + 1);
surfaceParams = surfaceBiome.getSurfaceGenerationParams();
heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
}
rVal = rVal + heightmapGen.getHeight(
this.terrainModel.getSeed(),
realX,
realZ
) * weight;
}
//sample 4
{
weight = percent1 * percent2;
surfaceBiome = this.terrainModel.getMacroData(finalWorldX / macroDataScale + 1, finalWorldZ / macroDataScale + 1);
surfaceParams = surfaceBiome.getSurfaceGenerationParams();
heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
}
rVal = rVal+ heightmapGen.getHeight(
this.terrainModel.getSeed(),
realX,
realZ
) * weight;
}
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;
return this.getMultisampleElevation(worldX,worldZ,chunkX,chunkZ);
}
@Override

View File

@ -68,7 +68,7 @@ public class AnimeMountainsGen implements VoxelGenerator {
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
int stride, float surfaceHeight, float surfaceGradient,
int stride, double surfaceHeight, double surfaceGradient,
BiomeData surfaceBiome,
GenerationContext generationContext
) {
@ -122,7 +122,7 @@ public class AnimeMountainsGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
double surfaceHeight,
BiomeData surfaceBiome
){
voxel.weight = (float)surfacePercent * 2 - 1;
@ -139,7 +139,7 @@ public class AnimeMountainsGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
double surfaceHeight,
BiomeData surfaceBiome
){
if(realY < surfaceHeight - 5){
@ -161,7 +161,7 @@ public class AnimeMountainsGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
double surfaceHeight,
BiomeData surfaceBiome
){
//default voxel value

View File

@ -28,7 +28,7 @@ public class HillsVoxelGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
int stride,
float surfaceHeight, float surfaceGradient,
double surfaceHeight, double surfaceGradient,
BiomeData surfaceBiome,
GenerationContext generationContext
){
@ -81,7 +81,7 @@ public class HillsVoxelGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
double surfaceHeight,
BiomeData surfaceBiome
){
voxel.weight = (float)surfacePercent * 2 - 1;
@ -98,7 +98,7 @@ public class HillsVoxelGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
double surfaceHeight,
BiomeData surfaceBiome
){
if(realY < surfaceHeight - 5){
@ -120,7 +120,7 @@ public class HillsVoxelGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight,
double surfaceHeight,
BiomeData surfaceBiome
){
voxel.weight = -1;

View File

@ -35,7 +35,7 @@ public class MountainVoxelGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
int stride,
float surfaceHeight, float surfaceGradient,
double surfaceHeight, double surfaceGradient,
BiomeData surfaceBiome,
GenerationContext generationContext
){
@ -88,7 +88,7 @@ public class MountainVoxelGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight, float surfaceGradient,
double surfaceHeight, double surfaceGradient,
BiomeData surfaceBiome
){
voxel.weight = (float)surfacePercent * 2 - 1;
@ -112,7 +112,7 @@ public class MountainVoxelGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight, float surfaceGradient,
double surfaceHeight, double surfaceGradient,
BiomeData surfaceBiome
){
if(realY < surfaceHeight - 5){
@ -134,7 +134,7 @@ public class MountainVoxelGen implements VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
double surfacePercent,
float surfaceHeight, float surfaceGradient,
double surfaceHeight, double surfaceGradient,
BiomeData surfaceBiome
){
voxel.weight = -1;

View File

@ -46,7 +46,7 @@ public class NoiseVoxelGen implements VoxelGenerator {
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
int stride, float surfaceHeight, float surfaceGradient,
int stride, double surfaceHeight, double surfaceGradient,
BiomeData surfaceBiome, GenerationContext generationContext
) {
double strideMultiplier = Math.pow(2,stride);

View File

@ -39,7 +39,7 @@ public interface VoxelGenerator {
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
int stride,
float surfaceHeight, float surfaceGradient,
double surfaceHeight, double surfaceGradient,
BiomeData surfaceBiome,
GenerationContext generationContext
);

View File

@ -3,6 +3,7 @@ package electrosphere.server.terrain.models;
import electrosphere.engine.Globals;
import electrosphere.game.data.biome.BiomeData;
import electrosphere.server.terrain.generation.TestGenerationChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.util.annotation.Exclude;
/**
@ -129,6 +130,7 @@ public class TerrainModel {
rVal.biome[x][z] = TestGenerationChunkGenerator.DEFAULT_BIOME_INDEX;
}
}
rVal.biome[1][0] = 0;
return rVal;
}
@ -463,9 +465,26 @@ public class TerrainModel {
* @param worldZ The world Z
* @return The biome
*/
public BiomeData getSurfaceBiome(int worldX, int worldZ){
int macroX = worldX / macroDataScale;
int macroZ = worldZ / macroDataScale;
public BiomeData getClosestSurfaceBiome(int worldX, int worldZ){
//essentially, select the closest macro data point, not just floor
int offsetX = worldX % macroDataScale > (macroDataScale / 2.0) ? 1 : 0;
int offsetZ = worldZ % macroDataScale > (macroDataScale / 2.0) ? 1 : 0;
int macroX = worldX / macroDataScale + offsetX;
int macroZ = worldZ / macroDataScale + offsetZ;
int surfaceBiomeIndex = this.biome[macroX][macroZ];
BiomeData biome = Globals.gameConfigCurrent.getBiomeMap().getBiomeByIndex(surfaceBiomeIndex);
return biome;
}
/**
* Gets the surface biome for a given macro position
* @param worldX The macro X
* @param worldZ The macro Z
* @return The biome
*/
public BiomeData getMacroData(int macroX, int macroZ){
int surfaceBiomeIndex = this.biome[macroX][macroZ];
BiomeData biome = Globals.gameConfigCurrent.getBiomeMap().getBiomeByIndex(surfaceBiomeIndex);
return biome;
@ -497,5 +516,22 @@ public class TerrainModel {
public void setMacroDataScale(int scale){
this.macroDataScale = scale;
}
/**
* Gets the scale of the macro data
* @return The scale
*/
public int getMacroDataScale(){
return this.macroDataScale;
}
/**
* Gets the width of the macro data in real terms
* @return The width of the macro data in real terms
*/
public double getMacroWidthInRealTerms(){
return this.macroDataScale * ServerTerrainChunk.CHUNK_DIMENSION;
}
}