Introduction
TerraDyne is a **next-generation native plugin for Unreal Engine 5.7**. It decouples landscape physics from visual representation, enabling infinite runtime deformation — digging, raising, and painting — without sacrificing cinematic visual fidelity.
The Scaling Problem
Legacy voxel systems are CPU heavy. Standard landscapes are static. Runtime mesh manipulation often leads to collision desyncs and visual seams.
TerraDyne solves this via **Synchronous Physics / Asynchronous Visuals**. We use a CPU-side float cache to drive instantaneous collision updates (Geometry Script) while simultaneously pushing R16f textures to the GPU for Virtual Heightfield Mesh displacement.
Technical Pillars
Built strictly for UE 5.7, TerraDyne leverages modern modules like Geometry Scripting and Virtual Texturing to achieve AAA performance.
Direct Memory Write
Bypasses slow material draws using low-level memory locking (`LOCK_READ_WRITE`) to push float arrays directly to R16f textures.
Lidar Import
Ignores Landscape API fragility. Uses a multi-channel raycast sweeper to physically scan your level geometry and "bake" it into dynamic chunks.
Self-Healing Sandbox
Chunks detect cache corruption or empty states and auto-regenerate collision meshes on BeginPlay to prevent physics tunneling.
Quick Start Guide (Developers)
1. Include the Module
TerraDyne relies on core geometry modules. Ensure your Build.cs includes:
PublicDependencyModuleNames.AddRange(new string[] {
"TerraDyne", "GeometryScripting", "VirtualHeightfieldMesh"
});
2. Access the Manager
The ATerraDyneManager is a singleton-style actor registered to the world subsystem. You do not need to scan for it.
// Get the Subsystem first
if (UTerraDyneSubsystem* Sys = GetWorld()->GetSubsystem())
{
// Retrieve the active Manager
if (ATerraDyneManager* Manager = Sys->GetTerrainManager())
{
// 3. Dig a hole (Radius 500, Strength -100)
Manager->ApplyGlobalBrush(HitLocation, 500.0f, -100.0f, false);
}
}
3. Configure a Projectile
For fast-moving objects (meteors, grenades) to interact with dynamic terrain, enable CCD and bind to the Hit event.
MeshComp->BodyInstance.bUseCCD = true; // Prevents Tunneling
MeshComp->OnComponentHit.AddDynamic(this, &ATerraDyneProjectile::OnHit);
Sandbox Mode (Designers)
The **"Safe Mode" Sandbox** allows designers to test deformation mechanics instantly, without importing a massive landscape.
1. Spawn Manager
Drag BP_TerraDyneManager into an empty level at 0,0,0.
2. Auto-Gen
Press Play. The Manager detects no landscape and spawns a **1km Procedural Perlin Noise** arena.
3. Drop Paver
Drag a TerraDynePaver actor into the sky. It will land, trace the surface, and begin carving.
Important Note on Scale
"In Safe Mode, TerraDyne uses a fixed Z-Scale of **1.0**. Ensure your Visual Material (VHFM) is also set to Z-Scale 1.0, otherwise visual displacement will desync from the physics collider, causing 'floating' or 'sinking' character bugs."
Using TerraDyne
This guide covers the operational workflows for TerraDyne: setting up the Manager, importing worlds via Lidar, and utilizing the runtime brush API for gameplay mechanics.
1 Manager Configuration
The BP_TerraDyneManager is the central orchestrator. It must be placed in the level (usually at 0,0,0) to manage chunk streaming and tool distribution.
Required Property Assignments
- Chunk Class:
BP_TerraDyneChunk(The tile actor) - Master Material:
M_TerraDyne_Master(VHFM Visuals) - Height Brush Material:
M_HeightBrush(Additive Shape) - Weight Brush Material:
M_WeightBrush(Layer Masking)
2 Importing via Lidar Scan
TerraDyne's "Nuclear Option" importer bypasses Unreal's internal landscape data structures. Instead, it fires thousands of vertical raycasts ("Lidar") to physically map the surface of your existing landscape.
The Process
- Select
BP_TerraDyneManagerin your level. - Assign your Landscape Actor to Target Landscape Source.
- (Optional) Set Global Chunk Size.
50000.0(500m) is standard. - Click Manual Import (under Tools).
3 Runtime Brush API
Unlike static meshes, TerraDyne expects gameplay actors to "Draw" into the world. Use ApplyGlobalBrush to deform terrain or paint layers.
// 1. Sculpting (Physics)
Deforms geometry. Physics updates automatically via Async Cook.
Manager->ApplyGlobalBrush(
HitLocation, // Center
500.0f, // Radius
-50.0f, // Strength (Neg=Dig)
false, // bIsHole
-1 // Layer (-1 = Sculpt)
);
// 2. Painting (Visuals)
Updates RGBA Weightmap. Does not affect collision.
Manager->ApplyGlobalBrush(
HitLocation,
500.0f, // Radius
1.0f, // Opacity
false,
1 // Layer ID (0=R, 1=G...)
);
How the Brush Works
4 Layer & Material Setup
TerraDyne uses a single master material (M_TerraDyne_Master). Layers are not defined by "Landscape Layer Info" objects but by raw RGBA channels in the WeightMap render target.
| Channel | Usage | Example Effect |
|---|---|---|
| Red (0) | Primary Splat | Dirt / Mud |
| Green (1) | Secondary Splat | Grass / Magma (Demo) |
| Blue (2) | Tertiary Splat | Snow / Charred Earth |
| Alpha (3) | Wetness / Special | Puddles mask |
Technical Reference (C++)
This section details the internal architecture, class hierarchy, and low-level data flow of the TerraDyne Runtime module.
System Architecture
The Manager resides in the persistent level (via Subsystem). Chunks are spatially loaded actors that contain their own physics (DynamicMesh) and rendering (VHFM) components.
ATerraDyneManager
Singleton-like actor responsible for routing "Brush" events to the correct world grid cells.
| Function Signature | Description |
|---|---|
| ApplyGlobalBrush(...) |
Calculates which grid cells overlap the brush radius and forwards the call.
Params: Location, Radius, Strength, bIsHole, PaintLayer |
| RebuildChunkMap() |
Scans the world for ATerraDyneChunk actors and rebuilds the TMap<int64, Chunk*> spatial hash. Call this if you spawn chunks manually at runtime.
|
| PerformLidarResample(...) | High Cost. Fires vertical raycasts (Multi-Channel) to capture the geometry of a source actor/landscape into the TerraDyne caches. |
ATerraDyneChunk
The atomic unit of terrain. Handles the sync between CPU float arrays, GPU textures, and Physics collision.
Critical: Z-Scale Logic
Data is stored in the HeightCache as raw World Z units (Float). However, for rendering, textures are 0-1 normalized. The system uses a fixed ZScale (e.g. 5000.0) where CacheValue / ZScale = TextureValue.
| Variable / Function | Description |
|---|---|
| TArray<float> HeightCache | The "Source of Truth". A simple 1D float array representing the Z-height of every vertex in the grid. |
| UpdateVisualTexture() |
Creates a transient UTexture2D, locks the Mip 0 memory, NeedMemcpy the HeightCache into it, calls UpdateResource(), and draws it to the VHFM Render Target via RHI.
|
| SyncPhysicsGeometry() |
Uses GeometryScript::EditMesh to iterate vertices and snap Z to the HeightCache values. This is thread-safe on the Game Thread.
|
Threading & Performance Model
Physics (Async Cook)
TerraDyne sets PhysicsMesh->UpdateCollision(true). This pushes the PhysX/Chaos geometry cooking to a background thread.
Visuals (RHI Command)
Texture updates use ENQUEUE_RENDER_COMMAND (implicit via UpdateResource) to upload float data. This avoids Game Thread stalls during frame rendering.
The Chunk System
The ATerraDyneChunk is the atomic unit of the landscape. It is a Spatially Loaded actor designed to stream in and out via World Partition, managing its own local physics state and visual representation autonomously.
Chunk Architecture Stack
Component Anatomy
-
HeightCache (Data)
A 1D
TArray<float>that serves as the "Source of Truth". It stores normalized values (0.0 to 1.0). All gameplay logic reads/writes to this array first. -
PhysicsMesh (Collision)
A
UDynamicMeshComponentusing "Complex as Simple" collision. It is invisible in-game. On update, vertices are snapped to match the HeightCache. -
VisualMesh (Rendering)
A
UVirtualHeightfieldMeshComponent. It does not contain geometry; instead, it renders a view-dependent grid displaced by theHeightRTtexture.
The Update Cycle
When ApplyLocalIdempotentEdit is called (via a brush or projectile):
Anti-Tunneling (CCD)
Because the dynamic mesh is a "thin sheet," fast-moving objects might pass through during frame updates. TerraDyne enables Continuous Collision Detection (CCD) and synchronous cooking during initialization to prevent this.
Empty-State "Self-Healing"
A critical feature of TerraDyne is robustness against uninitialized data. If a designer drags a Chunk Blueprint into the world manually (bypassing the Manager's import logic), the Chunk detects the specific HeightCache.Num() == 0 state in BeginPlay().
// TerraDyneChunk.cpp -> BeginPlay logic
if (HeightCache.Num() == 0 && !LinkedTileData)
{
// 1. initialize Flat Array (0.0f)
InitializeChunk(GridCoordinate, 10000.0f, 128, nullptr);
// 2. Force Physics Generation immediately
RebuildPhysicsMesh();
// 3. Force Visuals to match
UpdateVisualTexture();
UE_LOG(LogTemp, Warning, TEXT("TerraDyneChunk: Self-Initialized empty bucket."));
}
This guarantees that projectiles will never bounce off an undefined collision shell or pass through a "ghost" chunk.
Troubleshooting Guide
Common integration patterns, rendering artifacts, and physics desynchronization issues—and how to solve them programmatically.
Physics & Interaction
Diagnosis
The Dynamic Mesh is a "thin sheet" (2D surface). Fast-moving actors update their position past the mesh logic in a single frame tick.
The Cure
Enable Continuous Collision Detection (CCD) on your projectile actor.
MeshComp->BodyInstance.bUseCCD = true;
Checklist
- Ensure Manager is placed in the level.
- Ensure Tool Materials (
M_HeightBrush,M_WeightBrush) are assigned in the Manager details panel. - Check if chunk initialized via the "Self-Healing" log in Output: LogTerraDyne: Self-Initialized empty bucket.
Rendering & Visuals
Diagnosis
TerraDyne C++ logic works in Raw World Units. If your material applies a ZScale multiplier (e.g. 100x), a 5000 unit hill becomes 500,000 units high.
The Cure
Set Material ZScale to 1.0.
// TerraDyneChunk.cpp sets this automatically
MID->SetScalarParameterValue(TEXT("ZScale"), 1.0f);
Diagnosis
You are using standard mesh normals. Because chunks are separate actors, vertex normals break at the edges.
The Cure
In M_TerraDyne_Master, perform 3-tap texture sampling to calculate normals from the global heightmap. Uncheck "Tangent Space Normal" in the material details.
Compilation & Editor
You are missing a module dependency in TerraDyne.Build.cs.
"GeometryCore"
"UMG", "Slate", "SlateCore"
Remember to regenerate project files after changing Build.cs.
The Cure
The Blueprint editor has desynchronized from the C++ header.
- Close Unreal Editor.
- Delete
Binaries/within the plugin folder. - Rebuild directly from Visual Studio.
- Open
BP_TerraDyneManager→ File → Reparent → Actor → Reparent → TerraDyneManager.
GregOrigin
Created by Andras Gregori @ Gregorigin, a single dad currently based outside Budapest, HU.
"Building tools that empower creators to shape worlds."