wip(assembler): M1 round 4 -- cap rebuilt (no hiding), sub-voxel audit, routes + mesh-free queries
Luna, M1 round 4 (resumed after the night stop): - Cliff: sub-voxel audit (29,929 half-voxel XY columns, min run 3.75 voxels, 0 subvoxel runs; old taper control exposes 20,963) and a root-line shelf at the two-voxel minimum. - Horizon slab = aliased LOD8 cap ceiling. The first fix hid the ceiling at LOD8 (challenged: enclosed, interior-lit strates); now the LOD8 cap is rebuilt deterministically from the native heightfield, skirts preserved; looking up and out shows the cap present. - Routes: review manifest with 33 stable-ID route points + capsule/support constraints; capsule sweep 0 blocked samples / 0 unsupported centres, negative control detected. - Server path: World::densityAt answers without render meshes. Physics cooking not available standalone. - Hashes (evaluator + 2,226-tile stream) identical at W1/W8/W16. LOD8: 4.446 s for 529 tiles, 14.25 M field calls, 165 unknown tiles. - Captures at 60 fps cap + vsync (owner's GPU coil whine). One cliff probe crossed the ~8 GB guard (7.69 GiB) and was stopped before capturing. My review: cap view matches legacy closely, no slab; native cliff is a clean attached ledge (cleaner than legacy); a few small spikes on the ridge top. 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
20c6d86f0e
commit
2d0cec90e4
164 files changed
+7503
-31
No files matched your search
@@ -794,7 +794,18 @@ inline void AppendFarConnectorShells(const FStandaloneShapeWorld& Shape,const FV
|
||||
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(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;
|
||||
}
|
||||
|
||||
@@ -1029,5 +1040,17 @@ inline void AppendFarStratumShells(const FStandaloneOwnerWorld& World,const FSta
|
||||
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]);
|
||||
Mesh.Triangles=std::move(CombinedTriangles);Mesh.NumSurfaceVertices=OldSurfaceVertices+AddedVertices;
|
||||
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(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;
|
||||
}
|
||||
@@ -1153,6 +1153,33 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
bool GetNativeSurfaceHeightsAt(float X,float Y,int32 ChunkZ,
|
||||
float& OutGroundZ,float& OutCeilingZ) const
|
||||
{
|
||||
if(!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
|
||||
||!std::isfinite(X)||!std::isfinite(Y))return false;
|
||||
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
!=ECaveGeneratorType::SurfaceWorld)return false;
|
||||
const int32 StrateIndex=Owner->Manager->GetStrateIndex(
|
||||
static_cast<float>((static_cast<double>(ChunkZ*CHUNK_SIZE+CHUNK_SIZE/2))*VOXEL_SIZE));
|
||||
const auto* Plan=FindWorldAssemblyStratum(StrateIndex,ChunkZ);
|
||||
if(!Plan)return false;
|
||||
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double MaxVoxel=static_cast<double>(std::numeric_limits<int64>::max())/Scale;
|
||||
if(std::abs(static_cast<double>(X))>MaxVoxel||std::abs(static_cast<double>(Y))>MaxVoxel)
|
||||
return false;
|
||||
const auto ToFixed=[](float Value)
|
||||
{return static_cast<int64>(std::llround(static_cast<double>(Value)
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));};
|
||||
const VoxelForgeCore::Heightfield2D Block(WorldAssembly->recipe,*Plan);
|
||||
if(!Block.isValid())return false;
|
||||
const auto Column=Block.point(ToFixed(X),ToFixed(Y),
|
||||
static_cast<int64>(ChunkZ)*CHUNK_SIZE*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
OutGroundZ=static_cast<float>(Column.groundHeightSubvoxels/Scale);
|
||||
OutCeilingZ=static_cast<float>(Column.capHeightSubvoxels/Scale);
|
||||
return std::isfinite(OutGroundZ)&&std::isfinite(OutCeilingZ);
|
||||
}
|
||||
|
||||
EVoxelTileClass ClassifyBox(const FIntVector& Origin,int32 Step,int32 Cells) const
|
||||
{ return ClassifyBoxDetailed(Origin,Step,Cells).Verdict; }
|
||||
|
||||
|
||||
@@ -214,6 +214,279 @@ namespace VoxelForgeCore
|
||||
default:return FStandaloneShapeWorld::EDetailVariant::Coherent;
|
||||
}
|
||||
}
|
||||
|
||||
struct FNativeCapVertex
|
||||
{
|
||||
double XSub = 0.0;
|
||||
double YSub = 0.0;
|
||||
double ZSub = 0.0;
|
||||
double GroundSub = 0.0;
|
||||
FVector Normal = FVector(0.0,0.0,-1.0);
|
||||
FColor Color = FColor::White;
|
||||
};
|
||||
|
||||
struct FNativeCapRefinementStats
|
||||
{
|
||||
std::uint64_t Cells = 0;
|
||||
std::uint64_t Triangles = 0;
|
||||
bool Refined = false;
|
||||
};
|
||||
|
||||
FVector NormalizeCapNormal(const FVector& Value)
|
||||
{
|
||||
const double Length=std::sqrt(Value.X*Value.X+Value.Y*Value.Y+Value.Z*Value.Z);
|
||||
return Length>1.0e-12?FVector(Value.X/Length,Value.Y/Length,Value.Z/Length)
|
||||
:FVector(0.0,0.0,-1.0);
|
||||
}
|
||||
|
||||
FNativeCapVertex InterpolateCapVertex(const FNativeCapVertex& A,
|
||||
const FNativeCapVertex& B,double T)
|
||||
{
|
||||
FNativeCapVertex Out;
|
||||
Out.XSub=A.XSub+(B.XSub-A.XSub)*T;
|
||||
Out.YSub=A.YSub+(B.YSub-A.YSub)*T;
|
||||
Out.ZSub=A.ZSub+(B.ZSub-A.ZSub)*T;
|
||||
Out.GroundSub=A.GroundSub+(B.GroundSub-A.GroundSub)*T;
|
||||
Out.Normal=NormalizeCapNormal(A.Normal+(B.Normal-A.Normal)*T);
|
||||
const auto LerpByte=[T](uint8 AValue,uint8 BValue)
|
||||
{return static_cast<uint8>(std::clamp(std::lround(AValue+(BValue-AValue)*T),0l,255l));};
|
||||
Out.Color=FColor(LerpByte(A.Color.R,B.Color.R),LerpByte(A.Color.G,B.Color.G),
|
||||
LerpByte(A.Color.B,B.Color.B),LerpByte(A.Color.A,B.Color.A));
|
||||
return Out;
|
||||
}
|
||||
|
||||
template<typename DistanceFunction>
|
||||
int32 ClipNativeCapPolygon(const FNativeCapVertex* Input,int32 InputCount,
|
||||
FNativeCapVertex* Output,DistanceFunction Distance)
|
||||
{
|
||||
if(InputCount<=0)return 0;
|
||||
int32 OutputCount=0;
|
||||
FNativeCapVertex Previous=Input[InputCount-1];
|
||||
double PreviousDistance=Distance(Previous);
|
||||
bool bPreviousInside=PreviousDistance>=0.0;
|
||||
for(int32 I=0;I<InputCount;++I)
|
||||
{
|
||||
const FNativeCapVertex Current=Input[I];
|
||||
const double CurrentDistance=Distance(Current);
|
||||
const bool bCurrentInside=CurrentDistance>=0.0;
|
||||
if(bCurrentInside!=bPreviousInside)
|
||||
{
|
||||
const double Denominator=PreviousDistance-CurrentDistance;
|
||||
const double T=std::abs(Denominator)>1.0e-15
|
||||
?std::clamp(PreviousDistance/Denominator,0.0,1.0):0.5;
|
||||
if(OutputCount<16)Output[OutputCount++]=InterpolateCapVertex(Previous,Current,T);
|
||||
}
|
||||
if(bCurrentInside&&OutputCount<16)Output[OutputCount++]=Current;
|
||||
Previous=Current;PreviousDistance=CurrentDistance;bPreviousInside=bCurrentInside;
|
||||
}
|
||||
return OutputCount;
|
||||
}
|
||||
|
||||
FNativeCapRefinementStats RefineLod8NativeCap(const FStandaloneOwnerWorld& Owner,
|
||||
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,FVoxelMeshData& Mesh)
|
||||
{
|
||||
FNativeCapRefinementStats Stats;
|
||||
if(Key.Level<8||!Owner.Generator)return Stats;
|
||||
constexpr int32 GridStepVoxels=128;
|
||||
constexpr int64 Scale=WorldAssemblySubvoxelsPerVoxel;
|
||||
constexpr int64 GridStepSubvoxels=GridStepVoxels*Scale;
|
||||
const FIntVector TileOrigin=Key.OriginVoxels();
|
||||
const int32 TileExtent=Key.ExtentVoxels();
|
||||
if(TileExtent<=0||TileExtent%GridStepVoxels!=0)return Stats;
|
||||
const int32 Cells=TileExtent/GridStepVoxels;
|
||||
const int64 TileMinZ=static_cast<int64>(TileOrigin.Z)*Scale;
|
||||
const int64 TileMaxZ=static_cast<int64>(TileOrigin.Z+TileExtent)*Scale;
|
||||
const int32 OldSurfaceVertices=std::clamp(Mesh.NumSurfaceVertices,0,Mesh.Vertices.Num());
|
||||
const bool bHasUVs=Mesh.UVs.Num()==Mesh.Vertices.Num();
|
||||
const bool bHasColors=Mesh.Colors.Num()==Mesh.Vertices.Num();
|
||||
std::vector<FVector> AddedVertices;
|
||||
std::vector<FVector> AddedNormals;
|
||||
std::vector<FVector2f> AddedUVs;
|
||||
std::vector<FColor> AddedColors;
|
||||
std::vector<int32> AddedTriangles;
|
||||
const int32 OldCeilingTriangleCount=std::max(0,Mesh.NumCeilingTriangles);
|
||||
const int32 OldCeilingStart=std::max(0,Mesh.Triangles.Num()-OldCeilingTriangleCount*3);
|
||||
|
||||
for(const auto& Plan:Manifest.plan.strates)
|
||||
{
|
||||
const int64 StratumMinZ=static_cast<int64>(Plan.bottomChunkZ)*CHUNK_SIZE*Scale;
|
||||
const int64 StratumMaxZ=(static_cast<int64>(Plan.topChunkZ)+1)*CHUNK_SIZE*Scale;
|
||||
if(TileMaxZ<StratumMinZ||TileMinZ>StratumMaxZ)continue;
|
||||
const VoxelForgeCore::Heightfield2D Block(Manifest.recipe,Plan);
|
||||
if(!Block.isValid())continue;
|
||||
|
||||
struct FGridSample
|
||||
{
|
||||
double Ground=0.0,Cap=0.0;
|
||||
FVector Normal=FVector(0.0,0.0,-1.0);
|
||||
FColor Color=FColor::White;
|
||||
int32 VertexIndex=INDEX_NONE;
|
||||
};
|
||||
std::vector<FGridSample> Grid(static_cast<std::size_t>(Cells+1)*(Cells+1));
|
||||
const auto GridIndex=[Cells](int32 X,int32 Y)
|
||||
{return static_cast<std::size_t>(Y)*(Cells+1)+X;};
|
||||
for(int32 Y=0;Y<=Cells;++Y)
|
||||
for(int32 X=0;X<=Cells;++X)
|
||||
{
|
||||
const int64 XSub=(static_cast<int64>(TileOrigin.X)+static_cast<int64>(X)*GridStepVoxels)*Scale;
|
||||
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+static_cast<int64>(Y)*GridStepVoxels)*Scale;
|
||||
const auto Column=Block.point(XSub,YSub,0);
|
||||
auto& Sample=Grid[GridIndex(X,Y)];
|
||||
Sample.Ground=static_cast<double>(Column.groundHeightSubvoxels);
|
||||
Sample.Cap=static_cast<double>(Column.capHeightSubvoxels);
|
||||
int32 Dominant=0,Neighbor=0;float Blend=0.0f;
|
||||
Owner.Generator->GetBiomeMaterialAt(static_cast<float>(XSub)/Scale,
|
||||
static_cast<float>(YSub)/Scale,static_cast<float>(Sample.Cap/Scale),
|
||||
Dominant,Neighbor,Blend);
|
||||
const uint8 R=static_cast<uint8>(std::clamp(Dominant,0,255));
|
||||
const uint8 A=static_cast<uint8>(std::clamp(Neighbor,0,255));
|
||||
const uint8 B=static_cast<uint8>(std::clamp(std::lround(Blend*255.0f),0l,255l));
|
||||
Sample.Color=FColor(R,0,B,A);
|
||||
}
|
||||
for(int32 Y=0;Y<=Cells;++Y)
|
||||
for(int32 X=0;X<=Cells;++X)
|
||||
{
|
||||
const int32 Left=std::max(0,X-1),Right=std::min(Cells,X+1);
|
||||
const int32 Down=std::max(0,Y-1),Up=std::min(Cells,Y+1);
|
||||
const double DX=static_cast<double>(Right-Left)*GridStepSubvoxels;
|
||||
const double DY=static_cast<double>(Up-Down)*GridStepSubvoxels;
|
||||
const double SlopeX=DX>0.0?(Grid[GridIndex(Right,Y)].Cap-Grid[GridIndex(Left,Y)].Cap)/DX:0.0;
|
||||
const double SlopeY=DY>0.0?(Grid[GridIndex(X,Up)].Cap-Grid[GridIndex(X,Down)].Cap)/DY:0.0;
|
||||
auto& Sample=Grid[GridIndex(X,Y)];
|
||||
Sample.Normal=NormalizeCapNormal(FVector(SlopeX,SlopeY,-1.0));
|
||||
Sample.Color.G=static_cast<uint8>(std::clamp(
|
||||
std::lround((1.0-std::abs(Sample.Normal.Z))*255.0),0l,255l));
|
||||
}
|
||||
|
||||
// Share regular grid vertices across neighboring cells. Only boundary cells
|
||||
// that cross the solid or tile-Z limit need clipped extra vertices.
|
||||
for(auto& Sample:Grid)
|
||||
{
|
||||
if(Sample.Cap<Sample.Ground||Sample.Cap<TileMinZ||Sample.Cap>TileMaxZ)continue;
|
||||
Sample.VertexIndex=static_cast<int32>(AddedVertices.size());
|
||||
const std::size_t Index=static_cast<std::size_t>(&Sample-Grid.data());
|
||||
const int32 GX=static_cast<int32>(Index%static_cast<std::size_t>(Cells+1));
|
||||
const int32 GY=static_cast<int32>(Index/static_cast<std::size_t>(Cells+1));
|
||||
const double XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
|
||||
const double YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
|
||||
AddedVertices.emplace_back(XSub/Scale*VOXEL_SIZE,YSub/Scale*VOXEL_SIZE,
|
||||
Sample.Cap/Scale*VOXEL_SIZE);
|
||||
AddedNormals.push_back(Sample.Normal);
|
||||
if(bHasUVs)AddedUVs.emplace_back(static_cast<float>(XSub/Scale),
|
||||
static_cast<float>(YSub/Scale));
|
||||
if(bHasColors)AddedColors.push_back(Sample.Color);
|
||||
}
|
||||
|
||||
for(int32 Y=0;Y<Cells;++Y)
|
||||
for(int32 X=0;X<Cells;++X)
|
||||
{
|
||||
const int32 CellX[4]={X,X+1,X+1,X};
|
||||
const int32 CellY[4]={Y,Y,Y+1,Y+1};
|
||||
std::array<int32,4> GridVertices{};
|
||||
bool bCompleteCell=true;
|
||||
for(int32 Corner=0;Corner<4;++Corner)
|
||||
{
|
||||
GridVertices[Corner]=Grid[GridIndex(CellX[Corner],CellY[Corner])].VertexIndex;
|
||||
bCompleteCell=bCompleteCell&&GridVertices[Corner]!=INDEX_NONE;
|
||||
}
|
||||
if(bCompleteCell)
|
||||
{
|
||||
AddedTriangles.insert(AddedTriangles.end(),{GridVertices[0],GridVertices[2],GridVertices[1],
|
||||
GridVertices[0],GridVertices[3],GridVertices[2]});
|
||||
++Stats.Cells;Stats.Triangles+=2;
|
||||
continue;
|
||||
}
|
||||
std::array<FNativeCapVertex,16> PolygonA{},PolygonB{};
|
||||
for(int32 Corner=0;Corner<4;++Corner)
|
||||
{
|
||||
const int32 GX=CellX[Corner],GY=CellY[Corner];
|
||||
const auto& Sample=Grid[GridIndex(GX,GY)];
|
||||
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
|
||||
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
|
||||
PolygonA[Corner].ZSub=Sample.Cap;PolygonA[Corner].GroundSub=Sample.Ground;
|
||||
PolygonA[Corner].Normal=Sample.Normal;PolygonA[Corner].Color=Sample.Color;
|
||||
}
|
||||
int32 Count=ClipNativeCapPolygon(PolygonA.data(),4,PolygonB.data(),
|
||||
[](const auto& V){return V.ZSub-V.GroundSub;});
|
||||
Count=ClipNativeCapPolygon(PolygonB.data(),Count,PolygonA.data(),
|
||||
[TileMinZ](const auto& V){return V.ZSub-static_cast<double>(TileMinZ);});
|
||||
Count=ClipNativeCapPolygon(PolygonA.data(),Count,PolygonB.data(),
|
||||
[TileMaxZ](const auto& V){return static_cast<double>(TileMaxZ)-V.ZSub;});
|
||||
if(Count<3)continue;
|
||||
double TwiceArea=0.0;
|
||||
for(int32 I=0;I<Count;++I)
|
||||
{
|
||||
const auto& A=PolygonB[I];const auto& B=PolygonB[(I+1)%Count];
|
||||
TwiceArea+=A.XSub*B.YSub-B.XSub*A.YSub;
|
||||
}
|
||||
if(std::abs(TwiceArea)<1.0e-5)continue;
|
||||
++Stats.Cells;
|
||||
const int32 Base=static_cast<int32>(AddedVertices.size());
|
||||
for(int32 I=0;I<Count;++I)
|
||||
{
|
||||
const auto& V=PolygonB[I];
|
||||
AddedVertices.emplace_back(V.XSub/Scale*VOXEL_SIZE,
|
||||
V.YSub/Scale*VOXEL_SIZE,V.ZSub/Scale*VOXEL_SIZE);
|
||||
AddedNormals.push_back(V.Normal);
|
||||
if(bHasUVs)AddedUVs.emplace_back(static_cast<float>(V.XSub/Scale),
|
||||
static_cast<float>(V.YSub/Scale));
|
||||
if(bHasColors)AddedColors.push_back(V.Color);
|
||||
}
|
||||
for(int32 Fan=1;Fan+1<Count;++Fan)
|
||||
{
|
||||
// XY cells are counter-clockwise; reverse each fan triangle so the
|
||||
// ceiling's outward normal faces down into the enclosed strate.
|
||||
AddedTriangles.insert(AddedTriangles.end(),{Base,Base+Fan+1,Base+Fan});
|
||||
++Stats.Triangles;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(AddedTriangles.empty())return Stats;
|
||||
|
||||
const int32 AddedVertexCount=static_cast<int32>(AddedVertices.size());
|
||||
auto InsertSurfaceVertices=[&](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);
|
||||
};
|
||||
InsertSurfaceVertices(Mesh.Vertices,AddedVertices);
|
||||
InsertSurfaceVertices(Mesh.Normals,AddedNormals);
|
||||
if(bHasUVs)InsertSurfaceVertices(Mesh.UVs,AddedUVs);
|
||||
if(bHasColors)InsertSurfaceVertices(Mesh.Colors,AddedColors);
|
||||
for(int32& Index:Mesh.Triangles)if(Index>=OldSurfaceVertices)Index+=AddedVertexCount;
|
||||
|
||||
const int32 OldTriangleCount=Mesh.Triangles.Num()/3;
|
||||
TArray<uint8> OldMasks=Mesh.TriangleSkirtFaceMasks;
|
||||
OldMasks.SetNum(OldTriangleCount);
|
||||
TArray<int32> CombinedTriangles;CombinedTriangles.Reserve(
|
||||
OldCeilingStart+OldCeilingTriangleCount*3+static_cast<int32>(AddedTriangles.size()));
|
||||
TArray<uint8> CombinedMasks;CombinedMasks.Reserve(
|
||||
OldTriangleCount+static_cast<int32>(AddedTriangles.size()/3));
|
||||
for(int32 Triangle=0;Triangle<OldCeilingStart/3;++Triangle)
|
||||
{
|
||||
for(int32 Corner=0;Corner<3;++Corner)CombinedTriangles.Add(Mesh.Triangles[Triangle*3+Corner]);
|
||||
CombinedMasks.Add(OldMasks[Triangle]);
|
||||
}
|
||||
uint32 PreservedCeilingTriangles=0;
|
||||
for(int32 Triangle=OldCeilingStart/3;Triangle<OldTriangleCount;++Triangle)
|
||||
{
|
||||
if(OldMasks[Triangle]==0)continue;
|
||||
for(int32 Corner=0;Corner<3;++Corner)CombinedTriangles.Add(Mesh.Triangles[Triangle*3+Corner]);
|
||||
CombinedMasks.Add(OldMasks[Triangle]);++PreservedCeilingTriangles;
|
||||
}
|
||||
for(const int32 Index:AddedTriangles)CombinedTriangles.Add(OldSurfaceVertices+Index);
|
||||
for(std::size_t I=0;I<AddedTriangles.size()/3;++I)CombinedMasks.Add(0);
|
||||
Mesh.Triangles=std::move(CombinedTriangles);
|
||||
Mesh.TriangleSkirtFaceMasks=std::move(CombinedMasks);
|
||||
Mesh.NumSurfaceVertices=OldSurfaceVertices+AddedVertexCount;
|
||||
Mesh.NumCeilingTriangles=static_cast<int32>(PreservedCeilingTriangles+Stats.Triangles);
|
||||
Stats.Refined=true;
|
||||
return Stats;
|
||||
}
|
||||
}
|
||||
|
||||
struct detail_StandaloneSnapshotAdapter
|
||||
@@ -721,17 +994,12 @@ namespace VoxelForgeCore
|
||||
AppendFarStratumShells(world,farShape,nativeKey,step,mesh);
|
||||
AppendFarConnectorShells(farShape,nativeKey,step,world.CoarseTileCells,mesh);
|
||||
}
|
||||
if(bHasWorldAssembly&&key.lod>=8&&mesh.NumCeilingTriangles>0)
|
||||
if(impl_->worldAssembly&&key.lod>=8)
|
||||
{
|
||||
// The finite sky cap remains in the composed field, queries, and local meshes.
|
||||
// At the coarsest clipmap level its large planar triangles read as a detached
|
||||
// horizon slab, so omit only that far render group after far-shell construction.
|
||||
const int32 KeptTriangleCount=std::max(0,mesh.Triangles.Num()/3-mesh.NumCeilingTriangles);
|
||||
const int32 KeptIndexCount=KeptTriangleCount*3;
|
||||
out.diagnostics.farCapTrianglesSuppressed=static_cast<std::uint64_t>(mesh.NumCeilingTriangles);
|
||||
mesh.Triangles.SetNum(KeptIndexCount);
|
||||
mesh.TriangleSkirtFaceMasks.SetNum(KeptTriangleCount);
|
||||
mesh.NumCeilingTriangles=0;
|
||||
const auto CapStats=RefineLod8NativeCap(world,*impl_->worldAssembly,nativeKey,mesh);
|
||||
out.diagnostics.capRefined=CapStats.Refined;
|
||||
out.diagnostics.capRefinedCells=CapStats.Cells;
|
||||
out.diagnostics.capRefinedTriangles=CapStats.Triangles;
|
||||
}
|
||||
|
||||
out.densitySampleCount=static_cast<std::uint64_t>(std::max<int64>(0,sampleCount));
|
||||
|
||||
Reference in new issue
Block a user