refactor(core): bind generator fields once per resolved strate (L40, migration step 2); research R7
SurfaceWorld, FloatingIslands and legacy evaluators sit behind one immutable densityAt adapter held in a variant (no virtual call per sample); BindResolvedStratumFields builds a sorted strate table at world setup, so DensityAt, native SW/FI sampling and ClassifyBoxDetailed no longer ask the strate manager per sample. No arithmetic change: owner hashes identical to L37/L39 at W1/W12. Cost vs L39: FI -0.66%, SW -1.31% (noise). verify fast/medium/full PASS (full re-run alone: 407 gates). Research R7: disk cache design for UE5/Windows/multiplayer and a zlib probe (about 1/3 size, 0.1 ms/tile). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
753cc9c259
commit
efa9e89468
8 files changed
+859
-118
No files matched your search
@@ -25,6 +25,7 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
// Standalone-only world compiler. It compiles the runtime room/tunnel/passages into
|
||||
@@ -41,6 +42,114 @@ public:
|
||||
double SealThickness=0.0;
|
||||
};
|
||||
|
||||
// Immutable adapters share densityAt(x,y,z,out) without a virtual call in the sample
|
||||
// loop. The variant is selected once per resolved strate; callers retain concrete access
|
||||
// to fixed-point and interval operations where the legacy arithmetic requires them.
|
||||
struct FSurfaceWorldFieldAdapter
|
||||
{
|
||||
VoxelForgeCore::Heightfield2D Evaluator;
|
||||
FSurfaceWorldFieldAdapter(const VoxelForgeCore::WorldAssemblyRecipeV1& Recipe,
|
||||
const VoxelForgeCore::WorldAssemblyStratumPlanV1& Plan) noexcept
|
||||
:Evaluator(Recipe,Plan){}
|
||||
|
||||
bool densityAt(float X,float Y,float Z,float& OutDensity) const noexcept
|
||||
{
|
||||
if(!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))return false;
|
||||
constexpr double Scale=static_cast<double>(
|
||||
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double MaxVoxel=static_cast<double>(std::numeric_limits<int64>::max())/Scale;
|
||||
if(std::abs(static_cast<double>(X))>MaxVoxel
|
||||
||std::abs(static_cast<double>(Y))>MaxVoxel
|
||||
||std::abs(static_cast<double>(Z))>MaxVoxel)return false;
|
||||
const auto ToFixed=[](float Value)
|
||||
{return static_cast<int64>(std::llround(static_cast<double>(Value)
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));};
|
||||
if(!Evaluator.isValid())return false;
|
||||
OutDensity=static_cast<float>(Evaluator.point(ToFixed(X),ToFixed(Y),ToFixed(Z))
|
||||
.densitySubvoxels/Scale);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct FFloatingIslandFieldAdapter
|
||||
{
|
||||
VoxelForgeCore::FloatingIslandFieldV1 Evaluator;
|
||||
int32 BottomChunkZ=0;
|
||||
std::vector<FFloatingIslandParams> LegacyBoundaryParams;
|
||||
|
||||
FFloatingIslandFieldAdapter(const VoxelForgeCore::FloatingIslandRecipeV1& Recipe,
|
||||
const VoxelForgeCore::FloatingIslandPlanV1& Plan,int32 InBottomChunkZ,
|
||||
int32 InTopChunkZ,const UVoxelStrateManager* Manager)
|
||||
:Evaluator(Recipe,Plan),BottomChunkZ(InBottomChunkZ)
|
||||
{
|
||||
if(Manager&&Recipe.geometryVersion<4&&InTopChunkZ>=InBottomChunkZ)
|
||||
{
|
||||
LegacyBoundaryParams.reserve(static_cast<std::size_t>(
|
||||
static_cast<int64>(InTopChunkZ)-InBottomChunkZ+1));
|
||||
for(int32 ChunkZ=InBottomChunkZ;ChunkZ<=InTopChunkZ;++ChunkZ)
|
||||
LegacyBoundaryParams.push_back(Manager->GetFloatingIslandParamsForChunk(
|
||||
FIntVector(0,0,ChunkZ)));
|
||||
}
|
||||
}
|
||||
|
||||
std::int64_t densitySubvoxelsAt(std::int64_t X,std::int64_t Y,
|
||||
std::int64_t Z) const noexcept
|
||||
{return Evaluator.densitySubvoxels(X,Y,Z);}
|
||||
|
||||
const FFloatingIslandParams* legacyBoundaryParamsAt(int32 ChunkZ) const noexcept
|
||||
{
|
||||
const int64 Index=static_cast<int64>(ChunkZ)-BottomChunkZ;
|
||||
return Index>=0&&static_cast<std::size_t>(Index)<LegacyBoundaryParams.size()
|
||||
?&LegacyBoundaryParams[static_cast<std::size_t>(Index)]:nullptr;
|
||||
}
|
||||
|
||||
bool densityAt(float X,float Y,float Z,float& OutDensity) const noexcept
|
||||
{
|
||||
if(!Evaluator.isValid()||!std::isfinite(X)||!std::isfinite(Y)
|
||||
||!std::isfinite(Z))return false;
|
||||
constexpr double Scale=static_cast<double>(
|
||||
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
constexpr double Maximum=1000000000000.0/Scale;
|
||||
if(std::abs(static_cast<double>(X))>Maximum
|
||||
||std::abs(static_cast<double>(Y))>Maximum
|
||||
||std::abs(static_cast<double>(Z))>Maximum)return false;
|
||||
const auto ToFixed=[](float Value)
|
||||
{return static_cast<int64>(std::llround(static_cast<double>(Value)
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));};
|
||||
OutDensity=static_cast<float>(static_cast<double>(densitySubvoxelsAt(
|
||||
ToFixed(X),ToFixed(Y),ToFixed(Z)))/Scale);
|
||||
return std::isfinite(OutDensity);
|
||||
}
|
||||
};
|
||||
|
||||
struct FLegacyGeneratorFieldAdapter
|
||||
{
|
||||
const UVoxelGenerator* Generator=nullptr;
|
||||
bool densityAt(float X,float Y,float Z,float& OutDensity) const
|
||||
{
|
||||
if(!Generator)return false;
|
||||
OutDensity=Generator->GetDensityAt(X,Y,Z);
|
||||
return std::isfinite(OutDensity);
|
||||
}
|
||||
};
|
||||
|
||||
using FGeneratorFieldAdapterV1=std::variant<std::monostate,
|
||||
FSurfaceWorldFieldAdapter,FFloatingIslandFieldAdapter,
|
||||
FLegacyGeneratorFieldAdapter>;
|
||||
|
||||
struct FBoundStratumField
|
||||
{
|
||||
enum class EKind : uint8 { Legacy, SurfaceWorld, FloatingIslands };
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2* Resolved=nullptr;
|
||||
const VoxelForgeCore::WorldAssemblyStratumPlanV1* SurfacePlan=nullptr;
|
||||
const VoxelForgeCore::WorldAssemblyFloatingIslandPlanV1* IslandPlan=nullptr;
|
||||
FGeneratorFieldAdapterV1 Evaluator;
|
||||
int32 StrateIndex=-1;
|
||||
int32 BottomChunkZ=0;
|
||||
int32 TopChunkZ=-1;
|
||||
EKind Kind=EKind::Legacy;
|
||||
};
|
||||
|
||||
const std::vector<FStandalonePassageVoidCrossing>& GetPassageVoidCrossings() const
|
||||
{return PassageVoidCrossings;}
|
||||
|
||||
@@ -170,6 +279,117 @@ public:
|
||||
return it==ResolvedLayout->strates.end()?nullptr:&*it;
|
||||
}
|
||||
|
||||
void BindResolvedStratumFields()
|
||||
{
|
||||
BoundStratumFields.clear();
|
||||
BoundManagerLayoutChunkWidthVoxels=Owner&&Owner->Manager
|
||||
?Owner->Manager->GetStrateLayoutChunkWidthVoxels():0.0f;
|
||||
if(!ResolvedLayout||ResolvedLayout->strateLayoutChunkSizeMicrometres<=0)return;
|
||||
const int64 ChunkWidthMicrometres=ResolvedLayout->strateLayoutChunkSizeMicrometres;
|
||||
const auto FloorDiv64=[](int64 Value,int64 Divisor)
|
||||
{
|
||||
const int64 Quotient=Value/Divisor;
|
||||
const int64 Remainder=Value%Divisor;
|
||||
return Remainder<0?Quotient-1:Quotient;
|
||||
};
|
||||
BoundStratumFields.reserve(ResolvedLayout->strates.size());
|
||||
for(const auto& Stratum:ResolvedLayout->strates)
|
||||
{
|
||||
if(Stratum.strateIndex<0
|
||||
||Stratum.interiorTopMicrometres<=Stratum.interiorBottomMicrometres)
|
||||
continue;
|
||||
const int64 Bottom=FloorDiv64(Stratum.interiorBottomMicrometres,
|
||||
ChunkWidthMicrometres);
|
||||
const int64 Top=FloorDiv64(Stratum.interiorTopMicrometres-1,
|
||||
ChunkWidthMicrometres);
|
||||
if(Bottom<std::numeric_limits<int32>::min()
|
||||
||Bottom>std::numeric_limits<int32>::max()
|
||||
||Top<std::numeric_limits<int32>::min()
|
||||
||Top>std::numeric_limits<int32>::max()||Top<Bottom)continue;
|
||||
|
||||
FBoundStratumField Bound;
|
||||
Bound.Resolved=&Stratum;
|
||||
Bound.StrateIndex=Stratum.strateIndex;
|
||||
Bound.BottomChunkZ=static_cast<int32>(Bottom);
|
||||
Bound.TopChunkZ=static_cast<int32>(Top);
|
||||
if(Stratum.generatorKind=="SurfaceWorld")
|
||||
{
|
||||
Bound.Kind=FBoundStratumField::EKind::SurfaceWorld;
|
||||
if(WorldAssembly)
|
||||
{
|
||||
const auto Plan=std::find_if(WorldAssembly->plan.strates.begin(),
|
||||
WorldAssembly->plan.strates.end(),[&](const auto& Candidate)
|
||||
{return Candidate.strateIndex==Bound.StrateIndex
|
||||
&&Candidate.bottomChunkZ==Bound.BottomChunkZ
|
||||
&&Candidate.topChunkZ==Bound.TopChunkZ;});
|
||||
if(Plan!=WorldAssembly->plan.strates.end())
|
||||
{
|
||||
Bound.SurfacePlan=&*Plan;
|
||||
FSurfaceWorldFieldAdapter Adapter(WorldAssembly->recipe,*Plan);
|
||||
if(Adapter.Evaluator.isValid())
|
||||
Bound.Evaluator.emplace<FSurfaceWorldFieldAdapter>(
|
||||
std::move(Adapter));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(Stratum.generatorKind=="FloatingIslands")
|
||||
{
|
||||
Bound.Kind=FBoundStratumField::EKind::FloatingIslands;
|
||||
if(WorldAssembly)
|
||||
{
|
||||
const auto Plan=std::find_if(WorldAssembly->floatingIslands.begin(),
|
||||
WorldAssembly->floatingIslands.end(),[&](const auto& Candidate)
|
||||
{return Candidate.strateIndex==Bound.StrateIndex
|
||||
&&Candidate.bottomChunkZ==Bound.BottomChunkZ
|
||||
&&Candidate.topChunkZ==Bound.TopChunkZ;});
|
||||
if(Plan!=WorldAssembly->floatingIslands.end())
|
||||
{
|
||||
Bound.IslandPlan=&*Plan;
|
||||
Bound.Evaluator.emplace<FFloatingIslandFieldAdapter>(
|
||||
Plan->manifest.recipe,Plan->manifest.plan,
|
||||
Bound.BottomChunkZ,Bound.TopChunkZ,
|
||||
Owner?Owner->Manager.get():nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(std::holds_alternative<std::monostate>(Bound.Evaluator))
|
||||
Bound.Evaluator.emplace<FLegacyGeneratorFieldAdapter>(
|
||||
FLegacyGeneratorFieldAdapter{Owner?Owner->Generator.get():nullptr});
|
||||
BoundStratumFields.push_back(std::move(Bound));
|
||||
}
|
||||
std::sort(BoundStratumFields.begin(),BoundStratumFields.end(),
|
||||
[](const auto& A,const auto& B){return A.BottomChunkZ<B.BottomChunkZ;});
|
||||
}
|
||||
|
||||
const FBoundStratumField* FindBoundStratumForChunk(int32 ChunkZ) const
|
||||
{
|
||||
const auto Upper=std::upper_bound(BoundStratumFields.begin(),
|
||||
BoundStratumFields.end(),ChunkZ,[](int32 Z,const auto& Field)
|
||||
{return Z<Field.BottomChunkZ;});
|
||||
if(Upper==BoundStratumFields.begin())return nullptr;
|
||||
const auto& Candidate=*std::prev(Upper);
|
||||
return ChunkZ<=Candidate.TopChunkZ?&Candidate:nullptr;
|
||||
}
|
||||
|
||||
const FBoundStratumField* FindBoundStratumForZ(float Z) const
|
||||
{
|
||||
if(!std::isfinite(Z))return nullptr;
|
||||
const float LayoutWidth=static_cast<float>(GetResolvedLayoutChunkWidthVoxels());
|
||||
if(!(LayoutWidth>0.0f))return nullptr;
|
||||
return FindBoundStratumForChunk(FMath::FloorToInt(Z/LayoutWidth));
|
||||
}
|
||||
|
||||
const FBoundStratumField* FindBoundStratumForManagerZ(float Z) const
|
||||
{
|
||||
if(!std::isfinite(Z)||!Owner||!(BoundManagerLayoutChunkWidthVoxels>0.0f))
|
||||
return nullptr;
|
||||
const float WorldZCentimetres=static_cast<float>(
|
||||
static_cast<double>(Z)*GetCoreVoxelSizeCm());
|
||||
const int32 ChunkZ=FMath::FloorToInt((WorldZCentimetres/VOXEL_SIZE)
|
||||
/BoundManagerLayoutChunkWidthVoxels);
|
||||
return FindBoundStratumForChunk(ChunkZ);
|
||||
}
|
||||
|
||||
const VoxelForgeCore::ResolvedLayoutStratumV2* FindResolvedLayoutStratumByOrder(int32 Order) const
|
||||
{
|
||||
if(!ResolvedLayout||Order<0)return nullptr;
|
||||
@@ -479,10 +699,9 @@ public:
|
||||
// samples. The merged intervals are conservative, so a positive is safe.
|
||||
return true;
|
||||
}
|
||||
if(!ResolvedLayout||!Owner||!Owner->Manager||!std::isfinite(Z))return false;
|
||||
const double worldZCentimetres=static_cast<double>(Z)*GetCoreVoxelSizeCm();
|
||||
const int32 strateIndex=Owner->Manager->GetStrateIndex(static_cast<float>(worldZCentimetres));
|
||||
const auto* stratum=FindResolvedLayoutStratum(strateIndex);
|
||||
if(!ResolvedLayout||!Owner||!std::isfinite(Z))return false;
|
||||
const FBoundStratumField* bound=FindBoundStratumForManagerZ(Z);
|
||||
const auto* stratum=bound?FindResolvedLayoutStratum(bound->StrateIndex):nullptr;
|
||||
if(!stratum)return false;
|
||||
const double zMicrometres=static_cast<double>(Z)*Owner->CoreVoxelSizeMicrons;
|
||||
const auto solidAt=[&](const VoxelForgeCore::ResolvedLayoutBandV2& band)
|
||||
@@ -538,7 +757,7 @@ public:
|
||||
return density;
|
||||
}
|
||||
}
|
||||
if(!ResolvedLayout||!Owner||!Owner->Manager||Owner->CoreVoxelSizeMicrons==0
|
||||
if(!ResolvedLayout||!Owner||Owner->CoreVoxelSizeMicrons==0
|
||||
||!std::isfinite(Z))return density;
|
||||
const double zMicrometres=static_cast<double>(Z)*Owner->CoreVoxelSizeMicrons;
|
||||
if(ResolvedLayout->strates.size()==2)
|
||||
@@ -600,9 +819,8 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
const double worldZCentimetres=static_cast<double>(Z)*GetCoreVoxelSizeCm();
|
||||
const int32 strateIndex=Owner->Manager->GetStrateIndex(static_cast<float>(worldZCentimetres));
|
||||
const auto* stratum=FindResolvedLayoutStratum(strateIndex);
|
||||
const FBoundStratumField* bound=FindBoundStratumForManagerZ(Z);
|
||||
const auto* stratum=bound?FindResolvedLayoutStratum(bound->StrateIndex):nullptr;
|
||||
if(!stratum)return density;
|
||||
const auto solidAt=[&](const VoxelForgeCore::ResolvedLayoutBandV2& band)
|
||||
{return band.presentInCurrentField&&zMicrometres>=band.bottomMicrometres
|
||||
@@ -1665,7 +1883,7 @@ public:
|
||||
|
||||
bool GetResolvedStratumBounds(const FStrateSlot& Slot,FResolvedStratumBounds& Out) const
|
||||
{
|
||||
if(!Owner||!Owner->Manager)return false;
|
||||
if(!Owner)return false;
|
||||
if(const auto* resolved=FindResolvedLayoutStratum(Slot.StrateIndex))
|
||||
{
|
||||
const double coreMicrons=std::max<std::uint32_t>(1,Owner->CoreVoxelSizeMicrons);
|
||||
@@ -1678,11 +1896,11 @@ public:
|
||||
return MakeResolvedStratumBounds(bottom,top,std::max(ceiling,bedrock),Out);
|
||||
}
|
||||
const int32 MidChunkZ=(Slot.TopChunkZ+Slot.BottomChunkZ)/2;
|
||||
if(bUseNativeManifestFields&&WorldAssembly
|
||||
&&Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,MidChunkZ))
|
||||
==ECaveGeneratorType::FloatingIslands)
|
||||
const FBoundStratumField* Bound=FindBoundStratumForChunk(MidChunkZ);
|
||||
if(bUseNativeManifestFields&&WorldAssembly&&Bound
|
||||
&&Bound->Kind==FBoundStratumField::EKind::FloatingIslands)
|
||||
{
|
||||
const auto* Plan=FindWorldAssemblyFloatingIslandStratum(Slot.StrateIndex,MidChunkZ);
|
||||
const auto* Plan=Bound->IslandPlan;
|
||||
if(Plan&&Plan->manifest.recipe.geometryVersion>=3)
|
||||
{
|
||||
constexpr double Scale=static_cast<double>(
|
||||
@@ -1989,6 +2207,7 @@ public:
|
||||
WorkerCount=std::clamp(Workers,1,16);DetailVariant=Variant;
|
||||
Scheduler=InScheduler;RoundTunnelsEnabled=bInRoundTunnels;
|
||||
bUseNativeManifestFields=true;
|
||||
BindResolvedStratumFields();
|
||||
bAnalyticOnly=false;
|
||||
bProfileDetailBand=bInProfileDetailBand;
|
||||
for(int32 A=0;A<3;++A)
|
||||
@@ -2162,6 +2381,7 @@ public:
|
||||
// Analytic pages retain their bounded owner bins for route bookkeeping. Once published,
|
||||
// the Core world binds each page to its base shape as the single density evaluator.
|
||||
bUseNativeManifestFields=true;
|
||||
BindResolvedStratumFields();
|
||||
RoundTunnelsEnabled=bInRoundTunnels;bAnalyticOnly=true;bProfileDetailBand=false;
|
||||
for(int32 A=0;A<3;++A)
|
||||
{
|
||||
@@ -2199,6 +2419,7 @@ public:
|
||||
Owner=&World;WorldAssembly=InWorldAssembly;ResolvedLayout=InResolvedLayout;
|
||||
NativeOnlyMinimum=InMinimum;NativeOnlyMaximum=InMaximum;
|
||||
bUseNativeManifestFields=true;bNativeOnly=true;bAnalyticOnly=true;Built=true;
|
||||
BindResolvedStratumFields();
|
||||
NativeOnlyViolations.store(0,std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
@@ -2742,23 +2963,13 @@ public:
|
||||
if(NativeFloatingIslandDensityAt(X,Y,Z,Density))return "FloatingIslands-native";
|
||||
if(NativeSurfaceDensityAt(X,Y,Z,Density))return "SurfaceWorld-native";
|
||||
if(UsesLegacySurfaceField(X,Y,Z))return "SurfaceWorld-legacy";
|
||||
if(Owner&&Owner->Manager)
|
||||
const FBoundStratumField* Bound=FindBoundStratumForZ(Z);
|
||||
if(Bound&&Bound->Resolved)
|
||||
{
|
||||
const int32 ChunkZ=FMath::FloorToInt(Z/static_cast<float>(GetResolvedLayoutChunkWidthVoxels()));
|
||||
const ECaveGeneratorType Type=Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ));
|
||||
switch(Type)
|
||||
{
|
||||
case ECaveGeneratorType::TunnelNetwork:return "compiled-analytic/TunnelNetwork";
|
||||
case ECaveGeneratorType::FlatPlain:return "compiled-analytic/FlatPlain";
|
||||
case ECaveGeneratorType::CrystalChamber:return "compiled-analytic/CrystalChamber";
|
||||
case ECaveGeneratorType::Maze:return "compiled-analytic/Maze";
|
||||
case ECaveGeneratorType::SurfaceWorld:return "compiled-analytic/SurfaceWorld";
|
||||
case ECaveGeneratorType::VerticalShafts:return "compiled-analytic/VerticalShafts";
|
||||
case ECaveGeneratorType::FloatingIslands:return "compiled-analytic/FloatingIslands";
|
||||
case ECaveGeneratorType::Underwater:return "compiled-analytic/Underwater";
|
||||
}
|
||||
const std::string& Kind=Bound->Resolved->generatorKind;
|
||||
return "compiled-analytic/"+Kind;
|
||||
}
|
||||
return "compiled-analytic/fallback";
|
||||
return Owner?"compiled-analytic/TunnelNetwork":"compiled-analytic/fallback";
|
||||
}
|
||||
|
||||
float DensityAtLattice(int32 X,int32 Y,int32 Z) const
|
||||
@@ -3672,7 +3883,10 @@ private:
|
||||
&&!NativeSurfaceDensityAt(X,Y,Z,density))
|
||||
{
|
||||
if(UsesLegacySurfaceField(X,Y,Z)&&Owner&&Owner->Generator)
|
||||
density=Owner->Generator->GetDensityAt(X,Y,Z);
|
||||
{
|
||||
if(!LegacyGeneratorDensityAt(X,Y,Z,density))
|
||||
density=Owner->Generator->GetDensityAt(X,Y,Z);
|
||||
}
|
||||
else
|
||||
density=AnalyticDensityAt(X,Y,Z,true,nullptr,nullptr,nullptr,
|
||||
false,false);
|
||||
@@ -3961,7 +4175,9 @@ private:
|
||||
NativeDensity=ApplyResolvedStrateComposition(NativeDensity,X,Y,Z);
|
||||
return bApplyEdits?ApplyEditBrushes(NativeDensity,X,Y,Z):NativeDensity;
|
||||
}
|
||||
const float Density=Owner->Generator->GetDensityAt(X,Y,Z);
|
||||
float Density=0.0f;
|
||||
if(!LegacyGeneratorDensityAt(X,Y,Z,Density))
|
||||
Density=Owner->Generator->GetDensityAt(X,Y,Z);
|
||||
const float composed=bApplyComposition
|
||||
?ApplyResolvedStrateComposition(Density,X,Y,Z):Density;
|
||||
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
|
||||
@@ -4019,23 +4235,19 @@ public:
|
||||
{
|
||||
if(SharedDensitySource)
|
||||
return SharedDensitySource->GetNativeSurfaceHeightsAt(X,Y,ChunkZ,OutGroundZ,OutCeilingZ);
|
||||
if(!WorldAssembly||!ResolvedLayout||!Owner||!Owner->Manager||!Owner->Generator
|
||||
if(!WorldAssembly||!ResolvedLayout||!Owner||!Owner->Generator
|
||||
||!std::isfinite(X)||!std::isfinite(Y))return false;
|
||||
const double ChunkWidth=GetResolvedLayoutChunkWidthVoxels();
|
||||
const std::int64_t chunkMinimum=static_cast<std::int64_t>(std::llround(ChunkZ*ChunkWidth));
|
||||
const std::int64_t chunkMaximum=static_cast<std::int64_t>(std::llround((ChunkZ+1)*ChunkWidth));
|
||||
const FBoundStratumField* Bound=FindBoundStratumForChunk(ChunkZ);
|
||||
const auto* Field=Bound?std::get_if<FSurfaceWorldFieldAdapter>(&Bound->Evaluator):nullptr;
|
||||
if(bNativeOnly&&(!NativeOnlyContainsColumn(X,Y)
|
||||
||chunkMaximum<NativeOnlyMinimum.Z||chunkMinimum>NativeOnlyMaximum.Z
|
||||
||Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
!=ECaveGeneratorType::SurfaceWorld))
|
||||
||!Bound||Bound->Kind!=FBoundStratumField::EKind::SurfaceWorld))
|
||||
{RecordNativeOnlyViolation();return false;}
|
||||
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
!=ECaveGeneratorType::SurfaceWorld)return false;
|
||||
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
|
||||
static_cast<float>((static_cast<double>(ChunkZ)+0.5)
|
||||
*ResolvedLayout->strateLayoutChunkSizeMicrometres/10000.0));
|
||||
const auto* Plan=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
|
||||
if(!Plan)return false;
|
||||
if(!Bound||Bound->Kind!=FBoundStratumField::EKind::SurfaceWorld
|
||||
||!Bound->SurfacePlan||!Field||!Field->Evaluator.isValid())return false;
|
||||
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double MaxVoxel=static_cast<double>(std::numeric_limits<int64>::max())/Scale;
|
||||
if(std::abs(static_cast<double>(X))>MaxVoxel||std::abs(static_cast<double>(Y))>MaxVoxel)
|
||||
@@ -4043,9 +4255,7 @@ public:
|
||||
const auto ToFixed=[](float Value)
|
||||
{return static_cast<int64>(std::llround(static_cast<double>(Value)
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));};
|
||||
const VoxelForgeCore::Heightfield2D Block(WorldAssembly->recipe,*Plan);
|
||||
if(!Block.isValid())return false;
|
||||
const auto Column=Block.point(ToFixed(X),ToFixed(Y),
|
||||
const auto Column=Field->Evaluator.point(ToFixed(X),ToFixed(Y),
|
||||
static_cast<int64>(ChunkZ)*ResolvedLayout->strateLayoutChunkWidthSubvoxels);
|
||||
OutGroundZ=static_cast<float>(Column.groundHeightSubvoxels/Scale);
|
||||
OutCeilingZ=static_cast<float>(Column.capHeightSubvoxels/Scale);
|
||||
@@ -4058,25 +4268,22 @@ public:
|
||||
bool IsNativeSurfaceBox(const FIntVector& Origin,int32 Step,int32 Cells) const
|
||||
{
|
||||
if(SharedDensitySource)return SharedDensitySource->IsNativeSurfaceBox(Origin,Step,Cells);
|
||||
if(!WorldAssembly||!Owner||!Owner->Manager||Step<=0||Cells<=0)return false;
|
||||
if(!WorldAssembly||!ResolvedLayout||!Owner||Step<=0||Cells<=0)return false;
|
||||
const int32 Pad=std::max(Step,1);
|
||||
const int32 BMin=Origin.Z-Pad;
|
||||
const int32 BMax=Origin.Z+(Cells+1)*Step;
|
||||
const float LayoutWidth=static_cast<float>(GetResolvedLayoutChunkWidthVoxels());
|
||||
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin)/LayoutWidth);
|
||||
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax)/LayoutWidth);
|
||||
const VoxelForgeCore::WorldAssemblyStratumPlanV1* Plan=nullptr;
|
||||
const FBoundStratumField* BoundPlan=nullptr;
|
||||
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
|
||||
{
|
||||
const auto It=std::find_if(WorldAssembly->plan.strates.begin(),
|
||||
WorldAssembly->plan.strates.end(),[ChunkZ](const auto& Entry)
|
||||
{return ChunkZ<=Entry.topChunkZ&&ChunkZ>=Entry.bottomChunkZ;});
|
||||
if(It==WorldAssembly->plan.strates.end())return false;
|
||||
const auto* Candidate=&*It;
|
||||
if(Plan&&Plan!=Candidate)return false;
|
||||
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
!=ECaveGeneratorType::SurfaceWorld)return false;
|
||||
Plan=Candidate;
|
||||
const auto* Candidate=FindBoundStratumForChunk(ChunkZ);
|
||||
if(!Candidate||Candidate->Kind!=FBoundStratumField::EKind::SurfaceWorld
|
||||
||!Candidate->SurfacePlan||ChunkZ<Candidate->SurfacePlan->bottomChunkZ
|
||||
||ChunkZ>Candidate->SurfacePlan->topChunkZ)return false;
|
||||
if(BoundPlan&&BoundPlan!=Candidate)return false;
|
||||
BoundPlan=Candidate;
|
||||
}
|
||||
if(bNativeOnly)
|
||||
{
|
||||
@@ -4088,8 +4295,8 @@ public:
|
||||
||BMaxY>NativeOnlyMaximum.Y||BMax>NativeOnlyMaximum.Z)
|
||||
{RecordNativeOnlyViolation();return false;}
|
||||
}
|
||||
if(bNativeOnly&&!Plan)RecordNativeOnlyViolation();
|
||||
return Plan!=nullptr;
|
||||
if(bNativeOnly&&!BoundPlan)RecordNativeOnlyViolation();
|
||||
return BoundPlan!=nullptr;
|
||||
}
|
||||
|
||||
FBoxClassification ClassifyBoxDetailed(const FIntVector& Origin,int32 Step,int32 Cells,
|
||||
@@ -4104,31 +4311,39 @@ public:
|
||||
int32 BMax[3]={Origin.X+(Cells+1)*Step,Origin.Y+(Cells+1)*Step,Origin.Z+(Cells+1)*Step};
|
||||
const VoxelForgeCore::WorldAssemblyStratumPlanV1* SurfacePlan=nullptr;
|
||||
const VoxelForgeCore::WorldAssemblyFloatingIslandPlanV1* IslandPlan=nullptr;
|
||||
const FSurfaceWorldFieldAdapter* SurfaceField=nullptr;
|
||||
const FFloatingIslandFieldAdapter* IslandField=nullptr;
|
||||
bool bSurfaceOverlap=false,bIslandOverlap=false,bOtherStrateOverlap=false;
|
||||
bool bIslandOtherStrateOverlap=false,bBadPlan=false;
|
||||
if(Owner&&Owner->Manager&&ResolvedLayout)
|
||||
if(Owner&&ResolvedLayout)
|
||||
{
|
||||
const float LayoutWidth=static_cast<float>(GetResolvedLayoutChunkWidthVoxels());
|
||||
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin[2])/LayoutWidth);
|
||||
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax[2])/LayoutWidth);
|
||||
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
|
||||
{
|
||||
const ECaveGeneratorType Type=Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ));
|
||||
const int32 StrateIndex=Owner->Manager->GetStrateIndex(static_cast<float>(
|
||||
(static_cast<double>(ChunkZ)+0.5)*ResolvedLayout->strateLayoutChunkSizeMicrometres/10000.0));
|
||||
if(Type==ECaveGeneratorType::SurfaceWorld)
|
||||
const FBoundStratumField* Bound=FindBoundStratumForChunk(ChunkZ);
|
||||
if(Bound&&Bound->Kind==FBoundStratumField::EKind::SurfaceWorld)
|
||||
{
|
||||
bSurfaceOverlap=true;bIslandOtherStrateOverlap=true;
|
||||
const auto* Candidate=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
|
||||
if(!Candidate||(SurfacePlan&&SurfacePlan!=Candidate))bBadPlan=true;
|
||||
const auto* Candidate=Bound->SurfacePlan;
|
||||
if(!Candidate||ChunkZ<Candidate->bottomChunkZ
|
||||
||ChunkZ>Candidate->topChunkZ
|
||||
||(SurfacePlan&&SurfacePlan!=Candidate))bBadPlan=true;
|
||||
SurfacePlan=Candidate;
|
||||
SurfaceField=std::get_if<FSurfaceWorldFieldAdapter>(
|
||||
&Bound->Evaluator);
|
||||
}
|
||||
else if(Type==ECaveGeneratorType::FloatingIslands)
|
||||
else if(Bound&&Bound->Kind==FBoundStratumField::EKind::FloatingIslands)
|
||||
{
|
||||
bIslandOverlap=true;bOtherStrateOverlap=true;
|
||||
const auto* Candidate=FindWorldAssemblyFloatingIslandStratum(StrateIndex,ChunkZ);
|
||||
if(!Candidate||(IslandPlan&&IslandPlan!=Candidate))bBadPlan=true;
|
||||
const auto* Candidate=Bound->IslandPlan;
|
||||
if(!Candidate||ChunkZ<Candidate->bottomChunkZ
|
||||
||ChunkZ>Candidate->topChunkZ
|
||||
||(IslandPlan&&IslandPlan!=Candidate))bBadPlan=true;
|
||||
IslandPlan=Candidate;
|
||||
IslandField=std::get_if<FFloatingIslandFieldAdapter>(
|
||||
&Bound->Evaluator);
|
||||
}
|
||||
else
|
||||
{bOtherStrateOverlap=true;bIslandOtherStrateOverlap=true;}
|
||||
@@ -4213,12 +4428,12 @@ public:
|
||||
}
|
||||
if(bSurfaceOverlap)
|
||||
{
|
||||
if(!WorldAssembly||!SurfacePlan)return Finish(EVoxelTileClass::Mixed,true);
|
||||
const VoxelForgeCore::Heightfield2D Block(WorldAssembly->recipe,*SurfacePlan);
|
||||
if(!WorldAssembly||!SurfacePlan||!SurfaceField)
|
||||
return Finish(EVoxelTileClass::Mixed,true);
|
||||
// classifyLatticeBox takes the exact extrema of ground/cap columns at every
|
||||
// MC lattice XY in this box. Since density is min(z-ground, cap-z), those extrema
|
||||
// bound every lattice sample in Z without a noise amplitude guess.
|
||||
const auto Verdict=Block.classifyLatticeBox(
|
||||
const auto Verdict=SurfaceField->Evaluator.classifyLatticeBox(
|
||||
static_cast<int64>(BMin[0])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMin[1])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMin[2])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
@@ -4239,7 +4454,7 @@ public:
|
||||
}
|
||||
if(bIslandOverlap)
|
||||
{
|
||||
if(!IslandPlan)return Finish(EVoxelTileClass::Mixed,true);
|
||||
if(!IslandPlan||!IslandField)return Finish(EVoxelTileClass::Mixed,true);
|
||||
constexpr std::int64_t Scale=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
const std::int64_t Bottom=IslandPlan->manifest.recipe.layoutMinimumZSubvoxels;
|
||||
const std::int64_t Top=IslandPlan->manifest.recipe.layoutMaximumZSubvoxels;
|
||||
@@ -4259,10 +4474,8 @@ public:
|
||||
return Finish((IslandPlan->manifest.recipe.geometryVersion<3
|
||||
?BoxBottom>=Top-Seal:BoxBottom>Top-Seal)&&BoxTop<=Top
|
||||
?EVoxelTileClass::AllSolid:EVoxelTileClass::Mixed);
|
||||
const VoxelForgeCore::FloatingIslandFieldV1 Block(
|
||||
IslandPlan->manifest.recipe,IslandPlan->manifest.plan);
|
||||
if(!Block.isValid())return Finish(EVoxelTileClass::Mixed,true);
|
||||
const auto Interval=Block.interval(static_cast<int64>(BMin[0])*Scale,
|
||||
if(!IslandField->Evaluator.isValid())return Finish(EVoxelTileClass::Mixed,true);
|
||||
const auto Interval=IslandField->Evaluator.interval(static_cast<int64>(BMin[0])*Scale,
|
||||
static_cast<int64>(BMin[1])*Scale,static_cast<int64>(BMin[2])*Scale,
|
||||
static_cast<int64>(BMax[0])*Scale,static_cast<int64>(BMax[1])*Scale,
|
||||
static_cast<int64>(BMax[2])*Scale);
|
||||
@@ -4782,30 +4995,18 @@ private:
|
||||
|
||||
bool NativeSurfaceDensityAt(float X,float Y,float Z,float& OutDensity) const
|
||||
{
|
||||
if(!bUseNativeManifestFields||!WorldAssembly||!ResolvedLayout||!Owner||!Owner->Manager||!Owner->Generator
|
||||
||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))return false;
|
||||
if(!bUseNativeManifestFields||!WorldAssembly||!ResolvedLayout||!Owner
|
||||
||!Owner->Manager||!Owner->Generator||!std::isfinite(X)
|
||||
||!std::isfinite(Y)||!std::isfinite(Z))return false;
|
||||
const float LayoutWidth=static_cast<float>(GetResolvedLayoutChunkWidthVoxels());
|
||||
if(!(LayoutWidth>0.0f))return false;
|
||||
const int32 ChunkZ=FMath::FloorToInt(Z/LayoutWidth);
|
||||
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
!=ECaveGeneratorType::SurfaceWorld)return false;
|
||||
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
|
||||
static_cast<float>((static_cast<double>(ChunkZ)+0.5)
|
||||
*ResolvedLayout->strateLayoutChunkSizeMicrometres/10000.0));
|
||||
const auto* Plan=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
|
||||
if(!Plan)return false;
|
||||
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double MaxVoxel=static_cast<double>(std::numeric_limits<int64>::max())/Scale;
|
||||
if(std::abs(static_cast<double>(X))>MaxVoxel||std::abs(static_cast<double>(Y))>MaxVoxel
|
||||
||std::abs(static_cast<double>(Z))>MaxVoxel)return false;
|
||||
const auto ToFixed=[](float Value)
|
||||
{return static_cast<int64>(std::llround(static_cast<double>(Value)
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));};
|
||||
const VoxelForgeCore::Heightfield2D Block(WorldAssembly->recipe,*Plan);
|
||||
if(!Block.isValid())return false;
|
||||
OutDensity=static_cast<float>(Block.point(ToFixed(X),ToFixed(Y),ToFixed(Z)).densitySubvoxels
|
||||
/Scale);
|
||||
return true;
|
||||
const FBoundStratumField* Bound=FindBoundStratumForChunk(ChunkZ);
|
||||
if(!Bound||Bound->Kind!=FBoundStratumField::EKind::SurfaceWorld
|
||||
||!Bound->SurfacePlan||ChunkZ<Bound->SurfacePlan->bottomChunkZ
|
||||
||ChunkZ>Bound->SurfacePlan->topChunkZ)return false;
|
||||
const auto* Field=std::get_if<FSurfaceWorldFieldAdapter>(&Bound->Evaluator);
|
||||
return Field&&Field->densityAt(X,Y,Z,OutDensity);
|
||||
}
|
||||
|
||||
bool NativeFloatingIslandDensityAt(float X,float Y,float Z,float& OutDensity) const
|
||||
@@ -4818,13 +5019,15 @@ private:
|
||||
const double LayoutChunkWidth=GetResolvedLayoutChunkWidthVoxels();
|
||||
if(!(LayoutChunkWidth>0.0))return false;
|
||||
const int32 ChunkZ=FMath::FloorToInt(Z/static_cast<float>(LayoutChunkWidth));
|
||||
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
!=ECaveGeneratorType::FloatingIslands)return false;
|
||||
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
|
||||
static_cast<float>((static_cast<double>(ChunkZ)+0.5)
|
||||
*ResolvedLayout->strateLayoutChunkSizeMicrometres/10000.0));
|
||||
const auto* Plan=FindWorldAssemblyFloatingIslandStratum(StrateIndex,ChunkZ);
|
||||
if(!Plan)return false;
|
||||
const FBoundStratumField* Bound=FindBoundStratumForChunk(ChunkZ);
|
||||
if(!Bound||Bound->Kind!=FBoundStratumField::EKind::FloatingIslands
|
||||
||!Bound->IslandPlan||ChunkZ<Bound->IslandPlan->bottomChunkZ
|
||||
||ChunkZ>Bound->IslandPlan->topChunkZ)return false;
|
||||
const int32 StrateIndex=Bound->StrateIndex;
|
||||
const auto* Plan=Bound->IslandPlan;
|
||||
const auto* FieldAdapter=std::get_if<FFloatingIslandFieldAdapter>(
|
||||
&Bound->Evaluator);
|
||||
if(!FieldAdapter||!FieldAdapter->Evaluator.isValid())return false;
|
||||
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
constexpr double Maximum=1000000000000.0/Scale;
|
||||
if(std::abs(static_cast<double>(X))>Maximum||std::abs(static_cast<double>(Y))>Maximum
|
||||
@@ -4832,11 +5035,8 @@ private:
|
||||
const auto ToFixed=[](float Value)
|
||||
{return static_cast<int64>(std::llround(static_cast<double>(Value)
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));};
|
||||
const VoxelForgeCore::FloatingIslandFieldV1 Field(
|
||||
Plan->manifest.recipe,Plan->manifest.plan);
|
||||
if(!Field.isValid())return false;
|
||||
const std::int64_t qx=ToFixed(X),qy=ToFixed(Y),qz=ToFixed(Z);
|
||||
std::int64_t Density=Field.densitySubvoxels(qx,qy,qz);
|
||||
std::int64_t Density=FieldAdapter->densitySubvoxelsAt(qx,qy,qz);
|
||||
const std::int64_t NativeDensity=Density;
|
||||
// The anchor-root field keeps broad lobes, but its shortest authored noise
|
||||
// wavelength is below one metre. Filter that detail only around a FloatingIslands
|
||||
@@ -4963,7 +5163,7 @@ private:
|
||||
{
|
||||
const std::int64_t weight=(dx==0?2:1)*(dy==0?2:1)*(dz==0?2:1);
|
||||
const std::int64_t sample=(dx==0&&dy==0&&dz==0)?Density:
|
||||
Field.densitySubvoxels(qx+dx*FilterStep,qy+dy*FilterStep,
|
||||
FieldAdapter->densitySubvoxelsAt(qx+dx*FilterStep,qy+dy*FilterStep,
|
||||
qz+dz*FilterStep);
|
||||
weightedSum+=sample*weight;
|
||||
}
|
||||
@@ -5000,7 +5200,7 @@ private:
|
||||
{
|
||||
std::array<std::int64_t,3> sample{qx,qy,qz};
|
||||
sample[axis]+=sign*fragmentStep;
|
||||
const std::int64_t neighbor=Field.densitySubvoxels(
|
||||
const std::int64_t neighbor=FieldAdapter->densitySubvoxelsAt(
|
||||
sample[0],sample[1],sample[2]);
|
||||
if(neighbor<=0)
|
||||
{
|
||||
@@ -5031,11 +5231,11 @@ private:
|
||||
if(!MakeResolvedStratumBounds(static_cast<double>(Bottom)/Scale,
|
||||
static_cast<double>(Top)/Scale,static_cast<double>(Seal)/Scale,Bounds))
|
||||
return false;
|
||||
const FFloatingIslandParams Params=Owner->Manager->GetFloatingIslandParamsForChunk(
|
||||
FIntVector(0,0,ChunkZ));
|
||||
const FFloatingIslandParams* Params=FieldAdapter->legacyBoundaryParamsAt(ChunkZ);
|
||||
if(!Params)return false;
|
||||
float InternalDensity=-OutDensity;
|
||||
VF_ApplyUpperBoundarySeal(InternalDensity,Z,static_cast<float>(Bounds.TopZ),
|
||||
static_cast<float>(Bounds.SealThickness),Params.BaseDensity);
|
||||
static_cast<float>(Bounds.SealThickness),Params->BaseDensity);
|
||||
OutDensity=-InternalDensity;
|
||||
}
|
||||
else OutDensity=static_cast<float>(static_cast<double>(Density)/Scale);
|
||||
@@ -5044,12 +5244,16 @@ private:
|
||||
|
||||
bool UsesLegacySurfaceField(float X,float Y,float Z) const
|
||||
{
|
||||
if(!Owner||!Owner->Manager||!Owner->Generator)return false;
|
||||
const double LayoutChunkWidth=GetResolvedLayoutChunkWidthVoxels();
|
||||
if(!(LayoutChunkWidth>0.0))return false;
|
||||
const FIntVector ChunkCoord(0,0,FMath::FloorToInt(Z/static_cast<float>(LayoutChunkWidth)));
|
||||
return Owner->Manager->GetGeneratorTypeForChunk(ChunkCoord)
|
||||
==ECaveGeneratorType::SurfaceWorld;
|
||||
const FBoundStratumField* Bound=FindBoundStratumForZ(Z);
|
||||
return Bound&&Bound->Kind==FBoundStratumField::EKind::SurfaceWorld;
|
||||
}
|
||||
|
||||
bool LegacyGeneratorDensityAt(float X,float Y,float Z,float& OutDensity) const
|
||||
{
|
||||
const FBoundStratumField* Bound=FindBoundStratumForZ(Z);
|
||||
const auto* Legacy=Bound?std::get_if<FLegacyGeneratorFieldAdapter>(
|
||||
&Bound->Evaluator):nullptr;
|
||||
return Legacy&&Legacy->densityAt(X,Y,Z,OutDensity);
|
||||
}
|
||||
|
||||
float LegacySurfaceDensityAt(float X,float Y,float Z) const
|
||||
@@ -5057,7 +5261,10 @@ private:
|
||||
// SurfaceWorld consumes biome overrides, terrain ops, water level and its sky cap in
|
||||
// the legacy stack. Reuse that immutable evaluator as an exact temporary bridge.
|
||||
// SurfaceWorld lit biomes, ops de terrain, niveau d'eau et plafond; reutiliser ce champ.
|
||||
const float density=ApplyResolvedStrateComposition(Owner->Generator->GetDensityAt(X,Y,Z),X,Y,Z);
|
||||
float density=0.0f;
|
||||
if(!LegacyGeneratorDensityAt(X,Y,Z,density))
|
||||
density=Owner->Generator->GetDensityAt(X,Y,Z);
|
||||
density=ApplyResolvedStrateComposition(density,X,Y,Z);
|
||||
return ApplyEditBrushes(density,X,Y,Z);
|
||||
}
|
||||
|
||||
@@ -8378,11 +8585,13 @@ private:
|
||||
bool Built=false;
|
||||
bool bNativeOnly=false;
|
||||
bool bUseNativeManifestFields=false;
|
||||
float BoundManagerLayoutChunkWidthVoxels=0.0f;
|
||||
FIntVector NativeOnlyMinimum=FIntVector::ZeroValue;
|
||||
FIntVector NativeOnlyMaximum=FIntVector::ZeroValue;
|
||||
mutable std::atomic<uint32> NativeOnlyViolations{0};
|
||||
bool FarBuilt=false;
|
||||
std::vector<std::array<double,2>> ResolvedBoundaryZIntervalsMicrometres;
|
||||
std::vector<FBoundStratumField> BoundStratumFields;
|
||||
std::vector<FInterval> LayerRanges;
|
||||
std::vector<FLayerField> LayerFields;
|
||||
std::vector<FPrimitive> Primitives;
|
||||
|
||||
Reference in new issue
Block a user