11 grid.cellSize = std::max(
cellSize, 1e-4f);
12 const glm::vec3 extent = glm::vec3(sdf.
dims) * sdf.
cellSize;
13 const glm::vec3
cells = glm::ceil((extent + glm::vec3(2.f * grid.cellSize)) / grid.cellSize);
14 grid.origin = sdf.
origin - glm::vec3(grid.cellSize);
15 grid.dims = glm::max(glm::ivec3(1), glm::ivec3(
cells));
29 return glm::clamp(
c, glm::ivec3(0),
dims - glm::ivec3(1));
33 : params_(
params), maxParticles_(
std::
max(1, maxParticles)) {
34 particles_.resize(
size_t(maxParticles_));
35 densities_.resize(
size_t(maxParticles_));
36 lambdas_.resize(
size_t(maxParticles_));
37 gradSums_.resize(
size_t(maxParticles_));
38 cellNext_.resize(
size_t(maxParticles_));
44 cellHead_.assign(
size_t(grid_.
cellCount()), -1);
49 std::fill(cellHead_.begin(), cellHead_.end(), -1);
54 for (
int k = 0; k <
count && count_ < maxParticles_; ++k) {
56 const float z = (float((0x9E3779B9u * uint32_t(k + 1)) % 10000u) / 10000.f) * 2.f - 1.f;
57 const float a = 2.399963229728653f * float(k);
58 const float rr =
radius * std::sqrt(std::max(0.f, 1.f -
z *
z));
59 glm::vec3
p =
center + glm::vec3(std::cos(
a) * rr,
radius *
z, std::sin(
a) * rr);
64 const float nl = glm::length(
n);
68 n = glm::vec3(0.f, 1.f, 0.f);
73 densities_[size_t(count_)] = 0.f;
83 if (count_ <= 0 || sdf_.
voxelCount() <= 0)
return;
84 const int iters = std::max(1, substeps);
85 const float sub = dt / float(iters);
86 for (
int i = 0; i < iters; ++i) {
90 for (
int k = 0; k < pbf; ++k) {
92 computeDensitiesAndGrads();
94 applyPositionCorrections();
99void FluidSimulation::rebuildGrid() {
100 if (cellHead_.empty())
return;
101 std::fill(cellHead_.begin(), cellHead_.end(), -1);
102 for (
int i = 0; i < count_; ++i) {
103 const glm::ivec3
c = grid_.
cellOf(particles_[
size_t(i)].
pos);
105 cellNext_[size_t(i)] = cellHead_[size_t(
cell)];
106 cellHead_[size_t(
cell)] = i;
110void FluidSimulation::computeDensitiesAndGrads() {
112 const float h2 =
h *
h;
113 for (
int i = 0; i < count_; ++i) {
114 const glm::vec3 pi = particles_[size_t(i)].pos;
115 const glm::ivec3
c = grid_.
cellOf(pi);
117 glm::vec3 gradSum(0.f);
118 for (
int z = -1;
z <= 1; ++
z) {
119 for (
int y = -1;
y <= 1; ++
y) {
120 for (
int x = -1;
x <= 1; ++
x) {
121 const int cx =
c.x +
x;
122 const int cy =
c.y +
y;
123 const int cz =
c.z +
z;
126 for (
int j = cellHead_[
size_t(
cell)]; j >= 0; j = cellNext_[size_t(j)]) {
127 const glm::vec3
dx = pi - particles_[size_t(j)].pos;
128 const float r2 = glm::dot(
dx,
dx);
137 densities_[size_t(i)] = dens;
138 gradSums_[size_t(i)] = gradSum;
142void FluidSimulation::computeLambdas() {
143 const float rho0 = std::max(params_.
restDensity, 1e-6f);
144 for (
int i = 0; i < count_; ++i) {
145 const float rho = densities_[size_t(i)];
146 const float C = std::max(0.f, rho / rho0 - 1.f);
147 const float gradSq = glm::dot(gradSums_[
size_t(i)], gradSums_[
size_t(i)]);
148 lambdas_[size_t(i)] = -C / (gradSq + 1e-6f);
152void FluidSimulation::applyPositionCorrections() {
154 const float h2 =
h *
h;
155 const float rho0 = std::max(params_.
restDensity, 1e-6f);
157 for (
int i = 0; i < count_; ++i) {
158 const glm::vec3 pi = particles_[size_t(i)].pos;
159 const float li = lambdas_[size_t(i)];
160 const glm::ivec3
c = grid_.
cellOf(pi);
161 glm::vec3 delta(0.f);
162 for (
int z = -1;
z <= 1; ++
z) {
163 for (
int y = -1;
y <= 1; ++
y) {
164 for (
int x = -1;
x <= 1; ++
x) {
165 const int cx =
c.x +
x;
166 const int cy =
c.y +
y;
167 const int cz =
c.z +
z;
170 for (
int j = cellHead_[
size_t(
cell)]; j >= 0; j = cellNext_[size_t(j)]) {
171 if (j == i)
continue;
172 const glm::vec3
dx = pi - particles_[size_t(j)].pos;
173 const float r2 = glm::dot(
dx,
dx);
179 glm::vec3
p = pi + delta / rho0;
183 const float maxDelta = 0.0005f *
h;
184 const glm::vec3 correction = delta / rho0;
185 const float cl = glm::length(correction);
186 if (cl > maxDelta && cl > 1e-9f)
p = pi + correction * (maxDelta / cl);
190 const float nl = glm::length(
n);
194 n = glm::vec3(0.f, 1.f, 0.f);
197 particles_[size_t(i)].pos =
p;
201void FluidSimulation::integrate(
float dt) {
203 const float h2 =
h *
h;
205 for (
int i = 0; i < count_; ++i) {
206 glm::vec3
p = particles_[size_t(i)].pos;
207 glm::vec3
v = particles_[size_t(i)].vel;
211 glm::vec3 viscAcc(0.f);
212 glm::vec3 cohesionAcc(0.f);
213 float shearAcc = 0.f;
214 float neighborWeight = 0.f;
215 float cohesionWeight = 0.f;
216 const float particleVolume = std::pow(2.f *
radius, 3.f);
217 const glm::ivec3
c = grid_.
cellOf(
p);
218 for (
int z = -1;
z <= 1; ++
z) {
219 for (
int y = -1;
y <= 1; ++
y) {
220 for (
int x = -1;
x <= 1; ++
x) {
221 const int cx =
c.x +
x;
222 const int cy =
c.y +
y;
223 const int cz =
c.z +
z;
226 for (
int j = cellHead_[
size_t(
cell)]; j >= 0; j = cellNext_[size_t(j)]) {
227 if (j == i)
continue;
228 const glm::vec3
dx =
p - particles_[size_t(j)].pos;
229 const float r2 = glm::dot(
dx,
dx);
233 viscAcc += (particles_[size_t(j)].vel -
v) *
weight;
237 shearAcc += glm::length(particles_[
size_t(j)].vel -
v) *
weight;
249 float effectiveViscosity = params_.
viscosity;
251 if (neighborWeight > 1e-6f)
v += (viscAcc / neighborWeight) * std::clamp(effectiveViscosity * dt, 0.f, 1.f);
252 glm::vec3 cohesionNormal = sdf_.
gradient(
p);
253 if (glm::length(cohesionNormal) > 1e-6f)
254 cohesionNormal = glm::normalize(cohesionNormal);
256 cohesionNormal = glm::vec3(0.f, 1.f, 0.f);
257 cohesionAcc -= cohesionNormal * glm::dot(cohesionAcc, cohesionNormal);
258 if (cohesionWeight > 1e-6f)
263 v *= std::max(0.f, 1.f - params_.
damping * dt);
272 const float nl = glm::length(
n);
276 n = glm::vec3(0.f, 1.f, 0.f);
279 const float vn = glm::dot(
v,
n);
284 if (params_.
adhesion > 0.f &&
d <
h)
v += -
n * (params_.
adhesion * std::max(0.f, 1.f -
d /
h) * dt * 30.f);
286 particles_[size_t(i)].pos =
p;
287 particles_[size_t(i)].vel =
v;
std::array< PixelCell, kPixelChunkSize *kPixelChunkSize > cells
void clear()
Remove all particles.
FluidSimulation(int maxParticles, const FluidParams ¶ms=FluidParams{})
Fluid simulation.
void setSdf(const MeshSdf &sdf)
Replace the collision surface. Particles keep their state.
int spawnDrop(const glm::vec3 ¢er, float radius, int count)
Spawn a roughly spherical drop of particles near the surface.
const MeshSdf & sdf() const
Sdf.
void step(float dt)
Advance the simulation by dt seconds (params.iterations substeps).
Uniform signed-distance voxel field over a box.
glm::ivec3 dims
Voxel resolution along each axis.
float cellSize
World-space size of one voxel.
glm::vec3 gradient(const glm::vec3 &p) const
Analytic trilinear gradient (outward normal) at p.
glm::vec3 origin
World-space position of voxel (0,0,0).
int voxelCount() const
Voxel count.
float sample(const glm::vec3 &p) const
Trilinearly interpolated signed distance at p.
std::vector< ParamSpec > params
GLSL compute kernels for the GPU surface-flow solver.
float fluidPoly6(float r2, float h)
Poly6 kernel value W(r2, h).
glm::vec3 fluidClampSpeed(const glm::vec3 &v, float maxSpeed)
Clamp a vector's magnitude to maxSpeed.
glm::vec3 fluidSpikyGrad(const glm::vec3 &dx, float h)
Spiky gradient of the kernel w.r.t. particle i position.
Tuning knobs of one fluid simulation.
float damping
Linear air damping applied each substep.
float restDensity
Rest density; normalized to 1.
float supportRadius
SPH support radius h (kernel cutoff), typically 4x particleRadius.
float particleRadius
Resting particle radius in world units.
float adhesion
Fluid-surface adhesion strength (contact angle / sticking).
int pbfIterations
PBF density-constraint relaxation passes per substep.
float cohesion
Fluid-fluid cohesion strength (droplet formation).
int iterations
Solver substeps per call to step(dt).
float viscosity
XSPH viscosity strength (0 = inviscid).
float maxVelocity
Velocity clamp after integration.
float yieldStress
Bingham yield stress: below this shear rate particles "freeze".
glm::vec3 gravity
Gravity vector in world units / s^2.
One simulated particle (CPU reference layout).
Uniform grid used for neighbor queries.
bool inBounds(int x, int y, int z) const
In bounds.
static SimGrid make(const MeshSdf &sdf, float cellSize)
Build a grid covering the SDF domain plus one cell of padding.
float cellSize
Cell size in world units (== SPH support radius).
int cellCount() const
Cell count.
glm::vec3 origin
World position of cell (0,0,0).
glm::ivec3 cellOf(const glm::vec3 &p) const
Cell of.
int cellIndex(int x, int y, int z) const
Cell index.
glm::ivec3 dims
Cell counts per axis.