#pragma once #include "CoreMinimal.h" #include "Core/DescentMath.h" #include "Core/Tuning/DescentTune_World.h" #include "World/DescentSDF.h" #include "World/DescentWorldTypes.h" /** * The world definition (DESIGN_UE.md sections 2.2 and 2.7 - 2.12): every SDF primitive, deposit, beacon and * landmark of the level, in metres, Unreal axes (+X down-canyon, +Y east, +Z up). Built once, deterministically, * from the fixed seeds of DescentTune_World.h, and immutable afterwards: any thread may read it. The ported v2 * code is 03_world.js; the axes are converted with (x, y, z)v2 -> (X = -z, Y = x, Z = y). * * `FDescentWorldDef::Get()` is the process-wide instance (built on first use, the boot step logs how long that took). * `FDescentWorldDef::BuildNew()` makes a second one for the determinism test. */ namespace Descent::World { class THEDESCENT_API FDescentWorldDef { public: /** The process-wide instance, built on first use (thread-safe). */ static const FDescentWorldDef& Get(); /** A fresh build. Deterministic: two builds have equal Hash(). */ static TSharedRef BuildNew(double* OutMs = nullptr); /** How long the process-wide build took, milliseconds (0 until Get() has run). */ static double GetBuildMs(); FDescentWorldDef(); FDescentWorldDef(const FDescentWorldDef&) = delete; FDescentWorldDef& operator=(const FDescentWorldDef&) = delete; // ---- the primitives (read-only after Build) ---- TArray Tubes; TArray Segs; TArray Chambers; // designed chambers first, then worm pockets int32 NumDesignedChambers = 0; TArray Wells; TArray Pores; TArray Shelves; TArray Pinches; TArray Rings; TArray Sponges; TArray Deposits; TArray Beacons; TArray Places; int32 NumWorms = 0; int32 NumPockets = 0; const FDescentSDF& Sdf() const { return SdfObj; } // ---- hash and counts for the `world_def` state section ---- uint64 Hash() const { return HashValue; } FString HashHex() const; // ---- helpers ---- int32 FindTube(FName Id) const; int32 FindWell(FName Id) const; int32 FindChamber(FName Id) const; const FPlace* FindPlace(FName Name) const; /** The gallery `Tube` at S metres from its start (clamped). */ FTubeSample TubeAtS(int32 Tube, double S) const; /** Distance to the nearest well or gallery surface (carrier route); 1e9 if none. Linear scan, for build-time and tests. */ double CarrierRouteDistance(const FVector& P) const; /** Signed distance to the claim box (positive outside). */ static double ClaimDist(const FVector& Pm); /** Drill hardness at P (section 2.5). */ static double HardnessAt(const FVector& Pm); /** Indices of deposits whose centre is within Radius + 8 m of Centre. */ void DepositsNear(const FVector& Centre, double Radius, TArray>& Out) const; /** Index of the deposit nearest to P, optionally of one ore type (-1 = any); -1 if there is none. */ int32 NearestDeposit(const FVector& P, int32 TypeFilter, double* OutDist = nullptr) const; /** The ore deposit under a point of space: capsule distance < R. Returns the deposit index and k = 1 - dist/R, or -1. */ int32 DepositAt(const FVector& P, TConstArrayView Candidates, double& OutK) const; // ---- spatial index (80 m buckets), used by FDescentSDF::Gather ---- void GatherInto(const FVector& Centre, double Radius, FPrimList& Out) const; private: friend class FDescentWorldBuilder; void Build(); void RebuildGrid(); void ComputeHash(); struct FGrid { TMap> Segs; TMap> Chambers; TMap> Pores; TMap> Deposits; }; FGrid Grid; TArray BigSegIndices; FDescentSDF SdfObj; uint64 HashValue = 0; }; }