core: Vertical Shafts on the shared Core cave planner (L139, v237; 15/16 seeds)
Shaft mode reuses PlanResolvedCavernNetworkV1 with a capped full-height cylinder primitive (shapeKind=1), ledges, access bores and cross links; 11 VerticalShaftParams fields mapped. Basal law: arrival 2-3 to a walkable terminal landing; drops/climbs stay reporting tiers. Seed 2 fails shell/seams at the 2-3 arrival mouth (3 variants tried, stopped per AGENTS.md). Whole view faster than assembler/world in 8/8 cells; near-ready slower in 7/8. Source/: VoxelForgeCoreAdapter.cpp. 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
fa4417aad0
commit
318c5cf4f2
31 files changed
+1719
-106
No files matched your search
@@ -1284,7 +1284,8 @@ public:
|
||||
const std::uint32_t reliefSeed=static_cast<std::uint32_t>(Owner?Owner->Seed:0);
|
||||
const auto* queryStratum=FindBoundStratumForManagerZ(Z);
|
||||
const bool plannedTunnelStratum=queryStratum&&queryStratum->Resolved
|
||||
&&queryStratum->Resolved->generatorKind=="TunnelNetwork"
|
||||
&&(queryStratum->Resolved->generatorKind=="TunnelNetwork"
|
||||
||queryStratum->Resolved->generatorKind=="VerticalShafts")
|
||||
&&queryStratum->Resolved->recipeId=="Core.Caves.PrimitiveField"
|
||||
&&queryStratum->Resolved->recipeVersion==1;
|
||||
const auto* routePassageIndices=passageTileScopeMatches
|
||||
@@ -1617,9 +1618,12 @@ public:
|
||||
const auto plannedPortalWalkCorridorContains=[&]()
|
||||
{
|
||||
if(!queryStratum||!queryStratum->Resolved
|
||||
||queryStratum->Resolved->generatorKind!="TunnelNetwork"
|
||||
||(queryStratum->Resolved->generatorKind!="TunnelNetwork"
|
||||
&&queryStratum->Resolved->generatorKind!="VerticalShafts")
|
||||
||queryStratum->Resolved->recipeId!="Core.Caves.PrimitiveField"
|
||||
||queryStratum->Resolved->recipeVersion!=1)return false;
|
||||
const bool terminalShaftRoute=
|
||||
queryStratum->Resolved->generatorKind=="VerticalShafts";
|
||||
const VoxelForgeCore::ResolvedLayoutAnchorV2* first=nullptr;
|
||||
const VoxelForgeCore::ResolvedLayoutAnchorV2* second=nullptr;
|
||||
const VoxelForgeCore::ResolvedLayoutAnchorV2* third=nullptr;
|
||||
@@ -1638,8 +1642,12 @@ public:
|
||||
}
|
||||
if(routeCount<6||!first||!second||!third||!lastThree||!lastTwo||!last
|
||||
||!first->stableId.ends_with("000-arrival")
|
||||
||!second->stableId.ends_with("000-edge-start")
|
||||
||!lastTwo->stableId.ends_with("998-departure-inner")
|
||||
||!second->stableId.ends_with("000-edge-start"))return false;
|
||||
if(terminalShaftRoute)
|
||||
{
|
||||
if(!last->stableId.ends_with("999-basal-terminal"))return false;
|
||||
}
|
||||
else if(!lastTwo->stableId.ends_with("998-departure-inner")
|
||||
||!last->stableId.ends_with("999-departure"))return false;
|
||||
const double metresPerSubvoxel=1.0/qPerMetre;
|
||||
const auto position=[&](const VoxelForgeCore::ResolvedLayoutAnchorV2& anchor)
|
||||
@@ -1677,6 +1685,7 @@ public:
|
||||
if(nearRouteSegment(*first,*second,PortalWalkLeadMetres,false,0.0)
|
||||
||nearRouteSegment(*second,*third,PortalWalkLeadMetres,false,
|
||||
arrivalInnerDistance))return true;
|
||||
if(terminalShaftRoute)return false;
|
||||
const auto departureInner=position(*lastTwo),departure=position(*last);
|
||||
const double departureTailDistance=std::hypot(
|
||||
departure[0]-departureInner[0],departure[1]-departureInner[1]);
|
||||
@@ -3835,8 +3844,11 @@ private:
|
||||
const VoxelForgeCore::ResolvedCavernRoomV1* room=nullptr;
|
||||
const VoxelForgeCore::ResolvedCavernEdgeV1* edge=nullptr;
|
||||
bool nativeGraph=false;
|
||||
std::uint8_t roomShapeKind=0;
|
||||
std::uint8_t ledgeSide=0;
|
||||
std::array<std::int64_t,3> boundsMinimumMicrometres{},boundsMaximumMicrometres{};
|
||||
std::array<double,3> centerMetres{},halfExtentsMetres{};
|
||||
double ledgeSpacingMetres=0.0,ledgeDepthMetres=0.0;
|
||||
struct FSegment
|
||||
{
|
||||
std::array<double,3> start{},end{},minimum{},maximum{};
|
||||
@@ -4285,7 +4297,9 @@ private:
|
||||
||(ResolvedLayout->cavernGraphVocabularyVersion
|
||||
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV1
|
||||
&&ResolvedLayout->cavernGraphVocabularyVersion
|
||||
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV2))return;
|
||||
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV2
|
||||
&&ResolvedLayout->cavernGraphVocabularyVersion
|
||||
!=VoxelForgeCore::ResolvedLayoutCavernGraphVocabularyVersionV3))return;
|
||||
const auto isNativeGraphStrate=[this](std::int32_t strateIndex)
|
||||
{
|
||||
const auto found=std::find_if(ResolvedLayout->strates.begin(),
|
||||
@@ -4302,6 +4316,10 @@ private:
|
||||
{
|
||||
FCavernRuntimeFeature runtime;runtime.room=&room;
|
||||
runtime.nativeGraph=isNativeGraphStrate(room.strateIndex);
|
||||
runtime.roomShapeKind=room.shapeKind;
|
||||
runtime.ledgeSide=room.ledgeSide;
|
||||
runtime.ledgeSpacingMetres=room.ledgeSpacingMicrometres/1000000.0;
|
||||
runtime.ledgeDepthMetres=room.ledgeDepthMicrometres/1000000.0;
|
||||
runtime.boundsMinimumMicrometres=room.boundsMinimumMicrometres;
|
||||
runtime.boundsMaximumMicrometres=room.boundsMaximumMicrometres;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
@@ -4956,7 +4974,8 @@ private:
|
||||
ResolvedLayout->strates.end(),[&](const auto& stratum)
|
||||
{return stratum.strateIndex==passage.lowerStrateIndex;});
|
||||
if(lowerStratum!=ResolvedLayout->strates.end()
|
||||
&&lowerStratum->generatorKind=="TunnelNetwork"
|
||||
&&(lowerStratum->generatorKind=="TunnelNetwork"
|
||||
||lowerStratum->generatorKind=="VerticalShafts")
|
||||
&&routeEnd<controls.size())
|
||||
{
|
||||
double routeHeight=0.0,distanceSquared=0.0;
|
||||
@@ -5667,6 +5686,11 @@ public:
|
||||
FMetricInterval featureVoid;
|
||||
if(feature.room)
|
||||
{
|
||||
// Shaft wall and ledge SDFs are evaluated exactly per sample.
|
||||
// Until a conservative interval bound for their carved shelves is
|
||||
// available, keep intersecting boxes Mixed instead of proving a
|
||||
// potentially false uniform sign.
|
||||
if(feature.roomShapeKind==1)return {0.0,0.0,false};
|
||||
double normalizedMinimumSquared=0.0,normalizedMaximumSquared=0.0;
|
||||
double meanRadius=0.0;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
@@ -7884,6 +7908,64 @@ private:
|
||||
double edgeFloor=0.0;
|
||||
if(feature.room)
|
||||
{
|
||||
if(feature.roomShapeKind==1)
|
||||
{
|
||||
const double radius=feature.halfExtentsMetres[0];
|
||||
const double bottom=feature.centerMetres[2]-feature.halfExtentsMetres[2];
|
||||
const double top=feature.centerMetres[2]+feature.halfExtentsMetres[2];
|
||||
const double radial=std::hypot(point[0]-feature.centerMetres[0],
|
||||
point[1]-feature.centerMetres[1]);
|
||||
const double radialQ=radial-radius;
|
||||
const double verticalQ=std::max(bottom-point[2],point[2]-top);
|
||||
double shaftDistance=std::min(std::max(radialQ,verticalQ),0.0)
|
||||
+std::hypot(std::max(radialQ,0.0),std::max(verticalQ,0.0));
|
||||
if(feature.irregularityAmplitudeVoxels>0.0f)
|
||||
shaftDistance+=StampValueNoise(
|
||||
X-feature.centerVoxels[0],Y-feature.centerVoxels[1],
|
||||
Z-feature.centerVoxels[2],feature.irregularityWavelengthVoxels,
|
||||
feature.irregularitySeed)
|
||||
*feature.irregularityAmplitudeVoxels*metresPerVoxel;
|
||||
|
||||
// A cardinal ledge faces the walkable backbone. Shelves are
|
||||
// cut out of the otherwise open cylinder, leaving most of the
|
||||
// shaft cross-section clear at every interval.
|
||||
if(feature.ledgeSpacingMetres>0.0&&feature.ledgeDepthMetres>0.0)
|
||||
{
|
||||
const long long nearest=static_cast<long long>(std::llround(
|
||||
(top-point[2])/feature.ledgeSpacingMetres));
|
||||
const long long maximum=static_cast<long long>(std::floor(
|
||||
(top-bottom-1000000.0)/feature.ledgeSpacingMetres));
|
||||
if(nearest>=1&&nearest<=maximum)
|
||||
{
|
||||
const double ledgeZ=top-nearest*feature.ledgeSpacingMetres;
|
||||
constexpr double LedgeThicknessMetres=0.6;
|
||||
constexpr double LedgeFilletMetres=0.1;
|
||||
const double dx=point[0]-feature.centerMetres[0];
|
||||
const double dy=point[1]-feature.centerMetres[1];
|
||||
const std::array<std::array<double,2>,4> sides={{{1.0,0.0},
|
||||
{0.0,1.0},{-1.0,0.0},{0.0,-1.0}}};
|
||||
const auto side=sides[feature.ledgeSide%4];
|
||||
const double radialLocal=dx*side[0]+dy*side[1];
|
||||
const double tangentLocal=-dx*side[1]+dy*side[0];
|
||||
const double halfDepth=feature.ledgeDepthMetres*0.5;
|
||||
const double halfWidth=radius*0.45;
|
||||
const double halfThickness=LedgeThicknessMetres*0.5;
|
||||
const double qx=std::abs(radialLocal-(radius-halfDepth))-halfDepth;
|
||||
const double qy=std::abs(tangentLocal)-halfWidth;
|
||||
const double qz=std::abs(point[2]-(ledgeZ-halfThickness))
|
||||
-halfThickness;
|
||||
const double outside=std::hypot(std::max(qx,0.0),
|
||||
std::max(qy,0.0),std::max(qz,0.0));
|
||||
const double platformDistance=outside
|
||||
+std::min(std::max(qx,std::max(qy,qz)),0.0)
|
||||
-LedgeFilletMetres;
|
||||
shaftDistance=std::max(shaftDistance,-platformDistance);
|
||||
}
|
||||
}
|
||||
featureVoid=shaftDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
double normalizedSquared=0.0,meanRadius=0.0;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
@@ -7907,6 +7989,7 @@ private:
|
||||
+feature.floorGradient[1]*(point[1]-feature.centerMetres[1]);
|
||||
const double width=feature.room->smoothWidthMicrometres/1000000.0;
|
||||
featureVoid=SmoothMaxMetres(ellipsoid,floor,width);
|
||||
}
|
||||
}
|
||||
else if(feature.edge&&!feature.segments.empty())
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user