feat(editor): walk a composed candidate in PIE + why rooms read angular
PART B -- the thing Jahni asked for: "any way to test those in game?" A candidate existed only as measurements and PNGs. Now he can stand in one. Select the VoxelWorld actor in PIE -> Live Edit|Composer -> ComposerSeed / ComposerCandidateIndex / ComposerTargetStrateIndex / bComposerRollStructure -> click Apply Composer Candidate. The button and the automation tests call the SAME roll wrapper, which was the load-bearing requirement: a second roll path could drift, and then he would be judging a different world than the contact sheet describes. A determinism sample is logged on apply (seed, index, recipe, archetype, metrics, epoch, eight-point density verdict) so drift is caught the day it happens rather than in a month. Hazards handled, since this is the first composer code to touch the live runtime path: preflight materialisation before any live mutation, generation and decoration tasks drained, PassagesVersion bumped to invalidate thread_local caches, reuse of the existing RegenerateAllChunks / Epoch invalidation rather than a new path, no Initialize() or GeneratePassages() during apply (the re-entrancy trap), DiffLayer locks untouched, and custom recipes conservatively force ClassifyTile to Mixed. Scope is deliberate: a DENSITY-ONLY overlay. Layout height, authored passages, content, water and biome metadata stay from the existing slot; RebuildStrates() clears it. A full composed layout replacement needs a safe rebuild path that does not exist yet. Editor-only, verified: a Shipping build succeeds with the bridge excluded (96/96 actions), so §3.1's "offline only, never on a player's machine" still holds. ⚠️ UNVERIFIED BY DESIGN: a button cannot be tested headlessly and Codex was told not to claim it works. Jahni presses it and confirms the visual plus the "determinism=PASS" log line. PART A -- why rooms read angular, measured not guessed. One of three room shapes is VoxelSDF::RoundedBox: ShapeA = (.8 Rxy, .8 Rxy, .8 Rz) ShapeR = .25 Rxy so rounding is 31% of the horizontal half-extent but 78% of the vertical. Corners are rounded; the broad planar FACES survive. SmoothMin (blend 4) softens room/tunnel junctions not isolated corners; the cave warp bends geometry but preserves flat regions; roughness is surface-scale only. The name is accurate -- "rounded box" is not "blob". NOT ACTED ON. Jahni corrected his own read: the angular archetype is Maze (lattice source, rectilinear BY DESIGN and correct), and TunnelNetwork is "alright-ish". Codex's recommendation is recorded for if he ever wants it: ~5-10% boxes, 10-20% capsules, rest ellipsoids, or a larger ShapeR. Also closed REVIEW_FINDINGS EVoxelPassageType/Style as WONTFIX, enacting a 2026-08-16 judgement that recommended closure and was left open. Build clean (Editor + Shipping), 25 succeeded / 0 failed, run by me. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU
This commit is contained in:
1 parent
90c40c8d5a
commit
d717bc74ff
13 files changed
+1182
-32
No files matched your search
@@ -2270,6 +2270,131 @@ FVoxelOpStackRecipe VF_RollStrateStructure(int32 Seed, int32 Index)
|
||||
return Recipe;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
namespace
|
||||
{
|
||||
struct FComposerStructureBlockSpec
|
||||
{
|
||||
EVoxelStrateParamBlock Block;
|
||||
ECaveGeneratorType Archetype;
|
||||
uint32 SeedSalt;
|
||||
};
|
||||
|
||||
// Keep these salts in the composer implementation so the editor action and the offline
|
||||
// structure test cannot silently acquire different block streams.
|
||||
static const FComposerStructureBlockSpec GComposerStructureBlocks[] =
|
||||
{
|
||||
{ EVoxelStrateParamBlock::TunnelNetwork, ECaveGeneratorType::TunnelNetwork, 0x1001u },
|
||||
{ EVoxelStrateParamBlock::Slab, ECaveGeneratorType::FlatPlain, 0x1003u },
|
||||
{ EVoxelStrateParamBlock::Maze, ECaveGeneratorType::Maze, 0x1005u },
|
||||
{ EVoxelStrateParamBlock::Surface, ECaveGeneratorType::SurfaceWorld, 0x1007u },
|
||||
{ EVoxelStrateParamBlock::VerticalShaft, ECaveGeneratorType::VerticalShafts, 0x1009u },
|
||||
{ EVoxelStrateParamBlock::FloatingIsland, ECaveGeneratorType::FloatingIslands, 0x100Bu },
|
||||
};
|
||||
|
||||
void VF_CopyComposerBlock(FVoxelStrateArchetypeParams& OutParams,
|
||||
const FVoxelStrateRollInfo& Roll,
|
||||
ECaveGeneratorType Archetype)
|
||||
{
|
||||
switch (Archetype)
|
||||
{
|
||||
case ECaveGeneratorType::TunnelNetwork:
|
||||
case ECaveGeneratorType::Underwater:
|
||||
OutParams.TunnelNetworkParams = Roll.ArchetypeParams.TunnelNetworkParams;
|
||||
break;
|
||||
case ECaveGeneratorType::FlatPlain:
|
||||
case ECaveGeneratorType::CrystalChamber:
|
||||
OutParams.SlabParams = Roll.ArchetypeParams.SlabParams;
|
||||
break;
|
||||
case ECaveGeneratorType::Maze:
|
||||
OutParams.MazeParams = Roll.ArchetypeParams.MazeParams;
|
||||
break;
|
||||
case ECaveGeneratorType::SurfaceWorld:
|
||||
OutParams.SurfaceParams = Roll.ArchetypeParams.SurfaceParams;
|
||||
break;
|
||||
case ECaveGeneratorType::VerticalShafts:
|
||||
OutParams.VerticalShaftParams = Roll.ArchetypeParams.VerticalShaftParams;
|
||||
break;
|
||||
case ECaveGeneratorType::FloatingIslands:
|
||||
OutParams.FloatingIslandParams = Roll.ArchetypeParams.FloatingIslandParams;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ECaveGeneratorType VF_ComposerArchetypeForRecipe(const FVoxelOpStackRecipe& Recipe)
|
||||
{
|
||||
switch (Recipe.StructuralParamBlock)
|
||||
{
|
||||
case EVoxelStrateParamBlock::TunnelNetwork: return ECaveGeneratorType::TunnelNetwork;
|
||||
case EVoxelStrateParamBlock::Slab: return ECaveGeneratorType::FlatPlain;
|
||||
case EVoxelStrateParamBlock::Maze: return ECaveGeneratorType::Maze;
|
||||
case EVoxelStrateParamBlock::Surface: return ECaveGeneratorType::SurfaceWorld;
|
||||
case EVoxelStrateParamBlock::VerticalShaft: return ECaveGeneratorType::VerticalShafts;
|
||||
case EVoxelStrateParamBlock::FloatingIsland: return ECaveGeneratorType::FloatingIslands;
|
||||
default: return ECaveGeneratorType::TunnelNetwork;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FVoxelStrateComposerCandidate VF_RollStrateCandidate(
|
||||
const FVoxelStrateCorpus& Corpus, int32 Seed, int32 Index, bool bRollStructure)
|
||||
{
|
||||
FVoxelStrateComposerCandidate Candidate;
|
||||
Candidate.Seed = Seed;
|
||||
Candidate.Index = Index;
|
||||
Candidate.bStructureRoll = bRollStructure;
|
||||
|
||||
if (!bRollStructure)
|
||||
{
|
||||
// This is the exact Tier 4a call used by the parameter-roll measurement test.
|
||||
Candidate.ParameterRoll = VF_RollStrateParamsDetailed(Corpus, Seed, Index);
|
||||
if (!Candidate.ParameterRoll.bValid)
|
||||
{
|
||||
Candidate.FailureReason = Candidate.ParameterRoll.FailureReason;
|
||||
return Candidate;
|
||||
}
|
||||
|
||||
Candidate.Archetype = Candidate.ParameterRoll.Archetype;
|
||||
Candidate.ArchetypeParams = Candidate.ParameterRoll.ArchetypeParams;
|
||||
Candidate.bValid = true;
|
||||
return Candidate;
|
||||
}
|
||||
|
||||
// This is the exact Tier 4b structure call plus the six independent native-family rolls used
|
||||
// to materialise every structure candidate in the offline test.
|
||||
Candidate.Recipe = VF_RollStrateStructure(Seed, Index);
|
||||
Candidate.Archetype = VF_ComposerArchetypeForRecipe(Candidate.Recipe);
|
||||
Candidate.StructureBlockRolls.Reserve(UE_ARRAY_COUNT(GComposerStructureBlocks));
|
||||
|
||||
bool bAllBlocksValid = true;
|
||||
for (const FComposerStructureBlockSpec& Spec : GComposerStructureBlocks)
|
||||
{
|
||||
const FVoxelStrateRollInfo Roll = VF_RollStrateParamsDetailedForArchetype(
|
||||
Corpus, Spec.Archetype, Seed ^ static_cast<int32>(Spec.SeedSalt), Index);
|
||||
Candidate.StructureBlockRolls.Add(Roll);
|
||||
if (!Roll.bValid)
|
||||
{
|
||||
bAllBlocksValid = false;
|
||||
if (Candidate.FailureReason.IsEmpty())
|
||||
{
|
||||
Candidate.FailureReason = FString::Printf(
|
||||
TEXT("parameter block %d (%s) failed: %s"),
|
||||
static_cast<int32>(Spec.Block),
|
||||
VF_GetStrateArchetypeName(Spec.Archetype),
|
||||
*Roll.FailureReason);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
VF_CopyComposerBlock(Candidate.ArchetypeParams, Roll, Spec.Archetype);
|
||||
}
|
||||
|
||||
Candidate.bValid = bAllBlocksValid;
|
||||
return Candidate;
|
||||
}
|
||||
#endif
|
||||
|
||||
FString VF_FormatStrateStructureRecipe(const FVoxelOpStackRecipe& Recipe)
|
||||
{
|
||||
FString Modifiers;
|
||||
|
||||
Reference in new issue
Block a user