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:
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;
|
||||
|
||||
|
||||
Reference in new issue
Block a user