wip(assembler): round L29 -- strate transitions honoured at the bedrock/ceiling interface

EVoxelStrateTransition from the upper strate shapes the bedrock(N)/ceiling(N+1) interface in the
shared composition layer: Hard flat; Gradient = smooth 2D hash surface (16 m owner width, about 4 m
measured undulation); Interleaved = 3D domain-warped fingered overlap within the width. Each band
keeps >= 2 Core voxels solid; faces return to the seam near passage mouths. Interface audit over
512 m: 0 hole columns for all modes; wider-than-declared and too-thin controls rejected. All passage,
route, proof and validator audits pass; deterministic Clang = GCC. Evaluator 15. Cost +4% SW,
+1.3% FI vs L28. Open: ring-like artifact on the FI floor capture; transition look unverified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-21 09:16:12 +02:00
1 parent f23ab7b9fa
commit 63c7e563d0
16 files changed
+794 -42

No files matched your search

+184 -8
View File
@@ -13,6 +13,97 @@ 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<std::uint8_t>(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<std::uint32_t>(seed));
TransitionHashWord(hash, static_cast<std::uint32_t>(interfaceIndex));
TransitionHashWord(hash, static_cast<std::uint64_t>(x));
TransitionHashWord(hash, static_cast<std::uint64_t>(y));
TransitionHashWord(hash, static_cast<std::uint64_t>(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<std::uint32_t>(
TransitionHash(seed, interfaceIndex, x, y, z, salt) >> 32);
return static_cast<std::int64_t>(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);
const auto at = [&](std::int64_t dx, std::int64_t dy)
{ return TransitionNoiseValue(seed, interfaceIndex, ix + dx, iy + dy, 0, salt); };
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<std::int64_t>(0, stratum.transitionWidthMicrometres);
}
std::string LayoutEscape(std::string_view value)
{
std::string out="\"";
@@ -41,12 +132,6 @@ namespace
}
std::string LayoutHex(std::uint64_t value)
{std::ostringstream out;out<<std::hex<<std::setfill('0')<<std::setw(16)<<value;return out.str();}
std::int64_t LayoutTransitionLimit(const ResolvedLayoutStratumV2& stratum)
{
if(stratum.transitionMode=="Hard"||stratum.transitionFallsBackToHard)return 0;
return stratum.transitionWidthMicrometres
+(stratum.transitionMode=="Interleaved"?stratum.transitionWarpAmplitudeMicrometres:0);
}
std::string LayoutBandJson(const ResolvedLayoutBandV2& band)
{
std::ostringstream out;out<<"{\"kind\":"<<LayoutEscape(band.kind)
@@ -205,6 +290,84 @@ namespace
}
}
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<std::int64_t>(0, upperThickness);
result.lowerCeilingBottomMicrometres = seam - std::max<std::int64_t>(0, lowerThickness);
result.lowerCeilingTopMicrometres = seam;
const std::int64_t width = upper.transitionFallsBackToHard
? 0 : std::max<std::int64_t>(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<std::int64_t>(
1000000, std::max<std::int64_t>(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<std::int64_t>(1000000,
std::max<std::int64_t>(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);
@@ -216,7 +379,8 @@ bool ResolvedLayoutSnapshotV3::finalize(std::string& error)
{
error.clear();
if(schemaVersion!=ResolvedLayoutSchemaVersionV3||worldIdentity.empty()||strates.empty()
||strateLayoutChunkSizeMicrometres==0||strateLayoutChunkWidthSubvoxels<=0)
||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
@@ -257,7 +421,8 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
{
issues.clear();error.clear();
if(schemaVersion!=ResolvedLayoutSchemaVersionV3||worldIdentity.empty()||strates.empty()
||strateLayoutChunkSizeMicrometres==0||strateLayoutChunkWidthSubvoxels<=0)
||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)});};
@@ -271,6 +436,8 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
if(s.transitionInterpenetrationMicrometres>effectiveTransitionWidth
||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");
@@ -290,6 +457,10 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
{
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<minimumThickness)
add("band_thinner_than_minimum",s.stableId+" "+std::string(expected)
+" is thinner than the two-Core-voxel minimum");
if(b.protectedBand&&b.diggable)
add("invalid_protection",s.stableId+" band is both protected and diggable");
if(b.solidDensityQ16<=0)
@@ -1055,6 +1226,11 @@ ResolvedLayoutValidatorAuditV3 AuditResolvedLayoutValidatorV3()
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;