perf(core): FloatingIslands whole view 16.7 s -> 7.4 s (L51); research R20
Measured first: 45% of FI tiles are all-air, yet FloatingIslandFieldV1 returned unknown for any box near an island AABB (25,536 unknown boxes, 0 tile skips). Conservative ranges for island bodies, root lobes, buttresses, connectors, surface detail and smooth-min budgets now let the shared box classifier prove FI air/solid (216 tile skips; 28.6 M audited samples, 0 violations). The X8 path reuses its FI preflight samples. LOD7-8 far-shell and column work is skipped outside per-tile island/connector influence bounds (farHorizonVersion 4 -> 5). FI 16.74 -> 7.36 s (-56%), FI moving entering-tile p50 halved; SW unchanged. Peer determinism (Clang/GCC W1/W8/W12, random cameras) PASS; fast/medium/full PASS. Research R20: world-type catalogue and dreamcore/liminal concepts (revised after a deepening pass). 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
ca85e876ad
commit
75550256ef
9 files changed
+793
-23
No files matched your search
@@ -1634,7 +1634,7 @@ namespace VoxelForgeCore
|
||||
// LOD4-8 mesh hashes are salted; density and LOD0-3 keep their direct identities.
|
||||
// Incremente quand les plafonds partages ou l'horizon lointain changent.
|
||||
// Les hashes de maillage LOD4-8 incluent cette version; LOD0-3 gardent leur identite directe.
|
||||
std::uint32_t farHorizonVersion = 4;
|
||||
std::uint32_t farHorizonVersion = 5;
|
||||
// Linux research controls for reproducing L5 negative controls. Defaults preserve output.
|
||||
std::uint32_t diagnosticGlobalOctaveDrop = 0;
|
||||
std::uint32_t diagnosticCellStride = 1;
|
||||
|
||||
@@ -695,6 +695,38 @@ namespace floating_island_detail
|
||||
return (std::sqrt(std::max(q, 0.0)) - 1.0) * Parameters.MinimumRadius;
|
||||
}
|
||||
|
||||
inline FRootLobeParameters RootLobeParametersForBounds(
|
||||
const FloatingIslandRecipeV1& recipe,const FloatingIslandAnchorV1& island,
|
||||
std::int32_t rootIndex)
|
||||
{
|
||||
constexpr std::array<std::array<double,2>,4> pattern{{
|
||||
{{-0.92,-0.12}},{{0.28,0.98}},{{0.83,-0.42}},{{-0.16,-0.94}}}};
|
||||
const std::uint64_t salt=0x524f4f540000ull+static_cast<std::uint64_t>(rootIndex);
|
||||
const std::uint64_t roll=Roll(recipe.seed,island.stableId,salt);
|
||||
const double radius=static_cast<double>(island.radiusSubvoxels);
|
||||
const double rootDepth=radius*Q16ToDouble(recipe.rootDepthRatioQ16);
|
||||
const double crustBottom=static_cast<double>(IslandCrustBottom(recipe,island));
|
||||
const bool centralButtress=rootIndex==4;
|
||||
const double orbit=centralButtress?0.0:radius*(0.17+0.07*UnitHash(roll));
|
||||
const double rootX=static_cast<double>(island.centerSubvoxels[0])
|
||||
+(centralButtress?0.0:pattern[static_cast<std::size_t>(rootIndex)][0]*orbit);
|
||||
const double rootY=static_cast<double>(island.centerSubvoxels[1])
|
||||
+(centralButtress?0.0:pattern[static_cast<std::size_t>(rootIndex)][1]*orbit);
|
||||
const double shortRootVariation=UnitHash(Roll(recipe.seed,island.stableId,salt^0x4c454eull));
|
||||
const double lengthRatio=centralButtress?1.0:(rootIndex==0?1.0
|
||||
:Q16ToDouble(island.taperEndQ16)*(0.82+0.18*shortRootVariation));
|
||||
const double lowerDepth=rootDepth*lengthRatio;
|
||||
const double embedDepth=rootDepth*(centralButtress?0.22:(rootIndex==0?0.30:0.22));
|
||||
const double verticalRadius=0.5*(lowerDepth+embedDepth);
|
||||
const double centerZ=crustBottom+0.5*(embedDepth-lowerDepth);
|
||||
const double rx=radius*(centralButtress?0.25
|
||||
:0.15+0.055*UnitHash(Roll(recipe.seed,island.stableId,salt^0x5258ull)));
|
||||
const double ry=radius*(centralButtress?0.25
|
||||
:0.15+0.065*UnitHash(Roll(recipe.seed,island.stableId,salt^0x5259ull)));
|
||||
return {rootX,rootY,centerZ,rx*rx,ry*ry,verticalRadius*verticalRadius,
|
||||
std::min({rx,ry,verticalRadius})};
|
||||
}
|
||||
|
||||
inline double ApplySurfaceDetail(const FloatingIslandRecipeV1& recipe,
|
||||
std::uint64_t featureId, double structural,
|
||||
double x, double y, double z, double detailScale = 1.0)
|
||||
@@ -845,6 +877,227 @@ namespace floating_island_detail
|
||||
return std::max({horizontal, upper, lower});
|
||||
}
|
||||
|
||||
struct FScalarRange
|
||||
{
|
||||
double minimum=0.0,maximum=0.0;
|
||||
};
|
||||
|
||||
struct FDensityRange
|
||||
{
|
||||
double minimum=0.0,maximum=0.0;
|
||||
};
|
||||
|
||||
inline double DistanceToRange(double minimum,double maximum,double value)
|
||||
{
|
||||
if(value<minimum)return minimum-value;
|
||||
if(value>maximum)return value-maximum;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
inline double MaximumDistanceToRange(double minimum,double maximum,double value)
|
||||
{
|
||||
return std::max(std::abs(minimum-value),std::abs(maximum-value));
|
||||
}
|
||||
|
||||
inline FScalarRange AffineRange(double xMinimum,double xMaximum,double xOffset,
|
||||
double xScale,double yMinimum,double yMaximum,double yOffset,double yScale)
|
||||
{
|
||||
const double xa=(xMinimum-xOffset)*xScale,xb=(xMaximum-xOffset)*xScale;
|
||||
const double ya=(yMinimum-yOffset)*yScale,yb=(yMaximum-yOffset)*yScale;
|
||||
return {std::min(xa,xb)+std::min(ya,yb),
|
||||
std::max(xa,xb)+std::max(ya,yb)};
|
||||
}
|
||||
|
||||
inline FScalarRange AbsoluteRange(double minimum,double maximum)
|
||||
{
|
||||
const double minimumAbsolute=minimum<=0.0&&maximum>=0.0
|
||||
?0.0:std::min(std::abs(minimum),std::abs(maximum));
|
||||
return {minimumAbsolute,std::max(std::abs(minimum),std::abs(maximum))};
|
||||
}
|
||||
|
||||
inline double RectangleSdf(double x,double y)
|
||||
{
|
||||
return std::hypot(std::max(x,0.0),std::max(y,0.0))
|
||||
+std::min(std::max(x,y),0.0);
|
||||
}
|
||||
|
||||
inline double RoundedBoxSdf(double x,double y,double z)
|
||||
{
|
||||
return std::sqrt(std::max(x,0.0)*std::max(x,0.0)
|
||||
+std::max(y,0.0)*std::max(y,0.0)
|
||||
+std::max(z,0.0)*std::max(z,0.0))
|
||||
+std::min(std::max(x,std::max(y,z)),0.0);
|
||||
}
|
||||
|
||||
inline FDensityRange RootLobeRange(const FloatingIslandRecipeV1& recipe,
|
||||
const FloatingIslandAnchorV1& island,std::int32_t rootIndex,
|
||||
const std::array<double,3>& minimum,const std::array<double,3>& maximum)
|
||||
{
|
||||
const FRootLobeParameters root=RootLobeParametersForBounds(recipe,island,rootIndex);
|
||||
const double dxMinimum=DistanceToRange(minimum[0],maximum[0],root.RootX);
|
||||
const double dyMinimum=DistanceToRange(minimum[1],maximum[1],root.RootY);
|
||||
const double dzMinimum=DistanceToRange(minimum[2],maximum[2],root.CenterZ);
|
||||
const double dxMaximum=MaximumDistanceToRange(minimum[0],maximum[0],root.RootX);
|
||||
const double dyMaximum=MaximumDistanceToRange(minimum[1],maximum[1],root.RootY);
|
||||
const double dzMaximum=MaximumDistanceToRange(minimum[2],maximum[2],root.CenterZ);
|
||||
const double qMinimum=dxMinimum*dxMinimum/root.RadiusXSquared
|
||||
+dyMinimum*dyMinimum/root.RadiusYSquared
|
||||
+dzMinimum*dzMinimum/root.VerticalRadiusSquared;
|
||||
const double qMaximum=dxMaximum*dxMaximum/root.RadiusXSquared
|
||||
+dyMaximum*dyMaximum/root.RadiusYSquared
|
||||
+dzMaximum*dzMaximum/root.VerticalRadiusSquared;
|
||||
return {(std::sqrt(std::max(qMinimum,0.0))-1.0)*root.MinimumRadius,
|
||||
(std::sqrt(std::max(qMaximum,0.0))-1.0)*root.MinimumRadius};
|
||||
}
|
||||
|
||||
inline FDensityRange IslandDensityRange(const FloatingIslandRecipeV1& recipe,
|
||||
const FloatingIslandAnchorV1& island,const std::array<double,3>& minimum,
|
||||
const std::array<double,3>& maximum)
|
||||
{
|
||||
const double radius=static_cast<double>(island.radiusSubvoxels);
|
||||
const double contour=static_cast<double>(recipe.contourWarpSubvoxels);
|
||||
const double radialMinimum=std::hypot(
|
||||
DistanceToRange(minimum[0],maximum[0],
|
||||
static_cast<double>(island.centerSubvoxels[0])),
|
||||
DistanceToRange(minimum[1],maximum[1],
|
||||
static_cast<double>(island.centerSubvoxels[1])));
|
||||
const double radialMaximum=std::max({
|
||||
std::hypot(minimum[0]-island.centerSubvoxels[0],minimum[1]-island.centerSubvoxels[1]),
|
||||
std::hypot(minimum[0]-island.centerSubvoxels[0],maximum[1]-island.centerSubvoxels[1]),
|
||||
std::hypot(maximum[0]-island.centerSubvoxels[0],minimum[1]-island.centerSubvoxels[1]),
|
||||
std::hypot(maximum[0]-island.centerSubvoxels[0],maximum[1]-island.centerSubvoxels[1])});
|
||||
|
||||
const double top=static_cast<double>(island.topZSubvoxels);
|
||||
const double topDrop=std::max(0.0,(1.0-Q16ToDouble(recipe.topFlattenQ16))
|
||||
*static_cast<double>(island.topHalfSubvoxels)*2.0);
|
||||
const double topDistanceMinimum=minimum[2]-top;
|
||||
const double topDistanceMaximum=maximum[2]-(top-topDrop);
|
||||
const double sideDistanceMinimum=radialMinimum-std::max(0.0,radius+contour);
|
||||
const double sideDistanceMaximum=recipe.geometryVersion<2
|
||||
?radialMaximum:radialMaximum-std::max(0.0,radius-contour);
|
||||
double lowerMinimum=0.0,lowerMaximum=0.0;
|
||||
if(recipe.geometryVersion<2)
|
||||
{
|
||||
const double bottom=static_cast<double>(island.bottomZSubvoxels);
|
||||
lowerMinimum=bottom-maximum[2];
|
||||
lowerMaximum=bottom-minimum[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
const double crust=static_cast<double>(IslandCrustBottom(recipe,island));
|
||||
lowerMinimum=crust-maximum[2];
|
||||
lowerMaximum=crust+radius*0.16-minimum[2];
|
||||
}
|
||||
double structuralMinimum=std::max({topDistanceMinimum,
|
||||
sideDistanceMinimum,lowerMinimum});
|
||||
double structuralMaximum=std::max({topDistanceMaximum,
|
||||
sideDistanceMaximum,lowerMaximum});
|
||||
|
||||
if(recipe.geometryVersion>=2)
|
||||
{
|
||||
const double rootBlend=std::clamp(radius*0.12,2.0*Q,5.0*Q);
|
||||
for(std::int32_t root=0;root<RootCount(recipe,island);++root)
|
||||
{
|
||||
const FDensityRange rootRange=RootLobeRange(recipe,island,root,minimum,maximum);
|
||||
structuralMinimum=std::min(structuralMinimum,rootRange.minimum)-rootBlend*0.25;
|
||||
structuralMaximum=std::min(structuralMaximum,rootRange.maximum);
|
||||
}
|
||||
if(recipe.geometryVersion>=5&&recipe.geometryVersion<8
|
||||
&&island.ordinal==recipe.anchorIslandOrdinal
|
||||
&&recipe.anchorRootBottomZSubvoxels<island.bottomZSubvoxels)
|
||||
{
|
||||
const double rootTop=static_cast<double>(IslandCrustBottom(recipe,island));
|
||||
const double rootBottom=static_cast<double>(recipe.anchorRootBottomZSubvoxels);
|
||||
const double rootRadius=static_cast<double>(recipe.anchorRootRadiusSubvoxels);
|
||||
const double anchorMinimum=std::max({radialMinimum-rootRadius,
|
||||
rootBottom-maximum[2],minimum[2]-rootTop});
|
||||
const double anchorMaximum=std::max({radialMaximum-0.92*rootRadius,
|
||||
rootBottom-minimum[2],maximum[2]-rootTop});
|
||||
const double anchorDetail=0.75*static_cast<double>(recipe.roughnessSubvoxels);
|
||||
const double anchorBlend=std::clamp(rootRadius*0.12,3.0*Q,6.0*Q);
|
||||
structuralMinimum=std::min(structuralMinimum,anchorMinimum-anchorDetail)
|
||||
-anchorBlend*0.25;
|
||||
structuralMaximum=std::min(structuralMaximum,anchorMaximum+anchorDetail);
|
||||
}
|
||||
}
|
||||
const double finalDetail=static_cast<double>(recipe.roughnessSubvoxels);
|
||||
return {structuralMinimum-finalDetail,structuralMaximum+finalDetail};
|
||||
}
|
||||
|
||||
inline FDensityRange ConnectorDensityRange(const FloatingIslandRecipeV1& recipe,
|
||||
const FloatingIslandConnectorV1& edge,const std::array<double,3>& minimum,
|
||||
const std::array<double,3>& maximum)
|
||||
{
|
||||
const double ax=static_cast<double>(edge.startSubvoxels[0]);
|
||||
const double ay=static_cast<double>(edge.startSubvoxels[1]);
|
||||
const double az=static_cast<double>(edge.startSubvoxels[2]);
|
||||
const double dx=static_cast<double>(edge.endSubvoxels[0])-ax;
|
||||
const double dy=static_cast<double>(edge.endSubvoxels[1])-ay;
|
||||
const double dz=static_cast<double>(edge.endSubvoxels[2])-az;
|
||||
const double length=std::hypot(dx,dy);
|
||||
if(!(length>0.0))return {static_cast<double>(MaxDensity),static_cast<double>(MaxDensity)};
|
||||
const double ux=dx/length,uy=dy/length;
|
||||
const FScalarRange along=AffineRange(minimum[0],maximum[0],ax,ux,
|
||||
minimum[1],maximum[1],ay,uy);
|
||||
const FScalarRange signedSide=AffineRange(minimum[0],maximum[0],ax,-uy,
|
||||
minimum[1],maximum[1],ay,ux);
|
||||
const FScalarRange side=AbsoluteRange(signedSide.minimum,signedSide.maximum);
|
||||
const double qxMinimum=side.minimum-static_cast<double>(edge.halfWidthSubvoxels);
|
||||
const double qxMaximum=side.maximum-static_cast<double>(edge.halfWidthSubvoxels);
|
||||
const FScalarRange alongCenter=AbsoluteRange(along.minimum-0.5*length,
|
||||
along.maximum-0.5*length);
|
||||
const double qyMinimum=alongCenter.minimum-0.5*length;
|
||||
const double qyMaximum=alongCenter.maximum-0.5*length;
|
||||
const double horizontalMinimum=RectangleSdf(qxMinimum,qyMinimum);
|
||||
const double horizontalMaximum=RectangleSdf(qxMaximum,qyMaximum);
|
||||
|
||||
const double tMinimum=std::clamp(along.minimum/length,0.0,1.0);
|
||||
const double tMaximum=std::clamp(along.maximum/length,0.0,1.0);
|
||||
const double deckAtMinimum=az+dz*tMinimum;
|
||||
const double deckAtMaximum=az+dz*tMaximum;
|
||||
const double deckMinimum=std::min(deckAtMinimum,deckAtMaximum);
|
||||
const double deckMaximum=std::max(deckAtMinimum,deckAtMaximum);
|
||||
const std::uint64_t westernEdgeId=StableId(recipe.seed,0x200000000ull,
|
||||
0x425249444745ull);
|
||||
const bool westernSupportConnector=edge.stableId==westernEdgeId;
|
||||
if(recipe.geometryVersion>=2&&WorldAssemblyEvaluatorVersion>=183
|
||||
&&westernSupportConnector)
|
||||
{
|
||||
const double minimumHalfWidth=static_cast<double>(recipe.routeConstraints.capsuleRadiusSubvoxels+Q);
|
||||
const double baseHalfWidth=static_cast<double>(edge.halfWidthSubvoxels);
|
||||
const double halfWidthMinimum=std::max(minimumHalfWidth,baseHalfWidth-0.48*Q);
|
||||
const double halfWidthMaximum=std::max(minimumHalfWidth,baseHalfWidth+0.48*Q);
|
||||
const double roundingMinimum=std::min({1.5*Q,halfWidthMinimum*0.6,
|
||||
static_cast<double>(edge.thicknessSubvoxels)*0.45});
|
||||
const double roundingMaximum=std::min({1.5*Q,halfWidthMaximum*0.6,
|
||||
static_cast<double>(edge.thicknessSubvoxels)*0.45});
|
||||
const double roundedXMinimum=side.minimum-halfWidthMaximum+roundingMinimum;
|
||||
const double roundedXMaximum=side.maximum-halfWidthMinimum+roundingMaximum;
|
||||
const double roundedYMinimum=alongCenter.minimum-0.5*length+roundingMinimum;
|
||||
const double roundedYMaximum=alongCenter.maximum-0.5*length+roundingMaximum;
|
||||
const double halfThickness=0.5*static_cast<double>(edge.thicknessSubvoxels);
|
||||
const double centerMinimum=deckMinimum-halfThickness;
|
||||
const double centerMaximum=deckMaximum-halfThickness;
|
||||
const double differenceMinimum=minimum[2]-centerMaximum;
|
||||
const double differenceMaximum=maximum[2]-centerMinimum;
|
||||
const FScalarRange absoluteDifference=AbsoluteRange(differenceMinimum,differenceMaximum);
|
||||
const double roundedZMinimum=absoluteDifference.minimum-halfThickness+roundingMinimum;
|
||||
const double roundedZMaximum=absoluteDifference.maximum-halfThickness+roundingMaximum;
|
||||
return {RoundedBoxSdf(roundedXMinimum,roundedYMinimum,roundedZMinimum),
|
||||
RoundedBoxSdf(roundedXMaximum,roundedYMaximum,roundedZMaximum)};
|
||||
}
|
||||
|
||||
const double thickness=static_cast<double>(edge.thicknessSubvoxels);
|
||||
const double maximumArch=recipe.geometryVersion>=2&&WorldAssemblyEvaluatorVersion<172
|
||||
?2.0*thickness:0.0;
|
||||
const double upperMinimum=minimum[2]-deckMaximum;
|
||||
const double upperMaximum=maximum[2]-deckMinimum;
|
||||
const double lowerMinimum=deckMinimum-thickness-maximumArch-maximum[2];
|
||||
const double lowerMaximum=deckMaximum-thickness-minimum[2];
|
||||
return {std::max({horizontalMinimum,upperMinimum,lowerMinimum}),
|
||||
std::max({horizontalMaximum,upperMaximum,lowerMaximum})};
|
||||
}
|
||||
|
||||
inline std::int64_t FieldDensity(const FloatingIslandRecipeV1& recipe,
|
||||
const FloatingIslandPlanV1& plan,
|
||||
std::int64_t x, std::int64_t y, std::int64_t z)
|
||||
@@ -2104,19 +2357,46 @@ FieldInterval FloatingIslandFieldV1::interval(std::int64_t minX, std::int64_t mi
|
||||
|| !floating_island_detail::ValidScalar(minX) || !floating_island_detail::ValidScalar(minY)
|
||||
|| !floating_island_detail::ValidScalar(minZ) || !floating_island_detail::ValidScalar(maxX)
|
||||
|| !floating_island_detail::ValidScalar(maxY) || !floating_island_detail::ValidScalar(maxZ)) return result;
|
||||
const std::array<std::int64_t, 3> minimum{minX, minY, minZ};
|
||||
const std::array<std::int64_t, 3> maximum{maxX, maxY, maxZ};
|
||||
for (const auto& island : plan_->islands)
|
||||
if (floating_island_detail::Intersects(minimum, maximum,
|
||||
island.influenceMinimumSubvoxels, island.influenceMaximumSubvoxels)) return result;
|
||||
for (const auto& edge : plan_->clusterEdges)
|
||||
if (floating_island_detail::Intersects(minimum, maximum,
|
||||
edge.influenceMinimumSubvoxels, edge.influenceMaximumSubvoxels)) return result;
|
||||
// Root is void. A box outside every lobe/taper/blend/connector influence bound proves air.
|
||||
// La racine est le vide : une boite hors des influences bornees prouve l'air.
|
||||
result.known = true;
|
||||
result.minimumDensitySubvoxels = 0;
|
||||
result.maximumDensitySubvoxels = floating_island_detail::MaxDensity;
|
||||
using namespace floating_island_detail;
|
||||
if (plan_->islands.empty())
|
||||
return {true,MaxDensity,MaxDensity};
|
||||
|
||||
const std::array<double,3> minimum{static_cast<double>(minX),
|
||||
static_cast<double>(minY),static_cast<double>(minZ)};
|
||||
const std::array<double,3> maximum{static_cast<double>(maxX),
|
||||
static_cast<double>(maxY),static_cast<double>(maxZ)};
|
||||
double fieldMinimum=static_cast<double>(MaxDensity);
|
||||
double fieldMaximum=static_cast<double>(MaxDensity);
|
||||
for(const auto& island:plan_->islands)
|
||||
{
|
||||
FDensityRange group=IslandDensityRange(*recipe_,island,minimum,maximum);
|
||||
for(const auto& edge:plan_->clusterEdges)
|
||||
{
|
||||
if(edge.fromIslandId!=island.stableId)continue;
|
||||
const FDensityRange connector=ConnectorDensityRange(*recipe_,edge,minimum,maximum);
|
||||
const double detail=recipe_->geometryVersion>=2
|
||||
?static_cast<double>(recipe_->roughnessSubvoxels):0.0;
|
||||
group.minimum=std::min(group.minimum,connector.minimum-detail)
|
||||
-static_cast<double>(recipe_->blendRadiusSubvoxels)*0.25;
|
||||
group.maximum=std::min(group.maximum,connector.maximum+detail);
|
||||
}
|
||||
fieldMinimum=std::min(fieldMinimum,group.minimum);
|
||||
fieldMaximum=std::min(fieldMaximum,group.maximum);
|
||||
}
|
||||
if(!std::isfinite(fieldMinimum)||!std::isfinite(fieldMaximum))return result;
|
||||
|
||||
// These interval operations are shared by native and future generator classifiers: SDF
|
||||
// intersections take max, unions take min, and smooth unions subtract at most k/4.
|
||||
// Keep two subvoxels of slack for floating bounds followed by the field's integer rounding.
|
||||
// Ces bornes servent les generateurs actuels et futurs : intersections=max, unions=min.
|
||||
const double minimumRounded=std::clamp(std::floor(fieldMinimum)-2.0,
|
||||
-static_cast<double>(MaxDensity),static_cast<double>(MaxDensity));
|
||||
const double maximumRounded=std::clamp(std::ceil(fieldMaximum)+2.0,
|
||||
-static_cast<double>(MaxDensity),static_cast<double>(MaxDensity));
|
||||
if(minimumRounded>maximumRounded)return result;
|
||||
result.known=true;
|
||||
result.minimumDensitySubvoxels=static_cast<std::int64_t>(minimumRounded);
|
||||
result.maximumDensitySubvoxels=static_cast<std::int64_t>(maximumRounded);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -3121,20 +3121,36 @@ public:
|
||||
}
|
||||
bool bHasLegacySurfaceSample=false;
|
||||
bool bHasNativeIslandSample=false;
|
||||
std::array<float,8> NativeIslandDensity{};
|
||||
std::array<bool,8> HasNativeIslandSample{};
|
||||
for(int32 Lane=0;Lane<Count;++Lane)
|
||||
{
|
||||
const float SampleX=static_cast<float>(XStart+Lane*XStep);
|
||||
const float SampleY=static_cast<float>(Y);
|
||||
const float SampleZ=static_cast<float>(Z);
|
||||
bHasLegacySurfaceSample|=UsesLegacySurfaceField(SampleX,SampleY,SampleZ);
|
||||
float IslandDensity=0.0f;
|
||||
bHasNativeIslandSample|=NativeFloatingIslandDensityAt(
|
||||
SampleX,SampleY,SampleZ,IslandDensity);
|
||||
HasNativeIslandSample[static_cast<std::size_t>(Lane)]=NativeFloatingIslandDensityAt(
|
||||
SampleX,SampleY,SampleZ,NativeIslandDensity[static_cast<std::size_t>(Lane)]);
|
||||
bHasNativeIslandSample|=HasNativeIslandSample[static_cast<std::size_t>(Lane)];
|
||||
}
|
||||
if(bHasLegacySurfaceSample||bHasNativeIslandSample)
|
||||
{
|
||||
for(int32 Lane=0;Lane<Count;++Lane)
|
||||
OutDensity[Lane]=DensityAtLattice(XStart+Lane*XStep,Y,Z);
|
||||
{
|
||||
const std::size_t Index=static_cast<std::size_t>(Lane);
|
||||
if(HasNativeIslandSample[Index])
|
||||
{
|
||||
++ThreadClassificationStats().NativeFloatFieldCalls;
|
||||
const float SampleX=static_cast<float>(XStart+Lane*XStep);
|
||||
const float SampleY=static_cast<float>(Y);
|
||||
const float SampleZ=static_cast<float>(Z);
|
||||
OutDensity[Lane]=ApplyEditBrushes(ApplyResolvedStrateComposition(
|
||||
NativeIslandDensity[Index],SampleX,SampleY,SampleZ),
|
||||
SampleX,SampleY,SampleZ);
|
||||
}
|
||||
else
|
||||
OutDensity[Lane]=DensityAtLattice(XStart+Lane*XStep,Y,Z);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(bAnalyticOnly)
|
||||
@@ -5044,8 +5060,7 @@ public:
|
||||
// A box crossing recipes cannot use one field interval; unknown composition stays Mixed.
|
||||
// Une boite qui traverse plusieurs recettes reste Mixed, car l'intervalle d'un seul champ ne suffit pas.
|
||||
if(bBadPlan||(bSurfaceOverlap&&(bIslandOverlap||bOtherStrateOverlap))
|
||||
||(bIslandOverlap&&(bIslandOtherStrateOverlap
|
||||
||(!bSurfaceOverlap&&bOtherStrateOverlap))))
|
||||
||(bIslandOverlap&&bIslandOtherStrateOverlap))
|
||||
return Finish(EVoxelTileClass::Mixed,true);
|
||||
// The native interval path below is the exact field selected first by DensityAtLattice
|
||||
// for SurfaceWorld and FloatingIslands. Legacy analytic primitives/layer bounds cannot
|
||||
|
||||
@@ -2151,14 +2151,43 @@ namespace VoxelForgeCore
|
||||
bHasNativeSurfaceHorizonPlan=bHasNativeSurfaceHorizonPlan
|
||||
||(TileMinimumZ<=PlanMaximumZ&&TileMaximumZ>=PlanMinimumZ);
|
||||
}
|
||||
constexpr int64 IslandQueryPad=2*WorldAssemblySubvoxelsPerVoxel;
|
||||
const std::array<int64,3> TileMinimum={
|
||||
(static_cast<int64>(origin.X)*WorldAssemblySubvoxelsPerVoxel)-IslandQueryPad,
|
||||
(static_cast<int64>(origin.Y)*WorldAssemblySubvoxelsPerVoxel)-IslandQueryPad,
|
||||
(static_cast<int64>(origin.Z)*WorldAssemblySubvoxelsPerVoxel)-IslandQueryPad};
|
||||
const std::array<int64,3> TileMaximum={
|
||||
((static_cast<int64>(origin.X)+extent)*WorldAssemblySubvoxelsPerVoxel)+IslandQueryPad,
|
||||
((static_cast<int64>(origin.Y)+extent)*WorldAssemblySubvoxelsPerVoxel)+IslandQueryPad,
|
||||
((static_cast<int64>(origin.Z)+extent)*WorldAssemblySubvoxelsPerVoxel)+IslandQueryPad};
|
||||
for(const auto& Entry:impl_->worldAssembly->floatingIslands)
|
||||
{
|
||||
const int64 PlanMinimumZ=static_cast<int64>(std::min(Entry.bottomChunkZ,Entry.topChunkZ))
|
||||
*impl_->resolvedLayout->strateLayoutChunkWidthSubvoxels/WorldAssemblySubvoxelsPerVoxel;
|
||||
const int64 PlanMaximumZ=(static_cast<int64>(std::max(Entry.bottomChunkZ,Entry.topChunkZ)+1))
|
||||
*impl_->resolvedLayout->strateLayoutChunkWidthSubvoxels/WorldAssemblySubvoxelsPerVoxel;
|
||||
if(TileMinimumZ>PlanMaximumZ||TileMaximumZ<PlanMinimumZ)continue;
|
||||
|
||||
const auto IntersectsTile=[&](const auto& Feature)
|
||||
{
|
||||
for(std::size_t Axis=0;Axis<3;++Axis)
|
||||
if(Feature.influenceMinimumSubvoxels[Axis]>TileMaximum[Axis]
|
||||
||Feature.influenceMaximumSubvoxels[Axis]<TileMinimum[Axis])return false;
|
||||
return true;
|
||||
};
|
||||
bool bIslandFeatureOverlap=std::any_of(Entry.manifest.plan.islands.begin(),
|
||||
Entry.manifest.plan.islands.end(),IntersectsTile)
|
||||
||std::any_of(Entry.manifest.plan.clusterEdges.begin(),
|
||||
Entry.manifest.plan.clusterEdges.end(),IntersectsTile);
|
||||
const auto& Recipe=Entry.manifest.recipe;
|
||||
const int64 Bottom=Recipe.layoutMinimumZSubvoxels;
|
||||
const int64 Top=Recipe.layoutMaximumZSubvoxels;
|
||||
const int64 Seal=Recipe.boundarySealSubvoxels;
|
||||
const bool bBoundarySealOverlap=(TileMinimum[2]<=Top&&TileMaximum[2]>=Top-Seal)
|
||||
||(Recipe.geometryVersion<3&&TileMinimum[2]<Bottom+Seal
|
||||
&&TileMaximum[2]>=Bottom);
|
||||
bHasIslandPlanInTile=bHasIslandPlanInTile
|
||||
||(TileMinimumZ<=PlanMaximumZ&&TileMaximumZ>=PlanMinimumZ);
|
||||
||bIslandFeatureOverlap||bBoundarySealOverlap;
|
||||
}
|
||||
}
|
||||
const bool bUseNativeSurfaceHorizon=key.lod>=5&&!bOverlapsPlayerEdit
|
||||
|
||||
Reference in new issue
Block a user