wip(assembler): M3 round 3 -- separated islands + Unreal island route (owner build 18:46)

Islands read as separate (91.89% open sky at mid-height), underside cavity closed,
rim teeth were capture shading. Island manifest import + BuildHost proof passed on the
pre-cache tree; M1 SurfaceWorld and M2 V1/V2 gates passed.

Known open: final XY-column cache compiled in the owner build but is unverified;
island LOD0 p95 ~155 ms misses the 133 ms sprint deadline (legacy ~43 ms); the island
manifest only fits the BuildHost fixture layout (owner's world: Core init fails,
empty world) -> round 4 builds plans inside Unreal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-10 20:53:03 +02:00
1 parent 4afea7c802
commit 48fa028086
37 files changed
+3788 -107

No files matched your search

@@ -128,6 +128,110 @@ struct FCoreSurfaceSnapshot
}
};
struct FCoreFloatingIslandFieldSnapshot
{
VoxelForgeCore::FloatingIslandRecipeV1 Recipe;
VoxelForgeCore::FloatingIslandPlanV1 Plan;
TUniquePtr<VoxelForgeCore::FloatingIslandFieldV1> Field;
};
struct FCoreFloatingIslandSnapshot
{
TMap<int32, TSharedPtr<const FCoreFloatingIslandFieldSnapshot, ESPMode::ThreadSafe>> Fields;
const FCoreFloatingIslandFieldSnapshot* FindField(int32 StrateIndex) const
{
const auto* Found = Fields.Find(StrateIndex);
return Found != nullptr && Found->IsValid() ? Found->Get() : nullptr;
}
bool GetDensity(int32 StrateIndex, float LegacyVoxelX, float LegacyVoxelY,
float LegacyVoxelZ, float& OutDensity) const
{
OutDensity = 1.0f;
const FCoreFloatingIslandFieldSnapshot* Entry = FindField(StrateIndex);
if (Entry == nullptr || !Entry->Field.IsValid()
|| !FMath::IsFinite(LegacyVoxelX) || !FMath::IsFinite(LegacyVoxelY)
|| !FMath::IsFinite(LegacyVoxelZ))
{
return false;
}
// A legacy voxel is 128 subvoxels at 12.5 cm; Core field distances are converted back to legacy units.
// Un voxel historique vaut 128 sous-voxels a 12,5 cm ; la distance Core revient en unites historiques.
constexpr double LegacyVoxelSubvoxels = 128.0;
constexpr double MaximumInput = static_cast<double>(MAX_int64) / LegacyVoxelSubvoxels;
if (FMath::Abs(static_cast<double>(LegacyVoxelX)) > MaximumInput
|| FMath::Abs(static_cast<double>(LegacyVoxelY)) > MaximumInput
|| FMath::Abs(static_cast<double>(LegacyVoxelZ)) > MaximumInput)
{
return false;
}
const int64 Density = Entry->Field->densitySubvoxels(
static_cast<int64>(std::llround(static_cast<double>(LegacyVoxelX) * LegacyVoxelSubvoxels)),
static_cast<int64>(std::llround(static_cast<double>(LegacyVoxelY) * LegacyVoxelSubvoxels)),
static_cast<int64>(std::llround(static_cast<double>(LegacyVoxelZ) * LegacyVoxelSubvoxels)));
OutDensity = static_cast<float>(static_cast<double>(Density) / LegacyVoxelSubvoxels);
return FMath::IsFinite(OutDensity);
}
bool GetTopAnchor(int32 StrateIndex, const FVector2D& LocalXYCm,
float& OutGroundLocalZCm) const
{
OutGroundLocalZCm = 0.0f;
const FCoreFloatingIslandFieldSnapshot* Entry = FindField(StrateIndex);
if (Entry == nullptr || !Entry->Field.IsValid()
|| Entry->Plan.islands.empty()
|| !FMath::IsFinite(LocalXYCm.X) || !FMath::IsFinite(LocalXYCm.Y))
{
return false;
}
constexpr double SubvoxelsPerCoreVoxel = 256.0;
constexpr double CentimetersPerCoreVoxel = 25.0;
const double XSubvoxels = static_cast<double>(LocalXYCm.X)
* SubvoxelsPerCoreVoxel / CentimetersPerCoreVoxel;
const double YSubvoxels = static_cast<double>(LocalXYCm.Y)
* SubvoxelsPerCoreVoxel / CentimetersPerCoreVoxel;
constexpr double MaximumInput = static_cast<double>(MAX_int64) - 1024.0;
if (FMath::Abs(XSubvoxels) > MaximumInput || FMath::Abs(YSubvoxels) > MaximumInput)
{
return false;
}
const int64 X = static_cast<int64>(std::llround(XSubvoxels));
const int64 Y = static_cast<int64>(std::llround(YSubvoxels));
int64 SearchTop = Entry->Plan.islands.front().topZSubvoxels;
int64 SearchBottom = Entry->Plan.islands.front().bottomZSubvoxels;
for (const auto& Island : Entry->Plan.islands)
{
SearchTop = FMath::Max(SearchTop, Island.topZSubvoxels);
SearchBottom = FMath::Min(SearchBottom, Island.bottomZSubvoxels);
}
SearchTop += 4 * 256;
SearchBottom -= 4 * 256;
constexpr int64 ScanStepSubvoxels = 128;
int64 PreviousZ = SearchTop;
int64 PreviousDensity = Entry->Field->densitySubvoxels(X, Y, PreviousZ);
if (PreviousDensity < 0) return false;
for (int64 Z = SearchTop - ScanStepSubvoxels; Z >= SearchBottom; Z -= ScanStepSubvoxels)
{
const int64 Density = Entry->Field->densitySubvoxels(X, Y, Z);
if (PreviousDensity >= 0 && Density < 0)
{
const double Denominator = static_cast<double>(PreviousDensity - Density);
const double Alpha = Denominator > 0.0
? static_cast<double>(PreviousDensity) / Denominator : 0.5;
const double SurfaceSubvoxels = static_cast<double>(PreviousZ)
+ static_cast<double>(Z - PreviousZ) * Alpha;
OutGroundLocalZCm = static_cast<float>(SurfaceSubvoxels
* CentimetersPerCoreVoxel / SubvoxelsPerCoreVoxel);
return FMath::IsFinite(OutGroundLocalZCm);
}
PreviousZ = Z;
PreviousDensity = Density;
}
return false;
}
};
// Global safety cap on spawned decoration ACTORS per cell (across all entries).
// HISM instances are exempt — they're batched render data, capped per entry by MaxPerChunk.
static constexpr int32 GMaxDecorationActorsPerCell = 400;
@@ -174,6 +278,7 @@ void UVoxelContentManager::Initialize(AActor* InOwner, UVoxelStrateManager* InSt
Settings = InSettings;
Seed = InSeed;
NativeSurfaceSnapshot.Reset();
NativeFloatingIslandSnapshot.Reset();
if (Settings)
{
@@ -196,6 +301,33 @@ void UVoxelContentManager::Initialize(AActor* InOwner, UVoxelStrateManager* InSt
Snapshot->Plans.Add(Plan.strateIndex, Plan);
}
NativeSurfaceSnapshot = Snapshot;
if (!Manifest->floatingIslands.empty())
{
TSharedPtr<FCoreFloatingIslandSnapshot, ESPMode::ThreadSafe> IslandSnapshot =
MakeShared<FCoreFloatingIslandSnapshot, ESPMode::ThreadSafe>();
for (const auto& IslandPlan : Manifest->floatingIslands)
{
TSharedPtr<FCoreFloatingIslandFieldSnapshot, ESPMode::ThreadSafe> Entry =
MakeShared<FCoreFloatingIslandFieldSnapshot, ESPMode::ThreadSafe>();
Entry->Recipe = IslandPlan.manifest.recipe;
Entry->Plan = IslandPlan.manifest.plan;
Entry->Field = MakeUnique<VoxelForgeCore::FloatingIslandFieldV1>(
Entry->Recipe, Entry->Plan);
if (!Entry->Field->isValid())
{
UE_LOG(LogTemp, Warning,
TEXT("[VoxelContentManager] Core floating-island field rejected strate %d."),
IslandPlan.strateIndex);
continue;
}
IslandSnapshot->Fields.Add(IslandPlan.strateIndex, Entry);
}
if (!IslandSnapshot->Fields.IsEmpty())
{
NativeFloatingIslandSnapshot = IslandSnapshot;
}
}
}
else
{
@@ -244,6 +376,66 @@ bool UVoxelContentManager::QueryNativeSurfaceAnchors(
return true;
}
bool UVoxelContentManager::QueryNativeFloatingIslandAnchor(
int32 StrateIndex, const FVector2D& LocalXYCm, float& OutGroundLocalZCm) const
{
OutGroundLocalZCm = 0.0f;
const AVoxelWorld* VoxelWorld = Cast<AVoxelWorld>(Owner.Get());
if (VoxelWorld == nullptr || !VoxelWorld->bUseVoxelForgeCore
|| !NativeFloatingIslandSnapshot.IsValid())
{
return false;
}
return NativeFloatingIslandSnapshot->GetTopAnchor(
StrateIndex, LocalXYCm, OutGroundLocalZCm);
}
bool UVoxelContentManager::QueryStrateWaterAnchor(
int32 StrateIndex, bool& bOutHasWater, float& OutWaterLocalZCm) const
{
bOutHasWater = false;
OutWaterLocalZCm = -FLT_MAX;
const AVoxelWorld* VoxelWorld = Cast<AVoxelWorld>(Owner.Get());
if (VoxelWorld == nullptr || StrateManager == nullptr)
{
return false;
}
if (VoxelWorld->bUseVoxelForgeCore && NativeSurfaceSnapshot.IsValid())
{
bool bHasPlan = false;
bool bEnabled = false;
float NativeWaterLocalZCm = -FLT_MAX;
if (NativeSurfaceSnapshot->GetWater(
StrateIndex, bHasPlan, bEnabled, NativeWaterLocalZCm) && bHasPlan)
{
bOutHasWater = bEnabled;
OutWaterLocalZCm = bEnabled ? NativeWaterLocalZCm : -FLT_MAX;
return true;
}
}
for (const FStrateSlot& Slot : StrateManager->GetLayout())
{
if (Slot.StrateIndex != StrateIndex)
{
continue;
}
const int32 MidChunkZ = Slot.BottomChunkZ
+ (Slot.TopChunkZ - Slot.BottomChunkZ) / 2;
const float ManagerWaterVoxelZ = StrateManager->GetWaterLevelWorldZForChunk(
FIntVector(0, 0, MidChunkZ));
if (ManagerWaterVoxelZ != -FLT_MAX)
{
bOutHasWater = true;
const float ManagerWaterLocalZCm = ManagerWaterVoxelZ * VOXEL_SIZE;
OutWaterLocalZCm = VoxelWorld->FromStrateManagerLocalZ(ManagerWaterLocalZCm);
}
return true;
}
return false;
}
void UVoxelContentManager::BeginDestroy()
{
// Backstop: stop any worker from touching us. EndPlay → NotifyShutdown should have already drained.
@@ -395,6 +587,11 @@ void UVoxelContentManager::UpdateDecorations(const FVector& PlayerWorldPos)
&& CurrentCtx.bSurfaceWorld
&& CurrentCtx.NativeSurface.IsValid()
&& CurrentCtx.NativeSurface->FindPlan(StrateIndex) != nullptr;
CurrentCtx.NativeIslands = NativeFloatingIslandSnapshot;
CurrentCtx.bNativeFloatingIslands = VoxelWorld != nullptr && VoxelWorld->bUseVoxelForgeCore
&& StrateManager->GetGeneratorTypeForChunk(RepChunk) == ECaveGeneratorType::FloatingIslands
&& CurrentCtx.NativeIslands.IsValid()
&& CurrentCtx.NativeIslands->FindField(StrateIndex) != nullptr;
{
bool bHasNativePlan = false;
bool bNativeWaterEnabled = false;
@@ -710,7 +907,17 @@ void UVoxelContentManager::BuildCellSpawns(const UVoxelGenerator* Gen, const FTr
FChunkBiomeCache BiomeCache;
const bool bHasBiomes = Ctx.BiomeCtx.IsValid();
auto D = [&](float VX, float VY, float VZ) { return Gen->GetDensityAt(VX, VY, VZ); };
auto D = [&](float VX, float VY, float VZ)
{
if (Ctx.bNativeFloatingIslands && Ctx.NativeIslands.IsValid())
{
float NativeDensity = 1.0f;
if (Ctx.NativeIslands->GetDensity(Ctx.StrateIndex, VX, VY, VZ, NativeDensity))
return NativeDensity;
return 1.0f;
}
return Gen->GetDensityAt(VX, VY, VZ);
};
auto GetSurface = [&](float VX, float VY, float& OutGround, float& OutCap)
{
if (Ctx.bNativeSurface && Ctx.NativeSurface.IsValid())
@@ -1338,6 +1545,21 @@ bool UVoxelContentManager::FindLandmarkColumn(const UVoxelGenerator* Gen, const
{
if (!Gen) return false;
const auto Density = [&](float SampleX, float SampleY, float SampleZ)
{
if (Ctx.bNativeFloatingIslands && Ctx.NativeIslands.IsValid())
{
float NativeDensity = 1.0f;
if (Ctx.NativeIslands->GetDensity(
Ctx.StrateIndex, SampleX, SampleY, SampleZ, NativeDensity))
{
return NativeDensity;
}
return 1.0f;
}
return Gen->GetDensityAt(SampleX, SampleY, SampleZ);
};
if (Ctx.bSurfaceWorld)
{
float hC, cC;
@@ -1368,7 +1590,7 @@ bool UVoxelContentManager::FindLandmarkColumn(const UVoxelGenerator* Gen, const
}
// Floor / Wall / Any → the terrain top.
if (!(hC >= Ctx.BottomVoxelZ && hC <= Ctx.TopVoxelZ)) return false;
if (!Ctx.bNativeSurface && Gen->GetDensityAt(VX, VY, hC) > 0.5f) return false;
if (!Ctx.bNativeSurface && Density(VX, VY, hC) > 0.5f) return false;
float d, hXp, hXm, hYp, hYm;
GetSurface(VX + 1.0f, VY, hXp, d);
GetSurface(VX - 1.0f, VY, hXm, d);
@@ -1384,19 +1606,19 @@ bool UVoxelContentManager::FindLandmarkColumn(const UVoxelGenerator* Gen, const
// Bounded by ColDepth like the decoration march (this runs SYNCHRONOUSLY on the game thread):
// once past open air, a solid run longer than ColDepth means bedrock down to the strate floor —
// stop instead of paying GetDensityAt across the whole remaining band.
float PrevD = Gen->GetDensityAt(VX, VY, Ctx.TopVoxelZ);
float PrevD = Density(VX, VY, Ctx.TopVoxelZ);
bool bSeenAir = (PrevD >= 0.0f);
float SolidRun = 0.0f;
for (float Z = Ctx.TopVoxelZ - Step; Z >= Ctx.BottomVoxelZ; Z -= Step)
{
const float Dz = Gen->GetDensityAt(VX, VY, Z);
const float Dz = Density(VX, VY, Z);
if ((PrevD >= 0.0f) != (Dz >= 0.0f)) // air ↔ solid crossing
{
float ZLo = Z, ZHi = Z + Step, DHi = PrevD, DLo = Dz;
for (int32 It = 0; It < 4; ++It)
{
const float ZM = 0.5f * (ZLo + ZHi);
const float DM = Gen->GetDensityAt(VX, VY, ZM);
const float DM = Density(VX, VY, ZM);
if ((DM >= 0.0f) == (DHi >= 0.0f)) { ZHi = ZM; DHi = DM; }
else { ZLo = ZM; DLo = DM; }
}
@@ -1405,9 +1627,9 @@ bool UVoxelContentManager::FindLandmarkColumn(const UVoxelGenerator* Gen, const
const float ZC = ZLo + (ZHi - ZLo) * T;
const FVector LocalGrad(
Gen->GetDensityAt(VX + 1.0f, VY, ZC) - Gen->GetDensityAt(VX - 1.0f, VY, ZC),
Gen->GetDensityAt(VX, VY + 1.0f, ZC) - Gen->GetDensityAt(VX, VY - 1.0f, ZC),
Gen->GetDensityAt(VX, VY, ZC + 1.0f) - Gen->GetDensityAt(VX, VY, ZC - 1.0f));
Density(VX + 1.0f, VY, ZC) - Density(VX - 1.0f, VY, ZC),
Density(VX, VY + 1.0f, ZC) - Density(VX, VY - 1.0f, ZC),
Density(VX, VY, ZC + 1.0f) - Density(VX, VY, ZC - 1.0f));
FVector N = OwnerXf.TransformVectorNoScale(LocalGrad).GetSafeNormal();
if (N.IsNearlyZero()) N = FVector::UpVector;
@@ -18,6 +18,7 @@ namespace
{
using FCoreJsonWriter = TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>;
constexpr double CoreVoxelSizeCm = 25.0;
constexpr double OwnerVoxelSizeCm = 12.5;
TSharedPtr<FJsonValue> SerializeCoreStruct(
const UStruct* Type, const void* Data, TSet<const UObject*>& ActiveObjects,
@@ -309,20 +310,32 @@ bool AttachValidatedWorldAssembly(const UVoxelSettings* Settings,
}
int32 SurfaceSlotCount = 0;
int32 FloatingIslandSlotCount = 0;
for (const FStrateSlot& Slot : StrateManager->GetLayout())
{
if (Slot.Definition != nullptr
&& Slot.Definition->GeneratorType == ECaveGeneratorType::SurfaceWorld)
if (Slot.Definition == nullptr)
{
continue;
}
const ECaveGeneratorType Archetype = StrateManager->GetGeneratorTypeForChunk(
FIntVector(0, 0, Slot.TopChunkZ));
if (Archetype == ECaveGeneratorType::SurfaceWorld)
{
++SurfaceSlotCount;
}
else if (Archetype == ECaveGeneratorType::FloatingIslands)
{
++FloatingIslandSlotCount;
}
}
if (SurfaceSlotCount == 0
|| Manifest->plan.strates.size() != static_cast<std::size_t>(SurfaceSlotCount))
if (Manifest->plan.strates.size() != static_cast<std::size_t>(SurfaceSlotCount)
|| Manifest->floatingIslands.size() != static_cast<std::size_t>(FloatingIslandSlotCount))
{
OutError = FString::Printf(
TEXT("Core world assembly has %d plans for %d resolved SurfaceWorld strates."),
static_cast<int32>(Manifest->plan.strates.size()), SurfaceSlotCount);
TEXT("Core world assembly has %d SurfaceWorld and %d floating-island plans for %d resolved SurfaceWorld and %d FloatingIslands strates."),
static_cast<int32>(Manifest->plan.strates.size()),
static_cast<int32>(Manifest->floatingIslands.size()),
SurfaceSlotCount, FloatingIslandSlotCount);
return false;
}
@@ -332,7 +345,8 @@ bool AttachValidatedWorldAssembly(const UVoxelSettings* Settings,
const FStrateSlot* MatchingSlot = StrateManager->GetLayout().FindByPredicate(
[&Plan](const FStrateSlot& Slot) { return Slot.StrateIndex == Plan.strateIndex; });
if (MatchingSlot == nullptr || MatchingSlot->Definition == nullptr
|| MatchingSlot->Definition->GeneratorType != ECaveGeneratorType::SurfaceWorld
|| StrateManager->GetGeneratorTypeForChunk(FIntVector(0, 0, Plan.topChunkZ))
!= ECaveGeneratorType::SurfaceWorld
|| MatchingSlot->TopChunkZ != Plan.topChunkZ
|| MatchingSlot->BottomChunkZ != Plan.bottomChunkZ
|| MatchedStrateIndices.Contains(Plan.strateIndex))
@@ -345,6 +359,25 @@ bool AttachValidatedWorldAssembly(const UVoxelSettings* Settings,
MatchedStrateIndices.Add(Plan.strateIndex);
}
for (const VoxelForgeCore::WorldAssemblyFloatingIslandPlanV1& Plan : Manifest->floatingIslands)
{
const FStrateSlot* MatchingSlot = StrateManager->GetLayout().FindByPredicate(
[&Plan](const FStrateSlot& Slot) { return Slot.StrateIndex == Plan.strateIndex; });
if (MatchingSlot == nullptr || MatchingSlot->Definition == nullptr
|| StrateManager->GetGeneratorTypeForChunk(FIntVector(0, 0, Plan.topChunkZ))
!= ECaveGeneratorType::FloatingIslands
|| MatchingSlot->TopChunkZ != Plan.topChunkZ
|| MatchingSlot->BottomChunkZ != Plan.bottomChunkZ
|| MatchedStrateIndices.Contains(Plan.strateIndex))
{
OutError = FString::Printf(
TEXT("Core floating-island plan for strate %d does not exactly match a unique resolved FloatingIslands slot."),
Plan.strateIndex);
return false;
}
MatchedStrateIndices.Add(Plan.strateIndex);
}
const std::string CanonicalJson = Manifest->ToJson();
if (CanonicalJson.empty())
{
@@ -403,6 +436,9 @@ bool VoxelForgeCoreAdapter::SerializeResolvedWorld(
Root->SetNumberField(TEXT("schema_version"), 1);
Root->SetStringField(TEXT("source"), TEXT("VoxelForge Runtime Core snapshot"));
Root->SetNumberField(TEXT("voxel_size_cm"), CoreVoxelSizeCm);
Root->SetNumberField(TEXT("owner_voxel_size_cm"), OwnerVoxelSizeCm);
Root->SetNumberField(TEXT("core_voxel_size_cm"), CoreVoxelSizeCm);
Root->SetNumberField(TEXT("owner_to_core_length_scale"), OwnerVoxelSizeCm / CoreVoxelSizeCm);
Root->SetNumberField(TEXT("seed"), Seed);
Root->SetBoolField(TEXT("open_surface_entry"), Settings->bOpenSurfaceEntry);
Root->SetNumberField(TEXT("origin_spine_radius"), OriginSpineRadius);
+4 -2
View File
@@ -96,8 +96,10 @@ bool UVoxelSettings::SetWorldAssemblyManifestJson(const FString& InJson, FString
WorldAssemblyManifestHash = FString::Printf(TEXT("%016llx"),
static_cast<unsigned long long>(Manifest->contentHash));
MarkPackageDirty();
OutReport = FString::Printf(TEXT("imported native world assembly %s (%d stratum plans)"),
*WorldAssemblyManifestHash, static_cast<int32>(Manifest->plan.strates.size()));
OutReport = FString::Printf(
TEXT("imported native world assembly %s (%d SurfaceWorld plans, %d floating-island plans)"),
*WorldAssemblyManifestHash, static_cast<int32>(Manifest->plan.strates.size()),
static_cast<int32>(Manifest->floatingIslands.size()));
return true;
}
+5 -4
View File
@@ -7264,11 +7264,12 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld()
if (const VoxelForgeCore::WorldAssemblyManifest* Assembly = Snapshot->worldAssembly())
{
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeCoreRuntime] world_assembly_hash=%016llx recipe_hash=%016llx plan_hash=%016llx strates=%d"),
TEXT("[VoxelForgeCoreRuntime] world_assembly_hash=%016llx recipe_hash=%016llx plan_hash=%016llx surface_strates=%d island_strates=%d"),
static_cast<unsigned long long>(Assembly->contentHash),
static_cast<unsigned long long>(Assembly->recipe.contentHash),
static_cast<unsigned long long>(Assembly->plan.contentHash),
static_cast<int32>(Assembly->plan.strates.size()));
static_cast<int32>(Assembly->plan.strates.size()),
static_cast<int32>(Assembly->floatingIslands.size()));
}
TSharedPtr<FVoxelForgeCoreRuntimeState, ESPMode::ThreadSafe> State =
@@ -13285,8 +13286,8 @@ int32 AVoxelWorld::GetCurrentSeason() const
FString AVoxelWorld::GetCurrentSeasonContentHash() const
{
const FString SeasonHash = StrateManager ? StrateManager->GetSeasonContentHash() : FString();
// Only active Core generation includes the optional native surface manifest in world identity.
// Seul le Core actif inclut le manifeste de surface natif dans l'identite du monde.
// Only active Core generation includes the optional native world assembly in world identity.
// Seul le Core actif inclut l'assemblage natif facultatif dans l'identite du monde.
if (!Settings || !bUseVoxelForgeCore)
{
return SeasonHash;
@@ -34,6 +34,9 @@ public:
* dominant-biome change. */
void UpdateForPlayer(const FVector& PlayerWorldPos);
/** Read the player's current atmosphere strate for isolated Core validation. / Lit la strate d'atmosphere active pour le test Core isole. */
int32 GetCurrentStrateIndex() const { return CurrentStrateIndex; }
/** Tear down spawned layer actors + reset (season reset / shutdown). */
void Reset();
@@ -43,6 +43,8 @@
#include "CoreMinimal.h"
#include "Containers/Queue.h"
struct FCoreFloatingIslandSnapshot;
#include "VoxelTypes.h"
#include "VoxelStrateTypes.h" // FStrateDecoration (resolved per dominant biome)
#include "VoxelBiomeTypes.h" // FBiomeContext (per-column biome resolve on the worker)
@@ -103,6 +105,16 @@ public:
float& OutGroundLocalZCm, bool& bOutHasWater,
float& OutWaterLocalZCm) const;
/** Find the top air-to-rock crossing in a Core FloatingIslands plan for content placement.
* / Trouve le premier contact air-roche d'un plan Core FloatingIslands pour placer le contenu. */
bool QueryNativeFloatingIslandAnchor(int32 StrateIndex, const FVector2D& LocalXYCm,
float& OutGroundLocalZCm) const;
/** Read the effective water datum used by this strate's content plane.
* / Lit le niveau d'eau effectif utilise par le plan de contenu de cette strate. */
bool QueryStrateWaterAnchor(int32 StrateIndex, bool& bOutHasWater,
float& OutWaterLocalZCm) const;
//--- DECORATIONS (distance-based world grid, async march) ----------------
/** Stream decoration cells around the player: recompute the desired set on cell/strate change,
* launch async march tasks (capped), and apply finished results budgeted. Call every Tick. */
@@ -254,6 +266,8 @@ private:
bool bSurfaceWorld = false; // heightfield archetype → use the GetSurfaceHeightAt oracle
bool bNativeSurface = false; // Core manifest is the height oracle and water datum / Le manifeste Core definit le relief et le niveau d'eau.
TSharedPtr<const FCoreSurfaceSnapshot, ESPMode::ThreadSafe> NativeSurface;
bool bNativeFloatingIslands = false; // islands use the Core volume for column snaps / les iles utilisent le volume Core pour les ancres.
TSharedPtr<const FCoreFloatingIslandSnapshot, ESPMode::ThreadSafe> NativeIslands;
// Strate biome field (PODs only → worker-safe). Empty ⇒ biomes disabled for this strate. The
// worker resolves the dominant biome PER COLUMN (ResolveBiomeSampleAt) so decoration borders
@@ -349,6 +363,7 @@ private:
// Immutable Core manifest snapshot captured by decoration workers; workers never touch UObjects.
// Instantane immuable du manifeste Core pour les workers de decoration; aucun worker ne touche aux UObjects.
TSharedPtr<const FCoreSurfaceSnapshot, ESPMode::ThreadSafe> NativeSurfaceSnapshot;
TSharedPtr<const FCoreFloatingIslandSnapshot, ESPMode::ThreadSafe> NativeFloatingIslandSnapshot;
int32 Seed = 0;
+4 -4
View File
@@ -373,10 +373,10 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Voxel|Strates")
TSoftObjectPtr<UVoxelSeasonAsset> Season;
// Optional reviewed Core surface plan. The canonical JSON stays in this cooked settings asset;
// its Core FNV digest is included in GetCurrentSeasonContentHash when present.
// Plan de surface Core facultatif; le JSON canonique est cuit avec les reglages et son hash
// Core est inclus dans GetCurrentSeasonContentHash lorsqu'il est present.
// Optional reviewed Core SurfaceWorld and FloatingIslands plans. Canonical JSON stays in this
// cooked settings asset; its Core FNV digest enters the season hash only while Core is enabled.
// Plans Core SurfaceWorld et FloatingIslands facultatifs. Le JSON canonique est cuit ici ;
// son hash FNV Core entre dans le hash de saison uniquement lorsque Core est actif.
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Voxel|Strates", meta = (MultiLine = true))
FString WorldAssemblyManifestJson;
+4 -2
View File
@@ -1741,7 +1741,8 @@ struct VOXELFORGE_API FFloatingIslandParams
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Islands", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float IslandDensity = 0.5f;
// Island horizontal radius range (voxels): 12-24 m landmass diameters by default.
// Owner scale: 24–48 legacy voxels = 3–6 m radius at 12.5 cm/voxel (6–12 m diameter).
// Echelle proprietaire : 24–48 voxels legacy = rayon de 3–6 m a 12,5 cm/voxel (diametre 6–12 m).
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Islands", meta = (ClampMin = "2.0"))
float IslandMinRadius = 24.0f;
@@ -1765,7 +1766,8 @@ struct VOXELFORGE_API FFloatingIslandParams
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Islands", meta = (ClampMin = "0.0"))
float SurfaceRoughness = 3.0f;
// SmoothMin blend radius for merging overlapping islands (voxels): 1.5 m by default.
// SmoothMin blend radius: 6 legacy voxels = 0.75 m at the owner's 12.5 cm scale.
// Rayon de fusion SmoothMin : 6 voxels legacy = 0,75 m a l'echelle proprietaire de 12,5 cm.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Islands", meta = (ClampMin = "0.0"))
float SDFBlendRadius = 6.0f;
File diff suppressed because it is too large. Load diff