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:
Fr0zkaandClaude Opus 5.5 committed 2026-11-02 11:10:43 +01:00
1 parent e1d87eed26
commit 3b71d35eba
13 files changed
+868 -8

No files matched your search

@@ -0,0 +1,114 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "VoxelForgeLivingPlaneActor.generated.h"
class UMaterialInterface;
class UMaterialInstanceDynamic;
class UStaticMeshComponent;
class UTextureRenderTarget2D;
USTRUCT(BlueprintType)
struct FVFLivingPlaneObjectInput
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Object")
FVector WorldPositionCm = FVector(0.0, 0.0, 43.0);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Object")
FVector WorldVelocityCmPerSecond = FVector::ZeroVector;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Object", meta=(ClampMin="0.1"))
float RadiusCm = 43.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Object", meta=(ClampMin="0.0"))
float PushAccelerationMetersPerSecondSquared = 36.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Object")
bool bActive = false;
};
/** A render-only, GPU-stepped heightfield plane. Object state is uploaded; the field is never read back. */
UCLASS(BlueprintType, Blueprintable)
class VOXELFORGELIVINGPLANE_API AVoxelForgeLivingPlaneActor : public AActor
{
GENERATED_BODY()
public:
AVoxelForgeLivingPlaneActor();
virtual void BeginPlay() override;
virtual void Tick(float DeltaSeconds) override;
virtual void OnConstruction(const FTransform& Transform) override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UFUNCTION(BlueprintCallable, Category="Living Plane|Object")
void SetObjectState(int32 ObjectIndex, const FVFLivingPlaneObjectInput& State);
UFUNCTION(BlueprintCallable, Category="Living Plane")
void ResetSimulation();
UFUNCTION(BlueprintPure, Category="Living Plane")
int64 GetSimulationStepCount() const { return SimulationStepCount; }
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Living Plane")
TObjectPtr<UStaticMeshComponent> PlaneMesh;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Living Plane|Rendering")
TObjectPtr<UMaterialInterface> PlaneMaterial;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Simulation", meta=(ClampMin="16", ClampMax="2048"))
int32 GridSize = 256;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Simulation", meta=(ClampMin="1.0", ClampMax="100.0"))
float PlaneLengthMeters = 12.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Simulation", meta=(ClampMin="0.0"))
float WaveSpeedMetersPerSecond = 4.5f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Simulation", meta=(ClampMin="0.0"))
float DampingPerSecond = 1.35f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Simulation", meta=(ClampMin="0.0"))
float FoundationStiffnessPerSecondSquared = 28.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Simulation")
bool bEnableBreathing = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Simulation", meta=(ClampMin="0.0", ClampMax="30.0"))
float BreathingStrengthMetersPerSecondSquared = 4.8f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Imprint")
bool bEnableImprint = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Imprint", meta=(ClampMin="0.0", ClampMax="1.0"))
float ImprintCaptureMetersPerSecond = 0.15f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Imprint", meta=(ClampMin="0.1", ClampMax="60.0"))
float ImprintRecoverySeconds = 6.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Imprint", meta=(ClampMin="0.0", ClampMax="1.0"))
float ImprintMaxDepthMeters = 0.34f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Living Plane|Object")
TArray<FVFLivingPlaneObjectInput> ObjectInputs;
private:
void CreateFieldTargets();
void EnqueueSimulationSteps(int32 NumSteps);
void UpdateMaterialField();
float GetStableStepSeconds() const;
UPROPERTY(Transient)
TObjectPtr<UTextureRenderTarget2D> FieldTargets[2];
UPROPERTY(Transient)
TObjectPtr<UMaterialInstanceDynamic> DynamicPlaneMaterial;
int32 CurrentFieldIndex = 0;
int64 SimulationStepCount = 0;
double SimulationAccumulatorSeconds = 0.0;
double FixedStepSeconds = 0.0;
};