some C# cause why not. C# guy might like this? idk. some python too. mostly simulations. WindowsAPI C# is good i guess
This commit is contained in:
50
simulations/fluids/fluidsC#/Camera.cs
Normal file
50
simulations/fluids/fluidsC#/Camera.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace fluidsC_;
|
||||
|
||||
public class Camera
|
||||
{
|
||||
public Vector3 Position;
|
||||
public float AspectRatio;
|
||||
|
||||
public Vector3 Front = -Vector3.UnitZ;
|
||||
public Vector3 Up = Vector3.UnitY;
|
||||
public Vector3 Right = Vector3.UnitX;
|
||||
|
||||
public float Pitch;
|
||||
public float Yaw = -MathHelper.PiOver2;
|
||||
|
||||
public float Speed = 2.5f;
|
||||
public float Sensitivity = 0.002f;
|
||||
|
||||
public Camera(Vector3 position, float aspectRatio)
|
||||
{
|
||||
Position = position;
|
||||
AspectRatio = aspectRatio;
|
||||
UpdateVectors();
|
||||
}
|
||||
|
||||
public Matrix4 GetViewMatrix()
|
||||
{
|
||||
return Matrix4.LookAt(Position, Position + Front, Up);
|
||||
}
|
||||
|
||||
public Matrix4 GetProjectionMatrix()
|
||||
{
|
||||
return Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, AspectRatio, 0.01f, 100.0f);
|
||||
}
|
||||
|
||||
public void UpdateVectors()
|
||||
{
|
||||
// Calculate the new Front vector
|
||||
Front.X = MathF.Cos(Yaw) * MathF.Cos(Pitch);
|
||||
Front.Y = MathF.Sin(Pitch);
|
||||
Front.Z = MathF.Sin(Yaw) * MathF.Cos(Pitch);
|
||||
|
||||
Front = Vector3.Normalize(Front);
|
||||
|
||||
// Also re-calculate the Right and Up vector
|
||||
Right = Vector3.Normalize(Vector3.Cross(Front, Vector3.UnitY));
|
||||
Up = Vector3.Normalize(Vector3.Cross(Right, Front));
|
||||
}
|
||||
}
|
||||
188
simulations/fluids/fluidsC#/FluidSimulation.cs
Normal file
188
simulations/fluids/fluidsC#/FluidSimulation.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace fluidsC_;
|
||||
|
||||
public class FluidSimulation
|
||||
{
|
||||
private readonly int _sizeX;
|
||||
private readonly int _sizeY;
|
||||
|
||||
// Fluid fields
|
||||
private float[,] _density;
|
||||
private float[,] _densityOld;
|
||||
|
||||
// Velocity fields
|
||||
private float[,] _vx;
|
||||
private float[,] _vy;
|
||||
private float[,] _vxOld;
|
||||
private float[,] _vyOld;
|
||||
|
||||
public FluidSimulation(int sizeX, int sizeY)
|
||||
{
|
||||
_sizeX = sizeX;
|
||||
_sizeY = sizeY;
|
||||
|
||||
// Initialize arrays in constructor to fix nullable warnings
|
||||
_density = new float[_sizeX, _sizeY];
|
||||
_densityOld = new float[_sizeX, _sizeY];
|
||||
_vx = new float[_sizeX, _sizeY];
|
||||
_vy = new float[_sizeX, _sizeY];
|
||||
_vxOld = new float[_sizeX, _sizeY];
|
||||
_vyOld = new float[_sizeX, _sizeY];
|
||||
}
|
||||
|
||||
public void AddDensity(int x, int y, float amount)
|
||||
{
|
||||
if (x >= 0 && x < _sizeX && y >= 0 && y < _sizeY)
|
||||
{
|
||||
_density[x, y] += amount;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVelocity(int x, int y, float amountX, float amountY)
|
||||
{
|
||||
if (x >= 0 && x < _sizeX && y >= 0 && y < _sizeY)
|
||||
{
|
||||
_vx[x, y] += amountX;
|
||||
_vy[x, y] += amountY;
|
||||
}
|
||||
}
|
||||
|
||||
public void Step(float dt, float diffusion, float viscosity)
|
||||
{
|
||||
Diffuse(1, _vxOld, _vx, viscosity, dt);
|
||||
Diffuse(2, _vyOld, _vy, viscosity, dt);
|
||||
|
||||
Project(_vxOld, _vyOld, _vx, _vy);
|
||||
|
||||
Advect(1, _vx, _vxOld, _vxOld, _vyOld, dt);
|
||||
Advect(2, _vy, _vyOld, _vxOld, _vyOld, dt);
|
||||
|
||||
Project(_vx, _vy, _vxOld, _vyOld);
|
||||
|
||||
Diffuse(0, _densityOld, _density, diffusion, dt);
|
||||
Advect(0, _density, _densityOld, _vx, _vy, dt);
|
||||
}
|
||||
|
||||
private void Diffuse(int b, float[,] x, float[,] x0, float diff, float dt)
|
||||
{
|
||||
float a = dt * diff * (_sizeX - 2) * (_sizeY - 2);
|
||||
LinearSolve(b, x, x0, a, 1 + 4 * a);
|
||||
}
|
||||
|
||||
private void LinearSolve(int b, float[,] x, float[,] x0, float a, float c)
|
||||
{
|
||||
for (int k = 0; k < 20; k++)
|
||||
{
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
x[i, j] = (x0[i, j] + a * (x[i - 1, j] + x[i + 1, j] + x[i, j - 1] + x[i, j + 1])) / c;
|
||||
}
|
||||
}
|
||||
SetBoundary(b, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void Project(float[,] velocX, float[,] velocY, float[,] p, float[,] div)
|
||||
{
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
div[i, j] = -0.5f * (
|
||||
velocX[i + 1, j] - velocX[i - 1, j] +
|
||||
velocY[i, j + 1] - velocY[i, j - 1]
|
||||
) / _sizeX;
|
||||
p[i, j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SetBoundary(0, div);
|
||||
SetBoundary(0, p);
|
||||
LinearSolve(0, p, div, 1, 4);
|
||||
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
velocX[i, j] -= 0.5f * (p[i + 1, j] - p[i - 1, j]) * _sizeX;
|
||||
velocY[i, j] -= 0.5f * (p[i, j + 1] - p[i, j - 1]) * _sizeY;
|
||||
}
|
||||
}
|
||||
|
||||
SetBoundary(1, velocX);
|
||||
SetBoundary(2, velocY);
|
||||
}
|
||||
|
||||
private void Advect(int b, float[,] d, float[,] d0, float[,] velocX, float[,] velocY, float dt)
|
||||
{
|
||||
float dt0 = dt * (_sizeX - 2);
|
||||
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
float x = i - dt0 * velocX[i, j];
|
||||
float y = j - dt0 * velocY[i, j];
|
||||
|
||||
x = Math.Clamp(x, 0.5f, _sizeX - 1.5f);
|
||||
y = Math.Clamp(y, 0.5f, _sizeY - 1.5f);
|
||||
|
||||
int i0 = (int)Math.Floor(x);
|
||||
int i1 = i0 + 1;
|
||||
int j0 = (int)Math.Floor(y);
|
||||
int j1 = j0 + 1;
|
||||
|
||||
float s1 = x - i0;
|
||||
float s0 = 1 - s1;
|
||||
float t1 = y - j0;
|
||||
float t0 = 1 - t1;
|
||||
|
||||
d[i, j] = s0 * (t0 * d0[i0, j0] + t1 * d0[i0, j1]) +
|
||||
s1 * (t0 * d0[i1, j0] + t1 * d0[i1, j1]);
|
||||
}
|
||||
}
|
||||
|
||||
SetBoundary(b, d);
|
||||
}
|
||||
|
||||
private void SetBoundary(int b, float[,] x)
|
||||
{
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
x[i, 0] = b == 2 ? -x[i, 1] : x[i, 1];
|
||||
x[i, _sizeY - 1] = b == 2 ? -x[i, _sizeY - 2] : x[i, _sizeY - 2];
|
||||
}
|
||||
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
x[0, j] = b == 1 ? -x[1, j] : x[1, j];
|
||||
x[_sizeX - 1, j] = b == 1 ? -x[_sizeX - 2, j] : x[_sizeX - 2, j];
|
||||
}
|
||||
|
||||
x[0, 0] = 0.5f * (x[1, 0] + x[0, 1]);
|
||||
x[0, _sizeY - 1] = 0.5f * (x[1, _sizeY - 1] + x[0, _sizeY - 2]);
|
||||
x[_sizeX - 1, 0] = 0.5f * (x[_sizeX - 2, 0] + x[_sizeX - 1, 1]);
|
||||
x[_sizeX - 1, _sizeY - 1] = 0.5f * (x[_sizeX - 2, _sizeY - 1] + x[_sizeX - 1, _sizeY - 2]);
|
||||
}
|
||||
|
||||
public float GetDensity(int x, int y)
|
||||
{
|
||||
if (x >= 0 && x < _sizeX && y >= 0 && y < _sizeY)
|
||||
return _density[x, y];
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void FadeDensity(float amount)
|
||||
{
|
||||
for (int i = 0; i < _sizeX; i++)
|
||||
{
|
||||
for (int j = 0; j < _sizeY; j++)
|
||||
{
|
||||
_density[i, j] = Math.Max(0, _density[i, j] - amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
313
simulations/fluids/fluidsC#/FluidSimulation3D.cs
Normal file
313
simulations/fluids/fluidsC#/FluidSimulation3D.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace fluidsC_;
|
||||
|
||||
public class FluidSimulation3D
|
||||
{
|
||||
private readonly int _sizeX;
|
||||
private readonly int _sizeY;
|
||||
private readonly int _sizeZ;
|
||||
|
||||
// Fluid fields
|
||||
private float[,,] _density;
|
||||
private float[,,] _densityOld;
|
||||
|
||||
// Velocity fields
|
||||
private float[,,] _vx;
|
||||
private float[,,] _vy;
|
||||
private float[,,] _vz;
|
||||
private float[,,] _vxOld;
|
||||
private float[,,] _vyOld;
|
||||
private float[,,] _vzOld;
|
||||
|
||||
// Obstacle field
|
||||
private bool[,,] _obstacles;
|
||||
|
||||
public FluidSimulation3D(int sizeX, int sizeY, int sizeZ)
|
||||
{
|
||||
_sizeX = sizeX;
|
||||
_sizeY = sizeY;
|
||||
_sizeZ = sizeZ;
|
||||
|
||||
// Initialize arrays in constructor
|
||||
_density = new float[_sizeX, _sizeY, _sizeZ];
|
||||
_densityOld = new float[_sizeX, _sizeY, _sizeZ];
|
||||
_vx = new float[_sizeX, _sizeY, _sizeZ];
|
||||
_vy = new float[_sizeX, _sizeY, _sizeZ];
|
||||
_vz = new float[_sizeX, _sizeY, _sizeZ];
|
||||
_vxOld = new float[_sizeX, _sizeY, _sizeZ];
|
||||
_vyOld = new float[_sizeX, _sizeY, _sizeZ];
|
||||
_vzOld = new float[_sizeX, _sizeY, _sizeZ];
|
||||
_obstacles = new bool[_sizeX, _sizeY, _sizeZ];
|
||||
|
||||
CreateDefaultObstacles();
|
||||
}
|
||||
|
||||
private void CreateDefaultObstacles()
|
||||
{
|
||||
// Create some default obstacles
|
||||
int centerX = _sizeX / 2;
|
||||
int centerZ = _sizeZ / 2;
|
||||
|
||||
// Create a sphere obstacle in the center
|
||||
int sphereRadius = Math.Min(_sizeX, Math.Min(_sizeY, _sizeZ)) / 6;
|
||||
for (int x = 0; x < _sizeX; x++)
|
||||
{
|
||||
for (int y = 0; y < _sizeY; y++)
|
||||
{
|
||||
for (int z = 0; z < _sizeZ; z++)
|
||||
{
|
||||
float dx = x - centerX;
|
||||
float dy = y - _sizeY / 3;
|
||||
float dz = z - centerZ;
|
||||
float dist = MathF.Sqrt(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (dist <= sphereRadius)
|
||||
{
|
||||
_obstacles[x, y, z] = true;
|
||||
}
|
||||
|
||||
// Create walls
|
||||
if (x == 0 || x == _sizeX - 1 || z == 0 || z == _sizeZ - 1)
|
||||
{
|
||||
_obstacles[x, y, z] = true;
|
||||
}
|
||||
|
||||
// Create floor
|
||||
if (y == 0)
|
||||
{
|
||||
_obstacles[x, y, z] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddDensity(int x, int y, int z, float amount)
|
||||
{
|
||||
if (IsInBounds(x, y, z) && !_obstacles[x, y, z])
|
||||
{
|
||||
_density[x, y, z] += amount;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVelocity(int x, int y, int z, float amountX, float amountY, float amountZ)
|
||||
{
|
||||
if (IsInBounds(x, y, z) && !_obstacles[x, y, z])
|
||||
{
|
||||
_vx[x, y, z] += amountX;
|
||||
_vy[x, y, z] += amountY;
|
||||
_vz[x, y, z] += amountZ;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddObstacle(int x, int y, int z, bool obstacle)
|
||||
{
|
||||
if (IsInBounds(x, y, z))
|
||||
{
|
||||
_obstacles[x, y, z] = obstacle;
|
||||
}
|
||||
}
|
||||
|
||||
public void Step(float dt, float diffusion, float viscosity)
|
||||
{
|
||||
Diffuse(1, _vxOld, _vx, viscosity, dt);
|
||||
Diffuse(2, _vyOld, _vy, viscosity, dt);
|
||||
Diffuse(3, _vzOld, _vz, viscosity, dt);
|
||||
|
||||
// Fixed Project3D calls - use correct parameters
|
||||
Project3D(_vxOld, _vyOld, _vzOld, _vxOld, _vyOld, _vzOld);
|
||||
|
||||
Advect(1, _vx, _vxOld, _vxOld, _vyOld, _vzOld, dt);
|
||||
Advect(2, _vy, _vyOld, _vxOld, _vyOld, _vzOld, dt);
|
||||
Advect(3, _vz, _vzOld, _vxOld, _vyOld, _vzOld, dt);
|
||||
|
||||
// Fixed Project3D calls - use correct parameters
|
||||
Project3D(_vx, _vy, _vz, _vx, _vy, _vz);
|
||||
|
||||
Diffuse(0, _densityOld, _density, diffusion, dt);
|
||||
Advect(0, _density, _densityOld, _vx, _vy, _vz, dt);
|
||||
}
|
||||
|
||||
private void Diffuse(int b, float[,,] x, float[,,] x0, float diff, float dt)
|
||||
{
|
||||
float a = dt * diff * (_sizeX - 2) * (_sizeY - 2) * (_sizeZ - 2);
|
||||
LinearSolve(b, x, x0, a, 1 + 6 * a);
|
||||
}
|
||||
|
||||
private void LinearSolve(int b, float[,,] x, float[,,] x0, float a, float c)
|
||||
{
|
||||
for (int k = 0; k < 20; k++)
|
||||
{
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
for (int l = 1; l < _sizeZ - 1; l++)
|
||||
{
|
||||
if (!_obstacles[i, j, l])
|
||||
{
|
||||
x[i, j, l] = (x0[i, j, l] + a * (
|
||||
x[i - 1, j, l] + x[i + 1, j, l] +
|
||||
x[i, j - 1, l] + x[i, j + 1, l] +
|
||||
x[i, j, l - 1] + x[i, j, l + 1])) / c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SetBoundary(b, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void Project3D(float[,,] velocX, float[,,] velocY, float[,,] velocZ, float[,,] p, float[,,] div, float[,,] temp)
|
||||
{
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
for (int l = 1; l < _sizeZ - 1; l++)
|
||||
{
|
||||
if (!_obstacles[i, j, l])
|
||||
{
|
||||
div[i, j, l] = -0.5f * (
|
||||
velocX[i + 1, j, l] - velocX[i - 1, j, l] +
|
||||
velocY[i, j + 1, l] - velocY[i, j - 1, l] +
|
||||
velocZ[i, j, l + 1] - velocZ[i, j, l - 1]
|
||||
) / _sizeX;
|
||||
p[i, j, l] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetBoundary(0, div);
|
||||
SetBoundary(0, p);
|
||||
LinearSolve(0, p, div, 1, 6);
|
||||
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
for (int l = 1; l < _sizeZ - 1; l++)
|
||||
{
|
||||
if (!_obstacles[i, j, l])
|
||||
{
|
||||
velocX[i, j, l] -= 0.5f * (p[i + 1, j, l] - p[i - 1, j, l]) * _sizeX;
|
||||
velocY[i, j, l] -= 0.5f * (p[i, j + 1, l] - p[i, j - 1, l]) * _sizeY;
|
||||
velocZ[i, j, l] -= 0.5f * (p[i, j, l + 1] - p[i, j, l - 1]) * _sizeZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetBoundary(1, velocX);
|
||||
SetBoundary(2, velocY);
|
||||
SetBoundary(3, velocZ);
|
||||
}
|
||||
|
||||
private void Advect(int b, float[,,] d, float[,,] d0, float[,,] velocX, float[,,] velocY, float[,,] velocZ, float dt)
|
||||
{
|
||||
float dt0 = dt * (_sizeX - 2);
|
||||
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
for (int l = 1; l < _sizeZ - 1; l++)
|
||||
{
|
||||
if (!_obstacles[i, j, l])
|
||||
{
|
||||
float x = i - dt0 * velocX[i, j, l];
|
||||
float y = j - dt0 * velocY[i, j, l];
|
||||
float z = l - dt0 * velocZ[i, j, l];
|
||||
|
||||
x = Math.Clamp(x, 0.5f, _sizeX - 1.5f);
|
||||
y = Math.Clamp(y, 0.5f, _sizeY - 1.5f);
|
||||
z = Math.Clamp(z, 0.5f, _sizeZ - 1.5f);
|
||||
|
||||
int i0 = (int)Math.Floor(x);
|
||||
int i1 = i0 + 1;
|
||||
int j0 = (int)Math.Floor(y);
|
||||
int j1 = j0 + 1;
|
||||
int l0 = (int)Math.Floor(z);
|
||||
int l1 = l0 + 1;
|
||||
|
||||
float s1 = x - i0;
|
||||
float s0 = 1 - s1;
|
||||
float t1 = y - j0;
|
||||
float t0 = 1 - t1;
|
||||
float u1 = z - l0;
|
||||
float u0 = 1 - u1;
|
||||
|
||||
d[i, j, l] =
|
||||
s0 * (t0 * (u0 * d0[i0, j0, l0] + u1 * d0[i0, j0, l1]) +
|
||||
t1 * (u0 * d0[i0, j1, l0] + u1 * d0[i0, j1, l1])) +
|
||||
s1 * (t0 * (u0 * d0[i1, j0, l0] + u1 * d0[i1, j0, l1]) +
|
||||
t1 * (u0 * d0[i1, j1, l0] + u1 * d0[i1, j1, l1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetBoundary(b, d);
|
||||
}
|
||||
|
||||
private void SetBoundary(int b, float[,,] x)
|
||||
{
|
||||
// Set boundary conditions with obstacles
|
||||
for (int i = 1; i < _sizeX - 1; i++)
|
||||
{
|
||||
for (int j = 1; j < _sizeY - 1; j++)
|
||||
{
|
||||
for (int l = 1; l < _sizeZ - 1; l++)
|
||||
{
|
||||
if (_obstacles[i, j, l])
|
||||
{
|
||||
// Reflect velocity at obstacles
|
||||
if (_obstacles[i - 1, j, l]) x[i, j, l] = b == 1 ? -x[i + 1, j, l] : x[i + 1, j, l];
|
||||
if (_obstacles[i + 1, j, l]) x[i, j, l] = b == 1 ? -x[i - 1, j, l] : x[i - 1, j, l];
|
||||
if (_obstacles[i, j - 1, l]) x[i, j, l] = b == 2 ? -x[i, j + 1, l] : x[i, j + 1, l];
|
||||
if (_obstacles[i, j + 1, l]) x[i, j, l] = b == 2 ? -x[i, j - 1, l] : x[i, j - 1, l];
|
||||
if (_obstacles[i, j, l - 1]) x[i, j, l] = b == 3 ? -x[i, j, l + 1] : x[i, j, l + 1];
|
||||
if (_obstacles[i, j, l + 1]) x[i, j, l] = b == 3 ? -x[i, j, l - 1] : x[i, j, l - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsInBounds(int x, int y, int z)
|
||||
{
|
||||
return x >= 0 && x < _sizeX && y >= 0 && y < _sizeY && z >= 0 && z < _sizeZ;
|
||||
}
|
||||
|
||||
public float GetDensity(int x, int y, int z)
|
||||
{
|
||||
if (IsInBounds(x, y, z))
|
||||
return _density[x, y, z];
|
||||
return 0;
|
||||
}
|
||||
|
||||
public bool GetObstacle(int x, int y, int z)
|
||||
{
|
||||
if (IsInBounds(x, y, z))
|
||||
return _obstacles[x, y, z];
|
||||
return false;
|
||||
}
|
||||
|
||||
public void FadeDensity(float amount)
|
||||
{
|
||||
for (int i = 0; i < _sizeX; i++)
|
||||
{
|
||||
for (int j = 0; j < _sizeY; j++)
|
||||
{
|
||||
for (int l = 0; l < _sizeZ; l++)
|
||||
{
|
||||
_density[i, j, l] = Math.Max(0, _density[i, j, l] - amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public (int X, int Y, int Z) GetSize() => (_sizeX, _sizeY, _sizeZ);
|
||||
}
|
||||
276
simulations/fluids/fluidsC#/FluidWindow.cs
Normal file
276
simulations/fluids/fluidsC#/FluidWindow.cs
Normal file
@@ -0,0 +1,276 @@
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using OpenTK.Windowing.Common;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
|
||||
namespace fluidsC_;
|
||||
|
||||
public class FluidWindow : GameWindow
|
||||
{
|
||||
private FluidSimulation _fluid;
|
||||
private int _vertexArrayObject;
|
||||
private int _vertexBufferObject;
|
||||
private int _elementBufferObject;
|
||||
private int _shaderProgram;
|
||||
private int _texture;
|
||||
|
||||
private readonly int _fluidSizeX = 128;
|
||||
private readonly int _fluidSizeY = 128;
|
||||
private float[] _pixels;
|
||||
|
||||
private bool _mouseDown = false;
|
||||
private Vector2 _lastMousePos;
|
||||
private MouseButton _activeButton;
|
||||
|
||||
public FluidWindow(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
|
||||
: base(gameWindowSettings, nativeWindowSettings)
|
||||
{
|
||||
_fluid = new FluidSimulation(_fluidSizeX, _fluidSizeY);
|
||||
_pixels = new float[_fluidSizeX * _fluidSizeY * 4];
|
||||
}
|
||||
|
||||
protected override void OnLoad()
|
||||
{
|
||||
base.OnLoad();
|
||||
|
||||
GL.ClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
|
||||
SetupShaders();
|
||||
SetupTexture();
|
||||
SetupQuad();
|
||||
|
||||
Console.WriteLine("Fluid Simulation Controls:");
|
||||
Console.WriteLine("<22> Click and drag to add fluid");
|
||||
Console.WriteLine("<22> Right click to add velocity");
|
||||
Console.WriteLine("<22> R key to reset simulation");
|
||||
Console.WriteLine("<22> ESC to exit");
|
||||
}
|
||||
|
||||
private void SetupShaders()
|
||||
{
|
||||
const string vertexShaderSource = @"
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 aPosition;
|
||||
layout (location = 1) in vec2 aTexCoord;
|
||||
|
||||
out vec2 texCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(aPosition, 0.0, 1.0);
|
||||
texCoord = aTexCoord;
|
||||
}";
|
||||
|
||||
const string fragmentShaderSource = @"
|
||||
#version 330 core
|
||||
in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D fluidTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color = texture(fluidTexture, texCoord);
|
||||
// Create a nice blue-to-cyan color gradient based on density
|
||||
fragColor = vec4(color.r * 0.2, color.r * 0.5, color.r, 1.0);
|
||||
}";
|
||||
|
||||
var vertexShader = GL.CreateShader(ShaderType.VertexShader);
|
||||
GL.ShaderSource(vertexShader, vertexShaderSource);
|
||||
GL.CompileShader(vertexShader);
|
||||
|
||||
var fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
|
||||
GL.ShaderSource(fragmentShader, fragmentShaderSource);
|
||||
GL.CompileShader(fragmentShader);
|
||||
|
||||
_shaderProgram = GL.CreateProgram();
|
||||
GL.AttachShader(_shaderProgram, vertexShader);
|
||||
GL.AttachShader(_shaderProgram, fragmentShader);
|
||||
GL.LinkProgram(_shaderProgram);
|
||||
|
||||
GL.DeleteShader(vertexShader);
|
||||
GL.DeleteShader(fragmentShader);
|
||||
}
|
||||
|
||||
private void SetupTexture()
|
||||
{
|
||||
_texture = GL.GenTexture();
|
||||
GL.BindTexture(TextureTarget.Texture2D, _texture);
|
||||
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
|
||||
|
||||
// Initialize with empty data
|
||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32f,
|
||||
_fluidSizeX, _fluidSizeY, 0, PixelFormat.Rgba, PixelType.Float, IntPtr.Zero);
|
||||
}
|
||||
|
||||
private void SetupQuad()
|
||||
{
|
||||
float[] vertices = {
|
||||
// positions // texture coords
|
||||
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
||||
1.0f, -1.0f, 1.0f, 0.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
|
||||
uint[] indices = {
|
||||
0, 1, 2,
|
||||
2, 3, 0
|
||||
};
|
||||
|
||||
_vertexArrayObject = GL.GenVertexArray();
|
||||
_vertexBufferObject = GL.GenBuffer();
|
||||
_elementBufferObject = GL.GenBuffer();
|
||||
|
||||
GL.BindVertexArray(_vertexArrayObject);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(uint), indices, BufferUsageHint.StaticDraw);
|
||||
|
||||
// Position attribute
|
||||
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
|
||||
// Texture coordinate attribute
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 2 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
}
|
||||
|
||||
protected override void OnRenderFrame(FrameEventArgs e)
|
||||
{
|
||||
base.OnRenderFrame(e);
|
||||
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
|
||||
UpdateTextureFromFluid();
|
||||
|
||||
GL.UseProgram(_shaderProgram);
|
||||
GL.BindTexture(TextureTarget.Texture2D, _texture);
|
||||
GL.BindVertexArray(_vertexArrayObject);
|
||||
GL.DrawElements(PrimitiveType.Triangles, 6, DrawElementsType.UnsignedInt, 0);
|
||||
|
||||
SwapBuffers();
|
||||
}
|
||||
|
||||
protected override void OnUpdateFrame(FrameEventArgs e)
|
||||
{
|
||||
base.OnUpdateFrame(e);
|
||||
|
||||
// Update fluid simulation
|
||||
_fluid.Step(0.1f, 0.0001f, 0.0001f);
|
||||
_fluid.FadeDensity(0.002f);
|
||||
|
||||
Title = $"Fluid Simulation :3 - FPS: {1.0 / e.Time:0}";
|
||||
}
|
||||
|
||||
private void UpdateTextureFromFluid()
|
||||
{
|
||||
// Convert fluid density to pixel data
|
||||
for (int y = 0; y < _fluidSizeY; y++)
|
||||
{
|
||||
for (int x = 0; x < _fluidSizeX; x++)
|
||||
{
|
||||
int index = (y * _fluidSizeX + x) * 4;
|
||||
float density = Math.Clamp(_fluid.GetDensity(x, y), 0, 1);
|
||||
|
||||
_pixels[index] = density; // R
|
||||
_pixels[index + 1] = density; // G
|
||||
_pixels[index + 2] = density; // B
|
||||
_pixels[index + 3] = 1.0f; // A
|
||||
}
|
||||
}
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, _texture);
|
||||
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, _fluidSizeX, _fluidSizeY,
|
||||
PixelFormat.Rgba, PixelType.Float, _pixels);
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseDown(e);
|
||||
_mouseDown = true;
|
||||
_activeButton = e.Button;
|
||||
_lastMousePos = new Vector2(MousePosition.X, MousePosition.Y);
|
||||
}
|
||||
|
||||
protected override void OnMouseUp(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseUp(e);
|
||||
_mouseDown = false;
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseMoveEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
if (_mouseDown)
|
||||
{
|
||||
var mousePos = new Vector2(e.X, e.Y);
|
||||
var delta = mousePos - _lastMousePos;
|
||||
|
||||
int fluidX = (int)(e.X / Size.X * _fluidSizeX);
|
||||
int fluidY = (int)((Size.Y - e.Y) / Size.Y * _fluidSizeY);
|
||||
|
||||
// Ensure coordinates are within bounds
|
||||
fluidX = Math.Clamp(fluidX, 1, _fluidSizeX - 2);
|
||||
fluidY = Math.Clamp(fluidY, 1, _fluidSizeY - 2);
|
||||
|
||||
if (_activeButton == MouseButton.Left)
|
||||
{
|
||||
// Add density in a small area
|
||||
for (int i = -2; i <= 2; i++)
|
||||
{
|
||||
for (int j = -2; j <= 2; j++)
|
||||
{
|
||||
_fluid.AddDensity(fluidX + i, fluidY + j, 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_activeButton == MouseButton.Right)
|
||||
{
|
||||
// Add velocity
|
||||
_fluid.AddVelocity(fluidX, fluidY, delta.X * 0.5f, -delta.Y * 0.5f);
|
||||
}
|
||||
|
||||
_lastMousePos = mousePos;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyboardKeyEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
|
||||
if (e.Key == Keys.Escape)
|
||||
Close();
|
||||
else if (e.Key == Keys.R)
|
||||
{
|
||||
_fluid = new FluidSimulation(_fluidSizeX, _fluidSizeY);
|
||||
Console.WriteLine("Simulation reset!");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnResize(ResizeEventArgs e)
|
||||
{
|
||||
base.OnResize(e);
|
||||
GL.Viewport(0, 0, e.Width, e.Height);
|
||||
}
|
||||
|
||||
protected override void OnUnload()
|
||||
{
|
||||
GL.DeleteBuffer(_vertexBufferObject);
|
||||
GL.DeleteBuffer(_elementBufferObject);
|
||||
GL.DeleteVertexArray(_vertexArrayObject);
|
||||
GL.DeleteProgram(_shaderProgram);
|
||||
GL.DeleteTexture(_texture);
|
||||
|
||||
base.OnUnload();
|
||||
}
|
||||
}
|
||||
844
simulations/fluids/fluidsC#/FluidWindow3D_GPU.cs
Normal file
844
simulations/fluids/fluidsC#/FluidWindow3D_GPU.cs
Normal file
@@ -0,0 +1,844 @@
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using OpenTK.Windowing.Common;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
|
||||
namespace fluidsC_;
|
||||
|
||||
public class FluidWindow3D_GPU : GameWindow
|
||||
{
|
||||
private int _fluidSizeX = 64;
|
||||
private int _fluidSizeY = 64;
|
||||
private int _fluidSizeZ = 64;
|
||||
|
||||
// GPU buffers - double buffered
|
||||
private int[] _densityTextures = new int[2];
|
||||
private int[] _velocityTextures = new int[2];
|
||||
private int[] _pressureTextures = new int[2];
|
||||
private int _divergenceTexture;
|
||||
private int _obstacleTexture;
|
||||
|
||||
// Shader programs
|
||||
private int _advectionShader;
|
||||
private int _forceShader;
|
||||
private int _divergenceShader;
|
||||
private int _pressureShader;
|
||||
private int _gradientShader;
|
||||
private int _renderShader;
|
||||
private int _clearShader;
|
||||
private int _obstacleShader;
|
||||
|
||||
private int _vertexArrayObject;
|
||||
private int _pointCount;
|
||||
|
||||
private Camera _camera;
|
||||
private bool _firstMove = true;
|
||||
private Vector2 _lastPos;
|
||||
private float _time = 0;
|
||||
|
||||
private int _currentDensity = 0;
|
||||
private int _currentVelocity = 0;
|
||||
private int _currentPressure = 0;
|
||||
|
||||
// Simulation parameters
|
||||
private float _diffusion = 0.0001f;
|
||||
private float _viscosity = 0.0001f;
|
||||
|
||||
public FluidWindow3D_GPU(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
|
||||
: base(gameWindowSettings, nativeWindowSettings)
|
||||
{
|
||||
_camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);
|
||||
}
|
||||
|
||||
protected override void OnLoad()
|
||||
{
|
||||
base.OnLoad();
|
||||
|
||||
Console.WriteLine($"OpenGL Version: {GL.GetString(StringName.Version)}");
|
||||
Console.WriteLine($"GLSL Version: {GL.GetString(StringName.ShadingLanguageVersion)}");
|
||||
|
||||
GL.ClearColor(0.02f, 0.02f, 0.05f, 1.0f);
|
||||
GL.Enable(EnableCap.DepthTest);
|
||||
GL.Enable(EnableCap.Blend);
|
||||
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
||||
GL.Enable(EnableCap.ProgramPointSize);
|
||||
|
||||
CreateTextures();
|
||||
SetupShaders();
|
||||
SetupPointCloud();
|
||||
InitializeObstacles();
|
||||
|
||||
// Add initial fluid
|
||||
AddInitialFluid();
|
||||
|
||||
Console.WriteLine("🎯 WORKING GPU Fluid Simulation - CLICK AND DRAG!");
|
||||
Console.WriteLine("Left Click: Add Fluid | Right Click: Add Force | R: Reset");
|
||||
}
|
||||
|
||||
private void CreateTextures()
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
_densityTextures[i] = Create3DTexture(_fluidSizeX, _fluidSizeY, _fluidSizeZ, false);
|
||||
_velocityTextures[i] = Create3DTexture(_fluidSizeX, _fluidSizeY, _fluidSizeZ, true);
|
||||
_pressureTextures[i] = Create3DTexture(_fluidSizeX, _fluidSizeY, _fluidSizeZ, false);
|
||||
}
|
||||
_divergenceTexture = Create3DTexture(_fluidSizeX, _fluidSizeY, _fluidSizeZ, false);
|
||||
_obstacleTexture = Create3DTexture(_fluidSizeX, _fluidSizeY, _fluidSizeZ, false);
|
||||
|
||||
ClearAllTextures();
|
||||
}
|
||||
|
||||
private int Create3DTexture(int width, int height, int depth, bool rgb)
|
||||
{
|
||||
int texture = GL.GenTexture();
|
||||
GL.BindTexture(TextureTarget.Texture3D, texture);
|
||||
|
||||
GL.TexParameter(TextureTarget.Texture3D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture3D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture3D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture3D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture3D, TextureParameterName.TextureWrapR, (int)TextureWrapMode.ClampToEdge);
|
||||
|
||||
var format = rgb ? PixelInternalFormat.Rgb32f : PixelInternalFormat.R32f;
|
||||
var pixelFormat = rgb ? PixelFormat.Rgb : PixelFormat.Red;
|
||||
|
||||
GL.TexImage3D(TextureTarget.Texture3D, 0, format, width, height, depth, 0, pixelFormat, PixelType.Float, IntPtr.Zero);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
private void SetupShaders()
|
||||
{
|
||||
// PROPER FLUID SIMULATION SHADERS
|
||||
_advectionShader = CreateComputeShader(@"
|
||||
#version 430
|
||||
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
|
||||
|
||||
layout(r32f, binding = 0) uniform image3D densityIn;
|
||||
layout(rgba32f, binding = 1) uniform image3D velocityIn;
|
||||
layout(r32f, binding = 2) uniform image3D densityOut;
|
||||
layout(rgba32f, binding = 3) uniform image3D velocityOut;
|
||||
layout(r32f, binding = 4) uniform image3D obstacles;
|
||||
|
||||
uniform float dt;
|
||||
uniform float dissipation;
|
||||
|
||||
vec3 sampleVelocity(vec3 pos) {
|
||||
ivec3 coord = ivec3(clamp(pos, vec3(0), vec3(imageSize(velocityIn)) - vec3(1)));
|
||||
return imageLoad(velocityIn, coord).rgb;
|
||||
}
|
||||
|
||||
float sampleDensity(vec3 pos) {
|
||||
ivec3 coord = ivec3(clamp(pos, vec3(0), vec3(imageSize(densityIn)) - vec3(1)));
|
||||
return imageLoad(densityIn, coord).r;
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec3 coord = ivec3(gl_GlobalInvocationID.xyz);
|
||||
ivec3 size = imageSize(densityIn);
|
||||
|
||||
if(coord.x >= size.x || coord.y >= size.y || coord.z >= size.z)
|
||||
return;
|
||||
|
||||
if(imageLoad(obstacles, coord).r > 0.5) {
|
||||
imageStore(densityOut, coord, vec4(0.0));
|
||||
imageStore(velocityOut, coord, vec4(0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
// Semi-Lagrangian advection
|
||||
vec3 vel = sampleVelocity(vec3(coord));
|
||||
vec3 prevPos = vec3(coord) - vel * dt * 10.0;
|
||||
|
||||
// Advect density
|
||||
float density = sampleDensity(prevPos);
|
||||
imageStore(densityOut, coord, vec4(density * dissipation, 0.0, 0.0, 1.0));
|
||||
|
||||
// Advect velocity (self-advection)
|
||||
vec3 prevVel = sampleVelocity(prevPos);
|
||||
imageStore(velocityOut, coord, vec4(prevVel, 1.0));
|
||||
}");
|
||||
|
||||
_forceShader = CreateComputeShader(@"
|
||||
#version 430
|
||||
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
|
||||
|
||||
layout(rgba32f, binding = 0) uniform image3D velocity;
|
||||
layout(r32f, binding = 1) uniform image3D density;
|
||||
layout(r32f, binding = 2) uniform image3D obstacles;
|
||||
|
||||
uniform vec3 force;
|
||||
uniform vec3 position;
|
||||
uniform float radius;
|
||||
uniform float densityAmount;
|
||||
|
||||
void main() {
|
||||
ivec3 coord = ivec3(gl_GlobalInvocationID.xyz);
|
||||
|
||||
if(imageLoad(obstacles, coord).r > 0.5)
|
||||
return;
|
||||
|
||||
vec3 cellPos = vec3(coord);
|
||||
vec3 diff = cellPos - position;
|
||||
float dist = length(diff);
|
||||
|
||||
if(dist < radius) {
|
||||
float influence = (1.0 - dist/radius) * (1.0 - dist/radius);
|
||||
|
||||
// Add force to velocity
|
||||
vec3 currentVel = imageLoad(velocity, coord).rgb;
|
||||
vec3 newVel = currentVel + force * influence * 0.5;
|
||||
imageStore(velocity, coord, vec4(newVel, 1.0));
|
||||
|
||||
// Add density
|
||||
if(densityAmount > 0.0) {
|
||||
float currentDensity = imageLoad(density, coord).r;
|
||||
float newDensity = currentDensity + densityAmount * influence;
|
||||
imageStore(density, coord, vec4(newDensity, 0.0, 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
}");
|
||||
|
||||
_divergenceShader = CreateComputeShader(@"
|
||||
#version 430
|
||||
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
|
||||
|
||||
layout(rgba32f, binding = 0) uniform image3D velocity;
|
||||
layout(r32f, binding = 1) uniform image3D divergence;
|
||||
layout(r32f, binding = 2) uniform image3D obstacles;
|
||||
|
||||
void main() {
|
||||
ivec3 coord = ivec3(gl_GlobalInvocationID.xyz);
|
||||
ivec3 size = imageSize(velocity);
|
||||
|
||||
if(coord.x < 1 || coord.x >= size.x-1 ||
|
||||
coord.y < 1 || coord.y >= size.y-1 ||
|
||||
coord.z < 1 || coord.z >= size.z-1)
|
||||
return;
|
||||
|
||||
if(imageLoad(obstacles, coord).r > 0.5) {
|
||||
imageStore(divergence, coord, vec4(0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate divergence
|
||||
float left = imageLoad(velocity, coord - ivec3(1,0,0)).r;
|
||||
float right = imageLoad(velocity, coord + ivec3(1,0,0)).r;
|
||||
float down = imageLoad(velocity, coord - ivec3(0,1,0)).g;
|
||||
float up = imageLoad(velocity, coord + ivec3(0,1,0)).g;
|
||||
float back = imageLoad(velocity, coord - ivec3(0,0,1)).b;
|
||||
float front = imageLoad(velocity, coord + ivec3(0,0,1)).b;
|
||||
|
||||
float div = (right - left + up - down + front - back) * 0.5;
|
||||
imageStore(divergence, coord, vec4(div, 0.0, 0.0, 1.0));
|
||||
}");
|
||||
|
||||
_pressureShader = CreateComputeShader(@"
|
||||
#version 430
|
||||
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
|
||||
|
||||
layout(r32f, binding = 0) uniform image3D pressureIn;
|
||||
layout(r32f, binding = 1) uniform image3D pressureOut;
|
||||
layout(r32f, binding = 2) uniform image3D divergence;
|
||||
layout(r32f, binding = 3) uniform image3D obstacles;
|
||||
|
||||
void main() {
|
||||
ivec3 coord = ivec3(gl_GlobalInvocationID.xyz);
|
||||
ivec3 size = imageSize(pressureIn);
|
||||
|
||||
if(coord.x < 1 || coord.x >= size.x-1 ||
|
||||
coord.y < 1 || coord.y >= size.y-1 ||
|
||||
coord.z < 1 || coord.z >= size.z-1)
|
||||
return;
|
||||
|
||||
if(imageLoad(obstacles, coord).r > 0.5) {
|
||||
imageStore(pressureOut, coord, vec4(0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
// Jacobi iteration for pressure
|
||||
float left = imageLoad(pressureIn, coord - ivec3(1,0,0)).r;
|
||||
float right = imageLoad(pressureIn, coord + ivec3(1,0,0)).r;
|
||||
float down = imageLoad(pressureIn, coord - ivec3(0,1,0)).r;
|
||||
float up = imageLoad(pressureIn, coord + ivec3(0,1,0)).r;
|
||||
float back = imageLoad(pressureIn, coord - ivec3(0,0,1)).r;
|
||||
float front = imageLoad(pressureIn, coord + ivec3(0,0,1)).r;
|
||||
|
||||
float div = imageLoad(divergence, coord).r;
|
||||
float pressure = (left + right + down + up + back + front - div) / 6.0;
|
||||
imageStore(pressureOut, coord, vec4(pressure, 0.0, 0.0, 1.0));
|
||||
}");
|
||||
|
||||
_gradientShader = CreateComputeShader(@"
|
||||
#version 430
|
||||
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
|
||||
|
||||
layout(rgba32f, binding = 0) uniform image3D velocity;
|
||||
layout(r32f, binding = 1) uniform image3D pressure;
|
||||
layout(r32f, binding = 2) uniform image3D obstacles;
|
||||
|
||||
void main() {
|
||||
ivec3 coord = ivec3(gl_GlobalInvocationID.xyz);
|
||||
ivec3 size = imageSize(velocity);
|
||||
|
||||
if(coord.x < 1 || coord.x >= size.x-1 ||
|
||||
coord.y < 1 || coord.y >= size.y-1 ||
|
||||
coord.z < 1 || coord.z >= size.z-1)
|
||||
return;
|
||||
|
||||
if(imageLoad(obstacles, coord).r > 0.5) {
|
||||
imageStore(velocity, coord, vec4(0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
// Subtract pressure gradient
|
||||
float pressureLeft = imageLoad(pressure, coord - ivec3(1,0,0)).r;
|
||||
float pressureRight = imageLoad(pressure, coord + ivec3(1,0,0)).r;
|
||||
float pressureDown = imageLoad(pressure, coord - ivec3(0,1,0)).r;
|
||||
float pressureUp = imageLoad(pressure, coord + ivec3(0,1,0)).r;
|
||||
float pressureBack = imageLoad(pressure, coord - ivec3(0,0,1)).r;
|
||||
float pressureFront = imageLoad(pressure, coord + ivec3(0,0,1)).r;
|
||||
|
||||
vec3 grad = vec3(
|
||||
pressureRight - pressureLeft,
|
||||
pressureUp - pressureDown,
|
||||
pressureFront - pressureBack
|
||||
) * 0.5;
|
||||
|
||||
vec3 currentVel = imageLoad(velocity, coord).rgb;
|
||||
vec3 newVel = currentVel - grad;
|
||||
imageStore(velocity, coord, vec4(newVel, 1.0));
|
||||
}");
|
||||
|
||||
_clearShader = CreateComputeShader(@"
|
||||
#version 430
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
|
||||
layout(r32f, binding = 0) uniform image3D textureToClear;
|
||||
|
||||
uniform float value;
|
||||
|
||||
void main() {
|
||||
ivec3 coord = ivec3(gl_GlobalInvocationID.xyz);
|
||||
ivec3 size = imageSize(textureToClear);
|
||||
if(coord.x >= size.x || coord.y >= size.y || coord.z >= size.z)
|
||||
return;
|
||||
|
||||
imageStore(textureToClear, coord, vec4(value, 0.0, 0.0, 1.0));
|
||||
}");
|
||||
|
||||
_obstacleShader = CreateComputeShader(@"
|
||||
#version 430
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
|
||||
layout(r32f, binding = 0) uniform image3D obstacles;
|
||||
|
||||
uniform vec3 position;
|
||||
uniform float radius;
|
||||
uniform float setValue;
|
||||
|
||||
void main() {
|
||||
ivec3 coord = ivec3(gl_GlobalInvocationID.xyz);
|
||||
ivec3 size = imageSize(obstacles);
|
||||
if(coord.x >= size.x || coord.y >= size.y || coord.z >= size.z)
|
||||
return;
|
||||
|
||||
vec3 cellPos = vec3(coord);
|
||||
float dist = length(cellPos - position);
|
||||
|
||||
if(dist <= radius) {
|
||||
imageStore(obstacles, coord, vec4(setValue, 0.0, 0.0, 1.0));
|
||||
}
|
||||
}");
|
||||
|
||||
// RENDER SHADER
|
||||
_renderShader = CreateRenderShader();
|
||||
}
|
||||
|
||||
private int CreateComputeShader(string source)
|
||||
{
|
||||
var shader = GL.CreateShader(ShaderType.ComputeShader);
|
||||
GL.ShaderSource(shader, source);
|
||||
GL.CompileShader(shader);
|
||||
|
||||
GL.GetShader(shader, ShaderParameter.CompileStatus, out int success);
|
||||
if (success == 0)
|
||||
{
|
||||
var infoLog = GL.GetShaderInfoLog(shader);
|
||||
Console.WriteLine($"Compute Shader Compilation Failed:\n{infoLog}");
|
||||
}
|
||||
|
||||
var program = GL.CreateProgram();
|
||||
GL.AttachShader(program, shader);
|
||||
GL.LinkProgram(program);
|
||||
|
||||
GL.DeleteShader(shader);
|
||||
return program;
|
||||
}
|
||||
|
||||
private int CreateRenderShader()
|
||||
{
|
||||
const string vertexShaderSource = @"
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPosition;
|
||||
out vec3 fragTexCoord;
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * view * model * vec4(aPosition, 1.0);
|
||||
fragTexCoord = (aPosition + 1.0) * 0.5;
|
||||
gl_PointSize = 4.0;
|
||||
}";
|
||||
|
||||
const string fragmentShaderSource = @"
|
||||
#version 330 core
|
||||
in vec3 fragTexCoord;
|
||||
out vec4 fragColor;
|
||||
uniform sampler3D densityTexture;
|
||||
uniform sampler3D obstacleTexture;
|
||||
uniform float time;
|
||||
void main()
|
||||
{
|
||||
vec3 texCoord = fragTexCoord;
|
||||
if(texCoord.x < 0.0 || texCoord.x > 1.0 ||
|
||||
texCoord.y < 0.0 || texCoord.y > 1.0 ||
|
||||
texCoord.z < 0.0 || texCoord.z > 1.0)
|
||||
discard;
|
||||
|
||||
float density = texture(densityTexture, texCoord).r;
|
||||
float obstacle = texture(obstacleTexture, texCoord).r;
|
||||
|
||||
if(obstacle > 0.5) {
|
||||
fragColor = vec4(0.3, 0.3, 0.3, 1.0);
|
||||
} else if(density > 0.01) {
|
||||
vec3 color = mix(vec3(0.1, 0.3, 0.8), vec3(0.0, 0.8, 1.0), density * 2.0);
|
||||
color += 0.1 * sin(time + fragTexCoord.y * 10.0);
|
||||
fragColor = vec4(color, density * 0.9);
|
||||
} else {
|
||||
discard;
|
||||
}
|
||||
}";
|
||||
|
||||
var vertexShader = GL.CreateShader(ShaderType.VertexShader);
|
||||
GL.ShaderSource(vertexShader, vertexShaderSource);
|
||||
GL.CompileShader(vertexShader);
|
||||
|
||||
var fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
|
||||
GL.ShaderSource(fragmentShader, fragmentShaderSource);
|
||||
GL.CompileShader(fragmentShader);
|
||||
|
||||
var program = GL.CreateProgram();
|
||||
GL.AttachShader(program, vertexShader);
|
||||
GL.AttachShader(program, fragmentShader);
|
||||
GL.LinkProgram(program);
|
||||
|
||||
GL.DeleteShader(vertexShader);
|
||||
GL.DeleteShader(fragmentShader);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
private void SetupPointCloud()
|
||||
{
|
||||
var vertices = new List<Vector3>();
|
||||
|
||||
// Reduced point count for performance but still good coverage
|
||||
for (int x = 0; x < _fluidSizeX; x += 1)
|
||||
{
|
||||
for (int y = 0; y < _fluidSizeY; y += 1)
|
||||
{
|
||||
for (int z = 0; z < _fluidSizeZ; z += 1)
|
||||
{
|
||||
float px = (x / (float)_fluidSizeX) * 2.0f - 1.0f;
|
||||
float py = (y / (float)_fluidSizeY) * 2.0f - 1.0f;
|
||||
float pz = (z / (float)_fluidSizeZ) * 2.0f - 1.0f;
|
||||
vertices.Add(new Vector3(px, py, pz));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_pointCount = vertices.Count;
|
||||
Console.WriteLine($"Rendering {_pointCount} points");
|
||||
|
||||
_vertexArrayObject = GL.GenVertexArray();
|
||||
int vbo = GL.GenBuffer();
|
||||
|
||||
GL.BindVertexArray(_vertexArrayObject);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Count * 3 * sizeof(float), vertices.ToArray(), BufferUsageHint.StaticDraw);
|
||||
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
}
|
||||
|
||||
private void InitializeObstacles()
|
||||
{
|
||||
// Simple box boundaries
|
||||
AddBoxObstacle(new Vector3(0, 0, 0), new Vector3(_fluidSizeX, 2, _fluidSizeZ)); // Floor
|
||||
AddBoxObstacle(new Vector3(0, 0, 0), new Vector3(2, _fluidSizeY, _fluidSizeZ)); // Left
|
||||
AddBoxObstacle(new Vector3(_fluidSizeX - 2, 0, 0), new Vector3(2, _fluidSizeY, _fluidSizeZ)); // Right
|
||||
AddBoxObstacle(new Vector3(0, 0, 0), new Vector3(_fluidSizeX, _fluidSizeY, 2)); // Back
|
||||
AddBoxObstacle(new Vector3(0, 0, _fluidSizeZ - 2), new Vector3(_fluidSizeX, _fluidSizeY, 2)); // Front
|
||||
|
||||
// Central obstacle
|
||||
AddSphereObstacle(new Vector3(_fluidSizeX / 2, _fluidSizeY / 4, _fluidSizeZ / 2), 6.0f);
|
||||
}
|
||||
|
||||
private void AddInitialFluid()
|
||||
{
|
||||
// Add initial falling fluid
|
||||
Vector3 sourcePos = new Vector3(_fluidSizeX / 2, _fluidSizeY - 10, _fluidSizeZ / 2);
|
||||
AddForce(sourcePos, new Vector3(0.0f, -2.0f, 0.0f), 8.0f, 10.0f);
|
||||
|
||||
// Add some random fluid blobs
|
||||
Random rand = new Random();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
Vector3 randomPos = new Vector3(
|
||||
rand.Next(10, _fluidSizeX - 10),
|
||||
rand.Next(10, _fluidSizeY - 10),
|
||||
rand.Next(10, _fluidSizeZ - 10)
|
||||
);
|
||||
AddForce(randomPos, Vector3.Zero, 4.0f, 3.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSphereObstacle(Vector3 center, float radius)
|
||||
{
|
||||
GL.UseProgram(_obstacleShader);
|
||||
BindImageTexture(0, _obstacleTexture, TextureAccess.WriteOnly);
|
||||
|
||||
GL.Uniform3(GL.GetUniformLocation(_obstacleShader, "position"), center);
|
||||
GL.Uniform1(GL.GetUniformLocation(_obstacleShader, "radius"), radius);
|
||||
GL.Uniform1(GL.GetUniformLocation(_obstacleShader, "setValue"), 1.0f);
|
||||
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 8, 8, 8);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
}
|
||||
|
||||
private void AddBoxObstacle(Vector3 min, Vector3 max)
|
||||
{
|
||||
Vector3 center = (min + max) * 0.5f;
|
||||
Vector3 size = max - min;
|
||||
AddSphereObstacle(center, Math.Max(size.X, Math.Max(size.Y, size.Z)) * 0.5f);
|
||||
}
|
||||
|
||||
protected override void OnRenderFrame(FrameEventArgs e)
|
||||
{
|
||||
base.OnRenderFrame(e);
|
||||
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||
|
||||
StepSimulation((float)e.Time);
|
||||
RenderFluid();
|
||||
|
||||
SwapBuffers();
|
||||
}
|
||||
|
||||
private void StepSimulation(float dt)
|
||||
{
|
||||
dt = Math.Min(dt, 0.016f); // Cap at ~60fps
|
||||
|
||||
// Full fluid simulation steps
|
||||
Advect(dt, 0.99f);
|
||||
AddExternalForces(dt);
|
||||
Project(dt);
|
||||
AddDemoSources();
|
||||
|
||||
// Swap buffers
|
||||
_currentDensity = 1 - _currentDensity;
|
||||
_currentVelocity = 1 - _currentVelocity;
|
||||
_currentPressure = 1 - _currentPressure;
|
||||
}
|
||||
|
||||
private void Advect(float dt, float dissipation)
|
||||
{
|
||||
GL.UseProgram(_advectionShader);
|
||||
|
||||
BindImageTexture(0, _densityTextures[_currentDensity], TextureAccess.ReadOnly);
|
||||
BindImageTexture(1, _velocityTextures[_currentVelocity], TextureAccess.ReadOnly);
|
||||
BindImageTexture(2, _densityTextures[1 - _currentDensity], TextureAccess.WriteOnly);
|
||||
BindImageTexture(3, _velocityTextures[1 - _currentVelocity], TextureAccess.WriteOnly);
|
||||
BindImageTexture(4, _obstacleTexture, TextureAccess.ReadOnly);
|
||||
|
||||
GL.Uniform1(GL.GetUniformLocation(_advectionShader, "dt"), dt);
|
||||
GL.Uniform1(GL.GetUniformLocation(_advectionShader, "dissipation"), dissipation);
|
||||
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 4, 4, 4);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
}
|
||||
|
||||
private void AddExternalForces(float dt)
|
||||
{
|
||||
// Gravity
|
||||
Vector3 gravity = new Vector3(0.0f, -9.8f, 0.0f) * dt;
|
||||
|
||||
GL.UseProgram(_forceShader);
|
||||
BindImageTexture(0, _velocityTextures[_currentVelocity], TextureAccess.ReadWrite);
|
||||
BindImageTexture(1, _densityTextures[_currentDensity], TextureAccess.ReadWrite);
|
||||
BindImageTexture(2, _obstacleTexture, TextureAccess.ReadOnly);
|
||||
|
||||
GL.Uniform3(GL.GetUniformLocation(_forceShader, "force"), gravity);
|
||||
GL.Uniform3(GL.GetUniformLocation(_forceShader, "position"), new Vector3(_fluidSizeX / 2, _fluidSizeY / 2, _fluidSizeZ / 2));
|
||||
GL.Uniform1(GL.GetUniformLocation(_forceShader, "radius"), _fluidSizeX * 2.0f); // Affect entire field
|
||||
GL.Uniform1(GL.GetUniformLocation(_forceShader, "densityAmount"), 0.0f);
|
||||
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 4, 4, 4);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
}
|
||||
|
||||
private void Project(float dt)
|
||||
{
|
||||
// Calculate divergence
|
||||
GL.UseProgram(_divergenceShader);
|
||||
BindImageTexture(0, _velocityTextures[_currentVelocity], TextureAccess.ReadOnly);
|
||||
BindImageTexture(1, _divergenceTexture, TextureAccess.WriteOnly);
|
||||
BindImageTexture(2, _obstacleTexture, TextureAccess.ReadOnly);
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 4, 4, 4);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
|
||||
// Solve pressure (Jacobi iterations)
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
GL.UseProgram(_pressureShader);
|
||||
BindImageTexture(0, _pressureTextures[_currentPressure], TextureAccess.ReadOnly);
|
||||
BindImageTexture(1, _pressureTextures[1 - _currentPressure], TextureAccess.WriteOnly);
|
||||
BindImageTexture(2, _divergenceTexture, TextureAccess.ReadOnly);
|
||||
BindImageTexture(3, _obstacleTexture, TextureAccess.ReadOnly);
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 4, 4, 4);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
_currentPressure = 1 - _currentPressure;
|
||||
}
|
||||
|
||||
// Subtract pressure gradient
|
||||
GL.UseProgram(_gradientShader);
|
||||
BindImageTexture(0, _velocityTextures[_currentVelocity], TextureAccess.ReadWrite);
|
||||
BindImageTexture(1, _pressureTextures[_currentPressure], TextureAccess.ReadOnly);
|
||||
BindImageTexture(2, _obstacleTexture, TextureAccess.ReadOnly);
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 4, 4, 4);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
}
|
||||
|
||||
private void AddDemoSources()
|
||||
{
|
||||
// Continuous falling source
|
||||
Vector3 sourcePos = new Vector3(_fluidSizeX / 2, _fluidSizeY - 8, _fluidSizeZ / 2);
|
||||
AddForce(sourcePos, new Vector3(0.0f, -3.0f, 0.0f), 6.0f, 2.0f);
|
||||
|
||||
// Random turbulence
|
||||
if ((int)(_time * 2) % 4 == 0)
|
||||
{
|
||||
Vector3 randomPos = new Vector3(
|
||||
_fluidSizeX * 0.3f + MathF.Sin(_time) * 5.0f,
|
||||
_fluidSizeY - 5,
|
||||
_fluidSizeZ * 0.7f + MathF.Cos(_time) * 5.0f
|
||||
);
|
||||
AddForce(randomPos, new Vector3(0.0f, -2.0f, 0.0f), 4.0f, 1.5f);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddForce(Vector3 position, Vector3 force, float radius, float densityAmount)
|
||||
{
|
||||
GL.UseProgram(_forceShader);
|
||||
|
||||
BindImageTexture(0, _velocityTextures[_currentVelocity], TextureAccess.ReadWrite);
|
||||
BindImageTexture(1, _densityTextures[_currentDensity], TextureAccess.ReadWrite);
|
||||
BindImageTexture(2, _obstacleTexture, TextureAccess.ReadOnly);
|
||||
|
||||
GL.Uniform3(GL.GetUniformLocation(_forceShader, "force"), force);
|
||||
GL.Uniform3(GL.GetUniformLocation(_forceShader, "position"), position);
|
||||
GL.Uniform1(GL.GetUniformLocation(_forceShader, "radius"), radius);
|
||||
GL.Uniform1(GL.GetUniformLocation(_forceShader, "densityAmount"), densityAmount);
|
||||
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 4, 4, 4);
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||
}
|
||||
|
||||
private void RenderFluid()
|
||||
{
|
||||
GL.UseProgram(_renderShader);
|
||||
|
||||
var model = Matrix4.Identity;
|
||||
var view = _camera.GetViewMatrix();
|
||||
var projection = _camera.GetProjectionMatrix();
|
||||
|
||||
GL.UniformMatrix4(GL.GetUniformLocation(_renderShader, "model"), false, ref model);
|
||||
GL.UniformMatrix4(GL.GetUniformLocation(_renderShader, "view"), false, ref view);
|
||||
GL.UniformMatrix4(GL.GetUniformLocation(_renderShader, "projection"), false, ref projection);
|
||||
GL.Uniform1(GL.GetUniformLocation(_renderShader, "time"), _time);
|
||||
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(TextureTarget.Texture3D, _densityTextures[_currentDensity]);
|
||||
GL.Uniform1(GL.GetUniformLocation(_renderShader, "densityTexture"), 0);
|
||||
|
||||
GL.ActiveTexture(TextureUnit.Texture1);
|
||||
GL.BindTexture(TextureTarget.Texture3D, _obstacleTexture);
|
||||
GL.Uniform1(GL.GetUniformLocation(_renderShader, "obstacleTexture"), 1);
|
||||
|
||||
GL.BindVertexArray(_vertexArrayObject);
|
||||
GL.DrawArrays(PrimitiveType.Points, 0, _pointCount);
|
||||
}
|
||||
|
||||
private void BindImageTexture(int unit, int texture, TextureAccess access)
|
||||
{
|
||||
GL.BindImageTexture(unit, texture, 0, false, 0, access, SizedInternalFormat.R32f);
|
||||
}
|
||||
|
||||
private void DispatchCompute(int sizeX, int sizeY, int sizeZ, int localSizeX, int localSizeY, int localSizeZ)
|
||||
{
|
||||
int groupsX = (sizeX + localSizeX - 1) / localSizeX;
|
||||
int groupsY = (sizeY + localSizeY - 1) / localSizeY;
|
||||
int groupsZ = (sizeZ + localSizeZ - 1) / localSizeZ;
|
||||
|
||||
GL.DispatchCompute(groupsX, groupsY, groupsZ);
|
||||
}
|
||||
|
||||
private void ClearAllTextures()
|
||||
{
|
||||
GL.UseProgram(_clearShader);
|
||||
|
||||
foreach (var texture in _densityTextures)
|
||||
{
|
||||
BindImageTexture(0, texture, TextureAccess.WriteOnly);
|
||||
GL.Uniform1(GL.GetUniformLocation(_clearShader, "value"), 0.0f);
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 8, 8, 8);
|
||||
}
|
||||
|
||||
foreach (var texture in _velocityTextures)
|
||||
{
|
||||
BindImageTexture(0, texture, TextureAccess.WriteOnly);
|
||||
GL.Uniform1(GL.GetUniformLocation(_clearShader, "value"), 0.0f);
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 8, 8, 8);
|
||||
}
|
||||
|
||||
foreach (var texture in _pressureTextures)
|
||||
{
|
||||
BindImageTexture(0, texture, TextureAccess.WriteOnly);
|
||||
GL.Uniform1(GL.GetUniformLocation(_clearShader, "value"), 0.0f);
|
||||
DispatchCompute(_fluidSizeX, _fluidSizeY, _fluidSizeZ, 8, 8, 8);
|
||||
}
|
||||
|
||||
GL.MemoryBarrier(MemoryBarrierFlags.AllBarrierBits);
|
||||
|
||||
// Re-add initial fluid
|
||||
AddInitialFluid();
|
||||
}
|
||||
|
||||
protected override void OnUpdateFrame(FrameEventArgs e)
|
||||
{
|
||||
base.OnUpdateFrame(e);
|
||||
|
||||
_time += (float)e.Time;
|
||||
|
||||
_camera.UpdateVectors();
|
||||
|
||||
var input = KeyboardState;
|
||||
float cameraSpeed = _camera.Speed * (float)e.Time;
|
||||
|
||||
if (input.IsKeyDown(Keys.W))
|
||||
_camera.Position += _camera.Front * cameraSpeed;
|
||||
if (input.IsKeyDown(Keys.S))
|
||||
_camera.Position -= _camera.Front * cameraSpeed;
|
||||
if (input.IsKeyDown(Keys.A))
|
||||
_camera.Position -= _camera.Right * cameraSpeed;
|
||||
if (input.IsKeyDown(Keys.D))
|
||||
_camera.Position += _camera.Right * cameraSpeed;
|
||||
if (input.IsKeyDown(Keys.Space))
|
||||
_camera.Position += _camera.Up * cameraSpeed;
|
||||
if (input.IsKeyDown(Keys.LeftShift))
|
||||
_camera.Position -= _camera.Up * cameraSpeed;
|
||||
|
||||
Title = $"🎯 WORKING Fluid Simulation - FPS: {1.0 / e.Time:0} - CLICK TO INTERACT!";
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseMoveEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
if (IsFocused)
|
||||
{
|
||||
if (_firstMove)
|
||||
{
|
||||
_lastPos = new Vector2(e.X, e.Y);
|
||||
_firstMove = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var deltaX = e.X - _lastPos.X;
|
||||
var deltaY = e.Y - _lastPos.Y;
|
||||
_lastPos = new Vector2(e.X, e.Y);
|
||||
|
||||
_camera.Yaw += deltaX * _camera.Sensitivity;
|
||||
_camera.Pitch -= deltaY * _camera.Sensitivity;
|
||||
|
||||
_camera.Pitch = Math.Clamp(_camera.Pitch, -MathHelper.PiOver2 + 0.1f, MathHelper.PiOver2 - 0.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseDown(e);
|
||||
|
||||
var mousePos = new Vector2(MouseState.X, MouseState.Y);
|
||||
|
||||
// Simple direct mapping
|
||||
Vector3 fluidPos = new Vector3(
|
||||
(mousePos.X / Size.X) * _fluidSizeX,
|
||||
(1.0f - mousePos.Y / Size.Y) * _fluidSizeY,
|
||||
_fluidSizeZ / 2
|
||||
);
|
||||
|
||||
// Clamp to reasonable bounds
|
||||
fluidPos.X = Math.Clamp(fluidPos.X, 5, _fluidSizeX - 5);
|
||||
fluidPos.Y = Math.Clamp(fluidPos.Y, 5, _fluidSizeY - 5);
|
||||
fluidPos.Z = Math.Clamp(fluidPos.Z, 5, _fluidSizeZ - 5);
|
||||
|
||||
if (e.Button == MouseButton.Left)
|
||||
{
|
||||
AddForce(fluidPos, Vector3.Zero, 8.0f, 15.0f);
|
||||
Console.WriteLine($"💧 Added DENSITY at: {fluidPos}");
|
||||
}
|
||||
else if (e.Button == MouseButton.Right)
|
||||
{
|
||||
// Add velocity away from center for explosion effect
|
||||
Vector3 center = new Vector3(_fluidSizeX / 2, _fluidSizeY / 2, _fluidSizeZ / 2);
|
||||
Vector3 dir = Vector3.Normalize(fluidPos - center);
|
||||
AddForce(fluidPos, dir * 20.0f, 6.0f, 0.0f);
|
||||
Console.WriteLine($"💨 Added VELOCITY at: {fluidPos}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyboardKeyEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
|
||||
if (e.Key == Keys.Escape)
|
||||
Close();
|
||||
else if (e.Key == Keys.R)
|
||||
{
|
||||
ClearAllTextures();
|
||||
Console.WriteLine("🔄 Simulation RESET!");
|
||||
}
|
||||
else if (e.Key == Keys.O)
|
||||
{
|
||||
var mousePos = new Vector2(MouseState.X, MouseState.Y);
|
||||
Vector3 fluidPos = new Vector3(
|
||||
(mousePos.X / Size.X) * _fluidSizeX,
|
||||
(1.0f - mousePos.Y / Size.Y) * _fluidSizeY,
|
||||
_fluidSizeZ / 2
|
||||
);
|
||||
AddSphereObstacle(fluidPos, 5.0f);
|
||||
Console.WriteLine($"🧱 Added OBSTACLE at: {fluidPos}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnResize(ResizeEventArgs e)
|
||||
{
|
||||
base.OnResize(e);
|
||||
GL.Viewport(0, 0, Size.X, Size.Y);
|
||||
_camera.AspectRatio = Size.X / (float)Size.Y;
|
||||
}
|
||||
}
|
||||
26
simulations/fluids/fluidsC#/Program.cs
Normal file
26
simulations/fluids/fluidsC#/Program.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using OpenTK.Windowing.Common;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
|
||||
namespace fluidsC_;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
var settings = GameWindowSettings.Default;
|
||||
var nativeSettings = new NativeWindowSettings()
|
||||
{
|
||||
Size = new OpenTK.Mathematics.Vector2i(1200, 800),
|
||||
Title = "GPU Fluid Simulation - FIXED",
|
||||
Flags = ContextFlags.ForwardCompatible,
|
||||
Profile = ContextProfile.Core,
|
||||
API = ContextAPI.OpenGL,
|
||||
APIVersion = new System.Version(4, 3)
|
||||
};
|
||||
|
||||
using (var window = new FluidWindow3D_GPU(settings, nativeSettings))
|
||||
{
|
||||
window.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
15
simulations/fluids/fluidsC#/fluidsC#.csproj
Normal file
15
simulations/fluids/fluidsC#/fluidsC#.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<RootNamespace>fluidsC_</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK" Version="4.9.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user