reFlow Devlog #2 - moar suffering!!!!
round two. we are back. the wind tunnel is no longer just a 3d playground; it’s a quantum control deck. here is how i spent the last few hours losing sleep over perspective cameras, slice projection math, and custom state toggle controls.
hour 7: the flatland incursion (toggling 2d slice mode)
why turn a 3d tunnel back to 2d? because aerodynamics is all about cross-sections.
how to constrain a 3d particle swarm to 2d without a total refactor: when “is2D” is true, we hijack the particle spawner. instead of spreading particles across x-axis depth, we force positions[idx] = (Math.random() - 0.5) * 0.1, wrapping the flow into a super thin sheet at x = 0.
we also restricted the smoke line emitters to a vertical slice. the result looks like a clean laser-cut sheet of wind passing through the wing. (not really, but it’s the thought that counts)
hour 8: camera capture and control hijacking
particles are in 2d, but the camera still rotates in 3d. rotating it breaks the illusion (just a flat sheet of dots).
solution: camera hijacking.
when 2d mode triggers, we fly the camera to (38, 0, 0) looking down the x-axis at the wing. we disable OrbitControls rotation (controls.enableRotate = false) and force camera.up.set(0, 1, 0).
debugging this was a nightmare—OrbitControls flips the camera if target/up vectors aren’t updated first. now, swapping dimensions is smooth.
hour 9: freezing time (the pause engine)
sometimes wind moves too fast to see gradients. we need a freeze-frame.
added “isPaused” state.
first try: skip the animate loop. but that froze the rendering, stopping camera rotation.
second try: keep the rendering loop active, but bypass particle position updates.
now you can pause, orbit in 3d, and inspect smoke streams and vectors suspended in amber. sci-fi vibes.
hour 10: tuning the storm (particle customizability)
default 6,000 particles is cool, but my GPU was barely breaking a sweat.
expanded particle count up to 20,000 (step 1000) and added a size slider (0.1 to 1.5).
adjusting particle size from tiny dust specks to fat plasma spheres is satisfying. setting count to 20,000 makes the wind tunnel look like a dense gas nebula. the cpu is sweating, but performance is smooth thanks to typed buffer array updates.
hour 11: cyberpunk telemetry deck (hud overhaul)
with new toggleable states (2d/3d and pause/play), the dashboard needed a facelift.
built a cyber-cyan “Switch 2D” button with custom ambient glow shadows (shadow-[0_0_15px_rgba(125,249,255,0.15)]) and a yellow pause button changing to play icon.
added a footer telemetry status pill showing “2D Slice | [Active Object]” vs “3D Space | [Active Object]”. makes the app feel like a high-budget engineering interface.
hour 12: collision math strikes back (local normals)
particles clipped through spherical and box obstacles due to lazy velocity mirroring.
introduced a robust getNormalLocal(type, x, y, z) helper.
when a particle collides with a sphere, we calculate the exact radial normal: (x/r, y/r, z/r). for box geometry, the helper evaluates which face was breached (min === dx logic) and returns an axis normal.
now particles bounce and slide off physical bodies with realistic deflection vectors. no more ghosting.
next up: actually making the visuals work (someone send help)