void CalcVelocity(Fluid fluid) { for (int i = 0, lim = fluid.Particles.Count; i < lim; i++) { Particle p = fluid.Particles[i]; float fx = p.x; float fy = p.y; int cx = Math.Min(GWidth - 3, Math.Max(0, (int)(fx - 0.5f))); int cy = Math.Min(GHeight - 3, Math.Max(0, (int)(fy - 0.5f))); // Add grid acceleration to the particle velocities CellWeight weight = fluid.Weights[i]; for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { GridCell cell = Grid[cy + y, cx + x]; float w = weight.w(x, y); p.vx += w * cell.ax; p.vy += w * cell.ay; } } p.vx += GravityX; p.vy += GravityY; // Check new position and push away from distance field boundaries float nx = fx + p.vx; float ny = fy + p.vy; float d = SDF.SampleDistance(nx, ny); if (d < 1f) { float dirx, diry; SDF.SampleGradient(nx, ny, out dirx, out diry); p.vx += (dirx) * (1f - d) * (float)(1f + rand.NextDouble() * 0.01f); p.vy += (diry) * (1f - d) * (float)(1f + rand.NextDouble() * 0.01f); } // Update fluid specific velocity grid for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { float w = weight.w(x, y); fluid.Grid[cy + y, cx + x].m += w; fluid.Grid[cy + y, cx + x].vx += (w * p.vx); fluid.Grid[cy + y, cx + x].vy += (w * p.vy); } } fluid.Particles[i] = p; } // Average out the fluid velocity grid for (int y = 0, ylim = GHeight; y < ylim; y++) { for (int x = 0, xlim = GWidth; x < xlim; x++) { //GridCell & cell = fluid.Grid[y, x]; float m = fluid.Grid[y, x].m; if (m == 0f) continue; fluid.Grid[y, x].vx /= m; fluid.Grid[y, x].vy /= m; } } }
public void UpdateParticles(Fluid fluid) { for (int i = 0, lim = fluid.Particles.Count; i < lim; i++) { Particle p = fluid.Particles[i]; int cx = Math.Min(GWidth - 3, Math.Max(0, (int)(p.x - 0.5f))); int cy = Math.Min(GHeight - 3, Math.Max(0, (int)(p.y - 0.5f))); // Get interpolated velocity CellWeight weight = fluid.Weights[i]; float vx = 0f, vy = 0f, tweight = 0f; for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { GridCell cell = fluid.Grid[cy + y, cx + x]; float w = weight.w(x, y); vx += w * cell.vx; vy += w * cell.vy; } } // Update particle position, velocity p.x += MathHelper.Clamp(vx, -1, 1); p.y += MathHelper.Clamp(vy, -1, 1); p.vx += GridCoeff * (vx - p.vx); p.vy += GridCoeff * (vy - p.vy); // Resolve collisions, clamp positions, update velocities based on this float x0 = p.x; float y0 = p.y; float d = SDF.SampleDistance(x0, y0); if (d < 0f) { float dx, dy; SDF.SampleGradient(x0, y0, out dx, out dy); x0 -= dx; y0 -= dy; } x0 = Math.Min(Math.Max(x0, 1f), GWidth - 2f); y0 = Math.Min(Math.Max(y0, 1f), GHeight - 2f); p.x = x0; p.y = y0; //F**K! fluid.Particles[i] = p; } }
void CalcAccel(Fluid fluid) { for (int i = 0, lim = fluid.Particles.Count; i < lim; i++) { float fx = fluid.Particles[i].x; float fy = fluid.Particles[i].y; int cx = Math.Min(GWidth - 3, Math.Max(0, (int)(fx - 0.5f))); int cy = Math.Min(GHeight - 3, Math.Max(0, (int)(fy - 0.5f))); CellWeight weight = fluid.Weights[i]; // Determine interpolated mass and velocity derivatives float dudx = 0f, dudy = 0f; float dvdx = 0f, dvdy = 0f; float mass = 10.0f; for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { float w = weight.w(x, y); float dx = weight.dx(x, y); float dy = weight.dy(x, y); GridCell cell = Grid[cy + y, cx + x]; dudx += cell.vx * dx; dudy += cell.vx * dy; dvdx += cell.vy * dx; dvdy += cell.vy * dy; mass += cell.m * w; } } float pressure = (fluid.Stiffness / Math.Max(1f, fluid.Density)) * (mass - fluid.Density); // Add a bit of a pushing force near the collision boundaries float ax = 0f, ay = 0f; float d = SDF.SampleDistance(fx, fy); if (d < 3f) { float dirx, diry; SDF.SampleGradient(fx, fy, out dirx, out diry); ax += dirx * (1f - (d / 3f)); ay += diry * (1f - (d / 3f)); } // Update grid acceleration values for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { float w = weight.w(x, y); float dx = weight.dx(x, y); float dy = weight.dy(x, y); Grid[cy + y, cx + x].ax += ax * w - dx * pressure - (dudx * dx + dudy * dy) * fluid.Viscosity * w; Grid[cy + y, cx + x].ay += ay * w - dy * pressure - (dvdx * dx + dvdy * dy) * fluid.Viscosity * w; } } } }
public Fluid InitGrid(Fluid fluid) { for (int i = 0, lim = fluid.Particles.Count; i < lim; i++) { Particle p = fluid.Particles[i]; int cx = Math.Min(GWidth - 3, Math.Max(0, (int)(p.x - 0.5f))); int cy = Math.Min(GHeight - 3, Math.Max(0, (int)(p.y - 0.5f))); float u = (float)cx - p.x; float v = (float)cy - p.y; // Biquadratic interpolation weights along each axis fluid.Weights[i].SetValues(u, v); for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { float w = fluid.Weights[i].w(x, y); Grid[cy + y, cx + x].m += w; Grid[cy + y, cx + x].vx += p.vx * w; Grid[cy + y, cx + x].vy += p.vy * w; } } } return fluid; }