So many changes again

This commit is contained in:
2026-04-15 16:56:19 +02:00
parent 495e683522
commit 424dea0012
30 changed files with 566 additions and 235 deletions
+35
View File
@@ -0,0 +1,35 @@
import FluidGridData;
struct Params
{
// read-write: phi to add source to
FluidGridData<float> phi;
FluidGridData<float> density;
};
ParameterBlock<Params> params;
[shader("compute")]
[numthreads(32, 8, 1)]
void addSource(uint3 dispatchThreadID : SV_DispatchThreadID)
{
FluidGridData<float> phi = params.phi;
FluidGridData<float> 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);
}
}
+7 -7
View File
@@ -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);
+31
View File
@@ -0,0 +1,31 @@
import FluidGridData;
struct Params
{
FluidGridData<float> velocityX;
FluidGridData<float> velocityY;
FluidGridData<float> velocityZ;
float3 force;
float dt;
};
ParameterBlock<Params> params;
[shader("compute")]
[numthreads(32, 8, 1)]
void applyForces(uint3 dispatchThreadID : SV_DispatchThreadID)
{
FluidGridData<float> velocityX = params.velocityX;
FluidGridData<float> velocityY = params.velocityY;
FluidGridData<float> 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;
}
+5 -6
View File
@@ -1,7 +1,6 @@
import FluidGridData;
struct Params
{
struct Params {
// read-only
FluidGridData<float> velocityX;
// read-only
@@ -17,8 +16,7 @@ ParameterBlock<Params> params;
[shader("compute")]
[numthreads(32, 8, 1)]
void computeDivergence(uint3 dispatchThreadID : SV_DispatchThreadID)
{
void computeDivergence(uint3 dispatchThreadID: SV_DispatchThreadID) {
FluidGridData<float> velocityX = params.velocityX;
FluidGridData<float> velocityY = params.velocityY;
FluidGridData<float> 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;
}
+2 -2
View File
@@ -13,7 +13,7 @@ ParameterBlock<Parameter> 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<float> 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),
+9 -5
View File
@@ -1,15 +1,19 @@
static const uint3 gridSize = uint3(256, 256, 256);
struct GridParams
{
uint3 gridSize;
};
ParameterBlock<GridParams> gridParams;
struct FluidGridData<T>
{
RWStructuredBuffer<T> 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; }
}
};
+1 -1
View File
@@ -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;
}
+3 -3
View File
@@ -12,7 +12,7 @@ struct Params
ParameterBlock<Params> 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;
+4 -4
View File
@@ -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]);
}
+4 -1
View File
@@ -5,6 +5,7 @@ import MaterialParameter;
struct Params
{
float4x4 transform;
StructuredBuffer<float> vertexBuffer;
StructuredBuffer<float> normalBuffer;
StructuredBuffer<uint> 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;
+51 -29
View File
@@ -14,18 +14,29 @@ void setBound(uint3 dispatchThreadID : SV_DispatchThreadID)
{
int b = params.b;
FluidGridData<float> grid = params.grid;
int x = dispatchThreadID.x + 1;
int y = dispatchThreadID.y + 1;
if(x >= gridSize.x - 1 || y >= gridSize.y - 1) return;
int i = dispatchThreadID.x + 1;
int j = dispatchThreadID.y + 1;
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];
// 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];
}
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];
// 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];
}
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];
// 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<float> 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]);
// 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]);
}
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]);
// 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<float> 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]);
}
+1 -1
View File
@@ -25,7 +25,7 @@ void reinitialize(uint3 dispatchThreadID : SV_DispatchThreadID)
uint z = dispatchThreadID.z + 1;
FluidGridData<float> phi = params.phi;
FluidGridData<float> 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;
+6 -3
View File
@@ -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
@@ -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 =
{
@@ -8,6 +8,7 @@
#include "Math/Vector.h"
#include "RenderGraph.h"
#include "Scene/Scene.h"
#include "Scene/LightEnvironment.h"
using namespace Seele;
@@ -6,6 +6,7 @@
#include "Graphics/StaticMeshVertexData.h"
#include "Material/Material.h"
#include "Material/MaterialInstance.h"
#include "Scene/LightEnvironment.h"
#include "RenderPass.h"
#include <iostream>
@@ -4,6 +4,7 @@
#include "Graphics/Initializer.h"
#include "Graphics/Shader.h"
#include "Math/Matrix.h"
#include "Scene/LightEnvironment.h"
#include <glm/ext/matrix_transform.hpp>
#include <glm/matrix.hpp>
@@ -7,13 +7,18 @@
#include "Graphics/Initializer.h"
#include "Graphics/Shader.h"
#include "Math/Vector.h"
#include <iostream>
#include "Scene/FluidScene.h"
#include <algorithm>
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));
@@ -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
@@ -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));
@@ -20,6 +20,7 @@ private:
Gfx::OPipelineLayout pipelineLayout;
Gfx::ODescriptorLayout descriptorLayout;
Gfx::ODescriptorLayout gridSizeLayout;
Gfx::ODescriptorSet descriptorSet;
Gfx::OComputeShader computeShader;
Gfx::PComputePipeline pipeline;
+1
View File
@@ -848,6 +848,7 @@ void Graphics::pickPhysicalDevice() {
vkGetPhysicalDeviceProperties2(physicalDevice, &props.get());
features.get<VkPhysicalDeviceFeatures2>().features = {
// .robustBufferAccess = true,
.geometryShader = true,
.sampleRateShading = true,
.fillModeNonSolid = true,
+54 -95
View File
@@ -1,6 +1,8 @@
#include "slang-compile.h"
#include "Containers/Array.h"
#include "Graphics/Descriptor.h"
#include <array>
#include <cstdlib>
#include <filesystem>
#include <fmt/core.h>
#include <iostream>
@@ -29,113 +31,54 @@ thread_local Slang::ComPtr<slang::ISession> session;
thread_local Array<std::string> 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<std::filesystem::path> 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<std::filesystem::path, 7> 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<std::filesystem::path> 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 =
+112 -40
View File
@@ -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<float> initialDensity(bufferSize / sizeof(float));
Array<float> initialVelocityX(bufferSize / sizeof(float));
Array<float> initialVelocityY(bufferSize / sizeof(float));
Array<float> initialVelocityZ(bufferSize / sizeof(float));
Array<float> initialPhi(bufferSize / sizeof(float));
// Array<float> initialDensity(bufferSize / sizeof(float));
// Array<float> initialVelocityX(bufferSize / sizeof(float));
// Array<float> initialVelocityY(bufferSize / sizeof(float));
// Array<float> initialVelocityZ(bufferSize / sizeof(float));
// Array<float> initialPhi(bufferSize / sizeof(float));
// Initialize level set to positive (outside) everywhere
float halfGridX = static_cast<float>(grid.gridSize.x) * 0.5f;
float halfGridY = static_cast<float>(grid.gridSize.y) * 0.5f;
float radius = static_cast<float>(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<float>(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<float>(x) - cx;
float dy = static_cast<float>(y) - cy;
float dz = static_cast<float>(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<float>(grid.gridSize.x) * 0.5f;
// float halfGridY = static_cast<float>(grid.gridSize.y) * 0.5f;
// float radius = static_cast<float>(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<float>(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<float>(x) - cx;
// float dy = static_cast<float>(y) - cy;
// float dz = static_cast<float>(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;
}
}
+13 -1
View File
@@ -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<uint32, FluidData>& 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<uint32, FluidData> fluidDataMap;
};
+2
View File
@@ -1,5 +1,7 @@
#include "Scene.h"
#include "Graphics/Graphics.h"
#include "LightEnvironment.h"
#include "FluidScene.h"
using namespace Seele;
+2 -2
View File
@@ -1,14 +1,14 @@
#pragma once
#include "MinimalEngine.h"
#include "Graphics/Graphics.h"
#include "LightEnvironment.h"
#include "Physics/PhysicsSystem.h"
#include "FluidScene.h"
#include <entt/entt.hpp>
namespace Seele {
DECLARE_REF(Material)
DECLARE_REF(Entity)
DECLARE_REF(FluidScene)
DECLARE_REF(LightEnvironment)
class Scene {
public:
Scene(Gfx::PGraphics graphics);
+35
View File
@@ -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);
}
+1
View File
@@ -1,4 +1,5 @@
#include "LightGather.h"
#include "Scene/LightEnvironment.h"
using namespace Seele;
using namespace Seele::System;
+1
View File
@@ -17,6 +17,7 @@
#include "System/LightGather.h"
#include "System/MeshUpdater.h"
#include "Window/Window.h"
#include "Scene/LightEnvironment.h"
using namespace Seele;