#pragma once #include "Json.h" #include "VoxelForgeCore/VoxelForgeCore.h" #include #include #include #include #include namespace VoxelForgeCore { namespace { constexpr std::int64_t TransitionQ16 = 1ll << 16; void TransitionHashWord(std::uint64_t& hash, std::uint64_t word) { for (int byte = 0; byte < 8; ++byte) { hash ^= static_cast(word >> (byte * 8)); hash *= 1099511628211ull; } } std::uint64_t TransitionHash(std::int32_t seed, std::int32_t interfaceIndex, std::int64_t x, std::int64_t y, std::int64_t z, std::uint32_t salt) { std::uint64_t hash = 14695981039346656037ull; TransitionHashWord(hash, static_cast(seed)); TransitionHashWord(hash, static_cast(interfaceIndex)); TransitionHashWord(hash, static_cast(x)); TransitionHashWord(hash, static_cast(y)); TransitionHashWord(hash, static_cast(z)); TransitionHashWord(hash, salt); hash ^= hash >> 30; hash *= 0xbf58476d1ce4e5b9ull; hash ^= hash >> 27; hash *= 0x94d049bb133111ebull; hash ^= hash >> 31; return hash; } std::int64_t TransitionNoiseValue(std::int32_t seed, std::int32_t interfaceIndex, std::int64_t x, std::int64_t y, std::int64_t z, std::uint32_t salt) { const std::uint32_t bits = static_cast( TransitionHash(seed, interfaceIndex, x, y, z, salt) >> 32); return static_cast(bits >> 15) - TransitionQ16; } std::int64_t TransitionFloorDiv(std::int64_t value, std::int64_t divisor) { const std::int64_t quotient = value / divisor; const std::int64_t remainder = value % divisor; return quotient - (remainder < 0 ? 1 : 0); } std::int64_t TransitionFade(std::int64_t valueQ16) { valueQ16 = std::clamp(valueQ16, std::int64_t{0}, TransitionQ16); const std::int64_t square = (valueQ16 * valueQ16) / TransitionQ16; return (square * (3 * TransitionQ16 - 2 * valueQ16)) / TransitionQ16; } std::int64_t TransitionLerp(std::int64_t a, std::int64_t b, std::int64_t tQ16) { return a + ((b - a) * tQ16) / TransitionQ16; } std::int64_t TransitionValueNoise2D(std::int32_t seed, std::int32_t interfaceIndex, std::int64_t x, std::int64_t y, std::int64_t cell, std::uint32_t salt) { const std::int64_t ix = TransitionFloorDiv(x, cell), iy = TransitionFloorDiv(y, cell); const std::int64_t tx = TransitionFade(((x - ix * cell) * TransitionQ16) / cell); const std::int64_t ty = TransitionFade(((y - iy * cell) * TransitionQ16) / cell); std::uint64_t prefix=14695981039346656037ull; TransitionHashWord(prefix,static_cast(seed)); TransitionHashWord(prefix,static_cast(interfaceIndex)); const auto at = [&](std::int64_t dx, std::int64_t dy) { std::uint64_t hash=prefix; TransitionHashWord(hash,static_cast(ix+dx)); TransitionHashWord(hash,static_cast(iy+dy)); TransitionHashWord(hash,0); TransitionHashWord(hash,salt); hash ^= hash >> 30; hash *= 0xbf58476d1ce4e5b9ull; hash ^= hash >> 27; hash *= 0x94d049bb133111ebull; hash ^= hash >> 31; const std::uint32_t bits=static_cast(hash>>32); return static_cast(bits>>15)-TransitionQ16; }; return TransitionLerp(TransitionLerp(at(0, 0), at(1, 0), tx), TransitionLerp(at(0, 1), at(1, 1), tx), ty); } std::int64_t TransitionValueNoise3D(std::int32_t seed, std::int32_t interfaceIndex, std::int64_t x, std::int64_t y, std::int64_t z, std::int64_t cell, std::uint32_t salt) { const std::int64_t ix = TransitionFloorDiv(x, cell), iy = TransitionFloorDiv(y, cell); const std::int64_t iz = TransitionFloorDiv(z, cell); const std::int64_t tx = TransitionFade(((x - ix * cell) * TransitionQ16) / cell); const std::int64_t ty = TransitionFade(((y - iy * cell) * TransitionQ16) / cell); const std::int64_t tz = TransitionFade(((z - iz * cell) * TransitionQ16) / cell); const auto at = [&](std::int64_t dx, std::int64_t dy, std::int64_t dz) { return TransitionNoiseValue(seed, interfaceIndex, ix + dx, iy + dy, iz + dz, salt); }; const std::int64_t z0 = TransitionLerp( TransitionLerp(at(0, 0, 0), at(1, 0, 0), tx), TransitionLerp(at(0, 1, 0), at(1, 1, 0), tx), ty); const std::int64_t z1 = TransitionLerp( TransitionLerp(at(0, 0, 1), at(1, 0, 1), tx), TransitionLerp(at(0, 1, 1), at(1, 1, 1), tx), ty); return TransitionLerp(z0, z1, tz); } std::int64_t LayoutTransitionLimit(const ResolvedLayoutStratumV2& stratum) { if (stratum.transitionMode == "Hard" || stratum.transitionFallsBackToHard) return 0; return std::max(0, stratum.transitionWidthMicrometres); } std::string LayoutEscape(std::string_view value) { std::string out="\""; for(unsigned char c:value) { switch(c) { case '"':out+="\\\"";break; case '\\':out+="\\\\";break; case '\b':out+="\\b";break; case '\f':out+="\\f";break; case '\n':out+="\\n";break; case '\r':out+="\\r";break; case '\t':out+="\\t";break; default:if(c<0x20){char b[7];std::snprintf(b,sizeof(b),"\\u%04x",c);out+=b;} else out+=static_cast(c); } } out+='"';return out; } std::uint64_t LayoutHash(std::string_view value) { std::uint64_t hash=14695981039346656037ull; for(unsigned char c:value){hash^=c;hash*=1099511628211ull;} return hash; } std::uint64_t LayoutStampMix(std::uint64_t value) { value^=value>>30;value*=0xbf58476d1ce4e5b9ull; value^=value>>27;value*=0x94d049bb133111ebull; return value^(value>>31); } std::uint64_t LayoutStampSeed(std::int32_t seed,std::int32_t strate, std::string_view stableId) { std::uint64_t hash=14695981039346656037ull; TransitionHashWord(hash,static_cast(seed)); TransitionHashWord(hash,static_cast(strate)); for(unsigned char c:stableId){hash^=c;hash*=1099511628211ull;} return LayoutStampMix(hash); } std::uint64_t LayoutStampNext(std::uint64_t& state) {state=LayoutStampMix(state+0x9e3779b97f4a7c15ull);return state;} bool LayoutStampKindValid(ResolvedSdfStampKindV1 kind) { const auto value=static_cast(kind); return value>=static_cast(ResolvedSdfStampKindV1::carveEllipsoid) &&value<=static_cast(ResolvedSdfStampKindV1::fillDais); } double LayoutStampPointSegmentDistance(const std::array& point, const std::array& a,const std::array& b) { double lengthSquared=0.0,dot=0.0; for(std::size_t axis=0;axis<3;++axis) { const double delta=static_cast(b[axis])-a[axis]; lengthSquared+=delta*delta; dot+=(static_cast(point[axis])-a[axis])*delta; } const double t=lengthSquared>0.0?std::clamp(dot/lengthSquared,0.0,1.0):0.0; double distanceSquared=0.0; for(std::size_t axis=0;axis<3;++axis) { const double nearest=static_cast(a[axis]) +t*(static_cast(b[axis])-a[axis]); const double delta=static_cast(point[axis])-nearest; distanceSquared+=delta*delta; } return std::sqrt(distanceSquared); } double LayoutStampBoundRadius(const ResolvedLayoutStampV1& stamp) { double radiusSquared=0.0; for(std::size_t axis=0;axis<3;++axis) { const double extent=(static_cast(stamp.boundsMaximumMicrometres[axis]) -stamp.boundsMinimumMicrometres[axis])*0.5; radiusSquared+=extent*extent; } return std::sqrt(radiusSquared); } bool LayoutStampSafe(const ResolvedLayoutSnapshotV3& layout, const ResolvedLayoutStampV1& stamp,std::string* reason=nullptr) { const auto strate=std::find_if(layout.strates.begin(),layout.strates.end(), [&](const auto& value){return value.strateIndex==stamp.strateIndex;}); if(strate==layout.strates.end()||!strate->content.presentInCurrentField) {if(reason)*reason="stamp strate has no current content band";return false;} if(stamp.boundsMinimumMicrometres[2]<=strate->content.bottomMicrometres ||stamp.boundsMaximumMicrometres[2]>=strate->content.topMicrometres) {if(reason)*reason="stamp bounds touch or cross the ceiling/bedrock content faces";return false;} for(const auto* band:{&strate->ceiling,&strate->bedrock}) if(band->presentInCurrentField &&stamp.boundsMinimumMicrometres[2]topMicrometres &&stamp.boundsMaximumMicrometres[2]>band->bottomMicrometres) {if(reason)*reason="stamp bounds overlap a ceiling or bedrock band";return false;} const double boundRadius=LayoutStampBoundRadius(stamp); const double coreMicrometres=std::max(1,layout.coreVoxelSizeMicrometres); constexpr double RouteClearanceMicrometres=1000000.0; const auto routeSafe=[&](const std::array& a, const std::array& b,double radiusSubvoxels) { const double radius=radiusSubvoxels*coreMicrometres /WorldAssemblySubvoxelsPerVoxel; return LayoutStampPointSegmentDistance(stamp.positionMicrometres,a,b) >boundRadius+radius+RouteClearanceMicrometres; }; const auto qToMicrometres=[&](const std::array& q) { std::array result{}; for(std::size_t axis=0;axis<3;++axis) result[axis]=static_cast(std::llround( static_cast(q[axis])*coreMicrometres /WorldAssemblySubvoxelsPerVoxel)); return result; }; for(const auto& passage:layout.passages) { if(passage.upperStrateIndex!=stamp.strateIndex &&passage.lowerStrateIndex!=stamp.strateIndex)continue; const auto upper=qToMicrometres(passage.upperMouthSubvoxels); const auto lower=qToMicrometres(passage.lowerMouthSubvoxels); const double mouthRadius=static_cast(passage.mouthRadiusSubvoxels) *coreMicrometres/WorldAssemblySubvoxelsPerVoxel; if(LayoutStampPointSegmentDistance(stamp.positionMicrometres,upper,upper) <=boundRadius+mouthRadius+RouteClearanceMicrometres ||LayoutStampPointSegmentDistance(stamp.positionMicrometres,lower,lower) <=boundRadius+mouthRadius+RouteClearanceMicrometres) {if(reason)*reason="stamp is inside a passage mouth safety volume";return false;} for(std::size_t i=0;i+1 route; for(const auto& anchor:strate->anchors) if(anchor.kind=="route_point"&&anchor.values.size()>=3) route.push_back(&anchor); for(std::size_t i=0;i{ static_cast(std::llround(static_cast(anchor.values[0]) *coreMicrometres/WorldAssemblySubvoxelsPerVoxel)), static_cast(std::llround(static_cast(anchor.values[1]) *coreMicrometres/WorldAssemblySubvoxelsPerVoxel)), static_cast(std::llround(static_cast(anchor.values[2]) *coreMicrometres/WorldAssemblySubvoxelsPerVoxel))};}; const auto a=makePoint(*route[i]); const auto b=i+1values.size()>=4?route[i]->values[3]:0, route[i+1]->values.size()>=4?route[i+1]->values[3]:0) :route[i]->values.size()>=4?route[i]->values[3]:0; if(!routeSafe(a,b,radius)) {if(reason)*reason="stamp overlaps a resolved strate walk route";return false;} } return true; } double LayoutSegmentDistance(const std::array& p0, const std::array& p1,const std::array& q0, const std::array& q1) { std::array u{},v{},w{}; double a=0.0,b=0.0,c=0.0,d=0.0,e=0.0; for(std::size_t axis=0;axis<3;++axis) { u[axis]=p1[axis]-p0[axis];v[axis]=q1[axis]-q0[axis]; w[axis]=p0[axis]-q0[axis]; a+=u[axis]*u[axis];b+=u[axis]*v[axis]; c+=v[axis]*v[axis];d+=u[axis]*w[axis];e+=v[axis]*w[axis]; } const double denominator=a*c-b*b; double sN=0.0,sD=denominator,tN=0.0,tD=denominator; if(denominator<1.0e-18){sN=0.0;sD=1.0;tN=e;tD=c;} else { sN=b*e-c*d;tN=a*e-b*d; if(sN<0.0){sN=0.0;tN=e;tD=c;} else if(sN>sD){sN=sD;tN=e+b;tD=c;} } if(tN<0.0) { tN=0.0; if(-d<0.0)sN=0.0; else if(-d>a)sN=sD; else{sN=-d;sD=a;} } else if(tN>tD) { tN=tD; if(-d+b<0.0)sN=0.0; else if(-d+b>a)sN=sD; else{sN=-d+b;sD=a;} } const double s=std::abs(sN)<1.0e-18?0.0:sN/sD; const double t=std::abs(tN)<1.0e-18?0.0:tN/tD; double distanceSquared=0.0; for(std::size_t axis=0;axis<3;++axis) { const double delta=w[axis]+s*u[axis]-t*v[axis]; distanceSquared+=delta*delta; } return std::sqrt(std::max(0.0,distanceSquared)); } std::array LayoutOrePointUm(const std::array& point) {return {static_cast(point[0]),static_cast(point[1]), static_cast(point[2])};} bool LayoutOreVeinSafe(const ResolvedLayoutSnapshotV3& layout, const ResolvedLayoutOreVeinV1& vein,std::string* reason=nullptr) { const auto fail=[&](const char* text) {if(reason)*reason=text;return false;}; const auto strate=std::find_if(layout.strates.begin(),layout.strates.end(), [&](const auto& value){return value.strateIndex==vein.strateIndex;}); if(strate==layout.strates.end()||!strate->content.presentInCurrentField) return fail("vein strate has no current content band"); if(vein.boundsMinimumMicrometres[2]<=strate->content.bottomMicrometres ||vein.boundsMaximumMicrometres[2]>=strate->content.topMicrometres) return fail("vein bounds touch or cross the ceiling/bedrock content faces"); for(const auto* band:{&strate->ceiling,&strate->bedrock}) if(band->presentInCurrentField &&vein.boundsMinimumMicrometres[2]topMicrometres &&vein.boundsMaximumMicrometres[2]>band->bottomMicrometres) return fail("vein bounds overlap a ceiling or bedrock band"); const auto veinA=LayoutOrePointUm(vein.endpointsMicrometres[0]); const auto veinB=LayoutOrePointUm(vein.endpointsMicrometres[1]); const double coreRadius=static_cast(vein.radiusMicrometres +vein.irregularityAmplitudeMicrometres+vein.blendWidthMicrometres); const double coreMicrometres=std::max(1,layout.coreVoxelSizeMicrometres); constexpr double RouteClearanceMicrometres=100000.0; const auto qToUm=[&](const std::array& q) {return std::array{q[0]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel, q[1]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel, q[2]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel};}; for(const auto& passage:layout.passages) { if(passage.upperStrateIndex!=vein.strateIndex &&passage.lowerStrateIndex!=vein.strateIndex)continue; const auto upper=qToUm(passage.upperMouthSubvoxels); const auto lower=qToUm(passage.lowerMouthSubvoxels); const double mouthRadius=passage.mouthRadiusSubvoxels*coreMicrometres /WorldAssemblySubvoxelsPerVoxel; if(LayoutSegmentDistance(veinA,veinB,upper,upper) <=coreRadius+mouthRadius+RouteClearanceMicrometres ||LayoutSegmentDistance(veinA,veinB,lower,lower) <=coreRadius+mouthRadius+RouteClearanceMicrometres) return fail("vein overlaps a clear passage landing"); for(std::size_t i=0;i+1 route; for(const auto& anchor:strate->anchors) { if(anchor.values.size()<3)continue; if(anchor.kind=="route_point")route.push_back(&anchor); if(anchor.kind.find("landing")!=std::string::npos) { const std::array landing={ anchor.values[0]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel, anchor.values[1]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel, anchor.values[2]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel}; const double radius=anchor.values.size()>=4 ?anchor.values[3]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel:3000000.0; if(LayoutSegmentDistance(veinA,veinB,landing,landing) <=coreRadius+radius+RouteClearanceMicrometres) return fail("vein overlaps a resolved landing footprint"); } } for(std::size_t i=0;i+1{anchor.values[0]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel, anchor.values[1]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel, anchor.values[2]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel};}; const auto a=qPoint(*route[i]),b=qPoint(*route[i+1]); const double routeRadius=std::max(route[i]->values.size()>=4?route[i]->values[3]:0, route[i+1]->values.size()>=4?route[i+1]->values[3]:0) *coreMicrometres/WorldAssemblySubvoxelsPerVoxel; if(LayoutSegmentDistance(veinA,veinB,a,b) <=coreRadius+routeRadius+RouteClearanceMicrometres) return fail("vein enters a clear strate route corridor"); } for(const auto& room:layout.cavernRooms) if(room.strateIndex==vein.strateIndex) for(std::size_t axis=0;axis<3;++axis) if(vein.boundsMinimumMicrometres[axis] <=room.boundsMaximumMicrometres[axis] &&vein.boundsMaximumMicrometres[axis] >=room.boundsMinimumMicrometres[axis]) return fail("vein overlaps a cavern landmark footprint"); for(const auto& edge:layout.cavernEdges) { if(edge.strateIndex!=vein.strateIndex)continue; std::array previous{}; const auto bezier=[&](double t) { const double one=1.0-t; const std::array weights={one*one*one,3.0*one*one*t, 3.0*one*t*t,t*t*t}; std::array point{}; for(std::size_t i=0;i<4;++i) for(std::size_t axis=0;axis<3;++axis) point[axis]+=weights[i]*edge.controlPointsMicrometres[i][axis]; return point; }; previous=bezier(0.0); for(int step=1;step<=256;++step) { const auto next=bezier(static_cast(step)/256.0); if(LayoutSegmentDistance(veinA,veinB,previous,next) <=coreRadius+edge.boreRadiusMicrometres+RouteClearanceMicrometres) return fail("vein enters a landmark passage corridor"); previous=next; } } for(const auto& stamp:layout.stamps) { if(stamp.strateIndex!=vein.strateIndex ||(stamp.kind!=ResolvedSdfStampKindV1::fillDais &&stamp.kind!=ResolvedSdfStampKindV1::fillPillar))continue; bool overlaps=true; for(std::size_t axis=0;axis<3;++axis) overlaps=overlaps&&vein.boundsMinimumMicrometres[axis] <=stamp.boundsMaximumMicrometres[axis] &&vein.boundsMaximumMicrometres[axis] >=stamp.boundsMinimumMicrometres[axis]; if(overlaps)return fail("vein overlaps a resolved landmark solid footprint"); } return true; } std::string LayoutHex(std::uint64_t value) {std::ostringstream out;out<(stamp.kind) <<",\"position_um\":["<(stamp.yawQuarterTurns)<<'}'; } out<<']'; } if(!s.cavernRooms.empty()||!s.cavernEdges.empty()) { out<<",\"cavern_graph_vocabulary_version\":" <>30))*0xbf58476d1ce4e5b9ull; value=(value^(value>>27))*0x94d049bb133111ebull; return value^(value>>31); } std::uint64_t PassageSeed(std::int32_t seed,std::int32_t upper, std::int32_t lower,std::uint32_t connection, std::uint32_t salt) { std::uint64_t h=PassageHash(static_cast(seed)); h=PassageHash(h^static_cast(upper)); h=PassageHash(h^static_cast(lower)); h=PassageHash(h^connection);return PassageHash(h^salt); } std::uint64_t PassageIsqrt(std::uint64_t n) { std::uint64_t x=0,bit=std::uint64_t{1}<<62; while(bit>n)bit>>=2; while(bit) { if(n>=x+bit){n-=x+bit;x=(x>>1)+bit;} else x>>=1; bit>>=2; } return x; } std::int64_t PassageRoundRatio(std::int64_t value,std::int64_t numerator, std::int64_t denominator) { if(denominator<=0)return 0; const bool negative=value<0; const std::uint64_t magnitude=static_cast(negative?-value:value); const std::uint64_t product=magnitude*static_cast(numerator); const std::uint64_t rounded=(product+static_cast(denominator)/2) /static_cast(denominator); return negative?-static_cast(rounded):static_cast(rounded); } std::array PassageRandomXY(std::uint64_t seed, std::int64_t inner,std::int64_t outer) { if(outer<=0)return {}; inner=std::clamp(inner,std::int64_t{0},outer); const std::uint64_t span=static_cast(outer)*2+1; for(std::uint32_t attempt=0;attempt<64;++attempt) { const std::uint64_t hx=PassageHash(seed+2ull*attempt); const std::uint64_t hy=PassageHash(seed+2ull*attempt+1); const std::int64_t x=static_cast(hx%span)-outer; const std::int64_t y=static_cast(hy%span)-outer; const std::uint64_t radiusSquared=static_cast(x*x+y*y); if(radiusSquared<=static_cast(outer*outer) &&radiusSquared>=static_cast(inner*inner))return {x,y}; } return {outer,0}; } } ResolvedLayoutTransitionBandsV1 EvaluateResolvedLayoutTransitionBandsV1( const ResolvedLayoutStratumV2& upper, const ResolvedLayoutStratumV2& lower, std::int32_t worldSeed, std::int64_t layoutChunkSizeMicrometres, std::int64_t xMicrometres, std::int64_t yMicrometres) { ResolvedLayoutTransitionBandsV1 result; const std::int64_t seam = upper.bedrock.bottomMicrometres; const std::int64_t upperThickness = upper.bedrock.topMicrometres - seam; const std::int64_t lowerThickness = lower.ceiling.topMicrometres - lower.ceiling.bottomMicrometres; result.upperBedrockBottomMicrometres = seam; result.upperBedrockTopMicrometres = seam + std::max(0, upperThickness); result.lowerCeilingBottomMicrometres = seam - std::max(0, lowerThickness); result.lowerCeilingTopMicrometres = seam; const std::int64_t width = upper.transitionFallsBackToHard ? 0 : std::max(0, upper.transitionWidthMicrometres); if (upper.transitionMode == "Hard" || width == 0 || (upper.transitionMode != "Gradient" && upper.transitionMode != "Interleaved") || !upper.bedrock.presentInCurrentField || !lower.ceiling.presentInCurrentField || upperThickness <= 0 || lowerThickness <= 0) return result; if (width <= 0) return result; std::int64_t noise = 0; if (upper.transitionMode == "Gradient") { const std::int64_t wavelength = std::max( 1000000, std::max(1, layoutChunkSizeMicrometres) * 4); noise = TransitionValueNoise2D(worldSeed, upper.strateIndex, xMicrometres, yMicrometres, wavelength, 0x47524144u); } else { // A face crossing wider than the sum of the authored bands would separate // their far edges and leave an air slab. Limit the physical crossing to the // smaller of the declared transition width and the combined band thickness. const std::int64_t maximumContinuousCrossing=upperThickness+lowerThickness; const std::int64_t crossingLimit=std::min(width,maximumContinuousCrossing); const std::int64_t cell = std::max(1000000, std::max(1, layoutChunkSizeMicrometres) * 2); const std::int64_t warp = std::min(crossingLimit / 2, cell / 2); const std::int64_t warpX = (TransitionValueNoise3D(worldSeed, upper.strateIndex, xMicrometres, yMicrometres, seam, cell, 0x49574158u) * warp) / TransitionQ16; const std::int64_t warpY = (TransitionValueNoise3D(worldSeed, upper.strateIndex, xMicrometres, yMicrometres, seam, cell, 0x49574159u) * warp) / TransitionQ16; const std::int64_t warpZ = (TransitionValueNoise3D(worldSeed, upper.strateIndex, xMicrometres, yMicrometres, seam, cell, 0x4957415au) * warp) / TransitionQ16; noise = TransitionValueNoise3D(worldSeed, upper.strateIndex, xMicrometres + warpX, yMicrometres + warpY, seam + warpZ, cell, 0x494e544cu); // Interleaved means physical crossing: the two faces enter each other's // bands by a total depth in [0, declared width]. Split that overlap across // both faces so neither stratum owns the entire displacement. Since both // resulting intervals are solid and overlap, the transition cannot open a gap. const std::int64_t overlap = ((noise + TransitionQ16) * crossingLimit) / (2 * TransitionQ16); const std::int64_t upperInset = overlap / 2; const std::int64_t lowerInset = overlap - upperInset; result.crossingDepthMicrometres = overlap; result.upperBedrockBottomMicrometres = seam - upperInset; result.upperBedrockTopMicrometres = result.upperBedrockBottomMicrometres + upperThickness; result.lowerCeilingTopMicrometres = seam + lowerInset; result.lowerCeilingBottomMicrometres = result.lowerCeilingTopMicrometres - lowerThickness; return result; } const std::int64_t displacement = (noise * (width / 8)) / TransitionQ16; const std::int64_t interfaceZ = seam + displacement; result.upperBedrockBottomMicrometres = interfaceZ; result.upperBedrockTopMicrometres = interfaceZ + upperThickness; result.lowerCeilingBottomMicrometres = interfaceZ - lowerThickness; result.lowerCeilingTopMicrometres = interfaceZ; return result; } std::string ResolvedLayoutSnapshotV3::ToJson() const { const std::string body=LayoutBodyJson(*this); return body.substr(0,body.size()-1)+",\"stable_identity\":" +LayoutEscape(stableIdentity.empty()?"resolved-layout-v3-fnv64:"+LayoutHex(LayoutHash(body)):stableIdentity)+"}"; } bool ResolvedLayoutSnapshotV3::finalize(std::string& error) { error.clear(); if(schemaVersion!=ResolvedLayoutSchemaVersionV3||worldIdentity.empty()||strates.empty() ||coreVoxelSizeMicrometres==0||strateLayoutChunkSizeMicrometres==0 ||strateLayoutChunkWidthSubvoxels<=0) {error="resolved layout has an unsupported version, empty world identity, or no strates";return false;} std::stable_sort(strates.begin(),strates.end(),[](const auto& a,const auto& b) {return a.interiorTopMicrometres!=b.interiorTopMicrometres ?a.interiorTopMicrometres>b.interiorTopMicrometres:a.order(i); if(x.stableId.empty())x.stableId=worldIdentity+"/strate/"+std::to_string(x.strateIndex); if(x.content.kind.empty())x.content.kind="content"; if(x.ceiling.kind.empty())x.ceiling.kind="ceiling"; if(x.bedrock.kind.empty())x.bedrock.kind="bedrock"; if(x.interiorTopMicrometres<=x.interiorBottomMicrometres||x.voxelSizeMicrometres==0) {error="resolved layout contains an invalid interior interval or voxel size";return false;} // Materialize generator-specific composition policy at layout resolution. The // sampling/composition path consumes these capabilities without examining names. x.passageCapabilities={}; x.lowerInterfaceCapabilities={}; if(x.generatorKind=="SurfaceWorld") x.passageCapabilities.hasSurfaceLanding=true; else if(x.generatorKind=="FloatingIslands") { x.passageCapabilities.hasFloatingIslandLanding=true; x.passageCapabilities.usePassageAnchorForColumns=true; } if(i+1(std::llround( 1.13*WorldAssemblySubvoxelsPerVoxel*1000000.0/coreVoxelSizeMicrometres)); for(auto& passage:passages) { passage.lowerMouthControlIndex=passage.controlPoints.empty()?0:passage.controlPoints.size()-1; for(std::size_t i=1;i issues; return validate(ResolvedLayoutValidationModeV3::compatibility,issues,error); } bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode, std::vector& issues,std::string& error) const { issues.clear();error.clear(); if(schemaVersion!=ResolvedLayoutSchemaVersionV3||worldIdentity.empty()||strates.empty() ||coreVoxelSizeMicrometres==0||strateLayoutChunkSizeMicrometres==0 ||strateLayoutChunkWidthSubvoxels<=0) {error="resolved layout schema/world/strata header is invalid";return false;} const auto add=[&](std::string code,std::string detail) {issues.push_back({std::move(code),std::move(detail)});}; const bool strict=mode==ResolvedLayoutValidationModeV3::strict; for(std::size_t i=0;ieffectiveTransitionWidth ||s.transitionInterpenetrationMicrometres<0) add("transition_overlap_exceeds_width",s.stableId+" transition interpenetration exceeds its declared width"); if(s.transitionWidthMicrometres<0||s.transitionBlendChunks<0) add("invalid_transition_width",s.stableId+" has a negative transition width"); if(s.managerChunkWidthMicrometres>0&&s.dispatchChunkWidthMicrometres>0 &&s.managerChunkWidthMicrometres!=s.dispatchChunkWidthMicrometres) add("ambiguous_dispatch_units",s.stableId+" manager chunk width differs from Core dispatch chunk width"); if(i+1(i))add("wrong_order",s.stableId+" order is not top-to-bottom"); if(!s.ceiling.presentInCurrentField||!s.bedrock.presentInCurrentField) add(!s.ceiling.presentInCurrentField?"missing_ceiling":"missing_bedrock",s.stableId+" lacks a required boundary band"); if(!s.content.presentInCurrentField) add("missing_content",s.stableId+" has no content generator band"); const auto bandShape=[&](const ResolvedLayoutBandV2& b,std::string_view expected) { if(b.kind!=expected||b.topMicrometres<=b.bottomMicrometres) add("wrong_order",s.stableId+" has an invalid "+std::string(expected)+" band"); const std::int64_t minimumThickness=2ll*coreVoxelSizeMicrometres; if(b.topMicrometres-b.bottomMicrometres content -> bedrock"); if(i+1strates[i+1].ceiling.topMicrometres?"gap":"overlap", s.stableId+" bedrock and next strate ceiling are not nominally contiguous"); if(s.transitionInterpenetrationMicrometres>effectiveTransitionWidth) add("transition_overlap_exceeds_width",s.stableId+" interpenetration is wider than the declared transition zone"); } } if(!strict) { for(const auto& s:strates) { if(s.content.presentInCurrentField&&s.ceiling.presentInCurrentField) { if(s.ceiling.bottomMicrometress.content.topMicrometres) add("uncovered_interval",s.stableId+" has a "+std::to_string( s.ceiling.bottomMicrometres-s.content.topMicrometres)+" um gap below its ceiling"); } if(s.content.presentInCurrentField&&s.bedrock.presentInCurrentField) { if(s.bedrock.topMicrometres>s.content.bottomMicrometres) add("band_overlap",s.stableId+" bedrock overlaps content by "+std::to_string( s.bedrock.topMicrometres-s.content.bottomMicrometres)+" um"); else if(s.bedrock.topMicrometresb.interiorTopMicrometres) add("uncovered_interval",a.stableId+" to "+b.stableId+" leaves "+std::to_string( a.interiorBottomMicrometres-b.interiorTopMicrometres)+" um uncovered"); else if(a.interiorBottomMicrometreseffectiveTransitionWidth) add("overlap",a.stableId+" and "+b.stableId+" interiors overlap by "+std::to_string(overlap)+" um"); else add("transition_interpenetration",a.stableId+" and "+b.stableId+" overlap inside the declared transition zone"); } if(a.bedrock.presentInCurrentField&&b.ceiling.presentInCurrentField) { if(a.bedrock.bottomMicrometres>b.ceiling.topMicrometres) add("uncovered_interval",a.stableId+" bedrock and "+b.stableId+" ceiling leave "+std::to_string( a.bedrock.bottomMicrometres-b.ceiling.topMicrometres)+" um uncovered"); else if(a.bedrock.bottomMicrometreseffectiveTransitionWidth) add("overlap",a.stableId+" bedrock and "+b.stableId+" ceiling overlap by "+std::to_string(overlap)+" um"); else add("transition_interpenetration",a.stableId+" bedrock and "+b.stableId+" ceiling interpenetrate by "+std::to_string(overlap)+" um inside the declared transition zone"); } } } } const auto findStratum=[this](std::int32_t index)->const ResolvedLayoutStratumV2* { const auto it=std::find_if(strates.begin(),strates.end(),[index](const auto& s) {return s.strateIndex==index;}); return it==strates.end()?nullptr:&*it; }; std::vector passageIds; for(std::size_t i=0;strict&&i+1order!=upper->order+1) add("passage_interface",passage.stableId+" does not join adjacent resolved strates"); if(!upper||!lower||passage.placementRadiusSubvoxels<=0 ||passage.mouthRadiusSubvoxels<=0||passage.midRadiusSubvoxels<=0 ||passage.controlPoints.size()<2) { add("passage_geometry",passage.stableId+" has invalid radii, placement bound, or control points"); continue; } const auto insidePlacement=[&](const std::array& p) { const auto x=p[0],y=p[1],r=passage.placementRadiusSubvoxels; return std::abs(x)<=r&&std::abs(y)<=r&&x*x+y*y<=r*r; }; const auto insideContent=[&](const ResolvedLayoutStratumV2& s, const std::array& p) { const std::int64_t zMicrometres=p[2]*static_cast(coreVoxelSizeMicrometres) /WorldAssemblySubvoxelsPerVoxel; return s.content.presentInCurrentField&&zMicrometres>=s.content.bottomMicrometres &&zMicrometres0 ?static_cast(std::llround(1.13*WorldAssemblySubvoxelsPerVoxel *1000000.0/coreVoxelSizeMicrometres)):1157; const auto floorProfile=[&](const ResolvedLayoutPassagePointV1& p) {return p.positionSubvoxels[2]-p.radiusSubvoxels+PlayerCenterAboveFloorQ;}; std::size_t lowerMouthControlIndex=passage.controlPoints.size(); for(std::size_t i=1;i(dx*dx+dy*dy)); const std::int64_t signedDz=floorProfile(point)-floorProfile(previous); const std::int64_t dz=std::abs(signedDz); const bool landingExtension=i>lowerMouthControlIndex; if((landingExtension?dz*65536>static_cast(horizontal)*17560 :signedDz>=0||dz*65536>static_cast(horizontal)*17560) ||horizontal==0) add("passage_grade",passage.stableId+" is not strictly descending or exceeds the 15 degree grade limit"); } } if(stamps.empty()) { if(stampVocabularyVersion!=0) add("stamp_vocabulary_version","stamp vocabulary version is set without any stamp records"); } else if(stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV1 &&stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV2) add("stamp_vocabulary_version","resolved stamps require supported vocabulary version 1 or 2"); std::vector stampIds; for(const auto& stamp:stamps) { if(stamp.stableId.empty() ||std::find(stampIds.begin(),stampIds.end(),stamp.stableId)!=stampIds.end()) add("stamp_identity","stamp has an empty or duplicate stable identity"); else stampIds.push_back(stamp.stableId); if(!LayoutStampKindValid(stamp.kind)||stamp.yawQuarterTurns>3) add("stamp_kind","stamp has an unsupported kind or orientation"); bool validExtents=true; for(const auto extent:stamp.halfExtentsMicrometres) validExtents=validExtents&&extent>=500000&&extent<=100000000; if(!validExtents||stamp.smoothWidthMicrometres<0 ||stamp.smoothWidthMicrometres>10000000 ||stamp.irregularityAmplitudeMicrometres<0 ||stamp.irregularityAmplitudeMicrometres>10000000 ||stamp.irregularityWavelengthMicrometres<0 ||stamp.irregularityWavelengthMicrometres>100000000 ||(stamp.irregularityAmplitudeMicrometres>0 &&stamp.irregularityWavelengthMicrometres<=0)) add("stamp_parameters",stamp.stableId+" has invalid or unbounded geometry parameters"); const std::int64_t pad=stamp.smoothWidthMicrometres +stamp.irregularityAmplitudeMicrometres; const std::array extents={ stamp.halfExtentsMicrometres[stamp.yawQuarterTurns%2?1:0]+pad, stamp.halfExtentsMicrometres[stamp.yawQuarterTurns%2?0:1]+pad, stamp.halfExtentsMicrometres[2]+pad}; for(std::size_t axis=0;axis<3;++axis) if(stamp.boundsMinimumMicrometres[axis] !=stamp.positionMicrometres[axis]-extents[axis] ||stamp.boundsMaximumMicrometres[axis] !=stamp.positionMicrometres[axis]+extents[axis]) add("stamp_bounds",stamp.stableId+" conservative bounds do not match its parameters"); std::string safetyReason; if(!LayoutStampSafe(*this,stamp,&safetyReason)) add("stamp_placement",stamp.stableId+" is unsafe: "+safetyReason); } if(cavernRooms.empty()&&cavernEdges.empty()) { if(cavernGraphVocabularyVersion!=0) add("cavern_graph_vocabulary_version", "cavern graph vocabulary version is set without graph records"); } else if(cavernGraphVocabularyVersion!=ResolvedLayoutCavernGraphVocabularyVersionV1) add("cavern_graph_vocabulary_version", "cavern graph records require vocabulary version 1"); if(cavernGraphVocabularyVersion!=0) { if(cavernRooms.empty()||cavernEdges.empty()) add("cavern_graph_shape","a resolved cavern graph needs at least one room and one edge"); std::vector graphIds; const auto graphIdValid=[&](const std::string& id) { if(id.empty()||std::find(graphIds.begin(),graphIds.end(),id)!=graphIds.end()) return false; graphIds.push_back(id);return true; }; for(const auto& room:cavernRooms) { if(!graphIdValid(room.stableId)) add("cavern_graph_identity","room has an empty or duplicate stable identity"); if(!findStratum(room.strateIndex)) add("cavern_graph_strate",room.stableId+" names an unknown strate"); bool validExtents=true; for(const auto extent:room.halfExtentsMicrometres) validExtents=validExtents&&extent>=500000&&extent<=100000000; if(!validExtents||room.smoothWidthMicrometres<0 ||room.smoothWidthMicrometres>10000000 ||room.irregularityAmplitudeMicrometres<0 ||room.irregularityAmplitudeMicrometres>10000000 ||room.irregularityWavelengthMicrometres<0 ||room.irregularityWavelengthMicrometres>100000000 ||(room.irregularityAmplitudeMicrometres>0 &&room.irregularityWavelengthMicrometres<=0)) add("cavern_room_parameters",room.stableId+" has invalid or unbounded parameters"); const std::int64_t pad=room.smoothWidthMicrometres +room.irregularityAmplitudeMicrometres; for(std::size_t axis=0;axis<3;++axis) if(room.boundsMinimumMicrometres[axis] !=room.centerMicrometres[axis]-room.halfExtentsMicrometres[axis]-pad ||room.boundsMaximumMicrometres[axis] !=room.centerMicrometres[axis]+room.halfExtentsMicrometres[axis]+pad) add("cavern_room_bounds",room.stableId+" conservative bounds do not match its parameters"); if(room.floorMicrometresroom.centerMicrometres[2]+room.halfExtentsMicrometres[2]) add("cavern_room_floor",room.stableId+" floor lies outside its room envelope"); } for(const auto& edge:cavernEdges) { if(!graphIdValid(edge.stableId)) add("cavern_graph_identity","edge has an empty or duplicate stable identity"); if(edge.startAnchorStableId.empty()||edge.endRoomStableId.empty()) add("cavern_edge_binding",edge.stableId+" has a missing endpoint identity"); const auto room=std::find_if(cavernRooms.begin(),cavernRooms.end(), [&](const auto& value){return value.stableId==edge.endRoomStableId;}); if(room==cavernRooms.end()||room->strateIndex!=edge.strateIndex) add("cavern_edge_binding",edge.stableId+" does not terminate at a room in its strate"); if(!findStratum(edge.strateIndex)) add("cavern_graph_strate",edge.stableId+" names an unknown strate"); if(edge.boreRadiusMicrometres<500000||edge.boreRadiusMicrometres>10000000 ||edge.boreHeightMicrometres<1000000||edge.boreHeightMicrometres>20000000 ||edge.floorSupportDepthMicrometres<0 ||edge.floorSupportDepthMicrometres>2000000 ||edge.smoothWidthMicrometres<0||edge.smoothWidthMicrometres>10000000 ||edge.irregularityAmplitudeMicrometres<0 ||edge.irregularityAmplitudeMicrometres>10000000 ||edge.irregularityWavelengthMicrometres<0 ||edge.irregularityWavelengthMicrometres>100000000 ||(edge.irregularityAmplitudeMicrometres>0 &&edge.irregularityWavelengthMicrometres<=0)) add("cavern_edge_parameters",edge.stableId+" has invalid or unbounded parameters"); std::array low=edge.controlPointsMicrometres[0]; std::array high=low; for(const auto& point:edge.controlPointsMicrometres) for(std::size_t axis=0;axis<3;++axis) {low[axis]=std::min(low[axis],point[axis]);high[axis]=std::max(high[axis],point[axis]);} const std::int64_t xyPad=edge.boreRadiusMicrometres +edge.smoothWidthMicrometres+edge.irregularityAmplitudeMicrometres; const std::int64_t zPad=edge.smoothWidthMicrometres +edge.irregularityAmplitudeMicrometres; for(std::size_t axis=0;axis<3;++axis) { const std::int64_t pad=axis==2?zPad:xyPad; const std::int64_t lowSupport=axis==2?edge.floorSupportDepthMicrometres:0; const std::int64_t highBore=axis==2?edge.boreHeightMicrometres:0; if(edge.boundsMinimumMicrometres[axis]!=low[axis]-pad-lowSupport ||edge.boundsMaximumMicrometres[axis]!=high[axis]+pad+highBore) add("cavern_edge_bounds",edge.stableId+" conservative bounds do not match its parameters"); } if(room!=cavernRooms.end()) { const auto& endpoint=edge.controlPointsMicrometres.back(); if(endpoint[0]centerMicrometres[0]-room->halfExtentsMicrometres[0] ||endpoint[0]>room->centerMicrometres[0]+room->halfExtentsMicrometres[0] ||endpoint[1]centerMicrometres[1]-room->halfExtentsMicrometres[1] ||endpoint[1]>room->centerMicrometres[1]+room->halfExtentsMicrometres[1] ||std::abs(endpoint[2]-room->floorMicrometres)>500000) add("cavern_edge_portal",edge.stableId+" endpoint does not meet its room floor portal"); } } } if(oreVeins.empty()) { if(oreVeinVocabularyVersion!=0) add("ore_vein_vocabulary_version", "ore-vein vocabulary version is set without records"); } else if(oreVeinVocabularyVersion!=ResolvedLayoutOreVeinVocabularyVersionV1) add("ore_vein_vocabulary_version", "ore-vein records require vocabulary version 1"); std::vector oreVeinIds; for(const auto& vein:oreVeins) { if(vein.stableId.empty() ||std::find(oreVeinIds.begin(),oreVeinIds.end(),vein.stableId)!=oreVeinIds.end()) add("ore_vein_identity","ore vein has an empty or duplicate stable identity"); else oreVeinIds.push_back(vein.stableId); if(vein.resourceMaterialId==0||vein.radiusMicrometres<250000 ||vein.radiusMicrometres>20000000 ||vein.irregularityAmplitudeMicrometres<0 ||vein.irregularityAmplitudeMicrometres>10000000 ||vein.irregularityWavelengthMicrometres<0 ||vein.irregularityWavelengthMicrometres>100000000 ||(vein.irregularityAmplitudeMicrometres>0 &&vein.irregularityWavelengthMicrometres<=0) ||vein.blendWidthMicrometres<50000 ||vein.blendWidthMicrometres>2000000 ||vein.irregularitySeed>0x001fffffffffffffull) add("ore_vein_parameters",vein.stableId+" has invalid or unbounded parameters"); const std::int64_t pad=vein.radiusMicrometres +vein.irregularityAmplitudeMicrometres+vein.blendWidthMicrometres; for(std::size_t axis=0;axis<3;++axis) { if(vein.endpointsMicrometres[0][axis]<-1000000000000ll ||vein.endpointsMicrometres[0][axis]>1000000000000ll ||vein.endpointsMicrometres[1][axis]<-1000000000000ll ||vein.endpointsMicrometres[1][axis]>1000000000000ll) add("ore_vein_bounds",vein.stableId+" endpoint is outside the bounded world envelope"); if(vein.boundsMinimumMicrometres[axis] !=std::min(vein.endpointsMicrometres[0][axis], vein.endpointsMicrometres[1][axis])-pad ||vein.boundsMaximumMicrometres[axis] !=std::max(vein.endpointsMicrometres[0][axis], vein.endpointsMicrometres[1][axis])+pad) add("ore_vein_bounds",vein.stableId+" conservative bounds do not match its parameters"); } std::string safetyReason; if(!LayoutOreVeinSafe(*this,vein,&safetyReason)) add("ore_vein_placement",vein.stableId+" is unsafe: "+safetyReason); } if(strict&&!issues.empty()){error="strict resolved-layout validation rejected "+issues.front().code;return false;} return true; } std::optional ResolvedLayoutSnapshotV3::FromJson( std::string_view json,std::string& error) { VFJson::Value root;if(!VFJson::Parser(std::string(json)).Parse(root,error))return std::nullopt; if(!root.IsObject()){error="resolved layout JSON root must be an object";return std::nullopt;} ResolvedLayoutSnapshotV3 s;s.schemaVersion=static_cast(LayoutInt(root,"schema_version")); s.worldIdentity=LayoutString(root,"world_identity"); s.ownerVoxelSizeMicrometres=static_cast(LayoutInt(root,"owner_voxel_size_um")); s.coreVoxelSizeMicrometres=static_cast(LayoutInt(root,"core_voxel_size_um")); s.strateLayoutChunkSizeMicrometres=static_cast(LayoutInt(root,"strate_layout_chunk_size_um")); s.strateLayoutChunkWidthSubvoxels=LayoutInt(root,"strate_layout_chunk_width_subvoxels"); s.stampVocabularyVersion=static_cast(LayoutInt(root,"stamp_vocabulary_version")); s.cavernGraphVocabularyVersion=static_cast( LayoutInt(root,"cavern_graph_vocabulary_version")); s.oreVeinVocabularyVersion=static_cast( LayoutInt(root,"ore_vein_vocabulary_version")); for(const auto& v:root.Get("strates").AsArray()) { ResolvedLayoutStratumV2 x;x.stableId=LayoutString(v,"stable_id"); x.order=static_cast(LayoutInt(v,"order"));x.strateIndex=static_cast(LayoutInt(v,"strate_index")); x.generatorKind=LayoutString(v,"generator_kind");x.recipeId=LayoutString(v,"recipe_id"); x.recipeVersion=static_cast(LayoutInt(v,"recipe_version")); x.interiorBottomMicrometres=LayoutInt(v,"interior_bottom_um");x.interiorTopMicrometres=LayoutInt(v,"interior_top_um"); x.voxelSizeMicrometres=static_cast(LayoutInt(v,"voxel_size_um")); const auto& origin=v.Get("lattice_origin_um").AsArray();for(std::size_t i=0;i<3&&i(LayoutInt(v,"transition_blend_chunks")); x.transitionWidthMicrometres=LayoutInt(v,"transition_width_um"); x.transitionWarpAmplitudeMicrometres=LayoutInt(v,"transition_warp_amplitude_um"); x.transitionInterpenetrationMicrometres=LayoutInt(v,"transition_interpenetration_um"); x.transitionHonouredInCore=LayoutBool(v,"transition_honoured_in_core"); x.transitionFallsBackToHard=LayoutBool(v,"transition_falls_back_to_hard"); x.transitionDispatchPath=LayoutString(v,"transition_dispatch_path"); x.boundDispatchPath=LayoutString(v,"bound_dispatch_path");x.queryDispatchPath=LayoutString(v,"query_dispatch_path"); x.managerLookup=LayoutString(v,"manager_lookup");x.managerChunkWidthMicrometres=LayoutInt(v,"manager_chunk_width_um"); x.dispatchChunkWidthMicrometres=LayoutInt(v,"dispatch_chunk_width_um"); for(const auto& a:v.Get("anchors").AsArray()) {ResolvedLayoutAnchorV2 p;p.kind=LayoutString(a,"kind");p.stableId=LayoutString(a,"stable_id");p.units=LayoutString(a,"units"); for(const auto& value:a.Get("values").AsArray())p.values.push_back(value.Integer());x.anchors.push_back(std::move(p));} s.strates.push_back(std::move(x)); } for(const auto& v:root.Get("passages").AsArray()) { ResolvedLayoutPassageV1 p;p.stableId=LayoutString(v,"stable_id"); p.upperStrateIndex=static_cast(LayoutInt(v,"upper_strate_index")); p.lowerStrateIndex=static_cast(LayoutInt(v,"lower_strate_index")); if(v.Get("style").IsNumber()) p.style=static_cast(LayoutInt(v,"style")); p.placementRadiusSubvoxels=LayoutInt(v,"placement_radius_q"); p.mouthRadiusSubvoxels=LayoutInt(v,"mouth_radius_q");p.midRadiusSubvoxels=LayoutInt(v,"mid_radius_q"); const auto& upper=v.Get("upper_mouth_q").AsArray(); const auto& lower=v.Get("lower_mouth_q").AsArray(); for(std::size_t axis=0;axis<3&&axis(LayoutInt(v,"strate_index")); stamp.kind=static_cast(LayoutInt(v,"kind")); const auto readTriplet=[&](std::string_view key,std::array& target) { const auto& values=v.Get(key).AsArray(); for(std::size_t axis=0;axis<3&&axis(LayoutInt(v,"irregularity_seed")); stamp.yawQuarterTurns=static_cast(LayoutInt(v,"yaw_quarter_turns")); s.stamps.push_back(std::move(stamp)); } for(const auto& v:root.Get("cavern_rooms").AsArray()) { ResolvedCavernRoomV1 room;room.stableId=LayoutString(v,"stable_id"); room.strateIndex=static_cast(LayoutInt(v,"strate_index")); const auto readTriplet=[&](std::string_view key,std::array& target) { const auto& values=v.Get(key).AsArray(); for(std::size_t axis=0;axis<3&&axis(LayoutInt(v,"irregularity_seed")); s.cavernRooms.push_back(std::move(room)); } for(const auto& v:root.Get("cavern_edges").AsArray()) { ResolvedCavernEdgeV1 edge;edge.stableId=LayoutString(v,"stable_id"); edge.startAnchorStableId=LayoutString(v,"start_anchor_stable_id"); edge.endRoomStableId=LayoutString(v,"end_room_stable_id"); edge.strateIndex=static_cast(LayoutInt(v,"strate_index")); const auto& points=v.Get("control_points_um").AsArray(); for(std::size_t i=0;i& target) { const auto& values=v.Get(key).AsArray(); for(std::size_t axis=0;axis<3&&axis(LayoutInt(v,"irregularity_seed")); s.cavernEdges.push_back(std::move(edge)); } for(const auto& v:root.Get("ore_veins").AsArray()) { ResolvedLayoutOreVeinV1 vein;vein.stableId=LayoutString(v,"stable_id"); vein.strateIndex=static_cast(LayoutInt(v,"strate_index")); vein.resourceMaterialId=static_cast(LayoutInt(v,"resource_material_id")); const auto& endpoints=v.Get("endpoints_um").AsArray(); for(std::size_t endpoint=0;endpoint<2&&endpoint& target) { const auto& values=v.Get(key).AsArray(); for(std::size_t axis=0;axis<3&&axis(LayoutInt(v,"irregularity_seed")); s.oreVeins.push_back(std::move(vein)); } const std::string serializedIdentity=LayoutString(root,"stable_identity"); if(!s.finalize(error))return std::nullopt; if(s.stableIdentity!=serializedIdentity){error="resolved layout stable identity does not match its canonical payload";return std::nullopt;} return s; } bool ResolveResolvedLayoutStampsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t seed, std::span requests,std::string& error) { error.clear(); if(requests.empty()) { ResolvedLayoutSnapshotV3 candidate=layout; candidate.stampVocabularyVersion=0;candidate.stamps.clear(); if(!candidate.finalize(error))return false; layout=std::move(candidate);return true; } std::vector ordered(requests.begin(),requests.end()); std::sort(ordered.begin(),ordered.end(),[](const auto& a,const auto& b) {return a.stableId0 &&ordered[requestIndex-1].stableId==request.stableId)) {error="stamp requests require non-empty unique stable IDs";return false;} if(!LayoutStampKindValid(request.kind)||request.yawQuarterTurns>3 ||request.placementRadiusMicrometres<0 ||request.placementRadiusMicrometres>100000000 ||request.smoothWidthMicrometres<0||request.smoothWidthMicrometres>10000000 ||request.irregularityAmplitudeMicrometres<0 ||request.irregularityAmplitudeMicrometres>10000000 ||request.irregularityWavelengthMicrometres<0 ||request.irregularityWavelengthMicrometres>100000000 ||(request.irregularityAmplitudeMicrometres>0 &&request.irregularityWavelengthMicrometres<=0)) {error="stamp request "+request.stableId+" has invalid or unbounded parameters";return false;} if(std::any_of(request.halfExtentsMicrometres.begin(),request.halfExtentsMicrometres.end(), [](std::int64_t extent){return extent<500000||extent>100000000;})) {error="stamp request "+request.stableId+" has an extent outside 0.5..100 m";return false;} const auto stratum=std::find_if(candidate.strates.begin(),candidate.strates.end(), [&](const auto& value){return value.strateIndex==request.strateIndex;}); if(stratum==candidate.strates.end()) {error="stamp request "+request.stableId+" names an unknown strate";return false;} for(const auto coordinate:request.anchorPositionMicrometres) if(coordinate<-1000000000000ll||coordinate>1000000000000ll) {error="stamp request "+request.stableId+" has an unbounded anchor";return false;} // The shared JSON value type stores numbers as IEEE-754 doubles; keep the // serialized seed in its exact integer range so the canonical round trip holds. const std::uint64_t baseSeed=LayoutStampSeed(seed,request.strateIndex,request.stableId) &0x001fffffffffffffull; std::uint64_t state=baseSeed; const auto randomOffset=[&](std::uint64_t bits) { const std::int64_t radius=request.placementRadiusMicrometres; if(radius==0)return std::int64_t{0}; const std::uint64_t span=static_cast(radius)*2+1; return static_cast(bits%span)-radius; }; bool placed=false; for(std::uint32_t attempt=0;attempt<(request.placementRadiusMicrometres==0?1u:512u);++attempt) { ResolvedLayoutStampV1 stamp;stamp.stableId=request.stableId; stamp.strateIndex=request.strateIndex;stamp.kind=request.kind; stamp.halfExtentsMicrometres=request.halfExtentsMicrometres; stamp.smoothWidthMicrometres=request.smoothWidthMicrometres; stamp.irregularityAmplitudeMicrometres=request.irregularityAmplitudeMicrometres; stamp.irregularityWavelengthMicrometres=request.irregularityWavelengthMicrometres; stamp.irregularitySeed=baseSeed;stamp.yawQuarterTurns=request.yawQuarterTurns; std::array offset{}; if(request.placementRadiusMicrometres>0) { offset={randomOffset(LayoutStampNext(state)), randomOffset(LayoutStampNext(state)), randomOffset(LayoutStampNext(state))/3}; const long double radius=static_cast(request.placementRadiusMicrometres); const long double distanceSquared=static_cast(offset[0])*offset[0] +static_cast(offset[1])*offset[1] +static_cast(offset[2])*offset[2]; if(distanceSquared>radius*radius)continue; } for(std::size_t axis=0;axis<3;++axis) stamp.positionMicrometres[axis]=request.anchorPositionMicrometres[axis]+offset[axis]; const std::int64_t pad=stamp.smoothWidthMicrometres +stamp.irregularityAmplitudeMicrometres; const std::array extents={ stamp.halfExtentsMicrometres[stamp.yawQuarterTurns%2?1:0]+pad, stamp.halfExtentsMicrometres[stamp.yawQuarterTurns%2?0:1]+pad, stamp.halfExtentsMicrometres[2]+pad}; for(std::size_t axis=0;axis<3;++axis) { stamp.boundsMinimumMicrometres[axis]=stamp.positionMicrometres[axis]-extents[axis]; stamp.boundsMaximumMicrometres[axis]=stamp.positionMicrometres[axis]+extents[axis]; } if(!LayoutStampSafe(candidate,stamp))continue; candidate.stamps.push_back(std::move(stamp));placed=true;break; } if(!placed) {error="stamp request "+request.stableId+" has no deterministic passage-safe content placement";return false;} } if(!candidate.finalize(error))return false; std::vector issues;std::string strictError; if(!candidate.validate(ResolvedLayoutValidationModeV3::strict,issues,strictError)) {error="strict stamp placement validation failed: "+strictError;return false;} layout=std::move(candidate);return true; } bool ResolveResolvedLayoutStampsV2(ResolvedLayoutSnapshotV3& layout,std::int32_t seed, std::span requests,std::string& error) { if(!ResolveResolvedLayoutStampsV1(layout,seed,requests,error))return false; if(requests.empty())return true; ResolvedLayoutSnapshotV3 candidate=layout; candidate.stampVocabularyVersion=ResolvedLayoutStampVocabularyVersionV2; if(!candidate.finalize(error))return false; std::vector issues;std::string strictError; if(!candidate.validate(ResolvedLayoutValidationModeV3::strict,issues,strictError)) {error="strict stamp vocabulary-2 validation failed: "+strictError;return false;} layout=std::move(candidate);return true; } bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t seed, std::span rooms, std::span edges,std::string& error) { error.clear(); ResolvedLayoutSnapshotV3 candidate=layout; candidate.cavernRooms.clear();candidate.cavernEdges.clear(); if(rooms.empty()&&edges.empty()) { candidate.cavernGraphVocabularyVersion=0; if(!candidate.finalize(error))return false; layout=std::move(candidate);return true; } candidate.cavernGraphVocabularyVersion=ResolvedLayoutCavernGraphVocabularyVersionV1; candidate.cavernRooms.assign(rooms.begin(),rooms.end()); candidate.cavernEdges.assign(edges.begin(),edges.end()); for(auto& room:candidate.cavernRooms) { // The shared JSON value type stores numbers as IEEE-754 doubles. Keep the // serialized seed in the exact integer range, as the stamp vocabulary does. room.irregularitySeed=LayoutStampSeed(seed,room.strateIndex,room.stableId) &0x001fffffffffffffull; const std::int64_t pad=room.smoothWidthMicrometres +room.irregularityAmplitudeMicrometres; for(std::size_t axis=0;axis<3;++axis) { room.boundsMinimumMicrometres[axis]=room.centerMicrometres[axis] -room.halfExtentsMicrometres[axis]-pad; room.boundsMaximumMicrometres[axis]=room.centerMicrometres[axis] +room.halfExtentsMicrometres[axis]+pad; } } for(auto& edge:candidate.cavernEdges) { edge.irregularitySeed=LayoutStampSeed(seed,edge.strateIndex,edge.stableId) &0x001fffffffffffffull; std::array low=edge.controlPointsMicrometres[0]; std::array high=low; for(const auto& point:edge.controlPointsMicrometres) for(std::size_t axis=0;axis<3;++axis) {low[axis]=std::min(low[axis],point[axis]);high[axis]=std::max(high[axis],point[axis]);} const std::int64_t xyPad=edge.boreRadiusMicrometres +edge.smoothWidthMicrometres+edge.irregularityAmplitudeMicrometres; const std::int64_t zPad=edge.smoothWidthMicrometres +edge.irregularityAmplitudeMicrometres; for(std::size_t axis=0;axis<3;++axis) { const std::int64_t pad=axis==2?zPad:xyPad; edge.boundsMinimumMicrometres[axis]=low[axis]-pad -(axis==2?edge.floorSupportDepthMicrometres:0); edge.boundsMaximumMicrometres[axis]=high[axis]+pad +(axis==2?edge.boreHeightMicrometres:0); } } if(!candidate.finalize(error))return false; std::vector issues;std::string strictError; if(!candidate.validate(ResolvedLayoutValidationModeV3::strict,issues,strictError)) {error="strict cavern graph validation failed: "+strictError;return false;} layout=std::move(candidate);return true; } bool ResolveResolvedLayoutOreVeinsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t seed, std::span requests,std::string& error) { error.clear(); ResolvedLayoutSnapshotV3 candidate=layout; candidate.oreVeins.clear();candidate.oreVeinVocabularyVersion=0; if(requests.empty()) { if(!candidate.finalize(error))return false; layout=std::move(candidate);return true; } candidate.oreVeinVocabularyVersion=ResolvedLayoutOreVeinVocabularyVersionV1; std::vector ordered(requests.begin(),requests.end()); std::sort(ordered.begin(),ordered.end(),[](const auto& a,const auto& b) {return a.stableId requestIds; candidate.oreVeins.reserve(ordered.size()); for(const auto& request:ordered) { if(request.stableId.empty() ||std::find(requestIds.begin(),requestIds.end(),request.stableId)!=requestIds.end()) {error="ore-vein requests require non-empty unique stable IDs";return false;} requestIds.push_back(request.stableId); if(request.resourceMaterialId==0||request.placementRadiusMicrometres<0 ||request.placementRadiusMicrometres>10000000 ||request.radiusMicrometres<250000||request.radiusMicrometres>20000000 ||request.irregularityAmplitudeMicrometres<0 ||request.irregularityAmplitudeMicrometres>10000000 ||request.irregularityWavelengthMicrometres<0 ||request.irregularityWavelengthMicrometres>100000000 ||(request.irregularityAmplitudeMicrometres>0 &&request.irregularityWavelengthMicrometres<=0) ||request.blendWidthMicrometres<50000 ||request.blendWidthMicrometres>2000000) {error="ore-vein request "+request.stableId+" has invalid or unbounded parameters";return false;} const auto strate=std::find_if(candidate.strates.begin(),candidate.strates.end(), [&](const auto& value){return value.strateIndex==request.strateIndex;}); if(strate==candidate.strates.end()) {error="ore-vein request "+request.stableId+" names an unknown strate";return false;} const std::uint64_t baseSeed=LayoutStampSeed(seed,request.strateIndex, std::string("ore-vein/")+request.stableId)&0x001fffffffffffffull; std::uint64_t state=baseSeed; const auto randomOffset=[&](std::uint64_t bits) { const std::int64_t radius=request.placementRadiusMicrometres; if(radius==0)return std::int64_t{0}; const std::uint64_t span=static_cast(radius)*2+1; return static_cast(bits%span)-radius; }; bool placed=false; std::string lastUnsafeReason="all candidates were outside the bounded content volume"; const std::uint32_t attempts=request.placementRadiusMicrometres==0?1u:512u; for(std::uint32_t attempt=0;attempt offset{}; if(request.placementRadiusMicrometres>0) { offset={randomOffset(LayoutStampNext(state)), randomOffset(LayoutStampNext(state)), randomOffset(LayoutStampNext(state))/3}; const long double radius=request.placementRadiusMicrometres; const long double distanceSquared=static_cast(offset[0])*offset[0] +static_cast(offset[1])*offset[1] +static_cast(offset[2])*offset[2]; if(distanceSquared>radius*radius)continue; } ResolvedLayoutOreVeinV1 vein;vein.stableId=request.stableId; vein.strateIndex=request.strateIndex; vein.resourceMaterialId=request.resourceMaterialId; vein.radiusMicrometres=request.radiusMicrometres; vein.irregularityAmplitudeMicrometres=request.irregularityAmplitudeMicrometres; vein.irregularityWavelengthMicrometres=request.irregularityWavelengthMicrometres; vein.blendWidthMicrometres=request.blendWidthMicrometres; vein.irregularitySeed=baseSeed; for(std::size_t endpoint=0;endpoint<2;++endpoint) for(std::size_t axis=0;axis<3;++axis) vein.endpointsMicrometres[endpoint][axis] =request.anchorEndpointsMicrometres[endpoint][axis]+offset[axis]; const std::int64_t pad=vein.radiusMicrometres +vein.irregularityAmplitudeMicrometres+vein.blendWidthMicrometres; for(std::size_t axis=0;axis<3;++axis) { vein.boundsMinimumMicrometres[axis]=std::min( vein.endpointsMicrometres[0][axis],vein.endpointsMicrometres[1][axis])-pad; vein.boundsMaximumMicrometres[axis]=std::max( vein.endpointsMicrometres[0][axis],vein.endpointsMicrometres[1][axis])+pad; } if(!LayoutOreVeinSafe(candidate,vein,&lastUnsafeReason))continue; candidate.oreVeins.push_back(std::move(vein));placed=true;break; } if(!placed) {error="ore-vein request "+request.stableId +" has no deterministic passage-safe content placement: "+lastUnsafeReason;return false;} } if(!candidate.finalize(error))return false; std::vector issues;std::string strictError; if(!candidate.validate(ResolvedLayoutValidationModeV3::strict,issues,strictError)) {error="strict ore-vein placement validation failed: "+strictError;return false;} layout=std::move(candidate);return true; } ResolvedLayoutOreVeinPlacementAuditV1 AuditResolvedLayoutOreVeinPlacementV1( const ResolvedLayoutSnapshotV3& layout) { ResolvedLayoutOreVeinPlacementAuditV1 result; if(layout.passages.empty()||layout.passages.front().controlPoints.empty())return result; const auto& passage=layout.passages.front(); const auto& control=passage.controlPoints.front().positionSubvoxels; ResolvedLayoutOreVeinV1 forced;forced.stableId="negative-control/forced-clear-passage"; forced.strateIndex=passage.upperStrateIndex;forced.resourceMaterialId=1; forced.radiusMicrometres=500000;forced.blendWidthMicrometres=500000; forced.irregularitySeed=1; for(std::size_t axis=0;axis<3;++axis) { const std::int64_t point=static_cast(std::llround( static_cast(control[axis])*layout.coreVoxelSizeMicrometres /WorldAssemblySubvoxelsPerVoxel)); forced.endpointsMicrometres[0][axis]=point; forced.endpointsMicrometres[1][axis]=point; forced.boundsMinimumMicrometres[axis]=point-1000000; forced.boundsMaximumMicrometres[axis]=point+1000000; } ResolvedLayoutSnapshotV3 candidate=layout; candidate.oreVeinVocabularyVersion=ResolvedLayoutOreVeinVocabularyVersionV1; candidate.oreVeins.push_back(forced); std::string finalizeError; ++result.negativeControlsRun; if(candidate.finalize(finalizeError)) { std::vector issues;std::string validationError; if(!candidate.validate(ResolvedLayoutValidationModeV3::strict,issues,validationError) &&std::any_of(issues.begin(),issues.end(),[](const auto& issue) {return issue.code=="ore_vein_placement" &&issue.detail.find("clear passage")!=std::string::npos;})) ++result.negativeControlsRejected; } result.passed=result.negativeControlsRun>0 &&result.negativeControlsRejected==result.negativeControlsRun; return result; } ResolvedLayoutStampPlacementAuditV1 AuditResolvedLayoutStampPlacementV1( const ResolvedLayoutSnapshotV3& layout) { ResolvedLayoutStampPlacementAuditV1 result; const auto makeControl=[&](const std::array& position, std::int32_t strateIndex,std::array minimum, std::array maximum,std::string id, std::string_view expectedReason) { ResolvedLayoutSnapshotV3 candidate=layout; ResolvedLayoutStampV1 stamp;stamp.stableId=std::move(id); stamp.strateIndex=strateIndex;stamp.kind=ResolvedSdfStampKindV1::carveEllipsoid; stamp.positionMicrometres=position;stamp.boundsMinimumMicrometres=minimum; stamp.boundsMaximumMicrometres=maximum; stamp.halfExtentsMicrometres={250000,250000,250000}; stamp.irregularitySeed=1; candidate.stampVocabularyVersion=ResolvedLayoutStampVocabularyVersionV1; candidate.stamps={stamp}; std::vector issues;std::string error; ++result.negativeControlsRun; if(!candidate.validate(ResolvedLayoutValidationModeV3::strict,issues,error) &&std::any_of(issues.begin(),issues.end(),[&](const auto& issue) {return issue.code=="stamp_placement" &&issue.detail.find(expectedReason)!=std::string::npos;})) ++result.negativeControlsRejected; }; const auto passage=std::find_if(layout.passages.begin(),layout.passages.end(), [](const auto& value){return value.controlPoints.size()>=2;}); if(passage!=layout.passages.end()) { const double scale=static_cast(layout.coreVoxelSizeMicrometres) /WorldAssemblySubvoxelsPerVoxel; const std::size_t routeControlIndex=passage->lowerMouthControlIndex>4 ?passage->lowerMouthControlIndex-4:0; std::array position{}; for(std::size_t axis=0;axis<3;++axis) position[axis]=static_cast(std::llround( passage->controlPoints[routeControlIndex].positionSubvoxels[axis]*scale)); makeControl(position,passage->lowerStrateIndex, {position[0]-250000,position[1]-250000,position[2]-250000}, {position[0]+250000,position[1]+250000,position[2]+250000}, "negative-control/forced-on-route","resolved passage walk corridor"); } const auto stratum=std::find_if(layout.strates.begin(),layout.strates.end(), [](const auto& value){return value.content.presentInCurrentField;}); if(stratum!=layout.strates.end()) { const std::array position={0,0,stratum->content.topMicrometres}; makeControl(position,stratum->strateIndex, {position[0]-250000,position[1]-250000,position[2]-250000}, {position[0]+250000,position[1]+250000,position[2]+250000}, "negative-control/forced-into-ceiling-band","touch or cross the ceiling/bedrock"); } result.passed=result.negativeControlsRun==2 &&result.negativeControlsRejected==result.negativeControlsRun; return result; } bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t seed, const std::vector& configs,std::string& error) { error.clear();layout.passages.clear(); if(layout.strates.size()<2)return true; std::stable_sort(layout.strates.begin(),layout.strates.end(),[](const auto& a,const auto& b) {return a.interiorTopMicrometres!=b.interiorTopMicrometres ?a.interiorTopMicrometres>b.interiorTopMicrometres:a.order(i); if(layout.coreVoxelSizeMicrometres==0) {error="passage planner requires the resolved Core voxel size";return false;} const std::int64_t qPerMetre=static_cast(std::llround( static_cast(WorldAssemblySubvoxelsPerVoxel)*1000000.0 /layout.coreVoxelSizeMicrometres)); if(qPerMetre<=0){error="passage planner could not resolve fixed-point metres";return false;} const std::int64_t playerCenterAboveFloorQ=static_cast(std::llround(1.13*qPerMetre)); const std::array,16> directions{{ {{65536,0}},{{60547,25080}},{{46341,46341}},{{25080,60547}}, {{0,65536}},{{-25080,60547}},{{-46341,46341}},{{-60547,25080}}, {{-65536,0}},{{-60547,-25080}},{{-46341,-46341}},{{-25080,-60547}}, {{0,-65536}},{{25080,-60547}},{{46341,-46341}},{{60547,-25080}}}}; const auto configFor=[&](std::int32_t strate)->const ResolvedLayoutPassageConfigV1* { const auto it=std::find_if(configs.begin(),configs.end(),[strate](const auto& c) {return c.upperStrateIndex==strate;}); return it==configs.end()?nullptr:&*it; }; const auto routeAnchor=[](const ResolvedLayoutStratumV2& s,bool departure, std::array& out) { if(departure) for(const auto& a:s.anchors) if(a.kind=="passage_anchor"&&a.values.size()>=10) {out={a.values[7],a.values[8],a.values[9]};return true;} if(!departure) for(const auto& a:s.anchors) if(a.kind=="passage_arrival"&&a.values.size()>=3) {out={a.values[0],a.values[1],a.values[2]};return true;} const ResolvedLayoutAnchorV2* selected=nullptr; for(const auto& a:s.anchors) if(a.kind=="route_point"&&a.values.size()>=3) {selected=&a;if(!departure)break;} if(!selected)return false; out={selected->values[0],selected->values[1],selected->values[2]};return true; }; const auto routeTangent=[](const ResolvedLayoutStratumV2& s,bool departure, std::array& out) { const ResolvedLayoutAnchorV2* first=nullptr,*second=nullptr,*last=nullptr,*previous=nullptr; for(const auto& a:s.anchors) if(a.kind=="route_point"&&a.values.size()>=3) {if(!first)first=&a;else if(!second)second=&a;previous=last;last=&a;} if(!first||!second||!last||!previous)return false; const auto* from=departure?previous:first; const auto* to=departure?last:second; out={to->values[0]-from->values[0],to->values[1]-from->values[1]}; return out[0]!=0||out[1]!=0; }; const auto radialWithin=[](const std::array& p,std::int64_t radius) {return radius>0&&p[0]*p[0]+p[1]*p[1]<=radius*radius;}; const auto reachValue=[](std::uint64_t hash,std::int64_t lo,std::int64_t hi) { if(hilo?static_cast(hash%static_cast(hi-lo+1)):0); }; const auto contentZQ=[&](std::int64_t micrometres) {return static_cast(std::llround(static_cast(micrometres) *WorldAssemblySubvoxelsPerVoxel/layout.coreVoxelSizeMicrometres));}; const auto distanceQ=[](std::int64_t dx,std::int64_t dy) { const auto square=static_cast(dx*dx+dy*dy); const auto floor=PassageIsqrt(square);return floor*floor==square?floor:floor+1; }; for(std::size_t interface=0;interface+1distanceMinimumSubvoxels, cfg->distanceMaximumSubvoxels):24*qPerMetre; const std::int64_t inner=cfg?std::min(cfg->distanceMinimumSubvoxels, cfg->distanceMaximumSubvoxels):8*qPerMetre; const std::int64_t placementRadius=std::max(1,outer); // Leave room for the capsule and bounded wall relief at the floor/wall seam. const bool upperIsSurfaceWorld=upper.generatorKind=="SurfaceWorld"; const std::int64_t minimumMouthRadius=static_cast(std::llround( (upperIsSurfaceWorld?2.0:1.5)*qPerMetre)); const std::int64_t mouthRadius=std::max(minimumMouthRadius, cfg?cfg->mouthRadiusSubvoxels:0); const std::int64_t midRadius=std::max( static_cast(std::llround((upperIsSurfaceWorld?3.5:1.5)*qPerMetre)), cfg?cfg->midRadiusSubvoxels:mouthRadius); const std::int64_t reachMinimum=cfg?std::max(0,cfg->reachMinimumSubvoxels):8*qPerMetre; const std::int64_t reachMaximum=cfg?std::max(reachMinimum,cfg->reachMaximumSubvoxels):24*qPerMetre; const std::uint32_t style=cfg?std::min(cfg->style,3u):1u; const std::uint32_t segments=cfg?std::clamp(cfg->segments,16u,48u):20u; const std::int64_t wander=cfg?std::max(0,cfg->wanderSubvoxels):3*qPerMetre; const std::int64_t spiralRadius=cfg?std::max(0,cfg->spiralRadiusSubvoxels):6*qPerMetre; const std::int64_t spiralTurnsQ16=cfg?std::max(16384,cfg->spiralTurnsQ16):2ll<<16; const std::int64_t verticalWobble=cfg?std::max(0,cfg->verticalWobbleSubvoxels):0; const std::uint32_t connectionCount=cfg?static_cast(std::clamp(cfg->connections,1,12)):1; for(std::uint32_t connection=0;connection upperMouth{},lowerMouth{}; const bool hasUpperRoute=connection==0&&routeAnchor(upper,true,upperMouth); const bool hasLowerRoute=connection==0&&routeAnchor(lower,false,lowerMouth); const bool surfaceGroundDeparture=hasUpperRoute&&upperIsSurfaceWorld; const ResolvedLayoutAnchorV2* passageAnchor=nullptr; if(connection==0) for(const auto& anchor:upper.anchors) if(anchor.kind=="passage_anchor"&&anchor.values.size()>=7) {passageAnchor=&anchor;break;} const bool anchoredIslandPassage=passageAnchor!=nullptr &&upper.generatorKind=="FloatingIslands"; const bool anchoredSpiral=style==2&&anchoredIslandPassage; if(!hasUpperRoute||!hasLowerRoute) { const auto xy=PassageRandomXY(PassageSeed(seed,upper.strateIndex,lower.strateIndex, connection,0x504c4143u),inner,placementRadius); if(!hasUpperRoute) { upperMouth[0]=xy[0];upperMouth[1]=xy[1]; const auto reach=reachValue(PassageSeed(seed,upper.strateIndex,lower.strateIndex, connection,0x55505248u),reachMinimum,reachMaximum); upperMouth[2]=contentZQ(upper.content.bottomMicrometres)+reach; } if(!hasLowerRoute) { lowerMouth[0]=xy[0];lowerMouth[1]=xy[1]; const auto reach=reachValue(PassageSeed(seed,upper.strateIndex,lower.strateIndex, connection,0x4c4f5752u),reachMinimum,reachMaximum); lowerMouth[2]=contentZQ(lower.content.topMicrometres)-reach; } } if(!radialWithin(upperMouth,placementRadius)||!radialWithin(lowerMouth,placementRadius)) {error=passage.stableId+" route mouth is outside the resolved PassageConfig.DistanceMax radius";return false;} passage.upperMouthSubvoxels=upperMouth;passage.lowerMouthSubvoxels=lowerMouth; constexpr std::int64_t LandingExtensionQ=8; std::array upperTangent{},lowerTangent{}; const bool hasUpperTangent=hasUpperRoute &&routeTangent(upper,true,upperTangent); const bool hasPassageArrival=std::any_of(lower.anchors.begin(),lower.anchors.end(), [](const auto& anchor){return anchor.kind=="passage_arrival" &&anchor.values.size()>=3;}); const bool hasLowerTangent=hasLowerRoute&&!hasPassageArrival &&routeTangent(lower,false,lowerTangent); const bool profiledSurfaceJoin=hasLowerTangent &&std::any_of(lower.anchors.begin(),lower.anchors.end(), [](const auto& anchor){return anchor.kind=="heightfield_profile" &&anchor.values.size()>=2&&anchor.values[0]>0;}); const std::int64_t outerRingRadius=std::max(1, placementRadius-mouthRadius-qPerMetre/2); const std::uint32_t pathStyle=anchoredSpiral?1u:style; std::int64_t ringRadius=outerRingRadius; if(pathStyle==1u) ringRadius=std::min(outerRingRadius, std::max(mouthRadius+2*qPerMetre,placementRadius*5/8)); else if(pathStyle==2u) ringRadius=std::clamp(spiralRadius, std::min(outerRingRadius,mouthRadius+qPerMetre/2),outerRingRadius); else if(pathStyle==3u) ringRadius=std::clamp(outerRingRadius -std::min(outerRingRadius/3, std::max(0,cfg?cfg->cascadeLedgeSubvoxels:0) *static_cast(cfg?cfg->cascadeSteps:4)), mouthRadius+2*qPerMetre,outerRingRadius); if(surfaceGroundDeparture) { const double departureRadius=std::hypot(static_cast(upperMouth[0]), static_cast(upperMouth[1])); const std::int64_t clearRingRadius=static_cast(std::ceil( departureRadius+mouthRadius+0.5*qPerMetre)); const std::int64_t maximumRingRadius=std::max(1, placementRadius-qPerMetre); ringRadius=std::min(maximumRingRadius, std::max(ringRadius,clearRingRadius)); } if(anchoredIslandPassage) { const std::int64_t craterRimRouteRadius=VoxelForgeCore::WorldAssemblyEvaluatorVersion>=185 &&style==0&&!anchoredSpiral&&passageAnchor&&passageAnchor->values.size()>3 ?std::max(midRadius, passageAnchor->values[3]+2*qPerMetre) :mouthRadius+qPerMetre/2; ringRadius=std::min(ringRadius,anchoredSpiral ?mouthRadius+qPerMetre/2:craterRimRouteRadius); } std::uint32_t phase=static_cast(PassageSeed(seed, upper.strateIndex,lower.strateIndex,connection,0x50484153u)%16); if(hasUpperRoute) { std::int64_t bestAlignment=std::numeric_limits::min(); const std::uint32_t hashedPhase=phase; const std::int64_t phaseX=style==0&&anchoredIslandPassage &&!anchoredSpiral&&passageAnchor ?upperMouth[0]-passageAnchor->values[0] :(hasUpperTangent?upperTangent[0]:upperMouth[0]); const std::int64_t phaseY=style==0&&anchoredIslandPassage &&!anchoredSpiral&&passageAnchor ?upperMouth[1]-passageAnchor->values[1] :(hasUpperTangent?upperTangent[1]:upperMouth[1]); for(std::uint32_t offset=0;offset<16;++offset) { const std::uint32_t candidate=static_cast( (hashedPhase+offset)%16); const std::int64_t alignment=phaseX*directions[candidate][0] +phaseY*directions[candidate][1]; if(style==0&&anchoredIslandPassage&&!anchoredSpiral&&passageAnchor) { const std::int64_t targetProjection=ringRadius*65536; if(alignment<=0)continue; const std::int64_t tangentError=std::abs(alignment-targetProjection); if(tangentError::min()) {bestAlignment=tangentError;phase=candidate;} } else if(alignment>bestAlignment) {bestAlignment=alignment;phase=candidate;} } } if(hasUpperRoute&&upperIsSurfaceWorld) { const auto& directionAtPhase=directions[phase]; const std::int64_t radialAlignment=upperMouth[0]*directionAtPhase[0] +upperMouth[1]*directionAtPhase[1]; if(radialAlignment<0) { std::int64_t bestOutwardAlignment=std::numeric_limits::min(); for(std::uint32_t candidate=0;candidatebestOutwardAlignment) {bestOutwardAlignment=alignment;phase=candidate;} } } } bool clockwise=(PassageSeed(seed,upper.strateIndex,lower.strateIndex, connection,0x44495245u)&1u)!=0; if(style==0&&anchoredIslandPassage&&!anchoredSpiral&&passageAnchor) { const auto& ringDirection=directions[phase]; const double radiusX=static_cast(ringRadius)*ringDirection[0]/65536.0; const double radiusY=static_cast(ringRadius)*ringDirection[1]/65536.0; const double anchorX=static_cast(passageAnchor->values[0]); const double anchorY=static_cast(passageAnchor->values[1]); const double connectorX=anchorX+radiusX-upperMouth[0]; const double connectorY=anchorY+radiusY-upperMouth[1]; clockwise=connectorX*(-radiusY)+connectorY*radiusX>=0.0; } else if(anchoredSpiral&&passageAnchor&&hasUpperTangent) { const double radialX=static_cast(upperMouth[0]-passageAnchor->values[0]); const double radialY=static_cast(upperMouth[1]-passageAnchor->values[1]); const double clockwiseTangentX=-radialY,clockwiseTangentY=radialX; clockwise=clockwiseTangentX*upperTangent[0] +clockwiseTangentY*upperTangent[1]>=0.0; } const int direction=clockwise?1:-1; constexpr std::int64_t TargetGradeQ16=12500; // leave margin below the 15-degree helix contract. const std::int64_t wormRadialWobble=std::min( std::max(0,wander/3),2*qPerMetre); const std::int64_t wormCenterWobble=std::min( std::max(0,wander/6),qPerMetre); const auto ringPoint=[&](std::uint32_t directionIndex,std::uint32_t loopIndex=0) { const double angle=static_cast(directionIndex)* (3.14159265358979323846/8.0); double radius=static_cast(ringRadius); double centerX=0.0,centerY=0.0; if(pathStyle==1u) { const double loop=static_cast(loopIndex); const double phaseA=loop*0.6180339887498948+0.37; const double phaseB=loop*0.4142135623730950+1.11; const double jitterX=static_cast(wormCenterWobble)*std::sin(phaseB); const double jitterY=static_cast(wormCenterWobble)*std::cos(phaseB); radius+=static_cast(wormRadialWobble) *std::sin(angle+phaseA); centerX=jitterX+(anchoredIslandPassage&&passageAnchor ?static_cast(passageAnchor->values[0]):0.0); centerY=jitterY+(anchoredIslandPassage&&passageAnchor ?static_cast(passageAnchor->values[1]):0.0); } else if(pathStyle==3u) { const std::uint32_t steps=std::max(1, cfg?cfg->cascadeSteps:4u); const double ledge=std::min(ringRadius/4, static_cast(cfg?std::max(0, cfg->cascadeLedgeSubvoxels):0)); // Give each turn a seeded, shallow angular profile so the same cosine // scallop cannot repeat at full ledge depth on every revolution. const std::uint32_t controlCount=std::clamp(steps*2u,8u, segments/2u); const double coordinate=angle*controlCount /(2.0*3.14159265358979323846); const std::uint32_t control0=static_cast(std::floor(coordinate)); const std::uint32_t control1=(control0+1u)%controlCount; const double blend=coordinate-std::floor(coordinate); const auto controlValue=[&](std::uint32_t control) { const std::uint32_t salt=0x43415343u ^(loopIndex*0x9e3779b9u)^(control*0x85ebca6bu); const std::uint32_t bits=static_cast(PassageSeed(seed, upper.strateIndex,lower.strateIndex,connection,salt)>>40); return 2.0*static_cast(bits)/16777215.0-1.0; }; const double smooth=blend*blend*(3.0-2.0*blend); const double irregular=std::lerp(controlValue(control0), controlValue(control1),smooth); const std::uint32_t loopHash=static_cast(PassageSeed(seed, upper.strateIndex,lower.strateIndex,connection, 0x4c454447u+loopIndex)>>40); const double amplitude=0.60+0.40 *static_cast(loopHash)/16777215.0; radius-=ledge*amplitude*(0.24+0.10*irregular); } if(pathStyle!=1u&&anchoredIslandPassage&&passageAnchor) { centerX=static_cast(passageAnchor->values[0]); centerY=static_cast(passageAnchor->values[1]); } return std::array{ static_cast(std::llround(centerX+radius*directions[directionIndex][0]/65536.0)), static_cast(std::llround(centerY+radius*directions[directionIndex][1]/65536.0))}; }; std::vector> xy; const auto pushXY=[&](std::array p) {if(xy.empty()||xy.back()!=p)xy.push_back(p);}; std::size_t helixEndIndex=0; std::int64_t helixBedrockZ=0; bool hasAnchorHelix=false; pushXY({upperMouth[0],upperMouth[1]}); if(anchoredSpiral&&passageAnchor&&upper.bedrock.presentInCurrentField &&spiralRadius>0) { const std::int64_t anchorX=passageAnchor->values[0]; const std::int64_t anchorY=passageAnchor->values[1]; const std::int64_t rootRadius=passageAnchor->values[2]; const std::int64_t rootBottom=passageAnchor->values[5]; const std::int64_t bedrockTop=rootBottom!=0?rootBottom :contentZQ(upper.bedrock.topMicrometres); // Passage route heights are player-center positions. End one capsule // half-height above the bedrock top so the final ramp step is walkable. helixBedrockZ=bedrockTop+qPerMetre; const std::int64_t helixRadius=spiralRadius; if(rootRadius(upperMouth[0]-anchorX), static_cast(upperMouth[1]-anchorY)) >static_cast(rootRadius+qPerMetre/2) ||upperMouth[2]<=helixBedrockZ) {error=passage.stableId+" anchor root cannot contain its authored spiral mouth or bedrock descent";return false;} const double drop=static_cast(upperMouth[2]-helixBedrockZ); const double pointsPerTurn=static_cast(segments); const double polygonCircumference=2.0*3.14159265358979323846 *static_cast(helixRadius) *(std::sin(3.14159265358979323846/pointsPerTurn) /(3.14159265358979323846/pointsPerTurn)); const double horizontalPerTurn=polygonCircumference *static_cast(TargetGradeQ16)/65536.0; const std::uint32_t authoredTurns=static_cast(std::max(1, (spiralTurnsQ16+65535)/65536)); const std::uint32_t gradeTurns=static_cast(std::max(1.0, std::ceil(drop/std::max(1.0,horizontalPerTurn)))); // The first coil grows from the island mouth offset to the full authored // radius, so reserve one extra turn for that shorter opening revolution. const std::uint32_t turns=std::max(authoredTurns,gradeTurns+1); if(turns>512) {error=passage.stableId+" authored spiral radius needs more than 512 turns to meet walkable grade";return false;} const double startAngle=std::atan2(static_cast(upperMouth[1]-anchorY), static_cast(upperMouth[0]-anchorX)); const std::uint32_t helixPoints=turns*segments; for(std::uint32_t point=1;point<=helixPoints;++point) { const double t=static_cast(point)/helixPoints; const double angle=startAngle+static_cast(clockwise?1:-1) *2.0*3.14159265358979323846*t*turns; const double radialT=std::min(1.0,static_cast(point)/segments); const double mouthOffset=std::clamp(static_cast(passageAnchor->values[3]), 0.0,static_cast(helixRadius)); const double radial=mouthOffset+(static_cast(helixRadius)-mouthOffset) *radialT; const std::array p{ static_cast(std::llround(anchorX +radial*std::cos(angle))), static_cast(std::llround(anchorY +radial*std::sin(angle)))}; pushXY(p); } helixEndIndex=xy.size()-1; hasAnchorHelix=helixEndIndex>0; } const std::int64_t routeGradeQ16=profiledSurfaceJoin?8000:hasAnchorHelix &&lower.generatorKind=="SurfaceWorld"?8000 :style==0&&anchoredIslandPassage&&!anchoredSpiral?15500:TargetGradeQ16; const std::array firstRing=ringPoint(phase); if(hasAnchorHelix&&pathStyle==1u) { // Let the anchored helix widen into the winding inter-strate route // over a full, smooth coil. A direct jump from the root radius to the // outer worm ring creates a sharp support seam inside the rock neck. const double anchorX=static_cast(passageAnchor->values[0]); const double anchorY=static_cast(passageAnchor->values[1]); const double startAngle=std::atan2(static_cast(upperMouth[1])-anchorY, static_cast(upperMouth[0])-anchorX); const double targetX=static_cast(firstRing[0])-anchorX; const double targetY=static_cast(firstRing[1])-anchorY; const double targetAngle=std::atan2(targetY,targetX); const double angleDelta=static_cast(direction)*2.0 *3.14159265358979323846 +std::remainder(targetAngle-startAngle,2.0*3.14159265358979323846); const double startRadius=static_cast(spiralRadius); const std::int64_t targetDx=firstRing[0]-passageAnchor->values[0]; const std::int64_t targetDy=firstRing[1]-passageAnchor->values[1]; const double targetRadius=static_cast(PassageIsqrt( static_cast(targetDx*targetDx+targetDy*targetDy))); const std::uint32_t transitionSegments=static_cast( std::clamp(std::ceil(std::abs(angleDelta)*(startRadius+targetRadius) /(2.0*0.75*qPerMetre)),static_cast(segments),256.0)); for(std::uint32_t step=1;step<=transitionSegments;++step) { const double t=static_cast(step)/transitionSegments; const double smooth=t*t*(3.0-2.0*t); const double radius=startRadius+(targetRadius-startRadius)*smooth; const double angle=startAngle+angleDelta*t; const std::array point=step==transitionSegments?firstRing: std::array{ static_cast(std::llround(anchorX+radius*std::cos(angle))), static_cast(std::llround(anchorY+radius*std::sin(angle)))}; pushXY(point); } } pushXY(firstRing); std::uint64_t totalHorizontal=0;std::uint32_t loops=0; const std::int64_t verticalDistance=hasAnchorHelix ?std::abs(lowerMouth[2]-helixBedrockZ):std::abs(lowerMouth[2]-upperMouth[2]); auto measurePath=[&]() { std::uint64_t total=0; const std::size_t start=hasAnchorHelix?helixEndIndex:0; for(std::size_t i=start+1;i(routeGradeQ16) >=static_cast(verticalDistance)*65536ull)break; for(int step=1;step<=16;++step) { int directionIndex=static_cast(phase)+direction*step; directionIndex=(directionIndex%16+16)%16; pushXY(ringPoint(static_cast(directionIndex), loops+(step==16?1u:0u))); } ++loops; } if(hasLowerTangent) { std::uint32_t lowerPhase=static_cast(PassageSeed(seed, upper.strateIndex,lower.strateIndex,connection,0x4c505248u)%16); std::int64_t bestAlignment=std::numeric_limits::min(); bool lowerRouteReversed=false; const std::uint32_t hashedLowerPhase=lowerPhase; for(std::uint32_t offset=0;offset<16;++offset) { const std::uint32_t candidate=static_cast( (hashedLowerPhase+offset)%16); const auto terminal=ringPoint(candidate,loops); const std::int64_t dx=lowerMouth[0]-terminal[0]; const std::int64_t dy=lowerMouth[1]-terminal[1]; const std::int64_t alignment=dx*lowerTangent[0]+dy*lowerTangent[1]; const std::int64_t cross=std::abs(dx*lowerTangent[1]-dy*lowerTangent[0]); if(alignment<=0||cross==0)continue; const auto length=distanceQ(dx,dy); const std::int64_t score=alignment*65536/static_cast(length); if(score>bestAlignment) {bestAlignment=score;lowerPhase=candidate;} } // A route is walkable in either direction. If the approach ring lies on // the wrong side of its first route tangent, attach to the same endpoint // while traversing the route backwards instead of rejecting the layout. if(bestAlignment==std::numeric_limits::min()) { for(std::uint32_t offset=0;offset<16;++offset) { const std::uint32_t candidate=static_cast( (hashedLowerPhase+offset)%16); const auto terminal=ringPoint(candidate,loops); const std::int64_t dx=lowerMouth[0]-terminal[0]; const std::int64_t dy=lowerMouth[1]-terminal[1]; const std::int64_t alignment=dx*lowerTangent[0]+dy*lowerTangent[1]; const std::int64_t cross=std::abs(dx*lowerTangent[1]-dy*lowerTangent[0]); if(alignment>=0||cross==0)continue; const auto length=distanceQ(dx,dy); const std::int64_t score=-alignment*65536/static_cast(length); if(score>bestAlignment) {bestAlignment=score;lowerPhase=candidate;lowerRouteReversed=true;} } } if(bestAlignment==std::numeric_limits::min()) {error=passage.stableId+" could not find a non-overlapping lower landing approach";return false;} std::printf("[ResolvedPassageLowerApproach] id=%s direction=%s alignment_q16=%lld status=PASS\n", passage.stableId.c_str(),lowerRouteReversed?"reverse":"forward", static_cast(bestAlignment)); if(phase!=lowerPhase) for(int step=1;step<=16;++step) { int directionIndex=static_cast(phase)+direction*step; directionIndex=(directionIndex%16+16)%16; pushXY(ringPoint(static_cast(directionIndex),loops)); if(static_cast(directionIndex)==lowerPhase)break; } pushXY(ringPoint(lowerPhase)); } pushXY({lowerMouth[0],lowerMouth[1]});totalHorizontal=measurePath(); std::size_t lowerMouthIndex=xy.size()-1; std::vector> lowerLandingPoints; if(hasLowerTangent) { std::array previousRoutePoint=lowerMouth; std::int64_t remainingQ=LandingExtensionQ*qPerMetre; bool skippedMouth=false; for(const auto& anchor:lower.anchors) if(anchor.kind=="route_point"&&anchor.values.size()>=3) { const std::array routePoint{{anchor.values[0], anchor.values[1],anchor.values[2]}}; if(!skippedMouth){skippedMouth=true;continue;} if(remainingQ<=0)break; const std::int64_t dx=routePoint[0]-previousRoutePoint[0]; const std::int64_t dy=routePoint[1]-previousRoutePoint[1]; const std::uint64_t segmentLength=distanceQ(dx,dy); if(segmentLength==0)continue; const std::int64_t advance=std::min(remainingQ, static_cast(segmentLength)); std::array landingPoint{}; for(std::size_t axis=0;axis<3;++axis) landingPoint[axis]=previousRoutePoint[axis]+PassageRoundRatio( routePoint[axis]-previousRoutePoint[axis],advance, static_cast(segmentLength)); if(!radialWithin(landingPoint,placementRadius))break; lowerLandingPoints.push_back(landingPoint); pushXY({landingPoint[0],landingPoint[1]}); previousRoutePoint=landingPoint;remainingQ-=advance; if(advance(segmentLength))break; } if(lowerLandingPoints.empty()) {error=passage.stableId+" lower route has no landing length inside PassageConfig.DistanceMax";return false;} } if(VoxelForgeCore::WorldAssemblyEvaluatorVersion>=188 &&style==0&&anchoredIslandPassage&&!anchoredSpiral&&xy.size()>4) { std::vector> refined; refined.reserve(xy.size()*2); for(std::size_t index=0;index+1(std::llround(0.5*( static_cast(xy[index][0]) +static_cast(xy[index+1][0])))), static_cast(std::llround(0.5*( static_cast(xy[index][1]) +static_cast(xy[index+1][1]))))}); } refined.push_back(xy.back()); if(lowerMouthIndex>3)lowerMouthIndex+=lowerMouthIndex-3; xy.swap(refined); } totalHorizontal=measurePath(); if(loops>=256||totalHorizontal==0) {error=passage.stableId+" could not fit a supported grade inside the resolved placement radius";return false;} std::vector cumulative(xy.size(),0); for(std::size_t i=1;i(std::llround(1.5*qPerMetre))) :mouthRadius; const std::uint64_t surfaceMouthBlendLength=static_cast( std::max(8*qPerMetre,2*mouthRadius)); for(std::size_t i=0;i=xy.size()-1)?mouthRadius:midRadius; if(surfaceGroundDeparture&&i(cumulative[i]) /surfaceMouthBlendLength,0.0,1.0); const double smooth=t*t*(3.0-2.0*t); radius=surfacePortalRadius+static_cast(std::llround( (midRadius-surfacePortalRadius)*smooth)); } std::int64_t playerZ=0; if(i>lowerMouthIndex) playerZ=lowerLandingPoints[i-lowerMouthIndex-1][2]; else if(hasAnchorHelix&&i<=helixEndIndex) playerZ=upperMouth[2]+PassageRoundRatio(helixBedrockZ-upperMouth[2], static_cast(cumulative[i]), static_cast(helixHorizontal)); else { const std::uint64_t segmentStart=hasAnchorHelix?helixHorizontal:0; const std::int64_t startZ=hasAnchorHelix?helixBedrockZ:upperMouth[2]; const std::uint64_t segmentLength=mouthHorizontal-segmentStart; playerZ=startZ+PassageRoundRatio(lowerMouth[2]-startZ, static_cast(cumulative[i]-segmentStart), static_cast(segmentLength)); } ResolvedLayoutPassagePointV1 point; point.positionSubvoxels={xy[i][0],xy[i][1],playerZ+radius-playerCenterAboveFloorQ}; point.radiusSubvoxels=radius;passage.controlPoints.push_back(point); } if(surfaceGroundDeparture) std::printf("[ResolvedSurfaceWorldDepartureMouth] passage=%s ground_floor_z_m=%.3f portal_radius_m=%.3f tunnel_radius_m=%.3f blend_length_m=%.3f departure_xy_m=(%.3f,%.3f) first_tunnel_xy_m=(%.3f,%.3f) first_tunnel_floor_z_m=%.3f first_tunnel_radius_m=%.3f\n", passage.stableId.c_str(),(upperMouth[2]-static_cast(std::llround(0.88*qPerMetre)))/static_cast(qPerMetre), surfacePortalRadius/static_cast(qPerMetre), midRadius/static_cast(qPerMetre), surfaceMouthBlendLength/static_cast(qPerMetre), upperMouth[0]/static_cast(qPerMetre), upperMouth[1]/static_cast(qPerMetre), passage.controlPoints.size()>1?passage.controlPoints[1].positionSubvoxels[0] /static_cast(qPerMetre):0.0, passage.controlPoints.size()>1?passage.controlPoints[1].positionSubvoxels[1] /static_cast(qPerMetre):0.0, passage.controlPoints.size()>1? (passage.controlPoints[1].positionSubvoxels[2] -passage.controlPoints[1].radiusSubvoxels+playerCenterAboveFloorQ -static_cast(std::llround(0.88*qPerMetre))) /static_cast(qPerMetre):0.0, passage.controlPoints.size()>1?passage.controlPoints[1].radiusSubvoxels /static_cast(qPerMetre):0.0); passage.controlPoints.front().positionSubvoxels[2]=upperMouth[2] +passage.controlPoints.front().radiusSubvoxels-playerCenterAboveFloorQ; passage.controlPoints[lowerMouthIndex].positionSubvoxels[2]=lowerMouth[2] +passage.controlPoints[lowerMouthIndex].radiusSubvoxels -playerCenterAboveFloorQ; layout.passages.push_back(std::move(passage)); } } return true; } ResolvedLayoutValidatorAuditV3 AuditResolvedLayoutValidatorV3() { ResolvedLayoutValidatorAuditV3 result; auto make=[](std::string id,std::int32_t order,std::int64_t top,std::int64_t bottom) { ResolvedLayoutStratumV2 s;s.stableId=std::move(id);s.order=order;s.strateIndex=order;s.generatorKind="synthetic"; s.interiorTopMicrometres=top;s.interiorBottomMicrometres=bottom;s.voxelSizeMicrometres=250000; s.transitionMode="Gradient";s.transitionWidthMicrometres=4000000; s.transitionHonouredInCore=true; s.ceiling={"ceiling",top,top+1000000,true,true,false,"closed ceiling band","upper face","synthetic"}; s.content={"content",bottom,top,true,true,false,"generator content","none","none"}; s.bedrock={"bedrock",bottom-1000000,bottom,true,true,false,"breakable bedrock","lower face","none"}; return s; }; const auto expectReject=[&](std::string name,ResolvedLayoutSnapshotV3 candidate) { ++result.controlsRun;std::vector issues;std::string error; if(!candidate.validate(ResolvedLayoutValidationModeV3::strict,issues,error))++result.controlsRejectedAsExpected; else result.failures.push_back({"negative_control_accepted",std::move(name)+" was accepted by strict validator"}); }; auto base=ResolvedLayoutSnapshotV3{};base.worldIdentity="synthetic";base.ownerVoxelSizeMicrometres=125000; base.coreVoxelSizeMicrometres=250000;base.strates={make("a",0,10000000,0),make("b",1,-2000000,-12000000)}; ResolvedLayoutPassageConfigV1 passageConfig;passageConfig.upperStrateIndex=0;passageConfig.connections=1; passageConfig.mouthRadiusSubvoxels=1280;passageConfig.midRadiusSubvoxels=1280; passageConfig.reachMinimumSubvoxels=2048;passageConfig.reachMaximumSubvoxels=2048; passageConfig.distanceMinimumSubvoxels=1024;passageConfig.distanceMaximumSubvoxels=10240; std::string planError; if(!PlanResolvedLayoutPassagesV1(base,19,{passageConfig},planError)) result.failures.push_back({"synthetic_passage_plan_failed",planError}); {std::vector issues;std::string error; if(!base.validate(ResolvedLayoutValidationModeV3::strict,issues,error)) result.failures.push_back({"positive_control_rejected","valid ceiling/content/bedrock layout was rejected"});} auto missingPassage=base;missingPassage.passages.clear();expectReject("missing interface passage",missingPassage); auto outsidePassage=base; if(!outsidePassage.passages.empty()) { outsidePassage.passages.front().upperMouthSubvoxels[0] =outsidePassage.passages.front().placementRadiusSubvoxels+1; outsidePassage.passages.front().controlPoints.front().positionSubvoxels[0] =outsidePassage.passages.front().upperMouthSubvoxels[0]; } expectReject("passage mouth outside placement radius",outsidePassage); auto boundaryGap=base;boundaryGap.strates[1].ceiling.topMicrometres-=1; expectReject("bedrock/next-ceiling gap",boundaryGap); auto missingBedrock=base;missingBedrock.strates[0].bedrock.presentInCurrentField=false;expectReject("missing bedrock",missingBedrock); auto missingCeiling=base;missingCeiling.strates[0].ceiling.presentInCurrentField=false;expectReject("missing ceiling",missingCeiling); auto wrongOrder=base;wrongOrder.strates[0].content.kind="bedrock";expectReject("wrong band order",wrongOrder); auto largeOverlap=base;largeOverlap.strates[0].transitionInterpenetrationMicrometres=4000001;expectReject("interpenetration exceeds transition width",largeOverlap); auto thinBand=base; thinBand.strates[0].bedrock.topMicrometres=thinBand.strates[0].bedrock.bottomMicrometres +2ll*base.coreVoxelSizeMicrometres-1; thinBand.strates[0].content.bottomMicrometres=thinBand.strates[0].bedrock.topMicrometres; expectReject("bedrock band below minimum thickness",thinBand); auto genericOverlap=base;genericOverlap.strates[0].bedrock.bottomMicrometres-=1;expectReject("nominal band overlap",genericOverlap); auto genericGap=base;genericGap.strates[0].bedrock.bottomMicrometres+=1;expectReject("nominal band gap",genericGap); result.passed=result.controlsRun==result.controlsRejectedAsExpected&&result.failures.empty();return result; } }