experiment: LivingPlane repeatable + standalone UE runtime module (L164)
Readback barrier fix (GL_TEXTURE_UPDATE_BARRIER_BIT before glGetTexImage); check_repeatability.py compares full-field checksums across query/capture settings - Intel GPU PASS (3b9d45905c7aafff), llvmpipe PASS, mismatch control fails. New runtime module VoxelForgeLivingPlane (separate from generation): RDG global shader (Shaders/LivingPlane.usf), ping-pong RGBA32F render targets, actor with WPO plane and object inputs, no CPU readback. Compiles in Linux UE 5.7. Windows build: Source/VoxelForgeLivingPlane/*, Shaders/LivingPlane.usf, VoxelForge.uplugin. Setup: UE-SETUP.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
e1d87eed26
commit
3b71d35eba
13 files changed
+868
-8
No files matched your search
@@ -0,0 +1,55 @@
|
||||
# Living Plane in Unreal Engine 5.7
|
||||
|
||||
`VoxelForgeLivingPlane` is a separate runtime module. It does not call the VoxelForge terrain generators and it does not read the simulated texture back to the CPU. The actor owns two transient `RGBA32f` render targets and advances them with an RDG compute pass. The CPU sends object state and fixed-step uniforms only.
|
||||
|
||||
## Add it to a level
|
||||
|
||||
1. Enable the VoxelForge plugin and restart the editor so the `VoxelForgeLivingPlane` runtime module and its shader mapping load.
|
||||
2. Create a material named `M_LivingPlane` using the graph below.
|
||||
3. Place `VoxelForgeLivingPlaneActor` in the level. Assign `M_LivingPlane` to **Plane Material**.
|
||||
4. Keep the actor on the world XY plane with unit actor scale. `Plane Length Meters` controls the mesh width and simulation width; the built-in plane mesh is scaled to that size.
|
||||
5. The actor creates and clears its two GPU state targets on `BeginPlay`. The mesh has collision disabled; this surface is render-only and does not change VoxelForge density or collision queries.
|
||||
|
||||
## Material graph
|
||||
|
||||
Use a Surface, Opaque material. The actor binds these parameters on its dynamic material instance:
|
||||
|
||||
- `LivingField`: Texture Object Parameter. The texture is the current RGBA32f simulation state; **R** is displacement in metres.
|
||||
- `PlaneLengthMeters`: Scalar Parameter.
|
||||
- `FieldSize`: Scalar Parameter, equal to the render target width and height.
|
||||
|
||||
For World Position Offset:
|
||||
|
||||
1. Read `LivingField` at `TextureCoordinate[0]` with a Texture Sample.
|
||||
2. Multiply its **R** output by `100` to convert metres to Unreal centimetres.
|
||||
3. Append `(0, 0)` to that value, making `(0, 0, heightCm)`, and connect it to **World Position Offset**.
|
||||
|
||||
For the displaced normal, sample the height at four UV offsets using the same texture object:
|
||||
|
||||
- Left/right UV: `TexCoord + (-1/FieldSize, 0)` and `TexCoord + (1/FieldSize, 0)`.
|
||||
- Down/up UV: `TexCoord + (0, -1/FieldSize)` and `TexCoord + (0, 1/FieldSize)`.
|
||||
- `dHdx = (Right.R - Left.R) * FieldSize / (2 * PlaneLengthMeters)`.
|
||||
- `dHdy = (Up.R - Down.R) * FieldSize / (2 * PlaneLengthMeters)`.
|
||||
- Make and normalize the tangent-space vector `(-dHdx, -dHdy, 1)`, then connect it to **Normal**.
|
||||
|
||||
The built-in plane's U/V tangent directions align with local X/Y. Use a neutral base colour and roughness around `0.7` to start. The graph assumes a horizontal plane; add a local-to-world transform if the plane needs arbitrary rotation.
|
||||
|
||||
## Drive objects
|
||||
|
||||
`ObjectInputs` has two slots. Each `FVFLivingPlaneObjectInput` contains world position in centimetres, world velocity in centimetres per second, radius in centimetres, base push acceleration in metres per second squared, and an active flag. Blueprint can use **Set Object State** or set the array entry; C++ can call the same method. Set the object's center about one radius above the plane while it contacts it, and mark the slot inactive after lift-off.
|
||||
|
||||
The compute shader uses the radius to scale the Gaussian contact footprint. The original wave equation keeps the L163 wave speed, damping, foundation spring, travelling breathing force, and recovering imprint. The base push acceleration supplies the object force; lateral speed adds `0.25 m/s²` per `1 m/s`, and downward speed adds `0.5 m/s²` per `1 m/s`. Upward velocity does not add force. Object positions and velocities are transformed into the plane's local frame and converted from centimetres to metres before upload.
|
||||
|
||||
Each step uses `dt = 1.30 / sqrt(8*c*c/(dx*dx) + foundationStiffness)`, with `dx = PlaneLengthMeters / FieldSize`. Simulation time passed to breathing is the fixed step index times `dt`. Game-thread delta time only determines how many fixed steps are queued; no GPU field data is read back in the normal path.
|
||||
|
||||
## Build files
|
||||
|
||||
The owner's Windows build needs every added source and shader file:
|
||||
|
||||
- `Source/VoxelForgeLivingPlane/VoxelForgeLivingPlane.Build.cs`
|
||||
- `Source/VoxelForgeLivingPlane/Public/VoxelForgeLivingPlaneActor.h`
|
||||
- `Source/VoxelForgeLivingPlane/Private/VoxelForgeLivingPlaneActor.cpp`
|
||||
- `Source/VoxelForgeLivingPlane/Private/VoxelForgeLivingPlaneModule.cpp`
|
||||
- `Shaders/LivingPlane.usf`
|
||||
|
||||
The `VoxelForge.uplugin` runtime-module entry must ship with them.
|
||||
Reference in new issue
Block a user