feat: 集成空间索引到碰撞检测系统(单层架构)
重构说明: - 移除两层架构的 GetPotentialColliders() 方法 - 移除不再使用的 CalculatePathBounds() 方法 - 在 PrecomputeAnimationFrames() 中构建全局空间索引 - 每帧使用空间索引直接查询附近对象(O(1) 复杂度) 技术细节: - 网格大小:基于动画对象包围盒尺寸 - 搜索半径:对象对角线的一半 + 检测间隙 - 自动排除动画对象本身 性能优势: - 旧架构:290 对象 → 108 对象(预过滤)→ 每帧遍历 108 对象 - 新架构:290 对象 → 每帧空间查询 ~5-15 对象 - 预期性能提升:10-15x
This commit is contained in:
parent
360d55ffa8
commit
c7f6586fa9
@ -377,17 +377,36 @@ namespace NavisworksTransport.Core.Animation
|
||||
originalBoundingBox.Max.Y - originalBoundingBox.Min.Y,
|
||||
originalBoundingBox.Max.Z - originalBoundingBox.Min.Z
|
||||
);
|
||||
|
||||
// 获取场景中所有可能碰撞的对象
|
||||
var potentialColliders = GetPotentialColliders();
|
||||
LogManager.Info($"潜在碰撞对象数: {potentialColliders.Count}");
|
||||
|
||||
|
||||
// 【新架构】构建全局空间索引(单层架构)
|
||||
LogManager.Info("=== 构建全局空间索引 ===");
|
||||
var spatialIndexManager = SpatialIndexManager.Instance;
|
||||
|
||||
// 使用包围盒对角线作为网格大小的参考(确保合理的网格粒度)
|
||||
double cellSizeInModelUnits = Math.Max(
|
||||
boundingBoxSize.X,
|
||||
Math.Max(boundingBoxSize.Y, boundingBoxSize.Z)
|
||||
);
|
||||
|
||||
spatialIndexManager.BuildGlobalIndex(cellSizeInModelUnits);
|
||||
LogManager.Info(spatialIndexManager.GetStatistics());
|
||||
|
||||
// 计算搜索半径:动画对象包围盒的对角线 + 检测间隙
|
||||
double objectDiagonal = Math.Sqrt(
|
||||
boundingBoxSize.X * boundingBoxSize.X +
|
||||
boundingBoxSize.Y * boundingBoxSize.Y +
|
||||
boundingBoxSize.Z * boundingBoxSize.Z
|
||||
);
|
||||
double searchRadiusInModelUnits = objectDiagonal / 2 + _detectionGap;
|
||||
|
||||
LogManager.Info($"空间查询半径: {searchRadiusInModelUnits:F2} 模型单位");
|
||||
|
||||
// 预计算每一帧
|
||||
for (int i = 0; i < totalFrames; i++)
|
||||
{
|
||||
double progress = (double)i / totalFrames;
|
||||
var framePosition = InterpolatePosition(progress);
|
||||
|
||||
|
||||
// 创建帧数据
|
||||
var frame = new AnimationFrame
|
||||
{
|
||||
@ -396,12 +415,19 @@ namespace NavisworksTransport.Core.Animation
|
||||
Position = framePosition,
|
||||
Collisions = new List<CollisionResult>()
|
||||
};
|
||||
|
||||
|
||||
// 虚拟碰撞检测(不移动实际物体)
|
||||
var virtualBoundingBox = CreateVirtualBoundingBox(framePosition, boundingBoxSize);
|
||||
|
||||
// 检测与所有潜在对象的碰撞
|
||||
foreach (var collider in potentialColliders)
|
||||
|
||||
// 【新架构】使用空间索引查询附近对象
|
||||
var nearbyObjects = spatialIndexManager.FindNearbyObjects(
|
||||
framePosition,
|
||||
searchRadiusInModelUnits,
|
||||
excludeObject: _animatedObject
|
||||
);
|
||||
|
||||
// 检测与附近对象的碰撞
|
||||
foreach (var collider in nearbyObjects)
|
||||
{
|
||||
var colliderBox = collider.BoundingBox();
|
||||
|
||||
@ -455,122 +481,6 @@ namespace NavisworksTransport.Core.Animation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取潜在碰撞对象列表
|
||||
/// </summary>
|
||||
private List<ModelItem> GetPotentialColliders()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("开始获取潜在碰撞对象");
|
||||
|
||||
// 构建通道缓存
|
||||
ClashDetectiveIntegration.Instance.BuildChannelObjectsCache();
|
||||
ClashDetectiveIntegration.BuildAllGeometryItemsCache();
|
||||
|
||||
// 获取所有有几何体的对象
|
||||
var allItems = NavisApplication.ActiveDocument.Models.RootItemDescendantsAndSelf
|
||||
.Where(item => item.HasGeometry)
|
||||
.ToList();
|
||||
|
||||
// 构建排除列表:动画对象本身及其子对象
|
||||
var excludeList = new ModelItemCollection();
|
||||
excludeList.AddRange(_animatedObject.DescendantsAndSelf);
|
||||
|
||||
// 计算路径包围盒
|
||||
var pathBounds = CalculatePathBounds();
|
||||
|
||||
// 计算路径包围盒中心点(模型单位)
|
||||
var pathCenter = new Point3D(
|
||||
(pathBounds.Min.X + pathBounds.Max.X) / 2,
|
||||
(pathBounds.Min.Y + pathBounds.Max.Y) / 2,
|
||||
(pathBounds.Min.Z + pathBounds.Max.Z) / 2
|
||||
);
|
||||
|
||||
// 计算包围盒对角线长度
|
||||
var diagonal = Math.Sqrt(
|
||||
Math.Pow(pathBounds.Max.X - pathBounds.Min.X, 2) +
|
||||
Math.Pow(pathBounds.Max.Y - pathBounds.Min.Y, 2) +
|
||||
Math.Pow(pathBounds.Max.Z - pathBounds.Min.Z, 2)
|
||||
);
|
||||
|
||||
// 检测半径 = 包围盒对角线的一半(精确覆盖整条路径)
|
||||
var detectionGap = diagonal / 2;
|
||||
|
||||
LogManager.Info($"路径包围盒: Min=({pathBounds.Min.X:F2}, {pathBounds.Min.Y:F2}, {pathBounds.Min.Z:F2}), " +
|
||||
$"Max=({pathBounds.Max.X:F2}, {pathBounds.Max.Y:F2}, {pathBounds.Max.Z:F2})");
|
||||
LogManager.Info($"路径中心: ({pathCenter.X:F2}, {pathCenter.Y:F2}, {pathCenter.Z:F2})");
|
||||
LogManager.Info($"包围盒对角线: {diagonal:F2} 模型单位");
|
||||
|
||||
// 临时移动物体到路径中心,以便在路径范围内检测潜在碰撞对象
|
||||
var offset = ModelItemTransformHelper.MoveItemToPosition(_animatedObject, pathCenter);
|
||||
|
||||
try
|
||||
{
|
||||
LogManager.Info($"检测半径: {detectionGap:F2} 模型单位(对角线的一半)");
|
||||
|
||||
// 调用ClashDetectiveIntegration的DetectCollisions方法
|
||||
// 现在物体在路径中心,检测路径长度半径内的对象
|
||||
// 该方法内部会自动排除通道对象(通过IsChannelObject方法)
|
||||
var collisionResults = ClashDetectiveIntegration.Instance.DetectCollisions(
|
||||
_animatedObject,
|
||||
excludeList,
|
||||
detectionGap
|
||||
);
|
||||
|
||||
// 从碰撞结果中提取唯一的碰撞对象
|
||||
var potentialColliders = new HashSet<ModelItem>();
|
||||
foreach (var result in collisionResults)
|
||||
{
|
||||
if (result.Item2 != null && !potentialColliders.Contains(result.Item2))
|
||||
{
|
||||
potentialColliders.Add(result.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
var finalList = potentialColliders.ToList();
|
||||
|
||||
LogManager.Info($"获取潜在碰撞对象完成: 总对象={allItems.Count}, " +
|
||||
$"排除自身及子对象={excludeList.Count}, " +
|
||||
$"路径沿途潜在碰撞对象={finalList.Count}(已自动排除通道)");
|
||||
|
||||
return finalList;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 恢复物体到原位置
|
||||
ModelItemTransformHelper.RestoreItemPosition(_animatedObject, offset);
|
||||
LogManager.Info("物体已恢复到原始位置");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取潜在碰撞对象失败: {ex.Message}");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算路径的包围盒
|
||||
/// </summary>
|
||||
private BoundingBox3D CalculatePathBounds()
|
||||
{
|
||||
if (_pathPoints == null || _pathPoints.Count == 0)
|
||||
return new BoundingBox3D();
|
||||
|
||||
var minX = _pathPoints.Min(p => p.X);
|
||||
var minY = _pathPoints.Min(p => p.Y);
|
||||
var minZ = _pathPoints.Min(p => p.Z);
|
||||
var maxX = _pathPoints.Max(p => p.X);
|
||||
var maxY = _pathPoints.Max(p => p.Y);
|
||||
var maxZ = _pathPoints.Max(p => p.Z);
|
||||
|
||||
return new BoundingBox3D(
|
||||
new Point3D(minX, minY, minZ),
|
||||
new Point3D(maxX, maxY, maxZ)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建虚拟包围盒(用于碰撞检测)
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user