Terrain4J_Experimental_branch/resources/EngineResources/VoxelComputeShaders/voxelGenerationShared.glsl

582 lines
No EOL
20 KiB
GLSL

#version 460
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
const int CHUNK_SIZE = 16;
const int CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE;
const int STRUCTURE_CELL_SIZE = 16;
const int REPLACE_AIR_ONLY = 1;
const int REPLACE_FOLIAGE = 2;
const int REQUIRE_FLAT_GROUND = 4;
const int HAS_FOUNDATION = 8;
layout(push_constant) uniform ChunkInfo {
ivec3 chunkPos;
int slot;
uint faceOffset;
uint voxelTypeCount;
uint indirectCommandIndex;
uint biomeCount;
uint structureCount;
uint worldSeed;
};
struct Biome {
uint surfaceBlock;
uint dirtBlock;
uint stoneBlock;
uint grassFoliage;
uint[4] flowers;
uint[4] tallBlocks;
uint[4] otherFoliage;
};
struct Structure {
uint sizeX;
uint sizeY;
uint sizeZ;
uint blockOffset;
uint spawnSpacing;
uint spawnChance;
uint allowedBiome;
uint groundBlock;
uint flags;
};
struct StructureVoxelResult {
uint block;
uint flags;
};
layout(std430, binding = 0) buffer VoxelData {
uint voxels[];
};
layout(std430, set = 1, binding = 0) readonly buffer BiomeUniform {
Biome biomes[];
} BiomeReg;
layout(std430, set = 2, binding = 0) readonly buffer StructureRegistry {
Structure structures[];
} structureReg;
layout(std430, set = 2, binding = 1) readonly buffer StructureBlocks {
uint blocks[];
} structureBlocks;
uint hash21(uvec2 p) {
p = p * 1664525u + 1013904223u;
p.x += p.y * 1664525u;
p.y += p.x * 1664525u;
return p.x ^ (p.x >> 16);
}
float random3to1(vec3 st) {
return fract(sin(dot(st, vec3(127.1, 311.7, 74.7))) * 43758.5453123);
}
float interpolate(float a, float b, float c, float d, float x) {
float p = (d - c) - (a - b);
return x * (x * (x * p + ((a - b) - p)) + (c - a)) + b;
}
float sampleX(vec3 at) {
float floored = floor(at.x);
return interpolate(
random3to1(vec3(floored - 1.0, at.yz)),
random3to1(vec3(floored, at.yz)),
random3to1(vec3(floored + 1.0, at.yz)),
random3to1(vec3(floored + 2.0, at.yz)),
at.x - floored) * 0.5 + 0.25;
}
float sampleY(vec3 at) {
float floored = floor(at.y);
return interpolate(
sampleX(vec3(at.x, floored - 1.0, at.z)),
sampleX(vec3(at.x, floored, at.z)),
sampleX(vec3(at.x, floored + 1.0, at.z)),
sampleX(vec3(at.x, floored + 2.0, at.z)),
at.y - floored);
}
float cubicNoise(vec3 at) {
float floored = floor(at.z);
return interpolate(
sampleY(vec3(at.xy, floored - 1.0)),
sampleY(vec3(at.xy, floored)),
sampleY(vec3(at.xy, floored + 1.0)),
sampleY(vec3(at.xy, floored + 2.0)),
at.z - floored);
}
float rand(vec2 co) {
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
float valueNoise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = rand(i);
float b = rand(i + vec2(1.0, 0.0));
float c = rand(i + vec2(0.0, 1.0));
float d = rand(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a) * u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
uint pcg_hash(uint seed) {
uint state = seed * 747796405u + 289133645u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
uint randomRangeUint(uint seed, uint minVal, uint maxVal) {
uint range = maxVal - minVal + 1u;
return minVal + (pcg_hash(seed) % range);
}
uint GetBiome(vec3 worldPos, uint MaxBiomes) {
if (MaxBiomes == 0u) return 0u;
vec3 X = vec3(worldPos);
X = mat3(
0.788675134594813, -0.211324865405187, -0.577350269189626,
-0.211324865405187, 0.788675134594813, -0.577350269189626,
0.577350269189626, 0.577350269189626, 0.577350269189626) * X;
float n = clamp(cubicNoise(X), 0.0, 0.9999);
uint Biome = uint(floor(n * float(MaxBiomes)));
return min(Biome, MaxBiomes - 1u);
}
bool isCave(vec3 worldPos) {
vec3 X = vec3(worldPos);
X = mat3(
0.788675134594813, -0.211324865405187, -0.577350269189626,
-0.211324865405187, 0.788675134594813, -0.577350269189626,
0.577350269189626, 0.577350269189626, 0.577350269189626) * X;
float n = cubicNoise(X);
return n > 0.5;
}
bool shouldSpawn(uint seed, Structure s) {
return pcg_hash(seed) % 1000u < s.spawnChance;
}
bool hasFlag(uint flags, uint flag) {
return (flags & flag) != 0u;
}
int floorDiv(int value, int divisor) {
int q = value / divisor;
int r = value - q * divisor;
if (r != 0 && ((r < 0) != (divisor < 0))) {
q -= 1;
}
return q;
}
ivec2 getStructureCell(ivec2 worldXZ, int spacing) {
return ivec2(
floorDiv(worldXZ.x, spacing),
floorDiv(worldXZ.y, spacing)
);
}
float getPrimarySurfaceHeight(int worldX, int worldY, int worldZ) {
float YZone = floor(float(worldY) / 150.0) * 150.0;
return valueNoise(vec2(float(worldX) + YZone * 2.0, float(worldZ) + YZone * 2.0) * 0.005) * 100.0 +
valueNoise(vec2(float(worldX) + YZone * 2.0, float(worldZ) + YZone * 2.0) * 0.01) * 10.0 +
valueNoise(vec2(float(worldX) + YZone * 2.0, float(worldZ) + YZone * 2.0) * 0.1) * 5.0 +
valueNoise(vec2(float(worldX) + YZone * 2.0, float(worldZ) + YZone * 2.0)) * 0.5 +
YZone;
}
float getSecondarySurfaceHeight(int worldX, int worldY, int worldZ) {
float YZone2 = floor(float(worldY) / 75.0) * 75.0;
return valueNoise(vec2(float(worldX) + YZone2 * 2.0, float(worldZ) + YZone2 * 2.0) * 0.005) * 50.0 +
valueNoise(vec2(float(worldX) + YZone2 * 2.0, float(worldZ) + YZone2 * 2.0) * 0.01) * 10.0 +
valueNoise(vec2(float(worldX) + YZone2 * 2.0, float(worldZ) + YZone2 * 2.0) * 0.1) * 5.0 +
valueNoise(vec2(float(worldX) + YZone2 * 2.0, float(worldZ) + YZone2 * 2.0)) * 0.5 +
YZone2;
}
float getSurfaceHeight(int worldX, int worldZ, int referenceY) {
float primary = getPrimarySurfaceHeight(worldX, referenceY, worldZ);
float secondary = getSecondarySurfaceHeight(worldX, referenceY, worldZ);
float primaryDist = abs(primary - float(referenceY));
float secondaryDist = abs(secondary - float(referenceY));
return primaryDist <= secondaryDist ? primary : secondary;
}
bool cellSpawnsStructure(uint seed, Structure s) {
if (s.spawnChance == 0u) {
return false;
}
return pcg_hash(seed) % 1000u < s.spawnChance;
}
uint chooseStructure(uint seed, uint biome) {
if (structureCount == 0u) {
return 0u;
}
uint start = pcg_hash(seed ^ 0xA53A9D31u) % structureCount;
for (uint i = 0u; i < structureCount; i++) {
uint id = (start + i) % structureCount;
Structure s = structureReg.structures[id];
if (s.allowedBiome == 0u || s.allowedBiome == biome + 1u) {
return id;
}
}
return start;
}
ivec2 getStructureAnchorXZ(ivec2 cell, uint seed, uint spacing) {
int structureSpacing = int(max(spacing, 1u));
uint jitterX = randomRangeUint(seed ^ 0xB5297A4Du, 0u, uint(structureSpacing - 1));
uint jitterZ = randomRangeUint(seed ^ 0x68E31DA4u, 0u, uint(structureSpacing - 1));
return cell * structureSpacing + ivec2(int(jitterX), int(jitterZ));
}
uint getStructureBlock(uint structureId, ivec3 localPos) {
Structure s = structureReg.structures[structureId];
if (localPos.x < 0 || localPos.x >= int(s.sizeX)) return 0u;
if (localPos.y < 0 || localPos.y >= int(s.sizeY)) return 0u;
if (localPos.z < 0 || localPos.z >= int(s.sizeZ)) return 0u;
uint index =
uint(localPos.x) * s.sizeY * s.sizeZ +
uint(localPos.y) * s.sizeZ +
uint(localPos.z);
return structureBlocks.blocks[s.blockOffset + index];
}
bool isGroundFlatEnough(ivec2 anchorXZ, uint structureId, int referenceY) {
Structure s = structureReg.structures[structureId];
float h0 = getSurfaceHeight(anchorXZ.x, anchorXZ.y, referenceY);
float h1 = getSurfaceHeight(anchorXZ.x + int(s.sizeX) - 1, anchorXZ.y, referenceY);
float h2 = getSurfaceHeight(anchorXZ.x, anchorXZ.y + int(s.sizeZ) - 1, referenceY);
float h3 = getSurfaceHeight(anchorXZ.x + int(s.sizeX) - 1, anchorXZ.y + int(s.sizeZ) - 1, referenceY);
float minH = min(min(h0, h1), min(h2, h3));
float maxH = max(max(h0, h1), max(h2, h3));
return maxH - minH <= 2.0;
}
ivec3 getStructureCell3D(ivec3 worldPos, int spacing) {
return ivec3(
floorDiv(worldPos.x, spacing),
floorDiv(worldPos.y, 75),
floorDiv(worldPos.z, spacing)
);
}
uint hash31(ivec3 p) {
uvec3 v = uvec3(p) * uvec3(1664525u, 22695477u, 1103515245u) + uvec3(1013904223u);
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;
return v.x ^ v.y ^ v.z;
}
uint getBaseTerrainVoxelAt(ivec3 worldPos, uint biome) {
Biome currentBiome = BiomeReg.biomes[biome];
float YZone = floor(float(worldPos.y) / 150.0) * 150.0;
float SurfaceHeight =
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.005) * 100.0 +
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.01) * 10.0 +
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.1) * 5.0 +
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0)) * 0.5 +
YZone;
float IslandUnderneathHeight =
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.005) * 100.0 +
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.1) * 20.0 +
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.1) * 5.0 +
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0)) * 0.5 -
15.0 +
YZone;
float grassNoise = valueNoise(vec2(float(worldPos.z) * 0.5 - YZone * 2.0, float(worldPos.x) * 0.5 - YZone * 2.0));
uint randomBlockVal = randomRangeUint(uint(worldPos.x) * 73856093u ^ uint(worldPos.z) * 19349663u, 0u, 3u);
uint voxelType = 0u;
if (float(worldPos.y) <= SurfaceHeight && float(worldPos.y) >= IslandUnderneathHeight) {
float depthBelowSurface = SurfaceHeight - float(worldPos.y);
if (depthBelowSurface < 0.5) {
if (grassNoise < 0.65) voxelType = currentBiome.grassFoliage;
else if (grassNoise < 0.75) voxelType = currentBiome.flowers[randomBlockVal];
else if (grassNoise < 0.85) voxelType = currentBiome.otherFoliage[randomBlockVal];
else voxelType = 0u;
} else if (depthBelowSurface < 2.5) {
voxelType = currentBiome.surfaceBlock;
} else if (depthBelowSurface < 6.0) {
voxelType = currentBiome.dirtBlock;
} else {
voxelType = currentBiome.stoneBlock;
}
} else {
float YZone2 = floor(float(worldPos.y) / 75.0) * 75.0;
float IslandUnderneathHeight2 =
valueNoise(vec2(float(worldPos.x) - YZone2 * 2.0, float(worldPos.z) - YZone2 * 2.0) * 0.005) * 60.0 +
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.1) * 20.0 +
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.1) * 10.0 +
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0)) * 0.5 -
5.0 +
YZone2;
float height2 =
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.005) * 50.0 +
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.01) * 10.0 +
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.1) * 5.0 +
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0)) * 0.5 +
YZone2;
if (float(worldPos.y) <= height2 && float(worldPos.y) >= IslandUnderneathHeight2) {
float depthBelowSurface = height2 - float(worldPos.y);
if (depthBelowSurface < 0.5) {
if (grassNoise < 0.65) voxelType = currentBiome.grassFoliage;
else if (grassNoise < 0.75) voxelType = currentBiome.flowers[randomBlockVal];
else if (grassNoise < 0.85) voxelType = currentBiome.otherFoliage[randomBlockVal];
else voxelType = 0u;
} else if (depthBelowSurface < 1.5) {
voxelType = currentBiome.surfaceBlock;
} else if (depthBelowSurface < 6.0) {
voxelType = currentBiome.dirtBlock;
} else {
voxelType = currentBiome.stoneBlock;
}
}
}
if (voxelType != 0u && isCave(vec3(worldPos) * 0.05)) {
voxelType = 0u;
}
return voxelType;
}
bool isValidStructureAnchor(ivec3 origin, uint structureId, uint biome) {
Structure s = structureReg.structures[structureId];
ivec3 groundPos = origin + ivec3(0, -1, 0);
ivec3 basePos = origin;
uint groundVoxel = getBaseTerrainVoxelAt(groundPos, biome);
uint baseVoxel = getBaseTerrainVoxelAt(basePos, biome);
if (groundVoxel == 0u) {
return false;
}
if (s.groundBlock != 0u && groundVoxel != s.groundBlock) {
return false;
}
if (baseVoxel != 0u) {
return false;
}
if (isCave(vec3(groundPos) * 0.05)) {
return false;
}
return true;
}
int getStructureReferenceY(int worldY) {
return floorDiv(worldY, 75) * 75;
}
bool isStructureFootprintSupported(ivec3 origin, uint structureId, uint biome) {
Structure s = structureReg.structures[structureId];
int supportedCorners = 0;
ivec3 p0 = origin + ivec3(0, -1, 0);
ivec3 p1 = origin + ivec3(int(s.sizeX) - 1, -1, 0);
ivec3 p2 = origin + ivec3(0, -1, int(s.sizeZ) - 1);
ivec3 p3 = origin + ivec3(int(s.sizeX) - 1, -1, int(s.sizeZ) - 1);
if (getBaseTerrainVoxelAt(p0, biome) != 0u) supportedCorners++;
if (getBaseTerrainVoxelAt(p1, biome) != 0u) supportedCorners++;
if (getBaseTerrainVoxelAt(p2, biome) != 0u) supportedCorners++;
if (getBaseTerrainVoxelAt(p3, biome) != 0u) supportedCorners++;
return supportedCorners == 4;
}
StructureVoxelResult getStructureVoxelAt(ivec3 worldPos, uint biome) {
StructureVoxelResult result;
result.block = 0u;
result.flags = 0u;
if (structureCount == 0u) {
return result;
}
for (uint structureId = 0u; structureId < structureCount; structureId++) {
Structure s = structureReg.structures[structureId];
if (s.allowedBiome != 0u && s.allowedBiome != biome + 1u) {
continue;
}
int spacing = int(max(s.spawnSpacing, 1u));
ivec3 baseCell = getStructureCell3D(worldPos, spacing);
for (int ox = -1; ox <= 1; ox++) {
for (int oz = -1; oz <= 1; oz++) {
ivec3 cell = baseCell + ivec3(ox, 0, oz);
uint seed = hash31(cell + ivec3(0, int(structureId) * 97, 0)) ^ worldSeed;
if (!cellSpawnsStructure(seed, s)) {
continue;
}
ivec2 anchorXZ = getStructureAnchorXZ(cell.xz, seed, s.spawnSpacing);
int referenceY = cell.y * 75;
float anchorHeight = getSurfaceHeight(anchorXZ.x, anchorXZ.y, referenceY);
int anchorY = int(floor(anchorHeight)) + 1;
ivec3 origin = ivec3(anchorXZ.x, anchorY, anchorXZ.y);
if (!isValidStructureAnchor(origin, structureId, biome)) {
continue;
}
ivec3 local = worldPos - origin;
uint block = getStructureBlock(structureId, local);
if (block != 0u) {
result.block = block;
result.flags = s.flags;
return result;
}
}
}
}
return result;
}
void main() {
ivec3 localPos = ivec3(gl_GlobalInvocationID.xyz);
ivec3 worldPos = chunkPos * CHUNK_SIZE + localPos;
uint biome = GetBiome(vec3(worldPos.z, worldPos.y, worldPos.x) * 0.05, biomeCount);
Biome currentBiome = BiomeReg.biomes[biome];
float YZone = floor(worldPos.y/150.0)*150.0;
float SurfaceHeight = valueNoise(vec2(worldPos.x + YZone * 2, worldPos.z + YZone * 2) * 0.005) * 100 +
valueNoise(vec2(worldPos.x + YZone * 2, worldPos.z + YZone * 2) * 0.01) * 10 +
valueNoise(vec2(worldPos.x + YZone * 2, worldPos.z + YZone * 2) * 0.1) * 5 +
valueNoise(vec2(worldPos.x + YZone * 2, worldPos.z + YZone * 2)) * 0.5 + YZone;
uint RandomBlockVal = randomRangeUint(uint(worldPos.x) * 73856093u ^ uint(worldPos.z) * 19349663u,0u ,3u);
float IslandUnderneathHeight = valueNoise(vec2(worldPos.x + YZone * 2, worldPos.z + YZone * 2) * 0.005) * 100 +
valueNoise(vec2(worldPos.x + YZone * 2, worldPos.z + YZone * 2) * 0.1) * 20 +
valueNoise(vec2(worldPos.x + YZone * 2, worldPos.z + YZone * 2) * 0.1) * 5 +
valueNoise(vec2(worldPos.x + YZone * 2, worldPos.z + YZone * 2)) * 0.5 - 15 + YZone;
float grassNoise = valueNoise(vec2(worldPos.z * 0.5 - YZone * 2, worldPos.x * 0.5 - YZone * 2));
uint voxelType = 0u;
if (float(worldPos.y) <= SurfaceHeight && float(worldPos.y) >= IslandUnderneathHeight) {
float depthBelowSurface = SurfaceHeight - float(worldPos.y);
if (depthBelowSurface < 0.5) {
if(grassNoise < 0.65) voxelType = currentBiome.grassFoliage; // Grass foliage
else if (grassNoise < 0.75) voxelType = currentBiome.flowers[RandomBlockVal];
else if (grassNoise < 0.85) voxelType = currentBiome.otherFoliage[RandomBlockVal];
else voxelType = 0u;
}else if (depthBelowSurface < 2.5) {
voxelType = currentBiome.surfaceBlock; // Grass
} else if (depthBelowSurface < 6.0) {
voxelType = currentBiome.dirtBlock; // Dirt(also stone rn)
} else {
voxelType = currentBiome.stoneBlock; // Stone
}
}
else{
float YZone2 = floor(worldPos.y/75.0)*75.0;
float IslandUnderneathHeight2 = valueNoise(vec2(worldPos.x - YZone2 * 2, worldPos.z -YZone2 * 2) * 0.005) * 60 +
valueNoise(vec2(worldPos.x + YZone2 * 2, worldPos.z + YZone2 * 2) * 0.1) * 20 +
valueNoise(vec2(worldPos.x + YZone2 * 2, worldPos.z + YZone2 * 2) * 0.1) * 10+
valueNoise(vec2(worldPos.x + YZone2 * 2, worldPos.z + YZone2 * 2)) * 0.5 - 5 + YZone2;
float height2 = valueNoise(vec2(worldPos.x + YZone2 * 2, worldPos.z + YZone2 * 2) * 0.005) * 50 +
valueNoise(vec2(worldPos.x + YZone2 * 2, worldPos.z + YZone2 * 2) * 0.01) * 10 +
valueNoise(vec2(worldPos.x + YZone2 * 2, worldPos.z + YZone2 * 2) * 0.1) * 5+
valueNoise(vec2(worldPos.x + YZone2 * 2, worldPos.z + YZone2 * 2)) * 0.5 + YZone2;
if (float(worldPos.y) <= height2 && float(worldPos.y) >= IslandUnderneathHeight2) {
float depthBelowSurface = height2 - float(worldPos.y);
if (depthBelowSurface < 0.5) {
if (grassNoise < 0.65) voxelType = currentBiome.grassFoliage; // Grass foliage
else if (grassNoise < 0.75) voxelType = currentBiome.flowers[RandomBlockVal];
else if (grassNoise < 0.85) voxelType = currentBiome.otherFoliage[RandomBlockVal];
else voxelType = 0u;
} else if (depthBelowSurface < 1.5) {
voxelType = currentBiome.surfaceBlock; // Grass
} else if (depthBelowSurface < 6.0) {
voxelType = currentBiome.dirtBlock; // Dirt(also stone rn)
} else {
voxelType = currentBiome.stoneBlock; // Stone
}
}
}
if (voxelType != 0u) {
if (isCave(vec3(worldPos*0.05))) {
voxelType = 0u;
}
}
StructureVoxelResult structureVoxel = getStructureVoxelAt(worldPos, biome);
if (structureVoxel.block != 0u) {
if (hasFlag(structureVoxel.flags, uint(REPLACE_AIR_ONLY))) {
if (voxelType == 0u) {
voxelType = structureVoxel.block;
}
} else {
voxelType = structureVoxel.block;
}
}
uint index = uint((localPos.x * CHUNK_AREA) + (localPos.y * CHUNK_SIZE) + localPos.z);
voxels[index] = voxelType;
}