wip(assembler): round L18 -- FloatingIslands false floors removed; shared strate ceiling
FI geometry v3 drops the all-XY lower boundary seal that created false floors (3.5/8 m); the ceiling comes from the shared VF_ApplyUpperBoundarySeal and AppendStratumBoundaryCeilings path for every generator, at each strate's own configured upper boundary (owner: every strate has a ceiling). Far-LOD ceiling continuity: 0 gaps at LOD4-8. SurfaceWorld bit-identical to L14 (14 configs + 24 m crossing); FI deterministic (28-row matrix); route parity and proofs pass. Unreal: FI geometryVersion 2->3 and the shared upper-seal helper extracted (UBT compile needed on the owner PC). Open: small ceiling holes; lower-stratum LOD ring steps seen through the FI void. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
1 parent
87041290dd
commit
dcf0763ec5
36 files changed
+1690
-54
No files matched your search
@@ -864,6 +864,46 @@ struct FFarMeshColumnIndex
|
||||
return Hits;
|
||||
}
|
||||
|
||||
bool HasDownwardHit(double X,double Y,double Z,double Tolerance) const
|
||||
{
|
||||
const int32 CX=std::clamp(static_cast<int32>(std::floor((X-Origin.X)/Step)),0,Cells-1);
|
||||
const int32 CY=std::clamp(static_cast<int32>(std::floor((Y-Origin.Y)/Step)),0,Cells-1);
|
||||
const auto& Candidates=ByColumn[static_cast<size_t>(CY)*Cells+CX];
|
||||
for(const int32 Triangle:Candidates)
|
||||
{
|
||||
const int32 IA=Mesh.Triangles[Triangle*3],IB=Mesh.Triangles[Triangle*3+1],
|
||||
IC=Mesh.Triangles[Triangle*3+2];
|
||||
if(IA<0||IB<0||IC<0||IA>=Mesh.Normals.Num()||IB>=Mesh.Normals.Num()
|
||||
||IC>=Mesh.Normals.Num())continue;
|
||||
if(Mesh.Normals[IA].Z+Mesh.Normals[IB].Z+Mesh.Normals[IC].Z>=-1.5)continue;
|
||||
const double AX=PositionAxis(IA,0),AY=PositionAxis(IA,1),AZ=PositionAxis(IA,2);
|
||||
const double BX=PositionAxis(IB,0),BY=PositionAxis(IB,1),BZ=PositionAxis(IB,2);
|
||||
const double CXv=PositionAxis(IC,0),CYv=PositionAxis(IC,1),CZ=PositionAxis(IC,2);
|
||||
const double Den=(BX-AX)*(CYv-AY)-(BY-AY)*(CXv-AX);
|
||||
if(std::abs(Den)<1.0e-10)continue;
|
||||
const double U=((X-AX)*(CYv-AY)-(Y-AY)*(CXv-AX))/Den;
|
||||
const double V=((BX-AX)*(Y-AY)-(BY-AY)*(X-AX))/Den;
|
||||
const double W=1.0-U-V;
|
||||
if(U<-1.0e-7||V<-1.0e-7||W<-1.0e-7)continue;
|
||||
const double Hit=AZ+U*(BZ-AZ)+V*(CZ-AZ);
|
||||
if(std::abs(Hit-Z)<=Tolerance)return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasDownwardCellCoverage(double X0,double Y0,double X1,double Y1,
|
||||
double Z,double Tolerance) const
|
||||
{
|
||||
constexpr double Samples[3]={0.125,0.5,0.875};
|
||||
for(const double V:Samples)for(const double U:Samples)
|
||||
{
|
||||
const double X=X0+(X1-X0)*U;
|
||||
const double Y=Y0+(Y1-Y0)*V;
|
||||
if(!HasDownwardHit(X,Y,Z,Tolerance))return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
double PositionAxis(int32 Index,int32 Axis) const
|
||||
{
|
||||
@@ -974,11 +1014,11 @@ inline void AppendFarStratumShells(const FStandaloneOwnerWorld& World,const FSta
|
||||
std::vector<FBoundary> Boundaries;
|
||||
for(const FStrateSlot& Slot:Layout)
|
||||
{
|
||||
const FStrateGenerationParams Params=World.Manager->GetGenerationParams(
|
||||
FIntVector(0,0,(Slot.TopChunkZ+Slot.BottomChunkZ)/2));
|
||||
const double HalfWidth=std::max(8.0,static_cast<double>(Params.BoundarySealThickness)+8.0);
|
||||
Boundaries.push_back({static_cast<double>(Slot.BottomChunkZ)*CHUNK_SIZE,HalfWidth});
|
||||
Boundaries.push_back({static_cast<double>(Slot.TopChunkZ+1)*CHUNK_SIZE,HalfWidth});
|
||||
FStandaloneShapeWorld::FResolvedStratumBounds Resolved;
|
||||
if(!Shape.GetResolvedStratumBounds(Slot,Resolved))continue;
|
||||
const double HalfWidth=std::max(8.0,Resolved.SealThickness+8.0);
|
||||
Boundaries.push_back({Resolved.BottomZ,HalfWidth});
|
||||
Boundaries.push_back({Resolved.TopZ,HalfWidth});
|
||||
}
|
||||
std::sort(Boundaries.begin(),Boundaries.end(),[](const FBoundary& A,const FBoundary& B){return A.Z<B.Z;});
|
||||
Boundaries.erase(std::unique(Boundaries.begin(),Boundaries.end(),[](const FBoundary& A,const FBoundary& B)
|
||||
@@ -1132,3 +1172,129 @@ inline void AppendFarStratumShells(const FStandaloneOwnerWorld& World,const FSta
|
||||
Mesh.TriangleSkirtFaceMasks=std::move(CombinedFaceMasks);
|
||||
Mesh.NumSurfaceVertices=OldSurfaceVertices+AddedVertices;
|
||||
}
|
||||
|
||||
inline void AppendStratumBoundaryCeilings(const FStandaloneOwnerWorld& World,
|
||||
const FStandaloneShapeWorld& Shape,const FVoxelTileKey& Key,int32 Step,FVoxelMeshData& Mesh)
|
||||
{
|
||||
if(Key.Level<0||Step<=0||!World.Manager)return;
|
||||
const FIntVector Origin=Key.OriginVoxels();
|
||||
const int32 Extent=Key.ExtentVoxels();
|
||||
const int32 GridStep=Step;
|
||||
if(Extent<=0||GridStep<=0||Extent%GridStep!=0)return;
|
||||
const int32 GridSide=Extent/GridStep+1;
|
||||
struct FCandidate
|
||||
{
|
||||
FStandaloneShapeWorld::FResolvedStratumBounds Bounds;
|
||||
std::vector<uint8> Crossing;
|
||||
};
|
||||
std::vector<FCandidate> Candidates;
|
||||
for(const FStrateSlot& Slot:World.Manager->GetLayout())
|
||||
{
|
||||
FCandidate Candidate;
|
||||
if(!Shape.GetResolvedStratumBounds(Slot,Candidate.Bounds)
|
||||
||Candidate.Bounds.SealThickness<=0.0
|
||||
||Candidate.Bounds.TopZ<Origin.Z-1.0e-6
|
||||
||Candidate.Bounds.TopZ>Origin.Z+Extent+1.0e-6)continue;
|
||||
const double Epsilon=std::min(0.5,Candidate.Bounds.SealThickness*0.25);
|
||||
if(Epsilon<=0.0)continue;
|
||||
Candidate.Crossing.resize(static_cast<std::size_t>(GridSide)*GridSide,0);
|
||||
bool AnyCrossing=false;
|
||||
for(int32 Y=0;Y<GridSide;++Y)for(int32 X=0;X<GridSide;++X)
|
||||
{
|
||||
const double WorldX=Origin.X+X*GridStep;
|
||||
const double WorldY=Origin.Y+Y*GridStep;
|
||||
const float Below=Shape.DensityAt(static_cast<float>(WorldX),static_cast<float>(WorldY),
|
||||
static_cast<float>(Candidate.Bounds.TopZ-Epsilon));
|
||||
const float Above=Shape.DensityAt(static_cast<float>(WorldX),static_cast<float>(WorldY),
|
||||
static_cast<float>(Candidate.Bounds.TopZ+Epsilon));
|
||||
const bool bCrosses=std::isfinite(Below)&&std::isfinite(Above)&&Below<0.0f&&Above>0.0f;
|
||||
Candidate.Crossing[static_cast<std::size_t>(Y*GridSide+X)]=bCrosses?1:0;
|
||||
AnyCrossing=AnyCrossing||bCrosses;
|
||||
}
|
||||
if(AnyCrossing)Candidates.push_back(std::move(Candidate));
|
||||
}
|
||||
if(Candidates.empty())return;
|
||||
|
||||
const int32 Cells=std::max(1,World.CoarseTileCells);
|
||||
FFarMeshColumnIndex MeshIndex(Mesh,Origin,Extent,Cells,Step);
|
||||
const int32 OldVertexCount=Mesh.Vertices.Num();
|
||||
const int32 OldSurfaceVertices=std::clamp(Mesh.NumSurfaceVertices,0,OldVertexCount);
|
||||
const bool bHasUVs=Mesh.UVs.Num()==OldVertexCount;
|
||||
const bool bHasColors=Mesh.Colors.Num()==OldVertexCount;
|
||||
const FColor Color=bHasColors&&OldVertexCount>0?Mesh.Colors[0]:FColor(160,160,160,255);
|
||||
const double VoxelSizeCm=static_cast<double>(World.CoreVoxelSizeMicrons)/10000.0;
|
||||
std::vector<FVector> NewVertices,NewNormals;
|
||||
std::vector<FVector2f> NewUVs;
|
||||
std::vector<FColor> NewColors;
|
||||
std::vector<int32> NewTriangles;
|
||||
for(const FCandidate& Candidate:Candidates)
|
||||
{
|
||||
std::vector<int32> VertexMap(static_cast<std::size_t>(GridSide)*GridSide,INDEX_NONE);
|
||||
const double CeilingZ=Candidate.Bounds.TopZ;
|
||||
const auto AddVertex=[&](int32 X,int32 Y)
|
||||
{
|
||||
const std::size_t GridIndex=static_cast<std::size_t>(Y*GridSide+X);
|
||||
int32& Mapped=VertexMap[GridIndex];
|
||||
if(Mapped!=INDEX_NONE)return Mapped;
|
||||
Mapped=static_cast<int32>(NewVertices.size());
|
||||
const double WorldX=Origin.X+X*GridStep,WorldY=Origin.Y+Y*GridStep;
|
||||
NewVertices.emplace_back(WorldX*VoxelSizeCm,WorldY*VoxelSizeCm,CeilingZ*VoxelSizeCm);
|
||||
NewNormals.emplace_back(0.0,0.0,-1.0);
|
||||
if(bHasUVs)NewUVs.emplace_back(static_cast<float>(WorldX),static_cast<float>(WorldY));
|
||||
if(bHasColors)NewColors.push_back(Color);
|
||||
return Mapped;
|
||||
};
|
||||
for(int32 Y=0;Y+1<GridSide;++Y)for(int32 X=0;X+1<GridSide;++X)
|
||||
{
|
||||
const std::size_t I00=static_cast<std::size_t>(Y*GridSide+X);
|
||||
const std::size_t I10=I00+1,I01=I00+GridSide,I11=I01+1;
|
||||
if(!Candidate.Crossing[I00]||!Candidate.Crossing[I10]
|
||||
||!Candidate.Crossing[I01]||!Candidate.Crossing[I11])continue;
|
||||
const double CellX0=Origin.X+X*GridStep,CellY0=Origin.Y+Y*GridStep;
|
||||
const double CellX1=CellX0+GridStep,CellY1=CellY0+GridStep;
|
||||
// Suppress a coincident plane only when it covers the full cell. A hit at just the
|
||||
// center of a curved shell triangle is not enough to leave the rest of this cap cell open.
|
||||
if(MeshIndex.HasDownwardCellCoverage(CellX0,CellY0,CellX1,CellY1,CeilingZ,0.001))continue;
|
||||
const int32 A=AddVertex(X,Y),B=AddVertex(X+1,Y);
|
||||
const int32 C=AddVertex(X+1,Y+1),D=AddVertex(X,Y+1);
|
||||
NewTriangles.insert(NewTriangles.end(),{A,C,B,A,D,C});
|
||||
}
|
||||
}
|
||||
if(NewTriangles.empty())return;
|
||||
|
||||
const int32 AddedVertices=static_cast<int32>(NewVertices.size());
|
||||
auto InsertVertices=[&](auto& Existing,const auto& Added)
|
||||
{
|
||||
using TArrayType=std::remove_reference_t<decltype(Existing)>;
|
||||
TArrayType Combined;Combined.Reserve(Existing.Num()+static_cast<int32>(Added.size()));
|
||||
for(int32 I=0;I<OldSurfaceVertices;++I)Combined.Add(Existing[I]);
|
||||
for(const auto& Value:Added)Combined.Add(Value);
|
||||
for(int32 I=OldSurfaceVertices;I<Existing.Num();++I)Combined.Add(Existing[I]);
|
||||
Existing=std::move(Combined);
|
||||
};
|
||||
InsertVertices(Mesh.Vertices,NewVertices);InsertVertices(Mesh.Normals,NewNormals);
|
||||
if(bHasUVs)InsertVertices(Mesh.UVs,NewUVs);
|
||||
if(bHasColors)InsertVertices(Mesh.Colors,NewColors);
|
||||
for(int32& Index:Mesh.Triangles)if(Index>=OldSurfaceVertices)Index+=AddedVertices;
|
||||
const int32 CeilingStart=std::max(0,Mesh.Triangles.Num()-Mesh.NumCeilingTriangles*3);
|
||||
TArray<int32> CombinedTriangles;CombinedTriangles.Reserve(
|
||||
Mesh.Triangles.Num()+static_cast<int32>(NewTriangles.size()));
|
||||
for(int32 I=0;I<CeilingStart;++I)CombinedTriangles.Add(Mesh.Triangles[I]);
|
||||
for(const int32 Index:NewTriangles)CombinedTriangles.Add(OldSurfaceVertices+Index);
|
||||
for(int32 I=CeilingStart;I<Mesh.Triangles.Num();++I)CombinedTriangles.Add(Mesh.Triangles[I]);
|
||||
const int32 OldTriangleCount=Mesh.Triangles.Num()/3;
|
||||
TArray<uint8> ExistingFaceMasks=Mesh.TriangleSkirtFaceMasks;
|
||||
ExistingFaceMasks.SetNum(OldTriangleCount);
|
||||
TArray<uint8> CombinedFaceMasks;CombinedFaceMasks.Reserve(
|
||||
OldTriangleCount+static_cast<int32>(NewTriangles.size()/3));
|
||||
for(int32 Triangle=0;Triangle<CeilingStart/3;++Triangle)
|
||||
CombinedFaceMasks.Add(ExistingFaceMasks[Triangle]);
|
||||
for(std::size_t Triangle=0;Triangle<NewTriangles.size()/3;++Triangle)
|
||||
CombinedFaceMasks.Add(0);
|
||||
for(int32 Triangle=CeilingStart/3;Triangle<OldTriangleCount;++Triangle)
|
||||
CombinedFaceMasks.Add(ExistingFaceMasks[Triangle]);
|
||||
Mesh.Triangles=std::move(CombinedTriangles);
|
||||
Mesh.TriangleSkirtFaceMasks=std::move(CombinedFaceMasks);
|
||||
Mesh.NumSurfaceVertices=OldSurfaceVertices+AddedVertices;
|
||||
Mesh.NumCeilingTriangles+=static_cast<int32>(NewTriangles.size()/3);
|
||||
}
|
||||
@@ -266,7 +266,7 @@ namespace floating_island_detail
|
||||
? recipe.ownerVoxelSizeMicrons / ScaleGcd : 0;
|
||||
const std::uint32_t ExpectedDenominator = ScaleGcd > 0
|
||||
? recipe.coreVoxelSizeMicrons / ScaleGcd : 0;
|
||||
if (recipe.schemaVersion != 1 || (recipe.geometryVersion != 1 && recipe.geometryVersion != 2)
|
||||
if (recipe.schemaVersion != 1 || recipe.geometryVersion < 1 || recipe.geometryVersion > 3
|
||||
|| (recipe.geometryVersion == 1
|
||||
&& (recipe.crustDepthBonusQ16 != 0 || recipe.rootDepthRatioQ16 != 0))
|
||||
|| recipe.unitsPerVoxel != Q
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "VoxelCaveMorphology.h"
|
||||
#include "VoxelNoise.h"
|
||||
#include "VoxelStrateManager.h"
|
||||
#include "VoxelDensityPrimitives.h"
|
||||
#include "VoxelForgeCore/VoxelForgeCore.h"
|
||||
#include "VoxelForgeFieldShared.h"
|
||||
|
||||
@@ -32,6 +33,13 @@
|
||||
class FStandaloneShapeWorld
|
||||
{
|
||||
public:
|
||||
struct FResolvedStratumBounds
|
||||
{
|
||||
double BottomZ=0.0;
|
||||
double TopZ=0.0;
|
||||
double SealThickness=0.0;
|
||||
};
|
||||
|
||||
double GetCoreVoxelSizeCm() const
|
||||
{
|
||||
return Owner == nullptr || Owner->CoreVoxelSizeMicrons == 0
|
||||
@@ -47,6 +55,41 @@ public:
|
||||
return static_cast<double>(CHUNK_SIZE) * OwnerVoxelSizeCm / GetCoreVoxelSizeCm();
|
||||
}
|
||||
|
||||
static bool MakeResolvedStratumBounds(double BottomZ,double TopZ,double SealThickness,
|
||||
FResolvedStratumBounds& Out)
|
||||
{
|
||||
if(!std::isfinite(BottomZ)||!std::isfinite(TopZ)||!std::isfinite(SealThickness)
|
||||
||TopZ<=BottomZ||SealThickness<0.0)return false;
|
||||
Out={BottomZ,TopZ,SealThickness};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetResolvedStratumBounds(const FStrateSlot& Slot,FResolvedStratumBounds& Out) const
|
||||
{
|
||||
if(!Owner||!Owner->Manager)return false;
|
||||
const int32 MidChunkZ=(Slot.TopChunkZ+Slot.BottomChunkZ)/2;
|
||||
if(bUseNativeManifestFields&&WorldAssembly
|
||||
&&Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,MidChunkZ))
|
||||
==ECaveGeneratorType::FloatingIslands)
|
||||
{
|
||||
const auto* Plan=FindWorldAssemblyFloatingIslandStratum(Slot.StrateIndex,MidChunkZ);
|
||||
if(Plan&&Plan->manifest.recipe.geometryVersion>=3)
|
||||
{
|
||||
constexpr double Scale=static_cast<double>(
|
||||
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
return MakeResolvedStratumBounds(
|
||||
static_cast<double>(Plan->manifest.recipe.layoutMinimumZSubvoxels)/Scale,
|
||||
static_cast<double>(Plan->manifest.recipe.layoutMaximumZSubvoxels)/Scale,
|
||||
static_cast<double>(Plan->manifest.recipe.boundarySealSubvoxels)/Scale,Out);
|
||||
}
|
||||
}
|
||||
const FStrateGenerationParams Params=Owner->Manager->GetGenerationParams(
|
||||
FIntVector(0,0,MidChunkZ));
|
||||
return MakeResolvedStratumBounds(static_cast<double>(Slot.BottomChunkZ)*CHUNK_SIZE,
|
||||
static_cast<double>(Slot.TopChunkZ+1)*CHUNK_SIZE,
|
||||
static_cast<double>(Params.BoundarySealThickness),Out);
|
||||
}
|
||||
|
||||
enum class EDetailVariant : uint8 { Coherent, Smooth, PerVoxel };
|
||||
struct FBoxClassification
|
||||
{
|
||||
@@ -714,12 +757,12 @@ public:
|
||||
const double TileZ0=Origin.Z,TileZ1=Origin.Z+static_cast<double>(Cells)*Step;
|
||||
for(const FStrateSlot& Slot:Owner->Manager->GetLayout())
|
||||
{
|
||||
const FStrateGenerationParams Params=Owner->Manager->GetGenerationParams(
|
||||
FIntVector(0,0,(Slot.TopChunkZ+Slot.BottomChunkZ)/2));
|
||||
FResolvedStratumBounds Bounds;
|
||||
if(!GetResolvedStratumBounds(Slot,Bounds))continue;
|
||||
const float HalfWidth=static_cast<float>(std::max(8.0,
|
||||
static_cast<double>(Params.BoundarySealThickness)+8.0));
|
||||
const float Boundaries[2]={static_cast<float>(Slot.BottomChunkZ)*CHUNK_SIZE,
|
||||
static_cast<float>(Slot.TopChunkZ+1)*CHUNK_SIZE};
|
||||
Bounds.SealThickness+8.0));
|
||||
const float Boundaries[2]={static_cast<float>(Bounds.BottomZ),
|
||||
static_cast<float>(Bounds.TopZ)};
|
||||
for(const float Z:Boundaries)
|
||||
if(Z+HalfWidth>=TileZ0&&Z-HalfWidth<=TileZ1)
|
||||
Out.stratumBoundaries.push_back({Z,HalfWidth});
|
||||
@@ -1549,13 +1592,18 @@ public:
|
||||
const std::int64_t Seal=IslandPlan->manifest.recipe.boundarySealSubvoxels;
|
||||
const std::int64_t BoxBottom=static_cast<std::int64_t>(BMin[2])*Scale;
|
||||
const std::int64_t BoxTop=static_cast<std::int64_t>(BMax[2])*Scale;
|
||||
const bool bBottomSealOverlap=BoxBottom<Bottom+Seal&&BoxTop>=Bottom;
|
||||
const bool bTopSealOverlap=BoxBottom<=Top&&BoxTop>Top-Seal;
|
||||
const bool bSharedTopSealEnabled=IslandPlan->manifest.recipe.geometryVersion<3
|
||||
||!VoxelDensityAblation::IsBoundarySealOff();
|
||||
const bool bTopSealOverlap=bSharedTopSealEnabled
|
||||
&&BoxBottom<=Top&&BoxTop>Top-Seal;
|
||||
const bool bBottomSealOverlap=IslandPlan->manifest.recipe.geometryVersion<3
|
||||
&&BoxBottom<Bottom+Seal&&BoxTop>=Bottom;
|
||||
if(bBottomSealOverlap)
|
||||
return Finish(BoxBottom>=Bottom&&BoxTop<=Bottom+Seal
|
||||
?EVoxelTileClass::AllSolid:EVoxelTileClass::Mixed);
|
||||
if(bTopSealOverlap)
|
||||
return Finish(BoxBottom>=Top-Seal&&BoxTop<=Top
|
||||
return Finish((IslandPlan->manifest.recipe.geometryVersion<3
|
||||
?BoxBottom>=Top-Seal:BoxBottom>Top-Seal)&&BoxTop<=Top
|
||||
?EVoxelTileClass::AllSolid:EVoxelTileClass::Mixed);
|
||||
const VoxelForgeCore::FloatingIslandFieldV1 Block(
|
||||
IslandPlan->manifest.recipe,IslandPlan->manifest.plan);
|
||||
@@ -2130,9 +2178,28 @@ private:
|
||||
const std::int64_t Top=Plan->manifest.recipe.layoutMaximumZSubvoxels;
|
||||
const std::int64_t Seal=Plan->manifest.recipe.boundarySealSubvoxels;
|
||||
const std::int64_t Zq=ToFixed(Z);
|
||||
if(Zq>=Bottom&&Zq<Bottom+Seal)Density=std::min(Density,-std::max<std::int64_t>(1,Zq-Bottom));
|
||||
if(Zq<=Top&&Zq>Top-Seal)Density=std::min(Density,-std::max<std::int64_t>(1,Top-Zq));
|
||||
OutDensity=static_cast<float>(static_cast<double>(Density)/Scale);
|
||||
if(Plan->manifest.recipe.geometryVersion<3)
|
||||
{
|
||||
if(Zq>=Bottom&&Zq<Bottom+Seal)
|
||||
Density=std::min(Density,-std::max<std::int64_t>(1,Zq-Bottom));
|
||||
if(Zq<=Top&&Zq>Top-Seal)
|
||||
Density=std::min(Density,-std::max<std::int64_t>(1,Top-Zq));
|
||||
OutDensity=static_cast<float>(static_cast<double>(Density)/Scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutDensity=static_cast<float>(static_cast<double>(Density)/Scale);
|
||||
FResolvedStratumBounds Bounds;
|
||||
if(!MakeResolvedStratumBounds(static_cast<double>(Bottom)/Scale,
|
||||
static_cast<double>(Top)/Scale,static_cast<double>(Seal)/Scale,Bounds))
|
||||
return false;
|
||||
const FFloatingIslandParams Params=Owner->Manager->GetFloatingIslandParamsForChunk(
|
||||
FIntVector(0,0,ChunkZ));
|
||||
float InternalDensity=-OutDensity;
|
||||
VF_ApplyUpperBoundarySeal(InternalDensity,Z,static_cast<float>(Bounds.TopZ),
|
||||
static_cast<float>(Bounds.SealThickness),Params.BaseDensity);
|
||||
OutDensity=-InternalDensity;
|
||||
}
|
||||
return std::isfinite(OutDensity);
|
||||
}
|
||||
|
||||
@@ -2351,19 +2418,23 @@ private:
|
||||
int32 Top=MIN_int32,Bottom=MAX_int32;
|
||||
for(const FStrateSlot& Slot:Layout)
|
||||
{
|
||||
Top=std::max(Top,Slot.TopChunkZ);Bottom=std::min(Bottom,Slot.BottomChunkZ);
|
||||
FResolvedStratumBounds Bounds;
|
||||
if(!GetResolvedStratumBounds(Slot,Bounds))continue;
|
||||
const int32 ResolvedBottom=FMath::FloorToInt(static_cast<float>(Bounds.BottomZ));
|
||||
const int32 ResolvedTop=FMath::CeilToInt(static_cast<float>(Bounds.TopZ));
|
||||
Top=std::max(Top,ResolvedTop);Bottom=std::min(Bottom,ResolvedBottom);
|
||||
if(!Slot.Definition)continue;
|
||||
const int32 MidChunkZ=(Slot.TopChunkZ+Slot.BottomChunkZ)/2;
|
||||
FLayerField Field;
|
||||
Field.Lo=Slot.BottomChunkZ*CHUNK_SIZE;
|
||||
Field.Hi=(Slot.TopChunkZ+1)*CHUNK_SIZE;
|
||||
Field.Lo=ResolvedBottom;
|
||||
Field.Hi=ResolvedTop;
|
||||
Field.StrateIndex=Slot.StrateIndex;
|
||||
Field.Params=Owner->Manager->GetGenerationParams(FIntVector(0,0,MidChunkZ));
|
||||
LayerFields.push_back(std::move(Field));
|
||||
}
|
||||
if(Top>=Bottom)
|
||||
{
|
||||
LayerRanges.push_back({Bottom*CHUNK_SIZE,(Top+1)*CHUNK_SIZE});
|
||||
LayerRanges.push_back({Bottom,Top});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2204,6 +2204,8 @@ namespace VoxelForgeCore
|
||||
out.diagnostics.horizonGroundTrianglesRemoved=GroundStats.RemovedTriangles;
|
||||
out.diagnostics.horizonCapTriangles=CapStats.Triangles;
|
||||
}
|
||||
if(impl_->worldAssembly&&!bOverlapsPlayerEdit)
|
||||
AppendStratumBoundaryCeilings(world,farShape,nativeKey,step,mesh);
|
||||
|
||||
out.densitySampleCount=static_cast<std::uint64_t>(std::max<int64>(0,sampleCount));
|
||||
if(bHasWorldAssembly)
|
||||
|
||||
Reference in new issue
Block a user