Files
VoxelForge/Standalone/Source/OwnerWorldLoader.h
T
Fr0zkaandClaude Opus 5.5 bd68aee1ca parity: Unreal and headless resolve the same four-strate plan (L70, v214)
- Export commandlet writes the live Core adapter's world_assembly + resolved layout pair;
  the headless owner path imports that pair (manifest/layout equal, layout_chunk_width_q
  validated). owner_world.json re-exported from the owner's staged assets on the Linux host.
- Adapter SW route: Core clearance/radius floors, 0.5 m support search, outward-facing
  tie-break; passage mouth/mid radius floors match Core (36/24 owner voxels).
- FI in Unreal now uses the legacy (compiled analytic) generator unless a plan is imported:
  the auto native-FI plan failed the passage audit on the owner plan (1843 blocked samples,
  945 open-air violations). Owner decides whether native FI returns.
- In-engine spawn traces per strate (3 each): SW GenerateTile 42.7 worker-s vs 20.1 headless
  for equal tile counts; LOD0 p95 45-169 ms in UE.
- Fast tier green; all three passages pass on the shared plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-27 06:22:48 +01:00

477 lines
25 KiB
C++

#pragma once
#include "CoreMinimal.h"
#include "Json.h"
#include "VoxelBiomeDefinition.h"
#include "VoxelGenerator.h"
#include "VoxelSettings.h"
#include "VoxelStrateDefinition.h"
#include "VoxelStrateManager.h"
#include "VoxelTerrainOpDefinition.h"
#include <cmath>
#include <array>
#include <cstdint>
#include <memory>
#include <mutex>
#include <unordered_map>
struct FStandaloneParitySample
{
FVector PositionVoxels;
float PluginDensity = 0.0f;
std::string Kind;
int32 StrateIndex = INDEX_NONE;
};
struct FStandalonePassageVoidCrossing
{
std::size_t passageIndex=0;
std::size_t firstSegment=0;
std::size_t lastSegment=0;
std::uint64_t unsupportedSamples=0;
double unsupportedLengthMetres=0.0;
std::array<double,4> startPoint{};
std::array<double,4> endPoint{};
bool needsColumn=false;
bool connectedAtStart=false;
bool connectedAtEnd=false;
std::int64_t centerXSubvoxels=0;
std::int64_t centerYSubvoxels=0;
std::int64_t topZSubvoxels=0;
std::int64_t bottomZSubvoxels=0;
std::int64_t minimumRadiusSubvoxels=0;
std::int64_t nominalRadiusSubvoxels=0;
std::int64_t maximumRadiusSubvoxels=0;
double naturalSurfaceReliefMetres=0.0;
double maximumRouteOffsetMetres=0.0;
double maximumBoreRadiusMetres=0.0;
bool needsRootTaper=false;
};
struct FStandalonePassageVoidCrossingCache
{
std::mutex mutex;
std::unordered_map<std::string,std::vector<FStandalonePassageVoidCrossing>> byLayout;
};
struct FStandaloneOwnerWorld
{
std::unique_ptr<UVoxelSettings> Settings;
std::unique_ptr<UVoxelStrateManager> Manager;
std::unique_ptr<UVoxelGenerator> Generator;
std::vector<std::unique_ptr<UVoxelStrateDefinition>> DefinitionStorage;
std::vector<std::unique_ptr<UVoxelBiomeDefinition>> BiomeStorage;
std::vector<std::unique_ptr<UVoxelTerrainOpDefinition>> OperationStorage;
std::vector<FStandaloneParitySample> DensitySamples;
FVector ViewerStartVoxels = FVector::ZeroVector;
bool ViewerStartFound = false;
int32 Seed = 0;
int32 ClipRadius = 6;
int32 MaxClipLevel = 8;
int32 FullResClipLevels = 2;
int32 CoarseTileCells = 16;
int32 RenderDistanceChunks = 1536;
int32 StrateContentCutMinLevel = 9;
bool bGenerateSkirts = true;
int32 SkirtCells = 2;
int32 LODOctaveDrop = 0;
std::uint32_t OwnerVoxelSizeMicrons = 125000;
std::uint32_t CoreVoxelSizeMicrons = 250000;
std::uint32_t StrateLayoutChunkSizeMicrometres = 8000000;
std::int64_t StrateLayoutChunkWidthSubvoxels = 8192;
bool bHasUnrealResolvedCorePlan = false;
std::shared_ptr<FStandalonePassageVoidCrossingCache> PassageVoidCrossingCache =
std::make_shared<FStandalonePassageVoidCrossingCache>();
};
namespace StandaloneOwnerLoader
{
template<class T>
inline void ReadField(const VFJson::Value& Object, const char* Name, T& Out)
{
const VFJson::Value& Value = Object.Get(Name);
if constexpr (std::is_same_v<T, bool>)
{
if (std::holds_alternative<bool>(Value.Data)) Out = Value.Boolean(Out);
else if (Value.IsNumber()) Out = Value.Number() != 0.0;
}
else if constexpr (std::is_enum_v<T>)
{
if (Value.IsNumber()) Out = static_cast<T>(Value.Integer());
}
else if constexpr (std::is_arithmetic_v<T>)
{
if (Value.IsNumber()) Out = static_cast<T>(Value.Number());
}
}
inline void ReadText(const VFJson::Value& Object, const char* Name, FText& Out)
{
const VFJson::Value& Value = Object.Get(Name);
if (Value.IsString()) Out = FText::FromString(FString(Value.String()));
}
inline void ReadLinearColor(const VFJson::Value& Object, FLinearColor& Out)
{
ReadField(Object, "R", Out.R); ReadField(Object, "G", Out.G);
ReadField(Object, "B", Out.B); ReadField(Object, "A", Out.A);
}
inline void ReadVector(const VFJson::Value& Object, FVector& Out)
{
ReadField(Object, "x", Out.X); ReadField(Object, "y", Out.Y); ReadField(Object, "z", Out.Z);
}
inline void ReadGenerationParams(const VFJson::Value& J, FStrateGenerationParams& P)
{
#define VF_LOAD_STRATE_FIELD(Name) ReadField(J, #Name, P.Name);
VF_STRATE_PARAM_FIELDS(VF_LOAD_STRATE_FIELD, VF_LOAD_STRATE_FIELD)
#undef VF_LOAD_STRATE_FIELD
ReadField(J, "bTunnelFloorEnabled", P.bTunnelFloorEnabled);
}
inline void ReadDisturbances(const VFJson::Value& J, FStrateDisturbanceParams& P)
{
ReadField(J,"ChasmDensity",P.ChasmDensity); ReadField(J,"ChasmSpacing",P.ChasmSpacing);
ReadField(J,"ChasmRadius",P.ChasmRadius); ReadField(J,"BridgeDensity",P.BridgeDensity);
ReadField(J,"BridgeSpacing",P.BridgeSpacing); ReadField(J,"BridgeRadius",P.BridgeRadius);
ReadField(J,"RidgeDensity",P.RidgeDensity); ReadField(J,"RidgeSpacing",P.RidgeSpacing);
ReadField(J,"RidgeHeight",P.RidgeHeight); ReadField(J,"RidgeThickness",P.RidgeThickness);
ReadField(J,"BaseDensity",P.BaseDensity); ReadField(J,"BoundarySealThickness",P.BoundarySealThickness);
}
inline void ReadPassageConfig(const VFJson::Value& J, FStratePassageConfig& P)
{
ReadField(J,"Connections",P.Connections); ReadField(J,"Style",P.Style);
ReadField(J,"MouthRadius",P.MouthRadius); ReadField(J,"MidRadius",P.MidRadius);
ReadField(J,"ReachMin",P.ReachMin); ReadField(J,"ReachMax",P.ReachMax);
ReadField(J,"DistanceMin",P.DistanceMin); ReadField(J,"DistanceMax",P.DistanceMax);
ReadField(J,"Wander",P.Wander); ReadField(J,"Segments",P.Segments);
ReadField(J,"VerticalWobble",P.VerticalWobble); ReadField(J,"SpiralRadius",P.SpiralRadius);
ReadField(J,"SpiralTurns",P.SpiralTurns); ReadField(J,"CascadeSteps",P.CascadeSteps);
ReadField(J,"CascadeLedge",P.CascadeLedge);
}
inline void ReadSurfaceParams(const VFJson::Value& J, FSurfaceGenerationParams& P)
{
#define VF_SURFACE_FIELD(Name) ReadField(J, #Name, P.Name);
VF_SURFACE_FIELD(BaseGroundRelative) VF_SURFACE_FIELD(ElevationRange)
VF_SURFACE_FIELD(ContinentFrequency) VF_SURFACE_FIELD(MountainStrength)
VF_SURFACE_FIELD(MountainFrequency) VF_SURFACE_FIELD(DetailFrequency)
VF_SURFACE_FIELD(SurfaceRoughness) VF_SURFACE_FIELD(HeightWarpStrength)
VF_SURFACE_FIELD(HeightWarpFrequency) VF_SURFACE_FIELD(ReliefFrequency)
VF_SURFACE_FIELD(ReliefStrength) VF_SURFACE_FIELD(ReliefContrast)
VF_SURFACE_FIELD(TerraceStrength) VF_SURFACE_FIELD(TerraceHeight)
VF_SURFACE_FIELD(TerraceHardness) VF_SURFACE_FIELD(LayerLineDepth)
VF_SURFACE_FIELD(LayerLineSpacing) VF_SURFACE_FIELD(CliffStrength)
VF_SURFACE_FIELD(CliffSlopeThreshold) VF_SURFACE_FIELD(CliffSharpness)
VF_SURFACE_FIELD(CliffSampleDist) VF_SURFACE_FIELD(OverhangStrength)
VF_SURFACE_FIELD(OverhangReach) VF_SURFACE_FIELD(OverhangHeight)
VF_SURFACE_FIELD(OverhangFrequency) VF_SURFACE_FIELD(OverhangZScale)
VF_SURFACE_FIELD(OverhangSlopeThreshold) VF_SURFACE_FIELD(WaterLevelRelative)
VF_SURFACE_FIELD(BeachWidth) VF_SURFACE_FIELD(CeilingRelative)
VF_SURFACE_FIELD(CeilingRoughness) VF_SURFACE_FIELD(CeilingRoughnessFrequency)
VF_SURFACE_FIELD(CeilingUndulation) VF_SURFACE_FIELD(CeilingUndulationFrequency)
VF_SURFACE_FIELD(CeilingRidgeStrength) VF_SURFACE_FIELD(CeilingRidgeFrequency)
VF_SURFACE_FIELD(CeilingWarpStrength) VF_SURFACE_FIELD(CeilingWarpFrequency)
VF_SURFACE_FIELD(BoundarySealThickness) VF_SURFACE_FIELD(BaseDensity)
#undef VF_SURFACE_FIELD
}
inline void ReadSlabParams(const VFJson::Value& J, FSlabGenerationParams& P)
{
#define VF_SLAB_FIELD(Name) ReadField(J, #Name, P.Name);
VF_SLAB_FIELD(FloorRelativeHeight) VF_SLAB_FIELD(CeilingRelativeHeight)
VF_SLAB_FIELD(FloorRoughness) VF_SLAB_FIELD(FloorRoughnessFrequency)
VF_SLAB_FIELD(CeilingRoughness) VF_SLAB_FIELD(CeilingRoughnessFrequency)
VF_SLAB_FIELD(ColumnDensity) VF_SLAB_FIELD(ColumnMinRadius)
VF_SLAB_FIELD(ColumnMaxRadius) VF_SLAB_FIELD(ColumnSpacing)
VF_SLAB_FIELD(BoundarySealThickness) VF_SLAB_FIELD(BaseDensity)
#undef VF_SLAB_FIELD
}
inline void ReadMazeParams(const VFJson::Value& J, FMazeGenerationParams& P)
{
#define VF_MAZE_FIELD(Name) ReadField(J, #Name, P.Name);
VF_MAZE_FIELD(CellSize) VF_MAZE_FIELD(CorridorRadius)
VF_MAZE_FIELD(BranchProbability) VF_MAZE_FIELD(Verticality)
VF_MAZE_FIELD(SurfaceRoughness) VF_MAZE_FIELD(BoundarySealThickness)
VF_MAZE_FIELD(BaseDensity)
#undef VF_MAZE_FIELD
}
inline void ReadVerticalShaftParams(const VFJson::Value& J, FVerticalShaftParams& P)
{
#define VF_SHAFT_FIELD(Name) ReadField(J, #Name, P.Name);
VF_SHAFT_FIELD(ShaftSpacing) VF_SHAFT_FIELD(ShaftDensity)
VF_SHAFT_FIELD(ShaftMinRadius) VF_SHAFT_FIELD(ShaftMaxRadius)
VF_SHAFT_FIELD(CrossConnectChance) VF_SHAFT_FIELD(ConnectorRadius)
VF_SHAFT_FIELD(LedgeSpacing) VF_SHAFT_FIELD(LedgeDepth)
VF_SHAFT_FIELD(SurfaceRoughness) VF_SHAFT_FIELD(BoundarySealThickness)
VF_SHAFT_FIELD(BaseDensity)
#undef VF_SHAFT_FIELD
}
inline void ReadFloatingIslandParams(const VFJson::Value& J, FFloatingIslandParams& P)
{
#define VF_ISLAND_FIELD(Name) ReadField(J, #Name, P.Name);
VF_ISLAND_FIELD(IslandSpacing) VF_ISLAND_FIELD(IslandDensity)
VF_ISLAND_FIELD(IslandMinRadius) VF_ISLAND_FIELD(IslandMaxRadius)
VF_ISLAND_FIELD(ThicknessRatio) VF_ISLAND_FIELD(VerticalJitter)
VF_ISLAND_FIELD(TopFlatten) VF_ISLAND_FIELD(SurfaceRoughness)
VF_ISLAND_FIELD(SDFBlendRadius) VF_ISLAND_FIELD(BoundarySealThickness)
VF_ISLAND_FIELD(BaseDensity)
#undef VF_ISLAND_FIELD
}
inline void ReadBiomeMap(const VFJson::Value& J, FBiomeMapParams& P)
{
ReadField(J,"CellSize",P.CellSize); ReadField(J,"WarpStrength",P.WarpStrength);
ReadField(J,"WarpFrequency",P.WarpFrequency); ReadField(J,"BorderBlend",P.BorderBlend);
ReadField(J,"ReliefFrequency",P.ReliefFrequency); ReadField(J,"ReliefContrast",P.ReliefContrast);
ReadField(J,"MoistureFrequency",P.MoistureFrequency);
}
inline void ReadOperation(const VFJson::Value& J, UVoxelTerrainOpDefinition& O)
{
ReadField(J,"Type",O.Type);
#define VF_OPERATION_FIELD(Name) ReadField(J, #Name, O.Name);
VF_OPERATION_FIELD(TerraceStepHeight) VF_OPERATION_FIELD(TerraceHardness)
VF_OPERATION_FIELD(TerraceNoiseDisplacement) VF_OPERATION_FIELD(LayerLineSpacing)
VF_OPERATION_FIELD(LayerLineDepth) VF_OPERATION_FIELD(RibbingSpacing)
VF_OPERATION_FIELD(RibbingDepth) VF_OPERATION_FIELD(CliffStrength)
VF_OPERATION_FIELD(ScallopStrength) VF_OPERATION_FIELD(ScallopFrequency)
VF_OPERATION_FIELD(OverhangStrength) VF_OPERATION_FIELD(OverhangDepth)
VF_OPERATION_FIELD(OverhangFrequency) VF_OPERATION_FIELD(ArchDensity)
VF_OPERATION_FIELD(ArchMinRadius) VF_OPERATION_FIELD(ArchMaxRadius)
VF_OPERATION_FIELD(ColumnDensity) VF_OPERATION_FIELD(ColumnMinRadius)
VF_OPERATION_FIELD(ColumnMaxRadius) VF_OPERATION_FIELD(PitDensity)
VF_OPERATION_FIELD(PitMinRadius) VF_OPERATION_FIELD(PitMaxRadius)
VF_OPERATION_FIELD(PitDepth) VF_OPERATION_FIELD(ChimneyDensity)
VF_OPERATION_FIELD(ChimneyMinRadius) VF_OPERATION_FIELD(ChimneyMaxRadius)
VF_OPERATION_FIELD(ChimneyHeight) VF_OPERATION_FIELD(DomeDensity)
VF_OPERATION_FIELD(DomeMinRadius) VF_OPERATION_FIELD(DomeMaxRadius)
VF_OPERATION_FIELD(DomeHeightRatio) VF_OPERATION_FIELD(PinchDensity)
VF_OPERATION_FIELD(PinchStrength) VF_OPERATION_FIELD(PinchLength)
#undef VF_OPERATION_FIELD
}
inline void ReadBiome(const VFJson::Value& J, UVoxelBiomeDefinition& B)
{
ReadText(J,"BiomeName",B.BiomeName); ReadText(J,"BiomeDescription",B.BiomeDescription);
ReadLinearColor(J.Get("DebugColor"),B.DebugColor);
ReadField(J,"ReliefMin",B.ReliefMin); ReadField(J,"ReliefMax",B.ReliefMax);
ReadField(J,"MoistureMin",B.MoistureMin); ReadField(J,"MoistureMax",B.MoistureMax);
ReadField(J,"bOverrideTerrain",B.bOverrideTerrain); ReadField(J,"GeneratorType",B.GeneratorType);
ReadSurfaceParams(J.Get("SurfaceParams"),B.SurfaceParams);
ReadField(J,"bOverrideAtmosphere",B.bOverrideAtmosphere);
ReadLinearColor(J.Get("FogColor"),B.FogColor); ReadField(J,"FogDensity",B.FogDensity);
ReadLinearColor(J.Get("AmbientLightColor"),B.AmbientLightColor);
ReadField(J,"AmbientLightIntensity",B.AmbientLightIntensity);
ReadField(J,"MaterialPaletteIndex",B.MaterialPaletteIndex);
}
inline UVoxelStrateDefinition* ReadDefinition(
FStandaloneOwnerWorld& World, const VFJson::Value& Slot, int32 SlotIndex)
{
const VFJson::Value& J = Slot.Get("asset");
auto Definition = std::make_unique<UVoxelStrateDefinition>();
ReadText(J,"StrateName",Definition->StrateName);
ReadText(J,"StrateDescription",Definition->StrateDescription);
ReadField(J,"StrateHeightInChunks",Definition->StrateHeightInChunks);
ReadField(J,"TransitionType",Definition->TransitionType);
ReadField(J,"TransitionBlendChunks",Definition->TransitionBlendChunks);
ReadField(J,"GeneratorType",Definition->GeneratorType);
ReadField(J,"bEnableWorms",Definition->bEnableWorms);
ReadGenerationParams(Slot.Get("generation_params"),Definition->GenerationParams);
ReadSlabParams(J.Get("SlabParams"),Definition->SlabParams);
ReadMazeParams(J.Get("MazeParams"),Definition->MazeParams);
ReadVerticalShaftParams(J.Get("VerticalShaftParams"),Definition->VerticalShaftParams);
ReadFloatingIslandParams(J.Get("FloatingIslandParams"),Definition->FloatingIslandParams);
ReadDisturbances(Slot.Get("disturbances"),Definition->Disturbances);
ReadPassageConfig(Slot.Get("passage_config"),Definition->PassageConfig);
ReadSurfaceParams(J.Get("SurfaceParams"),Definition->SurfaceParams);
ReadBiomeMap(J.Get("BiomeMapParams"),Definition->BiomeMapParams);
ReadField(J,"bHasWater",Definition->bHasWater);
const auto& Biomes = J.Get("Biomes").AsArray();
Definition->Biomes.Reserve(static_cast<int32>(Biomes.size()));
for (const VFJson::Value& BiomeJson : Biomes)
{
auto Biome = std::make_unique<UVoxelBiomeDefinition>();
ReadBiome(BiomeJson,*Biome);
Definition->Biomes.Add(Biome.get());
World.BiomeStorage.push_back(std::move(Biome));
}
const auto& OperationEntries = Slot.Get("terrain_operation_assets").AsArray();
Definition->TerrainOperations.Reserve(static_cast<int32>(OperationEntries.size()));
for (int32 I = 0; I < static_cast<int32>(OperationEntries.size()); ++I)
{
const VFJson::Value& EntryJson = OperationEntries[static_cast<size_t>(I)];
const VFJson::Value& AssetJson = EntryJson.Get("asset");
const VFJson::Value& PathJson = EntryJson.Get("asset_path");
const FString Path = PathJson.IsString() ? FString(PathJson.String()) : FString{};
FStrateTerrainOpEntry Entry;
ReadField(EntryJson,"weight",Entry.Weight);
ReadField(EntryJson,"probability",Entry.Probability);
if (AssetJson.IsObject())
{
auto Operation = std::make_unique<UVoxelTerrainOpDefinition>();
ReadOperation(AssetJson,*Operation);
UVoxelTerrainOpDefinition* Raw = Operation.get();
World.OperationStorage.push_back(std::move(Operation));
Entry.Operation.Set(Raw,Path);
RegisterStandaloneAsset(Path,Raw);
}
else if (!Path.IsEmpty()) Entry.Operation = TSoftObjectPtr<UVoxelTerrainOpDefinition>(Path);
Definition->TerrainOperations.Add(std::move(Entry));
}
const FString SyntheticPath = FString::Printf("/Standalone/Strate_%d",SlotIndex);
Definition->PostLoad();
UVoxelStrateDefinition* Raw = Definition.get();
World.DefinitionStorage.push_back(std::move(Definition));
RegisterStandaloneAsset(SyntheticPath,Raw);
return Raw;
}
inline bool Resolve(const VFJson::Value& Root, FStandaloneOwnerWorld& Out, std::string& Error)
{
const VFJson::Value& CoreVoxelSize = Root.Get("core_voxel_size_cm");
const VFJson::Value& CoreVoxelSizeMicrons = Root.Get("core_voxel_size_microns");
const VFJson::Value& OwnerVoxelSize = Root.Get("owner_voxel_size_cm");
const VFJson::Value& OwnerVoxelSizeMicrons = Root.Get("owner_voxel_size_microns");
const VFJson::Value& OwnerToCoreScale = Root.Get("owner_to_core_length_scale");
const double CoreCm = CoreVoxelSize.Number(Root.Get("voxel_size_cm").Number(VOXEL_SIZE));
const double OwnerCm = OwnerVoxelSize.Number(VOXEL_SIZE * 0.5);
const double CoreMicrons = CoreVoxelSizeMicrons.Number(CoreCm * 10000.0);
const double OwnerMicrons = OwnerVoxelSizeMicrons.Number(OwnerCm * 10000.0);
if (!std::isfinite(CoreCm) || !std::isfinite(OwnerCm)
|| CoreCm < VOXEL_SIZE * 0.5 || CoreCm > VOXEL_SIZE || OwnerCm <= 0.0
|| std::abs(CoreMicrons - CoreCm * 10000.0) > 0.01
|| std::abs(OwnerMicrons - OwnerCm * 10000.0) > 0.01
|| !Root.Get("voxel_size_cm").IsNumber()
|| std::abs(Root.Get("voxel_size_cm").Number() - CoreCm) > 0.0001)
{
Error = "exported Core voxel size is invalid or inconsistent: core_cm="
+ std::to_string(CoreCm) + " owner_cm=" + std::to_string(OwnerCm)
+ " root_voxel_cm=" + std::to_string(Root.Get("voxel_size_cm").Number())
+ " core_microns=" + std::to_string(CoreMicrons)
+ " owner_microns=" + std::to_string(OwnerMicrons);
return false;
}
const double ExpectedRatio = OwnerCm / CoreCm;
const bool bHasImportScale = OwnerVoxelSize.IsNumber()
|| CoreVoxelSize.IsNumber() || OwnerToCoreScale.IsNumber();
if (bHasImportScale && (!OwnerVoxelSize.IsNumber() || !CoreVoxelSize.IsNumber()
|| !OwnerToCoreScale.IsNumber()
|| std::abs(OwnerToCoreScale.Number() - ExpectedRatio) > 1.0e-9))
{
Error = "owner-to-Core import scale does not match the resolved voxel sizes";
return false;
}
Out.OwnerVoxelSizeMicrons = static_cast<std::uint32_t>(std::llround(OwnerMicrons));
Out.CoreVoxelSizeMicrons = static_cast<std::uint32_t>(std::llround(CoreMicrons));
Out.Seed = static_cast<int32>(Root.Get("seed").Integer());
Out.ClipRadius = static_cast<int32>(Root.Get("clip_radius").Integer(6));
Out.MaxClipLevel = static_cast<int32>(Root.Get("max_clip_level").Integer(8));
Out.FullResClipLevels = static_cast<int32>(Root.Get("full_res_clip_levels").Integer(2));
Out.CoarseTileCells = static_cast<int32>(Root.Get("coarse_tile_cells").Integer(16));
Out.RenderDistanceChunks = static_cast<int32>(Root.Get("render_distance_chunks").Integer(1536));
Out.StrateContentCutMinLevel = static_cast<int32>(Root.Get("strate_content_cut_min_level").Integer(9));
Out.bGenerateSkirts = Root.Get("generate_skirts").Boolean(true);
Out.SkirtCells = static_cast<int32>(Root.Get("skirt_cells").Integer(2));
Out.LODOctaveDrop = static_cast<int32>(Root.Get("lod_octave_drop").Integer(0));
ReadVector(Root.Get("viewer_start"),Out.ViewerStartVoxels);
Out.ViewerStartFound = Root.Get("viewer_start").Get("found_open_space").Boolean();
for (const VFJson::Value& SampleJson : Root.Get("density_samples").AsArray())
{
FStandaloneParitySample Sample;
ReadField(SampleJson,"x",Sample.PositionVoxels.X);
ReadField(SampleJson,"y",Sample.PositionVoxels.Y);
ReadField(SampleJson,"z",Sample.PositionVoxels.Z);
ReadField(SampleJson,"density",Sample.PluginDensity);
ReadField(SampleJson,"strate_index",Sample.StrateIndex);
Sample.Kind = SampleJson.Get("kind").String();
Out.DensitySamples.push_back(std::move(Sample));
}
Out.Settings = std::make_unique<UVoxelSettings>();
const VFJson::Value& SettingsJson = Root.Get("settings_asset");
Out.Settings->Seed = Out.Seed;
ReadField(Root,"origin_spine_radius",Out.Settings->OriginSpineRadius);
ReadField(Root,"world_radius_voxels",Out.Settings->WorldRadiusVoxels);
ReadField(Root,"edge_seal_thickness",Out.Settings->EdgeSealThickness);
ReadField(Root,"open_surface_entry",Out.Settings->bOpenSurfaceEntry);
ReadField(Root,"inter_strate_gap_chunks",Out.Settings->InterStrateGapChunks);
ReadField(SettingsJson,"total_strates",Out.Settings->TotalStrates);
ReadField(Root,"clip_radius",Out.Settings->ClipRadius);
ReadField(Root,"max_clip_level",Out.Settings->MaxClipLevel);
ReadField(Root,"full_res_clip_levels",Out.Settings->FullResClipLevels);
ReadField(Root,"coarse_tile_cells",Out.Settings->CoarseTileCells);
ReadField(Root,"render_distance_chunks",Out.Settings->RenderDistanceChunks);
ReadField(Root,"generate_skirts",Out.Settings->bGenerateSkirts);
ReadField(Root,"skirt_cells",Out.Settings->SkirtCells);
ReadField(Root,"lod_octave_drop",Out.Settings->LODOctaveDrop);
ReadField(Root,"strate_content_cut_min_level",Out.Settings->StrateContentCutMinLevel);
ReadField(SettingsJson,"TotalStrates",Out.Settings->TotalStrates);
ReadField(SettingsJson,"InterStrateGapChunks",Out.Settings->InterStrateGapChunks);
ReadField(SettingsJson,"StrateLayoutChunkSizeMeters",Out.Settings->StrateLayoutChunkSizeMeters);
const VFJson::Value& LayoutChunkMicrometres=Root.Get("strate_layout_chunk_size_um");
const double LayoutChunkSizeMicrometres=LayoutChunkMicrometres.IsNumber()
?LayoutChunkMicrometres.Number()
:static_cast<double>(Out.Settings->StrateLayoutChunkSizeMeters)*1000000.0;
if(!std::isfinite(LayoutChunkSizeMicrometres)||LayoutChunkSizeMicrometres<250000.0
||LayoutChunkSizeMicrometres>64000000.0
||std::abs(LayoutChunkSizeMicrometres-std::round(LayoutChunkSizeMicrometres))>0.001)
{Error="strate layout chunk size must be an integral micrometre value between 0.25 m and 64 m";return false;}
Out.StrateLayoutChunkSizeMicrometres=static_cast<std::uint32_t>(std::llround(LayoutChunkSizeMicrometres));
Out.Settings->StrateLayoutChunkSizeMeters=static_cast<float>(LayoutChunkSizeMicrometres/1000000.0);
Out.StrateLayoutChunkWidthSubvoxels=static_cast<std::int64_t>(std::llround(
LayoutChunkSizeMicrometres*256.0/CoreMicrons));
if(Out.StrateLayoutChunkWidthSubvoxels<=0)
{Error="strate layout chunk size resolves to no Core subvoxels";return false;}
Out.Settings->StratePool.Reset();
Out.Settings->FixedStrates.Empty();
const auto& Strates = Root.Get("strates").AsArray();
Out.Settings->TotalStrates = static_cast<int32>(Strates.size());
for (int32 I = 0; I < static_cast<int32>(Strates.size()); ++I)
{
UVoxelStrateDefinition* Definition = ReadDefinition(Out,Strates[static_cast<size_t>(I)],I);
const FString SyntheticPath = FString::Printf("/Standalone/Strate_%d",I);
TSoftObjectPtr<UVoxelStrateDefinition> SoftDefinition;
SoftDefinition.Set(Definition,SyntheticPath);
Out.Settings->FixedStrates.Add(I,std::move(SoftDefinition));
}
if (Strates.empty()) { Error = "owner JSON contains no resolved strates"; return false; }
Out.Manager = std::make_unique<UVoxelStrateManager>();
if (!Out.Manager->Initialize(Out.Settings.get(),Out.Seed))
{
Error = "copied strate manager rejected the owner settings snapshot";
return false;
}
Out.Manager->SetHardTransitionFallbackForAssembly(true);
Out.Generator = std::make_unique<UVoxelGenerator>();
Out.Generator->InitializeSettings(Out.Settings.get());
Out.Generator->SetStrateManager(Out.Manager.get());
return true;
}
inline bool LoadJson(std::string_view Json,FStandaloneOwnerWorld& Out,std::string& Error)
{
VFJson::Value Root;
if(!VFJson::Parser(std::string(Json)).Parse(Root,Error))return false;
return Resolve(Root,Out,Error);
}
inline bool Load(const std::string& Path,FStandaloneOwnerWorld& Out,std::string& Error)
{
VFJson::Value Root;
if(!VFJson::Load(Path,Root,Error))return false;
return Resolve(Root,Out,Error);
}
}