I basically started studying Unreal Engine 5 because it has a really nice Navier-Stokes solver. However, it was not clear to me whether or not they allowed you to report collision data for things other than rendering purposes. That was a big disappointment for me, because I would have had to pay some major cash to get official support on the subject.
So I decided to see how far I could get using Claude AI. After some alterations, the code that I got from Claude is at:
It collects data on the collisions with obstacles, and reports it on the CPU end of things.
Below is a grayscale image, with orange highlights where the density field interacts with the obstacle field. Not bad for an AI (under some selective guidance).
I added in code and data for a colour texture, which advects and diffuses like the density texture. This way I can visually differentiate between enemy fire and friendly fire. Claude helped.
I have a bug, and I've noticed it in another OpenGL app that I wrote that I had the same problem. Basically, the sprites (e.g. stamps) are drawn too small, and not 64x64 pixels like they should be.
I am 95% sure that the following code is where the problem lies:
const char* stampObstacleFragmentShader = R"(
#version 330 core
uniform sampler2D obstacleTexture;
uniform sampler2D stampTexture;
uniform vec2 position;
uniform vec2 stampSize;
uniform float threshold;
out float FragColor;
in vec2 TexCoord;
void main()
{
// Get current obstacle value
float obstacle = texture(obstacleTexture, TexCoord).r;
// Calculate coordinates in stamp texture
vec2 stamp_size = textureSize(stampTexture, 0)/2.0;
vec2 stampCoord = (TexCoord - position) * textureSize(obstacleTexture, 0) / stamp_size + vec2(0.5);
vec2 adjustedCoord = stampCoord;
ivec2 obstacle_tex_size = textureSize(obstacleTexture, 0);
float aspect_ratio = float(obstacle_tex_size.x) / float(obstacle_tex_size.y);
// For non-square textures, adjust sampling to prevent stretching
if (aspect_ratio > 1.0) {
adjustedCoord.x = (adjustedCoord.x - 0.5) / aspect_ratio + 0.5;
} else if (aspect_ratio < 1.0) {
adjustedCoord.y = (adjustedCoord.y - 0.5) * aspect_ratio + 0.5;
}
stampCoord = adjustedCoord;
// Keep the stamps square
if(aspect_ratio > 1.0)
stampCoord *= aspect_ratio;
else
stampCoord /= aspect_ratio;
// Why do I need to perform a scale?
// stampCoord /= 1.5;
// Check if we're within stamp bounds
if (stampCoord.x >= 0.0 && stampCoord.x <= 1.0 &&
stampCoord.y >= 0.0 && stampCoord.y <= 1.0) {
// Sample stamp texture (use first channel for grayscale images)
//float stampValue = length(texture(stampTexture, stampCoord).rgb) / 1.732; // Normalize RGB length
float stampValue = texture(stampTexture, stampCoord).a;
// Apply threshold to make it binary
stampValue = stampValue > threshold ? 1.0 : 0.0;
// Combine with existing obstacle (using max for union)
obstacle = max(obstacle, stampValue);
}
FragColor = obstacle;
}
)";
Can you see why it doesn't render the sprite as 64x64?
taby said: The next step is to add force and colour to the fields based on the location and velocity of each bullet.
How do you represent color, and how does it change by what?
And, seems you have an idea to make a fluid game. I guess those stars should bounce off each other, and the fluid makes them flow and rotate. That's intersting. I have not seen such game yet.
But there is a problem, and i can tell what things i have tried related to that. The problem is the boundary of the simualtion. It kills everything intersting, and damps all the fluidy swirls of our imagination away, leaving us with boring nothingness. We have two options here. A static boundary, like the edge of a swimming pool. Ther are no tornados or river flows in those pools because of that. Boring. Much better is a ‘periodic’ baundary. (There are proper terms for those boundary conditions, but i have forgot them : ) I mean, what flows out left, comes back from the right. So you can make a stream actually. But the stream can only have one global direction, which is still boring. So i tried to add external force, actually using a procedural vector field. It's easy to create such field from sines, or tiling noise function, etc. You can see the low frequency sines in those images, now that you know:
There is the intersting high frequency details from the fluid simualtion, but there is also artistic control of the flow on the low frequencies. That's pretty promising for my goal of procedural generation, but i guess you could do similar things to design fluidy levels for a game, eventually, beside obvious ideas such as sources and sinks, explosions, etc.