diff --git a/resources/EngineResources/VoxelComputeShaders/meshGeneration.glsl b/resources/EngineResources/VoxelComputeShaders/meshGeneration.glsl new file mode 100644 index 0000000..91b77ec --- /dev/null +++ b/resources/EngineResources/VoxelComputeShaders/meshGeneration.glsl @@ -0,0 +1,236 @@ +#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 VOXEL_COUNT = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE; + +const uint FLOATS_PER_VERTEX = 14u; +const uint VERTICES_PER_FACE = 4u; +const uint INDICES_PER_FACE = 6u; + +const uint MAX_VISIBLE_FACES_PER_CHUNK = uint(CHUNK_SIZE * CHUNK_SIZE * 12); +const uint MAX_VERTICES = MAX_VISIBLE_FACES_PER_CHUNK * VERTICES_PER_FACE; +const uint MAX_INDICES = MAX_VISIBLE_FACES_PER_CHUNK * INDICES_PER_FACE; + +layout(std430, binding = 0) readonly buffer VoxelData { + uint voxels[]; +}; + +layout(std430, binding = 1) buffer VertexBuffer { + float vertices[]; +}; + +layout(std430, binding = 2) buffer IndexBuffer { + uint indices[]; +}; + +layout(std430, binding = 3) buffer DrawCommand { + uint indexCount; + uint instanceCount; + uint firstIndex; + int vertexOffset; + uint firstInstance; +} drawCmd; + +layout(std430, binding = 4) buffer Counters { + uint vertexCount; + uint indexCount; +} counters; + +layout(push_constant) uniform ChunkInfo { + ivec3 chunkPos; + int padding0; +}; + +uint flatten(ivec3 pos) { + return uint((pos.x * CHUNK_AREA) + (pos.y * CHUNK_SIZE) + pos.z); +} + +uint getVoxel(ivec3 pos) { + if (pos.x < 0 || pos.x >= CHUNK_SIZE) return 0; + if (pos.y < 0 || pos.y >= CHUNK_SIZE) return 0; + if (pos.z < 0 || pos.z >= CHUNK_SIZE) return 0; + + return voxels[flatten(pos)]; +} + +void writeVertex(uint vertexIndex,vec3 position,vec3 normal, + vec3 tangent,vec3 bitangent,vec2 uv) { + uint base = vertexIndex * FLOATS_PER_VERTEX; + + vertices[base + 0u] = position.x; + vertices[base + 1u] = position.y; + vertices[base + 2u] = position.z; + + vertices[base + 3u] = normal.x; + vertices[base + 4u] = normal.y; + vertices[base + 5u] = normal.z; + + vertices[base + 6u] = tangent.x; + vertices[base + 7u] = tangent.y; + vertices[base + 8u] = tangent.z; + + vertices[base + 9u] = bitangent.x; + vertices[base + 10u] = bitangent.y; + vertices[base + 11u] = bitangent.z; + + vertices[base + 12u] = uv.x; + vertices[base + 13u] = uv.y; +} + +void emitFace(ivec3 voxelPos, int faceIndex) { + uint baseVertex = atomicAdd(counters.vertexCount, VERTICES_PER_FACE); + uint baseIndex = atomicAdd(counters.indexCount, INDICES_PER_FACE); + + if (baseVertex + VERTICES_PER_FACE > MAX_VERTICES || + baseIndex + INDICES_PER_FACE > MAX_INDICES) { + return; + } + + vec3 p = vec3(chunkPos * CHUNK_SIZE + voxelPos); + + vec3 normal; + vec3 tangent; + vec3 bitangent; + + + vec3 v0; + vec3 v1; + vec3 v2; + vec3 v3; + + vec2 uv0; + vec2 uv1; + vec2 uv2; + vec2 uv3; + + if (faceIndex == 0) { + // Top +Y + normal = vec3(0.0, 1.0, 0.0); + tangent = vec3(1.0, 0.0, 0.0); + bitangent = vec3(0.0, 0.0, 1.0); + + v0 = p + vec3(0.0, 1.0, 0.0); + v1 = p + vec3(0.0, 1.0, 1.0); + v2 = p + vec3(1.0, 1.0, 1.0); + v3 = p + vec3(1.0, 1.0, 0.0); + + + uv0 = vec2(0.0, 0.5); + uv1 = vec2(0.0, 1.0); + uv2 = vec2(0.5, 1.0); + uv3 = vec2(0.5, 0.5); + } else if (faceIndex == 1) { + // Bottom -Y + normal = vec3(0.0, -1.0, 0.0); + tangent = vec3(1.0, 0.0, 0.0); + bitangent = vec3(0.0, 0.0, -1.0); + + v0 = p + vec3(0.0, 0.0, 0.0); + v1 = p + vec3(1.0, 0.0, 0.0); + v2 = p + vec3(1.0, 0.0, 1.0); + v3 = p + vec3(0.0, 0.0, 1.0); + uv0 = vec2(0.5, 0.0); + uv1 = vec2(0.5, 0.5); + uv2 = vec2(1.0, 0.5); + uv3 = vec2(1.0, 0.0); + } else if (faceIndex == 2) { + // Right +X + normal = vec3(1.0, 0.0, 0.0); + tangent = vec3(0.0, 0.0, -1.0); + bitangent = vec3(0.0, 1.0, 0.0); + + v0 = p + vec3(1.0, 0.0, 0.0); + v1 = p + vec3(1.0, 1.0, 0.0); + v2 = p + vec3(1.0, 1.0, 1.0); + v3 = p + vec3(1.0, 0.0, 1.0); + + uv0 = vec2(0.0, 0.0); + uv3 = vec2(0.0, 0.5); + uv2 = vec2(0.5, 0.5); + uv1 = vec2(0.5, 0.0); + } else if (faceIndex == 3) { + // Left -X + normal = vec3(-1.0, 0.0, 0.0); + tangent = vec3(0.0, 0.0, 1.0); + bitangent = vec3(0.0, 1.0, 0.0); + + v0 = p + vec3(0.0, 0.0, 0.0); + v1 = p + vec3(0.0, 0.0, 1.0); + v2 = p + vec3(0.0, 1.0, 1.0); + v3 = p + vec3(0.0, 1.0, 0.0); + + uv0 = vec2(0.0, 0.0); + uv1 = vec2(0.0, 0.5); + uv2 = vec2(0.5, 0.5); + uv3 = vec2(0.5, 0.0); + } else if (faceIndex == 4) { + // Front +Z + normal = vec3(0.0, 0.0, 1.0); + tangent = vec3(1.0, 0.0, 0.0); + bitangent = vec3(0.0, 1.0, 0.0); + + v0 = p + vec3(0.0, 0.0, 1.0); + v1 = p + vec3(1.0, 0.0, 1.0); + v2 = p + vec3(1.0, 1.0, 1.0); + v3 = p + vec3(0.0, 1.0, 1.0); + + uv0 = vec2(0.0, 0.0); + uv1 = vec2(0.0, 0.5); + uv2 = vec2(0.5, 0.5); + uv3 = vec2(0.5, 0.0); + } else { + // Back -Z + normal = vec3(0.0, 0.0, -1.0); + tangent = vec3(-1.0, 0.0, 0.0); + bitangent = vec3(0.0, 1.0, 0.0); + + v0 = p + vec3(0.0, 0.0, 0.0); + v1 = p + vec3(0.0, 1.0, 0.0); + v2 = p + vec3(1.0, 1.0, 0.0); + v3 = p + vec3(1.0, 0.0, 0.0); + + uv0 = vec2(0.0, 0.0); + uv1 = vec2(0.0, 0.5); + uv2 = vec2(0.5, 0.5); + uv3 = vec2(0.5, 0.0); + } + + writeVertex(baseVertex + 0u, v0, normal, tangent, bitangent, uv0); + writeVertex(baseVertex + 1u, v1, normal, tangent, bitangent, uv1); + writeVertex(baseVertex + 2u, v2, normal, tangent, bitangent, uv2); + writeVertex(baseVertex + 3u, v3, normal, tangent, bitangent, uv3); + + indices[baseIndex + 0u] = baseVertex + 0u; + indices[baseIndex + 1u] = baseVertex + 1u; + indices[baseIndex + 2u] = baseVertex + 2u; + + indices[baseIndex + 3u] = baseVertex + 0u; + indices[baseIndex + 4u] = baseVertex + 2u; + indices[baseIndex + 5u] = baseVertex + 3u; + + atomicAdd(drawCmd.indexCount, INDICES_PER_FACE); +} + +void main() { + ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); + + if (pos.x >= CHUNK_SIZE || pos.y >= CHUNK_SIZE || pos.z >= CHUNK_SIZE) { + return; + } + + uint current = getVoxel(pos); + + if (current == 0u) { + return; + } + + if (getVoxel(pos + ivec3( 0, 1, 0)) == 0u) emitFace(pos, 0); + if (getVoxel(pos + ivec3( 0, -1, 0)) == 0u) emitFace(pos, 1); + if (getVoxel(pos + ivec3( 1, 0, 0)) == 0u) emitFace(pos, 2); + if (getVoxel(pos + ivec3(-1, 0, 0)) == 0u) emitFace(pos, 3); + if (getVoxel(pos + ivec3( 0, 0, 1)) == 0u) emitFace(pos, 4); + if (getVoxel(pos + ivec3( 0, 0, -1)) == 0u) emitFace(pos, 5); +} \ No newline at end of file diff --git a/resources/EngineResources/VoxelComputeShaders/meshGenerationShared.glsl b/resources/EngineResources/VoxelComputeShaders/meshGenerationShared.glsl new file mode 100644 index 0000000..eeb73a7 --- /dev/null +++ b/resources/EngineResources/VoxelComputeShaders/meshGenerationShared.glsl @@ -0,0 +1,285 @@ +#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 VOXEL_COUNT = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE; + +const uint FLOATS_PER_VERTEX = 14u; +const uint VERTICES_PER_FACE = 4u; +const uint INDICES_PER_FACE = 6u; + +const uint MAX_VISIBLE_FACES_PER_CHUNK = uint(CHUNK_SIZE * CHUNK_SIZE * 12); +const uint MAX_VERTICES = MAX_VISIBLE_FACES_PER_CHUNK * VERTICES_PER_FACE; +const uint MAX_INDICES = MAX_VISIBLE_FACES_PER_CHUNK * INDICES_PER_FACE; + +layout(std430, binding = 0) readonly buffer VoxelData { + uint voxels[]; +}; + +layout(std430, binding = 1) buffer VertexBuffer { + float vertices[]; +}; + +layout(std430, binding = 2) buffer IndexBuffer { + uint indices[]; +}; + +struct DrawCommand { + uint indexCount; + uint instanceCount; + uint firstIndex; + int vertexOffset; + uint firstInstance; +}; + +layout(std430, binding = 3) buffer DrawCommands { + DrawCommand drawCmds[]; +}; + +layout(push_constant) uniform ChunkInfo { + ivec3 chunkPos; + int slot; + uint vertexFloatOffset; + uint indexOffset; + uint indirectCommandIndex; + uint padding0; +}; + + +layout(std430, binding = 4) buffer Counters { + uint vertexCount; + uint indexCount; +} counters; + + +uint flatten(ivec3 pos) { + return uint((pos.x * CHUNK_AREA) + (pos.y * CHUNK_SIZE) + pos.z); +} + +uint getVoxel(ivec3 pos) { + if (pos.x < 0 || pos.x >= CHUNK_SIZE) return 0; + if (pos.y < 0 || pos.y >= CHUNK_SIZE) return 0; + if (pos.z < 0 || pos.z >= CHUNK_SIZE) return 0; + + return voxels[flatten(pos)]; +} + +void writeVertex(uint vertexIndex, vec3 position, vec3 normal, vec3 tangent, vec3 bitangent, vec2 uv) { + uint base = vertexFloatOffset + vertexIndex * FLOATS_PER_VERTEX; + + vertices[base + 0u] = position.x; + vertices[base + 1u] = position.y; + vertices[base + 2u] = position.z; + + vertices[base + 3u] = normal.x; + vertices[base + 4u] = normal.y; + vertices[base + 5u] = normal.z; + + vertices[base + 6u] = tangent.x; + vertices[base + 7u] = tangent.y; + vertices[base + 8u] = tangent.z; + + vertices[base + 9u] = bitangent.x; + vertices[base + 10u] = bitangent.y; + vertices[base + 11u] = bitangent.z; + + vertices[base + 12u] = uv.x; + vertices[base + 13u] = uv.y; +} + +void emitFace(ivec3 voxelPos, int faceIndex, uint VoxelType) { + uint localBaseVertex = atomicAdd(counters.vertexCount, VERTICES_PER_FACE); + uint localBaseIndex = atomicAdd(counters.indexCount, INDICES_PER_FACE); + + if (localBaseVertex + VERTICES_PER_FACE > MAX_VERTICES || + localBaseIndex + INDICES_PER_FACE > MAX_INDICES) { + return; + } + + uint baseVertex = localBaseVertex; + uint baseIndex = indexOffset + localBaseIndex; + + vec3 p = vec3(chunkPos * CHUNK_SIZE + voxelPos); + + vec3 normal; + vec3 tangent; + vec3 bitangent; + + + vec3 v0; + vec3 v1; + vec3 v2; + vec3 v3; + + vec2 uv0; + vec2 uv1; + vec2 uv2; + vec2 uv3; + + if (faceIndex == 0) { + // Top +Y + normal = vec3(0.0, 1.0, 0.0); + tangent = vec3(1.0, 0.0, 0.0); + bitangent = vec3(0.0, 0.0, 1.0); + + v0 = p + vec3(0.0, 1.0, 0.0); + v1 = p + vec3(0.0, 1.0, 1.0); + v2 = p + vec3(1.0, 1.0, 1.0); + v3 = p + vec3(1.0, 1.0, 0.0); + + if(VoxelType == 1) { + uv0 = vec2(0.0, 0.5); + uv1 = vec2(0.0, 1.0); + uv2 = vec2(0.5, 1.0); + uv3 = vec2(0.5, 0.5); + } else{ + uv0 = vec2(0.5, 0.0); + uv1 = vec2(0.5, 0.5); + uv2 = vec2(1.0, 0.5); + uv3 = vec2(1.0, 0.0); + } + } else if (faceIndex == 1) { + // Bottom -Y + normal = vec3(0.0, -1.0, 0.0); + tangent = vec3(1.0, 0.0, 0.0); + bitangent = vec3(0.0, 0.0, -1.0); + + v0 = p + vec3(0.0, 0.0, 0.0); + v1 = p + vec3(1.0, 0.0, 0.0); + v2 = p + vec3(1.0, 0.0, 1.0); + v3 = p + vec3(0.0, 0.0, 1.0); + uv0 = vec2(0.5, 0.0); + uv1 = vec2(0.5, 0.5); + uv2 = vec2(1.0, 0.5); + uv3 = vec2(1.0, 0.0); + } else if (faceIndex == 2) { + // Right +X + normal = vec3(1.0, 0.0, 0.0); + tangent = vec3(0.0, 0.0, -1.0); + bitangent = vec3(0.0, 1.0, 0.0); + + v0 = p + vec3(1.0, 0.0, 0.0); + v1 = p + vec3(1.0, 1.0, 0.0); + v2 = p + vec3(1.0, 1.0, 1.0); + v3 = p + vec3(1.0, 0.0, 1.0); + if(VoxelType == 1) { + + uv2 = vec2(0.0, 0.0); + uv3 = vec2(0.0, 0.5); + uv0 = vec2(0.5, 0.5); + uv1 = vec2(0.5, 0.0); + } else{ + uv0 = vec2(0.5, 0.0); + uv1 = vec2(0.5, 0.5); + uv2 = vec2(1.0, 0.5); + uv3 = vec2(1.0, 0.0); + } + } else if (faceIndex == 3) { + // Left -X + normal = vec3(-1.0, 0.0, 0.0); + tangent = vec3(0.0, 0.0, 1.0); + bitangent = vec3(0.0, 1.0, 0.0); + + v0 = p + vec3(0.0, 0.0, 0.0); + v1 = p + vec3(0.0, 0.0, 1.0); + v2 = p + vec3(0.0, 1.0, 1.0); + v3 = p + vec3(0.0, 1.0, 0.0); + + if(VoxelType == 1) { + + uv3 = vec2(0.0, 0.0); + uv0 = vec2(0.0, 0.5); + uv1 = vec2(0.5, 0.5); + uv2 = vec2(0.5, 0.0); + } else{ + uv0 = vec2(0.5, 0.0); + uv1 = vec2(0.5, 0.5); + uv2 = vec2(1.0, 0.5); + uv3 = vec2(1.0, 0.0); + } + } else if (faceIndex == 4) { + // Front +Z + normal = vec3(0.0, 0.0, 1.0); + tangent = vec3(1.0, 0.0, 0.0); + bitangent = vec3(0.0, 1.0, 0.0); + + v0 = p + vec3(0.0, 0.0, 1.0); + v1 = p + vec3(1.0, 0.0, 1.0); + v2 = p + vec3(1.0, 1.0, 1.0); + v3 = p + vec3(0.0, 1.0, 1.0); + + if(VoxelType == 1) { + + uv3 = vec2(0.0, 0.0); + uv0 = vec2(0.0, 0.5); + uv1 = vec2(0.5, 0.5); + uv2 = vec2(0.5, 0.0); + } else{ + uv0 = vec2(0.5, 0.0); + uv1 = vec2(0.5, 0.5); + uv2 = vec2(1.0, 0.5); + uv3 = vec2(1.0, 0.0); + } + } else { + // Back -Z + normal = vec3(0.0, 0.0, -1.0); + tangent = vec3(-1.0, 0.0, 0.0); + bitangent = vec3(0.0, 1.0, 0.0); + + v0 = p + vec3(0.0, 0.0, 0.0); + v1 = p + vec3(0.0, 1.0, 0.0); + v2 = p + vec3(1.0, 1.0, 0.0); + v3 = p + vec3(1.0, 0.0, 0.0); + + if(VoxelType == 1) { + + uv2 = vec2(0.0, 0.0); + uv3 = vec2(0.0, 0.5); + uv0 = vec2(0.5, 0.5); + uv1 = vec2(0.5, 0.0); + } else{ + uv0 = vec2(0.5, 0.0); + uv1 = vec2(0.5, 0.5); + uv2 = vec2(1.0, 0.5); + uv3 = vec2(1.0, 0.0); + } + } + + writeVertex(baseVertex + 0u, v0, normal, tangent, bitangent, uv0); + writeVertex(baseVertex + 1u, v1, normal, tangent, bitangent, uv1); + writeVertex(baseVertex + 2u, v2, normal, tangent, bitangent, uv2); + writeVertex(baseVertex + 3u, v3, normal, tangent, bitangent, uv3); + + indices[baseIndex + 0u] = baseVertex + 0u; + indices[baseIndex + 1u] = baseVertex + 1u; + indices[baseIndex + 2u] = baseVertex + 2u; + + indices[baseIndex + 3u] = baseVertex + 0u; + indices[baseIndex + 4u] = baseVertex + 2u; + indices[baseIndex + 5u] = baseVertex + 3u; + + atomicAdd(drawCmds[indirectCommandIndex].indexCount, INDICES_PER_FACE); +} + +void main() { + ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); + + if (pos.x >= CHUNK_SIZE || pos.y >= CHUNK_SIZE || pos.z >= CHUNK_SIZE) { + return; + } + + uint current = getVoxel(pos); + + if (current == 0u) { + return; + } + + if (getVoxel(pos + ivec3( 0, 1, 0)) == 0u) emitFace(pos, 0,current); + if (getVoxel(pos + ivec3( 0, -1, 0)) == 0u) emitFace(pos, 1,current); + if (getVoxel(pos + ivec3( 1, 0, 0)) == 0u) emitFace(pos, 2,current); + if (getVoxel(pos + ivec3(-1, 0, 0)) == 0u) emitFace(pos, 3,current); + if (getVoxel(pos + ivec3( 0, 0, 1)) == 0u) emitFace(pos, 4,current); + if (getVoxel(pos + ivec3( 0, 0, -1)) == 0u) emitFace(pos, 5,current); +} \ No newline at end of file diff --git a/resources/EngineResources/VoxelComputeShaders/resetVoxelDraw.glsl b/resources/EngineResources/VoxelComputeShaders/resetVoxelDraw.glsl new file mode 100644 index 0000000..91747c9 --- /dev/null +++ b/resources/EngineResources/VoxelComputeShaders/resetVoxelDraw.glsl @@ -0,0 +1,27 @@ +#version 460 + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(std430, binding = 0) buffer DrawCommand { + uint indexCount; + uint instanceCount; + uint firstIndex; + int vertexOffset; + uint firstInstance; +} drawCmd; + +layout(std430, binding = 1) buffer Counters { + uint vertexCount; + uint indexCount; +} counters; + +void main() { + drawCmd.indexCount = 0u; + drawCmd.instanceCount = 1u; + drawCmd.firstIndex = 0u; + drawCmd.vertexOffset = 0; + drawCmd.firstInstance = 0u; + + counters.vertexCount = 0u; + counters.indexCount = 0u; +} \ No newline at end of file diff --git a/resources/EngineResources/VoxelComputeShaders/resetVoxelDrawShared.glsl b/resources/EngineResources/VoxelComputeShaders/resetVoxelDrawShared.glsl new file mode 100644 index 0000000..fab801d --- /dev/null +++ b/resources/EngineResources/VoxelComputeShaders/resetVoxelDrawShared.glsl @@ -0,0 +1,42 @@ +#version 460 + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +const uint FLOATS_PER_VERTEX = 14u; + +struct DrawCommand { + uint indexCount; + uint instanceCount; + uint firstIndex; + int vertexOffset; + uint firstInstance; +}; + +layout(std430, binding = 0) buffer DrawCommands { + DrawCommand drawCmds[]; +}; + +layout(std430, binding = 1) buffer Counters { + uint vertexCount; + uint indexCount; +} counters; + +layout(push_constant) uniform ChunkInfo { + ivec3 chunkPos; + int slot; + uint vertexFloatOffset; + uint indexOffset; + uint indirectCommandIndex; + uint padding0; +}; + +void main() { + drawCmds[indirectCommandIndex].indexCount = 0u; + drawCmds[indirectCommandIndex].instanceCount = 1u; + drawCmds[indirectCommandIndex].firstIndex = indexOffset; + drawCmds[indirectCommandIndex].vertexOffset = int(vertexFloatOffset / FLOATS_PER_VERTEX); + drawCmds[indirectCommandIndex].firstInstance = 0u; + + counters.vertexCount = 0u; + counters.indexCount = 0u; +} \ No newline at end of file diff --git a/resources/EngineResources/VoxelComputeShaders/voxelGeneration.glsl b/resources/EngineResources/VoxelComputeShaders/voxelGeneration.glsl new file mode 100644 index 0000000..e721a1d --- /dev/null +++ b/resources/EngineResources/VoxelComputeShaders/voxelGeneration.glsl @@ -0,0 +1,201 @@ +#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; + +// A 1D storage buffer representing a flat 3D array of voxel IDs +layout(std430, binding = 0) buffer VoxelData { + uint voxels[]; +}; + +layout(push_constant) uniform ChunkOffset { + ivec3 chunkPos; + int padding0; +}; +// Description : Array and textureless GLSL 2D/3D/4D simplex +// noise functions. +// Author : Ian McEwan, Ashima Arts. +// Maintainer : stegu +// Lastmod : 20110822 (ijm) +// License : Copyright (C) 2011 Ashima Arts. All rights reserved. +// Distributed under the MIT License. See LICENSE file. +// https://github.com + +vec4 permute(vec4 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); } +vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } + +float simplex_noise(vec3 v) { + const vec2 C = vec2(1.0/6.0, 1.0/3.0); + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); + + // First corner + vec3 i = floor(v + dot(v, C.yyy)); + vec3 x0 = v - i + dot(i, C.xxx); + + // Other corners + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min(g.xyz, l.zxy); + vec3 i2 = max(g.xyz, l.zxy); + + // x0 = x0 - 0.0 + 0.0 * C.xxx; + // x1 = x0 - i1 + 1.0 * C.xxx; + // x2 = x0 - i2 + 2.0 * C.xxx; + // x3 = x0 - 1.0 + 3.0 * C.xxx; + vec3 x1 = x0 - i1 + C.xxx; + vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y + vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y + + // Permutations + i = mod(i, 289.0); + vec4 p = permute(permute(permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0)) + + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + + i.x + vec4(0.0, i1.x, i2.x, 1.0)); + + // Gradients: 7x7 points over a square, mapped onto an octahedron. + // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) + float n_ = 0.142857142857; // 1.0/7.0 + vec3 ns = n_ * D.wyz - D.xzx; + + vec4 j = p - 49.0 * floor(p * ns.z); // mod(p,7*7) + + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) + + vec4 x = x_ * ns.x + ns.yyyy; + vec4 y = y_ * ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); + + vec4 b0 = vec4(x.xy, y.xy); + vec4 b1 = vec4(x.zw, y.zw); + + //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; + //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; + vec4 s0 = floor(b0) * 2.0 + 1.0; + vec4 s1 = floor(b1) * 2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0)); + + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + + vec3 p0 = vec3(a0.xy, h.x); + vec3 p1 = vec3(a0.zw, h.y); + vec3 p2 = vec3(a1.xy, h.z); + vec3 p3 = vec3(a1.zw, h.w); + + // Normalise gradients + vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + // Mix final noise value + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + m = m * m; + + // Returns a value scaled exactly between -1.0 and 1.0 + return 42.0 * dot(m * m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3))); +} +float noise2D(vec2 p) { + return simplex_noise(vec3(p.x, p.y, 0.0)); +} + +float fbm(vec2 p, int octaves, float lacunarity, float gain) { + float value = 0.0; + float amplitude = 0.5; + float frequency = 1.0; + float amplitudeSum = 0.0; + + for (int i = 0; i < octaves; i++) { + value += noise2D(p * frequency) * amplitude; + amplitudeSum += amplitude; + + frequency *= lacunarity; + amplitude *= gain; + } + + return value / amplitudeSum; +} + +float ridgedFbm(vec2 p, int octaves, float lacunarity, float gain) { + float value = 0.0; + float amplitude = 0.5; + float frequency = 1.0; + float amplitudeSum = 0.0; + + for (int i = 0; i < octaves; i++) { + float n = noise2D(p * frequency); + n = 1.0 - abs(n); + n = n * n; + + value += n * amplitude; + amplitudeSum += amplitude; + + frequency *= lacunarity; + amplitude *= gain; + } + + return value / amplitudeSum; +} + +vec2 domainWarp(vec2 p) { + float wx = fbm(p + vec2(17.31, 91.73), 3, 2.0, 0.5); + float wz = fbm(p + vec2(43.17, 12.89), 3, 2.0, 0.5); + + return p + vec2(wx, wz) * 35.0; +} + +float terrainHeight(vec2 worldXZ) { + vec2 p = worldXZ; + + vec2 warped = domainWarp(p * 0.006); + + float continent = fbm(warped * 0.45, 5, 2.0, 0.5); + continent = continent * 0.5 + 0.5; + + float hills = fbm(p * 0.025, 5, 2.0, 0.48); + float detail = fbm(p * 0.09, 3, 2.1, 0.45); + + float mountainMask = smoothstep(0.52, 0.82, continent); + float mountains = ridgedFbm(warped * 1.15, 5, 2.0, 0.52); + + float height = 18.0; + height += continent * 28.0; + height += hills * 14.0; + height += detail * 3.0; + height += mountainMask * mountains * 55.0; + + return height; +} + +void main() { + ivec3 localPos = ivec3(gl_GlobalInvocationID.xyz); + + if (localPos.x >= CHUNK_SIZE || localPos.y >= CHUNK_SIZE || localPos.z >= CHUNK_SIZE) { + return; + } + + ivec3 worldPos = chunkPos * CHUNK_SIZE + localPos; + + float height = terrainHeight(vec2(worldPos.x,worldPos.z) * 0.02) * 0.0015 + 10 ; + + uint voxelType = 0u; + + if (float(worldPos.y) <= height) { + float depthBelowSurface = height - float(worldPos.y); + + if (depthBelowSurface < 1.5) { + voxelType = 1u; // Grass/topsoil + } else if (depthBelowSurface < 5.0) { + voxelType = 2u; // Dirt + } else { + voxelType = 3u; // Stone + } + } + + uint index = uint((localPos.x * CHUNK_AREA) + (localPos.y * CHUNK_SIZE) + localPos.z); + voxels[index] = voxelType; +} \ No newline at end of file diff --git a/resources/EngineResources/VoxelComputeShaders/voxelGenerationShared.glsl b/resources/EngineResources/VoxelComputeShaders/voxelGenerationShared.glsl new file mode 100644 index 0000000..bfcebc6 --- /dev/null +++ b/resources/EngineResources/VoxelComputeShaders/voxelGenerationShared.glsl @@ -0,0 +1,260 @@ +#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; + +// A 1D storage buffer representing a flat 3D array of voxel IDs +layout(std430, binding = 0) buffer VoxelData { + uint voxels[]; +}; + +layout(push_constant) uniform ChunkOffset { + ivec3 chunkPos; + int slot; + uint vertexFloatOffset; + uint indexOffset; + uint indirectCommandIndex; + uint padding0; +}; +// Description : Array and textureless GLSL 2D/3D/4D simplex +// noise functions. +// Author : Ian McEwan, Ashima Arts. +// Maintainer : stegu +// Lastmod : 20110822 (ijm) +// License : Copyright (C) 2011 Ashima Arts. All rights reserved. +// Distributed under the MIT License. See LICENSE file. +// https://github.com + +vec4 permute(vec4 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); } +vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } + +float simplex_noise(vec3 v) { + const vec2 C = vec2(1.0/6.0, 1.0/3.0); + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); + + // First corner + vec3 i = floor(v + dot(v, C.yyy)); + vec3 x0 = v - i + dot(i, C.xxx); + + // Other corners + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min(g.xyz, l.zxy); + vec3 i2 = max(g.xyz, l.zxy); + + // x0 = x0 - 0.0 + 0.0 * C.xxx; + // x1 = x0 - i1 + 1.0 * C.xxx; + // x2 = x0 - i2 + 2.0 * C.xxx; + // x3 = x0 - 1.0 + 3.0 * C.xxx; + vec3 x1 = x0 - i1 + C.xxx; + vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y + vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y + + // Permutations + i = mod(i, 289.0); + vec4 p = permute(permute(permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0)) + + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + + i.x + vec4(0.0, i1.x, i2.x, 1.0)); + + // Gradients: 7x7 points over a square, mapped onto an octahedron. + // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) + float n_ = 0.142857142857; // 1.0/7.0 + vec3 ns = n_ * D.wyz - D.xzx; + + vec4 j = p - 49.0 * floor(p * ns.z); // mod(p,7*7) + + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) + + vec4 x = x_ * ns.x + ns.yyyy; + vec4 y = y_ * ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); + + vec4 b0 = vec4(x.xy, y.xy); + vec4 b1 = vec4(x.zw, y.zw); + + //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; + //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; + vec4 s0 = floor(b0) * 2.0 + 1.0; + vec4 s1 = floor(b1) * 2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0)); + + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + + vec3 p0 = vec3(a0.xy, h.x); + vec3 p1 = vec3(a0.zw, h.y); + vec3 p2 = vec3(a1.xy, h.z); + vec3 p3 = vec3(a1.zw, h.w); + + // Normalise gradients + vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + // Mix final noise value + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + m = m * m; + + // Returns a value scaled exactly between -1.0 and 1.0 + return 42.0 * dot(m * m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3))); +} +float noise2D(vec2 p) { + return simplex_noise(vec3(p.x, p.y, 0.0)); +} + +float fbm(vec2 p, int octaves, float lacunarity, float gain) { + float value = 0.0; + float amplitude = 0.5; + float frequency = 1.0; + float amplitudeSum = 0.0; + + for (int i = 0; i < octaves; i++) { + value += noise2D(p * frequency) * amplitude; + amplitudeSum += amplitude; + + frequency *= lacunarity; + amplitude *= gain; + } + + return value / amplitudeSum; +} + +float ridgedFbm(vec2 p, int octaves, float lacunarity, float gain) { + float value = 0.0; + float amplitude = 0.5; + float frequency = 1.0; + float amplitudeSum = 0.0; + + for (int i = 0; i < octaves; i++) { + float n = noise2D(p * frequency); + n = 1.0 - abs(n); + n = n * n; + + value += n * amplitude; + amplitudeSum += amplitude; + + frequency *= lacunarity; + amplitude *= gain; + } + + return value / amplitudeSum; +} + +vec2 domainWarp(vec2 p) { + float wx = fbm(p + vec2(17.31, 91.73), 3, 2.0, 0.5); + float wz = fbm(p + vec2(43.17, 12.89), 3, 2.0, 0.5); + + return p + vec2(wx, wz) * 35.0; +} + +float terrainHeight(vec2 worldXZ) { + vec2 p = worldXZ; + + vec2 warped = domainWarp(p * 0.004); + + float broad = fbm(warped * 0.45, 5, 2.0, 0.5); + broad = broad * 0.5 + 0.5; + + float hills = fbm(p * 0.025, 4, 2.0, 0.48); + float detail = fbm(p * 0.085, 2, 2.0, 0.4); + + float height = 8.0; + height += broad * 12.0; + height += hills * 6.0; + height += detail * 2.0; + + return clamp(height, 4.0, 28.0); +} + +float rand(vec2 co) { + return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453123); +} +float fbm3D(vec3 p, int octaves, float lacunarity, float gain) { + float value = 0.0; + float amplitude = 0.5; + float frequency = 1.0; + float amplitudeSum = 0.0; + + for (int i = 0; i < octaves; i++) { + value += simplex_noise(p * frequency) * amplitude; + amplitudeSum += amplitude; + + frequency *= lacunarity; + amplitude *= gain; + } + + return value / amplitudeSum; +} +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; +} + +vec2 randG(vec2 p) { + p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3))); + return -1.0 + 2.0 * fract(sin(p) * 43758.5453123); +} + +float perlinNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + + vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0); + + float dotTopLeft = dot(randG(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)); + float dotTopRight = dot(randG(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)); + float dotBottomLeft = dot(randG(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)); + float dotBottomRight = dot(randG(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)); + + return mix(mix(dotTopLeft, dotTopRight, u.x), + mix(dotBottomLeft, dotBottomRight, u.x), u.y); +} + +void main() { + ivec3 localPos = ivec3(gl_GlobalInvocationID.xyz); + ivec3 worldPos = chunkPos * CHUNK_SIZE + localPos; + + float height = valueNoise(vec2(worldPos.x, worldPos.z) * 0.005) * 100 + valueNoise(vec2(worldPos.x, worldPos.z) * 0.01) * 10 + valueNoise(vec2(worldPos.x, worldPos.z) * 0.1) * 5+ valueNoise(vec2(worldPos.x, worldPos.z)) * 0.5; + //height = clamp(height, 4.0, 28.0); + + uint voxelType = 0u; + + if (float(worldPos.y) <= height && float(floor(worldPos.y/CHUNK_SIZE) * CHUNK_SIZE + CHUNK_SIZE * 2) >= height) { + float depthBelowSurface = height - float(worldPos.y); + + if (depthBelowSurface < 1.5) { + voxelType = 1u; // Grass/topsoil + } else if (depthBelowSurface < 5.0) { + voxelType = 2u; // Dirt + } else { + voxelType = 3u; // Stone + } + } + +// if (voxelType != 0u && worldPos.y < int(height) - 6) { +// float cave = fbm3D(vec3(worldPos) * 0.045, 4, 2.0, 0.5); +// +// if (cave > 0.42) { +// voxelType = 0u; +// } +// } + + uint index = uint((localPos.x * CHUNK_AREA) + (localPos.y * CHUNK_SIZE) + localPos.z); + voxels[index] = voxelType; +} \ No newline at end of file diff --git a/resources/EngineResources/shaders/forward_skybox_fragment.glsl.spv b/resources/EngineResources/shaders/forward_skybox_fragment.glsl.spv deleted file mode 100644 index c62d0b9..0000000 Binary files a/resources/EngineResources/shaders/forward_skybox_fragment.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/lighting_frag.glsl b/resources/EngineResources/shaders/lighting_frag.glsl index 515758c..7a5f481 100644 --- a/resources/EngineResources/shaders/lighting_frag.glsl +++ b/resources/EngineResources/shaders/lighting_frag.glsl @@ -139,8 +139,6 @@ void main() { float emissiveness = emissive.r; float ssao = texture(ssaoBlur, inTextCoord).r; - outFragColor = vec4(vec3(normalW.a/2.0,normalW.a/2.0,normalW.a/2.0), 1); - return; float roughness = pbr.g; float metallic = pbr.b; diff --git a/resources/EngineResources/shaders/lighting_frag.glsl.spv b/resources/EngineResources/shaders/lighting_frag.glsl.spv deleted file mode 100644 index 2fdb098..0000000 Binary files a/resources/EngineResources/shaders/lighting_frag.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/multi-sampled_post_process_frag.glsl.spv b/resources/EngineResources/shaders/multi-sampled_post_process_frag.glsl.spv deleted file mode 100644 index 2b3bfbe..0000000 Binary files a/resources/EngineResources/shaders/multi-sampled_post_process_frag.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/post_process_frag.glsl.spv b/resources/EngineResources/shaders/post_process_frag.glsl.spv deleted file mode 100644 index ad42b72..0000000 Binary files a/resources/EngineResources/shaders/post_process_frag.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/scene_fragment.glsl b/resources/EngineResources/shaders/scene_fragment.glsl index fd71ca3..f0c1558 100644 --- a/resources/EngineResources/shaders/scene_fragment.glsl +++ b/resources/EngineResources/shaders/scene_fragment.glsl @@ -66,9 +66,13 @@ void main() } else{ outAlbedo = material.diffuseColor; } - vec4 Opacity = vec4(outAlbedo.a,outAlbedo.a,outAlbedo.a,1); - if(material.OpacityFactor != 1) Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1); - if(material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES){ + vec4 Opacity = vec4(outAlbedo.a, outAlbedo.a, outAlbedo.a, 1.0); + + if (material.OpacityFactor > 0.0 && material.OpacityFactor < 1.0) { + Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1.0); + } + + if (material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES) { Opacity = texture(textSampler[material.OpacityMapIdx], inTextCoords); } diff --git a/resources/EngineResources/shaders/scene_fragment.glsl.spv b/resources/EngineResources/shaders/scene_fragment.glsl.spv deleted file mode 100644 index a05b0b0..0000000 Binary files a/resources/EngineResources/shaders/scene_fragment.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/scene_fragment_deferred.glsl b/resources/EngineResources/shaders/scene_fragment_deferred.glsl index 654efaa..e41ff86 100644 --- a/resources/EngineResources/shaders/scene_fragment_deferred.glsl +++ b/resources/EngineResources/shaders/scene_fragment_deferred.glsl @@ -88,11 +88,16 @@ void main() outAlbedo = material.diffuseColor; } - vec4 Opacity = vec4(outAlbedo.a,outAlbedo.a,outAlbedo.a,1); - if(material.OpacityFactor != 1) Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1); - if(material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES){ + vec4 Opacity = vec4(outAlbedo.a, outAlbedo.a, outAlbedo.a, 1.0); + + if (material.OpacityFactor > 0.0 && material.OpacityFactor < 1.0) { + Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1.0); + } + + if (material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES) { Opacity = texture(textSampler[material.OpacityMapIdx], inTextCoords); } + outOpacity = Opacity; float opacityf = Opacity.x + Opacity.y + Opacity.z; diff --git a/resources/EngineResources/shaders/scene_fragment_deferred.glsl.spv b/resources/EngineResources/shaders/scene_fragment_deferred.glsl.spv deleted file mode 100644 index 0722619..0000000 Binary files a/resources/EngineResources/shaders/scene_fragment_deferred.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/scene_opaque_fragment.glsl b/resources/EngineResources/shaders/scene_opaque_fragment.glsl index 17d2403..8bf5bc4 100644 --- a/resources/EngineResources/shaders/scene_opaque_fragment.glsl +++ b/resources/EngineResources/shaders/scene_opaque_fragment.glsl @@ -65,11 +65,16 @@ void main() } else{ outAlbedo = material.diffuseColor; } - vec4 Opacity = vec4(outAlbedo.a,outAlbedo.a,outAlbedo.a,1); - if(material.OpacityFactor != 1) Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1); - if(material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES){ + vec4 Opacity = vec4(outAlbedo.a, outAlbedo.a, outAlbedo.a, 1.0); + + if (material.OpacityFactor > 0.0 && material.OpacityFactor < 1.0) { + Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1.0); + } + + if (material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES) { Opacity = texture(textSampler[material.OpacityMapIdx], inTextCoords); } + float opacityf = Opacity.x + Opacity.y + Opacity.z; opacityf = opacityf/3; if(opacityf < 0.9){discard;} diff --git a/resources/EngineResources/shaders/scene_opaque_fragment.glsl.spv b/resources/EngineResources/shaders/scene_opaque_fragment.glsl.spv deleted file mode 100644 index 156f36f..0000000 Binary files a/resources/EngineResources/shaders/scene_opaque_fragment.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/scene_opaque_fragment_deferred.glsl b/resources/EngineResources/shaders/scene_opaque_fragment_deferred.glsl index 8a14dc0..5d79877 100644 --- a/resources/EngineResources/shaders/scene_opaque_fragment_deferred.glsl +++ b/resources/EngineResources/shaders/scene_opaque_fragment_deferred.glsl @@ -94,9 +94,13 @@ void main() outAlbedo = material.diffuseColor; } - vec4 Opacity = vec4(outAlbedo.a,outAlbedo.a,outAlbedo.a,1); - if(material.OpacityFactor != 1) Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1); - if(material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES){ + vec4 Opacity = vec4(outAlbedo.a, outAlbedo.a, outAlbedo.a, 1.0); + + if (material.OpacityFactor > 0.0 && material.OpacityFactor < 1.0) { + Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1.0); + } + + if (material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES) { Opacity = texture(textSampler[material.OpacityMapIdx], inTextCoords); } outOpacity = Opacity; diff --git a/resources/EngineResources/shaders/scene_translucent_fragment.glsl b/resources/EngineResources/shaders/scene_translucent_fragment.glsl index a7c138a..f646f6c 100644 --- a/resources/EngineResources/shaders/scene_translucent_fragment.glsl +++ b/resources/EngineResources/shaders/scene_translucent_fragment.glsl @@ -64,9 +64,13 @@ void main() } else{ outAlbedo = material.diffuseColor; } - vec4 Opacity = vec4(outAlbedo.a,outAlbedo.a,outAlbedo.a,1); - if(material.OpacityFactor != 1) Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1); - if(material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES){ + vec4 Opacity = vec4(outAlbedo.a, outAlbedo.a, outAlbedo.a, 1.0); + + if (material.OpacityFactor > 0.0 && material.OpacityFactor < 1.0) { + Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1.0); + } + + if (material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES) { Opacity = texture(textSampler[material.OpacityMapIdx], inTextCoords); } diff --git a/resources/EngineResources/shaders/scene_translucent_fragment.glsl.spv b/resources/EngineResources/shaders/scene_translucent_fragment.glsl.spv deleted file mode 100644 index da41461..0000000 Binary files a/resources/EngineResources/shaders/scene_translucent_fragment.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/scene_translucent_fragment_deferred.glsl b/resources/EngineResources/shaders/scene_translucent_fragment_deferred.glsl index d3122f2..346bdb0 100644 --- a/resources/EngineResources/shaders/scene_translucent_fragment_deferred.glsl +++ b/resources/EngineResources/shaders/scene_translucent_fragment_deferred.glsl @@ -96,9 +96,13 @@ void main() outAlbedo = material.diffuseColor; } - vec4 Opacity = vec4(outAlbedo.a,outAlbedo.a,outAlbedo.a,1); - if(material.OpacityFactor != 1) Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1); - if(material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES){ + vec4 Opacity = vec4(outAlbedo.a, outAlbedo.a, outAlbedo.a, 1.0); + + if (material.OpacityFactor > 0.0 && material.OpacityFactor < 1.0) { + Opacity = vec4(material.OpacityFactor, material.OpacityFactor, material.OpacityFactor, 1.0); + } + + if (material.hasOpacityMap > 0 && material.OpacityMapIdx < MAX_TEXTURES) { Opacity = texture(textSampler[material.OpacityMapIdx], inTextCoords); } outOpacity = Opacity; diff --git a/resources/EngineResources/shaders/scene_vertex.glsl.spv b/resources/EngineResources/shaders/scene_vertex.glsl.spv deleted file mode 100644 index 4a1d6a1..0000000 Binary files a/resources/EngineResources/shaders/scene_vertex.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/skybox_fragment.glsl.spv b/resources/EngineResources/shaders/skybox_fragment.glsl.spv deleted file mode 100644 index 3f18f85..0000000 Binary files a/resources/EngineResources/shaders/skybox_fragment.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/skybox_vertex.glsl.spv b/resources/EngineResources/shaders/skybox_vertex.glsl.spv deleted file mode 100644 index bf0a2ce..0000000 Binary files a/resources/EngineResources/shaders/skybox_vertex.glsl.spv and /dev/null differ diff --git a/resources/EngineResources/shaders/swap_frag.glsl.spv b/resources/EngineResources/shaders/swap_frag.glsl.spv deleted file mode 100644 index 67de60a..0000000 Binary files a/resources/EngineResources/shaders/swap_frag.glsl.spv and /dev/null differ diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Display/VulkanRenderer.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Display/VulkanRenderer.java index fbd0a2e..8703583 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Display/VulkanRenderer.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Display/VulkanRenderer.java @@ -5,6 +5,8 @@ import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance; import net.halbear.Terrain4J.EngineCore.Logic.InitData; import net.halbear.Terrain4J.EngineCore.Logic.Rendering.SkyBox; import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelChunkGenerator; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.GUI.GuiRenderer; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.GUI.GuiTexture; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.Lighting.LightRenderer; @@ -28,6 +30,8 @@ import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.SwapChain. import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.SwapChain.SwapChainRender; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils; import net.halbear.Terrain4J.EngineCore.Threads.RenderThread; +import org.joml.Vector3f; +import org.joml.Vector3i; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.VkCommandBufferSubmitInfo; import org.lwjgl.vulkan.VkExtent2D; @@ -179,6 +183,8 @@ public class VulkanRenderer implements Renderer { spriteRenderer.CompileSprites(RendererContext,modelsCache, textureCache); spriteRenderer.LoadMaterials(RendererContext,materialsCache,textureCache); GuiRender.LoadTextures(RendererContext,initData.GuiTextures(),textureCache); + VoxelWorldManager.Init(RendererContext); + // VoxelWorldManager.GenerateChunks(new Vector3i(0,0,0), new Vector3i(16,0,16)); } public GuiRenderer GetGUIRenderer(){return GuiRender;} @@ -196,6 +202,9 @@ public class VulkanRenderer implements Renderer { public void cleanup() { RendererContext.GetDevice().waitIdle(); Logger.debug("Waiting Vulkan Context"); + + VoxelWorldManager.Cleanup(RendererContext); + sceneRender.cleanup(RendererContext); if(ssaoRenderer != null)ssaoRenderer.cleanup(RendererContext); if(ssrRender != null)ssrRender.cleanup(RendererContext); @@ -239,7 +248,7 @@ public class VulkanRenderer implements Renderer { public static volatile boolean Debug = false; public static volatile DebugRenderMode debugMode = DebugRenderMode.Albedo; - + Vector3f CamPos = new Vector3f(); public void DeferredRender(EngineInstance engineInstance){ SwapChain swapChain = RendererContext.GetSwapChain(); WaitForFence(CurrentFrame); @@ -247,9 +256,8 @@ public class VulkanRenderer implements Renderer { var CommandPool = CommandPools[CurrentFrame]; var CommandBuffer = CommandBuffers[CurrentFrame]; - RecordingStart(CommandPool, CommandBuffer); - + VoxelWorldManager.RecordGeneration(RendererContext,CommandBuffer); sceneRender.Render(engineInstance,RendererContext,CommandBuffer, modelsCache,materialsCache,CurrentFrame); boolean RenderShadows = ShadowRenderer.AllowShadowRendering() && EngineConfig.getInstance().RenderShadows(); if(RenderShadows) {shadowRender.render(engineInstance, RendererContext, CommandBuffer, modelsCache, materialsCache, CurrentFrame); } @@ -289,9 +297,8 @@ public class VulkanRenderer implements Renderer { resize(engineInstance); return; } - RecordingStart(CommandPool, CommandBuffer); - + VoxelWorldManager.RecordGeneration(RendererContext,CommandBuffer); sceneRender.Render(engineInstance,RendererContext,CommandBuffer, modelsCache,materialsCache,CurrentFrame); PostProcessor.Render(RendererContext,CommandBuffer,sceneRender.GetAttachmentColour()); spriteRenderer.Render(engineInstance,RendererContext,CommandBuffer,PostProcessor.GetAttachment(),modelsCache,CurrentFrame); diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Main/GameCore.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Main/GameCore.java index 77af47b..31345c3 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Main/GameCore.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Main/GameCore.java @@ -29,6 +29,8 @@ import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.Noise2D; import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.PermutationArray; import net.halbear.Terrain4J.EngineCore.Profiling.CPUMonitor; import net.halbear.Terrain4J.EngineCore.Profiling.GPUProfiler; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelChunkGenerator; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ServerSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.Threads.RenderThread; @@ -36,13 +38,11 @@ import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPass import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.MaterialData; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.ModelData; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.Forward.ForwardSceneRender; -import org.joml.Matrix4f; -import org.joml.Vector2f; -import org.joml.Vector3f; -import org.joml.Vector3i; +import org.joml.*; import org.lwjgl.openal.AL11; import org.tinylog.Logger; +import java.lang.Math; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -51,7 +51,7 @@ import static org.lwjgl.glfw.GLFW.*; public class GameCore implements GameLogic { private static final float MOUSE_SENSITIVITY = 0.1f; - private static final float MOVEMENT_SPEED = 0.01f; + private static final float MOVEMENT_SPEED = 0.1f; public static boolean LoadingLevel = false; private Vector2f LastMousePos = new Vector2f(0,0); @@ -161,6 +161,9 @@ public class GameCore implements GameLogic { } boolean createOfflinePlayer = !PrimaryRuntime.IsServer && !ClientSideNetworkUtils.Connected; + MaterialData VoxelMat = new MaterialData("VoxelTerrain","resources/EngineResources/Texture/test_cube.png","","",new Vector4f(1,1,1,1), + 0,0,"",new Vector4f(),"",0,"",1,new Vector4f(),1.0f,0); + materials.add(VoxelMat); materials.addAll(MelonaMat); materials.addAll(CollisionVisualisationMat); materials.addAll(MelonaMaterial); @@ -283,11 +286,11 @@ public class GameCore implements GameLogic { permutation.GeneratePermutationArray(5783904701859L); for(int x = -1; x < 2; x++){ for(int y = -1; y < 2; y++){ - GenerateChunk(new Vector3i(x, 0, y), 16); + //GenerateChunk(new Vector3i(x, 0, y), 16); } } for(int i = 0; i < 20; i++){ - SpawnNewActor(engineInstance, new Vector3f(-5 + (float)Math.random() * 10,10 + (float)Math.random() * 10, -5 + (float)Math.random() * 10), new Vector3f(0, 0, 0), new Vector3f(1, 1, 1)); + //SpawnNewActor(engineInstance, new Vector3f(-5 + (float)Math.random() * 10,10 + (float)Math.random() * 10, -5 + (float)Math.random() * 10), new Vector3f(0, 0, 0), new Vector3f(1, 1, 1)); } Settings.textBuffer.set(ClientSideNetworkUtils.ServerIP); Settings.UsernameBuffer.set("Player"); @@ -651,10 +654,20 @@ public class GameCore implements GameLogic { FetchingMetrics = false; return imGuiIO.getWantCaptureKeyboard(); } - + Vector3f CamPos = new Vector3f(); @Override public void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds) { if(!PrimaryRuntime.IsServer && !RenderThread.Headless) { + CamPos.set(engineInstance.scene().GetCamera().GetPosition()); + Vector3i ChunkPos = new Vector3i((int)Math.floor(CamPos.x/ (float) VoxelChunkGenerator.CHUNK_SIZE),(int)Math.floor(CamPos.y/ (float)VoxelChunkGenerator.CHUNK_SIZE),(int)Math.floor(CamPos.z/ (float)VoxelChunkGenerator.CHUNK_SIZE)); + if(ChunkPos.x != VoxelWorldManager.LastPosition.x || ChunkPos.y != VoxelWorldManager.LastPosition.y || ChunkPos.z != VoxelWorldManager.LastPosition.z) { + VoxelWorldManager.GenerateChunks(ChunkPos, new Vector3i(5, 3, 5)); + VoxelWorldManager.UnloadFarChunks(ChunkPos, 10); + } else { + VoxelWorldManager.GenerateChunks(ChunkPos, new Vector3i(24,10,24)); + VoxelWorldManager.UnloadFarChunks(ChunkPos, 48); + } + for (int i = 0; i < VisualisedCollisionActor3D.CollisionVisuals.size(); i++) { VisualisedCollisionActor3D.CollisionVisuals.get(i).Update(); } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/ComputeVoxelTerrain.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/ComputeVoxelTerrain.java new file mode 100644 index 0000000..6ad847f --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/ComputeVoxelTerrain.java @@ -0,0 +1,330 @@ +package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration; + +import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig; +import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance; +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VulkanUtil.ComputePipeline; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Pipeline; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.PushConstantsRange; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.*; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.MaterialsCache; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.ModelsCache; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.CommandBuffer; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.Device; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanBuffer; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils; +import org.joml.Matrix4f; +import org.joml.Vector3i; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.util.shaderc.Shaderc; +import org.lwjgl.vulkan.*; +import org.tinylog.Logger; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import static net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.BufferBarrier; +import static org.lwjgl.system.MemoryUtil.memFree; +import static org.lwjgl.util.vma.Vma.VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; +import static org.lwjgl.vulkan.VK10.*; +import static org.lwjgl.vulkan.VK10.VK_PIPELINE_BIND_POINT_COMPUTE; +import static org.lwjgl.vulkan.VK10.VK_SHADER_STAGE_COMPUTE_BIT; +import static org.lwjgl.vulkan.VK10.vkCmdDispatch; +import static org.lwjgl.vulkan.VK10.vkCmdPushConstants; +import static org.lwjgl.vulkan.VK13.*; + +public class ComputeVoxelTerrain { + public static final int CHUNK_SIZE = 16; + public static final int LOCAL_SIZE = 4; + public static final int DISPATCH_SIZE = CHUNK_SIZE / LOCAL_SIZE; + + public static final int CHUNK_PUSH_CONSTANT_SIZE = Integer.BYTES * 4; + + private static final int VOXEL_COUNT = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE; + private static final int MAX_FACES = VOXEL_COUNT * 6; + private static final int MAX_VISIBLE_FACES_PER_CHUNK = CHUNK_SIZE * CHUNK_SIZE * 16; + private static final int MAX_VERTICES = MAX_VISIBLE_FACES_PER_CHUNK * 4; + private static final int MAX_INDICES = MAX_VISIBLE_FACES_PER_CHUNK * 6; + + private static final int FLOATS_PER_VERTEX = 14; + private static final int VERTEX_SIZE_BYTES = FLOATS_PER_VERTEX * Float.BYTES; + private static final int DRAW_INDEXED_INDIRECT_COMMAND_SIZE = 5 * Integer.BYTES; + private static final int COUNTER_BUFFER_SIZE = 2 * Integer.BYTES; + + private final String DESCRIPTOR_ID_MESH; + private final String DESCRIPTOR_ID_RESET; + private final String DESCRIPTOR_ID_VOXEL; + + private static final String MESH_GENERATION_GLSL = "resources/EngineResources/VoxelComputeShaders/meshGeneration.glsl"; + private static final String VOXEL_GENERATION_GLSL = "resources/EngineResources/VoxelComputeShaders/voxelGeneration.glsl"; + private static final String VOXEL_RESET_GLSL = "resources/EngineResources/VoxelComputeShaders/resetVoxelDraw.glsl"; + + private static final String MESH_GENERATION_SPV = MESH_GENERATION_GLSL + ".spv"; + private static final String VOXEL_GENERATION_SPV = VOXEL_GENERATION_GLSL + ".spv"; + private static final String VOXEL_RESET_SPV = VOXEL_RESET_GLSL + ".spv"; + + private final VulkanBuffer VoxelData; + private final VulkanBuffer VertexBuffer; + private final VulkanBuffer IndexBuffer; + private final VulkanBuffer DrawCommand; + private final VulkanBuffer Counters; + + private final DescriptorSetLayout voxelGenerationLayout; + private final DescriptorSetLayout resetLayout; + private final DescriptorSetLayout meshGenerationLayout; + + private final Pipeline voxelGeneration; + private final Pipeline voxelReset; + private final Pipeline meshGeneration; + + private final ByteBuffer graphicsPushConstants; + + private final int chunkX; + private final int chunkY; + private final int chunkZ; + + private boolean generated; + + public ComputeVoxelTerrain(VulkanContext VkCtx, int chunkX, int chunkY, int chunkZ) { + this.chunkX = chunkX; + this.chunkY = chunkY; + this.chunkZ = chunkZ; + this.generated = false; + DESCRIPTOR_ID_MESH = "DESC_ID_MESH_" + chunkX + "_" + chunkY + "_" + chunkZ; + DESCRIPTOR_ID_RESET = "DESC_ID_RESET_" + chunkX + "_" + chunkY + "_" + chunkZ; + DESCRIPTOR_ID_VOXEL = "DESC_ID_VOXEL_" + chunkX + "_" + chunkY + "_" + chunkZ; + long voxelBufferSize = (long) VOXEL_COUNT * Integer.BYTES; + long vertexBufferSize = (long) MAX_VERTICES * VERTEX_SIZE_BYTES; + long indexBufferSize = (long) MAX_INDICES * Integer.BYTES; + + VoxelData = new VulkanBuffer(VkCtx,voxelBufferSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 ); + VertexBuffer = new VulkanBuffer( VkCtx, vertexBufferSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 ); + IndexBuffer = new VulkanBuffer(VkCtx,indexBufferSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 ); + DrawCommand = new VulkanBuffer(VkCtx,DRAW_INDEXED_INDIRECT_COMMAND_SIZE,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 ); + Counters = new VulkanBuffer(VkCtx,COUNTER_BUFFER_SIZE,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 ); + voxelGenerationLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{ + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1, + VK_SHADER_STAGE_COMPUTE_BIT ) }); + resetLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{ + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1, + VK_SHADER_STAGE_COMPUTE_BIT ), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,1,1, + VK_SHADER_STAGE_COMPUTE_BIT ) }); + meshGenerationLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{ + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0, + 1,VK_SHADER_STAGE_COMPUTE_BIT), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,1, + 1,VK_SHADER_STAGE_COMPUTE_BIT), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,2, + 1,VK_SHADER_STAGE_COMPUTE_BIT), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,3, + 1,VK_SHADER_STAGE_COMPUTE_BIT), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,4, + 1,VK_SHADER_STAGE_COMPUTE_BIT),}); + createDescriptorSets(VkCtx); + PushConstantsRange[] chunkPushConstants = new PushConstantsRange[]{ + new PushConstantsRange( + VK_SHADER_STAGE_COMPUTE_BIT, + 0, + CHUNK_PUSH_CONSTANT_SIZE + ) + }; + ShaderModule resetShader = createComputeShader(VkCtx, VOXEL_RESET_GLSL, VOXEL_RESET_SPV); + ShaderModule voxelShader = createComputeShader(VkCtx, VOXEL_GENERATION_GLSL, VOXEL_GENERATION_SPV); + ShaderModule meshShader = createComputeShader(VkCtx, MESH_GENERATION_GLSL, MESH_GENERATION_SPV); + voxelReset = new ComputePipeline( VkCtx, resetShader,new DescriptorSetLayout[]{resetLayout}, + new PushConstantsRange[0]); + voxelGeneration = new ComputePipeline( VkCtx,voxelShader, new DescriptorSetLayout[]{voxelGenerationLayout}, + chunkPushConstants ); + meshGeneration = new ComputePipeline(VkCtx, meshShader,new DescriptorSetLayout[]{meshGenerationLayout}, + chunkPushConstants ); + resetShader.CleanUp(VkCtx); + voxelShader.CleanUp(VkCtx); + meshShader.CleanUp(VkCtx); + graphicsPushConstants = org.lwjgl.system.MemoryUtil.memAlloc(VulkanUtils.MATRIX4X4_SIZE + VulkanUtils.INT_SIZE); + } + + private static ShaderModule createComputeShader(VulkanContext VkCtx, String glslPath, String spvPath) { + if (EngineConfig.getInstance().RecompileShaders()) { + ShaderCompiler.CompileGLSLShaderOnChange(glslPath, Shaderc.shaderc_glsl_compute_shader); + } + + return new ShaderModule(VkCtx, VK_SHADER_STAGE_COMPUTE_BIT, spvPath, null); + } + + private void createDescriptorSets(VulkanContext VkCtx) { + DescriptorAllocator allocator = VkCtx.GetDescriptorAllocator(); + Device device = VkCtx.GetDevice(); + + DescriptorSet resetSet = allocator.AddDescriptorSet(device, DESCRIPTOR_ID_RESET, resetLayout); + resetSet.SetBuffer(device, DrawCommand, DrawCommand.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + resetSet.SetBuffer(device, Counters, Counters.GetRequestedSize(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + + DescriptorSet voxelSet = allocator.AddDescriptorSet(device, DESCRIPTOR_ID_VOXEL, voxelGenerationLayout); + voxelSet.SetBuffer(device, VoxelData, VoxelData.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + + DescriptorSet meshSet = allocator.AddDescriptorSet(device, DESCRIPTOR_ID_MESH, meshGenerationLayout); + meshSet.SetBuffer(device, VoxelData, VoxelData.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + meshSet.SetBuffer(device, VertexBuffer, VertexBuffer.GetRequestedSize(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + meshSet.SetBuffer(device, IndexBuffer, IndexBuffer.GetRequestedSize(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + meshSet.SetBuffer(device, DrawCommand, DrawCommand.GetRequestedSize(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + meshSet.SetBuffer(device, Counters, Counters.GetRequestedSize(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + } + + public void RecordGeneration(VulkanContext VkCtx, CommandBuffer commandBuffer) { + if (generated) { + return; + } + + try (MemoryStack MemStack = MemoryStack.stackPush()) { + VkCommandBuffer CommandBuffer = commandBuffer.GetVulkanCommandBuffer(); + DescriptorAllocator allocator = VkCtx.GetDescriptorAllocator(); + + ByteBuffer chunkPushBuffer = MemStack.malloc(CHUNK_PUSH_CONSTANT_SIZE); + chunkPushBuffer.putInt(0, chunkX); + chunkPushBuffer.putInt(4, chunkY); + chunkPushBuffer.putInt(8, chunkZ); + chunkPushBuffer.putInt(12, 0); + + LongBuffer resetDescriptorSet = MemStack.longs(allocator.GetDescriptorSet(DESCRIPTOR_ID_RESET).GetVkDescriptorSet()); + + LongBuffer voxelDescriptorSet = MemStack.longs(allocator.GetDescriptorSet(DESCRIPTOR_ID_VOXEL).GetVkDescriptorSet()); + + LongBuffer meshDescriptorSet = MemStack.longs(allocator.GetDescriptorSet(DESCRIPTOR_ID_MESH).GetVkDescriptorSet()); + + vkCmdBindPipeline(CommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE,voxelReset.GetVulkanPipeline()); + + vkCmdBindDescriptorSets(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,voxelReset.GetVulkanPipelineLayout(),0,resetDescriptorSet,null); + + vkCmdDispatch(CommandBuffer, 1, 1, 1); + + VulkanUtils.BufferBarriers(MemStack, CommandBuffer, + new long[]{DrawCommand.GetBuffer(),Counters.GetBuffer()}, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_SHADER_WRITE_BIT,VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT); + + vkCmdBindPipeline(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,voxelGeneration.GetVulkanPipeline()); + + vkCmdBindDescriptorSets(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,voxelGeneration.GetVulkanPipelineLayout(),0, voxelDescriptorSet,null); + + vkCmdPushConstants(CommandBuffer,voxelGeneration.GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0,chunkPushBuffer); + + vkCmdDispatch(CommandBuffer, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE); + + BufferBarrier(MemStack, CommandBuffer, VoxelData.GetBuffer(), + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_SHADER_WRITE_BIT,VK_ACCESS_2_SHADER_READ_BIT); + + vkCmdBindPipeline(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,meshGeneration.GetVulkanPipeline()); + + vkCmdBindDescriptorSets(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,meshGeneration.GetVulkanPipelineLayout(),0, meshDescriptorSet,null); + + vkCmdPushConstants(CommandBuffer, meshGeneration.GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0,chunkPushBuffer); + + vkCmdDispatch(CommandBuffer, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE); + + VulkanUtils.BufferBarriers(MemStack, CommandBuffer, + new long[]{VertexBuffer.GetBuffer(), + IndexBuffer.GetBuffer(), + DrawCommand.GetBuffer()}, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT, + VK_ACCESS_2_SHADER_WRITE_BIT,VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_2_INDEX_READ_BIT | VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT); + + generated = true; + } + } + + public void RecordRender(VkCommandBuffer CommandBuffer, Pipeline graphicsPipeline,Matrix4f modelMatrix, int materialIndex) { + try (MemoryStack stack = MemoryStack.stackPush()) { + modelMatrix.get(0, graphicsPushConstants); + graphicsPushConstants.putInt(VulkanUtils.MATRIX4X4_SIZE, materialIndex); + // Logger.debug("Rendering Chunk {}:{}:{}",chunkX,chunkY,chunkZ); + vkCmdPushConstants(CommandBuffer,graphicsPipeline.GetVulkanPipelineLayout(),VK_SHADER_STAGE_VERTEX_BIT,0, graphicsPushConstants.slice(0, VulkanUtils.MATRIX4X4_SIZE)); + + vkCmdPushConstants(CommandBuffer,graphicsPipeline.GetVulkanPipelineLayout(),VK_SHADER_STAGE_FRAGMENT_BIT,VulkanUtils.MATRIX4X4_SIZE, graphicsPushConstants.slice(VulkanUtils.MATRIX4X4_SIZE, VulkanUtils.INT_SIZE)); + + LongBuffer vertexBuffers = stack.longs(VertexBuffer.GetBuffer()); + LongBuffer offsets = stack.longs(0L); + + vkCmdBindVertexBuffers(CommandBuffer, 0, vertexBuffers, offsets); + vkCmdBindIndexBuffer(CommandBuffer, IndexBuffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32); + + //vkCmdDrawIndexed(CommandBuffer,MAX_INDICES,1,0,0,0); + vkCmdDrawIndexedIndirect(CommandBuffer,DrawCommand.GetBuffer(),0,1,DRAW_INDEXED_INDIRECT_COMMAND_SIZE); + } + } + + public VulkanBuffer GetVoxelDataBuffer() { + return VoxelData; + } + + public VulkanBuffer GetVertexBuffer() { + return VertexBuffer; + } + + public VulkanBuffer GetIndexBuffer() { + return IndexBuffer; + } + + public VulkanBuffer GetDrawCommandBuffer() { + return DrawCommand; + } + + public VulkanBuffer GetCountersBuffer() { + return Counters; + } + + public boolean IsGenerated() { + return generated; + } + + public void MarkDirty() { + generated = false; + } + + public Vector3i GetPosition(){ + return new Vector3i(chunkX,chunkY,chunkZ); + } + + public void cleanup(VulkanContext VkCtx) { + voxelGeneration.CleanUp(VkCtx); + voxelReset.CleanUp(VkCtx); + meshGeneration.CleanUp(VkCtx); + + voxelGenerationLayout.CleanUp(VkCtx); + resetLayout.CleanUp(VkCtx); + meshGenerationLayout.CleanUp(VkCtx); + + VoxelData.cleanup(VkCtx); + VertexBuffer.cleanup(VkCtx); + IndexBuffer.cleanup(VkCtx); + DrawCommand.cleanup(VkCtx); + Counters.cleanup(VkCtx); + + org.lwjgl.system.MemoryUtil.memFree(graphicsPushConstants); + } + + public void recordComputeCommandBuffer(VkCommandBuffer cmd, long computePipeline, long pipelineLayout, + long descriptorSet, int chunkX, int chunkY, int chunkZ) { + try (MemoryStack MemStack = MemoryStack.stackPush()) { + ByteBuffer pushConstants = MemStack.malloc(CHUNK_PUSH_CONSTANT_SIZE); + pushConstants.putInt(0, chunkX); + pushConstants.putInt(4, chunkY); + pushConstants.putInt(8, chunkZ); + pushConstants.putInt(12, 0); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline); + vkCmdBindDescriptorSets(cmd,VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, + 0,MemStack.longs(descriptorSet),null); + + vkCmdPushConstants(cmd,pipelineLayout,VK_SHADER_STAGE_COMPUTE_BIT,0,pushConstants); + + vkCmdDispatch(cmd, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE); + } + } +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/RenderChunk.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/RenderChunk.java new file mode 100644 index 0000000..9ca26fd --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/RenderChunk.java @@ -0,0 +1,6 @@ +package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration; + +import org.joml.Vector3i; + +public record RenderChunk(Vector3i position, int slot,long vertexOffsetBytes, long indexOffsetBytes,int maxIndexCount,long indirectCommandOffsetBytes) { +} \ No newline at end of file diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/SharedVoxelTerrainResources.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/SharedVoxelTerrainResources.java new file mode 100644 index 0000000..6129788 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/SharedVoxelTerrainResources.java @@ -0,0 +1,182 @@ +package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration; + +import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig; +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VulkanUtil.ComputePipeline; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Pipeline; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.PushConstantsRange; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.*; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.Device; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanBuffer; +import org.lwjgl.util.shaderc.Shaderc; + +import static org.lwjgl.util.vma.Vma.VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; +import static org.lwjgl.vulkan.VK13.*; + +public class SharedVoxelTerrainResources { + public static final int CHUNK_SIZE = 16; + public static final int VOXEL_COUNT = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE; + + public static final int DRAW_INDEXED_INDIRECT_COMMAND_SIZE = 5 * Integer.BYTES; + public static final int COUNTER_BUFFER_SIZE = 2 * Integer.BYTES; + + + public static final int TERRAIN_GENERATION_PUSH_CONSTANT_SIZE = Integer.BYTES * 8; + /*Push constants: + int chunkX + int chunkY + int chunkZ + int slot + uint vertexFloatOffset + uint indexOffset + uint indirectCommandOffset + uint padding */ + private static final String DESC_ID_VOXEL_GENERATION = "VOXEL_SHARED_DESC_GENERATION"; + private static final String DESC_ID_RESET = "VOXEL_SHARED_DESC_RESET"; + private static final String DESC_ID_MESH = "VOXEL_SHARED_DESC_MESH"; + + private static final String MESH_GENERATION_GLSL = "resources/EngineResources/VoxelComputeShaders/meshGenerationShared.glsl"; + private static final String VOXEL_GENERATION_GLSL = "resources/EngineResources/VoxelComputeShaders/voxelGenerationShared.glsl"; + private static final String VOXEL_RESET_GLSL = "resources/EngineResources/VoxelComputeShaders/resetVoxelDrawShared.glsl"; + + private static final String MESH_GENERATION_SPV = MESH_GENERATION_GLSL + ".spv"; + private static final String VOXEL_GENERATION_SPV = VOXEL_GENERATION_GLSL + ".spv"; + private static final String VOXEL_RESET_SPV = VOXEL_RESET_GLSL + ".spv"; + + private final VoxelMeshPool meshPool; + + private final VulkanBuffer voxelData; + private final VulkanBuffer counters; + + private final DescriptorSetLayout voxelGenerationLayout; + private final DescriptorSetLayout resetLayout; + private final DescriptorSetLayout meshGenerationLayout; + + private final Pipeline voxelGenerationPipeline; + private final Pipeline resetPipeline; + private final Pipeline meshGenerationPipeline; + + public SharedVoxelTerrainResources(VulkanContext VkCtx, int maxResidentChunks) { + meshPool = new VoxelMeshPool(VkCtx, maxResidentChunks); + + voxelData = new VulkanBuffer(VkCtx, (long) VOXEL_COUNT * Integer.BYTES, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 ); + + counters = new VulkanBuffer( VkCtx,COUNTER_BUFFER_SIZE,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 ); + + voxelGenerationLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{ + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1,VK_SHADER_STAGE_COMPUTE_BIT) + }); + + resetLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{ + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1,VK_SHADER_STAGE_COMPUTE_BIT), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,1,1,VK_SHADER_STAGE_COMPUTE_BIT) + }); + + meshGenerationLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{ + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1,VK_SHADER_STAGE_COMPUTE_BIT), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,1,1,VK_SHADER_STAGE_COMPUTE_BIT), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,2,1,VK_SHADER_STAGE_COMPUTE_BIT), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,3,1,VK_SHADER_STAGE_COMPUTE_BIT), + new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,4,1,VK_SHADER_STAGE_COMPUTE_BIT) + }); + + createDescriptorSets(VkCtx); + + PushConstantsRange[] generationPushConstants = new PushConstantsRange[]{ + new PushConstantsRange(VK_SHADER_STAGE_COMPUTE_BIT,0, TERRAIN_GENERATION_PUSH_CONSTANT_SIZE) + }; + + ShaderModule resetShader = createComputeShader(VkCtx, VOXEL_RESET_GLSL, VOXEL_RESET_SPV); + ShaderModule voxelShader = createComputeShader(VkCtx, VOXEL_GENERATION_GLSL, VOXEL_GENERATION_SPV); + ShaderModule meshShader = createComputeShader(VkCtx, MESH_GENERATION_GLSL, MESH_GENERATION_SPV); + + resetPipeline = new ComputePipeline(VkCtx,resetShader,new DescriptorSetLayout[]{resetLayout},generationPushConstants); + + voxelGenerationPipeline = new ComputePipeline(VkCtx,voxelShader, new DescriptorSetLayout[]{voxelGenerationLayout},generationPushConstants); + + meshGenerationPipeline = new ComputePipeline( VkCtx,meshShader, new DescriptorSetLayout[]{meshGenerationLayout},generationPushConstants ); + + resetShader.CleanUp(VkCtx); + voxelShader.CleanUp(VkCtx); + meshShader.CleanUp(VkCtx); + } + + private static ShaderModule createComputeShader(VulkanContext VkCtx, String glslPath, String spvPath) { + if (EngineConfig.getInstance().RecompileShaders()) { + ShaderCompiler.CompileGLSLShaderOnChange(glslPath, Shaderc.shaderc_glsl_compute_shader); + } + + return new ShaderModule(VkCtx, VK_SHADER_STAGE_COMPUTE_BIT, spvPath, null); + } + + private void createDescriptorSets(VulkanContext VkCtx) { + DescriptorAllocator allocator = VkCtx.GetDescriptorAllocator(); + Device device = VkCtx.GetDevice(); + + DescriptorSet resetSet = allocator.AddDescriptorSet(device, DESC_ID_RESET, resetLayout); + resetSet.SetBuffer(device, meshPool.indirectPool(), meshPool.indirectPool().GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + resetSet.SetBuffer(device, counters, counters.GetRequestedSize(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + + DescriptorSet voxelSet = allocator.AddDescriptorSet(device, DESC_ID_VOXEL_GENERATION, voxelGenerationLayout); + voxelSet.SetBuffer(device, voxelData, voxelData.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + + DescriptorSet meshSet = allocator.AddDescriptorSet(device, DESC_ID_MESH, meshGenerationLayout); + meshSet.SetBuffer(device, voxelData, voxelData.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + meshSet.SetBuffer(device, meshPool.vertexPool(), meshPool.vertexPool().GetRequestedSize(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + meshSet.SetBuffer(device, meshPool.indexPool(), meshPool.indexPool().GetRequestedSize(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + meshSet.SetBuffer(device, meshPool.indirectPool(), meshPool.indirectPool().GetRequestedSize(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + meshSet.SetBuffer(device, counters, counters.GetRequestedSize(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + } + + public VoxelMeshPool meshPool() { + return meshPool; + } + + public VulkanBuffer voxelData() { + return voxelData; + } + + public VulkanBuffer counters() { + return counters; + } + + public Pipeline voxelGenerationPipeline() { + return voxelGenerationPipeline; + } + + public Pipeline resetPipeline() { + return resetPipeline; + } + + public Pipeline meshGenerationPipeline() { + return meshGenerationPipeline; + } + + public String voxelGenerationDescriptorId() { + return DESC_ID_VOXEL_GENERATION; + } + + public String resetDescriptorId() { + return DESC_ID_RESET; + } + + public String meshDescriptorId() { + return DESC_ID_MESH; + } + + public void cleanup(VulkanContext VkCtx) { + resetPipeline.CleanUp(VkCtx); + voxelGenerationPipeline.CleanUp(VkCtx); + meshGenerationPipeline.CleanUp(VkCtx); + + resetLayout.CleanUp(VkCtx); + voxelGenerationLayout.CleanUp(VkCtx); + meshGenerationLayout.CleanUp(VkCtx); + + voxelData.cleanup(VkCtx); + counters.cleanup(VkCtx); + meshPool.cleanup(VkCtx); + } +} \ No newline at end of file diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelChunkGenerator.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelChunkGenerator.java new file mode 100644 index 0000000..0dcfd58 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelChunkGenerator.java @@ -0,0 +1,121 @@ +package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration; + +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.DescriptorAllocator; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.CommandBuffer; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkCommandBuffer; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import static org.lwjgl.vulkan.VK13.*; + +public class VoxelChunkGenerator { + public static final int CHUNK_SIZE = 16; + public static final int LOCAL_SIZE = 4; + public static final int DISPATCH_SIZE = CHUNK_SIZE / LOCAL_SIZE; + + private final SharedVoxelTerrainResources resources; + + public VoxelChunkGenerator(SharedVoxelTerrainResources resources) { + this.resources = resources; + } + + public void recordGenerateChunk(VulkanContext VkCtx,CommandBuffer commandBuffer, RenderChunk chunk) { + try (MemoryStack stack = MemoryStack.stackPush()) { + VkCommandBuffer cmd = commandBuffer.GetVulkanCommandBuffer(); + DescriptorAllocator allocator = VkCtx.GetDescriptorAllocator(); + + ByteBuffer pushConstants = stack.malloc(SharedVoxelTerrainResources.TERRAIN_GENERATION_PUSH_CONSTANT_SIZE); + + int vertexFloatOffset = (int) (chunk.vertexOffsetBytes() / Float.BYTES); + int indexOffset = (int) (chunk.indexOffsetBytes() / Integer.BYTES); + int indirectCommandIndex = (int) (chunk.indirectCommandOffsetBytes() / VoxelMeshPool.DRAW_INDEXED_INDIRECT_COMMAND_SIZE); + + pushConstants.putInt(0, chunk.position().x); + pushConstants.putInt(4, chunk.position().y); + pushConstants.putInt(8, chunk.position().z); + pushConstants.putInt(12, chunk.slot()); + pushConstants.putInt(16, vertexFloatOffset); + pushConstants.putInt(20, indexOffset); + pushConstants.putInt(24, indirectCommandIndex); + pushConstants.putInt(28, 0); + + LongBuffer resetDescriptorSet = stack.longs( + allocator.GetDescriptorSet(resources.resetDescriptorId()).GetVkDescriptorSet() + ); + + LongBuffer voxelDescriptorSet = stack.longs( + allocator.GetDescriptorSet(resources.voxelGenerationDescriptorId()).GetVkDescriptorSet() + ); + + LongBuffer meshDescriptorSet = stack.longs( + allocator.GetDescriptorSet(resources.meshDescriptorId()).GetVkDescriptorSet() + ); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.resetPipeline().GetVulkanPipeline()); + + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.resetPipeline().GetVulkanPipelineLayout(),0, resetDescriptorSet,null ); + + vkCmdPushConstants( cmd, resources.resetPipeline().GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0, pushConstants ); + + vkCmdDispatch(cmd, 1, 1, 1); + + VulkanUtils.BufferBarriers(stack, cmd, + new long[]{ + resources.meshPool().indirectPoolHandle(), + resources.counters().GetBuffer() + }, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_SHADER_WRITE_BIT, + VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT + ); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.voxelGenerationPipeline().GetVulkanPipeline()); + + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.voxelGenerationPipeline().GetVulkanPipelineLayout(),0, voxelDescriptorSet,null ); + + vkCmdPushConstants( cmd, resources.voxelGenerationPipeline().GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0, pushConstants ); + + vkCmdDispatch(cmd, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE); + + VulkanUtils.BufferBarrier(stack, cmd,resources.voxelData().GetBuffer(), + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_SHADER_WRITE_BIT, + VK_ACCESS_2_SHADER_READ_BIT + ); + + vkCmdBindPipeline(cmd,VK_PIPELINE_BIND_POINT_COMPUTE,resources.meshGenerationPipeline().GetVulkanPipeline() ); + + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.meshGenerationPipeline().GetVulkanPipelineLayout(),0, meshDescriptorSet,null ); + + vkCmdPushConstants( cmd, resources.meshGenerationPipeline().GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0, pushConstants ); + + vkCmdDispatch(cmd, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE); + + VulkanUtils.BufferBarriers(stack, cmd, + new long[]{ + resources.voxelData().GetBuffer(), + resources.counters().GetBuffer(), + resources.meshPool().vertexPoolHandle(), + resources.meshPool().indexPoolHandle(), + resources.meshPool().indirectPoolHandle() + }, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT, + VK_ACCESS_2_SHADER_WRITE_BIT, + VK_ACCESS_2_SHADER_READ_BIT | + VK_ACCESS_2_SHADER_WRITE_BIT | + VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_2_INDEX_READ_BIT | + VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT + ); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelChunkVisibility.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelChunkVisibility.java new file mode 100644 index 0000000..2a75bcb --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelChunkVisibility.java @@ -0,0 +1,7 @@ +package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration; + +public enum VoxelChunkVisibility { + EMPTY_AIR, + FULLY_SOLID_UNDERGROUND, + SURFACE +} \ No newline at end of file diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelMeshPool.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelMeshPool.java new file mode 100644 index 0000000..fb5d577 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelMeshPool.java @@ -0,0 +1,126 @@ +package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration; + +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanBuffer; +import org.joml.Vector3i; + +import java.util.ArrayDeque; +import java.util.Queue; + +import static org.lwjgl.util.vma.Vma.VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; +import static org.lwjgl.vulkan.VK13.*; + +public class VoxelMeshPool{ + public static final int CHUNK_SIZE = 16; + + public static final int FLOATS_PER_VERTEX = 14; + public static final int VERTEX_SIZE_BYTES = FLOATS_PER_VERTEX * Float.BYTES; + + public static final int MAX_VISIBLE_FACES_PER_CHUNK = CHUNK_SIZE * CHUNK_SIZE * 12; + public static final int MAX_VERTICES_PER_CHUNK = MAX_VISIBLE_FACES_PER_CHUNK * 4; + public static final int MAX_INDICES_PER_CHUNK = MAX_VISIBLE_FACES_PER_CHUNK * 6; + + public static final int INDEX_SIZE_BYTES = Integer.BYTES; + public static final int DRAW_INDEXED_INDIRECT_COMMAND_SIZE = 5 * Integer.BYTES; + + private final int maxChunks; + + private final long vertexSlotSizeBytes; + private final long indexSlotSizeBytes; + private final long indirectSlotSizeBytes; + + private final VulkanBuffer vertexPool; + private final VulkanBuffer indexPool; + private final VulkanBuffer indirectPool; + + private final Queue freeSlots = new ArrayDeque<>(); + + public VoxelMeshPool(VulkanContext VkCtx, int maxChunks) { + this.maxChunks = maxChunks; + + this.vertexSlotSizeBytes = (long) MAX_VERTICES_PER_CHUNK * VERTEX_SIZE_BYTES; + this.indexSlotSizeBytes = (long) MAX_INDICES_PER_CHUNK * INDEX_SIZE_BYTES; + this.indirectSlotSizeBytes = DRAW_INDEXED_INDIRECT_COMMAND_SIZE; + + long vertexPoolSize = vertexSlotSizeBytes * maxChunks; + long indexPoolSize = indexSlotSizeBytes * maxChunks; + long indirectPoolSize = indirectSlotSizeBytes * maxChunks; + + vertexPool = new VulkanBuffer(VkCtx,vertexPoolSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0); + + indexPool = new VulkanBuffer(VkCtx,indexPoolSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0); + + indirectPool = new VulkanBuffer(VkCtx,indirectPoolSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0); + + for (int i = 0; i < maxChunks; i++) { + freeSlots.add(i); + } + } + + public RenderChunk allocate(Vector3i position) { + Integer slot = freeSlots.poll(); + + if (slot == null) { + return null; + } + + return new RenderChunk(new Vector3i(position),slot, vertexOffsetBytes(slot),indexOffsetBytes(slot),MAX_INDICES_PER_CHUNK,indirectCommandOffsetBytes(slot)); + } + + public void free(RenderChunk chunk) { + freeSlots.add(chunk.slot()); + } + + public long vertexOffsetBytes(int slot) { + return vertexSlotSizeBytes * slot; + } + + public long indexOffsetBytes(int slot) { + return indexSlotSizeBytes * slot; + } + + public long indirectCommandOffsetBytes(int slot) { + return indirectSlotSizeBytes * slot; + } + + public VulkanBuffer vertexPool() { + return vertexPool; + } + + public VulkanBuffer indexPool() { + return indexPool; + } + + public VulkanBuffer indirectPool() { + return indirectPool; + } + + public long vertexPoolHandle() { + return vertexPool.GetBuffer(); + } + + public long indexPoolHandle() { + return indexPool.GetBuffer(); + } + + public long indirectPoolHandle() { + return indirectPool.GetBuffer(); + } + + public int maxChunks() { + return maxChunks; + } + + public int freeSlotCount() { + return freeSlots.size(); + } + + public void cleanup(VulkanContext VkCtx) { + vertexPool.cleanup(VkCtx); + indexPool.cleanup(VkCtx); + indirectPool.cleanup(VkCtx); + } +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelTerrainHeightSampler.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelTerrainHeightSampler.java new file mode 100644 index 0000000..9210387 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelTerrainHeightSampler.java @@ -0,0 +1,113 @@ +package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration; + +public final class VoxelTerrainHeightSampler { + private VoxelTerrainHeightSampler() { + } + + private static double fract(double value) { + return value - Math.floor(value); + } + + private static double mix(double a, double b, double t) { + return a * (1.0 - t) + b * t; + } + + private static double rand(double x, double z) { + return fract(Math.sin(x * 12.9898 + z * 78.233) * 43758.5453123); + } + + private static double valueNoise(double x, double z) { + double ix = Math.floor(x); + double iz = Math.floor(z); + + double fx = fract(x); + double fz = fract(z); + + double a = rand(ix, iz); + double b = rand(ix + 1.0, iz); + double c = rand(ix, iz + 1.0); + double d = rand(ix + 1.0, iz + 1.0); + + double ux = fx * fx * (3.0 - 2.0 * fx); + double uz = fz * fz * (3.0 - 2.0 * fz); + + return mix(mix(a, b, ux), mix(c, d, ux), uz); + } + + public static double terrainHeight(double worldX, double worldZ) { + return valueNoise(worldX * 0.005, worldZ * 0.005) * 100.0 + + valueNoise(worldX * 0.01, worldZ * 0.01) * 10.0 + + valueNoise(worldX * 0.1, worldZ * 0.1) * 5.0 + + valueNoise(worldX, worldZ) * 0.5; + } + + public static double minHeightForChunkXZ(int chunkX, int chunkZ, int chunkSize) { + double min = Double.POSITIVE_INFINITY; + + int worldMinX = chunkX * chunkSize; + int worldMinZ = chunkZ * chunkSize; + int worldMaxX = worldMinX + chunkSize - 1; + int worldMaxZ = worldMinZ + chunkSize - 1; + + int samplesPerAxis = 5; + + for (int sx = 0; sx < samplesPerAxis; sx++) { + for (int sz = 0; sz < samplesPerAxis; sz++) { + double tx = sx / (double) (samplesPerAxis - 1); + double tz = sz / (double) (samplesPerAxis - 1); + + double x = mix(worldMinX, worldMaxX, tx); + double z = mix(worldMinZ, worldMaxZ, tz); + + min = Math.min(min, terrainHeight(x, z)); + } + } + + return min; + } + + public static double maxHeightForChunkXZ(int chunkX, int chunkZ, int chunkSize) { + double max = Double.NEGATIVE_INFINITY; + + int worldMinX = chunkX * chunkSize; + int worldMinZ = chunkZ * chunkSize; + int worldMaxX = worldMinX + chunkSize - 1; + int worldMaxZ = worldMinZ + chunkSize - 1; + + int samplesPerAxis = 5; + + for (int sx = 0; sx < samplesPerAxis; sx++) { + for (int sz = 0; sz < samplesPerAxis; sz++) { + double tx = sx / (double) (samplesPerAxis - 1); + double tz = sz / (double) (samplesPerAxis - 1); + + double x = mix(worldMinX, worldMaxX, tx); + double z = mix(worldMinZ, worldMaxZ, tz); + + max = Math.max(max, terrainHeight(x, z)); + } + } + + return max; + } + + public static VoxelChunkVisibility classifyChunk(int chunkX, int chunkY, int chunkZ, int chunkSize) { + int chunkMinY = chunkY * chunkSize; + int chunkMaxY = chunkMinY + chunkSize - 1; + + double minHeight = minHeightForChunkXZ(chunkX, chunkZ, chunkSize); + double maxHeight = maxHeightForChunkXZ(chunkX, chunkZ, chunkSize); + + double safetyPadding = 32.0; + + if (chunkMinY > maxHeight + safetyPadding) { + return VoxelChunkVisibility.EMPTY_AIR; + } + + if (chunkMaxY < minHeight - safetyPadding) { + return VoxelChunkVisibility.FULLY_SOLID_UNDERGROUND; + } + + return VoxelChunkVisibility.SURFACE; + } +} \ No newline at end of file diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelWorldManager.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelWorldManager.java new file mode 100644 index 0000000..29244e6 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VoxelTerrainGeneration/VoxelWorldManager.java @@ -0,0 +1,274 @@ +package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration; + +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Pipeline; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.MaterialsCache; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.CommandBuffer; +import org.joml.Matrix4f; +import org.joml.Vector3i; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkCommandBuffer; +import org.tinylog.Logger; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +import static org.lwjgl.vulkan.VK13.*; + +public class VoxelWorldManager { + private static final int MAX_RESIDENT_CHUNKS = 512 * 24; + private static final int MAX_CHUNK_GENERATIONS_PER_FRAME = 32; + + private static final ConcurrentLinkedQueue RequestedChunks = new ConcurrentLinkedQueue<>(); + private static final ConcurrentHashMap RenderChunks = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap KnownChunks = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap CulledChunks = new ConcurrentHashMap<>(); + + + private static SharedVoxelTerrainResources Resources; + private static VoxelChunkGenerator Generator; + + private static ByteBuffer GraphicsPushConstants; + + public static void Init(VulkanContext VkCtx) { + if (Resources != null) { + return; + } + + Resources = new SharedVoxelTerrainResources(VkCtx, MAX_RESIDENT_CHUNKS); + Generator = new VoxelChunkGenerator(Resources); + + GraphicsPushConstants = org.lwjgl.system.MemoryUtil.memAlloc( + net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE + + net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.INT_SIZE + ); + } + + public static void GenerateChunk(Vector3i Position) { + Vector3i key = new Vector3i(Position); + + if (KnownChunks.containsKey(key)) { + return; + } + + RequestedChunks.add(key); + KnownChunks.put(key, true); + } + + public static int GenerationOffset = 0; + public static Vector3i LastPosition = new Vector3i(0, 0, 0); + + static int IterationX = 0; + static Vector3i ChunkPos = new Vector3i(0, 0, 0); + static Vector3i ChunkPos2 = new Vector3i(0, 0, 0); + + public static void GenerateChunks(Vector3i Position, Vector3i Radius) { + int ChunkX = Position.x; + int ChunkY = Position.y; + int ChunkY2 = Position.y; + int ChunkZ = Position.z; + for (int Ry = 0; Ry <= Radius.y; Ry++) + { + for (int Rx = -IterationX; Rx <= IterationX; Rx++) + { + for (int Rz = -IterationX; Rz <= IterationX; Rz++) + { + ChunkX = Position.x + Rx; + ChunkY = Position.y + Ry; + ChunkY2 = Position.y - Ry; + ChunkZ = Position.z + Rz; + if (Rx == -IterationX || Rz == -IterationX || Rx == IterationX || Rz == IterationX) + { + GenerateChunk(ChunkPos.set(ChunkX, ChunkY, ChunkZ)); + GenerateChunk(ChunkPos2.set(ChunkX, ChunkY2, ChunkZ)); + } + } + } + } + IterationX++; + if (IterationX > Radius.x || Position.x != LastPosition.x || Position.y != LastPosition.y || Position.z != LastPosition.z) + { + IterationX = 0; + } + LastPosition.set(Position); + } + + public static void RecordGeneration(VulkanContext VkCtx, CommandBuffer commandBuffer) { + if (Resources == null) { + Init(VkCtx); + } + + int generatedThisFrame = 0; + + while (!RequestedChunks.isEmpty() && generatedThisFrame < MAX_CHUNK_GENERATIONS_PER_FRAME) { + Vector3i position = RequestedChunks.poll(); + + if (position == null) { + break; + } + +// VoxelChunkVisibility visibility = VoxelTerrainHeightSampler.classifyChunk( +// position.x, +// position.y, +// position.z, +// VoxelChunkGenerator.CHUNK_SIZE +// ); +// +// if (visibility != VoxelChunkVisibility.SURFACE) { +// CulledChunks.put(new Vector3i(position), visibility); +// continue; +// } + + RenderChunk renderChunk = Resources.meshPool().allocate(position); + + if (renderChunk == null) { + Logger.warn("Voxel mesh pool full. Could not allocate chunk {}", position); + RequestedChunks.add(position); + break; + } + + Generator.recordGenerateChunk(VkCtx, commandBuffer, renderChunk); + RenderChunks.put(new Vector3i(position), renderChunk); + + generatedThisFrame++; + } + } + + public static void RecordRender(VkCommandBuffer CommandBuffer,Pipeline graphicsPipeline, MaterialsCache materialsCache) { + if (Resources == null) { + return; + } + + int terrainMaterialIndex = materialsCache.GetPosition("VoxelTerrain"); + + if (terrainMaterialIndex < 0) { + terrainMaterialIndex = 0; + } + + try (MemoryStack stack = MemoryStack.stackPush()) { + Matrix4f identity = new Matrix4f().identity(); + + identity.get(0, GraphicsPushConstants); + GraphicsPushConstants.putInt( + net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE, + terrainMaterialIndex + ); + + vkCmdPushConstants(CommandBuffer, graphicsPipeline.GetVulkanPipelineLayout(),VK_SHADER_STAGE_VERTEX_BIT, + 0,GraphicsPushConstants.slice( + 0, + net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE)); + + vkCmdPushConstants(CommandBuffer,graphicsPipeline.GetVulkanPipelineLayout(), VK_SHADER_STAGE_FRAGMENT_BIT, + net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE, + GraphicsPushConstants.slice( + net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE, + net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.INT_SIZE ) ); + + LongBuffer vertexBuffers = stack.longs(Resources.meshPool().vertexPoolHandle()); + LongBuffer offsets = stack.longs(0L); + + vkCmdBindVertexBuffers(CommandBuffer, 0, vertexBuffers, offsets); + vkCmdBindIndexBuffer(CommandBuffer, Resources.meshPool().indexPoolHandle(), 0, VK_INDEX_TYPE_UINT32); + + RenderChunks.forEach((position, chunk) -> vkCmdDrawIndexedIndirect(CommandBuffer,Resources.meshPool().indirectPoolHandle(), + chunk.indirectCommandOffsetBytes(), 1,VoxelMeshPool.DRAW_INDEXED_INDIRECT_COMMAND_SIZE)); + } + } + + public static void UnloadFarChunks(Vector3i centerChunk, int unloadRadius) { + int maxDistSq = unloadRadius * unloadRadius; + + RenderChunks.entrySet().removeIf(entry -> { + Vector3i pos = entry.getKey(); + + int dx = pos.x - centerChunk.x; + int dy = pos.y - centerChunk.y; + int dz = pos.z - centerChunk.z; + + int distSq = dx * dx + dy * dy + dz * dz; + + if (distSq > maxDistSq) { + RenderChunk chunk = entry.getValue(); + + Resources.meshPool().free(chunk); + KnownChunks.remove(pos); + + return true; + } + + return false; + }); + + CulledChunks.entrySet().removeIf(entry -> { + Vector3i pos = entry.getKey(); + + int dx = pos.x - centerChunk.x; + int dy = pos.y - centerChunk.y; + int dz = pos.z - centerChunk.z; + + int distSq = dx * dx + dy * dy + dz * dz; + + if (distSq > maxDistSq) { + KnownChunks.remove(pos); + return true; + } + + return false; + }); + + RequestedChunks.removeIf(pos -> { + int dx = pos.x - centerChunk.x; + int dy = pos.y - centerChunk.y; + int dz = pos.z - centerChunk.z; + + int distSq = dx * dx + dy * dy + dz * dz; + + if (distSq > maxDistSq) { + KnownChunks.remove(pos); + return true; + } + + return false; + }); + } + + public static int GetRenderableChunkCount() { + return RenderChunks.size(); + } + + public static int GetCulledChunkCount() { + return CulledChunks.size(); + } + + public static int GetRequestedChunkCount() { + return RequestedChunks.size(); + } + + public static int GetFreeMeshSlots() { + if (Resources == null) { + return 0; + } + + return Resources.meshPool().freeSlotCount(); + } + + + public static void Cleanup(VulkanContext VkCtx) { + if (Resources != null) { + Resources.cleanup(VkCtx); + Resources = null; + } + + if (GraphicsPushConstants != null) { + org.lwjgl.system.MemoryUtil.memFree(GraphicsPushConstants); + GraphicsPushConstants = null; + } + + RequestedChunks.clear(); + RenderChunks.clear(); + KnownChunks.clear(); + } +} \ No newline at end of file diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VulkanUtil/ComputePipeline.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VulkanUtil/ComputePipeline.java new file mode 100644 index 0000000..c116cfa --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Computing/VulkanUtil/ComputePipeline.java @@ -0,0 +1,94 @@ +package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VulkanUtil; + +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Pipeline; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.PushConstantsRange; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.DescriptorSetLayout; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.ShaderModule; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.Device; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import static org.lwjgl.vulkan.VK10.*; + +public class ComputePipeline implements Pipeline { + + private final long VulkanPipeline; + private final long VulkanPipelineLayout; + + public ComputePipeline(VulkanContext VkCtx,ShaderModule shaderModule, DescriptorSetLayout[] descriptorSetLayouts,PushConstantsRange[] pushConstantsRanges) { + Device device = VkCtx.GetDevice(); + + try (MemoryStack stack = MemoryStack.stackPush()) { + LongBuffer pPipelineLayout = stack.mallocLong(1); + LongBuffer pPipeline = stack.mallocLong(1); + int layoutCount = descriptorSetLayouts != null ? descriptorSetLayouts.length : 0; + LongBuffer setLayouts = stack.mallocLong(layoutCount); + for (int i = 0; i < layoutCount; i++) { + setLayouts.put(i, descriptorSetLayouts[i].GetVkDescriptorLayout()); + } + + VkPushConstantRange.Buffer pushRanges = null; + int pushRangeCount = pushConstantsRanges != null ? pushConstantsRanges.length : 0; + if (pushRangeCount > 0) { + pushRanges = VkPushConstantRange.calloc(pushRangeCount, stack); + + for (int i = 0; i < pushRangeCount; i++) { + PushConstantsRange range = pushConstantsRanges[i]; + + pushRanges.get(i) + .stageFlags(range.Stage()) + .offset(range.Offset()) + .size(range.Size()); + } + } + VkPipelineLayoutCreateInfo layoutInfo = VkPipelineLayoutCreateInfo.calloc(stack) + .sType$Default() + .pSetLayouts(setLayouts) + .pPushConstantRanges(pushRanges); + + VulkanUtils.vkCheck( + vkCreatePipelineLayout(device.FetchVulkanDevice(), layoutInfo, null, pPipelineLayout), + "Unable to create compute pipeline layout" + ); + VulkanPipelineLayout = pPipelineLayout.get(0); + ByteBuffer main = stack.UTF8("main"); + VkPipelineShaderStageCreateInfo shaderStage = VkPipelineShaderStageCreateInfo.calloc(stack) + .sType$Default() + .stage(VK_SHADER_STAGE_COMPUTE_BIT) + .module(shaderModule.GetHandle()) + .pName(main); + + VkComputePipelineCreateInfo.Buffer pipelineInfo = VkComputePipelineCreateInfo.calloc(1, stack) + .sType$Default() + .stage(shaderStage) + .layout(VulkanPipelineLayout); + + VulkanUtils.vkCheck(vkCreateComputePipelines(device.FetchVulkanDevice(),VkCtx.GetVkPipelineCache().GetVkPipelineCache(), + pipelineInfo,null,pPipeline),"Unable to create compute pipeline" ); + + VulkanPipeline = pPipeline.get(0); + } + } + + @Override + public long GetVulkanPipeline() { + return VulkanPipeline; + } + + @Override + public long GetVulkanPipelineLayout() { + return VulkanPipelineLayout; + } + + @Override + public void CleanUp(VulkanContext VkCtx) { + VkDevice device = VkCtx.GetDevice().FetchVulkanDevice(); + vkDestroyPipeline(device, VulkanPipeline, null); + vkDestroyPipelineLayout(device, VulkanPipelineLayout, null); + } +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/DeferredRendering/DeferredSceneRender.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/DeferredRendering/DeferredSceneRender.java index 54f10b7..f1a879c 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/DeferredRendering/DeferredSceneRender.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/DeferredRendering/DeferredSceneRender.java @@ -6,6 +6,7 @@ import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3D; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3DElement; import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ImageView; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.SceneRenderer; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; @@ -379,6 +380,8 @@ public class DeferredSceneRender implements SceneRenderer {//dynamic rendering RenderActors(engineInstance, CommandHandle, modelsCache, materialsCache, false); + VoxelWorldManager.RecordRender(CommandHandle, DualPassRendering ? VkPipelineOpaque : VkPipeline,materialsCache); + Matrix4f skyboxViewMatrix = new Matrix4f().identity(); skyboxViewMatrix.set(engineInstance.scene().GetCamera().GetViewMatrix()); skyboxViewMatrix.m30(0.0f); diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/Forward/ForwardSceneRender.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/Forward/ForwardSceneRender.java index 3f92486..1ec818c 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/Forward/ForwardSceneRender.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/Forward/ForwardSceneRender.java @@ -7,6 +7,7 @@ import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3D; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3DElement; import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.DeferredRendering.DeferredSceneRender; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ImageView; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.SceneRenderer; @@ -363,6 +364,7 @@ public class ForwardSceneRender implements SceneRenderer {//dynamic rendering .put(3,descriptorAllocator.GetDescriptorSet(DESCRIPTOR_ID_TEXT).GetVkDescriptorSet()); vkCmdBindDescriptorSets(CommandHandle, VK_PIPELINE_BIND_POINT_GRAPHICS, DualPassRendering ? VkPipelineOpaque.GetVulkanPipelineLayout() : VkPipeline.GetVulkanPipelineLayout(),0,DescriptorSets,null); RenderActors(engineInstance, CommandHandle, modelsCache, materialsCache, false); + VoxelWorldManager.RecordRender(CommandHandle, DualPassRendering ? VkPipelineOpaque : VkPipeline,materialsCache); Matrix4f skyboxViewMatrix = new Matrix4f().identity(); skyboxViewMatrix.set(engineInstance.scene().GetCamera().GetViewMatrix()); diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/Shadows/ShadowRenderer.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/Shadows/ShadowRenderer.java index 3237d80..7d90759 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/Shadows/ShadowRenderer.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/RenderPasses/Shadows/ShadowRenderer.java @@ -6,6 +6,7 @@ import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3D; import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene; import net.halbear.Terrain4J.EngineCore.Main.Scene.SceneLightingManager; +import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.DefaultPipeline; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Images.Attachment; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Images.ITexture; @@ -393,7 +394,7 @@ public class ShadowRenderer { vkCmdDrawIndexed(cmdHandle, vulkanMesh.IndicesCount(), 1, 0, 0, 0); } } - + VoxelWorldManager.RecordRender(cmdHandle,pipeline,materialsCache); vkCmdEndRendering(cmdHandle); } } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/Shader/DescriptorSet.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/Shader/DescriptorSet.java index 41e990f..70d1e22 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/Shader/DescriptorSet.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Rendering/Shader/DescriptorSet.java @@ -47,6 +47,7 @@ public class DescriptorSet { .sType$Default() .dstSet(VkDescriptorSet) .dstBinding(Binding) + .dstArrayElement(0) .descriptorType(Type) .descriptorCount(1) .pBufferInfo(BufferInfo); diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Util/VulkanUtils.java b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Util/VulkanUtils.java index 3ba0d8f..6d8b588 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Util/VulkanUtils.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/RenderingAPI/Vulkan/Util/VulkanUtils.java @@ -206,6 +206,50 @@ public class VulkanUtils { } return result; } + + + public static void BufferBarrier(MemoryStack MemStack, VkCommandBuffer CommandHandle, long BufferHandle, + long SrcStage,long DstStage,long SrcAccess,long DstAccess){ + var BufferBarrier = VkBufferMemoryBarrier2.calloc(1, MemStack) + .sType$Default() + .srcStageMask(SrcStage) + .dstStageMask(DstStage) + .srcAccessMask(SrcAccess) + .dstAccessMask(DstAccess) + .srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .buffer(BufferHandle) + .offset(0) + .size(VK_WHOLE_SIZE); + VkDependencyInfo DependencyInfo = VkDependencyInfo.calloc(MemStack) + .sType$Default() + .pBufferMemoryBarriers(BufferBarrier); + vkCmdPipelineBarrier2(CommandHandle, DependencyInfo); + } + + public static void BufferBarriers(MemoryStack MemStack,VkCommandBuffer CommandHandle,long[] Buffers, + long SrcStage,long DstStage,long SrcAccess,long DstAccess ){ + var BufferBarriers = VkBufferMemoryBarrier2.calloc(Buffers.length, MemStack); + for (int i = 0; i < Buffers.length; i++) { + BufferBarriers.get(i) + .sType$Default() + .srcStageMask(SrcStage) + .dstStageMask(DstStage) + .srcAccessMask(SrcAccess) + .dstAccessMask(DstAccess) + .srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .buffer(Buffers[i]) + .offset(0) + .size(VK_WHOLE_SIZE); + } + VkDependencyInfo DependencyInfo = VkDependencyInfo.calloc(MemStack) + .sType$Default() + .pBufferMemoryBarriers(BufferBarriers); + + vkCmdPipelineBarrier2(CommandHandle, DependencyInfo); + } + public static void ImageBarrier(MemoryStack MemStack, VkCommandBuffer CommandHandle, long Image, int OldLayout, int NewLayout, long SrcStage, long DstStage, long SrcAccess, long DstAccess, int AspectMask){