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
115 lines
4.2 KiB
C++
115 lines
4.2 KiB
C++
#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;
|
|
};
|