wip(assembler): round S4 -- continuous far ceiling at the strate's upper boundary

S3's far cap sat up to 8.2 m above the near roof and read as floating pieces.
The far render cap now projects onto the owning strate's upper boundary
(density unchanged), on a global XY lattice, LOD4 16-voxel grid + LOD5-8
128-voxel. Cap agreement 0.05 cm (100 cm control detected), 2520/2520 upward
rays hit roof on the owner walk. Far recipe v2; LOD0-3 hashes equal S2/S3.
Tile p95 flat; cold clipmap +4.4%. Far-cliff fluting left open (coarse MC step).

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-19 00:41:13 +02:00
1 parent d0f97bf79a
commit 9d3c50ca95
14 files changed
+1376 -34

No files matched your search

+14 -4
View File
@@ -24,6 +24,7 @@
#include "VoxelClipmapDesiredTiles.h"
#include "VoxelCollisionDesiredTiles.h"
#include "VoxelPreEditDensityCache.h"
#include "RealtimeMeshLibrary.h"
// IWYU (FPSemantics = Precise ⇒ plus de PCH partagé) : GetPlayerPosition déréférence le pawn, donc
// APawn doit être COMPLET — `Casts.h` n'en donne qu'une déclaration avant. APlayerController n'est
// complet que par transitivité : on l'inclut explicitement.
@@ -12471,21 +12472,30 @@ bool AVoxelWorld::ApplyMeshToTile(FChunkResult& Result)
// (default config already maps material slot = polygroup index); only config sections that
// exist — the bHas* flags come from the worker. Collision at level 0 only (T1.c), both groups.
const uint64 CollisionConfigStartCycles = bProfileApply ? FPlatformTime::Cycles64() : 0;
const int32 NumIndices = Result.NumTriangles * 3;
const int32 FirstCapIndex = FMath::Clamp(
NumIndices - Result.NumCeilingTriangles * 3, 0, NumIndices);
if (bHasGroundTris)
{
FRealtimeMeshSectionConfig GroundConfig(0);
GroundConfig.bCastsShadow = bCastShadow;
RTMesh->UpdateSectionConfig(
const FRealtimeMeshStreamRange GroundRange =
URealtimeMeshBlueprintFunctionLibrary::MakeStreamRange(
0, Result.NumVertices, 0, FirstCapIndex);
RTMesh->CreateSection(
FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 0),
GroundConfig, /*bShouldCreateCollision*/ bWantsCollision);
GroundConfig, GroundRange, /*bShouldCreateCollision*/ bWantsCollision);
}
if (bHasCeilingTris)
{
FRealtimeMeshSectionConfig CapConfig(1);
CapConfig.bCastsShadow = false; // the cap never casts (see above)
RTMesh->UpdateSectionConfig(
const FRealtimeMeshStreamRange CapRange =
URealtimeMeshBlueprintFunctionLibrary::MakeStreamRange(
0, Result.NumVertices, FirstCapIndex, NumIndices);
RTMesh->CreateSection(
FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 1),
CapConfig, /*bShouldCreateCollision*/ bWantsCollision);
CapConfig, CapRange, /*bShouldCreateCollision*/ bWantsCollision);
}
const double CollisionConfigSeconds = bProfileApply
? FPlatformTime::ToSeconds64(FPlatformTime::Cycles64() - CollisionConfigStartCycles)
@@ -15221,7 +15221,8 @@ bool RunCoreFarHorizonAgreementAudit(VoxelForgeCore::World& World,
if(Other==Ground8.ByXY.end())continue;
const auto Column=Field.point(Pair.first.first,Pair.first.second,0);
const std::int64_t Ground=Column.groundHeightSubvoxels;
const std::int64_t Cap=Column.capHeightSubvoxels;
const std::int64_t Cap=Field.farSurfaceCapHeightSubvoxels(
Pair.first.first,Pair.first.second);
if(Ground>=Cap)continue;
const double GroundVoxel=static_cast<double>(Ground)/SubvoxelsPerVoxel;
if(GroundVoxel<Bounds7[2]||GroundVoxel>Bounds7[5]
@@ -15263,7 +15264,8 @@ bool RunCoreFarHorizonAgreementAudit(VoxelForgeCore::World& World,
const std::int64_t YSub=static_cast<std::int64_t>(std::llround(
static_cast<double>(RouteYSub)/GridPeriod))*GridPeriod;
const auto WalkColumn=Field.point(XSub,YSub,0);
if(WalkColumn.groundHeightSubvoxels>=WalkColumn.capHeightSubvoxels)continue;
const std::int64_t WalkCap=Field.farSurfaceCapHeightSubvoxels(XSub,YSub);
if(WalkColumn.groundHeightSubvoxels>=WalkCap)continue;
const std::int64_t XVoxel=XSub/SubvoxelsPerVoxel,YVoxel=YSub/SubvoxelsPerVoxel;
const std::int64_t WalkGroundVoxel=WalkColumn.groundHeightSubvoxels/SubvoxelsPerVoxel;
const auto MakeKey=[](std::int64_t X,std::int64_t Y,std::int64_t Z,std::int64_t Extent,int32 LOD)
@@ -15293,7 +15295,8 @@ bool RunCoreFarHorizonAgreementAudit(VoxelForgeCore::World& World,
if(Other==Ground8.ByXY.end())continue;
const auto Column=Field.point(Pair.first.first,Pair.first.second,0);
const std::int64_t Ground=Column.groundHeightSubvoxels;
if(Ground>=Column.capHeightSubvoxels)continue;
if(Ground>=Field.farSurfaceCapHeightSubvoxels(
Pair.first.first,Pair.first.second))continue;
const auto Bounds7=TileBounds(Key7),Bounds8=TileBounds(Key8);
const double GroundVoxel=static_cast<double>(Ground)/SubvoxelsPerVoxel;
if(GroundVoxel<Bounds7[2]||GroundVoxel>Bounds7[5]
@@ -15373,6 +15376,554 @@ bool RunCoreFarHorizonAgreementAudit(VoxelForgeCore::World& World,
return true;
}
bool RunCoreFarCapTileCensus(VoxelForgeCore::World& World,
const VoxelForgeCore::WorldAssemblyManifest& Assembly,FString& OutError)
{
OutError.Reset();
constexpr std::int64_t SubvoxelsPerVoxel=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t ProfileGridStep=128;
using FTileId=std::array<std::int32_t,4>;
using FXYLod=std::array<std::int32_t,3>;
const auto IdFor=[](const VoxelForgeCore::TileKey& Key)
{return FTileId{Key.coordinate.x,Key.coordinate.y,Key.coordinate.z,Key.lod};};
const auto FloorDiv64=[](std::int64_t Value,std::int64_t Divisor)
{
const std::int64_t Quotient=Value/Divisor, Remainder=Value%Divisor;
return Quotient-((Remainder!=0&&Remainder<0)?1:0);
};
const VoxelForgeCore::WorldAssemblyStratumPlanV1* Plan=nullptr;
for(const auto& Candidate:Assembly.plan.strates)
if(!Candidate.route.empty()) {Plan=&Candidate;break;}
if(Plan==nullptr)
{OutError=TEXT("Far-cap census has no SurfaceWorld walk route.");return false;}
const VoxelForgeCore::Heightfield2D Field(Assembly.recipe,*Plan);
if(!Field.isValid())
{OutError=TEXT("Far-cap census received an invalid SurfaceWorld heightfield.");return false;}
const auto& WalkStart=Plan->route.front();
const VoxelForgeCore::Vec3d Camera{
static_cast<double>(WalkStart.positionSubvoxels[0])/SubvoxelsPerVoxel,
static_cast<double>(WalkStart.positionSubvoxels[1])/SubvoxelsPerVoxel,
static_cast<double>(WalkStart.positionSubvoxels[2])/SubvoxelsPerVoxel};
const std::vector<VoxelForgeCore::TileKey> WalkKeys=World.desiredTiles(Camera);
std::set<FTileId> Desired;
std::set<FXYLod> FarXYTiles;
double ClipMaxZ=-std::numeric_limits<double>::infinity();
for(const auto& Key:WalkKeys)
{
Desired.insert(IdFor(Key));
const double Extent=static_cast<double>(32<<Key.lod);
ClipMaxZ=std::max(ClipMaxZ,(static_cast<double>(Key.coordinate.z)+1.0)*Extent);
if(Key.lod>=5&&Key.lod<=8)
FarXYTiles.insert({Key.coordinate.x,Key.coordinate.y,Key.lod});
}
std::set<FTileId> CapBands;
std::uint64_t ProfileGridSamples=0, ProfileSamplesWithoutRoof=0;
std::set<FXYLod> FarXYTilesWithoutRoof;
for(const FXYLod& XY:FarXYTiles)
{
const std::int32_t Lod=XY[2];
const std::int64_t Extent=static_cast<std::int64_t>(32)<<Lod;
const std::int64_t OriginX=static_cast<std::int64_t>(XY[0])*Extent;
const std::int64_t OriginY=static_cast<std::int64_t>(XY[1])*Extent;
for(std::int64_t Y=0;Y<=Extent;Y+=ProfileGridStep)
for(std::int64_t X=0;X<=Extent;X+=ProfileGridStep)
{
++ProfileGridSamples;
const auto Column=Field.point((OriginX+X)*SubvoxelsPerVoxel,
(OriginY+Y)*SubvoxelsPerVoxel,0);
const std::int64_t FarCap=Field.farSurfaceCapHeightSubvoxels(
(OriginX+X)*SubvoxelsPerVoxel,(OriginY+Y)*SubvoxelsPerVoxel);
if(FarCap<=Column.groundHeightSubvoxels)
{
++ProfileSamplesWithoutRoof;
FarXYTilesWithoutRoof.insert(XY);
continue;
}
const std::int64_t Band=FloorDiv64(FarCap,
Extent*SubvoxelsPerVoxel);
if(Band>=MIN_int32&&Band<=MAX_int32)
CapBands.insert({XY[0],XY[1],static_cast<std::int32_t>(Band),Lod});
if(FarCap%(Extent*SubvoxelsPerVoxel)==0
&&Band>MIN_int32)
CapBands.insert({XY[0],XY[1],static_cast<std::int32_t>(Band-1),Lod});
}
}
std::uint64_t RequestedCapBands=0, EmittedCapBands=0, RequestedWithoutTriangles=0;
std::uint64_t CulledCapBands=0;
std::map<std::int32_t,std::array<std::uint64_t,4>> ByLod;
VoxelForgeCore::TileGenerationOptions Options;
std::string CoreError;
struct FCapAgreementSample
{ std::int64_t XSub=0,YSub=0;double FarZVoxel=0.0; };
using FCapXYKey=std::pair<std::int64_t,std::int64_t>;
std::set<FCapXYKey> CapAgreementSeen;
std::vector<FCapAgreementSample> CapAgreementSamples;
CapAgreementSamples.reserve(128);
const double VoxelSizeCm=static_cast<double>(World.configuration().voxelSizeMicrons)/10000.0;
constexpr std::int64_t CapGridPeriod=128*SubvoxelsPerVoxel;
for(const FTileId& Id:CapBands)
{
const std::int32_t Lod=Id[3];
auto& Counts=ByLod[Lod];
++Counts[0];
if(Desired.find(Id)==Desired.end())
{
++CulledCapBands;++Counts[1];continue;
}
++RequestedCapBands;
VoxelForgeCore::TileKey Key{{Id[0],Id[1],Id[2]},Lod};
const auto Mesh=World.generateTile(Key,Options,CoreError);
if(!Mesh)
{
OutError=FString::Printf(TEXT("Far-cap census tile generation failed at (%d,%d,%d) LOD%d: %s"),
Id[0],Id[1],Id[2],Lod,UTF8_TO_TCHAR(CoreError.c_str()));
return false;
}
if(Mesh->diagnostics.horizonCapTriangles>0)
{
++EmittedCapBands;++Counts[2];
const std::size_t CapIndexStart=Mesh->indices.size()
-static_cast<std::size_t>(Mesh->diagnostics.horizonCapTriangles)*3u;
for(std::size_t Index=CapIndexStart;Index<Mesh->indices.size()
&&CapAgreementSamples.size()<128;++Index)
{
const std::uint32_t VertexIndex=Mesh->indices[Index];
if(VertexIndex>=Mesh->vertices.size())continue;
const auto& Vertex=Mesh->vertices[VertexIndex];
if(Vertex.normal.z>=-0.35)continue;
const std::int64_t XSub=static_cast<std::int64_t>(std::llround(
Vertex.position.x/VoxelSizeCm*SubvoxelsPerVoxel));
const std::int64_t YSub=static_cast<std::int64_t>(std::llround(
Vertex.position.y/VoxelSizeCm*SubvoxelsPerVoxel));
if(XSub%CapGridPeriod!=0||YSub%CapGridPeriod!=0)continue;
if(!CapAgreementSeen.insert({XSub,YSub}).second)continue;
CapAgreementSamples.push_back({XSub,YSub,
Vertex.position.z/VoxelSizeCm});
}
}
else
{++RequestedWithoutTriangles;++Counts[3];}
}
constexpr double CapAgreementToleranceCm=2.0;
constexpr double CapNegativeDatumOffsetCm=100.0;
const auto FindNearCapRoot=[&](std::int64_t XSub,std::int64_t YSub,
double ProfileCapVoxel,double& OutRootVoxel)
{
const double X=static_cast<double>(XSub)/SubvoxelsPerVoxel;
const double Y=static_cast<double>(YSub)/SubvoxelsPerVoxel;
for(double Radius=4.0;Radius<=4096.0;Radius*=2.0)
{
double Lo=ProfileCapVoxel-Radius,Hi=ProfileCapVoxel+Radius;
if(!(World.densityAt({X,Y,Lo})>=0.0f&&World.densityAt({X,Y,Hi})<0.0f))continue;
for(int Iteration=0;Iteration<16;++Iteration)
{
const double Mid=(Lo+Hi)*0.5;
if(World.densityAt({X,Y,Mid})>=0.0f)Lo=Mid;else Hi=Mid;
}
OutRootVoxel=(Lo+Hi)*0.5;
return true;
}
return false;
};
double MaximumCapAgreementErrorCm=0.0,MaximumCapNegativeControlErrorCm=0.0;
std::uint64_t CapAgreementCompared=0;
for(const FCapAgreementSample& Sample:CapAgreementSamples)
{
const auto Column=Field.point(Sample.XSub,Sample.YSub,0);
const std::int64_t FarCap=Field.farSurfaceCapHeightSubvoxels(
Sample.XSub,Sample.YSub);
if(FarCap<=Column.groundHeightSubvoxels)continue;
double NearRootVoxel=0.0;
if(!FindNearCapRoot(Sample.XSub,Sample.YSub,
static_cast<double>(FarCap)/SubvoxelsPerVoxel,NearRootVoxel))continue;
const double FarCapCm=Sample.FarZVoxel*VoxelSizeCm;
const double NearCapCm=NearRootVoxel*VoxelSizeCm;
MaximumCapAgreementErrorCm=std::max(MaximumCapAgreementErrorCm,
std::abs(FarCapCm-NearCapCm));
MaximumCapNegativeControlErrorCm=std::max(MaximumCapNegativeControlErrorCm,
std::abs(FarCapCm+CapNegativeDatumOffsetCm-NearCapCm));
++CapAgreementCompared;
}
const bool bCapNegativeControlDetected=CapAgreementCompared>0
&&MaximumCapNegativeControlErrorCm>CapAgreementToleranceCm;
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeFarCapAgreement] status=%s shared_points=%llu max_far_near_cm=%.4f tolerance_cm=%.2f near_source=exact_density_root negative_control=100cm_datum_offset max_error_cm=%.4f detected=%d"),
CapAgreementCompared>=32&&MaximumCapAgreementErrorCm<=CapAgreementToleranceCm
&&bCapNegativeControlDetected?TEXT("PASS"):TEXT("FAIL"),
static_cast<unsigned long long>(CapAgreementCompared),MaximumCapAgreementErrorCm,
CapAgreementToleranceCm,MaximumCapNegativeControlErrorCm,bCapNegativeControlDetected?1:0);
if(CapAgreementCompared<32||MaximumCapAgreementErrorCm>CapAgreementToleranceCm
||!bCapNegativeControlDetected)
{
OutError=FString::Printf(TEXT("Far-cap agreement failed: points=%llu max_error_cm=%.4f tolerance_cm=%.2f negative_control_cm=%.4f detected=%d."),
static_cast<unsigned long long>(CapAgreementCompared),MaximumCapAgreementErrorCm,
CapAgreementToleranceCm,MaximumCapNegativeControlErrorCm,bCapNegativeControlDetected?1:0);
return false;
}
// Les rayons couvrent l'hemisphere superieur depuis chaque point de marche.
// Cast into the upper view hemisphere from every authored walk point.
constexpr double Pi=3.14159265358979323846;
constexpr std::array<double,8> AzimuthDegrees={0.0,45.0,90.0,135.0,180.0,225.0,270.0,315.0};
constexpr std::array<double,6> ElevationDegrees={15.0,30.0,45.0,60.0,75.0,90.0};
constexpr double RayStepVoxels=128.0;
double ClipMinX=std::numeric_limits<double>::infinity();
double ClipMinY=std::numeric_limits<double>::infinity();
double ClipMaxX=-std::numeric_limits<double>::infinity();
double ClipMaxY=-std::numeric_limits<double>::infinity();
for(const FXYLod& XY:FarXYTiles)
{
const double Extent=static_cast<double>(32<<XY[2]);
ClipMinX=std::min(ClipMinX,static_cast<double>(XY[0])*Extent);
ClipMinY=std::min(ClipMinY,static_cast<double>(XY[1])*Extent);
ClipMaxX=std::max(ClipMaxX,(static_cast<double>(XY[0])+1.0)*Extent);
ClipMaxY=std::max(ClipMaxY,(static_cast<double>(XY[1])+1.0)*Extent);
}
enum class ECapRayTileResult : std::uint8_t
{ EmittedHit, CulledByClipmap, RequestedWithoutTriangles, EmittedWithoutHit };
struct FCapRayTileGeometry
{
std::vector<std::array<VoxelForgeCore::Vec3d,3>> Triangles;
};
std::uint64_t RayCount=0, RayHits=0, RayMissedClipEdge=0;
std::uint64_t RayMissedNoRoof=0, RayMissedCulledByClipmap=0;
std::uint64_t RayMissedRequestedWithoutTriangles=0, RayMissedEmittedWithoutHit=0;
std::uint64_t RayStartedAboveCap=0;
std::uint64_t RayProfileCrossings=0,RayProfileRoofCrossings=0,RayProfileRoofWithoutMeshHit=0;
std::array<std::array<std::uint64_t,6>,6> RayResultsByElevation{};
std::vector<double> RayHitDistancesMeters;
RayHitDistancesMeters.reserve(Plan->route.size()*AzimuthDegrees.size()*ElevationDegrees.size());
std::map<FTileId,FCapRayTileGeometry> RayTileGeometryCache;
const auto GetCapRayTileGeometry=[&](const FTileId& Id)->const FCapRayTileGeometry*
{
auto Found=RayTileGeometryCache.find(Id);
if(Found!=RayTileGeometryCache.end())return &Found->second;
const VoxelForgeCore::TileKey Key{{Id[0],Id[1],Id[2]},Id[3]};
const auto Mesh=World.generateTile(Key,Options,CoreError);
if(!Mesh)
{
OutError=FString::Printf(TEXT("Far-cap ray tile generation failed at (%d,%d,%d) LOD%d: %s"),
Id[0],Id[1],Id[2],Id[3],UTF8_TO_TCHAR(CoreError.c_str()));
return nullptr;
}
FCapRayTileGeometry Geometry;
Geometry.Triangles.reserve(Mesh->groups.ceilingTriangleCount);
for(std::size_t Index=0;Index+2<Mesh->indices.size();Index+=3)
{
const std::uint32_t IA=Mesh->indices[Index],IB=Mesh->indices[Index+1],IC=Mesh->indices[Index+2];
if(IA>=Mesh->vertices.size()||IB>=Mesh->vertices.size()||IC>=Mesh->vertices.size())continue;
const double AverageNormalZ=(Mesh->vertices[IA].normal.z+Mesh->vertices[IB].normal.z
+Mesh->vertices[IC].normal.z)/3.0;
// Native marching-cubes roofs and refined far caps both face out of the solid
// toward decreasing Z density. Include those ceiling faces at LOD0-8.
// Les plafonds MC natifs et les caps lointains pointent hors du solide, vers -Z.
if(AverageNormalZ>=-0.05)continue;
Geometry.Triangles.push_back({
VoxelForgeCore::Vec3d{Mesh->vertices[IA].position.x/VoxelSizeCm,
Mesh->vertices[IA].position.y/VoxelSizeCm,Mesh->vertices[IA].position.z/VoxelSizeCm},
VoxelForgeCore::Vec3d{Mesh->vertices[IB].position.x/VoxelSizeCm,
Mesh->vertices[IB].position.y/VoxelSizeCm,Mesh->vertices[IB].position.z/VoxelSizeCm},
VoxelForgeCore::Vec3d{Mesh->vertices[IC].position.x/VoxelSizeCm,
Mesh->vertices[IC].position.y/VoxelSizeCm,Mesh->vertices[IC].position.z/VoxelSizeCm}});
}
return &RayTileGeometryCache.emplace(Id,std::move(Geometry)).first->second;
};
const auto Dot3=[](const VoxelForgeCore::Vec3d& A,const VoxelForgeCore::Vec3d& B)
{return A.x*B.x+A.y*B.y+A.z*B.z;};
const auto Sub3=[](const VoxelForgeCore::Vec3d& A,const VoxelForgeCore::Vec3d& B)
{return VoxelForgeCore::Vec3d{A.x-B.x,A.y-B.y,A.z-B.z};};
const auto Cross3=[](const VoxelForgeCore::Vec3d& A,const VoxelForgeCore::Vec3d& B)
{return VoxelForgeCore::Vec3d{A.y*B.z-A.z*B.y,A.z*B.x-A.x*B.z,A.x*B.y-A.y*B.x};};
const auto RayTriangleDistance=[&](const VoxelForgeCore::Vec3d& Origin,
const VoxelForgeCore::Vec3d& Direction,const std::array<VoxelForgeCore::Vec3d,3>& Triangle,
double MinimumDistance,double MaximumDistance,double& OutDistance)
{
const auto Edge1=Sub3(Triangle[1],Triangle[0]);
const auto Edge2=Sub3(Triangle[2],Triangle[0]);
const auto P=Cross3(Direction,Edge2);
const double Determinant=Dot3(Edge1,P);
if(std::abs(Determinant)<1.0e-12)return false;
const double Inverse=1.0/Determinant;
const auto T=Sub3(Origin,Triangle[0]);
const double U=Dot3(T,P)*Inverse;
if(U< -1.0e-8||U>1.0+1.0e-8)return false;
const auto Q=Cross3(T,Edge1);
const double V=Dot3(Direction,Q)*Inverse;
if(V< -1.0e-8||U+V>1.0+1.0e-8)return false;
const double Distance=Dot3(Edge2,Q)*Inverse;
if(Distance<MinimumDistance-1.0e-6||Distance>MaximumDistance+1.0e-6)return false;
OutDistance=Distance;
return true;
};
const auto TraceEmittedCapRay=[&](const VoxelForgeCore::Vec3d& Origin,
const VoxelForgeCore::Vec3d& Direction,double MaximumDistance,double& OutHitDistance)
{
bool bFoundRequestedKey=false,bFoundEmptyRequestedKey=false,bFoundGeometryWithoutHit=false;
bool bFoundHit=false;
for(const FTileId& Id:Desired)
{
const double Extent=static_cast<double>(32<<Id[3]);
const std::array<double,3> BoundsMin={static_cast<double>(Id[0])*Extent,
static_cast<double>(Id[1])*Extent,static_cast<double>(Id[2])*Extent};
const std::array<double,3> BoundsMax={BoundsMin[0]+Extent,BoundsMin[1]+Extent,
BoundsMin[2]+Extent};
double TileEnter=0.0,TileExit=MaximumDistance;
bool bIntersects=true;
for(std::size_t Axis=0;Axis<3;++Axis)
{
const double RayOrigin=Axis==0?Origin.x:(Axis==1?Origin.y:Origin.z);
const double RayDirection=Axis==0?Direction.x:(Axis==1?Direction.y:Direction.z);
if(std::abs(RayDirection)<1.0e-12)
{
if(RayOrigin<BoundsMin[Axis]||RayOrigin>BoundsMax[Axis])
{bIntersects=false;break;}
continue;
}
double Near=(BoundsMin[Axis]-RayOrigin)/RayDirection;
double Far=(BoundsMax[Axis]-RayOrigin)/RayDirection;
if(Near>Far)std::swap(Near,Far);
TileEnter=std::max(TileEnter,Near);
TileExit=std::min(TileExit,Far);
if(TileEnter>TileExit){bIntersects=false;break;}
}
if(!bIntersects||TileExit<0.0||TileEnter>MaximumDistance)continue;
bFoundRequestedKey=true;
const FCapRayTileGeometry* Geometry=GetCapRayTileGeometry(Id);
if(Geometry==nullptr)continue;
if(Geometry->Triangles.empty())
{bFoundEmptyRequestedKey=true;continue;}
bFoundGeometryWithoutHit=true;
for(const auto& Triangle:Geometry->Triangles)
{
double HitDistance=0.0;
if(!RayTriangleDistance(Origin,Direction,Triangle,
std::max(0.0,TileEnter),std::min(MaximumDistance,TileExit),HitDistance))continue;
if(!bFoundHit||HitDistance<OutHitDistance)OutHitDistance=HitDistance;
bFoundHit=true;
}
if(!OutError.IsEmpty())return ECapRayTileResult::RequestedWithoutTriangles;
}
if(bFoundHit)return ECapRayTileResult::EmittedHit;
if(bFoundRequestedKey&&bFoundGeometryWithoutHit)return ECapRayTileResult::EmittedWithoutHit;
if(bFoundRequestedKey&&bFoundEmptyRequestedKey)return ECapRayTileResult::RequestedWithoutTriangles;
return ECapRayTileResult::CulledByClipmap;
};
for(std::size_t RouteIndex=0;RouteIndex<Plan->route.size();++RouteIndex)
{
const auto& RoutePoint=Plan->route[RouteIndex];
const double StartX=static_cast<double>(RoutePoint.positionSubvoxels[0])/SubvoxelsPerVoxel;
const double StartY=static_cast<double>(RoutePoint.positionSubvoxels[1])/SubvoxelsPerVoxel;
const double StartZ=static_cast<double>(RoutePoint.positionSubvoxels[2])/SubvoxelsPerVoxel;
const auto StartColumn=Field.point(RoutePoint.positionSubvoxels[0],
RoutePoint.positionSubvoxels[1],0);
const double StartCap=static_cast<double>(Field.farSurfaceCapHeightSubvoxels(
RoutePoint.positionSubvoxels[0],RoutePoint.positionSubvoxels[1]))/SubvoxelsPerVoxel;
for(const double Elevation:ElevationDegrees)
for(const double Azimuth:AzimuthDegrees)
{
++RayCount;
const std::size_t ElevationIndex=static_cast<std::size_t>(
std::find(ElevationDegrees.begin(),ElevationDegrees.end(),Elevation)
-ElevationDegrees.begin());
if(!(StartCap>StartZ))
{++RayStartedAboveCap;++RayMissedNoRoof;++RayResultsByElevation[ElevationIndex][4];continue;}
const double ElevationRadians=Elevation*Pi/180.0;
const double AzimuthRadians=Azimuth*Pi/180.0;
const double DX=std::cos(ElevationRadians)*std::cos(AzimuthRadians);
const double DY=std::cos(ElevationRadians)*std::sin(AzimuthRadians);
const double DZ=std::sin(ElevationRadians);
double EdgeDistance=std::numeric_limits<double>::infinity();
if(DX>1.0e-9)EdgeDistance=std::min(EdgeDistance,(ClipMaxX-StartX)/DX);
else if(DX< -1.0e-9)EdgeDistance=std::min(EdgeDistance,(ClipMinX-StartX)/DX);
if(DY>1.0e-9)EdgeDistance=std::min(EdgeDistance,(ClipMaxY-StartY)/DY);
else if(DY< -1.0e-9)EdgeDistance=std::min(EdgeDistance,(ClipMinY-StartY)/DY);
if(DZ>1.0e-9)
EdgeDistance=std::min(EdgeDistance,(ClipMaxZ-StartZ)/DZ);
if(!std::isfinite(EdgeDistance)||EdgeDistance<=0.0)
{++RayMissedClipEdge;++RayResultsByElevation[ElevationIndex][5];continue;}
bool bProfileCrossing=false,bProfileRoofAtCrossing=false;
double ProfileHitDistance=std::numeric_limits<double>::infinity();
double ProfileHitX=0.0,ProfileHitY=0.0,ProfileHitZ=0.0;
std::int64_t ProfileHitCapSubvoxels=0;
double PreviousDistance=0.0,PreviousDelta=StartZ-StartCap;
for(double Distance=std::min(RayStepVoxels,EdgeDistance);;)
{
const double X=StartX+DX*Distance,Y=StartY+DY*Distance;
const double Z=StartZ+DZ*Distance;
const std::int64_t CapSubvoxels=Field.farSurfaceCapHeightSubvoxels(
static_cast<std::int64_t>(std::llround(X*SubvoxelsPerVoxel)),
static_cast<std::int64_t>(std::llround(Y*SubvoxelsPerVoxel)));
const double Cap=static_cast<double>(CapSubvoxels)/SubvoxelsPerVoxel;
const double Delta=Z-Cap;
if(PreviousDelta<0.0&&Delta>=0.0)
{
const double Fraction=-PreviousDelta/(Delta-PreviousDelta);
ProfileHitDistance=PreviousDistance+(Distance-PreviousDistance)*Fraction;
const double HitX=StartX+DX*ProfileHitDistance;
const double HitY=StartY+DY*ProfileHitDistance;
const auto HitColumn=Field.point(
static_cast<std::int64_t>(std::llround(HitX*SubvoxelsPerVoxel)),
static_cast<std::int64_t>(std::llround(HitY*SubvoxelsPerVoxel)),0);
const std::int64_t HitCapSubvoxels=Field.farSurfaceCapHeightSubvoxels(
static_cast<std::int64_t>(std::llround(HitX*SubvoxelsPerVoxel)),
static_cast<std::int64_t>(std::llround(HitY*SubvoxelsPerVoxel)));
bProfileCrossing=true;
bProfileRoofAtCrossing=HitCapSubvoxels>HitColumn.groundHeightSubvoxels;
ProfileHitX=HitX;ProfileHitY=HitY;
ProfileHitZ=StartZ+DZ*ProfileHitDistance;
ProfileHitCapSubvoxels=HitCapSubvoxels;
++RayProfileCrossings;
if(bProfileRoofAtCrossing)++RayProfileRoofCrossings;
break;
}
PreviousDistance=Distance;PreviousDelta=Delta;
if(Distance>=EdgeDistance)break;
Distance=std::min(Distance+RayStepVoxels,EdgeDistance);
}
const VoxelForgeCore::Vec3d RayOrigin{StartX,StartY,StartZ};
const VoxelForgeCore::Vec3d RayDirection{DX,DY,DZ};
double MeshHitDistance=std::numeric_limits<double>::infinity();
const ECapRayTileResult TileResult=TraceEmittedCapRay(
RayOrigin,RayDirection,EdgeDistance,MeshHitDistance);
if(!OutError.IsEmpty())return false;
switch(TileResult)
{
case ECapRayTileResult::EmittedHit:
++RayHits;++RayResultsByElevation[ElevationIndex][0];
RayHitDistancesMeters.push_back(MeshHitDistance*VoxelSizeCm/100.0);break;
case ECapRayTileResult::CulledByClipmap:
++RayMissedCulledByClipmap;++RayResultsByElevation[ElevationIndex][1];break;
case ECapRayTileResult::RequestedWithoutTriangles:
++RayMissedRequestedWithoutTriangles;++RayResultsByElevation[ElevationIndex][2];break;
case ECapRayTileResult::EmittedWithoutHit:
{
++RayMissedEmittedWithoutHit;++RayResultsByElevation[ElevationIndex][3];
if(bProfileCrossing&&bProfileRoofAtCrossing)++RayProfileRoofWithoutMeshHit;
std::uint32_t FarRequestedMask=0,FarEmittedMask=0;
std::uint32_t RootRequestedMask=0,RootEmittedMask=0;
std::uint64_t RootDownwardTriangles=0;
if(bProfileCrossing&&bProfileRoofAtCrossing)
{
for(std::int32_t Lod=0;Lod<=8;++Lod)
{
const std::int64_t Extent=static_cast<std::int64_t>(32)<<Lod;
const std::int64_t TileX=FloorDiv64(static_cast<std::int64_t>(std::floor(ProfileHitX)),Extent);
const std::int64_t TileY=FloorDiv64(static_cast<std::int64_t>(std::floor(ProfileHitY)),Extent);
const std::int64_t TileZ=FloorDiv64(ProfileHitCapSubvoxels,Extent*SubvoxelsPerVoxel);
const FTileId Id{static_cast<std::int32_t>(TileX),static_cast<std::int32_t>(TileY),
static_cast<std::int32_t>(TileZ),Lod};
if(Desired.find(Id)==Desired.end())continue;
RootRequestedMask|=1u<<static_cast<std::uint32_t>(Lod);
const FCapRayTileGeometry* Geometry=GetCapRayTileGeometry(Id);
if(Geometry!=nullptr)
{
RootDownwardTriangles+=Geometry->Triangles.size();
if(!Geometry->Triangles.empty())
RootEmittedMask|=1u<<static_cast<std::uint32_t>(Lod);
}
if(Lod>=5)
{
FarRequestedMask|=1u<<static_cast<std::uint32_t>(Lod-5);
if(Geometry!=nullptr&&!Geometry->Triangles.empty())
FarEmittedMask|=1u<<static_cast<std::uint32_t>(Lod-5);
}
}
}
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeFarCapRayMiss] route_point=%llu elevation_deg=%.0f azimuth_deg=%.0f profile_crossing=%d profile_roof=%d profile_distance_vox=%.1f clip_distance_vox=%.1f profile_hit_vox=(%.1f,%.1f,%.1f) root_lod_requested_mask=0x%X root_lod_emitted_mask=0x%X root_downward_triangles=%llu far_requested_mask=0x%X far_emitted_mask=0x%X"),
static_cast<unsigned long long>(RouteIndex),Elevation,Azimuth,
bProfileCrossing?1:0,bProfileRoofAtCrossing?1:0,
ProfileHitDistance,EdgeDistance,ProfileHitX,ProfileHitY,ProfileHitZ,
RootRequestedMask,RootEmittedMask,
static_cast<unsigned long long>(RootDownwardTriangles),
FarRequestedMask,FarEmittedMask);
break;
}
}
}
}
const auto WalkColumn=Field.point(WalkStart.positionSubvoxels[0],
WalkStart.positionSubvoxels[1],0);
const std::int64_t WalkFarCap=Field.farSurfaceCapHeightSubvoxels(
WalkStart.positionSubvoxels[0],WalkStart.positionSubvoxels[1]);
std::size_t RayTriangleCount=0;
for(const auto& [Id,Geometry]:RayTileGeometryCache)
{ (void)Id;RayTriangleCount+=Geometry.Triangles.size(); }
std::sort(RayHitDistancesMeters.begin(),RayHitDistancesMeters.end());
const double HitDistanceP50Meters=RayHitDistancesMeters.empty()?0.0:
RayHitDistancesMeters[(RayHitDistancesMeters.size()-1)*50/100];
const double HitDistanceP95Meters=RayHitDistancesMeters.empty()?0.0:
RayHitDistancesMeters[(RayHitDistancesMeters.size()-1)*95/100];
const double HitDistanceMaximumMeters=RayHitDistancesMeters.empty()?0.0:
RayHitDistancesMeters.back();
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeFarCapCensus] source=surface_route_front route_points=%llu walk_keys=%llu far_xy_tiles=%llu cap_bands=%llu requested=%llu emitted=%llu requested_without_triangles=%llu culled_by_clipmap=%llu grid_samples=%llu samples_without_roof=%llu xy_tiles_with_roof_gaps=%llu walk_z_vox=%.3f ground_z_vox=%.3f cap_z_vox=%.3f"),
static_cast<unsigned long long>(Plan->route.size()),
static_cast<unsigned long long>(WalkKeys.size()),
static_cast<unsigned long long>(FarXYTiles.size()),
static_cast<unsigned long long>(CapBands.size()),
static_cast<unsigned long long>(RequestedCapBands),
static_cast<unsigned long long>(EmittedCapBands),
static_cast<unsigned long long>(RequestedWithoutTriangles),
static_cast<unsigned long long>(CulledCapBands),
static_cast<unsigned long long>(ProfileGridSamples),
static_cast<unsigned long long>(ProfileSamplesWithoutRoof),
static_cast<unsigned long long>(FarXYTilesWithoutRoof.size()),
Camera.z,static_cast<double>(WalkColumn.groundHeightSubvoxels)/SubvoxelsPerVoxel,
static_cast<double>(WalkFarCap)/SubvoxelsPerVoxel);
const double CoveragePercent=RayCount>0?100.0*static_cast<double>(RayHits)/RayCount:0.0;
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeFarCapRayCoverage] rays=%llu mesh_hits=%llu coverage_pct=%.3f missed_clip_edge=%llu missed_no_roof=%llu missed_cap_key_culled=%llu missed_requested_without_triangles=%llu missed_emitted_without_mesh_hit=%llu start_above_cap=%llu profile_crossings=%llu profile_roof_crossings=%llu profile_roof_without_mesh_hit=%llu traced_tiles=%llu traced_downward_triangles=%llu first_hit_distance_m_p50=%.2f_p95=%.2f_max=%.2f elevations_deg=15,30,45,60,75,90 azimuths=8 ray_method=all_downward_emitted_triangles_with_tile_aabb clip_edge=desired_far_xy_bounds lods_checked=0-8"),
static_cast<unsigned long long>(RayCount),static_cast<unsigned long long>(RayHits),CoveragePercent,
static_cast<unsigned long long>(RayMissedClipEdge),
static_cast<unsigned long long>(RayMissedNoRoof),
static_cast<unsigned long long>(RayMissedCulledByClipmap),
static_cast<unsigned long long>(RayMissedRequestedWithoutTriangles),
static_cast<unsigned long long>(RayMissedEmittedWithoutHit),
static_cast<unsigned long long>(RayStartedAboveCap),
static_cast<unsigned long long>(RayProfileCrossings),
static_cast<unsigned long long>(RayProfileRoofCrossings),
static_cast<unsigned long long>(RayProfileRoofWithoutMeshHit),
static_cast<unsigned long long>(RayTileGeometryCache.size()),
static_cast<unsigned long long>(RayTriangleCount),HitDistanceP50Meters,
HitDistanceP95Meters,HitDistanceMaximumMeters);
for(std::size_t Index=0;Index<ElevationDegrees.size();++Index)
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeFarCapRayElevation] degrees=%.0f rays=%llu mesh_hits=%llu key_culled=%llu requested_empty=%llu emitted_without_mesh_hit=%llu no_roof_or_above=%llu missed_clip_edge=%llu"),
ElevationDegrees[Index],
static_cast<unsigned long long>(AzimuthDegrees.size()*Plan->route.size()),
static_cast<unsigned long long>(RayResultsByElevation[Index][0]),
static_cast<unsigned long long>(RayResultsByElevation[Index][1]),
static_cast<unsigned long long>(RayResultsByElevation[Index][2]),
static_cast<unsigned long long>(RayResultsByElevation[Index][3]),
static_cast<unsigned long long>(RayResultsByElevation[Index][4]),
static_cast<unsigned long long>(RayResultsByElevation[Index][5]));
if(RayHits!=RayCount)
{
OutError=FString::Printf(TEXT("Far-cap ray coverage failed: rays=%llu mesh_hits=%llu clip_edge=%llu no_roof=%llu culled_keys=%llu requested_empty=%llu emitted_without_hit=%llu."),
static_cast<unsigned long long>(RayCount),static_cast<unsigned long long>(RayHits),
static_cast<unsigned long long>(RayMissedClipEdge),
static_cast<unsigned long long>(RayMissedNoRoof),
static_cast<unsigned long long>(RayMissedCulledByClipmap),
static_cast<unsigned long long>(RayMissedRequestedWithoutTriangles),
static_cast<unsigned long long>(RayMissedEmittedWithoutHit));
return false;
}
for(const auto& [Lod,Counts]:ByLod)
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeFarCapCensusLOD] lod=%d cap_bands=%llu culled=%llu emitted=%llu requested_without_triangles=%llu"),
Lod,static_cast<unsigned long long>(Counts[0]),
static_cast<unsigned long long>(Counts[1]),
static_cast<unsigned long long>(Counts[2]),
static_cast<unsigned long long>(Counts[3]));
return true;
}
// Paired engine timings include preparation and derived shells, with canonical output hashes.
// Mesures moteur appariees : preparation et coques derivees incluses, avec hashes canoniques.
bool RunCoreSharedFeatureCostAudit(VoxelForgeCore::World& World,
@@ -15441,8 +15992,12 @@ bool RunCoreSharedFeatureCostAudit(VoxelForgeCore::World& World,
P95(ConnectorShellTimes),P95(ConnectorFieldCalls),P95(ShellTriangles),
P95(GroundTriangles),P95(GroundRemoved),P95(CapTriangles));
}
if(Assembly!=nullptr&&!Assembly->plan.strates.empty()
&&!RunCoreFarHorizonAgreementAudit(World,Keys,*Assembly,OutError))return false;
if(Assembly!=nullptr&&!Assembly->plan.strates.empty())
{
if(FParse::Param(FCommandLine::Get(),TEXT("corefeatureauditonly"))
&&!RunCoreFarCapTileCensus(World,*Assembly,OutError))return false;
if(!RunCoreFarHorizonAgreementAudit(World,Keys,*Assembly,OutError))return false;
}
return true;
}