Get it on Fab→

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.

CPU CACHE GPU HEIGHT_RT MANAGER

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)
MANAGER BP MATERIALS CHUNK BP

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

  1. Select BP_TerraDyneManager in your level.
  2. Assign your Landscape Actor to Target Landscape Source.
  3. (Optional) Set Global Chunk Size. 50000.0 (500m) is standard.
  4. Click Manual Import (under Tools).
Warning: Ensure your Source Landscape has collision enabled (BlockAll) or the rays will pass through, resulting in flat chunks.
SOURCE

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

Radius
Cosine Falloff

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

UTerraDyneSubsystem ATerraDyneManager Orchestrator ATerraDyneChunk (0,0) ATerraDyneChunk (1,0) Streamed Out...

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.

Non-Blocking

Visuals (RHI Command)

Texture updates use ENQUEUE_RENDER_COMMAND (implicit via UpdateResource) to upload float data. This avoids Game Thread stalls during frame rendering.

Render Thread

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

CPU HEIGHT CACHE (TArray) DYNAMIC MESH (Collision) VHFM (Render Target)

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 UDynamicMeshComponent using "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 the HeightRT texture.

The Update Cycle

When ApplyLocalIdempotentEdit is called (via a brush or projectile):

1. CPU Math HeightCache[i] += Strength * Falloff
2. GPU Upload Memcpy Array → Texture2D → RenderTarget
3. Physics Sync Mesh.SetVertex(i, Z) → UpdateCollision(true)

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

SYMPTOM: Projectiles fall through the ground Error: Tunneling

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;
SYMPTOM: Orbs bounce without making craters Error: Logic Failure

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

SYMPTOM: Terrain has huge spiked distortions ("Grotesque") Error: Double Scaling

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);
SYMPTOM: Visible seams/cracks between Chunks Error: Normal Context

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

SYMPTOM: "Unresolved External Symbol" / LNK2019 Error: Linker Failure

You are missing a module dependency in TerraDyne.Build.cs.

Missing FDynamicMesh3? Add "GeometryCore"
Missing UUserWidget? Add "UMG", "Slate", "SlateCore"

Remember to regenerate project files after changing Build.cs.

SYMPTOM: "Import Landscape" button disappeared Error: Hot Reload Corruption

The Cure

The Blueprint editor has desynchronized from the C++ header.

  1. Close Unreal Editor.
  2. Delete Binaries/ within the plugin folder.
  3. Rebuild directly from Visual Studio.
  4. Open BP_TerraDyneManager → File → Reparent → Actor → Reparent → TerraDyneManager.
Budapest, HU

GregOrigin

Created by Andras Gregori @ Gregorigin, a single dad currently based outside Budapest, HU.

"Building tools that empower creators to shape worlds."