TerraDyne is a landscape plugin that unifies Runtime Terrain Sculpting with Real-Time Physics using a modern, unified architecture that moves away from legacy methods.
It started as an unassuming GitHub repo, and slowly built from there, with some help from the great OSS community. The 0.1 open-source version is still available there with 3 separate branches, but it only contains up to 15% of the present Fab version's functionality, which is being regularly updated.
UE 5.7 version is recommended over older versions: it's the version I develop on and the first to get updates.
TerraDyne currently features the following:
1. True Runtime Plasticity:
Unlike standard Unreal Landscapes, TerraDyne allows for real-time structural changes (sculpting, craters, tectonic shifts) during gameplay.
It successfully integrates a hybrid CPU/GPU pipeline, using Compute Shaders/Render Targets (HeightRT, SculptRT) for fast deformation and UDynamicMeshComponent for rendering.
2. "Live Takeover" Workflow:
The system can sample existing Landscape actors into its own data structures (16-bit precision), allowing developers to design in the editor and convert to TerraDyne for runtime interactivity without data loss.
3. Visual & Physical Parity:
It solves the complex problem of aligning visual meshes with physics collision at runtime.
4. Integrated Tooling:
It provides a standalone Runtime GUI (Slate-based STerraDynePanel) with GPU telemetry, proving it works as a "game-ready" tool, not just an editor plugin.
Includes a "Zero-Configuration" wizard (TerraDyneSceneSetup) for instant usability.
Root cause: single render target couldn't be read and written in the same Canvas draw pass
Added HeightRT_Swap for ping-pong rendering — material reads PrevHeight from current RT, draws to swap RT, pointers are swapped after each stroke
Added self-validation on startup: uploads CPU height data to RT, reads it back, spot-checks 16 samples (tolerance 0.01). Falls back to CPU automatically if validation fails or M_HeightBrush material is missing
Fixed RT upload to copy row-by-row respecting GPU row pitch (Stride from LockTexture2D), preventing corruption on GPUs with padded row alignment
Added FlushRenderingCommands() after RT upload in LoadFromData() to prevent stale pointer if chunk is immediately streamed out
Initialized HeightRT_Swap = nullptr in constructor for consistency
Was a non-functional stub that always returned true
Now performs grid-based triangle lookup: computes cell from mesh bounds and resolution, checks MaterialID on both triangles at that cell (non-zero = hole = not traceable)
Added mesh layout safety check: validates triangle count matches expected grid before using computed indices
Fixed const qualifier on GetMaterialID() return (const mesh in ProcessMesh lambda)
Was using GetFirstPlayerController() only — single player drove all chunk streaming
Tick() now iterates all player controllers via GetPlayerControllerIterator() to gather positions
UpdateStreaming() accepts TArray<FVector> and computes the union of all player load regions (diamond-shaped per player)
Chunks unload only when outside ALL players' unload radius (prevents one player unloading another's nearby chunks)
ProcessStreamingQueues() sorts by minimum Manhattan distance to nearest player (loads nearest first, unloads farthest first)
LOD updates use nearest player per chunk instead of first player globally
Replaced LastStreamingCenter (single FIntPoint) with LastStreamingHash (uint32 hash of all player chunk coords) for movement detection
Additional Defects Fixed
bMaterialsLoaded dead code (Manager.h/cpp): field was declared but never set. Now guards LoadMaterials() with early return and set to true at end, preventing redundant material loading on hot-reload
LODTimer first-frame fire (Manager.h): was initialized to 0.0f, causing streaming update with zero position on frame 1. Now initialized to 0.25f
GetWeightDataFast signature (Baker.cpp): was passing TArray<uint8> by value instead of pointer — would not compile
March 7 — Settings & Showcase Polish
Settings Expansion (TerraDyneSettings.h/cpp)
Corrected MasterMaterialPath default: removed VHFM/ subdirectory prefix from path
Added GrassDebounceTime setting (0.05s min, 0.5s default) — delay before regenerating grass after sculpt/paint edits
Added Streaming settings category:
ChunkLoadRadius (1-20, default 5) — diamond-shaped load region in chunk units
MaxChunkOpsPerTick (1-8, default 2) — throttles chunk spawn/teardown per tick
GridExtent (1-50, default 10) — half-width of world grid (-N..+N)
ChunkSaveDir (default TerraDyne/ChunkCache) — subdirectory for per-chunk cache files
Added Undo/Redo settings:
MaxUndoHistory (1-100, default 20) — max undo entries per player
Added Multiplayer settings:
MaxBrushRPCsPerSecond (1-120, default 30) — server-side rate limit per player
MaxBrushRadius (100+, default 10000) — max accepted brush radius
MaxBrushStrength (0+, default 5000) — max accepted brush strength
Grass Demo Fix (TerraDyneOrchestrator.cpp)
Removed MaterialOverride assignment from MWAM grass varieties in UpdateGrassDemo()
MWAM grass meshes (SMMWAM_GrassA-D) have their own embedded green materials — applying MTL_MWAMPlantsGrass as override was redundant and could conflict