#pragma once #include "CoreMinimal.h" class UProceduralMeshComponent; class UMaterialInterface; /** * The geometry builder of the carrier blockout (DESIGN_UE.md section 3.2). Pure data (no UObject), so the automation tests can * build every room and check the result without a world. Everything is in carrier-local METRES; Commit() converts to Unreal units. * Triangle winding is derived from the face normal you give, so no caller has to think about Unreal's clockwise convention. */ namespace Descent::Carrier { /** Blockout materials: instances /Game/Carrier/Materials/MI_Carrier_ made by Tools/ue/assets_35_carrier_materials.py. */ enum class EMat : uint8 { Wall, Wall2, Floor, Ceiling, Hazard, Wood, Fabric, White, Rust, Olive, Hull, DarkMetal, RustMetal, Shaft, LampWhite, LampCyan, LampAmber, LampRed, ScreenCyan, ScreenTeal, ScreenAmber, Core, Furnace, NavRed, NavGreen, NavWhite, WindowGlow, PoolWater, Count }; THEDESCENT_API const TCHAR* MatName(EMat M); /** Loads the material (cached); a missing asset logs one error naming the script and returns the engine's grid material. */ THEDESCENT_API UMaterialInterface* LoadCarrierMaterial(EMat M); struct FSectionData { EMat Mat = EMat::Wall; TArray V, N; TArray UV; TArray Tri; }; class THEDESCENT_API FMeshBuilder { public: TArray Sections; /** Convex collision hulls as point sets, metres. */ TArray> Convex; void Reset() { Sections.Reset(); Convex.Reset(); } FSectionData& Section(EMat M); /** A planar quad given counter-clockwise seen from the side the normal points to (right-handed). */ void Quad(EMat M, const FVector& A, const FVector& B, const FVector& C, const FVector& D, const FVector& N); void Tri(EMat M, const FVector& A, const FVector& B, const FVector& C, const FVector& N); /** Axis-aligned box (optionally rotated about its centre). bCollide adds one convex hull. */ void Box(EMat M, const FVector& Centre, const FVector& Size, bool bCollide = true, const FQuat& Rot = FQuat::Identity); void BoxMinMax(EMat M, const FVector& Min, const FVector& Max, bool bCollide = true) { Box(M, 0.5 * (Min + Max), Max - Min, bCollide); } /** Cylinder with the axis along Axis (0 = X, 1 = Y, 2 = Z), capped. */ void Cylinder(EMat M, const FVector& Centre, double Radius, double Length, int32 Segs, int32 Axis, bool bCollide, bool bCaps = true); /** Convex polygon in the X-Z plane (counter-clockwise seen from +Y... any order is fixed up) extruded along Y. */ void ExtrudeY(EMat M, const TArray& PolyXZ, double Y0, double Y1, bool bCollide); /** Torus about the X axis (major radius R, tube radius r). */ void TorusX(EMat M, const FVector& Centre, double R, double Tube, int32 SegsMajor, int32 SegsMinor); /** Collision only. */ void ColBox(const FVector& Min, const FVector& Max); void AddConvexPoints(const TArray& PointsM) { Convex.Add(PointsM); } int32 NumTriangles() const; void Append(const FMeshBuilder& Other); /** Replaces the sections, materials and simple collision of the component. bCollision false: render only. */ void Commit(UProceduralMeshComponent* Comp, bool bCollision) const; }; }