I’m working on a highly optimized custom ECS / Data-Oriented flocking framework in C++ capable of running 150,000+ boids. To render this, I am calculating all the math in C++ memory arenas and pushing the data to Niagara via a DMA Texture Bridge (UpdateTextureRegions).
The Setup: In Niagara, I use a Sample Texture module (reading UVs via ExecutionIndex) to grab the exact 3D world coordinate for each boid, and then I use a Set Parameters module to directly set Particles.Position = SampledPosition. This effectively teleports the particles every frame. The translation works flawlessly and runs incredibly fast.
The Problem: I am trying to get the meshes to orient to their direction of travel. Because I am manually teleporting them and bypassing Solve Forces and Velocity, Niagara doesn’t automatically generate a velocity vector for them.
What I’ve Tried: I tried to calculate the velocity manually by creating a frame-delay line using a custom Particles.OldPos vector. The logic was:
- Cache the old position (
Particles.OldPos = Particles.Position). - Sample the new texture position (
Particles.Position = SampledTexture). - Calculate the flight path (
Facing Direction = Position - OldPos).
The Result: The boids spawn and seem to rotate initially, but then they become permanently locked in that rotation. Depending on where I place the OldPos module in the stack, they either lock facing radially outward from the center of the map, or directly inwards. They do not actively update their rotation as they fly around.
My Question: Is there a specific way this needs to be ordered in the Niagara stack, or a specific namespace (Previous) that must be used? My theory is that the Niagara HLSL compiler might be aggressively reordering the modules or optimizing out the OldPos variable into a same-frame alias of Position, causing the delta math to break or evaluate to (0,0,0).
Does anyone know the proper, standard way to calculate finite difference (Position - PreviousPosition) for manually teleported particles in Niagara? I know the foolproof solution is to just generate a second Velocity Texture in C++ and pass it in, but I am trying to keep things as hyper-optimized as possible and avoid the extra PCIe/VRAM overhead if Niagara is capable of just calculating the math natively on the GPU.
Thanks in advance!
"I found a previous thread suggesting to store the Last Position and Current Position to calculate Velocity (link), but when I try to implement this using Set Parameter modules, the Niagara compiler seems to alias the variables. The math evaluates to exactly (0,0,0), causing the Orientation module to lock to its default fallback rotation. - How can I manually calculate Particle Velocity in Niagara - #2 by BRS_GerDAngelo