#pragma once #include "CoreMinimal.h" #include "Core/DescentMath.h" #include "World/DescentWorldTypes.h" namespace Descent::World { class FDescentWorldDef; /** * The primitives that can affect one region, gathered from the world definition's bucket grid (port of v2 * `gatherLists`, same margins). Pointers into the definition's arrays, which never move after Build(), so a list * is valid for the life of the process and may be used from any thread (each thread builds its own). */ struct FPrimList { TArray> Segs; TArray> Chambers; TArray> Wells; TArray> Pores; TArray> Shelves; TArray> Pinches; TArray> Rings; TArray> Sponges; bool bCanyon = false; bool bAbyss = false; void Reset() { Segs.Reset(); Chambers.Reset(); Wells.Reset(); Pores.Reset(); Shelves.Reset(); Pinches.Reset(); Rings.Reset(); Sponges.Reset(); bCanyon = false; bAbyss = false; } }; /** * The density function of the world (DESIGN_UE.md section 2.2): signed, positive = rock, negative = water, * approximately metres near surfaces. Immutable and thread-safe: it reads the definition and two noise tables. */ class THEDESCENT_API FDescentSDF { public: explicit FDescentSDF(const FDescentWorldDef& InDef); /** Unclamped density. */ double GenRaw(const FVector& Pm, const FPrimList& L) const; /** Clamped to +-ClampD: what chunks store. */ double Gen(const FVector& Pm, const FPrimList& L) const; /** Gathers the primitives for a sphere (Centre, Radius). */ void Gather(const FVector& Centre, double Radius, FPrimList& Out) const; /** Density with a one-off gather: slow, for tests and build-time marches. */ double GenRawSlow(const FVector& Pm) const; /** Gradient of GenRaw (points into rock), normalised; (0,0,1) if degenerate. */ FVector GradientRaw(const FVector& Pm, const FPrimList& L, double H = 0.5) const; const Descent::FNoise3& Noise3() const { return NoiseA; } const Descent::FNoise3& NoiseB() const { return NoiseBObj; } // ---- the primitive distance functions (public so tests and the slice tool can use them) ---- static double SegDistR(const FSegment& S, double X, double Y, double Z); static double SegDist3(const FVector& A, const FVector& B, double X, double Y, double Z); static double WellSDF(const FWell& W, double X, double Y, double Z); static double SlotSDF(const FShelf& S, double X, double Y); static double ShelfRock(const FShelf& S, double X, double Y, double Z, double Noise); static double PinchRock(const FPinch& P, double X, double Y, double Z); static double RingRock(const FRing& R, const FWell& W, double X, double Y, double Z); static double AbyssSDF(double X, double Y, double Z); static double BoxSDF(const FBox& B, double X, double Y, double Z); static double CanyonCentre(double X); // Yc(X) static double CanyonFloor(double X); // Zf(X) /** The sponge's own field (masked near carrier routes), exposed for the NoBypass test. */ double SpongeSDF(const FSpongeZone& Z, double X, double Y, double Z2) const; private: const FDescentWorldDef& Def; Descent::FNoise3 NoiseA; Descent::FNoise3 NoiseBObj; }; }