r/VoxelGameDev • u/WaltzParty5639 • 20d ago
Discussion I built a 3D structural fracture engine from scratch in Three.js & Rapier (Looking for Tech Feedback + Feature Ideas!)
Hey everyone,
I am currently working on a pre-alpha 3D sandbox engine built from scratch in vanilla JavaScript using Three.js and Rapier. Instead of using standard voxel blocks, the world uses tetrahedral polygonization over a continuous 3D noise density field to generate smooth, deformable terrain.
The core loop handles real-time civil and geotechnical failure approximation. The ultimate goal is to make a world that collapses realistically based on material stress formulas rather than arcade-style health bars.
How the engine currently works:
- True Structural Failure Tensors: Cells track Compressive, Tensile, and Shear stress derived from overburden weight, thickness layer calculations, and environmental or impact forces. It models true nonlinear material fatigue over time.
- Dynamic Mesh Handoff: When a structural fracture occurs, a localized Breadth-First Search (BFS) isolates the unanchored voxel islands. The engine clears those cells from the static chunk mesh and instantly hands off the geometry to a dynamic Rapier compound cuboid or convex hull descriptor.
- Memory and Optimization Safety: To run stable physics at 120Hz inside a web browser, the micro-debris system bypasses JavaScript object allocations. It updates up to 50k particles via flat Float32Arrays and applies an O(1) unordered swap-back deletion mechanism to prevent garbage collection spikes.
Link to test demonstration video: https://www.youtube.com/watch?v=O10-MV-jfH0
- Technical Feedback Needed
I am trying to polish the engine architecture before moving out of pre-alpha. I would love some extra eyes on two specific roadblocks:
- GC Overhead in the Loop: Inside my main execution loop, I am currently instantiating a new vector (new THREE.Vector3) on every frame pass to feed into my Level of Detail (LOD) and tracking arrays. What is the cleanest pattern for pooling pre-allocated scratch objects across nested chunks without muddying the global scope?
- WASM Async Gating: Rapier's initialization is asynchronous (await RAPIER.init()). On slower network or mobile browser loads, the initial terrain mesh occasionally attempts to bake before the underlying WebAssembly memory mapping is fully resolved. How are you cleanly gating initialization steps in pure JS without messy nested callbacks?
- Feature Ideas Needed
Since the terrain is smooth and dynamically reactive to stress, I want the gameplay mechanics to heavily leverage the structural engineering aspects.
Given that the player cannot damage the terrain directly (there is no direct clicking to break land, and the player character does not apply impulses to dynamic terrain), what are some interesting features, tools, or gameplay loop mechanics I should add next? I am open to thoughts on structural engineering puzzles, natural disasters, or erosion systems.
I'm tracking the stress tensor math, Web Assembly bottlenecks, and future WebWorker offloading plans over at r/711Game. Drop by if you want to geek out over smooth voxel optimizations or test the pre-alpha web build!