diff --git a/res/shaders/fluid/AddSource.slang b/res/shaders/fluid/AddSource.slang new file mode 100644 index 0000000..e1fc143 --- /dev/null +++ b/res/shaders/fluid/AddSource.slang @@ -0,0 +1,35 @@ +import FluidGridData; + +struct Params +{ + // read-write: phi to add source to + FluidGridData phi; + FluidGridData density; +}; +ParameterBlock params; + +[shader("compute")] +[numthreads(32, 8, 1)] +void addSource(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + FluidGridData phi = params.phi; + FluidGridData density = params.density; + int x = dispatchThreadID.x + 1; + int y = dispatchThreadID.y + 1; + int z = dispatchThreadID.z + 1; + if(x >= gridParams.gridSize.x - 1 || y >= gridParams.gridSize.y - 1 || z >= gridParams.gridSize.z - 1) return; + + // Add a spherical source and union it into the existing level set. + float3 center = float3(gridParams.gridSize) * 0.5f; + float radius = min(gridParams.gridSize.x, min(gridParams.gridSize.y, gridParams.gridSize.z)) * 0.25f; + float3 pos = float3(x, y, z); + float dist = length(pos - center); + float sourcePhi = dist - radius; + phi[x, y, z] = min(phi[x, y, z], sourcePhi); + + if (sourcePhi < 0.0f) + { + density[x, y, z] += 0.5f; + density[x, y, z] = min(density[x, y, z], 1.0f); + } +} \ No newline at end of file diff --git a/res/shaders/fluid/Advect.slang b/res/shaders/fluid/Advect.slang index 3b2467e..4d8010f 100644 --- a/res/shaders/fluid/Advect.slang +++ b/res/shaders/fluid/Advect.slang @@ -30,16 +30,16 @@ void advect(uint3 dispatchThreadID : SV_DispatchThreadID) int x = dispatchThreadID.x + 1; int y = dispatchThreadID.y + 1; int z = dispatchThreadID.z + 1; - if(x >= gridSize.x - 1 || y >= gridSize.y - 1 || z >= gridSize.z - 1) return; + if(x >= gridParams.gridSize.x - 1 || y >= gridParams.gridSize.y - 1 || z >= gridParams.gridSize.z - 1) return; - float dt0x = dt * (gridSize.x - 2); - float dt0y = dt * (gridSize.y - 2); - float dt0z = dt * (gridSize.z - 2); + float dt0x = dt * (gridParams.gridSize.x - 2); + float dt0y = dt * (gridParams.gridSize.y - 2); + float dt0z = dt * (gridParams.gridSize.z - 2); float3 pos = float3(x - dt0x * velocityX[x, y, z], y - dt0y * velocityY[x, y, z], z - dt0z * velocityZ[x, y, z]); - pos.x = clamp(pos.x, 0.5f, gridSize.x - 1.5f); - pos.y = clamp(pos.y, 0.5f, gridSize.y - 1.5f); - pos.z = clamp(pos.z, 0.5f, gridSize.z - 1.5f); + pos.x = clamp(pos.x, 0.5f, gridParams.gridSize.x - 1.5f); + pos.y = clamp(pos.y, 0.5f, gridParams.gridSize.y - 1.5f); + pos.z = clamp(pos.z, 0.5f, gridParams.gridSize.z - 1.5f); int3 i0 = int3(pos); int3 i1 = i0 + int3(1, 1, 1); diff --git a/res/shaders/fluid/ApplyForces.slang b/res/shaders/fluid/ApplyForces.slang new file mode 100644 index 0000000..82d8b07 --- /dev/null +++ b/res/shaders/fluid/ApplyForces.slang @@ -0,0 +1,31 @@ +import FluidGridData; + +struct Params +{ + FluidGridData velocityX; + FluidGridData velocityY; + FluidGridData velocityZ; + float3 force; + float dt; +}; +ParameterBlock params; + +[shader("compute")] +[numthreads(32, 8, 1)] +void applyForces(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + FluidGridData velocityX = params.velocityX; + FluidGridData velocityY = params.velocityY; + FluidGridData velocityZ = params.velocityZ; + float3 force = params.force; + float dt = params.dt; + + int x = dispatchThreadID.x + 1; + int y = dispatchThreadID.y + 1; + int z = dispatchThreadID.z + 1; + if(x >= gridParams.gridSize.x - 1 || y >= gridParams.gridSize.y - 1 || z >= gridParams.gridSize.z - 1) return; + + velocityX[x, y, z] += force.x * dt; + velocityY[x, y, z] += force.y * dt; + velocityZ[x, y, z] += force.z * dt; +} diff --git a/res/shaders/fluid/Divergence.slang b/res/shaders/fluid/Divergence.slang index d1c5c69..08db19c 100644 --- a/res/shaders/fluid/Divergence.slang +++ b/res/shaders/fluid/Divergence.slang @@ -1,7 +1,6 @@ import FluidGridData; -struct Params -{ +struct Params { // read-only FluidGridData velocityX; // read-only @@ -17,8 +16,7 @@ ParameterBlock params; [shader("compute")] [numthreads(32, 8, 1)] -void computeDivergence(uint3 dispatchThreadID : SV_DispatchThreadID) -{ +void computeDivergence(uint3 dispatchThreadID: SV_DispatchThreadID) { FluidGridData velocityX = params.velocityX; FluidGridData velocityY = params.velocityY; FluidGridData velocityZ = params.velocityZ; @@ -28,8 +26,9 @@ void computeDivergence(uint3 dispatchThreadID : SV_DispatchThreadID) int x = dispatchThreadID.x + 1; int y = dispatchThreadID.y + 1; int z = dispatchThreadID.z + 1; - if(x >= gridSize.x - 1 || y >= gridSize.y - 1 || z >= gridSize.z - 1) return; + if (x >= gridParams.gridSize.x - 1 || y >= gridParams.gridSize.y - 1 || z >= gridParams.gridSize.z - 1) + return; - divergence[x, y, z] = -0.5f * (velocityX[x + 1, y, z] - velocityX[x - 1, y, z] + velocityY[x, y + 1, z] - velocityY[x, y - 1, z] + velocityZ[x, y, z + 1] - velocityZ[x, y, z - 1]) / gridSize.x; + divergence[x, y, z] = -0.5f * (velocityX[x + 1, y, z] - velocityX[x - 1, y, z] + velocityY[x, y + 1, z] - velocityY[x, y - 1, z] + velocityZ[x, y, z + 1] - velocityZ[x, y, z - 1]); pressure[x, y, z] = 0; -} \ No newline at end of file +} diff --git a/res/shaders/fluid/Extract.slang b/res/shaders/fluid/Extract.slang index 4a6cc2a..3c01e97 100644 --- a/res/shaders/fluid/Extract.slang +++ b/res/shaders/fluid/Extract.slang @@ -13,7 +13,7 @@ ParameterBlock params; float getDensity(uint x, uint y, uint z) { - return params.density[x + gridSize.x * y + gridSize.x * gridSize.y * z]; + return params.density[x + gridParams.gridSize.x * y + gridParams.gridSize.x * gridParams.gridSize.y * z]; } [shader("compute")] @@ -24,7 +24,7 @@ void main(uint3 dispatchThreadID : SV_DispatchThreadID) uint y = dispatchThreadID.y+1; uint z = dispatchThreadID.z+1; FluidGridData density = params.density; - if(x >= gridSize.x-3 || y >= gridSize.y-3 || z >= gridSize.z-3) return; + if(x >= gridParams.gridSize.x-3 || y >= gridParams.gridSize.y-3 || z >= gridParams.gridSize.z-3) return; float phi[8] = { getDensity(x, y, z), diff --git a/res/shaders/fluid/FluidGridData.slang b/res/shaders/fluid/FluidGridData.slang index 6bd7ecd..b105946 100644 --- a/res/shaders/fluid/FluidGridData.slang +++ b/res/shaders/fluid/FluidGridData.slang @@ -1,15 +1,19 @@ -static const uint3 gridSize = uint3(256, 256, 256); +struct GridParams +{ + uint3 gridSize; +}; +ParameterBlock gridParams; struct FluidGridData { RWStructuredBuffer dataGrid; __subscript(uint3 index) -> T { - get { return dataGrid[index.x + index.y * gridSize.x + index.z * gridSize.x * gridSize.y]; } - set { dataGrid[index.x + index.y * gridSize.x + index.z * gridSize.x * gridSize.y] = newValue; } + get { return dataGrid[index.x + index.y * gridParams.gridSize.x + index.z * gridParams.gridSize.x * gridParams.gridSize.y]; } + set { dataGrid[index.x + index.y * gridParams.gridSize.x + index.z * gridParams.gridSize.x * gridParams.gridSize.y] = newValue; } } __subscript(uint x, uint y, uint z) -> T { - get { return dataGrid[x + y * gridSize.x + z * gridSize.x * gridSize.y]; } - set { dataGrid[x + y * gridSize.x + z * gridSize.x * gridSize.y] = newValue; } + get { return dataGrid[x + y * gridParams.gridSize.x + z * gridParams.gridSize.x * gridParams.gridSize.y]; } + set { dataGrid[x + y * gridParams.gridSize.x + z * gridParams.gridSize.x * gridParams.gridSize.y] = newValue; } } }; diff --git a/res/shaders/fluid/LinearSolve.slang b/res/shaders/fluid/LinearSolve.slang index c8dd9ef..7cd4f56 100644 --- a/res/shaders/fluid/LinearSolve.slang +++ b/res/shaders/fluid/LinearSolve.slang @@ -26,7 +26,7 @@ void linearSolve(uint3 dispatchThreadID : SV_DispatchThreadID) int x = dispatchThreadID.x + 1; int y = dispatchThreadID.y + 1; int z = dispatchThreadID.z + 1; - if(x >= gridSize.x - 1 || y >= gridSize.y - 1 || z >= gridSize.z - 1) return; + if(x >= gridParams.gridSize.x - 1 || y >= gridParams.gridSize.y - 1 || z >= gridParams.gridSize.z - 1) return; next[x, y, z] = (grid0[x, y, z] + a * (current[x + 1, y, z] + current[x - 1, y, z] + current[x, y + 1, z] + current[x, y - 1, z] + current[x, y, z + 1] + current[x, y, z - 1])) * cRecip; } \ No newline at end of file diff --git a/res/shaders/fluid/MarchingCubes.slang b/res/shaders/fluid/MarchingCubes.slang index 7ada291..f9f5ac9 100644 --- a/res/shaders/fluid/MarchingCubes.slang +++ b/res/shaders/fluid/MarchingCubes.slang @@ -12,7 +12,7 @@ struct Params ParameterBlock params; const static float isoLevel = 0.0f; -const static float3 cellSize = 1.0f / gridSize; +const static float3 cellSize = 1.0f / gridParams.gridSize; float3 vertexInterp(float isoLevel, float3 p1, float3 p2, float valp1, float valp2) { @@ -86,7 +86,7 @@ void marchingCubes(uint3 dispatchThreadID : SV_DispatchThreadID, uint groupIndex // Don't early-return — all threads must participate in barriers. // Use a flag to skip work for out-of-bounds threads. - bool valid = (x < gridSize.x - 2 && y < gridSize.y - 2 && z < gridSize.z - 2); + bool valid = (x < gridParams.gridSize.x - 2 && y < gridParams.gridSize.y - 2 && z < gridParams.gridSize.z - 2); float val[8]; uint cubeIndex = 0; @@ -169,7 +169,7 @@ void marchingCubes(uint3 dispatchThreadID : SV_DispatchThreadID, uint groupIndex float3 v0 = vertList[triTable[cubeIndex][i]]; float3 v1 = vertList[triTable[cubeIndex][i + 1]]; float3 v2 = vertList[triTable[cubeIndex][i + 2]]; - float3 n = normalize(cross(v1 - v0, v2 - v0)); + float3 n = normalize(cross(v2 - v0, v1 - v0)); localVertices[localOff] = v0; localVertices[localOff + 1] = v1; diff --git a/res/shaders/fluid/Project.slang b/res/shaders/fluid/Project.slang index 78cdfb2..9bbabd3 100644 --- a/res/shaders/fluid/Project.slang +++ b/res/shaders/fluid/Project.slang @@ -25,9 +25,9 @@ void project(uint3 dispatchThreadID : SV_DispatchThreadID) int x = dispatchThreadID.x + 1; int y = dispatchThreadID.y + 1; int z = dispatchThreadID.z + 1; - if(x >= gridSize.x - 1 || y >= gridSize.y - 1 || z >= gridSize.z - 1) return; + if(x >= gridParams.gridSize.x - 1 || y >= gridParams.gridSize.y - 1 || z >= gridParams.gridSize.z - 1) return; - velocityX[x, y, z] -= 0.5f * (pressure[x + 1, y, z] - pressure[x - 1, y, z]) * gridSize.x; - velocityY[x, y, z] -= 0.5f * (pressure[x, y + 1, z] - pressure[x, y - 1, z]) * gridSize.y; - velocityZ[x, y, z] -= 0.5f * (pressure[x, y, z + 1] - pressure[x, y, z - 1]) * gridSize.z; + velocityX[x, y, z] -= 0.5f * (pressure[x + 1, y, z] - pressure[x - 1, y, z]); + velocityY[x, y, z] -= 0.5f * (pressure[x, y + 1, z] - pressure[x, y - 1, z]); + velocityZ[x, y, z] -= 0.5f * (pressure[x, y, z + 1] - pressure[x, y, z - 1]); } \ No newline at end of file diff --git a/res/shaders/fluid/Render.slang b/res/shaders/fluid/Render.slang index a136b4c..a2ca61f 100644 --- a/res/shaders/fluid/Render.slang +++ b/res/shaders/fluid/Render.slang @@ -5,6 +5,7 @@ import MaterialParameter; struct Params { + float4x4 transform; StructuredBuffer vertexBuffer; StructuredBuffer normalBuffer; StructuredBuffer indexBuffer; @@ -25,10 +26,12 @@ VertexOut vertexMain(uint vertexId : SV_VertexID) uint index = params.indexBuffer[vertexId]; float3 vertex = float3(params.vertexBuffer[index * 3 + 0], params.vertexBuffer[index * 3 + 1], - params.vertexBuffer[index * 3 + 2]) * 5; + params.vertexBuffer[index * 3 + 2]); float3 normal = float3(params.normalBuffer[index * 3 + 0], params.normalBuffer[index * 3 + 1], params.normalBuffer[index * 3 + 2]); + vertex = mul(params.transform, float4(vertex, 1)).xyz; + normal = mul((float3x3)params.transform, normal); output.position_WS = vertex; output.position_CS = mul(pViewParams.viewProjectionMatrix, float4(vertex, 1)); output.normal = normal; diff --git a/res/shaders/fluid/SetBound.slang b/res/shaders/fluid/SetBound.slang index 48e1bb9..1120265 100644 --- a/res/shaders/fluid/SetBound.slang +++ b/res/shaders/fluid/SetBound.slang @@ -14,18 +14,29 @@ void setBound(uint3 dispatchThreadID : SV_DispatchThreadID) { int b = params.b; FluidGridData grid = params.grid; - int x = dispatchThreadID.x + 1; - int y = dispatchThreadID.y + 1; - if(x >= gridSize.x - 1 || y >= gridSize.y - 1) return; - - grid[0, x, y] = b == 1 ? -grid[1, x, y] : grid[1, x, y]; - grid[gridSize.x - 1, x, y] = b == 1 ? -grid[gridSize.x - 2, x, y] : grid[gridSize.x - 2, x, y]; - - grid[x, 0, y] = b == 2 ? -grid[x, 1, y] : grid[x, 1, y]; - grid[x, gridSize.y - 1, y] = b == 2 ? -grid[x, gridSize.y - 2, y] : grid[x, gridSize.y - 2, y]; + int i = dispatchThreadID.x + 1; + int j = dispatchThreadID.y + 1; - grid[x, y, 0] = b == 3 ? -grid[x, y, 1] : grid[x, y, 1]; - grid[x, y, gridSize.z - 1] = b == 3 ? -grid[x, y, gridSize.z - 2] : grid[x, y, gridSize.z - 2]; + // X-faces: indices range over (gridSize.y, gridSize.z) + if(i < gridParams.gridSize.y - 1 && j < gridParams.gridSize.z - 1) + { + grid[0, i, j] = b == 1 ? -grid[1, i, j] : grid[1, i, j]; + grid[gridParams.gridSize.x - 1, i, j] = b == 1 ? -grid[gridParams.gridSize.x - 2, i, j] : grid[gridParams.gridSize.x - 2, i, j]; + } + + // Y-faces: indices range over (gridSize.x, gridSize.z) + if(i < gridParams.gridSize.x - 1 && j < gridParams.gridSize.z - 1) + { + grid[i, 0, j] = b == 2 ? -grid[i, 1, j] : grid[i, 1, j]; + grid[i, gridParams.gridSize.y - 1, j] = b == 2 ? -grid[i, gridParams.gridSize.y - 2, j] : grid[i, gridParams.gridSize.y - 2, j]; + } + + // Z-faces: indices range over (gridSize.x, gridSize.y) + if(i < gridParams.gridSize.x - 1 && j < gridParams.gridSize.y - 1) + { + grid[i, j, 0] = b == 3 ? -grid[i, j, 1] : grid[i, j, 1]; + grid[i, j, gridParams.gridSize.z - 1] = b == 3 ? -grid[i, j, gridParams.gridSize.z - 2] : grid[i, j, gridParams.gridSize.z - 2]; + } } [shader("compute")] @@ -34,22 +45,33 @@ void setBoundEdges(uint3 dispatchThreadID : SV_DispatchThreadID) { FluidGridData grid = params.grid; int x = dispatchThreadID.x + 1; - if(x >= gridSize.x - 1) return; - grid[x, 0, 0] = 0.5f * (grid[x, 1, 0] + grid[x, 0, 1]); - grid[x, gridSize.y - 1, 0] = 0.5f * (grid[x, gridSize.y - 2, 0] + grid[x, gridSize.y - 1, 1]); - grid[x, 0, gridSize.z - 1] = 0.5f * (grid[x, 1, gridSize.z - 1] + grid[x, 0, gridSize.z - 2]); - grid[x, gridSize.y - 1, gridSize.z - 1] = 0.5f * (grid[x, gridSize.y - 2, gridSize.z - 1] + grid[x, gridSize.y - 1, gridSize.z - 2]); + // X-axis edges: x ranges [1, gridSize.x-2] + if(x < gridParams.gridSize.x - 1) + { + grid[x, 0, 0] = 0.5f * (grid[x, 1, 0] + grid[x, 0, 1]); + grid[x, gridParams.gridSize.y - 1, 0] = 0.5f * (grid[x, gridParams.gridSize.y - 2, 0] + grid[x, gridParams.gridSize.y - 1, 1]); + grid[x, 0, gridParams.gridSize.z - 1] = 0.5f * (grid[x, 1, gridParams.gridSize.z - 1] + grid[x, 0, gridParams.gridSize.z - 2]); + grid[x, gridParams.gridSize.y - 1, gridParams.gridSize.z - 1] = 0.5f * (grid[x, gridParams.gridSize.y - 2, gridParams.gridSize.z - 1] + grid[x, gridParams.gridSize.y - 1, gridParams.gridSize.z - 2]); + } - grid[0, x, 0] = 0.5f * (grid[1, x, 0] + grid[0, x, 1]); - grid[0, x, gridSize.z - 1] = 0.5f * (grid[1, x, gridSize.z - 1] + grid[0, x, gridSize.z - 2]); - grid[gridSize.x - 1, x, 0] = 0.5f * (grid[gridSize.x - 2, x, 0] + grid[gridSize.x - 1, x, 1]); - grid[gridSize.x - 1, x, gridSize.z - 1] = 0.5f * (grid[gridSize.x - 2, x, gridSize.z - 1] + grid[gridSize.x - 1, x, gridSize.z - 2]); - - grid[0, 0, x] = 0.5f * (grid[1, 0, x] + grid[0, 1, x]); - grid[0, gridSize.y - 1, x] = 0.5f * (grid[1, gridSize.y - 1, x] + grid[0, gridSize.y - 2, x]); - grid[gridSize.x - 1, 0, x] = 0.5f * (grid[gridSize.x - 2, 0, x] + grid[gridSize.x - 1, 1, x]); - grid[gridSize.x - 1, gridSize.y - 1, x] = 0.5f * (grid[gridSize.x - 2, gridSize.y - 1, x] + grid[gridSize.x - 1, gridSize.y - 2, x]); + // Y-axis edges: x ranges [1, gridSize.y-2] + if(x < gridParams.gridSize.y - 1) + { + grid[0, x, 0] = 0.5f * (grid[1, x, 0] + grid[0, x, 1]); + grid[0, x, gridParams.gridSize.z - 1] = 0.5f * (grid[1, x, gridParams.gridSize.z - 1] + grid[0, x, gridParams.gridSize.z - 2]); + grid[gridParams.gridSize.x - 1, x, 0] = 0.5f * (grid[gridParams.gridSize.x - 2, x, 0] + grid[gridParams.gridSize.x - 1, x, 1]); + grid[gridParams.gridSize.x - 1, x, gridParams.gridSize.z - 1] = 0.5f * (grid[gridParams.gridSize.x - 2, x, gridParams.gridSize.z - 1] + grid[gridParams.gridSize.x - 1, x, gridParams.gridSize.z - 2]); + } + + // Z-axis edges: x ranges [1, gridSize.z-2] + if(x < gridParams.gridSize.z - 1) + { + grid[0, 0, x] = 0.5f * (grid[1, 0, x] + grid[0, 1, x]); + grid[0, gridParams.gridSize.y - 1, x] = 0.5f * (grid[1, gridParams.gridSize.y - 1, x] + grid[0, gridParams.gridSize.y - 2, x]); + grid[gridParams.gridSize.x - 1, 0, x] = 0.5f * (grid[gridParams.gridSize.x - 2, 0, x] + grid[gridParams.gridSize.x - 1, 1, x]); + grid[gridParams.gridSize.x - 1, gridParams.gridSize.y - 1, x] = 0.5f * (grid[gridParams.gridSize.x - 2, gridParams.gridSize.y - 1, x] + grid[gridParams.gridSize.x - 1, gridParams.gridSize.y - 2, x]); + } } [shader("compute")] @@ -59,13 +81,13 @@ void setBoundCorners(uint3 dispatchThreadID : SV_DispatchThreadID) FluidGridData grid = params.grid; uint threadIdx = dispatchThreadID.x; - uint x = ((threadIdx & 1) == 0) ? 0 : (gridSize.x - 1); - uint y = ((threadIdx & 2) == 0) ? 0 : (gridSize.y - 1); - uint z = ((threadIdx & 4) == 0) ? 0 : (gridSize.z - 1); + uint x = ((threadIdx & 1) == 0) ? 0 : (gridParams.gridSize.x - 1); + uint y = ((threadIdx & 2) == 0) ? 0 : (gridParams.gridSize.y - 1); + uint z = ((threadIdx & 4) == 0) ? 0 : (gridParams.gridSize.z - 1); - uint nx = (x == 0) ? 1 : (gridSize.x - 2); - uint ny = (y == 0) ? 1 : (gridSize.y - 2); - uint nz = (z == 0) ? 1 : (gridSize.z - 2); + uint nx = (x == 0) ? 1 : (gridParams.gridSize.x - 2); + uint ny = (y == 0) ? 1 : (gridParams.gridSize.y - 2); + uint nz = (z == 0) ? 1 : (gridParams.gridSize.z - 2); - grid[x, y, z] = 0.33f * (grid[nx, y, z] + grid[x, ny, z] + grid[x, y, nz]); + grid[x, y, z] = (1.0f / 3.0f) * (grid[nx, y, z] + grid[x, ny, z] + grid[x, y, nz]); } \ No newline at end of file diff --git a/res/shaders/fluid/SignedDistance.slang b/res/shaders/fluid/SignedDistance.slang index 56ab5b3..0637dec 100644 --- a/res/shaders/fluid/SignedDistance.slang +++ b/res/shaders/fluid/SignedDistance.slang @@ -25,7 +25,7 @@ void reinitialize(uint3 dispatchThreadID : SV_DispatchThreadID) uint z = dispatchThreadID.z + 1; FluidGridData phi = params.phi; FluidGridData phi0 = params.phi0; - if(x >= gridSize.x - 1 || y >= gridSize.y - 1 || z >= gridSize.z - 1) return; + if(x >= gridParams.gridSize.x - 1 || y >= gridParams.gridSize.y - 1 || z >= gridParams.gridSize.z - 1) return; float dtau = params.dtau; float dx = 1.0f; diff --git a/src/Engine/Component/FluidGrid.h b/src/Engine/Component/FluidGrid.h index 5c9aa9e..7ba19d1 100644 --- a/src/Engine/Component/FluidGrid.h +++ b/src/Engine/Component/FluidGrid.h @@ -4,10 +4,13 @@ namespace Seele { namespace Component { struct FluidGrid { - float cellSize = 1.0f; - float viscosity = 0.1f; - float diffusion = 0.01f; + bool paused = true; + float dt = 0.1f; + float viscosity = 0.001f; + float diffusion = 0.0001f; UVector gridSize = {128, 128, 128}; + Vector gravity = {0.0f, -9.81f, 0.0f}; + bool enableGravity = true; }; } // namespace Component } // namespace Seele \ No newline at end of file diff --git a/src/Engine/Graphics/RenderPass/FluidRenderPass.cpp b/src/Engine/Graphics/RenderPass/FluidRenderPass.cpp index 6940367..acb099d 100644 --- a/src/Engine/Graphics/RenderPass/FluidRenderPass.cpp +++ b/src/Engine/Graphics/RenderPass/FluidRenderPass.cpp @@ -1,19 +1,26 @@ #include "FluidRenderPass.h" #include "Component/Transform.h" #include "Graphics/Command.h" +#include "Graphics/Descriptor.h" #include "Graphics/Enums.h" #include "Graphics/Graphics.h" #include "Graphics/Initializer.h" #include "Graphics/RenderTarget.h" #include "Graphics/Shader.h" +#include "Math/Matrix.h" #include "Scene/LightEnvironment.h" #include "Scene/Scene.h" +#include "Scene/FluidScene.h" using namespace Seele; FluidRenderPass::FluidRenderPass(Gfx::PGraphics graphics, PScene scene) : RenderPass(graphics), scene(scene) { pipelineLayout = graphics->createPipelineLayout("FluidPipelineLayout"); descriptorLayout = graphics->createDescriptorLayout("params"); + descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "transform", + .uniformLength = sizeof(Matrix4), + }); descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = "vertexBuffer", .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, @@ -54,6 +61,8 @@ void FluidRenderPass::render() { descriptorLayout->reset(); for (const auto& [id, data] : scene->getFluidScene()->getFluidDataMap()) { descriptorSet = descriptorLayout->allocateDescriptorSet(); + Matrix4 transformMatrix = data.transform.toMatrix(); + descriptorSet->updateConstants("transform", 0, &transformMatrix); descriptorSet->updateBuffer("vertexBuffer", 0, data.vertexBuffer); descriptorSet->updateBuffer("normalBuffer", 0, data.normalBuffer); descriptorSet->updateBuffer("indexBuffer", 0, data.indexBuffer); @@ -75,6 +84,7 @@ void FluidRenderPass::endFrame() {} void FluidRenderPass::publishOutputs() { } + void FluidRenderPass::createRenderPass() { colorAttachment = resources->requestRenderTarget("BASEPASS_COLOR"); colorAttachment.setLoadOp(Gfx::SE_ATTACHMENT_LOAD_OP_LOAD); @@ -112,6 +122,7 @@ void FluidRenderPass::createRenderPass() { Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, }, }; + renderPass = graphics->createRenderPass(layout, dependency, viewport->getRenderArea(), "FluidRenderPass"); pipeline = graphics->createGraphicsPipeline(Gfx::LegacyPipelineCreateInfo{ .vertexShader = vertexShader, @@ -124,7 +135,7 @@ void FluidRenderPass::createRenderPass() { }, .rasterizationState = { - .cullMode = Gfx::SE_CULL_MODE_FRONT_BIT, + .cullMode = Gfx::SE_CULL_MODE_NONE, }, .colorBlend = { diff --git a/src/Engine/Graphics/RenderPass/LightCullingPass.cpp b/src/Engine/Graphics/RenderPass/LightCullingPass.cpp index 4a3af87..a9ea5fb 100644 --- a/src/Engine/Graphics/RenderPass/LightCullingPass.cpp +++ b/src/Engine/Graphics/RenderPass/LightCullingPass.cpp @@ -8,6 +8,7 @@ #include "Math/Vector.h" #include "RenderGraph.h" #include "Scene/Scene.h" +#include "Scene/LightEnvironment.h" using namespace Seele; diff --git a/src/Engine/Graphics/RenderPass/RayTracingPass.cpp b/src/Engine/Graphics/RenderPass/RayTracingPass.cpp index c1f5732..a40bf8f 100644 --- a/src/Engine/Graphics/RenderPass/RayTracingPass.cpp +++ b/src/Engine/Graphics/RenderPass/RayTracingPass.cpp @@ -6,6 +6,7 @@ #include "Graphics/StaticMeshVertexData.h" #include "Material/Material.h" #include "Material/MaterialInstance.h" +#include "Scene/LightEnvironment.h" #include "RenderPass.h" #include diff --git a/src/Engine/Graphics/RenderPass/ShadowPass.cpp b/src/Engine/Graphics/RenderPass/ShadowPass.cpp index c820197..d6dbca9 100644 --- a/src/Engine/Graphics/RenderPass/ShadowPass.cpp +++ b/src/Engine/Graphics/RenderPass/ShadowPass.cpp @@ -4,6 +4,7 @@ #include "Graphics/Initializer.h" #include "Graphics/Shader.h" #include "Math/Matrix.h" +#include "Scene/LightEnvironment.h" #include #include diff --git a/src/Engine/Graphics/RenderPass/SimulationComputePass.cpp b/src/Engine/Graphics/RenderPass/SimulationComputePass.cpp index 92cb0e4..63fc77f 100644 --- a/src/Engine/Graphics/RenderPass/SimulationComputePass.cpp +++ b/src/Engine/Graphics/RenderPass/SimulationComputePass.cpp @@ -7,13 +7,18 @@ #include "Graphics/Initializer.h" #include "Graphics/Shader.h" #include "Math/Vector.h" -#include +#include "Scene/FluidScene.h" +#include using namespace Seele; -constexpr static int reinitInterval = 5; -constexpr static int reinitIterations = 5; -constexpr static float reinitDtau = 0.5f; +// Reinitialization is in pseudo-time; keep it gentle to avoid eroding the interface each frame. +constexpr static int reinitInterval = 20; +constexpr static int reinitIterations = 1; +constexpr static float reinitDtau = 0.05f; +constexpr static int velocityDiffuseIterations = 8; +constexpr static int pressureSolveIterations = 24; +constexpr static int densityDiffuseIterations = 8; #define SWAP(a, b) \ { \ @@ -23,6 +28,12 @@ constexpr static float reinitDtau = 0.5f; } SimulationComputePass::SimulationComputePass(Gfx::PGraphics graphics, PScene scene) : RenderPass(graphics), scene(scene) { + gridSizeLayout = graphics->createDescriptorLayout("gridParams"); + gridSizeLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "gridSize", + .uniformLength = sizeof(UVector), + }); + gridSizeLayout->create(); { setBounds.pipelineLayout = graphics->createPipelineLayout("SetBoundsPipelineLayout"); setBounds.descriptorLayout = graphics->createDescriptorLayout("params"); @@ -36,6 +47,7 @@ SimulationComputePass::SimulationComputePass(Gfx::PGraphics graphics, PScene sce }); setBounds.descriptorLayout->create(); setBounds.pipelineLayout->addDescriptorLayout(setBounds.descriptorLayout); + setBounds.pipelineLayout->addDescriptorLayout(gridSizeLayout); graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "SetBound", .modules = {"SetBound"}, @@ -84,6 +96,7 @@ SimulationComputePass::SimulationComputePass(Gfx::PGraphics graphics, PScene sce }); linearSolve.descriptorLayout->create(); linearSolve.pipelineLayout->addDescriptorLayout(linearSolve.descriptorLayout); + linearSolve.pipelineLayout->addDescriptorLayout(gridSizeLayout); graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "LinearSolve", .modules = {"LinearSolve"}, @@ -122,6 +135,7 @@ SimulationComputePass::SimulationComputePass(Gfx::PGraphics graphics, PScene sce }); computeDivergence.descriptorLayout->create(); computeDivergence.pipelineLayout->addDescriptorLayout(computeDivergence.descriptorLayout); + computeDivergence.pipelineLayout->addDescriptorLayout(gridSizeLayout); graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "Divergence", .modules = {"Divergence"}, @@ -157,6 +171,7 @@ SimulationComputePass::SimulationComputePass(Gfx::PGraphics graphics, PScene sce }); project.descriptorLayout->create(); project.pipelineLayout->addDescriptorLayout(project.descriptorLayout); + project.pipelineLayout->addDescriptorLayout(gridSizeLayout); graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "Project", .modules = {"Project"}, @@ -199,6 +214,7 @@ SimulationComputePass::SimulationComputePass(Gfx::PGraphics graphics, PScene sce }); advect.descriptorLayout->create(); advect.pipelineLayout->addDescriptorLayout(advect.descriptorLayout); + advect.pipelineLayout->addDescriptorLayout(gridSizeLayout); graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "Advect", .modules = {"Advect"}, @@ -230,7 +246,7 @@ SimulationComputePass::SimulationComputePass(Gfx::PGraphics graphics, PScene sce }); reinitialize.descriptorLayout->create(); reinitialize.pipelineLayout->addDescriptorLayout(reinitialize.descriptorLayout); - + reinitialize.pipelineLayout->addDescriptorLayout(gridSizeLayout); graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "SignedDistance", .modules = {"SignedDistance"}, @@ -244,6 +260,45 @@ SimulationComputePass::SimulationComputePass(Gfx::PGraphics graphics, PScene sce .pipelineLayout = reinitialize.pipelineLayout, }); } + { + applyForces.pipelineLayout = graphics->createPipelineLayout("ApplyForcesPipelineLayout"); + applyForces.descriptorLayout = graphics->createDescriptorLayout("params"); + applyForces.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityX", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + applyForces.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityY", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + applyForces.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityZ", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + applyForces.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "force", + .uniformLength = sizeof(Vector), + }); + applyForces.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "dt", + .uniformLength = sizeof(float), + }); + applyForces.descriptorLayout->create(); + applyForces.pipelineLayout->addDescriptorLayout(applyForces.descriptorLayout); + applyForces.pipelineLayout->addDescriptorLayout(gridSizeLayout); + graphics->beginShaderCompilation(ShaderCompilationInfo{ + .name = "ApplyForces", + .modules = {"ApplyForces"}, + .entryPoints = {{"applyForces", "ApplyForces"}}, + .rootSignature = applyForces.pipelineLayout, + }); + applyForces.pipelineLayout->create(); + applyForces.shader = graphics->createComputeShader({0}); + applyForces.pipeline = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = applyForces.shader, + .pipelineLayout = applyForces.pipelineLayout, + }); + } } SimulationComputePass::~SimulationComputePass() {} @@ -255,13 +310,21 @@ void SimulationComputePass::beginFrame(const Seele::Component::Camera&, const Se project.descriptorLayout->reset(); advect.descriptorLayout->reset(); reinitialize.descriptorLayout->reset(); + applyForces.descriptorLayout->reset(); + gridSizeLayout->reset(); } void SimulationComputePass::render() { for (auto& [entityID, data] : scene->getFluidScene()->getFluidDataMap()) { + if (data.paused) { + continue; + } + gridSizeSet = gridSizeLayout->allocateDescriptorSet(); + gridSizeSet->updateConstants("gridSize", 0, &data.gridSize); + gridSizeSet->writeChanges(); graphics->beginDebugRegion("Diffuse"); - for (uint iteration = 0; iteration < 4; ++iteration) { - float a = dt * data.viscosity * (data.gridSize.x - 2) * (data.gridSize.y - 2) * (data.gridSize.z - 2); + for (int iteration = 0; iteration < velocityDiffuseIterations; ++iteration) { + float a = data.dt * data.viscosity * (data.gridSize.x - 2) * (data.gridSize.x - 2); float c = 1 + 6 * a; linearSolvePass(data.velocityXNextBuffer, data.velocityXBuffer, data.velocityX0Buffer, a, c, data.gridSize); linearSolvePass(data.velocityYNextBuffer, data.velocityYBuffer, data.velocityY0Buffer, a, c, data.gridSize); @@ -287,7 +350,7 @@ void SimulationComputePass::render() { Gfx::PShaderBuffer pressureNext = data.velocityZBuffer; divergencePass(divergence, pressureCurrent, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, data.gridSize); - for (int iteration = 0; iteration < 4; ++iteration) { + for (int iteration = 0; iteration < pressureSolveIterations; ++iteration) { linearSolvePass(pressureNext, pressureCurrent, divergence, 1, 6, data.gridSize); setBoundsPass(pressureNext, 0, data.gridSize); @@ -304,15 +367,38 @@ void SimulationComputePass::render() { graphics->endDebugRegion(); graphics->beginDebugRegion("Advect"); - advectPass(data.velocityXBuffer, data.velocityX0Buffer, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, dt, data.gridSize); - advectPass(data.velocityYBuffer, data.velocityY0Buffer, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, dt, data.gridSize); - advectPass(data.velocityZBuffer, data.velocityZ0Buffer, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, dt, data.gridSize); + advectPass(data.velocityXBuffer, data.velocityX0Buffer, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, data.dt, + data.gridSize); + advectPass(data.velocityYBuffer, data.velocityY0Buffer, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, data.dt, + data.gridSize); + advectPass(data.velocityZBuffer, data.velocityZ0Buffer, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, data.dt, + data.gridSize); + + if (data.enableGravity) { + applyForcesPass(data.velocityXBuffer, data.velocityYBuffer, data.velocityZBuffer, data.gravity, data.dt, data.gridSize); + } + + // Set boundary conditions after advection/forces so the divergence sees correct wall values + setBoundsPass(data.velocityXBuffer, 1, data.gridSize); + setBoundsPass(data.velocityYBuffer, 2, data.gridSize); + setBoundsPass(data.velocityZBuffer, 3, data.gridSize); + + // Re-project after advection/forces to keep the final velocity field divergence-free. + Gfx::PShaderBuffer advectedDivergence = data.velocityXNextBuffer; + Gfx::PShaderBuffer advectedPressureCurrent = data.velocityYNextBuffer; + Gfx::PShaderBuffer advectedPressureNext = data.velocityZNextBuffer; + divergencePass(advectedDivergence, advectedPressureCurrent, data.velocityXBuffer, data.velocityYBuffer, data.velocityZBuffer, data.gridSize); + for (int iteration = 0; iteration < pressureSolveIterations; ++iteration) { + linearSolvePass(advectedPressureNext, advectedPressureCurrent, advectedDivergence, 1, 6, data.gridSize); + setBoundsPass(advectedPressureNext, 0, data.gridSize); + SWAP(advectedPressureCurrent, advectedPressureNext); + } + projectPass(data.velocityXBuffer, data.velocityYBuffer, data.velocityZBuffer, advectedPressureCurrent, data.gridSize); setBoundsPass(data.velocityXBuffer, 1, data.gridSize); setBoundsPass(data.velocityYBuffer, 2, data.gridSize); setBoundsPass(data.velocityZBuffer, 3, data.gridSize); graphics->endDebugRegion(); - // swap velocity buffers SWAP(data.velocityX0Buffer, data.velocityXBuffer); SWAP(data.velocityY0Buffer, data.velocityYBuffer); @@ -326,8 +412,8 @@ void SimulationComputePass::render() { graphics->copyBuffer(density0, densityCurrent); densityCurrent->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - for (int iteration = 0; iteration < 4; ++iteration) { - float a = dt * data.diffusion * (data.gridSize.x - 2) * (data.gridSize.y - 2) * (data.gridSize.z - 2); + for (int iteration = 0; iteration < densityDiffuseIterations; ++iteration) { + float a = data.dt * data.diffusion * (data.gridSize.x - 2) * (data.gridSize.x - 2); float c = 1 + 6 * a; linearSolvePass(densityNext, densityCurrent, density0, a, c, data.gridSize); setBoundsPass(densityNext, 0, data.gridSize); @@ -335,12 +421,13 @@ void SimulationComputePass::render() { } graphics->endDebugRegion(); graphics->beginDebugRegion("AdvectDensity"); - advectPass(data.density0Buffer, densityCurrent, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, dt, data.gridSize); + advectPass(data.density0Buffer, densityCurrent, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, data.dt, + data.gridSize); setBoundsPass(data.density0Buffer, 0, data.gridSize); graphics->endDebugRegion(); graphics->beginDebugRegion("AdvectLevelSet"); - advectPass(data.phiBuffer, data.phi0Buffer, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, dt, data.gridSize); + advectPass(data.phiBuffer, data.phi0Buffer, data.velocityX0Buffer, data.velocityY0Buffer, data.velocityZ0Buffer, data.dt, data.gridSize); setBoundsPass(data.phiBuffer, 0, data.gridSize); // Reinitialize level set every N frames to restore |grad phi| = 1 @@ -380,7 +467,11 @@ void SimulationComputePass::setBoundsPass(Gfx::PShaderBuffer grid, int b, UVecto Gfx::OComputeCommand boundsCommand = graphics->createComputeCommand(); boundsCommand->bindPipeline(setBounds.pipeline); boundsCommand->bindDescriptor(bounds); - boundsCommand->dispatch(getDispatchSize(gridSize, setBounds.threadGroupSize)); + boundsCommand->bindDescriptor(gridSizeSet); + // Dispatch covers max(gridSize.x, gridSize.y) x max(gridSize.y, gridSize.z) to handle all faces on non-cubic grids + UVector faceDispatchGrid = {std::max(gridSize.x, gridSize.y), std::max(gridSize.y, gridSize.z), 1}; + auto dispatchSize = getDispatchSize(faceDispatchGrid, setBounds.threadGroupSize); + boundsCommand->dispatch(dispatchSize.x, dispatchSize.y, 1); graphics->executeCommands(std::move(boundsCommand)); grid->pipelineBarrier(Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_WRITE_BIT, @@ -389,7 +480,10 @@ void SimulationComputePass::setBoundsPass(Gfx::PShaderBuffer grid, int b, UVecto Gfx::OComputeCommand edgesCommand = graphics->createComputeCommand(); edgesCommand->bindPipeline(setBounds.pipelineEdges); edgesCommand->bindDescriptor(bounds); - edgesCommand->dispatch(getDispatchSize(gridSize, setBounds.threadGroupSize).x, 1, 1); + edgesCommand->bindDescriptor(gridSizeSet); + // Dispatch covers max dimension to handle all 12 edges on non-cubic grids; edge shader uses numthreads(128,1,1) + uint32_t maxDim = std::max({gridSize.x, gridSize.y, gridSize.z}); + edgesCommand->dispatch((maxDim + 127) / 128, 1, 1); graphics->executeCommands(std::move(edgesCommand)); grid->pipelineBarrier(Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_WRITE_BIT, @@ -398,6 +492,7 @@ void SimulationComputePass::setBoundsPass(Gfx::PShaderBuffer grid, int b, UVecto Gfx::OComputeCommand cornersCommand = graphics->createComputeCommand(); cornersCommand->bindPipeline(setBounds.pipelineCorners); cornersCommand->bindDescriptor(bounds); + cornersCommand->bindDescriptor(gridSizeSet); cornersCommand->dispatch(1, 1, 1); graphics->executeCommands(std::move(cornersCommand)); @@ -405,7 +500,8 @@ void SimulationComputePass::setBoundsPass(Gfx::PShaderBuffer grid, int b, UVecto Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); } -void SimulationComputePass::linearSolvePass(Gfx::PShaderBuffer next, Gfx::PShaderBuffer current, Gfx::PShaderBuffer grid0, float a, float c, UVector gridSize) { +void SimulationComputePass::linearSolvePass(Gfx::PShaderBuffer next, Gfx::PShaderBuffer current, Gfx::PShaderBuffer grid0, float a, float c, + UVector gridSize) { Gfx::ODescriptorSet solve = linearSolve.descriptorLayout->allocateDescriptorSet(); solve->updateBuffer("next", 0, next); solve->updateBuffer("current", 0, current); @@ -416,6 +512,7 @@ void SimulationComputePass::linearSolvePass(Gfx::PShaderBuffer next, Gfx::PShade Gfx::OComputeCommand solveCommand = graphics->createComputeCommand(); solveCommand->bindPipeline(linearSolve.pipeline); solveCommand->bindDescriptor(solve); + solveCommand->bindDescriptor(gridSizeSet); solveCommand->dispatch(getDispatchSize(gridSize, linearSolve.threadGroupSize)); graphics->executeCommands(std::move(solveCommand)); @@ -435,6 +532,7 @@ void SimulationComputePass::divergencePass(Seele::Gfx::PShaderBuffer divergence, Gfx::OComputeCommand divergenceCommand = graphics->createComputeCommand(); divergenceCommand->bindPipeline(computeDivergence.pipeline); divergenceCommand->bindDescriptor(divergenceSet); + divergenceCommand->bindDescriptor(gridSizeSet); divergenceCommand->dispatch(getDispatchSize(gridSize, computeDivergence.threadGroupSize)); graphics->executeCommands(std::move(divergenceCommand)); // divergence is now in velocityXBuffer, pressure is in velocityYBuffer @@ -455,6 +553,7 @@ void SimulationComputePass::projectPass(Seele::Gfx::PShaderBuffer velocityX, See Gfx::OComputeCommand projectCommand = graphics->createComputeCommand(); projectCommand->bindPipeline(project.pipeline); projectCommand->bindDescriptor(projectSet); + projectCommand->bindDescriptor(gridSizeSet); projectCommand->dispatch(getDispatchSize(gridSize, project.threadGroupSize)); graphics->executeCommands(std::move(projectCommand)); @@ -479,6 +578,7 @@ void SimulationComputePass::advectPass(Gfx::PShaderBuffer quantity, Gfx::PShader Gfx::OComputeCommand advectCommand = graphics->createComputeCommand(); advectCommand->bindPipeline(advect.pipeline); advectCommand->bindDescriptor(advectSet); + advectCommand->bindDescriptor(gridSizeSet); advectCommand->dispatch(getDispatchSize(gridSize, advect.threadGroupSize)); graphics->executeCommands(std::move(advectCommand)); @@ -486,6 +586,29 @@ void SimulationComputePass::advectPass(Gfx::PShaderBuffer quantity, Gfx::PShader Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); } +void SimulationComputePass::applyForcesPass(Gfx::PShaderBuffer velocityX, Gfx::PShaderBuffer velocityY, Gfx::PShaderBuffer velocityZ, Vector gravity, float dt, UVector gridSize) { + Gfx::ODescriptorSet forcesSet = applyForces.descriptorLayout->allocateDescriptorSet(); + forcesSet->updateBuffer("velocityX", 0, velocityX); + forcesSet->updateBuffer("velocityY", 0, velocityY); + forcesSet->updateBuffer("velocityZ", 0, velocityZ); + forcesSet->updateConstants("force", 0, &gravity); + forcesSet->updateConstants("dt", 0, &dt); + forcesSet->writeChanges(); + Gfx::OComputeCommand forcesCommand = graphics->createComputeCommand(); + forcesCommand->bindPipeline(applyForces.pipeline); + forcesCommand->bindDescriptor(forcesSet); + forcesCommand->bindDescriptor(gridSizeSet); + forcesCommand->dispatch(getDispatchSize(gridSize, applyForces.threadGroupSize)); + graphics->executeCommands(std::move(forcesCommand)); + + velocityX->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + velocityY->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + velocityZ->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} + void SimulationComputePass::reinitializeLevelSetPass(Seele::Gfx::PShaderBuffer phi, Seele::Gfx::PShaderBuffer phi0, UVector gridSize) { graphics->beginDebugRegion("ReinitializeLevelSet"); // Snapshot current phi into phi0 for the sign function @@ -507,6 +630,7 @@ void SimulationComputePass::reinitializeLevelSetPass(Seele::Gfx::PShaderBuffer p Gfx::OComputeCommand reinitCommand = graphics->createComputeCommand(); reinitCommand->bindPipeline(reinitialize.pipeline); reinitCommand->bindDescriptor(reinitSet); + reinitCommand->bindDescriptor(gridSizeSet); reinitCommand->dispatch(getDispatchSize(gridSize, reinitialize.threadGroupSize)); graphics->executeCommands(std::move(reinitCommand)); diff --git a/src/Engine/Graphics/RenderPass/SimulationComputePass.h b/src/Engine/Graphics/RenderPass/SimulationComputePass.h index 656fcb6..4c0d0a8 100644 --- a/src/Engine/Graphics/RenderPass/SimulationComputePass.h +++ b/src/Engine/Graphics/RenderPass/SimulationComputePass.h @@ -75,8 +75,17 @@ class SimulationComputePass : public RenderPass { Gfx::PComputePipeline pipeline; constexpr static UVector threadGroupSize = {32, 8, 1}; } reinitialize; + struct ApplyForces { + Gfx::OPipelineLayout pipelineLayout; + Gfx::ODescriptorLayout descriptorLayout; + Gfx::OComputeShader shader; + Gfx::PComputePipeline pipeline; + constexpr static UVector threadGroupSize = {32, 8, 1}; + } applyForces; + void applyForcesPass(Gfx::PShaderBuffer velocityX, Gfx::PShaderBuffer velocityY, Gfx::PShaderBuffer velocityZ, Vector gravity, float dt, UVector gridSize); + Gfx::ODescriptorLayout gridSizeLayout; + Gfx::ODescriptorSet gridSizeSet; PScene scene; - constexpr static float dt = 0.1f; }; DEFINE_REF(SimulationComputePass) } // namespace Seele \ No newline at end of file diff --git a/src/Engine/Graphics/RenderPass/SurfaceExtractPass.cpp b/src/Engine/Graphics/RenderPass/SurfaceExtractPass.cpp index e013e70..34d5c8d 100644 --- a/src/Engine/Graphics/RenderPass/SurfaceExtractPass.cpp +++ b/src/Engine/Graphics/RenderPass/SurfaceExtractPass.cpp @@ -5,21 +5,18 @@ #include "Graphics/Graphics.h" #include "Graphics/Initializer.h" #include "Graphics/Shader.h" -#include "Math/Vector.h" +#include "Scene/FluidScene.h" using namespace Seele; -// Linearly interpolate the position where the isosurface crosses the edge -// between two grid points. -static Vector vertexInterp(float isolevel, const Vector& p1, const Vector& p2, float v1, float v2) { - if (std::abs(v1 - v2) < 1e-10f) - return p1; - float t = (isolevel - v1) / (v2 - v1); - return Vector(p1.x + t * (p2.x - p1.x), p1.y + t * (p2.y - p1.y), p1.z + t * (p2.z - p1.z)); -} - SurfaceExtractPass::SurfaceExtractPass(Gfx::PGraphics graphics, PScene scene) : RenderPass(graphics), scene(scene) { pipelineLayout = graphics->createPipelineLayout("SurfaceExtractPipelineLayout"); + gridSizeLayout = graphics->createDescriptorLayout("gridParams"); + gridSizeLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "gridSize", + .uniformLength = sizeof(UVector), + }); + gridSizeLayout->create(); descriptorLayout = graphics->createDescriptorLayout("params"); descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = "vertexBuffer", @@ -43,6 +40,7 @@ SurfaceExtractPass::SurfaceExtractPass(Gfx::PGraphics graphics, PScene scene) : }); descriptorLayout->create(); pipelineLayout->addDescriptorLayout(descriptorLayout); + pipelineLayout->addDescriptorLayout(gridSizeLayout); graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "MarchingCubes", .modules = {"MarchingCubes"}, @@ -62,8 +60,12 @@ SurfaceExtractPass::~SurfaceExtractPass() {} void SurfaceExtractPass::beginFrame(const Seele::Component::Camera&, const Seele::Component::Transform&) {} + void SurfaceExtractPass::render() { for (auto& [entityID, data] : scene->getFluidScene()->getFluidDataMap()) { + Gfx::ODescriptorSet gridSizeSet = gridSizeLayout->allocateDescriptorSet(); + gridSizeSet->updateConstants("gridSize", 0, &data.gridSize); + gridSizeSet->writeChanges(); data.vertexBuffer->rotateBuffer(data.getVertexBufferSize() * sizeof(float)); data.normalBuffer->rotateBuffer(data.getVertexBufferSize() * sizeof(float)); data.indexBuffer->rotateBuffer(data.getIndexBufferSize() * sizeof(uint32)); @@ -83,6 +85,7 @@ void SurfaceExtractPass::render() { Gfx::OComputeCommand cmd = graphics->createComputeCommand(); cmd->bindPipeline(pipeline); cmd->bindDescriptor(descriptorSet); + cmd->bindDescriptor(gridSizeSet); cmd->dispatch((data.gridSize + threadGroupSize - 1u) / threadGroupSize); graphics->executeCommands(std::move(cmd)); diff --git a/src/Engine/Graphics/RenderPass/SurfaceExtractPass.h b/src/Engine/Graphics/RenderPass/SurfaceExtractPass.h index 59dee6e..259bcbd 100644 --- a/src/Engine/Graphics/RenderPass/SurfaceExtractPass.h +++ b/src/Engine/Graphics/RenderPass/SurfaceExtractPass.h @@ -20,6 +20,7 @@ private: Gfx::OPipelineLayout pipelineLayout; Gfx::ODescriptorLayout descriptorLayout; + Gfx::ODescriptorLayout gridSizeLayout; Gfx::ODescriptorSet descriptorSet; Gfx::OComputeShader computeShader; Gfx::PComputePipeline pipeline; diff --git a/src/Engine/Graphics/Vulkan/Graphics.cpp b/src/Engine/Graphics/Vulkan/Graphics.cpp index 35833ef..dcd7f0d 100644 --- a/src/Engine/Graphics/Vulkan/Graphics.cpp +++ b/src/Engine/Graphics/Vulkan/Graphics.cpp @@ -848,6 +848,7 @@ void Graphics::pickPhysicalDevice() { vkGetPhysicalDeviceProperties2(physicalDevice, &props.get()); features.get().features = { + // .robustBufferAccess = true, .geometryShader = true, .sampleRateShading = true, .fillModeNonSolid = true, diff --git a/src/Engine/Graphics/slang-compile.cpp b/src/Engine/Graphics/slang-compile.cpp index 48a9d18..7b64112 100644 --- a/src/Engine/Graphics/slang-compile.cpp +++ b/src/Engine/Graphics/slang-compile.cpp @@ -1,6 +1,8 @@ #include "slang-compile.h" #include "Containers/Array.h" #include "Graphics/Descriptor.h" +#include +#include #include #include #include @@ -29,113 +31,54 @@ thread_local Slang::ComPtr session; thread_local Array entryPoints; namespace { -std::filesystem::path getExecutableDirectory() { - std::error_code error; - auto exePath = std::filesystem::read_symlink("/proc/self/exe", error); - if (!error) { - return exePath.parent_path(); +void appendEnvPath(const char* varName, const std::filesystem::path& path) { + if (!std::filesystem::exists(path) || !std::filesystem::is_directory(path)) { + return; } - return std::filesystem::current_path(); + + const std::string newEntry = path.lexically_normal().string(); + const char* currentRaw = std::getenv(varName); + std::string current = currentRaw ? currentRaw : ""; + if (current.find(newEntry) != std::string::npos) { + return; + } + + std::string updated = current.empty() ? newEntry : (newEntry + ":" + current); + setenv(varName, updated.c_str(), 1); } -std::filesystem::path findExistingDirectory(std::initializer_list candidates) { +void configureSlangRuntimeEnv() { + std::filesystem::path exePath; + try { + exePath = std::filesystem::canonical("/proc/self/exe"); + } catch (...) { + return; + } + + const std::filesystem::path exeDir = exePath.parent_path(); + const std::array candidates = { + exeDir / "vcpkg_installed/x64-linux/lib", + exeDir / "vcpkg_installed/x64-linux/debug/lib", + exeDir / "vcpkg_installed/x64-linux/tools/shader-slang", + exeDir.parent_path() / "vcpkg_installed/x64-linux/lib", + exeDir.parent_path() / "vcpkg_installed/x64-linux/debug/lib", + exeDir.parent_path() / "vcpkg_installed/x64-linux/tools/shader-slang", + std::filesystem::current_path() / "build/vcpkg_installed/x64-linux/tools/shader-slang", + }; + for (const auto& candidate : candidates) { - std::error_code error; - if (std::filesystem::exists(candidate, error) && std::filesystem::is_directory(candidate, error)) { - return candidate; - } + appendEnvPath("LD_LIBRARY_PATH", candidate); + appendEnvPath("PATH", candidate); } - return {}; -} - -bool containsDownstreamCompilerArtifacts(const std::filesystem::path& directory) { - std::error_code error; - if (!std::filesystem::exists(directory, error) || !std::filesystem::is_directory(directory, error)) { - return false; - } - - for (const auto& entry : std::filesystem::directory_iterator(directory, error)) { - if (error || !entry.is_regular_file(error)) { - continue; - } - - const auto fileName = entry.path().filename().string(); - if (fileName.rfind("slang-glslang", 0) == 0 || fileName.rfind("spirv-opt", 0) == 0 || fileName.rfind("spirv-dis", 0) == 0) { - return true; - } - } - - return false; -} - -bool hasArtifactWithPrefix(const std::filesystem::path& directory, const char* prefix) { - std::error_code error; - if (!std::filesystem::exists(directory, error) || !std::filesystem::is_directory(directory, error)) { - return false; - } - - for (const auto& entry : std::filesystem::directory_iterator(directory, error)) { - if (error || !entry.is_regular_file(error)) { - continue; - } - - if (entry.path().filename().string().rfind(prefix, 0) == 0) { - return true; - } - } - - return false; -} - -std::filesystem::path findDownstreamCompilerDirectory(std::initializer_list candidates) { - for (const auto& candidate : candidates) { - if (containsDownstreamCompilerArtifacts(candidate)) { - return candidate; - } - } - - return findExistingDirectory(candidates); -} - -void configureDownstreamCompilers(slang::IGlobalSession* session) { - const auto executableDirectory = getExecutableDirectory(); - const auto currentDirectory = std::filesystem::current_path(); - const auto glslangDirectory = findDownstreamCompilerDirectory({ - executableDirectory, - executableDirectory / "Seele", - executableDirectory / "vcpkg_installed" / "x64-linux" / "lib", - currentDirectory / "build", - currentDirectory / "build" / "Seele", - currentDirectory / "Seele", - currentDirectory / "vcpkg_installed" / "x64-linux" / "lib", - }); - - if (!glslangDirectory.empty()) { - const std::string path = glslangDirectory.string(); - if (hasArtifactWithPrefix(glslangDirectory, "slang-glslang") || hasArtifactWithPrefix(glslangDirectory, "libslang-glslang")) { - session->setDownstreamCompilerPath(SLANG_PASS_THROUGH_GLSLANG, path.c_str()); - } - if (hasArtifactWithPrefix(glslangDirectory, "spirv-opt")) { - session->setDownstreamCompilerPath(SLANG_PASS_THROUGH_SPIRV_OPT, path.c_str()); - } - if (hasArtifactWithPrefix(glslangDirectory, "spirv-dis")) { - session->setDownstreamCompilerPath(SLANG_PASS_THROUGH_SPIRV_DIS, path.c_str()); - } - } - - // Avoid optional SPIR-V downstream tools that are often unavailable in RenderDoc launch environments. - session->setDefaultDownstreamCompiler(SLANG_SOURCE_LANGUAGE_SPIRV, SLANG_PASS_THROUGH_NONE); - session->setDownstreamCompilerForTransition(SLANG_SPIRV, SLANG_SPIRV, SLANG_PASS_THROUGH_NONE); - session->setDownstreamCompilerForTransition(SLANG_SPIRV, SLANG_SPIRV_ASM, SLANG_PASS_THROUGH_NONE); -} } +} // namespace void Seele::beginCompilation(const ShaderCompilationInfo& info, SlangCompileTarget target, Gfx::PPipelineLayout layout) { if (!globalSession) { + configureSlangRuntimeEnv(); SlangGlobalSessionDesc sessionDesc = {}; sessionDesc.enableGLSL = true; slang::createGlobalSession(&sessionDesc, globalSession.writeRef()); - configureDownstreamCompilers(globalSession); } slang::SessionDesc sessionDesc; sessionDesc.flags = 0; @@ -156,6 +99,22 @@ void Seele::beginCompilation(const ShaderCompilationInfo& info, SlangCompileTarg .intValue0 = SLANG_OPTIMIZATION_LEVEL_NONE, }, }, + { + .name = slang::CompilerOptionName::EmitSpirvMethod, + .value = + { + .kind = slang::CompilerOptionValueKind::Int, + .intValue0 = SLANG_EMIT_SPIRV_DIRECTLY, + }, + }, + { + .name = slang::CompilerOptionName::PassThrough, + .value = + { + .kind = slang::CompilerOptionValueKind::Int, + .intValue0 = SLANG_PASS_THROUGH_NONE, + }, + }, { .name = slang::CompilerOptionName::SkipSPIRVValidation, .value = diff --git a/src/Engine/Scene/FluidScene.cpp b/src/Engine/Scene/FluidScene.cpp index 2d9e0dd..d8475ab 100644 --- a/src/Engine/Scene/FluidScene.cpp +++ b/src/Engine/Scene/FluidScene.cpp @@ -1,9 +1,45 @@ #include "FluidScene.h" +#include "Graphics/Command.h" +#include "Graphics/Descriptor.h" #include "Graphics/Enums.h" +#include "Graphics/Initializer.h" using namespace Seele; -FluidScene::FluidScene(Gfx::PGraphics graphics) : graphics(graphics) {} +FluidScene::FluidScene(Gfx::PGraphics graphics) : graphics(graphics) { + pipelineLayout = graphics->createPipelineLayout("FluidScenePipelineLayout"); + gridParamsLayout = graphics->createDescriptorLayout("gridParams"); + gridParamsLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "gridSize", + .uniformLength = sizeof(UVector), + }); + gridParamsLayout->create(); + descriptorLayout = graphics->createDescriptorLayout("params"); + descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "phi", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "density", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + descriptorLayout->create(); + pipelineLayout->addDescriptorLayout(descriptorLayout); + pipelineLayout->addDescriptorLayout(gridParamsLayout); + + graphics->beginShaderCompilation(ShaderCompilationInfo{ + .name = "AddSource", + .modules = {"AddSource"}, + .entryPoints = {{"addSource", "AddSource"}}, + .rootSignature = pipelineLayout, + }); + pipelineLayout->create(); + shader = graphics->createComputeShader({0}); + pipeline = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = shader, + .pipelineLayout = pipelineLayout, + }); +} FluidScene::~FluidScene() {} @@ -12,37 +48,37 @@ void FluidScene::updateFluidData(uint32 entityID, const Component::Transform& tr auto& data = fluidDataMap[entityID]; uint64 totalCells = (uint64)grid.gridSize.x * grid.gridSize.y * grid.gridSize.z; uint64 bufferSize = totalCells * sizeof(float); - Array initialDensity(bufferSize / sizeof(float)); - Array initialVelocityX(bufferSize / sizeof(float)); - Array initialVelocityY(bufferSize / sizeof(float)); - Array initialVelocityZ(bufferSize / sizeof(float)); - Array initialPhi(bufferSize / sizeof(float)); + // Array initialDensity(bufferSize / sizeof(float)); + // Array initialVelocityX(bufferSize / sizeof(float)); + // Array initialVelocityY(bufferSize / sizeof(float)); + // Array initialVelocityZ(bufferSize / sizeof(float)); + // Array initialPhi(bufferSize / sizeof(float)); - // Initialize level set to positive (outside) everywhere - float halfGridX = static_cast(grid.gridSize.x) * 0.5f; - float halfGridY = static_cast(grid.gridSize.y) * 0.5f; - float radius = static_cast(grid.gridSize.x) * 0.2f; - // Place sphere center in the lower portion of the grid - float cx = halfGridX; - float cy = halfGridY; - float cz = static_cast(grid.gridSize.z) * 0.3f; - for (uint32 z = 1; z < grid.gridSize.z - 1; ++z) { - for (uint32 y = 1; y < grid.gridSize.y - 1; ++y) { - for (uint32 x = 1; x < grid.gridSize.x - 1; ++x) { - uint32 idx = x + grid.gridSize.x * y + grid.gridSize.x * grid.gridSize.y * z; - float dx = static_cast(x) - cx; - float dy = static_cast(y) - cy; - float dz = static_cast(z) - cz; - float dist = std::sqrt(dx * dx + dy * dy + dz * dz); - initialPhi[idx] = dist - radius; - if (dist < radius) { - initialDensity[idx] = 1.0f; - // Give an initial upward velocity to kick the simulation - initialVelocityZ[idx] = 5.0f; - } - } - } - } + // // Initialize level set to positive (outside) everywhere + // float halfGridX = static_cast(grid.gridSize.x) * 0.5f; + // float halfGridY = static_cast(grid.gridSize.y) * 0.5f; + // float radius = static_cast(grid.gridSize.x) * 0.2f; + // // Place sphere center in the lower portion of the grid + // float cx = halfGridX; + // float cy = halfGridY; + // float cz = static_cast(grid.gridSize.z) * 0.3f; + // for (uint32 z = 1; z < grid.gridSize.z - 1; ++z) { + // for (uint32 y = 1; y < grid.gridSize.y - 1; ++y) { + // for (uint32 x = 1; x < grid.gridSize.x - 1; ++x) { + // uint32 idx = x + grid.gridSize.x * y + grid.gridSize.x * grid.gridSize.y * z; + // float dx = static_cast(x) - cx; + // float dy = static_cast(y) - cy; + // float dz = static_cast(z) - cz; + // float dist = std::sqrt(dx * dx + dy * dy + dz * dz); + // initialPhi[idx] = dist - radius; + // if (dist < radius) { + // initialDensity[idx] = 1.0f; + // // Give an initial upward velocity to kick the simulation + // initialVelocityZ[idx] = 5.0f; + // } + // } + // } + // } data.phiBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .sourceData = @@ -55,7 +91,7 @@ void FluidScene::updateFluidData(uint32 entityID, const Component::Transform& tr .sourceData = { .size = bufferSize, - .data = (uint8*)initialPhi.data(), + // .data = (uint8*)initialPhi.data(), }, .name = "Phi0Buffer", }); @@ -70,7 +106,7 @@ void FluidScene::updateFluidData(uint32 entityID, const Component::Transform& tr .sourceData = { .size = bufferSize, - .data = (uint8*)initialDensity.data(), + // .data = (uint8*)initialDensity.data(), }, .name = "Density0Buffer", }); @@ -127,7 +163,7 @@ void FluidScene::updateFluidData(uint32 entityID, const Component::Transform& tr .sourceData = { .size = bufferSize, - .data = (uint8*)initialVelocityX.data(), + // .data = (uint8*)initialVelocityX.data(), }, .name = "VelocityX0Buffer", }); @@ -135,7 +171,7 @@ void FluidScene::updateFluidData(uint32 entityID, const Component::Transform& tr .sourceData = { .size = bufferSize, - .data = (uint8*)initialVelocityY.data(), + // .data = (uint8*)initialVelocityY.data(), }, .name = "VelocityY0Buffer", }); @@ -143,7 +179,7 @@ void FluidScene::updateFluidData(uint32 entityID, const Component::Transform& tr .sourceData = { .size = bufferSize, - .data = (uint8*)initialVelocityZ.data(), + // .data = (uint8*)initialVelocityZ.data(), }, .name = "VelocityZ0Buffer", }); @@ -157,17 +193,53 @@ void FluidScene::updateFluidData(uint32 entityID, const Component::Transform& tr .name = "IndexBuffer", }); data.surfaceCountBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ - .sourceData = { - .size = 4 * sizeof(uint32), - }, + .sourceData = + { + .size = 4 * sizeof(uint32), + }, .usage = Gfx::SE_BUFFER_USAGE_INDIRECT_BUFFER_BIT, .name = "SurfaceCountBuffer", }); } auto& data = fluidDataMap[entityID]; + data.paused = grid.paused; data.transform = transform.transform; - data.cellSize = grid.cellSize; + data.dt = grid.dt; data.viscosity = grid.viscosity; data.diffusion = grid.diffusion; data.gridSize = grid.gridSize; + data.gravity = grid.gravity; + data.enableGravity = grid.enableGravity; +} + +void FluidScene::addSource() { + for (auto& [entityID, data] : fluidDataMap) { + Gfx::ODescriptorSet gridParams = gridParamsLayout->allocateDescriptorSet(); + gridParams->updateConstants("gridSize", 0, &data.gridSize); + gridParams->writeChanges(); + Gfx::ODescriptorSet descriptorSet = descriptorLayout->allocateDescriptorSet(); + descriptorSet->updateBuffer("phi", 0, data.phi0Buffer); + descriptorSet->updateBuffer("density", 0, data.density0Buffer); + descriptorSet->writeChanges(); + Gfx::OComputeCommand command = graphics->createComputeCommand(); + command->bindPipeline(pipeline); + command->bindDescriptor(descriptorSet); + command->bindDescriptor(gridParams); + command->dispatch((data.gridSize + threadGroupSize - UVector{1, 1, 1}) / threadGroupSize); + graphics->executeCommands(std::move(command)); + data.phi0Buffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_TRANSFER_READ_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT); + data.density0Buffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + // Keep render/extraction level set in sync with injected source immediately. + graphics->copyBuffer(data.phi0Buffer, data.phiBuffer); + data.phiBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + data.phi0Buffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_READ_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + // Reinitialize soon after source injection to restore signed-distance quality. + data.frameCounter = 0; + } } \ No newline at end of file diff --git a/src/Engine/Scene/FluidScene.h b/src/Engine/Scene/FluidScene.h index e8eb5e0..22a91a8 100644 --- a/src/Engine/Scene/FluidScene.h +++ b/src/Engine/Scene/FluidScene.h @@ -5,6 +5,7 @@ #include "Containers/Map.h" #include "Graphics/Buffer.h" #include "Graphics/Graphics.h" +#include "Graphics/Resources.h" #include "Graphics/Shader.h" #include "Math/Transform.h" #include "Math/Vector.h" @@ -17,10 +18,13 @@ class FluidScene { virtual ~FluidScene(); struct FluidData { Math::Transform transform; - float cellSize; + bool paused; float viscosity; float diffusion; + float dt = 0.01f; UVector gridSize; + Vector gravity; + bool enableGravity; Gfx::OShaderBuffer phiBuffer; Gfx::OShaderBuffer phi0Buffer; Gfx::OShaderBuffer densityBuffer; @@ -49,7 +53,15 @@ class FluidScene { void updateFluidData(uint32 entityID, const Component::Transform& transform, const Component::FluidGrid& grid); const Map& getFluidDataMap() const { return fluidDataMap; } + void addSource(); + private: + Gfx::OPipelineLayout pipelineLayout; + Gfx::ODescriptorLayout descriptorLayout; + Gfx::ODescriptorLayout gridParamsLayout; + Gfx::OComputeShader shader; + Gfx::PComputePipeline pipeline; + constexpr static UVector threadGroupSize = {32, 8, 1}; Gfx::PGraphics graphics; Map fluidDataMap; }; diff --git a/src/Engine/Scene/Scene.cpp b/src/Engine/Scene/Scene.cpp index 2ba2d53..b3edea7 100644 --- a/src/Engine/Scene/Scene.cpp +++ b/src/Engine/Scene/Scene.cpp @@ -1,5 +1,7 @@ #include "Scene.h" #include "Graphics/Graphics.h" +#include "LightEnvironment.h" +#include "FluidScene.h" using namespace Seele; diff --git a/src/Engine/Scene/Scene.h b/src/Engine/Scene/Scene.h index ae7ec9b..edbecb8 100644 --- a/src/Engine/Scene/Scene.h +++ b/src/Engine/Scene/Scene.h @@ -1,14 +1,14 @@ #pragma once #include "MinimalEngine.h" #include "Graphics/Graphics.h" -#include "LightEnvironment.h" #include "Physics/PhysicsSystem.h" -#include "FluidScene.h" #include namespace Seele { DECLARE_REF(Material) DECLARE_REF(Entity) +DECLARE_REF(FluidScene) +DECLARE_REF(LightEnvironment) class Scene { public: Scene(Gfx::PGraphics graphics); diff --git a/src/Engine/System/FluidUpdater.cpp b/src/Engine/System/FluidUpdater.cpp index 01b982d..965d407 100644 --- a/src/Engine/System/FluidUpdater.cpp +++ b/src/Engine/System/FluidUpdater.cpp @@ -1,4 +1,6 @@ #include "FluidUpdater.h" +#include "Graphics/DebugVertex.h" +#include "Scene/FluidScene.h" using namespace Seele; using namespace Seele::System; @@ -10,4 +12,37 @@ FluidUpdater::~FluidUpdater() {} void FluidUpdater::update(entt::entity id, Component::FluidGrid &grid, Component::Transform &transform) { auto fluidScene = scene->getFluidScene(); fluidScene->updateFluidData((uint32)id, transform, grid); + Vector corners[8]; + corners[0] = transform.getPosition(); + corners[1] = transform.getPosition() + Vector(transform.getScale().x, 0, 0); + corners[2] = transform.getPosition() + Vector(0, transform.getScale().y, 0); + corners[3] = transform.getPosition() + Vector(0, 0, transform.getScale().z); + corners[4] = transform.getPosition() + Vector(transform.getScale().x, transform.getScale().y, 0); + corners[5] = transform.getPosition() + Vector(transform.getScale().x, 0, transform.getScale().z); + corners[6] = transform.getPosition() + Vector(0, transform.getScale().y, transform.getScale().z); + corners[7] = transform.getPosition() + transform.getScale(); + const Vector debugColor(1, 0, 0); + auto addEdge = [&](int a, int b) { + addDebugVertex(DebugVertex{.position = corners[a], .color = debugColor}); + addDebugVertex(DebugVertex{.position = corners[b], .color = debugColor}); + }; + + // Bottom face + addEdge(0, 1); + addEdge(1, 4); + addEdge(4, 2); + addEdge(2, 0); + + // Top face + addEdge(3, 5); + addEdge(5, 7); + addEdge(7, 6); + addEdge(6, 3); + + // Vertical edges + addEdge(0, 3); + addEdge(1, 5); + addEdge(2, 6); + addEdge(4, 7); + } \ No newline at end of file diff --git a/src/Engine/System/LightGather.cpp b/src/Engine/System/LightGather.cpp index 5d3326b..0c03550 100644 --- a/src/Engine/System/LightGather.cpp +++ b/src/Engine/System/LightGather.cpp @@ -1,4 +1,5 @@ #include "LightGather.h" +#include "Scene/LightEnvironment.h" using namespace Seele; using namespace Seele::System; diff --git a/src/Engine/Window/GameView.cpp b/src/Engine/Window/GameView.cpp index 64b4ec8..96c3246 100644 --- a/src/Engine/Window/GameView.cpp +++ b/src/Engine/Window/GameView.cpp @@ -17,6 +17,7 @@ #include "System/LightGather.h" #include "System/MeshUpdater.h" #include "Window/Window.h" +#include "Scene/LightEnvironment.h" using namespace Seele;