feat(core): reachable SW landmark chamber demo with a reachability audit (L45); research R12
Stamp vocabulary v2; the flagged SW demo opens through a side-facing portal and a bent ramp (max 14 deg, two landings) into a 17 x 15 m chamber with a rock dais. New reachability audit: a capsule surface search from walkable ground near the arrival mouth to the chamber floor (66 samples, 0 blocked/unsupported); the buried L43 layout fails it. Demo off: owner hashes identical to L44. fast/medium/full PASS (Clang+GCC). Research R12 (read-only): composite cavern-graph generator design and slices. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
036bd530fc
commit
f13b145f23
14 files changed
+1404
-45
No files matched your search
@@ -706,6 +706,7 @@ namespace VoxelForgeCore
|
||||
// source fixed-point units so this record never rounds the recipe itself.
|
||||
inline constexpr std::uint32_t ResolvedLayoutSchemaVersionV3 = 3;
|
||||
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV1 = 1;
|
||||
inline constexpr std::uint32_t ResolvedLayoutStampVocabularyVersionV2 = 2;
|
||||
enum class ResolvedLayoutValidationModeV3 : std::uint8_t { compatibility, strict };
|
||||
|
||||
// Stable, bounded geometry edits composed into every Core field after the shared
|
||||
@@ -962,6 +963,10 @@ namespace VoxelForgeCore
|
||||
ResolvedLayoutSnapshotV3& layout, std::int32_t seed,
|
||||
std::span<const ResolvedLayoutStampRequestV1> requests,
|
||||
std::string& error);
|
||||
VFCORE_API bool ResolveResolvedLayoutStampsV2(
|
||||
ResolvedLayoutSnapshotV3& layout, std::int32_t seed,
|
||||
std::span<const ResolvedLayoutStampRequestV1> requests,
|
||||
std::string& error);
|
||||
VFCORE_API ResolvedLayoutStampPlacementAuditV1 AuditResolvedLayoutStampPlacementV1(
|
||||
const ResolvedLayoutSnapshotV3& layout);
|
||||
|
||||
@@ -1243,6 +1248,7 @@ namespace VoxelForgeCore
|
||||
const WorldConfiguration& configuration() const;
|
||||
const WorldAssemblyManifest* worldAssembly() const noexcept;
|
||||
const ResolvedLayoutSnapshotV3* resolvedLayout() const noexcept;
|
||||
ResolvedWorldSnapshot withResolvedLayout(const ResolvedLayoutSnapshotV3& layout) const;
|
||||
ResolvedWorldSnapshot withoutResolvedLayoutPassages() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -830,8 +830,9 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
if(stampVocabularyVersion!=0)
|
||||
add("stamp_vocabulary_version","stamp vocabulary version is set without any stamp records");
|
||||
}
|
||||
else if(stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV1)
|
||||
add("stamp_vocabulary_version","resolved stamps require vocabulary version 1");
|
||||
else if(stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV1
|
||||
&&stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV2)
|
||||
add("stamp_vocabulary_version","resolved stamps require supported vocabulary version 1 or 2");
|
||||
std::vector<std::string> stampIds;
|
||||
for(const auto& stamp:stamps)
|
||||
{
|
||||
@@ -1065,6 +1066,20 @@ bool ResolveResolvedLayoutStampsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
layout=std::move(candidate);return true;
|
||||
}
|
||||
|
||||
bool ResolveResolvedLayoutStampsV2(ResolvedLayoutSnapshotV3& layout,std::int32_t seed,
|
||||
std::span<const ResolvedLayoutStampRequestV1> 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<ResolvedLayoutIssueV2> 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;
|
||||
}
|
||||
|
||||
ResolvedLayoutStampPlacementAuditV1 AuditResolvedLayoutStampPlacementV1(
|
||||
const ResolvedLayoutSnapshotV3& layout)
|
||||
{
|
||||
|
||||
@@ -3339,6 +3339,10 @@ private:
|
||||
return result;
|
||||
}
|
||||
QueryStampSpatialNode(0,minimum,maximum,result);
|
||||
// Stamp union/carve composition follows canonical stable-ID order. The BVH query
|
||||
// order is spatial and can differ per tile, which would let a later carve erase a
|
||||
// fill on meshed tiles even though unscoped density queries use the canonical order.
|
||||
std::sort(result.begin(),result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -5673,9 +5677,14 @@ private:
|
||||
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;
|
||||
if(ResolvedLayout&&ResolvedLayout->stampVocabularyVersion
|
||||
>=VoxelForgeCore::ResolvedLayoutStampVocabularyVersionV2)
|
||||
distance=StampEllipsoidSdf(localX,localY,localZ,stamp.halfExtentsVoxels);
|
||||
else
|
||||
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)
|
||||
|
||||
@@ -989,6 +989,15 @@ namespace VoxelForgeCore
|
||||
const ResolvedLayoutSnapshotV3* ResolvedWorldSnapshot::resolvedLayout() const noexcept
|
||||
{ return data_?data_->resolvedLayout.get():nullptr; }
|
||||
|
||||
ResolvedWorldSnapshot ResolvedWorldSnapshot::withResolvedLayout(
|
||||
const ResolvedLayoutSnapshotV3& layout) const
|
||||
{
|
||||
if(!data_)return {};
|
||||
auto copy=std::make_shared<Data>(*data_);
|
||||
copy->resolvedLayout=std::make_shared<ResolvedLayoutSnapshotV3>(layout);
|
||||
return ResolvedWorldSnapshot(std::move(copy));
|
||||
}
|
||||
|
||||
ResolvedWorldSnapshot ResolvedWorldSnapshot::withoutResolvedLayoutPassages() const
|
||||
{
|
||||
if(!data_||!data_->resolvedLayout)return {};
|
||||
@@ -2222,6 +2231,40 @@ namespace VoxelForgeCore
|
||||
RelevantMinimum=std::min(RelevantMinimum,FeatureMinimum);
|
||||
RelevantMaximum=std::max(RelevantMaximum,FeatureMaximum);
|
||||
}
|
||||
// The native heightfield band only covers the owner terrain and its
|
||||
// manifest features. Resolved SDF stamps can carve below that band or
|
||||
// add a floor there, so include every overlapping stamp's bounded Z
|
||||
// support before clipping marching-cubes samples.
|
||||
if(impl_->resolvedLayout&&impl_->owner->CoreVoxelSizeMicrons>0)
|
||||
{
|
||||
const double MicronsToVoxels=1.0
|
||||
/static_cast<double>(impl_->owner->CoreVoxelSizeMicrons);
|
||||
const double TileMinimumX=static_cast<double>(origin.X-step);
|
||||
const double TileMinimumY=static_cast<double>(origin.Y-step);
|
||||
const double TileMaximumX=static_cast<double>(origin.X+extent+step);
|
||||
const double TileMaximumY=static_cast<double>(origin.Y+extent+step);
|
||||
for(const auto& Stamp:impl_->resolvedLayout->stamps)
|
||||
{
|
||||
if(Stamp.strateIndex!=PlanIt->strateIndex)continue;
|
||||
const double MinimumX=Stamp.boundsMinimumMicrometres[0]
|
||||
*MicronsToVoxels;
|
||||
const double MaximumX=Stamp.boundsMaximumMicrometres[0]
|
||||
*MicronsToVoxels;
|
||||
const double MinimumY=Stamp.boundsMinimumMicrometres[1]
|
||||
*MicronsToVoxels;
|
||||
const double MaximumY=Stamp.boundsMaximumMicrometres[1]
|
||||
*MicronsToVoxels;
|
||||
if(MaximumX<TileMinimumX||MinimumX>TileMaximumX
|
||||
||MaximumY<TileMinimumY||MinimumY>TileMaximumY)continue;
|
||||
const double MinimumZ=Stamp.boundsMinimumMicrometres[2]
|
||||
*MicronsToVoxels;
|
||||
const double MaximumZ=Stamp.boundsMaximumMicrometres[2]
|
||||
*MicronsToVoxels;
|
||||
if(MaximumZ<TileMinimumZ||MinimumZ>TileMaximumZ)continue;
|
||||
RelevantMinimum=std::min(RelevantMinimum,MinimumZ);
|
||||
RelevantMaximum=std::max(RelevantMaximum,MaximumZ);
|
||||
}
|
||||
}
|
||||
if(std::isfinite(RelevantMinimum)&&std::isfinite(RelevantMaximum))
|
||||
{
|
||||
const int32 NativeBandMinimum=FMath::FloorToInt(RelevantMinimum)-step;
|
||||
|
||||
Reference in new issue
Block a user