#pragma once // Deliberately small UE-style surface used by the copied VoxelForge CPU sources. // This is a value/container compatibility layer; it does not start or link Unreal. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UCLASS(...) #define USTRUCT(...) #define UENUM(...) #define UPROPERTY(...) #define UFUNCTION(...) #define UMETA(...) #define GENERATED_BODY() #define GENERATED_UCLASS_BODY() #define VOXELFORGE_API #define FORCEINLINE inline #define WITH_EDITOR 0 #define TRACE_CPUPROFILER_EVENT_SCOPE(...) #define PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS #define PRAGMA_ENABLE_SHADOW_VARIABLE_WARNINGS #define UE_ARRAY_COUNT(Array) (sizeof(Array) / sizeof((Array)[0])) #define TEXT(x) x #define UE_LOG(...) #define LogTemp 0 #define check(x) assert(x) #define checkf(x, ...) assert(x) #define ensure(x) (x) #define ensureMsgf(x, ...) (x) #define verify(x) (x) #define INDEX_NONE -1 #define MIN_int32 (std::numeric_limits::min()) #define MAX_int32 (std::numeric_limits::max()) #define MAX_uint32 (std::numeric_limits::max()) #define MIN_flt (-std::numeric_limits::max()) #define MAX_flt (std::numeric_limits::max()) #define PI 3.14159265358979323846f #define HALF_PI 1.57079632679489661923f #define SMALL_NUMBER 1.0e-8f #define KINDA_SMALL_NUMBER 1.0e-4f #define MoveTemp(x) std::move(x) using int8 = std::int8_t; using uint8 = std::uint8_t; using int16 = std::int16_t; using uint16 = std::uint16_t; using int32 = std::int32_t; using uint32 = std::uint32_t; using int64 = std::int64_t; using uint64 = std::uint64_t; using UPTRINT = std::uintptr_t; using TCHAR = char; using SIZE_T = std::size_t; constexpr uint64 MAX_uint64 = std::numeric_limits::max(); enum EForceInit { ForceInit }; enum class ESearchCase : uint8 { CaseSensitive, IgnoreCase }; struct FDefaultAllocator {}; template struct TInlineAllocator {}; enum class EAllowShrinking : uint8 { No, Yes }; template using TFunction = std::function; template using TFunctionRef = std::function; template constexpr T&& Forward(std::remove_reference_t& Value){return std::forward(Value);} template constexpr T&& Forward(std::remove_reference_t&& Value){return std::forward(Value);} template class TArray { public: using value_type = T; using FStored = std::conditional_t,uint8,T>; using FStorage = std::vector; using iterator = typename FStorage::iterator; using const_iterator = typename FStorage::const_iterator; using reference = typename FStorage::reference; using const_reference = typename FStorage::const_reference; TArray() = default; TArray(std::initializer_list Values) : Values(Values) {} int32 Num() const { return static_cast(Values.size()); } bool IsEmpty() const { return Values.empty(); } bool IsValidIndex(int32 Index) const { return Index >= 0 && Index < Num(); } void Reserve(int32 Count) { Values.reserve(static_cast(std::max(0, Count))); } void Reset(int32 Slack = 0) { Values.clear(); if (Slack > 0) Reserve(Slack); } void Empty(int32 Slack = 0) { Reset(Slack); } void SetNum(int32 Count) { Values.resize(static_cast(std::max(0, Count))); } void Init(const T& Value,int32 Count) { Values.assign(static_cast(std::max(0,Count)),static_cast(Value)); } void SetNumZeroed(int32 Count) { SetNum(Count); if constexpr (std::is_trivially_copyable_v) if (!Values.empty()) std::memset(Values.data(), 0, Values.size() * sizeof(T)); } void SetNumUninitialized(int32 Count) { SetNum(Count); } int32 Add(const T& Value) { Values.push_back(Value); return Num() - 1; } int32 Add(T&& Value) { Values.push_back(std::move(Value)); return Num() - 1; } template int32 Emplace(Args&&... ArgsIn) { Values.emplace_back(std::forward(ArgsIn)...); return Num() - 1; } reference AddDefaulted_GetRef() { Values.emplace_back(); return Values.back(); } int32 AddDefaulted() { Values.emplace_back(); return Num() - 1; } template reference Emplace_GetRef(Args&&... ArgsIn) { Values.emplace_back(std::forward(ArgsIn)...); return Values.back(); } template reference Add_GetRef(Args&&... ArgsIn) { Values.emplace_back(std::forward(ArgsIn)...); return Values.back(); } int32 AddUnique(const T& Value) { if (Contains(Value)) return IndexOfByKey(Value); return Add(Value); } void Append(const TArray& Other) { Values.insert(Values.end(), Other.Values.begin(), Other.Values.end()); } void Append(TArray&& Other) { Values.insert(Values.end(), std::make_move_iterator(Other.Values.begin()), std::make_move_iterator(Other.Values.end())); } void RemoveAt(int32 Index, int32 Count = 1, bool = true) { if (Index >= 0 && Count > 0 && Index < Num()) Values.erase(Values.begin()+Index, Values.begin()+std::min(Num(),Index+Count)); } void RemoveAt(int32 Index, int32 Count, EAllowShrinking) { RemoveAt(Index, Count, true); } void RemoveAtSwap(int32 Index, int32 Count = 1, bool = true) { if (Index < 0 || Count <= 0 || Index >= Num()) return; const int32 End = std::min(Num(), Index + Count); for (int32 I = Index; I < End; ++I) Values[I] = std::move(Values.back()); Values.resize(Values.size() - static_cast(End - Index)); } void RemoveAtSwap(int32 Index, int32 Count, EAllowShrinking) { RemoveAtSwap(Index, Count, true); } template int32 RemoveAll(Pred P) { const auto Before=Values.size(); Values.erase(std::remove_if(Values.begin(),Values.end(),P),Values.end()); return static_cast(Before-Values.size()); } bool Contains(const T& Value) const { return std::find(Values.begin(), Values.end(), Value) != Values.end(); } int32 IndexOfByKey(const T& Value) const { auto I=std::find(Values.begin(),Values.end(),Value); return I==Values.end()?INDEX_NONE:static_cast(I-Values.begin()); } T* Find(const T& Value) { const int32 I=IndexOfByKey(Value); return I==INDEX_NONE?nullptr:&Values[I]; } const T* Find(const T& Value) const { const int32 I=IndexOfByKey(Value); return I==INDEX_NONE?nullptr:&Values[I]; } T* GetData() { return reinterpret_cast(Values.data()); } const T* GetData() const { return reinterpret_cast(Values.data()); } std::size_t GetAllocatedSize() const { return Values.capacity() * sizeof(T); } T& Last() { return Values.back(); } const T& Last() const { return Values.back(); } T Pop(bool = false) { T Out=std::move(Values.back()); Values.pop_back(); return Out; } void Swap(int32 A,int32 B) { std::swap(Values[A],Values[B]); } template void Sort(Pred P) { std::sort(Values.begin(),Values.end(),P); } void Sort() { std::sort(Values.begin(),Values.end()); } reference operator[](int32 I) { return Values[static_cast(I)]; } const_reference operator[](int32 I) const { return Values[static_cast(I)]; } iterator begin() { return Values.begin(); } iterator end() { return Values.end(); } const_iterator begin() const { return Values.begin(); } const_iterator end() const { return Values.end(); } const_iterator cbegin() const { return Values.cbegin(); } const_iterator cend() const { return Values.cend(); } private: FStorage Values; }; template struct TPair { K Key{}; V Value{}; TPair()=default; TPair(const K& K0,const V& V0):Key(K0),Value(V0){}; }; template class TMap { public: int32 Num() const { return static_cast(Pairs.size()); } bool IsEmpty() const { return Pairs.empty(); } void Reserve(int32 N) { Pairs.reserve(static_cast(std::max(0,N))); } void Reset() { Pairs.clear(); } void Empty(int32 = 0) { Reset(); } bool Contains(const K& Key) const { return FindPair(Key) != Pairs.end(); } V* Find(const K& Key) { auto I=FindPair(Key); return I==Pairs.end()?nullptr:&I->Value; } const V* Find(const K& Key) const { auto I=FindPair(Key); return I==Pairs.end()?nullptr:&I->Value; } V& FindOrAdd(const K& Key) { if (V* P=Find(Key)) return *P; Pairs.emplace_back(Key,V{}); return Pairs.back().Value; } V& Add(const K& Key,const V& Value) { if (V* P=Find(Key)) { *P=Value; return *P; } Pairs.emplace_back(Key,Value); return Pairs.back().Value; } V& Add(const K& Key,V&& Value) { if (V* P=Find(Key)) { *P=std::move(Value); return *P; } Pairs.emplace_back(Key,std::move(Value)); return Pairs.back().Value; } int32 Remove(const K& Key) { auto I=FindPair(Key); if(I==Pairs.end())return 0; Pairs.erase(I);return 1; } V& operator[](const K& Key) { return FindOrAdd(Key); } const V& operator[](const K& Key) const { const V* P=Find(Key); assert(P); return *P; } auto begin(){return Pairs.begin();} auto end(){return Pairs.end();} auto begin()const{return Pairs.begin();} auto end()const{return Pairs.end();} private: using It=typename std::vector>::iterator; using CIt=typename std::vector>::const_iterator; It FindPair(const K& Key){return std::find_if(Pairs.begin(),Pairs.end(),[&](auto& P){return P.Key==Key;});} CIt FindPair(const K& Key)const{return std::find_if(Pairs.begin(),Pairs.end(),[&](auto& P){return P.Key==Key;});} std::vector> Pairs; }; template class TSet { public: int32 Num()const{return Values.Num();} void Reset(){Values.Reset();} void Empty(){Reset();} bool Contains(const T& V)const{return Values.Contains(V);} void Add(const T& V){Values.AddUnique(V);} void Add(T&& V){if(!Contains(V))Values.Add(std::move(V));} auto begin(){return Values.begin();}auto end(){return Values.end();}auto begin()const{return Values.begin();}auto end()const{return Values.end();} private:TArray Values; }; template class TUniquePtr { public: TUniquePtr()=default; TUniquePtr(std::nullptr_t){} explicit TUniquePtr(std::unique_ptr&&P):Ptr(std::move(P)){} template requires std::is_convertible_v TUniquePtr(TUniquePtr&& P):Ptr(P.Release()){} TUniquePtr(TUniquePtr&&)=default; TUniquePtr& operator=(TUniquePtr&&)=default; TUniquePtr(const TUniquePtr&)=delete; TUniquePtr& operator=(const TUniquePtr&)=delete; template requires std::is_convertible_v TUniquePtr& operator=(TUniquePtr&& P){Ptr.reset(P.Release());return *this;} T* Get()const{return Ptr.get();} T* get()const{return Ptr.get();} T* operator->()const{return Ptr.get();} T& operator*()const{return *Ptr;} explicit operator bool()const{return !!Ptr;} bool IsValid()const{return !!Ptr;} void Reset(T*P=nullptr){Ptr.reset(P);} void reset(T*P=nullptr){Ptr.reset(P);} T* Release(){return Ptr.release();} void Swap(TUniquePtr&O){Ptr.swap(O.Ptr);} private:std::unique_ptr Ptr; }; enum class ESPMode : uint8 { NotThreadSafe, ThreadSafe }; template class TSharedPtr { public:TSharedPtr()=default;TSharedPtr(std::nullptr_t){}TSharedPtr(std::shared_ptrP):Ptr(std::move(P)){}template requires std::is_convertible_v TSharedPtr(const TSharedPtr&P):Ptr(P.Std()){}T*Get()const{return Ptr.get();}T*operator->()const{return Ptr.get();}T&operator*()const{return *Ptr;}explicit operator bool()const{return !!Ptr;}bool IsValid()const{return !!Ptr;}void Reset(){Ptr.reset();}void Reset(T*P){Ptr.reset(P);}std::shared_ptr&Std(){return Ptr;}const std::shared_ptr&Std()const{return Ptr;}friend bool operator==(const TSharedPtr&P,std::nullptr_t){return !P.Ptr;}friend bool operator!=(const TSharedPtr&P,std::nullptr_t){return !!P.Ptr;}private:std::shared_ptrPtr; }; template using TSharedRef = TSharedPtr; template class TOptional { public:TOptional()=default;TOptional(const T&V):Value(V){}TOptional(T&&V):Value(std::move(V)){}bool IsSet()const{return Value.has_value();}explicit operator bool()const{return Value.has_value();}T&GetValue(){return Value.value();}const T&GetValue()const{return Value.value();}T&Get(){return Value.value();}const T&Get()const{return Value.value();}void Reset(){Value.reset();}private:std::optionalValue;}; template TUniquePtr MakeUnique(A&&... Args) { return TUniquePtr(std::make_unique(std::forward(Args)...)); } template TSharedRef MakeShared(A&&... Args) { return TSharedRef(std::make_shared(std::forward(Args)...)); } class FString : public std::string { public: using std::string::string; FString()=default; FString(const std::string& S):std::string(S){} FString(std::string&& S):std::string(std::move(S)){} bool IsEmpty()const{return empty();} void Reset(){clear();} int32 Len()const{return static_cast(size());} bool Contains(std::string_view S)const{return find(S)!=npos;} bool StartsWith(std::string_view S)const{return rfind(S,0)==0;} bool EndsWith(std::string_view S)const{return size()>=S.size()&&compare(size()-S.size(),S.size(),S)==0;} bool Equals(std::string_view S, ESearchCase SearchCase = ESearchCase::CaseSensitive)const{if(SearchCase==ESearchCase::CaseSensitive)return *this==S;if(size()!=S.size())return false;for(size_t I=0;I((*this)[I]))!=std::tolower(static_cast(S[I])))return false;return true;} void ToLowerInline(){for(char& C:*this)C=static_cast(std::tolower(static_cast(C)));} void TrimStartAndEndInline(){auto B=find_first_not_of(" \t\r\n"),E=find_last_not_of(" \t\r\n");*this=(B==npos)?FString{}:substr(B,E-B+1);} void LeftChopInline(std::size_t N){if(N>=size())clear();else resize(size()-N);} int32 ReplaceInline(std::string_view From,std::string_view To){if(From.empty())return 0;int32 C=0;std::size_t P=0;while((P=find(From,P))!=npos){replace(P,From.size(),To);P+=To.size();++C;}return C;} static FString Join(const TArray& Parts, std::string_view Delimiter){FString Result;for(int32 I=0;I(std::min(N,sizeof(Buf)-1)));} }; inline const char* operator*(const FString& S){return S.c_str();} using FName = FString; constexpr const char* NAME_None = ""; class UObject; template T* FindStandaloneAsset(const FString& Path); void RegisterStandaloneAsset(const FString& Path, UObject* Asset); class FText { FString Value; public: FText()=default; FText(FString S):Value(std::move(S)){} static FText FromString(const FString& S){return FText(S);} static FText FromName(const FName& S){return FText(S);} FString ToString()const{return Value;} bool IsEmpty()const{return Value.IsEmpty();} }; class UEnum { public: FString GetNameStringByValue(int64 V)const{return FString::Printf("Value_%lld",static_cast(V));} }; template const UEnum* StaticEnum(){static const UEnum Value;return &Value;} constexpr uint32 RF_Transient = 1; template T* NewObject(UObject* = nullptr,FName = NAME_None,uint32 = 0){return new T;} template T* DuplicateObject(const T* Source,UObject*){return Source?new T(*Source):nullptr;} template class TGuardValue { public:TGuardValue(T& Target,const T& NewValue):Ref(Target),Old(Target){Ref=NewValue;}~TGuardValue(){Ref=Old;}TGuardValue(const TGuardValue&)=delete;private:T&Ref;T Old;}; struct FIntPoint { int32 X=0,Y=0; constexpr FIntPoint()=default; constexpr FIntPoint(int32 A,int32 B):X(A),Y(B){} static const FIntPoint ZeroValue; bool operator==(const FIntPoint&O)const{return X==O.X&&Y==O.Y;} bool operator!=(const FIntPoint&O)const{return !(*this==O);} FIntPoint operator+(const FIntPoint&O)const{return {X+O.X,Y+O.Y};} FIntPoint operator-(const FIntPoint&O)const{return {X-O.X,Y-O.Y};} }; inline const FIntPoint FIntPoint::ZeroValue{0,0}; struct FIntVector { int32 X=0,Y=0,Z=0; constexpr FIntVector()=default; constexpr FIntVector(int32 A,int32 B,int32 C):X(A),Y(B),Z(C){} static const FIntVector ZeroValue; bool operator==(const FIntVector&O)const{return X==O.X&&Y==O.Y&&Z==O.Z;} bool operator!=(const FIntVector&O)const{return !(*this==O);} FIntVector operator+(const FIntVector&O)const{return {X+O.X,Y+O.Y,Z+O.Z};} FIntVector operator-(const FIntVector&O)const{return {X-O.X,Y-O.Y,Z-O.Z};} FIntVector operator*(int32 S)const{return {X*S,Y*S,Z*S};} FIntVector operator/(int32 S)const{return {X/S,Y/S,Z/S};} FIntVector& operator+=(const FIntVector&O){X+=O.X;Y+=O.Y;Z+=O.Z;return *this;} }; inline const FIntVector FIntVector::ZeroValue{0,0,0}; inline FIntVector operator*(int32 S,const FIntVector& V){return V*S;} struct FVector2D { double X=0,Y=0; constexpr FVector2D()=default; constexpr FVector2D(double A,double B):X(A),Y(B){} static const FVector2D ZeroVector; double SizeSquared()const{return X*X+Y*Y;} double Size()const{return std::sqrt(SizeSquared());} bool Normalize(double Tol=1.e-8){double L=Size();if(L<=Tol)return false;X/=L;Y/=L;return true;} FVector2D GetSafeNormal(double Tol=1.e-8)const{FVector2D V=*this;V.Normalize(Tol);return V;} double DistSquared(const FVector2D&O)const{return (*this-O).SizeSquared();} static double DistSquared(const FVector2D&A,const FVector2D&B){return (A-B).SizeSquared();} FVector2D operator-()const{return {-X,-Y};} FVector2D operator+(const FVector2D&O)const{return {X+O.X,Y+O.Y};} FVector2D operator-(const FVector2D&O)const{return {X-O.X,Y-O.Y};} FVector2D operator*(double S)const{return {X*S,Y*S};} FVector2D operator/(double S)const{return {X/S,Y/S};} FVector2D&operator+=(const FVector2D&O){X+=O.X;Y+=O.Y;return *this;} FVector2D&operator*=(double S){X*=S;Y*=S;return *this;} static double DotProduct(const FVector2D&A,const FVector2D&B){return A.X*B.X+A.Y*B.Y;} }; inline const FVector2D FVector2D::ZeroVector{0,0}; struct FVector2f { float X=0,Y=0; constexpr FVector2f()=default; constexpr FVector2f(float A,float B):X(A),Y(B){} }; struct FVector3f { float X=0,Y=0,Z=0; constexpr FVector3f()=default; constexpr FVector3f(float A,float B,float C):X(A),Y(B),Z(C){} FVector3f operator+(const FVector3f&O)const{return {X+O.X,Y+O.Y,Z+O.Z};} FVector3f operator-(const FVector3f&O)const{return {X-O.X,Y-O.Y,Z-O.Z};} FVector3f operator*(float S)const{return {X*S,Y*S,Z*S};} FVector3f&operator+=(const FVector3f&O){X+=O.X;Y+=O.Y;Z+=O.Z;return *this;} bool operator==(const FVector3f&O)const{return X==O.X&&Y==O.Y&&Z==O.Z;} }; struct FVector { using FReal = double; double X=0,Y=0,Z=0; constexpr FVector()=default; constexpr explicit FVector(double S):X(S),Y(S),Z(S){} constexpr FVector(double A,double B,double C):X(A),Y(B),Z(C){} constexpr explicit FVector(const FIntVector& V):X(V.X),Y(V.Y),Z(V.Z){} static const FVector ZeroVector, OneVector, UpVector, DownVector, ForwardVector, RightVector; FVector operator+(const FVector&O)const{return {X+O.X,Y+O.Y,Z+O.Z};} FVector operator-(const FVector&O)const{return {X-O.X,Y-O.Y,Z-O.Z};} FVector operator-()const{return {-X,-Y,-Z};} FVector operator*(double S)const{return {X*S,Y*S,Z*S};} FVector operator/(double S)const{return {X/S,Y/S,Z/S};} FVector operator/(const FVector&O)const{return {X/O.X,Y/O.Y,Z/O.Z};} FVector& operator+=(const FVector&O){X+=O.X;Y+=O.Y;Z+=O.Z;return *this;} FVector& operator-=(const FVector&O){X-=O.X;Y-=O.Y;Z-=O.Z;return *this;} FVector& operator*=(double S){X*=S;Y*=S;Z*=S;return *this;} FVector& operator/=(double S){X/=S;Y/=S;Z/=S;return *this;} bool operator==(const FVector&O)const{return X==O.X&&Y==O.Y&&Z==O.Z;} bool operator!=(const FVector&O)const{return !(*this==O);} double SizeSquared()const{return X*X+Y*Y+Z*Z;} double Size()const{return std::sqrt(SizeSquared());} double Dist(const FVector&O)const{return (*this-O).Size();} double DistSquared(const FVector&O)const{return (*this-O).SizeSquared();} static double Dist(const FVector&A,const FVector&B){return (A-B).Size();} static double DistSquared(const FVector&A,const FVector&B){return (A-B).SizeSquared();} bool Normalize(double Tolerance=1.e-8){double L=Size();if(L<=Tolerance)return false;*this=*this/L;return true;} FVector GetAbs()const{return {std::abs(X),std::abs(Y),std::abs(Z)};} FVector GetSafeNormal(double Tolerance=1.e-8)const{FVector V=*this;V.Normalize(Tolerance);return V;} bool IsNearlyZero(double Tolerance=1.e-8)const{return SizeSquared()<=Tolerance*Tolerance;} bool ContainsNaN()const{return !std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z);} bool Equals(const FVector&O,double Tolerance=1.e-8)const{return (*this-O).IsNearlyZero(Tolerance);} double Dot(const FVector&O)const{return X*O.X+Y*O.Y+Z*O.Z;} FVector Cross(const FVector&O)const{return {Y*O.Z-Z*O.Y,Z*O.X-X*O.Z,X*O.Y-Y*O.X};} static double DotProduct(const FVector&A,const FVector&B){return A.Dot(B);} static FVector CrossProduct(const FVector&A,const FVector&B){return A.Cross(B);} }; inline const FVector FVector::ZeroVector{0,0,0}; inline const FVector FVector::OneVector{1,1,1}; inline const FVector FVector::UpVector{0,0,1}; inline const FVector FVector::DownVector{0,0,-1}; inline const FVector FVector::ForwardVector{1,0,0}; inline const FVector FVector::RightVector{0,1,0}; inline FVector operator*(double S,const FVector&V){return V*S;} inline FVector operator*(float S,const FVector&V){return V*static_cast(S);} struct FBox { FVector Min{MAX_flt,MAX_flt,MAX_flt},Max{MIN_flt,MIN_flt,MIN_flt}; bool IsValid=false; FBox()=default; explicit FBox(EForceInit):FBox(){} FBox(const FVector&A,const FVector&B):Min(A),Max(B),IsValid(A.X<=B.X&&A.Y<=B.Y&&A.Z<=B.Z){} FVector GetCenter()const{return (Min+Max)*0.5;} FVector GetExtent()const{return (Max-Min)*0.5;} bool IsInside(const FVector&P)const{return P.X>=Min.X&&P.X<=Max.X&&P.Y>=Min.Y&&P.Y<=Max.Y&&P.Z>=Min.Z&&P.Z<=Max.Z;} bool operator==(const FBox&O)const{return Min==O.Min&&Max==O.Max&&IsValid==O.IsValid;} bool operator!=(const FBox&O)const{return !(*this==O);} }; struct FColor { uint8 R=0,G=0,B=0,A=255; constexpr FColor()=default; constexpr FColor(uint8 r,uint8 g,uint8 b,uint8 a=255):R(r),G(g),B(b),A(a){} static const FColor White, Black, Red, Green, Blue, Transparent; }; inline const FColor FColor::White{255,255,255,255}; inline const FColor FColor::Black{0,0,0,255}; inline const FColor FColor::Red{255,0,0,255}; inline const FColor FColor::Green{0,255,0,255}; inline const FColor FColor::Blue{0,0,255,255}; inline const FColor FColor::Transparent{0,0,0,0}; struct FLinearColor { float R=0,G=0,B=0,A=1; constexpr FLinearColor()=default; constexpr FLinearColor(float r,float g,float b,float a=1):R(r),G(g),B(b),A(a){} static const FLinearColor White,Black,Red,Green,Blue; FColor ToFColor(bool bSRGB)const{auto cv=[](float V,bool S){V=std::clamp(V,0.0f,1.0f);if(S)V=V<=0.0031308f?12.92f*V:1.055f*std::pow(V,1.0f/2.4f)-0.055f;return static_cast(std::clamp(static_cast(std::floor(V*255.0f+0.5f)),0,255));};return {cv(R,bSRGB),cv(G,bSRGB),cv(B,bSRGB),cv(A,false)};} }; inline const FLinearColor FLinearColor::White{1,1,1,1}; inline const FLinearColor FLinearColor::Black{0,0,0,1}; inline const FLinearColor FLinearColor::Red{1,0,0,1}; inline const FLinearColor FLinearColor::Green{0,1,0,1}; inline const FLinearColor FLinearColor::Blue{0,0,1,1}; struct FRotator { double Pitch=0,Yaw=0,Roll=0; constexpr FRotator()=default; constexpr FRotator(double P,double Y,double R):Pitch(P),Yaw(Y),Roll(R){} static const FRotator ZeroRotator; }; inline const FRotator FRotator::ZeroRotator{0,0,0}; namespace FMath { template constexpr auto Min(A A0,B B0){using R=std::common_type_t;R A1=static_cast(A0),B1=static_cast(B0);return A1 constexpr auto Max(A A0,B B0){using R=std::common_type_t;R A1=static_cast(A0),B1=static_cast(B0);return A1>B1?A1:B1;} template constexpr T Clamp(T V,A A0,B B0){T Lo=static_cast(A0),Hi=static_cast(B0);return VHi?Hi:V);} template constexpr T Abs(T V){return V(std::floor(V));} inline int32 CeilToInt(double V){return static_cast(std::ceil(V));} inline int32 RoundToInt(double V){return static_cast(std::floor(V+0.5));} inline double FloorToDouble(double V){return std::floor(V);} inline double CeilToDouble(double V){return std::ceil(V);} inline double RoundToDouble(double V){return std::floor(V+0.5);} inline float FloorToFloat(float V){return std::floor(V);} inline float CeilToFloat(float V){return std::ceil(V);} inline float RoundToFloat(float V){return std::floor(V+0.5f);} inline bool IsFinite(double V){return std::isfinite(V);} inline bool IsNearlyZero(double V,double T=1.e-8){return std::abs(V)<=T;} inline bool IsNearlyEqual(double A,double B,double T=1.e-8){return std::abs(A-B)<=T;} inline float Sqrt(float V){return std::sqrt(V);} inline double Sqrt(double V){return std::sqrt(V);} inline float InvSqrt(float V){return 1.0f/std::sqrt(V);} inline double InvSqrt(double V){return 1.0/std::sqrt(V);} inline float Sin(float V){return std::sin(V);} inline double Sin(double V){return std::sin(V);} inline float Cos(float V){return std::cos(V);} inline double Cos(double V){return std::cos(V);} inline float Tan(float V){return std::tan(V);} inline double Tan(double V){return std::tan(V);} inline float Asin(float V){return std::asin(V);} inline double Asin(double V){return std::asin(V);} inline float Acos(float V){return std::acos(V);} inline double Acos(double V){return std::acos(V);} inline float Atan2(float Y,float X){return std::atan2(Y,X);} inline double Atan2(double Y,double X){return std::atan2(Y,X);} inline float Pow(float A,float B){return std::pow(A,B);} inline double Pow(double A,double B){return std::pow(A,B);} inline float Exp(float V){return std::exp(V);} inline double Exp(double V){return std::exp(V);} inline float Loge(float V){return std::log(V);} inline float Fmod(float A,float B){return std::fmod(A,B);} inline double Fmod(double A,double B){return std::fmod(A,B);} template constexpr T Square(T V){return V*V;} template constexpr T Sign(T V){return VT(0)?T(1):T(0));} template constexpr T Max3(T A,T B,T C){return Max(Max(A,B),C);} template constexpr T Min3(T A,T B,T C){return Min(Min(A,B),C);} inline double DegreesToRadians(double D){return D*(3.1415926535897932384626433832795/180.0);} inline double RadiansToDegrees(double R){return R*(180.0/3.1415926535897932384626433832795);} inline float Frac(float V){return V-std::floor(V);} inline double Frac(double V){return V-std::floor(V);} inline int32 DivideAndRoundUp(int32 A,int32 B){return (A+B-1)/B;} template bool IsPowerOfTwo(T V){return V>0&&(V&(V-1))==0;} inline int32 FloorLog2(uint32 V){return V?31-std::countl_zero(V):0;} float PerlinNoise2D(const FVector2D& Location); float PerlinNoise3D(const FVector& Location); template inline auto Lerp(const A& A0,const B& B0,C Alpha)->decltype(A0+(B0-A0)*Alpha){return A0+(B0-A0)*Alpha;} inline float SmoothStep(float A,float B,float X){float T=Clamp((X-A)/(B-A),0.0f,1.0f);return T*T*(3-2*T);} } inline uint32 HashCombine(uint32 A,uint32 B){return A^(B+0x9e3779b9u+(A<<6)+(A>>2));} inline uint32 GetTypeHash(int32 V){return static_cast(V)*0x9e3779b9u;} inline uint32 GetTypeHash(uint32 V){return V*0x9e3779b9u;} inline uint32 GetTypeHash(float V){return std::bit_cast(V);} inline uint32 GetTypeHash(double V){return static_cast(std::bit_cast(V)^ (std::bit_cast(V)>>32));} inline uint32 GetTypeHash(int64 V){return static_cast(static_cast(V)^(static_cast(V)>>32));} inline uint32 GetTypeHash(uint64 V){return static_cast(V^(V>>32));} inline uint32 GetTypeHash(const FIntVector& V){return HashCombine(HashCombine(GetTypeHash(V.X),GetTypeHash(V.Y)),GetTypeHash(V.Z));} inline uint32 GetTypeHash(const FIntPoint& V){return HashCombine(GetTypeHash(V.X),GetTypeHash(V.Y));} struct FMemory { static void Memcpy(void*D,const void*S,std::size_t N){std::memcpy(D,S,N);} static void Memzero(void*D,std::size_t N){std::memset(D,0,N);} static int32 Memcmp(const void*A,const void*B,std::size_t N){return std::memcmp(A,B,N);} static SIZE_T GetAllocSize(const void* P){return P?sizeof(void*):0;} }; struct FPrimaryAssetId { FString Type,Name; FPrimaryAssetId()=default; FPrimaryAssetId(FString T,FName N):Type(std::move(T)),Name(std::move(N)){} }; class UObject { public: virtual ~UObject()=default; virtual void PostLoad(){} virtual FName GetFName()const{return FName{};} virtual FString GetPathName()const{return FString{};} virtual bool Modify(){return true;} virtual void MarkPackageDirty(){} }; inline std::unordered_map& StandaloneAssetRegistry() { static std::unordered_map Assets; return Assets; } inline void RegisterStandaloneAsset(const FString& Path, UObject* Asset) { if (!Path.IsEmpty() && Asset) StandaloneAssetRegistry()[Path] = Asset; } template inline T* FindStandaloneAsset(const FString& Path) { auto It = StandaloneAssetRegistry().find(Path); return It == StandaloneAssetRegistry().end() ? nullptr : dynamic_cast(It->second); } class UDataAsset : public UObject {}; class UPrimaryDataAsset : public UDataAsset { public: virtual FPrimaryAssetId GetPrimaryAssetId()const{return {};} }; using FSoftObjectPath = FString; template class TSoftObjectPtr { public: TSoftObjectPtr()=default; TSoftObjectPtr(T*P):Ptr(P){} TSoftObjectPtr(const FSoftObjectPath&P):Path(P){} bool IsNull()const{return Ptr==nullptr&&Path.IsEmpty();} T* Get()const{return Ptr?Ptr:FindStandaloneAsset(Path);} T* LoadSynchronous()const{return Get();} FString ToString()const{return Path;} void Set(T*P,FString S={}){Ptr=P;Path=std::move(S);} T* operator->()const{return Get();} explicit operator bool()const{return Get()!=nullptr;} private:T*Ptr=nullptr;FString Path; }; template class TWeakObjectPtr { public:TWeakObjectPtr()=default;TWeakObjectPtr(T*P):Ptr(P){}T*Get()const{return Ptr;}T*operator->()const{return Ptr;}private:T*Ptr=nullptr;}; template class TSubclassOf { public:TSubclassOf()=default; }; class FGameplayTag {}; class FGameplayTagContainer {}; struct FRandomStream { explicit FRandomStream(int32 S=0):Seed(S){} int32 RandRange(int32 Min,int32 Max){Seed=Seed*196314165+907633515;uint32 R=(Seed>>16)&0x7fff;return Min+static_cast((uint64(R)*(uint64(Max-Min)+1))>>15);} int32 GetCurrentSeed()const{return Seed;} private:int32 Seed; }; struct FCString { static int32 Strcmp(const char*A,const char*B){return std::strcmp(A,B);} static float Atof(const char*S){return static_cast(std::atof(S));} static int32 Atoi(const char*S){return std::atoi(S);} }; struct FPlatformTime { using Clock=std::chrono::steady_clock; static uint64 Cycles64(){return static_cast(std::chrono::duration_cast(Clock::now().time_since_epoch()).count());} static double Seconds(){return Cycles64()*1e-9;} static double GetSecondsPerCycle64(){return 1e-9;} }; struct FEvent { void Trigger(){} bool Wait(uint32=0,bool=false){return true;} }; struct FPlatformProcess { static void Sleep(float S){std::this_thread::sleep_for(std::chrono::duration(S));} static void YieldThread(){std::this_thread::yield();} static void Yield(){std::this_thread::yield();} static FEvent* GetSynchEventFromPool(bool=false){return new FEvent;} static void ReturnSynchEventToPool(FEvent*E){delete E;} }; using FCriticalSection = std::mutex; struct FScopeLock { explicit FScopeLock(std::mutex&M):Lock(M){} explicit FScopeLock(std::mutex* M):Lock(*M){} std::lock_guard Lock; }; struct FRWLock { mutable std::shared_mutex Mutex; }; struct FReadScopeLock { explicit FReadScopeLock(FRWLock&L):Lock(L.Mutex){} std::shared_lock Lock; }; struct FWriteScopeLock { explicit FWriteScopeLock(FRWLock&L):Lock(L.Mutex){} std::unique_lock Lock; }; enum EConsoleVariableFlags { ECVF_Default=0, ECVF_SetByCommandline=1, ECVF_SetByCode=2 }; class FAutoConsoleVariableRef { public:FAutoConsoleVariableRef(const char*,int32&,const char*,uint32=0){} FAutoConsoleVariableRef(const char*,float&,const char*,uint32=0){} FAutoConsoleVariableRef(const char*,bool&,const char*,uint32=0){} }; template class TAutoConsoleVariable { public:TAutoConsoleVariable(const char*,T Default,const char*,uint32=0):Value(Default){}T GetValueOnAnyThread()const{return Value.load();}T GetValueOnGameThread()const{return Value.load();}void Set(T V,uint32=0){Value.store(V);} TAutoConsoleVariable*operator->(){return this;}private:std::atomicValue;}; struct FCommandLine { static const char* Get(){return "";} }; struct FParse { static bool Value(const char*,const char*,int32&){return false;} static bool Value(const char*,const char*,float&){return false;} static bool Value(const char*,const char*,FString&){return false;} static bool Param(const char*,const char*){return false;} }; struct FCrc { static uint32 MemCrc32(const void*Data,int32 Length,uint32 Crc=0){const auto*P=static_cast(Data);uint32 V=~Crc;for(int32 I=0;I>1)^(0xEDB88320u&static_cast(-static_cast(V&1)));}return ~V;} static uint32 StrCrc32(const char*S,uint32 Crc=0){return MemCrc32(S,static_cast(std::strlen(S)),Crc);} }; template using TAtomic = std::atomic; class FThreadSafeCounter { public:int32 Increment(){return ++Value;}int32 Decrement(){return --Value;}int32 GetValue()const{return Value.load();}void Reset(){Value.store(0);}private:std::atomicValue{0};};