GregOrigin - TerraDyne: Unified Runtime Terrain Sculpting with Real-Time Physics

Watch a promo video: v0.1

Update video: v0.2

Read the manual

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.

Big update today, but still considered 0.1.0:

Core Terrain

  • Procedural terrain grid via ATerraDyneManager spawning ATerraDyneChunk actors with DynamicMeshComponents and FBM noise
  • Distance-based LOD (disables collision beyond 500m)
  • Collision debouncing (0.2s)

Sculpting

  • 5 brush modes: Raise, Lower, Smooth, Flatten, Paint
  • Multi-layer sculpting (Base / Sculpt / Detail)
  • 4 weight paint channels (RGBA8)
  • GPU brush path loaded but falls back to CPU (compute kernel not implemented)

Undo/Redo

  • Per-player stroke stacks via BeginStroke/CommitStroke
  • Full Undo/Redo with UI buttons and keyboard bindings

Grass/Foliage

  • FTerraDyneGrassSystem — async background thread worker
  • Samples height + weight buffers, outputs HISM transforms on game thread
  • Profile-driven (mesh, density, slope, scale, weight-layer filtering)

Persistence

  • Binary save/load with ZLib compression and magic header (0x5444594E)
  • Stores sculpt + weight data per chunk

Multiplayer

  • Server-authoritative brush via RPCs (Server_ApplyBrush, Server_CommitStroke)
  • Multicast_ApplyBrush for visual sync to all clients
  • Client_ReceiveChunkSync for late-join data push
  • Player stack cleanup on disconnect

Editor Tools

  • Landscape-to-TerraDyne baker
  • Slate toolbar (STerraDynePanel) with all 5 brush modes + paint layer picker
  • Runtime UMG widget (UTerraDyneToolWidget)

Automated Showcase (please see DEMO.md)

  • 63-second 10-phase directed tour demonstrating every feature above
  • Data-driven phase configs, camera waypoint system, on-screen overlay
  • Hands control to the player at the end

Update 0.1.1:

TerraDyne Changelog — March 7-8, 2026

March 8 — Assessment & Issue Resolution

5 identified issues resolved, 6 additional defects fixed from code review.

GPU Brush Path — Enabled (TerraDyneChunk.h/cpp)

  • 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

IsLocationTraceable — Implemented (TerraDyneCollision.cpp)

  • 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)
  • Fixed GetValue() call to pass int32* pointer (template requires subscriptable type)

Weight Baking — Enabled (TerraDyneBaker.cpp)

  • Was disabled due to WeightmapTextures being private in UE 5.6
  • Reimplemented using FLandscapeEditDataInterface::GetWeightDataFast() — the public editor API
  • Iterates GetWeightmapLayerAllocations() for up to 4 layer infos, reads per-layer weights, maps into RGBA channels
  • Added Foliage module dependency to TerraDyneEditor.Build.cs (required by LandscapeEdit.h)
  • Replaced magic number 128.0f with LandscapeHeightScale named constant
  • Removed RF_MarkAsRootSet from BakeComponent — was pinning baked assets to root set, causing permanent memory leak for the editor session

Multi-Player Streaming — Implemented (TerraDyneManager.h/cpp)

  • 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
    • ChunkUnloadRadius (2-25, default 7) — hysteresis unload boundary
    • 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

Files Changed

Date File Change
Mar 8 TerraDyneCollision.cpp IsLocationTraceable implemented
Mar 8 TerraDyneChunk.h Added HeightRT_Swap UPROPERTY
Mar 8 TerraDyneChunk.cpp GPU ping-pong, RT stride fix, init fix
Mar 8 TerraDyneManager.h Multi-player streaming sigs, field inits
Mar 8 TerraDyneManager.cpp Multi-player Tick/streaming, materials guard
Mar 8 TerraDyneBaker.cpp Weight extraction, magic number, GC fix
Mar 8 TerraDyneEditor.Build.cs Added Foliage dependency
Mar 7 TerraDyneSettings.h Streaming, undo, multiplayer settings
Mar 7 TerraDyneSettings.cpp Defaults for new settings, path fix
Mar 7 TerraDyneOrchestrator.cpp Removed MWAM MaterialOverride

Changelog

[0.3.0] - 2026-05-05

Added

  • Notification System: New UTerraDyneSubsystem::ShowNotification() with Info/Warning/Error severities, Slate toasts in editor, on-screen messages at runtime, and a bShowRuntimeNotifications setting for users to opt out at runtime.
  • Brush Preview Decal: ATerraDyneEditController now projects a live decal at the cursor showing the brush radius and tool mode (blue for sculpt, orange for paint).
  • Chunk Debug Overlay: New bShowDebugOverlay on the manager, color-coded by chunk state (green=loaded, yellow=loading, red=unloading, cyan=imported), wired to a toggle button on STerraDynePanel.
  • Marketplace Documentation: Docs/QuickStart.md, Docs/API.md, and Docs/ExampleBlueprints.md now ship with the plugin.
  • Asset Automation Scripts: Content/Scripts/CreateBrushPreviewMaterial.py, CreateDemoMap.py, and CreateExampleBlueprints.py create the supporting assets without manual editor work.

Changed

  • Error Reporting: Critical failure points (material load, chunk spawn, save/load, landscape import, cache write, buffer mismatch) now surface to the user via ShowNotification instead of log-only.
  • Input Validation: ATerraDyneEditController validates the TerraDyneClick input action at startup and shows an actionable error toast if missing.
  • Logging Hygiene: All LogTemp calls replaced with LogTerraDyne (runtime) or LogTerraDyneEditor (editor) — zero LogTemp references remain in plugin source.
  • Settings Tooltips: Every UTerraDyneSettings property in the Asset Paths and Defaults categories now exposes a tooltip in the project settings details panel.
  • Setup Wizard: Initialize World now dismisses its tab on success so it can’t be re-clicked indefinitely.
  • Platform Support: .uplugin runtime module now lists Linux and Mac alongside Win64 (editor module remains Win64-only).

Fixed

  • Setup Wizard: Initialize World button now dismisses the wizard tab on success — previously it could be re-clicked indefinitely.
  • Runtime Grass Blade Mesh: Material slot is now registered before BuildFromStaticMeshDescriptions, fixing a UVChannelData.bInitialized ensure() that fired during showcase startup.

Verified

  • Automation: Automation RunTests TerraDyne passes with 21/21 TerraDyne tests.

[0.2.0] - 2026-04-05

Added

  • Authored World Conversion: GitHub now includes the verified landscape migration path with paint layer capture, placed foliage transfer, and actor foliage transfer.
  • Persistent Runtime World: Terrain save/load, replicated chunk sync, persistent runtime actors, destruction state, harvest/regrowth flow, and runtime placement are part of the public release.
  • Procedural Extension: Seeded outskirts, biome overlays, runtime spawn rules, optional edge growth, and PCG-ready point export are now included.
  • Gameplay Hooks: Blueprint-callable biome queries, AI spawn zones, build-permission checks, navmesh dirtying, and terrain/foliage/population change events are exposed from the manager.
  • Designer Workflow: World presets, scene templates, showcase automation, and packaged docs now ship with the plugin.

Changed

  • Public Positioning: TerraDyne 0.3 is now presented as a persistent runtime world framework for survival, sandbox, and open-world games built on Unreal Landscapes.

0.3.1 update, 2026-05-16

Added

  • Play Mode Tool UI Switch: Added Enable Play Mode Tool UI on ATerraDyneEditController so gameplay maps can disable the TerraDyne runtime widget safely.

Changed

  • Edit Controller Safety: Disabling the play mode tool UI now removes the TerraDyne widget, restores GameOnly input, hides the cursor and brush decal, and blocks sculpt/undo/redo and related edit RPC paths so invisible edit mode cannot remain active during play.

Fixed

  • Landscape Conversion Coverage: Authored World Conversion now resolves the full landscape through ULandscapeInfo and imports all loaded landscape components instead of only the selected ALandscapeProxy, which fixes partial or empty imports on partitioned landscapes and streaming-proxy setups.

  • Landscape Import Diagnostics: Conversion now surfaces explicit user-facing errors when the selected landscape has no loaded components or when no TerraDyne chunks were created from the import.

  • Landscape Material Adoption Safety: Adopt Landscape Material As Master Material now refuses landscape materials that use landscape-only material expressions and keeps the existing TerraDyne chunk material instead of applying an incompatible graph.

  • Source Landscape Hiding: Hide Source Landscape now hides the full landscape proxy hierarchy instead of only the selected source actor.

  • Fab Startup Crash: TerraDyne now verifies the plugin shader directory before registering /Plugin/TerraDyne, preventing the RenderCore DirectoryExists assertion when a packaged install is missing Shaders/.

  • Marketplace Packaging Gate: Added a release validator and automation coverage for Shaders/TerraDyneSimulation.usf so the required RDG shader payload cannot be omitted silently.

0.4.0 Update: 2026-05-19 :globe_showing_asia_australia:

Added

  • Play Mode Tool UI Switch: Added Enable Play Mode Tool UI on ATerraDyneEditController so gameplay maps can disable the TerraDyne runtime widget safely.
  • PIE Controller Automation: Added a dedicated PIE automation harness that boots a real ATerraDyneEditController with bEnablePlayModeToolUI=false and asserts the no-UI branch end to end.
  • External QA Landscape Import Prep: Added repo-root tools/ImportFreeLandscape.ps1, tools/import_free_landscape.py, and tools/ImportFreeLandscape.md to fetch the latest public-domain USGS 3DEP 1/3-arc-second DEM for a curated profile, crop it, resample it to a UE-friendly resolution, export a 16-bit heightmap, and emit a manifest with exact Landscape import scales and source traceability.
  • Versioned Real-Map Fixture Output: External landscape prep now writes a reproducible package under Saved/ExternalLandscapes/... containing the cached source GeoTIFF, a preview image, the processed heightmap, raw TNMAccess metadata, and the Unreal import manifest so authored-world conversion QA can use real terrain without shipping source maps in the plugin payload.

Changed

  • Edit Controller Safety: Disabling the play mode tool UI now removes the TerraDyne widget, restores GameOnly input, hides the cursor and brush decal, and blocks sculpt/undo/redo and related edit RPC paths so invisible edit mode cannot remain active during play.
  • Chunk Material Initialization: ATerraDyneManager now assigns BrushMaterialBase when registering chunks and when applying materials to active chunks, allowing chunks created before manager material initialization to participate in the GPU brush path once compatible materials are available.
  • External Import Source Selection: The free-landscape import tool now selects only TNMAccess products that fully contain the configured crop bounds, retries transient Windows file-lock failures when finalizing downloads, and records the exact USGS tile revision used for each generated fixture.

Fixed

  • Disabled-UI Input Leak: ATerraDyneEditController no longer validates or binds TerraDyne edit input actions when play mode tool UI is disabled, so gameplay-mode PIE sessions do not emit false missing-input errors.
  • Showcase Population Mobility: TerraDyne now normalizes persistent/runtime-placed actors to Movable before transform and mesh assignment, eliminating live showcase mobility warnings and SetStaticMesh-while-static warnings during runtime validation.
  • Population Mesh Binding Duplication: Removed the redundant SetStaticMesh application in the persistent population spawn path.
  • UE 5.7 GPU Terrain Startup: ATerraDyneChunk no longer seeds the height render target by writing render-target memory directly. Height data is now uploaded through a transient PF_FloatRGBA texture and blitted into the render target through UCanvas, matching UE 5.7 render-target lifecycle expectations and restoring the GPU-backed chunk path.
  • GPU Validation Readback: Height render-target validation now uses float readback plus vertical-orientation detection, preventing false GPU fallback caused by readback ordering differences during startup validation.
  • Transient Texture Naming Noise: Height-upload and weightmap helper textures now use unique transient object names, removing generic overwrite warnings during repeated editor/runtime validation.
  • Landscape Conversion Coverage: Authored World Conversion now resolves the full landscape through ULandscapeInfo and imports all loaded landscape components instead of only the selected ALandscapeProxy, which fixes partial or empty imports on partitioned landscapes and streaming-proxy setups.
  • Landscape Import Diagnostics: Conversion now surfaces explicit user-facing errors when the selected landscape has no loaded components or when no TerraDyne chunks were created from the import.
  • Landscape Material Adoption Safety: Adopt Landscape Material As Master Material now refuses landscape materials that use landscape-only material expressions and keeps the existing TerraDyne chunk material instead of applying an incompatible graph.
  • Source Landscape Hiding: Hide Source Landscape now hides the full landscape proxy hierarchy instead of only the selected source actor.

Verified

  • Automation Coverage: Automation RunTests TerraDyne passes with 23/23 TerraDyne tests on UE 5.7 after the runtime warning cleanup and GPU terrain-path fixes.
  • Render Path Validation: TerraDyne.Rendering.MaterialBindings now reaches Chunk [0,0]: Ready (GPU: YES) instead of disabling GPU after render-target validation.
  • Live Showcase Smoke: /TerraDyne/Maps/DemoMap_Showcase now completes live smoke validation with zero render-target validation warnings, zero mobility warnings, and zero static-mesh mobility warnings while streamed chunks initialize on the GPU path.
  • Real-Map Fixture Generation: The external import tool successfully generated a Crater Lake package from USGS 1/3 Arc Second n43w123 20260202, including a 2017x2017 Unreal-ready heightmap and exact recommended import scales of X=886.9353, Y=881.6758, Z=229.7515 centimeters.