feat(core): deterministic bounded SDF landmark stamps, off by default (L43); research R10

Stamp vocabulary v1 (carve ellipsoid/arch, fill pillar/dais) with smooth CSG and seeded relief; the
director resolves records (stable ID, strate, micrometre position, conservative bounds) from
hash(seed, strate, ID) with up to 512 candidates and rejects any that touch a passage mouth, walk corridor,
content face or ceiling/bedrock band; a stamp BVH skips tiles outside all bounds. No requests: canonical
JSON and owner hashes unchanged. --landmark-chamber-demo adds a SW chamber (buried; see review).
fast/medium/full PASS on clean builds. Research R10 (read-only): ore/resource channel design.
Review also records a pre-existing W1/W12 determinism mismatch at a non-spawn camera (L37+, not S14).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-23 21:50:56 +02:00
1 parent 497e974178
commit 422dd7761a
12 files changed
+1138 -2

No files matched your search

+355 -1
View File
@@ -168,6 +168,7 @@ public:
minimumSubvoxels_,maximumSubvoxels_);
featureIndices_=shape.BuildPassageFeatureTileList(
minimumSubvoxels_,maximumSubvoxels_);
stampIndices_=shape.BuildStampTileList(minimumSubvoxels_,maximumSubvoxels_);
passageRoutes_.assign(shape.ResolvedLayout?shape.ResolvedLayout->passages.size():0,0);
passageColumns_=passageRoutes_;
passageMouthSupports_=passageRoutes_;
@@ -216,11 +217,14 @@ public:
&&shape_->ResolvedBoundaryBandMayAffectAtZMicrometres(zMicrometres);
}
std::size_t featureCount() const{return featureIndices_.size();}
bool hasStampCandidates() const{return !stampIndices_.empty();}
const std::vector<std::uint32_t>& stampCandidates() const{return stampIndices_;}
private:
const FStandaloneShapeWorld* shape_;
std::array<std::int64_t,3> minimumSubvoxels_,maximumSubvoxels_;
std::vector<std::uint32_t> featureIndices_;
std::vector<std::uint32_t> stampIndices_;
std::vector<std::uint8_t> passageRoutes_,passageColumns_,passageMouthSupports_;
bool routeFeatureOverlap_=false,columnZOverlap_=false;
bool mayContainResolvedBoundaryBand_=true;
@@ -1775,7 +1779,11 @@ public:
}
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
{return ApplyResolvedStratePassageCarves(ApplyResolvedStrateBoundaryBand(density,X,Y,Z),X,Y,Z);}
{
const float composed=ApplyResolvedStratePassageCarves(
ApplyResolvedStrateBoundaryBand(density,X,Y,Z),X,Y,Z);
return StampRuntime.empty()?composed:ApplyResolvedSdfStamps(composed,X,Y,Z);
}
bool ResolvedPassageIntersectsBox(const int32 minimum[3],const int32 maximum[3]) const
{
@@ -3170,6 +3178,169 @@ private:
std::size_t first=0,end=0;
int left=-1,right=-1;
};
struct FStampSpatialBound
{
std::array<std::int64_t,3> minimumSubvoxels{},maximumSubvoxels{};
};
struct FStampSpatialNode
{
std::array<std::int64_t,3> minimumSubvoxels{},maximumSubvoxels{};
std::size_t first=0,end=0;
int left=-1,right=-1;
};
struct FStampRuntime
{
const VoxelForgeCore::ResolvedLayoutStampV1* record=nullptr;
std::array<float,3> positionVoxels{},halfExtentsVoxels{};
float smoothWidthVoxels=0.0f;
float irregularityAmplitudeVoxels=0.0f;
float irregularityWavelengthVoxels=0.0f;
std::uint64_t irregularitySeed=0;
std::uint8_t quarterTurns=0;
};
int BuildStampSpatialNode(std::size_t first,std::size_t end)
{
FStampSpatialNode node;node.first=first;node.end=end;
node.minimumSubvoxels.fill(std::numeric_limits<std::int64_t>::max());
node.maximumSubvoxels.fill(std::numeric_limits<std::int64_t>::min());
for(std::size_t offset=first;offset<end;++offset)
{
const auto& bound=StampSpatialBounds[StampSpatialOrder[offset]];
for(std::size_t axis=0;axis<3;++axis)
{
node.minimumSubvoxels[axis]=std::min(node.minimumSubvoxels[axis],
bound.minimumSubvoxels[axis]);
node.maximumSubvoxels[axis]=std::max(node.maximumSubvoxels[axis],
bound.maximumSubvoxels[axis]);
}
}
const int nodeIndex=static_cast<int>(StampSpatialNodes.size());
StampSpatialNodes.push_back(node);
if(end-first>8)
{
std::size_t splitAxis=0;
for(std::size_t axis=1;axis<3;++axis)
if(static_cast<long double>(node.maximumSubvoxels[axis])
-node.minimumSubvoxels[axis]
>static_cast<long double>(node.maximumSubvoxels[splitAxis])
-node.minimumSubvoxels[splitAxis])splitAxis=axis;
const std::size_t middle=first+(end-first)/2;
std::nth_element(StampSpatialOrder.begin()+first,
StampSpatialOrder.begin()+middle,StampSpatialOrder.begin()+end,
[this,splitAxis](std::uint32_t a,std::uint32_t b)
{
const auto& left=StampSpatialBounds[a];const auto& right=StampSpatialBounds[b];
const long double leftCenter=static_cast<long double>(
left.minimumSubvoxels[splitAxis])+left.maximumSubvoxels[splitAxis];
const long double rightCenter=static_cast<long double>(
right.minimumSubvoxels[splitAxis])+right.maximumSubvoxels[splitAxis];
return leftCenter==rightCenter?a<b:leftCenter<rightCenter;
});
StampSpatialNodes[nodeIndex].left=BuildStampSpatialNode(first,middle);
StampSpatialNodes[nodeIndex].right=BuildStampSpatialNode(middle,end);
}
return nodeIndex;
}
void QueryStampSpatialNode(int nodeIndex,
const std::array<std::int64_t,3>& minimum,
const std::array<std::int64_t,3>& maximum,
std::vector<std::uint32_t>& result) const
{
if(nodeIndex<0)return;
const auto& node=StampSpatialNodes[static_cast<std::size_t>(nodeIndex)];
for(std::size_t axis=0;axis<3;++axis)
if(node.maximumSubvoxels[axis]<minimum[axis]
||node.minimumSubvoxels[axis]>maximum[axis])return;
if(node.left<0)
{
for(std::size_t offset=node.first;offset<node.end;++offset)
{
const auto index=StampSpatialOrder[offset];
const auto& bound=StampSpatialBounds[index];bool overlap=true;
for(std::size_t axis=0;axis<3;++axis)
overlap=overlap&&bound.minimumSubvoxels[axis]<=maximum[axis]
&&bound.maximumSubvoxels[axis]>=minimum[axis];
if(overlap)result.push_back(index);
}
return;
}
QueryStampSpatialNode(node.left,minimum,maximum,result);
QueryStampSpatialNode(node.right,minimum,maximum,result);
}
void BuildStampSpatialIndex()
{
StampSpatialBounds.clear();StampSpatialOrder.clear();StampSpatialNodes.clear();
StampRuntime.clear();
if(!ResolvedLayout||ResolvedLayout->stamps.empty()||!Owner
||Owner->CoreVoxelSizeMicrons==0)return;
constexpr long double Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
const long double scale=Q/Owner->CoreVoxelSizeMicrons;
StampSpatialBounds.reserve(ResolvedLayout->stamps.size());
StampRuntime.reserve(ResolvedLayout->stamps.size());
for(const auto& stamp:ResolvedLayout->stamps)
{
FStampSpatialBound bound;
for(std::size_t axis=0;axis<3;++axis)
{
bound.minimumSubvoxels[axis]=static_cast<std::int64_t>(std::floor(
static_cast<long double>(stamp.boundsMinimumMicrometres[axis])*scale));
bound.maximumSubvoxels[axis]=static_cast<std::int64_t>(std::ceil(
static_cast<long double>(stamp.boundsMaximumMicrometres[axis])*scale));
}
StampSpatialBounds.push_back(bound);
FStampRuntime runtime;runtime.record=&stamp;
for(std::size_t axis=0;axis<3;++axis)
runtime.positionVoxels[axis]=static_cast<float>(
static_cast<double>(stamp.positionMicrometres[axis])
/Owner->CoreVoxelSizeMicrons);
const std::size_t xAxis=stamp.yawQuarterTurns%2?1:0;
const std::size_t yAxis=stamp.yawQuarterTurns%2?0:1;
runtime.halfExtentsVoxels={
static_cast<float>(static_cast<double>(stamp.halfExtentsMicrometres[0])
/Owner->CoreVoxelSizeMicrons),
static_cast<float>(static_cast<double>(stamp.halfExtentsMicrometres[1])
/Owner->CoreVoxelSizeMicrons),
static_cast<float>(static_cast<double>(stamp.halfExtentsMicrometres[2])
/Owner->CoreVoxelSizeMicrons)};
(void)xAxis;(void)yAxis;
runtime.smoothWidthVoxels=static_cast<float>(
static_cast<double>(stamp.smoothWidthMicrometres)/Owner->CoreVoxelSizeMicrons);
runtime.irregularityAmplitudeVoxels=static_cast<float>(
static_cast<double>(stamp.irregularityAmplitudeMicrometres)
/Owner->CoreVoxelSizeMicrons);
runtime.irregularityWavelengthVoxels=static_cast<float>(
static_cast<double>(stamp.irregularityWavelengthMicrometres)
/Owner->CoreVoxelSizeMicrons);
runtime.irregularitySeed=stamp.irregularitySeed;
runtime.quarterTurns=stamp.yawQuarterTurns;
StampRuntime.push_back(runtime);
}
StampSpatialOrder.resize(StampSpatialBounds.size());
for(std::size_t i=0;i<StampSpatialOrder.size();++i)
StampSpatialOrder[i]=static_cast<std::uint32_t>(i);
if(!StampSpatialOrder.empty())BuildStampSpatialNode(0,StampSpatialOrder.size());
}
std::vector<std::uint32_t> BuildStampTileList(
const std::array<std::int64_t,3>& minimum,
const std::array<std::int64_t,3>& maximum) const
{
std::vector<std::uint32_t> result;
if(StampSpatialBounds.empty())return result;
for(std::size_t axis=0;axis<3;++axis)
if(minimum[axis]>maximum[axis])
{
result.resize(StampSpatialBounds.size());
for(std::size_t i=0;i<result.size();++i)
result[i]=static_cast<std::uint32_t>(i);
return result;
}
QueryStampSpatialNode(0,minimum,maximum,result);
return result;
}
int BuildPassageNearestNode(std::size_t passageIndex,std::vector<std::size_t> segments)
{
@@ -3343,6 +3514,7 @@ private:
void BuildPassageSpatialIndices()
{
BuildStampSpatialIndex();
PassageTileFeatures.clear();PassageFeatureBounds.clear();
PassageFeatureOrder.clear();PassageFeatureNodes.clear();
PassageFeatureBoundsFailOpen=false;
@@ -4270,6 +4442,19 @@ public:
const int32 Pad=std::max(Step,1);
const int32 BMin=Origin.Z-Pad;
const int32 BMax=Origin.Z+(Cells+1)*Step;
if(!StampSpatialBounds.empty())
{
constexpr std::int64_t Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
const std::array<std::int64_t,3> minimum={
static_cast<std::int64_t>(Origin.X-Pad)*Q,
static_cast<std::int64_t>(Origin.Y-Pad)*Q,
static_cast<std::int64_t>(BMin)*Q};
const std::array<std::int64_t,3> maximum={
static_cast<std::int64_t>(Origin.X+(Cells+1)*Step)*Q,
static_cast<std::int64_t>(Origin.Y+(Cells+1)*Step)*Q,
static_cast<std::int64_t>(BMax)*Q};
if(!BuildStampTileList(minimum,maximum).empty())return false;
}
const float LayoutWidth=static_cast<float>(GetResolvedLayoutChunkWidthVoxels());
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin)/LayoutWidth);
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax)/LayoutWidth);
@@ -4360,6 +4545,20 @@ public:
else ++Counts.MixedBoxes;
return Result;
};
if(!StampSpatialBounds.empty())
{
constexpr std::int64_t Q=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
const std::array<std::int64_t,3> minimum={
static_cast<std::int64_t>(BMin[0])*Q,
static_cast<std::int64_t>(BMin[1])*Q,
static_cast<std::int64_t>(BMin[2])*Q};
const std::array<std::int64_t,3> maximum={
static_cast<std::int64_t>(BMax[0])*Q,
static_cast<std::int64_t>(BMax[1])*Q,
static_cast<std::int64_t>(BMax[2])*Q};
if(!BuildStampTileList(minimum,maximum).empty())
return Finish(EVoxelTileClass::Mixed,true);
}
if(ResolvedPassageIntersectsBox(BMin,BMax))
return Finish(EVoxelTileClass::Mixed,true);
if(!EditBrushes.empty()&&EditBoundsMin.X<=BMax[0]&&EditBoundsMax.X>=BMin[0]
@@ -5368,6 +5567,157 @@ private:
}
static float SmoothMaxDistance(float A,float B,float Width)
{return -SmoothMinDistance(-A,-B,Width);}
static std::uint64_t StampMix64(std::uint64_t value)
{
value^=value>>30;value*=0xbf58476d1ce4e5b9ull;
value^=value>>27;value*=0x94d049bb133111ebull;
return value^(value>>31);
}
static float StampLatticeValue(std::uint64_t seed,std::int64_t x,
std::int64_t y,std::int64_t z)
{
std::uint64_t hash=StampMix64(seed^StampMix64(static_cast<std::uint64_t>(x)));
hash=StampMix64(hash^StampMix64(static_cast<std::uint64_t>(y)));
hash=StampMix64(hash^StampMix64(static_cast<std::uint64_t>(z)));
const double unit=static_cast<double>(hash>>11)
/static_cast<double>((std::uint64_t{1}<<53)-1);
return static_cast<float>(unit*2.0-1.0);
}
static float StampValueNoise(float x,float y,float z,float wavelength,
std::uint64_t seed)
{
if(!(wavelength>0.0f))return 0.0f;
const double sx=static_cast<double>(x)/wavelength;
const double sy=static_cast<double>(y)/wavelength;
const double sz=static_cast<double>(z)/wavelength;
const std::int64_t ix=static_cast<std::int64_t>(std::floor(sx));
const std::int64_t iy=static_cast<std::int64_t>(std::floor(sy));
const std::int64_t iz=static_cast<std::int64_t>(std::floor(sz));
const auto smooth=[](float t){return t*t*(3.0f-2.0f*t);};
const float tx=smooth(static_cast<float>(sx-ix));
const float ty=smooth(static_cast<float>(sy-iy));
const float tz=smooth(static_cast<float>(sz-iz));
const auto lerp=[](float a,float b,float t){return a+(b-a)*t;};
const float z0=lerp(lerp(StampLatticeValue(seed,ix,iy,iz),
StampLatticeValue(seed,ix+1,iy,iz),tx),
lerp(StampLatticeValue(seed,ix,iy+1,iz),
StampLatticeValue(seed,ix+1,iy+1,iz),tx),ty);
const float z1=lerp(lerp(StampLatticeValue(seed,ix,iy,iz+1),
StampLatticeValue(seed,ix+1,iy,iz+1),tx),
lerp(StampLatticeValue(seed,ix,iy+1,iz+1),
StampLatticeValue(seed,ix+1,iy+1,iz+1),tx),ty);
return lerp(z0,z1,tz);
}
static float StampEllipsoidSdf(float x,float y,float z,
const std::array<float,3>& radius)
{
const float minimum=std::min({radius[0],radius[1],radius[2]});
if(!(minimum>0.0f))return std::numeric_limits<float>::infinity();
const float k0=std::sqrt((x/radius[0])*(x/radius[0])
+(y/radius[1])*(y/radius[1])+(z/radius[2])*(z/radius[2]));
const float k1=std::sqrt((x/(radius[0]*radius[0]))*(x/(radius[0]*radius[0]))
+(y/(radius[1]*radius[1]))*(y/(radius[1]*radius[1]))
+(z/(radius[2]*radius[2]))*(z/(radius[2]*radius[2])));
return k1>1.0e-8f?k0*(k0-1.0f)/k1:-minimum;
}
static float StampRoundedBoxSdf(float x,float y,float z,
const std::array<float,3>& halfSize,float rounding)
{
const float qx=std::abs(x)-halfSize[0]+rounding;
const float qy=std::abs(y)-halfSize[1]+rounding;
const float qz=std::abs(z)-halfSize[2]+rounding;
const float outside=std::sqrt(std::max(qx,0.0f)*std::max(qx,0.0f)
+std::max(qy,0.0f)*std::max(qy,0.0f)
+std::max(qz,0.0f)*std::max(qz,0.0f));
return outside+std::min(std::max({qx,qy,qz}),0.0f)-rounding;
}
static float StampPillarSdf(float x,float y,float z,
const std::array<float,3>& halfSize)
{
const float radial=(std::sqrt((x/halfSize[0])*(x/halfSize[0])
+(y/halfSize[1])*(y/halfSize[1]))-1.0f)
*std::min(halfSize[0],halfSize[1]);
const float vertical=std::abs(z)-halfSize[2];
const float outside=std::sqrt(std::max(radial,0.0f)*std::max(radial,0.0f)
+std::max(vertical,0.0f)*std::max(vertical,0.0f));
return outside+std::min(std::max(radial,vertical),0.0f);
}
static float StampArchSdf(float x,float y,float z,
const std::array<float,3>& halfSize)
{
const float round=std::min(0.25f,std::min(halfSize[0],halfSize[1])*0.08f);
const float lower=StampRoundedBoxSdf(x,y,z+halfSize[2]*0.5f,
{halfSize[0],halfSize[1],halfSize[2]*0.5f},round);
const float top=std::max(StampEllipsoidSdf(x,y,z,halfSize),-z);
return SmoothMinDistance(lower,top,round*2.0f);
}
float StampSdf(const FStampRuntime& stamp,float x,float y,float z) const
{
float localX=x-stamp.positionVoxels[0];
float localY=y-stamp.positionVoxels[1];
const float localZ=z-stamp.positionVoxels[2];
switch(stamp.quarterTurns&3u)
{
case 1:{const float oldX=localX;localX=localY;localY=-oldX;break;}
case 2:localX=-localX;localY=-localY;break;
case 3:{const float oldX=localX;localX=-localY;localY=oldX;break;}
default:break;
}
float distance=0.0f;
switch(stamp.record->kind)
{
case VoxelForgeCore::ResolvedSdfStampKindV1::carveEllipsoid:
distance=StampEllipsoidSdf(localX,localY,localZ,stamp.halfExtentsVoxels);break;
case VoxelForgeCore::ResolvedSdfStampKindV1::carveArch:
distance=StampArchSdf(localX,localY,localZ,stamp.halfExtentsVoxels);break;
case VoxelForgeCore::ResolvedSdfStampKindV1::fillPillar:
distance=StampPillarSdf(localX,localY,localZ,stamp.halfExtentsVoxels);break;
case VoxelForgeCore::ResolvedSdfStampKindV1::fillDais:
distance=StampRoundedBoxSdf(localX,localY,localZ,stamp.halfExtentsVoxels,
std::min(0.35f,std::min({stamp.halfExtentsVoxels[0],
stamp.halfExtentsVoxels[1],stamp.halfExtentsVoxels[2]})*0.15f));break;
default:return std::numeric_limits<float>::infinity();
}
if(stamp.irregularityAmplitudeVoxels>0.0f)
distance+=StampValueNoise(localX,localY,localZ,
stamp.irregularityWavelengthVoxels,stamp.irregularitySeed)
*stamp.irregularityAmplitudeVoxels;
return distance;
}
float ApplyResolvedSdfStamps(float density,float X,float Y,float Z) const
{
if(StampRuntime.empty()||!Owner||Owner->CoreVoxelSizeMicrons==0)return density;
const auto* tileQueries=ActivePassageTileQueries;
const bool scoped=tileQueries&&tileQueries->matches(*this);
if(scoped&&!tileQueries->hasStampCandidates())return density;
const auto* bound=FindBoundStratumForManagerZ(Z);
if(!bound)return density;
const double micronsPerVoxel=Owner->CoreVoxelSizeMicrons;
const std::array<double,3> worldMicrometres={X*micronsPerVoxel,
Y*micronsPerVoxel,Z*micronsPerVoxel};
float composed=density;
const auto apply=[&](std::uint32_t index)
{
if(index>=StampRuntime.size())return;
const auto& runtime=StampRuntime[index];const auto& record=*runtime.record;
if(record.strateIndex!=bound->StrateIndex)return;
for(std::size_t axis=0;axis<3;++axis)
if(worldMicrometres[axis]<record.boundsMinimumMicrometres[axis]
||worldMicrometres[axis]>record.boundsMaximumMicrometres[axis])return;
const float distance=StampSdf(runtime,X,Y,Z);
if(record.kind==VoxelForgeCore::ResolvedSdfStampKindV1::carveEllipsoid
||record.kind==VoxelForgeCore::ResolvedSdfStampKindV1::carveArch)
composed=SmoothMaxDistance(composed,-distance,runtime.smoothWidthVoxels);
else
composed=SmoothMinDistance(composed,distance,runtime.smoothWidthVoxels);
};
if(scoped)
for(const auto index:tileQueries->stampCandidates())apply(index);
else
for(std::size_t index=0;index<StampRuntime.size();++index)
apply(static_cast<std::uint32_t>(index));
return composed;
}
static float SmoothUnit(float V)
{
V=std::clamp(V,0.0f,1.0f);
@@ -8614,6 +8964,10 @@ private:
FBuildStats Stats;
bool bAnalyticOnly=false;
inline static thread_local const FScopedPassageTileQueries* ActivePassageTileQueries=nullptr;
std::vector<FStampSpatialBound> StampSpatialBounds;
std::vector<FStampSpatialNode> StampSpatialNodes;
std::vector<std::uint32_t> StampSpatialOrder;
std::vector<FStampRuntime> StampRuntime;
std::vector<VoxelForgeCore::WorldAssemblyFeatureBoundsV1> PassageFeatureBounds;
std::vector<FPassageTileFeature> PassageTileFeatures;
std::vector<std::uint32_t> PassageFeatureOrder;