startup: parallel owner-shape compile and far-parent build (L105, exact); R45 simplification audit
Owner shapes compile per stratum on the Core parallel scheduler and merge in owner-layout order; far-parent ellipsoids/connectors generate in parallel and merge canonically; 512-unit far bins. Base compile 239 -> 203 ms, far-parent build 128 -> 33 ms. Near-ready @W12: SW 0.84 -> 0.75, FI 1.51 -> 1.42, TN 1.38 -> 1.30, VS 0.92 -> 0.85 s. All hashes equal L104 (Clang/GCC W1/W8/W12). Fast tier PASS; Linux UE builds; 6/6 Unreal arrival traces pass. R45 (read-only, owner's "are we overcomplicating?"): next simplifications = tunnel floor/mouth support inside the bounded passage field; boundary bands once per XY column; certified early SW tiles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
6a80cb0ab3
commit
f228d6319f
8 files changed
+1245
-25
No files matched your search
@@ -3563,12 +3563,20 @@ public:
|
||||
double LocalPrimitiveSeconds = 0.0;
|
||||
double LocalLineBinSeconds = 0.0;
|
||||
double CandidateMergeSeconds = 0.0;
|
||||
double PackedSignBuildSeconds = 0.0;
|
||||
double PassageSweepPrepSeconds = 0.0;
|
||||
double CandidateSweepPrepSeconds = 0.0;
|
||||
double VisualDetailPrepSeconds = 0.0;
|
||||
double DetailApplySeconds = 0.0;
|
||||
double SignEdgeDistanceSeconds = 0.0;
|
||||
double PassageValidationSeconds = 0.0;
|
||||
double SharedFieldSeconds = 0.0;
|
||||
double OwnerShapeWorkerSeconds = 0.0;
|
||||
double FarPrimitiveSeconds = 0.0;
|
||||
double FarParentShapeSeconds = 0.0;
|
||||
double FarSharedPrimitiveSeconds = 0.0;
|
||||
double FarBinSeconds = 0.0;
|
||||
FParallelTiming FarParentGeometryLines;
|
||||
FParallelTiming RockIntervalLines;
|
||||
FParallelTiming CandidateAxisLines;
|
||||
FParallelTiming CandidateMergeLines;
|
||||
@@ -3701,7 +3709,6 @@ public:
|
||||
size_t capacity() const {return bHeap?Storage.Heap.Capacity:InlineCapacity;}
|
||||
const uint32* begin() const {return bHeap?Storage.Heap.Data:Storage.Inline.data();}
|
||||
const uint32* end() const {return begin()+Count;}
|
||||
|
||||
private:
|
||||
void Release()
|
||||
{
|
||||
@@ -3734,6 +3741,7 @@ public:
|
||||
static constexpr float StrataCaveBlendRadius = 12.0f;
|
||||
static constexpr int32 LineBinSize = 32;
|
||||
static constexpr int32 SpatialBinSize = 256;
|
||||
static constexpr int32 FarSpatialBinSize = 512;
|
||||
|
||||
bool Build(FStandaloneOwnerWorld& World, const FIntVector& MinVoxels,
|
||||
const FIntVector& MaxVoxels, const FIntVector& ClipMinVoxels,
|
||||
@@ -3779,7 +3787,11 @@ public:
|
||||
auto Stage=Clock::now();
|
||||
BuildLayerBounds();
|
||||
auto Substage=Clock::now();
|
||||
BuildOwnerPrimitives();
|
||||
const auto& OwnerLayout=Owner->Manager->GetLayout();
|
||||
if(WorkerCount>1&&Scheduler&&OwnerLayout.Num()>1)
|
||||
BuildOwnerPrimitivesParallel();
|
||||
else
|
||||
BuildOwnerPrimitives();
|
||||
Stats.LocalPrimitiveSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
|
||||
Substage=Clock::now();
|
||||
if(RoundTunnelsEnabled)
|
||||
@@ -3798,15 +3810,23 @@ public:
|
||||
WordsPerZ=(NZ+63u)/64u;
|
||||
RockWords.assign(static_cast<size_t>(Size[0])*Size[1]*WordsPerZ,0);
|
||||
SurfaceDistanceQ=std::make_unique_for_overwrite<int16[]>(static_cast<size_t>(NodeCount));
|
||||
Substage=Clock::now();
|
||||
BuildPackedRockIntervals(Stats.RockIntervalLines);
|
||||
Stats.PackedSignBuildSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
|
||||
// Native density queries for non-heightfield strates use the exact interval
|
||||
// evaluator. Build those native intervals before scanning passage support;
|
||||
// the scan itself still completes before any resolved column is composed.
|
||||
Substage=Clock::now();
|
||||
BuildPassageVoidCrossingPlan();
|
||||
Stats.PassageSweepPrepSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
|
||||
Substage=Clock::now();
|
||||
BuildCandidateBands(Stats.CandidateAxisLines,Stats.CandidateMergeLines,
|
||||
Stats.CandidateMergeSeconds);
|
||||
Stats.CandidateSweepPrepSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
|
||||
Substage=Clock::now();
|
||||
if(!BuildGpuVisualDetailNodeSets(InGpuVisualDetailTiles,InCpuVisualDetailTiles,Error))
|
||||
return false;
|
||||
Stats.VisualDetailPrepSeconds=std::chrono::duration<double>(Clock::now()-Substage).count();
|
||||
Stats.IntervalSeconds=std::chrono::duration<double>(Clock::now()-Stage).count();
|
||||
|
||||
Stage=Clock::now();
|
||||
@@ -3861,6 +3881,14 @@ public:
|
||||
Stats.DetailApplySeconds*1000.0,Stats.SignEdgeDistanceSeconds*1000.0,
|
||||
Stats.PassageValidationSeconds*1000.0,Stats.SharedFieldSeconds*1000.0,
|
||||
Stats.FarShapeSeconds*1000.0);
|
||||
std::printf("[ShapeBreakdown] owner_shapes=%.3fms owner_shape_worker=%.3fms owner_bins=%.3fms packed_signs=%.3fms passage_prep=%.3fms candidate_sweeps=%.3fms visual_detail_prep=%.3fms detail_band=%.3fms sign_edge_repair=%.3fms route_validation=%.3fms far_parent_shapes=%.3fms far_shared_primitives=%.3fms far_bins=%.3fms\n",
|
||||
Stats.LocalPrimitiveSeconds*1000.0,Stats.OwnerShapeWorkerSeconds*1000.0,
|
||||
Stats.LocalLineBinSeconds*1000.0,
|
||||
Stats.PackedSignBuildSeconds*1000.0,Stats.PassageSweepPrepSeconds*1000.0,
|
||||
Stats.CandidateSweepPrepSeconds*1000.0,Stats.VisualDetailPrepSeconds*1000.0,
|
||||
Stats.DetailApplySeconds*1000.0,Stats.SignEdgeDistanceSeconds*1000.0,
|
||||
Stats.PassageValidationSeconds*1000.0,Stats.FarParentShapeSeconds*1000.0,
|
||||
Stats.FarSharedPrimitiveSeconds*1000.0,Stats.FarBinSeconds*1000.0);
|
||||
std::printf("[ShapeAllocation] words=%.2fMiB candidate_axis_words=%.2fMiB proxy=%.2fMiB primitives=%.2fMiB spatial_bins=%llu spatial_bin_refs=%llu spatial_bin_capacity=%llu inline_bins=%llu overflow_bins=%llu far_primitive_build=%.3fms far_bin_rebuild=%.3fms\n",
|
||||
static_cast<double>(WordBytes)/1048576.0,static_cast<double>(CandidateAxisBytes)/1048576.0,
|
||||
static_cast<double>(Stats.SignNodes*sizeof(int16))/1048576.0,
|
||||
@@ -4468,16 +4496,26 @@ public:
|
||||
if(FarBuilt)return;
|
||||
auto Stage=std::chrono::steady_clock::now();
|
||||
BuildFarParentShapes();
|
||||
RebuildSharedFieldPrimitives();
|
||||
Stats.FarPrimitiveSeconds=std::chrono::duration<double>(std::chrono::steady_clock::now()-Stage).count();
|
||||
Stats.FarParentShapeSeconds=std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now()-Stage).count();
|
||||
Stage=std::chrono::steady_clock::now();
|
||||
BuildSpatialBins(SpatialBinSize,false);
|
||||
Stats.FarBinSeconds=std::chrono::duration<double>(std::chrono::steady_clock::now()-Stage).count();
|
||||
RebuildSharedFieldPrimitives();
|
||||
Stats.FarSharedPrimitiveSeconds=std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now()-Stage).count();
|
||||
Stats.FarPrimitiveSeconds=Stats.FarParentShapeSeconds+Stats.FarSharedPrimitiveSeconds;
|
||||
Stage=std::chrono::steady_clock::now();
|
||||
BuildSpatialBins(FarSpatialBinSize,false);
|
||||
Stats.FarBinSeconds=std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now()-Stage).count();
|
||||
Stats.FarShapeSeconds=Stats.FarPrimitiveSeconds+Stats.FarBinSeconds;
|
||||
Stats.PrimitiveCount=Primitives.size();FarBuilt=true;
|
||||
std::printf("[FarShapeStage] parent_shapes=%.3f ms spatial_bins=%.3f ms total=%.3f ms primitives=%llu\n",
|
||||
Stats.FarPrimitiveSeconds*1000.0,Stats.FarBinSeconds*1000.0,Stats.FarShapeSeconds*1000.0,
|
||||
static_cast<unsigned long long>(Stats.PrimitiveCount));
|
||||
std::printf("[FarShapeBreakdown] parent_generation=%.3fms parent_geometry_wall=%.3fms parent_geometry_worker=%.3fms shared_fields=%.3fms bin_rebuild=%.3fms\n",
|
||||
Stats.FarParentShapeSeconds*1000.0,Stats.FarParentGeometryLines.WallSeconds*1000.0,
|
||||
Stats.FarParentGeometryLines.WorkerSeconds*1000.0,
|
||||
Stats.FarSharedPrimitiveSeconds*1000.0,Stats.FarBinSeconds*1000.0);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
|
||||
@@ -11277,14 +11315,112 @@ private:
|
||||
-FloorAt(P,X-UX*Epsilon,Y-UY*Epsilon))/(2.0*Epsilon);
|
||||
}
|
||||
|
||||
void BuildOwnerPrimitives()
|
||||
void AccumulateOwnerPrimitiveStats(const FBuildStats& Other)
|
||||
{
|
||||
Stats.RoomCount+=Other.RoomCount;
|
||||
Stats.RoomFloorJoinCount+=Other.RoomFloorJoinCount;
|
||||
Stats.TunnelCount+=Other.TunnelCount;
|
||||
if(Other.MaxTunnelBendDegrees>Stats.MaxTunnelBendDegrees)
|
||||
{
|
||||
Stats.MaxTunnelBendDegrees=Other.MaxTunnelBendDegrees;
|
||||
Stats.MaxTunnelBendVoxels=Other.MaxTunnelBendVoxels;
|
||||
Stats.MaxTunnelBendDirection=Other.MaxTunnelBendDirection;
|
||||
}
|
||||
Stats.LegacySegmentRadiusStep=std::max(Stats.LegacySegmentRadiusStep,
|
||||
Other.LegacySegmentRadiusStep);
|
||||
Stats.MaxCapsuleJoinFloorDelta=std::max(Stats.MaxCapsuleJoinFloorDelta,
|
||||
Other.MaxCapsuleJoinFloorDelta);
|
||||
Stats.MaxMarkedFloorSlopeDelta=std::max(Stats.MaxMarkedFloorSlopeDelta,
|
||||
Other.MaxMarkedFloorSlopeDelta);
|
||||
Stats.MaxRawUnmarkedFloorSlopeDelta=std::max(Stats.MaxRawUnmarkedFloorSlopeDelta,
|
||||
Other.MaxRawUnmarkedFloorSlopeDelta);
|
||||
Stats.SmoothedProfileRadiusStep=std::max(Stats.SmoothedProfileRadiusStep,
|
||||
Other.SmoothedProfileRadiusStep);
|
||||
Stats.MaxFloorCurveDeviation=std::max(Stats.MaxFloorCurveDeviation,
|
||||
Other.MaxFloorCurveDeviation);
|
||||
Stats.MaxFloorProfileEndpointValueError=std::max(
|
||||
Stats.MaxFloorProfileEndpointValueError,Other.MaxFloorProfileEndpointValueError);
|
||||
Stats.MaxFloorProfileEndpointSlopeError=std::max(
|
||||
Stats.MaxFloorProfileEndpointSlopeError,Other.MaxFloorProfileEndpointSlopeError);
|
||||
Stats.MaxCapsuleJoinRadiusDelta=std::max(Stats.MaxCapsuleJoinRadiusDelta,
|
||||
Other.MaxCapsuleJoinRadiusDelta);
|
||||
Stats.MaxCapsuleJoinRadiusSlopeDelta=std::max(Stats.MaxCapsuleJoinRadiusSlopeDelta,
|
||||
Other.MaxCapsuleJoinRadiusSlopeDelta);
|
||||
Stats.MaxCapsuleJoinFloorSlopeDelta=std::max(Stats.MaxCapsuleJoinFloorSlopeDelta,
|
||||
Other.MaxCapsuleJoinFloorSlopeDelta);
|
||||
Stats.MaxUnmarkedFloorSlopeDelta=std::max(Stats.MaxUnmarkedFloorSlopeDelta,
|
||||
Other.MaxUnmarkedFloorSlopeDelta);
|
||||
Stats.MaxUnmarkedFloorDirectionSlopeDelta=std::max(
|
||||
Stats.MaxUnmarkedFloorDirectionSlopeDelta,Other.MaxUnmarkedFloorDirectionSlopeDelta);
|
||||
Stats.MarkedFloorSlopeJoins+=Other.MarkedFloorSlopeJoins;
|
||||
Stats.CurvedFloorSegments+=Other.CurvedFloorSegments;
|
||||
Stats.CapsuleJoinCount+=Other.CapsuleJoinCount;
|
||||
}
|
||||
|
||||
void BuildOwnerPrimitivesParallel()
|
||||
{
|
||||
const auto& Layout=Owner->Manager->GetLayout();
|
||||
struct FStratumResult
|
||||
{
|
||||
std::vector<FPrimitive> Primitives;
|
||||
FBuildStats Stats;
|
||||
};
|
||||
struct FDispatch
|
||||
{
|
||||
FStandaloneShapeWorld* Parent=nullptr;
|
||||
const TArray<FStrateSlot>* Layout=nullptr;
|
||||
std::vector<FStratumResult>* Results=nullptr;
|
||||
};
|
||||
const size_t StratumCount=static_cast<size_t>(Layout.Num());
|
||||
std::vector<FStratumResult> Results(StratumCount);
|
||||
FDispatch Dispatch{this,&Layout,&Results};
|
||||
const auto BuildStratum=[](void* Opaque,size_t Index,size_t)
|
||||
{
|
||||
auto& Context=*static_cast<FDispatch*>(Opaque);
|
||||
const FStrateSlot& Slot=(*Context.Layout)[Index];
|
||||
if(!Slot.Definition)return;
|
||||
FStandaloneShapeWorld Partial;
|
||||
Partial.Owner=Context.Parent->Owner;
|
||||
Partial.WorldAssembly=Context.Parent->WorldAssembly;
|
||||
Partial.ResolvedLayout=Context.Parent->ResolvedLayout;
|
||||
Partial.LayerRanges=Context.Parent->LayerRanges;
|
||||
Partial.LayerFields=Context.Parent->LayerFields;
|
||||
for(int32 Axis=0;Axis<3;++Axis)
|
||||
{
|
||||
Partial.Min[Axis]=Context.Parent->Min[Axis];
|
||||
Partial.Max[Axis]=Context.Parent->Max[Axis];
|
||||
}
|
||||
Partial.BuildOwnerPrimitives(Slot.StrateIndex,true,false);
|
||||
FStratumResult& Result=(*Context.Results)[Index];
|
||||
Result.Primitives=std::move(Partial.Primitives);
|
||||
Result.Stats=Partial.Stats;
|
||||
};
|
||||
std::array<double,16> WorkerSeconds{};
|
||||
const size_t WorkerLimit=std::min<size_t>(static_cast<size_t>(WorkerCount),StratumCount);
|
||||
Stats.OwnerShapeWorkerSeconds=Scheduler->parallel_for(StratumCount,1,WorkerLimit,
|
||||
&Dispatch,BuildStratum,WorkerSeconds.data(),WorkerSeconds.size());
|
||||
size_t PrimitiveCount=0;
|
||||
for(const FStratumResult& Result:Results)PrimitiveCount+=Result.Primitives.size();
|
||||
Primitives.reserve(Primitives.size()+PrimitiveCount);
|
||||
for(FStratumResult& Result:Results)
|
||||
{
|
||||
AccumulateOwnerPrimitiveStats(Result.Stats);
|
||||
Primitives.insert(Primitives.end(),
|
||||
std::make_move_iterator(Result.Primitives.begin()),
|
||||
std::make_move_iterator(Result.Primitives.end()));
|
||||
}
|
||||
BuildOwnerPrimitives(-1,false,true);
|
||||
}
|
||||
|
||||
void BuildOwnerPrimitives(int32 OnlyStrateIndex=-1,bool bBuildStrata=true,
|
||||
bool bBuildPassages=true)
|
||||
{
|
||||
const auto& Layout=Owner->Manager->GetLayout();
|
||||
const float MinX=static_cast<float>(Min[0]-256),MinY=static_cast<float>(Min[1]-256);
|
||||
const float MaxX=static_cast<float>(Max[0]+256),MaxY=static_cast<float>(Max[1]+256);
|
||||
for(const FStrateSlot& Slot:Layout)
|
||||
if(bBuildStrata)for(const FStrateSlot& Slot:Layout)
|
||||
{
|
||||
if(!Slot.Definition)continue;
|
||||
if(!Slot.Definition||(OnlyStrateIndex>=0&&Slot.StrateIndex!=OnlyStrateIndex))continue;
|
||||
const FIntVector MidChunk(0,0,(Slot.TopChunkZ+Slot.BottomChunkZ)/2);
|
||||
const FStrateGenerationParams Params=Owner->Manager->GetGenerationParams(MidChunk);
|
||||
ActiveBlendRadius=0.0;
|
||||
@@ -11506,6 +11642,7 @@ private:
|
||||
// Reproduire dans chaque strate la salle d'origine finie et ancrée en haut.
|
||||
AddOriginLandingRoom(FieldForStrate(Slot.StrateIndex));
|
||||
}
|
||||
if(!bBuildPassages)return;
|
||||
const double PassageJoinBlend=ActiveBlendRadius;ActiveBlendRadius=JoinBlendRadius;
|
||||
ActiveFloorBlendRadius=FloorClipBlendRadius;
|
||||
for(const FVoxelPassage& P:Owner->Manager->GetPassages())
|
||||
@@ -11786,7 +11923,7 @@ private:
|
||||
ActiveSpatialBinSize=BinResolution;
|
||||
// Primitive bounds overlap multiple cells. Reserve from the measured bins-per-primitive
|
||||
// density for each grid instead of repeatedly rehashing from a 2x primitive guess.
|
||||
const size_t BinsPerPrimitive=bBuildAxisBins?96u:24u;
|
||||
const size_t BinsPerPrimitive=bBuildAxisBins?96u:(BinResolution>=FarSpatialBinSize?8u:24u);
|
||||
SpatialBins.reserve(std::max<size_t>(Primitives.size()*BinsPerPrimitive,1u));
|
||||
if(!bBuildAxisBins)
|
||||
{
|
||||
@@ -11812,7 +11949,8 @@ private:
|
||||
B.Items[static_cast<size_t>(IB-B.MinB)*B.CountA+(IA-B.MinA)].push_back(Id);
|
||||
}
|
||||
}
|
||||
for(uint32 Id=0;Id<Primitives.size();++Id)
|
||||
const auto EnumeratePrimitiveBins=[&](uint32 Id,auto&& AddSpatial,
|
||||
auto&& AddCritical,auto&& AddUnbinned)
|
||||
{
|
||||
const FPrimitive& P=Primitives[Id];
|
||||
const bool bCriticalAir=P.bCarve&&P.bCritical;
|
||||
@@ -11832,8 +11970,8 @@ private:
|
||||
// Large non-capsule bounds are deliberately omitted from point-query bins,
|
||||
// but interval proofs still need a complete candidate set. Keep these IDs
|
||||
// separate so block and tile proofs cannot mistake an omitted carve for air-free rock.
|
||||
UnbinnedSpatialPrimitiveIds.push_back(Id);
|
||||
continue;
|
||||
AddUnbinned(Id);
|
||||
return;
|
||||
}
|
||||
const double BinWidth=static_cast<double>(BinResolution);
|
||||
const double AxisStep=std::max(1.0,BinWidth*0.5);
|
||||
@@ -11874,17 +12012,28 @@ private:
|
||||
PrimitiveBins.end());
|
||||
for(const FBinKey& Key:PrimitiveBins)
|
||||
{
|
||||
SpatialBins[Key].push_back(Id);
|
||||
if(bCriticalAir)CriticalAirBins[Key].push_back(Id);
|
||||
AddSpatial(Key,Id);
|
||||
if(bCriticalAir)AddCritical(Key,Id);
|
||||
}
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
for(int32 Z=Z0;Z<=Z1;++Z)for(int32 Y=Y0;Y<=Y1;++Y)for(int32 X=X0;X<=X1;++X)
|
||||
{
|
||||
const FBinKey Key{X,Y,Z};
|
||||
SpatialBins[Key].push_back(Id);
|
||||
if(bCriticalAir)CriticalAirBins[Key].push_back(Id);
|
||||
AddSpatial(Key,Id);
|
||||
if(bCriticalAir)AddCritical(Key,Id);
|
||||
}
|
||||
};
|
||||
|
||||
const auto AddSpatial=[&](const FBinKey& Key,uint32 Id)
|
||||
{SpatialBins[Key].push_back(Id);};
|
||||
const auto AddCritical=[&](const FBinKey& Key,uint32 Id)
|
||||
{CriticalAirBins[Key].push_back(Id);};
|
||||
const auto AddUnbinned=[&](uint32 Id)
|
||||
{UnbinnedSpatialPrimitiveIds.push_back(Id);};
|
||||
for(uint32 Id=0;Id<Primitives.size();++Id)
|
||||
{
|
||||
EnumeratePrimitiveBins(Id,AddSpatial,AddCritical,AddUnbinned);
|
||||
}
|
||||
std::sort(UnbinnedSpatialPrimitiveIds.begin(),UnbinnedSpatialPrimitiveIds.end());
|
||||
UnbinnedSpatialPrimitiveIds.erase(std::unique(UnbinnedSpatialPrimitiveIds.begin(),
|
||||
@@ -11931,12 +12080,26 @@ private:
|
||||
FParent P{GX,GY,Slot.StrateIndex,FVector(X,Y,Z),Rx,Ry,Rz};
|
||||
const size_t Index=Parents.size();Parents.push_back(P);
|
||||
ByCell.emplace(Key(GX,GY,Slot.StrateIndex),Index);
|
||||
const std::array<double,3> Axis0={1,0,0},Axis1={0,1,0},Axis2={0,0,1};
|
||||
AddEllipsoid(P.Center,{P.Rx,P.Ry,P.Rz},Axis0,Axis1,Axis2,true);
|
||||
}
|
||||
}
|
||||
for(const FParent& P:Parents)
|
||||
struct FParentGeometry
|
||||
{
|
||||
FPrimitive Ellipsoid;
|
||||
std::vector<FPrimitive> Connectors;
|
||||
std::vector<FFarConnectorSegment> Segments;
|
||||
};
|
||||
std::vector<FParentGeometry> ParentGeometry(Parents.size());
|
||||
ParallelLines(Parents.size(),[&](size_t ParentIndex,int32)
|
||||
{
|
||||
const FParent& P=Parents[ParentIndex];
|
||||
FParentGeometry& Geometry=ParentGeometry[ParentIndex];
|
||||
FStandaloneShapeWorld Builder;
|
||||
Builder.ActiveBlendRadius=ActiveBlendRadius;
|
||||
Builder.AddEllipsoid(P.Center,{P.Rx,P.Ry,P.Rz},{1,0,0},{0,1,0},{0,0,1},true);
|
||||
Geometry.Ellipsoid=std::move(Builder.Primitives.back());
|
||||
Builder.Primitives.clear();
|
||||
Geometry.Connectors.reserve(2);
|
||||
Geometry.Segments.reserve(2);
|
||||
for(const auto [DX,DY]:std::array<std::pair<int32,int32>,2>{{{1,0},{0,1}}})
|
||||
{
|
||||
const auto It=ByCell.find(Key(P.GX+DX,P.GY+DY,P.Strate));
|
||||
@@ -11948,11 +12111,26 @@ private:
|
||||
const int32 MinY=static_cast<int32>(std::floor(std::min(A.Y,B.Y)-64.0));
|
||||
const int32 MaxY=static_cast<int32>(std::ceil(std::max(A.Y,B.Y)+64.0));
|
||||
if(MaxX>=Min[0]&&MinX<=Max[0]&&MaxY>=Min[1]&&MinY<=Max[1])continue;
|
||||
AddTaperedCapsule(A,B,64.0f,64.0f,true);
|
||||
Primitives.back().BlendRadius=0.0;
|
||||
FarConnectorSegments.push_back({A,B,64.0f});
|
||||
Builder.AddTaperedCapsule(A,B,64.0f,64.0f,true);
|
||||
Builder.Primitives.back().BlendRadius=0.0;
|
||||
Geometry.Connectors.push_back(std::move(Builder.Primitives.back()));
|
||||
Builder.Primitives.clear();
|
||||
Geometry.Segments.push_back({A,B,64.0f});
|
||||
}
|
||||
}
|
||||
},&Stats.FarParentGeometryLines);
|
||||
size_t ConnectorCount=0;
|
||||
for(const FParentGeometry& Geometry:ParentGeometry)
|
||||
ConnectorCount+=Geometry.Connectors.size();
|
||||
Primitives.reserve(Primitives.size()+Parents.size()+ConnectorCount);
|
||||
FarConnectorSegments.reserve(FarConnectorSegments.size()+ConnectorCount);
|
||||
for(FParentGeometry& Geometry:ParentGeometry)
|
||||
Primitives.push_back(std::move(Geometry.Ellipsoid));
|
||||
for(FParentGeometry& Geometry:ParentGeometry)
|
||||
for(FPrimitive& Connector:Geometry.Connectors)
|
||||
Primitives.push_back(std::move(Connector));
|
||||
for(FParentGeometry& Geometry:ParentGeometry)
|
||||
FarConnectorSegments.insert(FarConnectorSegments.end(),
|
||||
Geometry.Segments.begin(),Geometry.Segments.end());
|
||||
BuildFarConnectorSpatialBins();
|
||||
if(!Parents.empty())
|
||||
std::printf("[FarParents] pitch=%d rooms=%zu links=%llu clip=(%d,%d)..(%d,%d)\n",
|
||||
|
||||
Reference in new issue
Block a user