wip(standalone): shape-world join blends (room 12, strata/cave 3) -- source restored to last passing set, not rebuilt

Luna's continuation stopped when Windows could not start new processes (0xC0000142, low memory).
Fixed-view join step: chamber 0.041, tunnel 0.434 (bar 0.450, barely). Routes 5642/5642 open.
Shape stages 0.963 s / 0.870 s. Floor join plates still visible; final checks not run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-04 00:07:00 +02:00
1 parent 4f4e67c8ca
commit 7e3f926352
5 files changed
+300 -58

No files matched your search

+195 -58
View File
@@ -47,6 +47,10 @@ public:
uint64 WalkFailures = 0;
double MaxCapsuleJoinRadiusDelta = 0.0;
double MaxCapsuleJoinFloorDelta = 0.0;
double MaxCapsuleJoinFloorSlopeDelta = 0.0;
double MaxUnmarkedFloorSlopeDelta = 0.0;
double MaxMarkedFloorSlopeDelta = 0.0;
uint64 MarkedFloorSlopeJoins = 0;
double LegacySegmentRadiusStep = 0.0;
double SmoothedProfileRadiusStep = 0.0;
double MaxTunnelBendDegrees = 0.0;
@@ -99,6 +103,8 @@ public:
static constexpr int32 DetailBand = 4;
static constexpr float DetailSupremum = 4.0f;
static constexpr double JoinBlendRadius = 0.0;
static constexpr double RoomUnionBlendRadius = 12.0;
static constexpr float StrataCaveBlendRadius = 3.0f;
static constexpr int32 LineBinSize = 32;
static constexpr int32 SpatialBinSize = 256;
@@ -170,10 +176,12 @@ public:
static_cast<unsigned long long>(Stats.WalkSamples-Stats.WalkFailures),
static_cast<unsigned long long>(Stats.WalkSamples));
const double SmoothUnionGrowthBound=JoinBlendRadius/6.0;
std::printf("[TunnelRadiusAudit] shared_joins=%llu legacy_fixed_segment_max_step=%.4f vox smoothed_profile_max_step=%.4f vox tapered_endpoint_max_delta=%.6f vox floor_endpoint_max_delta=%.6f vox smooth_union_growth_bound=%.4f vox thresholds=(0.005,0.005,0.25) status=%s\n",
std::printf("[TunnelRadiusAudit] shared_joins=%llu legacy_fixed_segment_max_step=%.4f vox smoothed_profile_max_step=%.4f vox tapered_endpoint_max_delta=%.6f vox floor_endpoint_max_delta=%.6f vox floor_slope_join_delta=%.5f unmarked_floor_slope_delta=%.5f marked_floor_slope_delta=%.5f marked_slope_joins=%llu smooth_union_growth_bound=%.4f vox thresholds=(0.005,0.005,0.25) status=%s\n",
static_cast<unsigned long long>(Stats.CapsuleJoinCount),Stats.LegacySegmentRadiusStep,
Stats.SmoothedProfileRadiusStep,Stats.MaxCapsuleJoinRadiusDelta,
Stats.MaxCapsuleJoinFloorDelta,SmoothUnionGrowthBound,
Stats.MaxCapsuleJoinFloorDelta,Stats.MaxCapsuleJoinFloorSlopeDelta,
Stats.MaxUnmarkedFloorSlopeDelta,Stats.MaxMarkedFloorSlopeDelta,
static_cast<unsigned long long>(Stats.MarkedFloorSlopeJoins),SmoothUnionGrowthBound,
Stats.MaxCapsuleJoinRadiusDelta<=0.005&&Stats.MaxCapsuleJoinFloorDelta<=0.005
&&SmoothUnionGrowthBound<=0.25?"PASS":"FAIL");
const double BendHorizontal=std::sqrt(Stats.MaxTunnelBendDirection.X*Stats.MaxTunnelBendDirection.X
@@ -231,17 +239,23 @@ public:
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),1.0f/16.0f);
return Density;
}
return bRock?-(DetailBand+2.0f):(DetailBand+2.0f);
// Match the fixed fallback to the edge of the stored proxy band. The
// displacement is tapered to zero there, so this avoids a magnitude
// jump along the candidate bit-mask boundary.
constexpr float ProxyBandEdge=DetailSupremum+0.125f;
return bRock?-ProxyBandEdge:ProxyBandEdge;
}
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false;
float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,&bFloorSurface);
bool bFloorSurface=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence);
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]
&&Z>=Min[2]&&Z<=Max[2];
const bool bCriticalAir=IsInsideCriticalAir(X,Y,Z);
if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth
&&std::abs(RockDistance)<=DetailSupremum+0.125f)
RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,SurfaceFamily,bFloorSurface);
RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)
*DetailBandFade(RockDistance);
if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f);
float Density=-RockDistance;
if((Density<0.0f)!=bRock||std::abs(Density)<0.05f)
@@ -335,10 +349,12 @@ public:
const size_t Zi=W*64+Bit;if(Zi>=static_cast<size_t>(Size[2]))continue;
const int32 Z=Min[2]+static_cast<int32>(Zi);
ESurfaceFamily Family=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false;
const float Raw=EffectiveRockDistance(X,Y,Z,&Family,&SurfaceSDF,&bFloorSurface);
bool bFloorSurface=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
const float Raw=EffectiveRockDistance(X,Y,Z,&Family,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence);
if(std::abs(Raw)>=0.5f)continue;
const float Detail=DetailNoise(X,Y,Z,SurfaceSDF,Family,bFloorSurface);
const float Detail=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)
*DetailBandFade(Raw);
const size_t FamilyIndex=static_cast<size_t>(Family);
FFamilyStats& F=Families[FamilyIndex];++F.Count;F.Sum+=Detail;F.SumSq+=Detail*Detail;
if(std::abs(Detail)<0.05f)++F.NearZero;
@@ -392,22 +408,26 @@ public:
double Sum=0.0,SumSq=0.0;
};
std::array<FFamilyStats,3> FamilyStats{};
constexpr std::array<float,9> YawOffsets={-32.0f,-24.0f,-16.0f,-8.0f,0.0f,8.0f,16.0f,24.0f,32.0f};
constexpr std::array<float,5> PitchOffsets={-20.0f,-10.0f,0.0f,10.0f,20.0f};
constexpr std::array<float,13> YawOffsets={-36.0f,-30.0f,-24.0f,-18.0f,-12.0f,-6.0f,
0.0f,6.0f,12.0f,18.0f,24.0f,30.0f,36.0f};
constexpr std::array<float,11> PitchOffsets={-80.0f,-70.0f,-60.0f,-50.0f,-40.0f,
-30.0f,-20.0f,-10.0f,0.0f,10.0f,20.0f};
const double OX=CameraX*4.0,OY=CameraY*4.0,OZ=CameraZ*4.0;
auto Distance=[&](double X,double Y,double Z,ESurfaceFamily* OutFamily=nullptr,
float* OutRaw=nullptr,float* OutDetail=nullptr,
bool* OutFloorSurface=nullptr)
{
ESurfaceFamily Family=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false;
float D=EffectiveRockDistance(X,Y,Z,&Family,&SurfaceSDF,&bFloorSurface);
bool bFloorSurface=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
float D=EffectiveRockDistance(X,Y,Z,&Family,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence);
float Detail=0.0f;
const bool bInside=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]
&&Z>=Min[2]&&Z<=Max[2];
const bool bCriticalAir=IsInsideCriticalAir(X,Y,Z);
if(bInside&&DetailVariant!=EDetailVariant::Smooth&&std::abs(D)<=DetailSupremum+0.125f)
{Detail=DetailNoise(X,Y,Z,SurfaceSDF,Family,bFloorSurface);D+=Detail;}
{Detail=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)
*DetailBandFade(D);D+=Detail;}
if(bCriticalAir)D=std::min(D,-1.0f/16.0f);
if(OutFamily)*OutFamily=Family;
if(OutRaw)*OutRaw=D-Detail;
@@ -415,7 +435,9 @@ public:
if(OutFloorSurface)*OutFloorSurface=bFloorSurface;
return D;
};
uint64 TotalHits=0,TotalAxisLocked=0,TotalNearZero=0;
uint64 TotalHits=0,TotalAxisLocked=0,TotalNearZero=0,FloorTaggedHits=0,FloorAxisLocked=0;
uint64 FamilyTransitionPairs=0,StableFamilyPairs=0;
double FamilyTransitionGradientSq=0.0,StableFamilyGradientSq=0.0;
for(float YawOffset:YawOffsets)for(float PitchOffset:PitchOffsets)
{
const double YawRad=Yaw+YawOffset*0.017453292519943295;
@@ -447,10 +469,50 @@ public:
const double GZ=Distance(XH,YH,ZH+0.5)-Distance(XH,YH,ZH-0.5);
const double GL=std::sqrt(GX*GX+GY*GY+GZ*GZ);
const double AxisDot=GL>1.0e-8?std::max({std::abs(GX),std::abs(GY),std::abs(GZ)})/GL:1.0;
for(int32 Axis=0;Axis<3;++Axis)
{
double AP[3]={XH,YH,ZH},AM[3]={XH,YH,ZH};
AP[Axis]+=0.5;AM[Axis]-=0.5;
ESurfaceFamily FamilyP=ESurfaceFamily::Strata,FamilyM=ESurfaceFamily::Strata;
float DetailP=0.0f,DetailM=0.0f;bool bFloorP=false,bFloorM=false;
Distance(AP[0],AP[1],AP[2],&FamilyP,nullptr,&DetailP,&bFloorP);
Distance(AM[0],AM[1],AM[2],&FamilyM,nullptr,&DetailM,&bFloorM);
const double Gradient=static_cast<double>(DetailP-DetailM);
if(FamilyP!=FamilyM||bFloorP!=bFloorM)
{FamilyTransitionGradientSq+=Gradient*Gradient;++FamilyTransitionPairs;}
else
{StableFamilyGradientSq+=Gradient*Gradient;++StableFamilyPairs;}
}
constexpr double ScanRadius=16.0;
constexpr int32 ScanSteps=128;
double ScanT=std::max(0.0,HitT-ScanRadius);
ESurfaceFamily ScanFamily=ESurfaceFamily::Strata;
bool bScanFloor=false;float ScanRaw=0.0f,ScanDetail=0.0f;
Distance(OX+DX*ScanT,OY+DY*ScanT,OZ+DZ*ScanT,
&ScanFamily,&ScanRaw,&ScanDetail,&bScanFloor);
for(int32 Step=1;Step<=ScanSteps;++Step)
{
const double T=std::min(HitT+ScanRadius,ScanT+0.25);
ESurfaceFamily NextFamily=ESurfaceFamily::Strata;
bool bNextFloor=false;float NextRaw=0.0f,NextDetail=0.0f;
Distance(OX+DX*T,OY+DY*T,OZ+DZ*T,
&NextFamily,&NextRaw,&NextDetail,&bNextFloor);
if(std::abs(ScanRaw)<=5.0f&&std::abs(NextRaw)<=5.0f)
{
const double Gradient=static_cast<double>(NextDetail-ScanDetail);
if(ScanFamily!=NextFamily||bScanFloor!=bNextFloor)
{FamilyTransitionGradientSq+=Gradient*Gradient;++FamilyTransitionPairs;}
else
{StableFamilyGradientSq+=Gradient*Gradient;++StableFamilyPairs;}
}
ScanT=T;ScanFamily=NextFamily;bScanFloor=bNextFloor;
ScanRaw=NextRaw;ScanDetail=NextDetail;
}
FFamilyStats& S=FamilyStats[static_cast<size_t>(Family)];
++S.Hits;++TotalHits;S.Sum+=Detail;S.SumSq+=Detail*Detail;
if(std::abs(Detail)<0.05f){++S.NearZero;++TotalNearZero;}
if(AxisDot>0.985){++S.AxisLocked;++TotalAxisLocked;}
if(bFloorSurface){++FloorTaggedHits;if(AxisDot>0.985)++FloorAxisLocked;}
if(YawOffset==0.0f&&PitchOffset==0.0f)
{
const char* Name=Family==ESurfaceFamily::Strata?"strata":
@@ -466,17 +528,31 @@ public:
}
const double NearZeroShare=TotalHits?static_cast<double>(TotalNearZero)/TotalHits:1.0;
const double AxisShare=TotalHits?static_cast<double>(TotalAxisLocked)/TotalHits:1.0;
const double FloorAxisShare=FloorTaggedHits?static_cast<double>(FloorAxisLocked)/FloorTaggedHits:1.0;
const double TransitionRMS=FamilyTransitionPairs
?std::sqrt(FamilyTransitionGradientSq/FamilyTransitionPairs):0.0;
const double StableRMS=StableFamilyPairs
?std::sqrt(StableFamilyGradientSq/StableFamilyPairs):0.0;
const double TransitionRatio=StableRMS>1.0e-6?TransitionRMS/StableRMS:1000000.0;
double Sum=0.0,SumSq=0.0;
for(const FFamilyStats& S:FamilyStats){Sum+=S.Sum;SumSq+=S.SumSq;}
const double Mean=TotalHits?Sum/TotalHits:0.0;
const double Variance=TotalHits?std::max(0.0,SumSq/TotalHits-Mean*Mean):0.0;
const double SurfaceDetailRMS=std::sqrt(Variance);
const double RelativeJoinStep=SurfaceDetailRMS>1.0e-6?TransitionRMS/SurfaceDetailRMS:1000000.0;
const bool bDetailPass=TotalHits>=24&&std::sqrt(Variance)>=0.15&&NearZeroShare<0.50;
const bool bGridPass=TotalHits>=24&&AxisShare<0.60;
const bool bJoinCoverage=FloorTaggedHits>=8
&&(FamilyTransitionPairs==0||(FamilyTransitionPairs>=8&&RelativeJoinStep<0.45));
const bool bGridPass=TotalHits>=24&&AxisShare<0.60
&&bJoinCoverage;
std::printf("[FlatSurfaceAudit] rays=%llu detail_stddev=%.4f vox near_zero_share=%.1f%% threshold=50.0%% status=%s\n",
static_cast<unsigned long long>(TotalHits),std::sqrt(Variance),NearZeroShare*100.0,
bDetailPass?"PASS":"FAIL");
std::printf("[GridStairAudit] rays=%llu axis_locked_normals=%.1f%% threshold=60.0%% status=%s\n",
static_cast<unsigned long long>(TotalHits),AxisShare*100.0,bGridPass?"PASS":"FAIL");
std::printf("[GridStairAudit] rays=%llu axis_locked_normals=%.1f%% threshold=60.0%% floor_tagged_hits=%llu floor_axis_locked=%.1f%% family_transition_pairs=%llu transition_rms=%.4f vox stable_rms=%.4f vox surface_detail_rms=%.4f vox relative_join_step=%.3f threshold=0.450 legacy_join_ratio=%.3f status=%s\n",
static_cast<unsigned long long>(TotalHits),AxisShare*100.0,
static_cast<unsigned long long>(FloorTaggedHits),FloorAxisShare*100.0,
static_cast<unsigned long long>(FamilyTransitionPairs),TransitionRMS,StableRMS,
SurfaceDetailRMS,RelativeJoinStep,TransitionRatio,bGridPass?"PASS":"FAIL");
for(size_t I=0;I<FamilyStats.size();++I)
{
const char* Name=I==0?"strata":I==1?"cave":"solid_overlay";
@@ -523,6 +599,19 @@ private:
}
static float SmoothMaxDistance(float A,float B,float Width)
{return -SmoothMinDistance(-A,-B,Width);}
static float SmoothUnit(float V)
{
V=std::clamp(V,0.0f,1.0f);
return V*V*(3.0f-2.0f*V);
}
static float DetailBandFade(float SignedDistance)
{
// Keep full relief near the visible surface, then blend it to zero over
// the outer 1.5 voxels of the proxy support band.
constexpr float Edge=DetailSupremum+0.125f;
constexpr float Width=1.5f;
return SmoothUnit((Edge-std::abs(SignedDistance))/Width);
}
void BuildLayerBounds()
{
@@ -844,8 +933,13 @@ private:
FVector(R.Center.X,R.Center.Y,R.FloorCutZ-Relief),Params,
static_cast<uint32>(Owner->Seed)).Z;
for(size_t I=RoomPrimitiveStart;I<Primitives.size();++I)
if(Primitives[I].bCarve)ApplyFloorClip(Primitives[I],FloorZ,0.0,0.0,0.0);
if(Primitives[I].bCarve)ApplyFloorClip(Primitives[I],FloorZ,0.0,0.0,3.0);
}
// The packed signs still union room intervals exactly. Blend only
// the analytic cave field where neighboring authored rooms meet;
// this rounds the room-to-room edge without touching tunnel radii.
for(size_t I=RoomPrimitiveStart;I<Primitives.size();++I)
if(Primitives[I].bCarve)Primitives[I].BlendRadius=RoomUnionBlendRadius;
}
for(const FCachedRoomFloorJoin& J:Cache.RoomFloorJoins)
{
@@ -854,7 +948,7 @@ private:
}
const double RoomBlendRadius=ActiveBlendRadius;
ActiveBlendRadius=JoinBlendRadius;
ActiveFloorBlendRadius=1.5;
ActiveFloorBlendRadius=3.0;
for(const FCachedTunnel& T:Cache.Tunnels)
{
++Stats.TunnelCount;
@@ -887,9 +981,27 @@ private:
Stats.LegacySegmentRadiusStep=std::max(Stats.LegacySegmentRadiusStep,
static_cast<double>(std::abs(std::max(RPrev,RHere)-std::max(RHere,RNext))));
if(FloorProfiles.IsValidIndex(Join-1)&&FloorProfiles.IsValidIndex(Join))
{
Stats.MaxCapsuleJoinFloorDelta=std::max(Stats.MaxCapsuleJoinFloorDelta,
static_cast<double>(std::abs(FloorProfiles[Join-1].EndFloorZ
-FloorProfiles[Join].StartFloorZ)));
const FVector PrevD=Points[Join]-Points[Join-1];
const FVector NextD=Points[Join+1]-Points[Join];
const double PrevXY=std::sqrt(PrevD.X*PrevD.X+PrevD.Y*PrevD.Y);
const double NextXY=std::sqrt(NextD.X*NextD.X+NextD.Y*NextD.Y);
const double PrevSlope=PrevXY>1.0e-6
?(FloorProfiles[Join-1].EndFloorZ-FloorProfiles[Join-1].StartFloorZ)/PrevXY:0.0;
const double NextSlope=NextXY>1.0e-6
?(FloorProfiles[Join].EndFloorZ-FloorProfiles[Join].StartFloorZ)/NextXY:0.0;
const double SlopeDelta=std::abs(PrevSlope-NextSlope);
Stats.MaxCapsuleJoinFloorSlopeDelta=std::max(Stats.MaxCapsuleJoinFloorSlopeDelta,SlopeDelta);
if(FloorProfiles[Join-1].bLedgeTransition||FloorProfiles[Join].bLedgeTransition)
{
++Stats.MarkedFloorSlopeJoins;
Stats.MaxMarkedFloorSlopeDelta=std::max(Stats.MaxMarkedFloorSlopeDelta,SlopeDelta);
}
else Stats.MaxUnmarkedFloorSlopeDelta=std::max(Stats.MaxUnmarkedFloorSlopeDelta,SlopeDelta);
}
++Stats.CapsuleJoinCount;
}
if(Points.Num()>1)
@@ -904,7 +1016,7 @@ private:
const FVector Segment=Points[I+1]-Points[I];
const double SegmentLength=std::sqrt(Segment.X*Segment.X+Segment.Y*Segment.Y+Segment.Z*Segment.Z);
const double Extension=ContinuousJoinExtension(R0,R1,SegmentLength);
float ZA=Points[I].Z-R0, ZB=Points[I+1].Z-R1;
float ZA=Points[I].Z-R0,ZB=Points[I+1].Z-R1;
if(FloorProfiles.IsValidIndex(I))ZA=FloorProfiles[I].StartFloorZ;
if(FloorProfiles.IsValidIndex(I))ZB=FloorProfiles[I].EndFloorZ;
AddTaperedCapsule(Points[I],Points[I+1],R0,R1,true,false,true,ZA,ZB,
@@ -940,7 +1052,7 @@ private:
}
}
const double PassageJoinBlend=ActiveBlendRadius;ActiveBlendRadius=JoinBlendRadius;
ActiveFloorBlendRadius=1.5;
ActiveFloorBlendRadius=3.0;
for(const FVoxelPassage& P:Owner->Manager->GetPassages())
{
++Stats.PassageCount;
@@ -954,6 +1066,20 @@ private:
const float RNext=std::max(6.0f,Radii.IsValidIndex(Join+1)?Radii[Join+1]:P.Radius);
Stats.LegacySegmentRadiusStep=std::max(Stats.LegacySegmentRadiusStep,
static_cast<double>(std::abs(std::max(RPrev,RHere)-std::max(RHere,RNext))));
if(P.NativeFloorProfileZ.IsValidIndex(Join-1)&&P.NativeFloorProfileZ.IsValidIndex(Join))
{
const FVector PrevD=Points[Join]-Points[Join-1];
const FVector NextD=Points[Join+1]-Points[Join];
const double PrevXY=std::sqrt(PrevD.X*PrevD.X+PrevD.Y*PrevD.Y);
const double NextXY=std::sqrt(NextD.X*NextD.X+NextD.Y*NextD.Y);
const double PrevSlope=PrevXY>1.0e-6
?(P.NativeFloorProfileZ[Join]-P.NativeFloorProfileZ[Join-1])/PrevXY:0.0;
const double NextSlope=NextXY>1.0e-6
?(P.NativeFloorProfileZ[Join+1]-P.NativeFloorProfileZ[Join])/NextXY:0.0;
const double SlopeDelta=std::abs(PrevSlope-NextSlope);
Stats.MaxCapsuleJoinFloorSlopeDelta=std::max(Stats.MaxCapsuleJoinFloorSlopeDelta,SlopeDelta);
Stats.MaxUnmarkedFloorSlopeDelta=std::max(Stats.MaxUnmarkedFloorSlopeDelta,SlopeDelta);
}
++Stats.CapsuleJoinCount;
}
if(Points.Num()>1)
@@ -1562,10 +1688,12 @@ private:
++LocalCandidates;
const bool bCriticalAir=IsInsideCriticalAir(X,Y,Z);
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false;
const float SDF=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,&bFloorSurface);
bool bFloorSurface=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
const float SDF=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence);
if(std::abs(SDF)>DetailSupremum+0.125f)continue;
float Displaced=SDF+DetailNoise(X,Y,Z,SurfaceSDF,SurfaceFamily,bFloorSurface);
float Displaced=SDF+DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)
*DetailBandFade(SDF);
if(bCriticalAir)Displaced=std::min(Displaced,-1.0f/16.0f);
const bool bNewRock=Displaced>=0.0f;
const bool bOldRock=((RockWords[WordBase+(Zi>>6)]>>(Zi&63u))&1u)!=0;
@@ -1660,52 +1788,48 @@ private:
{
const FPrimitive& P=Primitives[Id];if(!P.bCarve||!P.bCritical)continue;
if(X<P.Min[0]||X>P.Max[0]||Y<P.Min[1]||Y>P.Max[1]||Z<P.Min[2]||Z>P.Max[2])continue;
if(ShapeDistance(P,X,Y,Z)>0.0f)continue;
if(P.bHasFloorClip)
{
const double Floor=P.FloorCenterZ+P.FloorSlopeX*(X-P.CenterSub[0]/16.0)
+P.FloorSlopeY*(Y-P.CenterSub[1]/16.0);
if(Z<Floor)continue;
}
return true;
if(ShapeContains(P,X,Y,Z))return true;
}
return false;
}
float CaveDistance(double X,double Y,double Z,bool* OutFloorSurface=nullptr) const
float CaveDistance(double X,double Y,double Z,bool* OutFloorSurface=nullptr,
float* OutFloorInfluence=nullptr) const
{
const auto It=SpatialBins.find(PointBin(X,Y,Z));
if(It==SpatialBins.end())
{
if(OutFloorSurface)*OutFloorSurface=false;
if(OutFloorInfluence)*OutFloorInfluence=0.0f;
return 100000.0f;
}
float Best=100000.0f;
bool bBestFloorSurface=false;
float BestFloorInfluence=0.0f;
for(uint32 Id:It->second)
{
const FPrimitive& P=Primitives[Id];if(!P.bCarve)continue;
if(X<P.Min[0]-DetailBand||X>P.Max[0]+DetailBand||Y<P.Min[1]-DetailBand||Y>P.Max[1]+DetailBand
||Z<P.Min[2]-DetailBand||Z>P.Max[2]+DetailBand)continue;
float Distance=ShapeDistance(P,X,Y,Z);
bool bFloorInfluencesSurface=false;
float PrimitiveFloorInfluence=0.0f;
if(P.bHasFloorClip)
{
const double Floor=P.FloorCenterZ+P.FloorSlopeX*(X-P.CenterSub[0]/16.0)
+P.FloorSlopeY*(Y-P.CenterSub[1]/16.0);
bFloorInfluencesSurface=OutFloorSurface!=nullptr
&&std::abs(Z-Floor)<=DetailBand+P.FloorBlendRadius;
const float FloorT=(static_cast<float>(std::abs(Z-Floor))-static_cast<float>(P.FloorBlendRadius))
/static_cast<float>(std::max(DetailBand*2,1));
PrimitiveFloorInfluence=1.0f-SmoothUnit(FloorT);
Distance=SmoothMaxDistance(Distance,static_cast<float>(Floor-Z),
static_cast<float>(P.FloorBlendRadius));
}
const float MetadataWidth=static_cast<float>(std::max(P.BlendRadius,6.0));
const float PrimitiveWeight=SmoothUnit(0.5f+(Best-Distance)/(2.0f*MetadataWidth));
const float Combined=SmoothMinDistance(Best,Distance,static_cast<float>(P.BlendRadius));
if(Combined<Best)
{
Best=Combined;
bBestFloorSurface=bFloorInfluencesSurface;
}
BestFloorInfluence=std::lerp(BestFloorInfluence,PrimitiveFloorInfluence,PrimitiveWeight);
Best=Combined;
}
if(OutFloorSurface)*OutFloorSurface=bBestFloorSurface;
if(OutFloorInfluence)*OutFloorInfluence=BestFloorInfluence;
if(OutFloorSurface)*OutFloorSurface=BestFloorInfluence>=0.5f;
return Best;
}
@@ -1725,27 +1849,37 @@ private:
float EffectiveRockDistance(double X,double Y,double Z,
ESurfaceFamily* OutFamily=nullptr,
float* OutSurfaceSDF=nullptr,
bool* OutFloorSurface=nullptr) const
bool* OutFloorSurface=nullptr,
float* OutCaveInfluence=nullptr,
float* OutFloorInfluence=nullptr) const
{
const float Base=StrataDistance(Z);
bool bCaveFloorSurface=false;
const float Cave=CaveDistance(X,Y,Z,OutFloorSurface?&bCaveFloorSurface:nullptr);
float CaveFloorInfluence=0.0f;
const float Cave=CaveDistance(X,Y,Z,nullptr,
(OutFloorSurface||OutFloorInfluence)?&CaveFloorInfluence:nullptr);
const float Overlay=SolidOverlayDistance(X,Y,Z);
const float CarveOrOverlay=std::max(Cave,Overlay);
const ESurfaceFamily Family=Base<=CarveOrOverlay?ESurfaceFamily::Strata:
(Cave>=Overlay?ESurfaceFamily::Cave:ESurfaceFamily::SolidOverlay);
constexpr float FamilyBlendWidth=6.0f;
const float CaveVsOverlay=SmoothUnit(0.5f+(Cave-Overlay)/(2.0f*FamilyBlendWidth));
const float CarveInfluence=SmoothUnit(0.5f+(Base-CarveOrOverlay)/(2.0f*FamilyBlendWidth));
const float CaveInfluence=CarveInfluence*CaveVsOverlay;
const float FloorInfluence=CaveInfluence*CaveFloorInfluence;
if(OutFamily)
*OutFamily=Family;
if(OutSurfaceSDF)
*OutSurfaceSDF=Family==ESurfaceFamily::Strata?Base:
(Family==ESurfaceFamily::Cave?Cave:Overlay);
if(OutFloorSurface)*OutFloorSurface=Family==ESurfaceFamily::Cave&&bCaveFloorSurface;
return std::min(Base,CarveOrOverlay);
if(OutCaveInfluence)*OutCaveInfluence=CaveInfluence;
if(OutFloorInfluence)*OutFloorInfluence=FloorInfluence;
if(OutFloorSurface)*OutFloorSurface=FloorInfluence>=0.5f;
return SmoothMinDistance(Base,CarveOrOverlay,StrataCaveBlendRadius);
}
float OwnerCaveRoughness(double X,double Y,double Z,float SurfaceSDF,
const FStrateGenerationParams& Params,
bool bCaveSurface) const
float CaveInfluence) const
{
const float Depth=Params.SurfaceRoughness*2.0f;
if(!(Depth>0.0f)||std::abs(SurfaceSDF)>=Depth||!(Params.RoughnessFrequency>0.0f))return 0.0f;
@@ -1795,7 +1929,8 @@ private:
FineValue=VoxelNoise::FBM(Fine.X,Fine.Y,Fine.Z,2);break;
}
float Total=(Rough+FineValue*0.4f)*VOXEL_NOISE_SCALE*Params.SurfaceRoughness;
if(bCaveSurface&&SurfaceSDF<0.0f)Total=std::min(Total,0.0f);
if(SurfaceSDF<0.0f)
Total=std::lerp(Total,std::min(Total,0.0f),std::clamp(CaveInfluence,0.0f,1.0f));
const float Fade=1.0f-std::abs(SurfaceSDF)/Depth;
return Total*Fade*Fade;
}
@@ -1808,7 +1943,7 @@ private:
}
float DetailNoise(double X,double Y,double Z,float SurfaceSDF,
ESurfaceFamily SurfaceFamily,bool bFloorSurface=false) const
float CaveInfluence,float FloorInfluence) const
{
if(DetailVariant==EDetailVariant::Smooth)return 0.0f;
if(DetailVariant==EDetailVariant::PerVoxel)
@@ -1820,9 +1955,9 @@ private:
static_cast<float>(Y),static_cast<float>(Z),static_cast<uint32>(Owner->Seed));
const FLayerField* Field=FindLayerField(Z);
const float OwnerRelief=Field?OwnerCaveRoughness(X,Y,Z,SurfaceSDF,Field->Params,
SurfaceFamily==ESurfaceFamily::Cave):0.0f;
const float Contrast=bFloorSurface?2.40f:
(SurfaceFamily==ESurfaceFamily::Cave?0.55f:1.35f);
CaveInfluence):0.0f;
const float Contrast=1.35f-0.80f*std::clamp(CaveInfluence,0.0f,1.0f)
+1.85f*std::clamp(FloorInfluence,0.0f,1.0f);
return std::clamp((Relief+OwnerRelief*0.5f)*Contrast,-3.9f,3.9f);
}
@@ -1830,12 +1965,14 @@ private:
{
const bool bCriticalAir=IsInsideCriticalAir(X,Y,Z);
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false;
float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,&bFloorSurface);
bool bFloorSurface=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence);
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]&&Z>=Min[2]&&Z<=Max[2];
if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth
&&std::abs(RockDistance)<=DetailSupremum+0.125f)
RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,SurfaceFamily,bFloorSurface);
RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)
*DetailBandFade(RockDistance);
if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f);
return RockDistance>=0.0f;
}