- 删除 DataBindingPerformanceMonitor.cs 文件 - 从 NavisworksTransportPlugin.csproj 移除编译引用 - 清理 ViewModelBase.cs 中的所有性能监控代码 - 清理 ThreadSafeObservableCollection.cs 中的性能监控集成 - 清理 SmartDataBindingOptimizer.cs 中的性能监控使用 - 清理 BindingExpressionOptimizer.cs 中的性能监控调用 该功能不再需要,移除后简化了代码结构
1196 lines
34 KiB
Markdown
1196 lines
34 KiB
Markdown
# geometry3Sharp 空间数据结构集成方案
|
||
|
||
## 1. 背景与动机
|
||
|
||
### 1.1 当前碰撞检测的性能瓶颈
|
||
|
||
NavisworksTransport 项目在物流路径动画中实现了碰撞检测功能,但存在以下性能问题:
|
||
|
||
**当前实现的问题**:
|
||
|
||
```csharp
|
||
// ClashDetectiveIntegration.cs - DetectCollisions()
|
||
// 线性遍历所有几何对象,O(n) 复杂度
|
||
foreach (var item in allGeometryItems)
|
||
{
|
||
if (IsColliding(animatedObject, item))
|
||
{
|
||
collisions.Add(item);
|
||
}
|
||
}
|
||
```
|
||
|
||
**性能数据**(来自实际日志):
|
||
|
||
- 场景对象总数:290 个
|
||
- 潜在碰撞对象:108 个(经过路径包围盒预过滤)
|
||
- 每帧碰撞检测:遍历 108 个对象
|
||
- 动画总帧数:300 帧
|
||
- **总计算量**:300 × 108 = 32,400 次包围盒检测
|
||
|
||
**优化空间**:
|
||
|
||
1. 空间查询仍然是线性遍历(即使经过预过滤)
|
||
2. 包围盒距离计算不精确(AABB vs 实际几何距离)
|
||
3. 没有层次化空间索引加速
|
||
4. 动态查询效率低(物体移动时重新遍历)
|
||
|
||
### 1.2 空间数据结构的必要性
|
||
|
||
**需求场景**:
|
||
|
||
1. **动画碰撞检测**:物体每帧移动,需要快速查询"当前位置附近有哪些障碍物"
|
||
2. **潜在碰撞对象过滤**:在路径规划前,快速找到路径沿途的所有可能碰撞物体
|
||
3. **精确距离计算**:计算物体到障碍物的真实距离(不是包围盒距离)
|
||
4. **路径可达性验证**:检查两点间的直线是否被障碍物阻挡
|
||
|
||
**理想的解决方案特征**:
|
||
|
||
- ✅ O(log n) 或 O(1) 平均查询复杂度
|
||
- ✅ 支持动态位置的范围查询
|
||
- ✅ 精确的几何距离计算
|
||
- ✅ 商业友好的许可证
|
||
- ✅ 与 .NET Framework 4.8 兼容
|
||
|
||
---
|
||
|
||
## 2. geometry3Sharp 库概述
|
||
|
||
### 2.1 基本信息
|
||
|
||
**项目信息**:
|
||
|
||
- **官方名称**:geometry3Sharp (本项目使用的是 fork: geometry4Sharp)
|
||
- **GitHub**:<https://github.com/gradientspace/geometry3Sharp> (原版)
|
||
- **GitHub**:<https://github.com/NewWheelTech/geometry4Sharp> (fork)
|
||
- **许可证**:Boost Software License 1.0(商业友好,无需开源衍生代码)
|
||
- **维护状态**:原版活跃度降低(2019后),fork 有维护
|
||
- **平台兼容**:.NET Standard 2.0, .NET Framework 4.8, .NET 6.0
|
||
- **语言**:纯 C#,无 C++ 互操作
|
||
|
||
**代码来源**:
|
||
|
||
- 核心算法移植自 David Eberly 的 WildMagic5 和 GTEngine(几何计算领域的经典库)
|
||
- MeshSignedDistanceGrid 基于 Christopher Batty 的 SDFGen C++ 代码
|
||
- 经过多年实战验证,代码质量高
|
||
|
||
### 2.2 与体素网格方案的关系
|
||
|
||
**重要说明**:geometry3Sharp 不是替代体素网格方案,而是**互补关系**。
|
||
|
||
| 模块 | 用途 | 应用场景 |
|
||
|------|------|---------|
|
||
| **体素网格 (MeshSignedDistanceGrid, Bitmap3, DSparseGrid3)** | 3D 空间离散化,路径规划 | 生成可通行网格,3D A* 路径规划 |
|
||
| **空间索引 (DMeshAABBTree3, PointHashGrid3d)** | 快速空间查询,碰撞检测 | 动画碰撞检测,范围查询优化 |
|
||
| **距离查询 (DistPoint3Triangle3)** | 精确几何距离 | 碰撞容差计算,最近点查询 |
|
||
| **相交查询 (IntrRay3Triangle3)** | 射线-几何相交 | 路径可达性验证,视线检测 |
|
||
|
||
**集成策略**:
|
||
|
||
- 体素网格用于**路径规划**(已在 `体素网格路径规划方案.md` 中详细设计)
|
||
- 空间索引用于**碰撞检测加速**(本文档重点)
|
||
- 两者共用 geometry3Sharp 的基础数学类型(Vector3d, AxisAlignedBox3d 等)
|
||
|
||
---
|
||
|
||
## 3. 高价值模块详细分析
|
||
|
||
### 3.1 DMeshAABBTree3 - 层次化 AABB 树
|
||
|
||
#### 功能描述
|
||
|
||
`DMeshAABBTree3` 是三角网格的层次化轴对齐包围盒树,提供 O(log n) 复杂度的空间查询。
|
||
|
||
**核心特性**:
|
||
|
||
- 自底向上或自顶向下构建策略(可选)
|
||
- 支持三角形级别的精确相交测试
|
||
- 多种空间查询接口
|
||
- 泛型遍历接口(DoTraversal)
|
||
|
||
**可用查询**:
|
||
|
||
```csharp
|
||
// 1. 最近三角形查找
|
||
int FindNearestTriangle(Point3d point, double maxDistance);
|
||
|
||
// 2. 射线-网格最近命中
|
||
int FindNearestHitTriangle(Ray3d ray, double maxDistance);
|
||
|
||
// 3. 射线-网格所有命中
|
||
List<int> FindAllHitTriangles(Ray3d ray, double maxDistance);
|
||
|
||
// 4. 三角形相交测试
|
||
bool TestIntersection(Triangle3d triangle);
|
||
|
||
// 5. 树-树相交测试
|
||
bool TestIntersection(DMeshAABBTree3 otherTree);
|
||
|
||
// 6. 树-树所有相交
|
||
List<Tuple<int,int>> FindAllIntersections(DMeshAABBTree3 otherTree);
|
||
|
||
// 7. 树-树范围内最近三角形
|
||
List<Tuple<int,int>> FindNearestTriangles(DMeshAABBTree3 otherTree, double maxDist);
|
||
|
||
// 8. 点包含测试
|
||
bool IsInside(Point3d point);
|
||
|
||
// 9. 缠绕数(Winding Number)
|
||
double WindingNumber(Point3d point);
|
||
```
|
||
|
||
#### 应用场景
|
||
|
||
场景 1:优化 GetPotentialColliders()**
|
||
|
||
当前实现(线性遍历):
|
||
|
||
```csharp
|
||
// PathAnimationManager.cs - GetPotentialColliders()
|
||
foreach (var item in allGeometryItems)
|
||
{
|
||
double distance = CalculateDistance(pathCenter, item);
|
||
if (distance <= detectionGap)
|
||
{
|
||
potentialColliders.Add(item);
|
||
}
|
||
}
|
||
```
|
||
|
||
使用 AABB 树优化(对数查询):
|
||
|
||
```csharp
|
||
// 1. 预构建场景的 AABB 树(一次性)
|
||
var sceneTree = BuildSceneAABBTree(allGeometryItems);
|
||
|
||
// 2. 快速范围查询
|
||
var potentialColliders = sceneTree.FindNearestTriangles(
|
||
animatedObjectTree,
|
||
detectionGap
|
||
);
|
||
```
|
||
|
||
**性能提升预估**:
|
||
|
||
- 当前:O(n) = 290 次包围盒测试
|
||
- 优化后:O(log n) ≈ log₂(290) ≈ 8 次树节点遍历
|
||
- **加速比**:约 36 倍
|
||
|
||
场景 2:精确碰撞检测**
|
||
|
||
替代简单包围盒检测,使用三角形级别的精确相交:
|
||
|
||
```csharp
|
||
// 检查动画对象是否与场景相交
|
||
bool hasCollision = animatedObjectTree.TestIntersection(sceneTree);
|
||
|
||
// 获取所有相交的三角形对
|
||
var intersections = animatedObjectTree.FindAllIntersections(sceneTree);
|
||
```
|
||
|
||
#### 构建策略选择
|
||
|
||
```csharp
|
||
public enum BuildStrategy
|
||
{
|
||
TopDownMidpoint, // 最快(默认)
|
||
BottomUpFromOneRings, // 平衡但慢 2.5 倍
|
||
TopDownMedian // 更平衡但慢 2-4 倍
|
||
}
|
||
|
||
// 推荐:快速构建 + 大量查询场景
|
||
var tree = new DMeshAABBTree3(mesh, autoBuild: false);
|
||
tree.Build(BuildStrategy.TopDownMidpoint);
|
||
```
|
||
|
||
#### 技术挑战
|
||
|
||
挑战 1:Navisworks Geometry → DMesh3 转换
|
||
|
||
Navisworks 的 `ModelItem` 不直接暴露三角网格,需要通过几何提取:
|
||
|
||
```csharp
|
||
// 方案 A:使用 GeometryExtractor(项目已有)
|
||
// 需要扩展为输出 geometry3Sharp 的 DMesh3 格式
|
||
|
||
// 方案 B:使用包围盒代替精确网格(简化版)
|
||
// 优点:实现简单,性能好
|
||
// 缺点:精度降低(但对预过滤足够)
|
||
```
|
||
|
||
**推荐方案**:渐进式实现
|
||
|
||
1. **Phase 1**:使用包围盒近似(快速验证)
|
||
2. **Phase 2**:实现精确几何提取(提升精度)
|
||
|
||
---
|
||
|
||
### 3.2 PointHashGrid3d - 空间哈希网格
|
||
|
||
#### 功能描述
|
||
|
||
`PointHashGrid3d<T>` 是泛型的 3D 空间哈希表,提供 O(1) 平均时间复杂度的范围查询。
|
||
|
||
**核心特性**:
|
||
|
||
- 基于网格单元的哈希索引
|
||
- 支持动态插入/删除/更新
|
||
- 线程安全(SpinLock)
|
||
- 泛型设计,可存储任意类型
|
||
|
||
**关键方法**:
|
||
|
||
```csharp
|
||
// 初始化(cellSize = 查询半径的 1-2 倍)
|
||
var grid = new PointHashGrid3d<ModelItem>(cellSize: 2.0, invalidValue: null);
|
||
|
||
// 插入点(线程安全)
|
||
grid.InsertPoint(modelItem, position);
|
||
|
||
// 删除点
|
||
grid.RemovePoint(modelItem, position);
|
||
|
||
// 更新点(移动)
|
||
grid.UpdatePoint(modelItem, oldPosition, newPosition);
|
||
|
||
// 范围查询(核心功能)
|
||
var nearby = grid.FindNearestInRadius(
|
||
queryPosition,
|
||
radius,
|
||
distanceFunc: (item) => Distance(queryPosition, item.Position)
|
||
);
|
||
|
||
// 批量查询
|
||
List<T> FindPointsInBox(AxisAlignedBox3d box);
|
||
```
|
||
|
||
#### 应用场景
|
||
|
||
场景 1:加速动画碰撞检测
|
||
|
||
当前问题:
|
||
|
||
- 动画每帧都要遍历 108 个潜在碰撞对象
|
||
- 300 帧 × 108 对象 = 32,400 次检测
|
||
|
||
使用空间哈希优化:
|
||
|
||
```csharp
|
||
// === 初始化阶段(动画开始前)===
|
||
var spatialGrid = new PointHashGrid3d<ModelItem>(
|
||
cellSize: vehicleRadius * 2, // 格子大小 = 车辆直径
|
||
invalidValue: null
|
||
);
|
||
|
||
// 索引所有障碍物(一次性)
|
||
foreach (var obstacle in potentialColliders)
|
||
{
|
||
spatialGrid.InsertPoint(obstacle, obstacle.BoundingBox().Center);
|
||
}
|
||
|
||
// === 动画每帧 ===
|
||
// 快速查询当前位置附近的障碍物
|
||
var nearbyObstacles = spatialGrid.FindNearestInRadius(
|
||
currentPosition,
|
||
vehicleRadius + safetyMargin,
|
||
(item) => Vector3d.Distance(item.Position, currentPosition)
|
||
);
|
||
|
||
// 只检测附近的对象(通常 < 10 个)
|
||
foreach (var obstacle in nearbyObstacles)
|
||
{
|
||
if (IsColliding(animatedObject, obstacle))
|
||
{
|
||
collisions.Add(obstacle);
|
||
}
|
||
}
|
||
```
|
||
|
||
**性能提升预估**:
|
||
|
||
- 当前:每帧检测 108 个对象
|
||
- 优化后:每帧检测 5-15 个对象(仅邻近格子)
|
||
- **加速比**:约 7-20 倍
|
||
|
||
场景 2:动态场景更新
|
||
|
||
支持障碍物动态添加/移动:
|
||
|
||
```csharp
|
||
// 添加新障碍物
|
||
spatialGrid.InsertPoint(newObstacle, newObstacle.Position);
|
||
|
||
// 移动障碍物
|
||
spatialGrid.UpdatePoint(movingObstacle, oldPosition, newPosition);
|
||
|
||
// 删除障碍物
|
||
spatialGrid.RemovePoint(removedObstacle, removedObstacle.Position);
|
||
```
|
||
|
||
#### 格子大小选择
|
||
|
||
**原则**:cellSize 应该接近查询半径
|
||
|
||
```csharp
|
||
// 推荐设置
|
||
double vehicleRadius = 0.3; // 米
|
||
double safetyMargin = 0.1; // 米
|
||
double detectionRadius = vehicleRadius + safetyMargin;
|
||
|
||
// cellSize = 查询半径 * 1.5
|
||
double cellSize = detectionRadius * 1.5; // 约 0.6 米
|
||
|
||
var spatialGrid = new PointHashGrid3d<ModelItem>(cellSize, null);
|
||
```
|
||
|
||
**权衡**:
|
||
|
||
- cellSize 过小 → 格子太多,内存和查询开销大
|
||
- cellSize 过大 → 每个格子对象太多,退化为线性查询
|
||
|
||
---
|
||
|
||
### 3.3 DistPoint3Triangle3 - 精确距离查询
|
||
|
||
#### 功能描述
|
||
|
||
计算点到三角形的最短距离及最近点位置。
|
||
|
||
**核心方法**:
|
||
|
||
```csharp
|
||
var query = new DistPoint3Triangle3(point, triangle);
|
||
double distance = query.Get(); // 最短距离
|
||
Vector3d closestPoint = query.TriangleClosest; // 三角形上最近点
|
||
int closestType = query.TriangleClosestType; // 最近点类型(顶点/边/面)
|
||
```
|
||
|
||
**相关类**:
|
||
|
||
```csharp
|
||
DistPoint3Circle3 // 点到圆的距离
|
||
DistPoint3Cylinder3 // 点到圆柱的距离(带符号)
|
||
DistLine3Triangle3 // 线段到三角形的距离
|
||
DistSegment3Triangle3 // 线段到三角形的距离
|
||
DistTriangle3Triangle3 // 三角形到三角形的距离
|
||
```
|
||
|
||
#### 应用场景
|
||
|
||
场景 1:精确碰撞容差
|
||
|
||
当前问题:
|
||
|
||
```csharp
|
||
// BoundingBoxGeometryUtils.cs - CalculateDistance()
|
||
// 只计算包围盒中心的距离,不精确
|
||
double distance = Vector3.Distance(bbox1.Center, bbox2.Center);
|
||
```
|
||
|
||
使用精确距离:
|
||
|
||
```csharp
|
||
// 计算物体表面到障碍物表面的真实距离
|
||
foreach (var triangle1 in animatedObjectMesh.Triangles)
|
||
{
|
||
foreach (var triangle2 in obstacleMesh.Triangles)
|
||
{
|
||
var query = new DistTriangle3Triangle3(triangle1, triangle2);
|
||
double distance = query.Get();
|
||
|
||
if (distance < collisionThreshold)
|
||
{
|
||
// 记录碰撞,包含精确距离和接触点
|
||
collisions.Add(new CollisionInfo
|
||
{
|
||
Distance = distance,
|
||
ContactPoint1 = query.Triangle1Closest,
|
||
ContactPoint2 = query.Triangle2Closest
|
||
});
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
场景 2:碰撞报告增强
|
||
|
||
在碰撞报告中显示:
|
||
|
||
- 精确的间隙距离(而非包围盒距离)
|
||
- 最近接触点的 3D 坐标
|
||
- 可视化最短距离向量
|
||
|
||
---
|
||
|
||
### 3.4 IntrRay3Triangle3 - 射线相交查询
|
||
|
||
#### 功能描述
|
||
|
||
计算射线/线段与三角形的相交。
|
||
|
||
**核心方法**:
|
||
|
||
```csharp
|
||
// 射线-三角形相交
|
||
var query = new IntrRay3Triangle3(ray, triangle);
|
||
bool intersects = query.Find(); // 是否相交
|
||
double rayParameter = query.RayParameter; // 射线参数 t (交点 = ray.Origin + t * ray.Direction)
|
||
Vector3d intersectionPoint = ray.PointAt(rayParameter);
|
||
|
||
// 线段-三角形相交
|
||
var segmentQuery = new IntrSegment3Triangle3(segment, triangle);
|
||
bool intersects = segmentQuery.Find();
|
||
```
|
||
|
||
**相关类**:
|
||
|
||
```csharp
|
||
IntrLine2Line2 // 2D 直线相交
|
||
IntrSegment2Segment2 // 2D 线段相交
|
||
IntrTriangle3Triangle3 // 3D 三角形相交
|
||
IntrRay3Box3 // 射线-AABB 相交
|
||
IntrRay3AxisAlignedBox3 // 射线-轴对齐盒相交
|
||
```
|
||
|
||
#### 应用场景
|
||
|
||
场景 1:路径可达性验证
|
||
|
||
检查两点间的直线路径是否被障碍物阻挡:
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 检查直线路径是否可达(无障碍物阻挡)
|
||
/// </summary>
|
||
public bool IsPathClear(Point3D start, Point3D end, IEnumerable<DMesh3> obstacles)
|
||
{
|
||
Vector3d direction = (end - start).Normalized;
|
||
double distance = Vector3d.Distance(start, end);
|
||
var ray = new Ray3d(start, direction);
|
||
|
||
foreach (var obstacleMesh in obstacles)
|
||
{
|
||
foreach (var triangleId in obstacleMesh.TriangleIndices())
|
||
{
|
||
var triangle = GetTriangle(obstacleMesh, triangleId);
|
||
var query = new IntrRay3Triangle3(ray, triangle);
|
||
|
||
if (query.Find() && query.RayParameter < distance)
|
||
{
|
||
// 射线在到达终点前与障碍物相交
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
return true; // 路径畅通
|
||
}
|
||
```
|
||
|
||
场景 2:视线检测
|
||
|
||
判断两个物体之间是否有遮挡:
|
||
|
||
```csharp
|
||
// 检查从起重机到目标物体的视线是否被遮挡
|
||
bool hasLineOfSight = IsPathClear(crane.Position, targetObject.Position, obstacles);
|
||
```
|
||
|
||
**场景 3:改进 A* 路径规划**
|
||
|
||
在 A* 算法中增加射线检测,避免规划出会穿模的路径:
|
||
|
||
```csharp
|
||
// 生成路径后,后处理优化
|
||
var optimizedPath = new List<Point3D>();
|
||
optimizedPath.Add(path[0]);
|
||
|
||
for (int i = 0; i < path.Count - 1; i++)
|
||
{
|
||
// 尝试直接连接当前点和后续点
|
||
for (int j = path.Count - 1; j > i + 1; j--)
|
||
{
|
||
if (IsPathClear(path[i], path[j], obstacles))
|
||
{
|
||
optimizedPath.Add(path[j]);
|
||
i = j - 1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 中等价值模块(可选)
|
||
|
||
### 4.1 Polygon2d - 2D 多边形操作
|
||
|
||
**功能**:
|
||
|
||
- 点在多边形内测试(Contains)
|
||
- 多边形简化(Simplify)
|
||
- 多边形裁剪
|
||
- 多边形布尔运算
|
||
|
||
**应用场景**:
|
||
|
||
- 楼层区域判断(路径点是否在楼层边界内)
|
||
- 2D 路径规划(作为 3D 规划的补充)
|
||
|
||
### 4.2 ConvexHull2 - 2D 凸包
|
||
|
||
**功能**:
|
||
|
||
- 计算点集的 2D 凸包
|
||
|
||
**应用场景**:
|
||
|
||
- 楼层边界自动识别
|
||
- 路径覆盖区域计算
|
||
|
||
### 4.3 MeshPlaneCut - 网格平面切割
|
||
|
||
**功能**:
|
||
|
||
- 用平面切割三角网格
|
||
- 返回切割边界(EdgeLoop)
|
||
|
||
**应用场景**:
|
||
|
||
- 楼层切片(按高度切分建筑模型)
|
||
- 断面分析(生成路径断面视图)
|
||
|
||
---
|
||
|
||
## 5. 集成架构设计
|
||
|
||
### 5.1 整体架构
|
||
|
||
```graph
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Navisworks API │
|
||
│ (ModelItem, BoundingBox3D, Geometry) │
|
||
└────────────────────────┬────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ NavisworksGeometryAdapter (新) │
|
||
│ - ModelItem → DMesh3 │
|
||
│ - Point3D ↔ Vector3d │
|
||
│ - BoundingBox3D ↔ AxisAlignedBox3d │
|
||
└────────────────────────┬────────────────────────────────────┘
|
||
│
|
||
┌────────────┴────────────┐
|
||
▼ ▼
|
||
┌─────────────────────┐ ┌─────────────────────┐
|
||
│ SpatialIndexCache │ │ CollisionQueryHelper│
|
||
│ (新) │ │ (新) │
|
||
│ │ │ │
|
||
│ - SceneAABBTree │ │ - DistanceQuery │
|
||
│ - ObstacleHashGrid │ │ - IntersectionQuery │
|
||
│ - 缓存管理 │ │ - 精确碰撞检测 │
|
||
└──────────┬──────────┘ └──────────┬──────────┘
|
||
│ │
|
||
│ ┌─────────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ ClashDetectiveIntegration (改进) │
|
||
│ - GetPotentialColliders() → 使用 SpatialIndexCache │
|
||
│ - DetectCollisions() → 使用 CollisionQueryHelper │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ PathAnimationManager (无需修改) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 5.2 核心类设计
|
||
|
||
#### 5.2.1 NavisworksGeometryAdapter - 适配层
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// Navisworks API 与 geometry3Sharp 之间的转换适配器
|
||
/// </summary>
|
||
public static class NavisworksGeometryAdapter
|
||
{
|
||
#region 坐标转换
|
||
|
||
/// <summary>
|
||
/// Navisworks Point3D → geometry3Sharp Vector3d
|
||
/// </summary>
|
||
public static Vector3d ToVector3d(this Point3D point)
|
||
{
|
||
return new Vector3d(point.X, point.Y, point.Z);
|
||
}
|
||
|
||
/// <summary>
|
||
/// geometry3Sharp Vector3d → Navisworks Point3D
|
||
/// </summary>
|
||
public static Point3D ToPoint3D(this Vector3d vector)
|
||
{
|
||
return new Point3D(vector.x, vector.y, vector.z);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Navisworks BoundingBox3D → geometry3Sharp AxisAlignedBox3d
|
||
/// </summary>
|
||
public static AxisAlignedBox3d ToAxisAlignedBox3d(this BoundingBox3D bbox)
|
||
{
|
||
return new AxisAlignedBox3d(
|
||
bbox.Min.ToVector3d(),
|
||
bbox.Max.ToVector3d()
|
||
);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 几何提取
|
||
|
||
/// <summary>
|
||
/// 从 ModelItem 提取三角网格(简化版:使用包围盒)
|
||
/// </summary>
|
||
public static DMesh3 ExtractMeshSimple(ModelItem item)
|
||
{
|
||
var bbox = item.BoundingBox();
|
||
var mesh = new DMesh3();
|
||
|
||
// 将包围盒转为 8 个顶点 + 12 个三角形
|
||
// (简化实现,适用于快速验证)
|
||
AddBoxToMesh(mesh, bbox.ToAxisAlignedBox3d());
|
||
|
||
return mesh;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从 ModelItem 提取精确三角网格(完整版,Phase 2 实现)
|
||
/// </summary>
|
||
public static DMesh3 ExtractMeshAccurate(ModelItem item)
|
||
{
|
||
// TODO: 使用 GeometryExtractor 获取实际三角形
|
||
// 需要遍历 Navisworks Geometry API
|
||
throw new NotImplementedException("Phase 2");
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
```
|
||
|
||
#### 5.2.2 SpatialIndexCache - 空间索引缓存
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 场景空间索引缓存管理器
|
||
/// 在动画开始前预构建空间索引,动画过程中复用
|
||
/// </summary>
|
||
public class SpatialIndexCache
|
||
{
|
||
private static SpatialIndexCache _instance;
|
||
public static SpatialIndexCache Instance => _instance ??= new SpatialIndexCache();
|
||
|
||
// 场景 AABB 树(用于精确查询)
|
||
private DMeshAABBTree3 _sceneAABBTree;
|
||
|
||
// 障碍物空间哈希网格(用于快速范围查询)
|
||
private PointHashGrid3d<ModelItem> _obstacleHashGrid;
|
||
|
||
// 缓存版本(检测场景变化)
|
||
private int _cacheVersion = 0;
|
||
|
||
#region 构建方法
|
||
|
||
/// <summary>
|
||
/// 构建场景的空间索引
|
||
/// </summary>
|
||
public void BuildSpatialIndex(
|
||
IEnumerable<ModelItem> obstacles,
|
||
double hashGridCellSize)
|
||
{
|
||
LogManager.Info("[空间索引] 开始构建...");
|
||
var sw = Stopwatch.StartNew();
|
||
|
||
// 1. 构建 AABB 树
|
||
var sceneMesh = CombineMeshes(obstacles);
|
||
_sceneAABBTree = new DMeshAABBTree3(sceneMesh, autoBuild: true);
|
||
LogManager.Info($"[空间索引] AABB 树构建完成,节点数: {_sceneAABBTree.MaxLeafSize}");
|
||
|
||
// 2. 构建空间哈希网格
|
||
_obstacleHashGrid = new PointHashGrid3d<ModelItem>(hashGridCellSize, null);
|
||
foreach (var obstacle in obstacles)
|
||
{
|
||
var center = obstacle.BoundingBox().Center.ToVector3d();
|
||
_obstacleHashGrid.InsertPoint(obstacle, center);
|
||
}
|
||
LogManager.Info($"[空间索引] 哈希网格构建完成,格子大小: {hashGridCellSize}m");
|
||
|
||
_cacheVersion++;
|
||
sw.Stop();
|
||
LogManager.Info($"[空间索引] 构建完成,耗时: {sw.ElapsedMilliseconds}ms");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 查询方法
|
||
|
||
/// <summary>
|
||
/// 快速范围查询(使用哈希网格)
|
||
/// </summary>
|
||
public List<ModelItem> FindNearbyObstacles(Point3D center, double radius)
|
||
{
|
||
if (_obstacleHashGrid == null)
|
||
throw new InvalidOperationException("空间索引未初始化");
|
||
|
||
var centerVec = center.ToVector3d();
|
||
var nearby = new List<ModelItem>();
|
||
|
||
// 哈希网格范围查询
|
||
// 注意:PointHashGrid3d 没有直接的 FindNearestInRadius 方法
|
||
// 需要手动实现或使用 FindPointsInBox
|
||
var searchBox = new AxisAlignedBox3d(centerVec, radius);
|
||
|
||
// 遍历搜索框内的所有格子(简化实现)
|
||
// 实际应该调用哈希网格的范围查询方法
|
||
// 这里需要根据 geometry3Sharp 的实际 API 调整
|
||
|
||
return nearby;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 精确相交查询(使用 AABB 树)
|
||
/// </summary>
|
||
public bool TestIntersection(DMesh3 objectMesh)
|
||
{
|
||
if (_sceneAABBTree == null)
|
||
throw new InvalidOperationException("空间索引未初始化");
|
||
|
||
var objectTree = new DMeshAABBTree3(objectMesh, autoBuild: true);
|
||
return _sceneAABBTree.TestIntersection(objectTree);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
|
||
private DMesh3 CombineMeshes(IEnumerable<ModelItem> items)
|
||
{
|
||
var combinedMesh = new DMesh3();
|
||
|
||
foreach (var item in items)
|
||
{
|
||
var itemMesh = NavisworksGeometryAdapter.ExtractMeshSimple(item);
|
||
var editor = new MeshEditor(combinedMesh);
|
||
editor.AppendMesh(itemMesh);
|
||
}
|
||
|
||
return combinedMesh;
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
```
|
||
|
||
#### 5.2.3 CollisionQueryHelper - 碰撞查询助手
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 精确碰撞查询辅助类
|
||
/// </summary>
|
||
public static class CollisionQueryHelper
|
||
{
|
||
/// <summary>
|
||
/// 计算物体到障碍物的最短距离
|
||
/// </summary>
|
||
public static double CalculateMinDistance(
|
||
ModelItem movingObject,
|
||
ModelItem obstacle)
|
||
{
|
||
var mesh1 = NavisworksGeometryAdapter.ExtractMeshSimple(movingObject);
|
||
var mesh2 = NavisworksGeometryAdapter.ExtractMeshSimple(obstacle);
|
||
|
||
double minDistance = double.MaxValue;
|
||
|
||
// 遍历所有三角形对,找到最短距离
|
||
foreach (var tri1Id in mesh1.TriangleIndices())
|
||
{
|
||
var tri1 = GetTriangle(mesh1, tri1Id);
|
||
|
||
foreach (var tri2Id in mesh2.TriangleIndices())
|
||
{
|
||
var tri2 = GetTriangle(mesh2, tri2Id);
|
||
|
||
var query = new DistTriangle3Triangle3(tri1, tri2);
|
||
double distance = query.Get();
|
||
|
||
if (distance < minDistance)
|
||
{
|
||
minDistance = distance;
|
||
}
|
||
}
|
||
}
|
||
|
||
return minDistance;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查射线路径是否畅通
|
||
/// </summary>
|
||
public static bool IsPathClear(
|
||
Point3D start,
|
||
Point3D end,
|
||
DMeshAABBTree3 sceneTree)
|
||
{
|
||
var direction = (end.ToVector3d() - start.ToVector3d()).Normalized;
|
||
var distance = Vector3d.Distance(start.ToVector3d(), end.ToVector3d());
|
||
var ray = new Ray3d(start.ToVector3d(), direction);
|
||
|
||
// 使用 AABB 树快速查询射线相交
|
||
int hitTriangle = sceneTree.FindNearestHitTriangle(ray);
|
||
|
||
if (hitTriangle == DMesh3.InvalidID)
|
||
return true; // 无相交
|
||
|
||
// 检查相交点是否在起终点之间
|
||
// TODO: 需要获取相交点位置并比较距离
|
||
|
||
return false;
|
||
}
|
||
|
||
private static Triangle3d GetTriangle(DMesh3 mesh, int triangleId)
|
||
{
|
||
var tri = mesh.GetTriangle(triangleId);
|
||
var v0 = mesh.GetVertex(tri.a);
|
||
var v1 = mesh.GetVertex(tri.b);
|
||
var v2 = mesh.GetVertex(tri.c);
|
||
return new Triangle3d(v0, v1, v2);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.3 集成到现有系统
|
||
|
||
#### 修改 ClashDetectiveIntegration.cs
|
||
|
||
```csharp
|
||
// 在 GetPotentialColliders() 中使用空间索引
|
||
private List<ModelItem> GetPotentialColliders()
|
||
{
|
||
// 1. 计算路径包围盒
|
||
var pathBounds = CalculatePathBounds();
|
||
var pathCenter = GetBoundingBoxCenter(pathBounds);
|
||
var detectionRadius = GetBoundingBoxDiagonal(pathBounds) / 2;
|
||
|
||
// 2. 使用空间哈希网格快速查询
|
||
var potentialColliders = SpatialIndexCache.Instance.FindNearbyObstacles(
|
||
pathCenter,
|
||
detectionRadius
|
||
);
|
||
|
||
LogManager.Info($"空间索引查询结果: {potentialColliders.Count} 个潜在碰撞对象");
|
||
|
||
return potentialColliders;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 实施方案
|
||
|
||
### 6.1 分阶段实施
|
||
|
||
#### Phase 1: PointHashGrid3d 集成(2-3 天,立即可用)
|
||
|
||
**目标**:使用空间哈希网格优化动画碰撞检测。
|
||
|
||
**任务**:
|
||
|
||
1. 创建 `NavisworksGeometryAdapter` 类(坐标转换)
|
||
2. 创建 `SpatialIndexCache` 类(仅哈希网格部分)
|
||
3. 修改 `ClashDetectiveIntegration.GetPotentialColliders()` 使用哈希网格
|
||
4. 修改动画碰撞检测使用哈希网格查询
|
||
5. 性能测试和日志记录
|
||
|
||
**验收标准**:
|
||
|
||
- ✅ 动画碰撞检测速度提升 5-10 倍
|
||
- ✅ 日志显示每帧实际检测的对象数量减少
|
||
- ✅ 功能正确性不变(碰撞结果与原方案一致)
|
||
|
||
---
|
||
|
||
#### Phase 2: DMeshAABBTree3 优化(1 周)
|
||
|
||
**目标**:构建场景 AABB 树,优化范围查询和相交检测。
|
||
|
||
**任务**:
|
||
|
||
1. 实现 `NavisworksGeometryAdapter.ExtractMeshSimple()`(使用包围盒)
|
||
2. 在 `SpatialIndexCache` 中添加 AABB 树构建
|
||
3. 使用 AABB 树优化 `FindNearestTriangles()` 查询
|
||
4. 实现精确相交检测(替代简单包围盒测试)
|
||
5. 性能对比测试
|
||
|
||
**验收标准**:
|
||
|
||
- ✅ AABB 树构建时间 < 5 秒(中等场景)
|
||
- ✅ 范围查询速度提升 10-30 倍
|
||
- ✅ 支持精确三角形级别的碰撞检测
|
||
|
||
---
|
||
|
||
#### Phase 3: 精确距离和相交查询(可选,3-5 天)
|
||
|
||
**目标**:使用精确几何查询提升碰撞报告质量。
|
||
|
||
**任务**:
|
||
|
||
1. 创建 `CollisionQueryHelper` 类
|
||
2. 实现精确距离计算(DistTriangle3Triangle3)
|
||
3. 实现路径可达性验证(IntrRay3Triangle3)
|
||
4. 在碰撞报告中显示精确距离和接触点
|
||
5. (可选)实现 `ExtractMeshAccurate()` 精确几何提取
|
||
|
||
**验收标准**:
|
||
|
||
- ✅ 碰撞报告显示精确距离(非包围盒距离)
|
||
- ✅ 支持路径可达性验证功能
|
||
- ✅ 性能可接受(精确查询耗时 < 2x 简化查询)
|
||
|
||
---
|
||
|
||
### 6.2 时间与资源估算
|
||
|
||
| 阶段 | 任务 | 工作量 | 依赖 |
|
||
|------|------|--------|------|
|
||
| Phase 1 | PointHashGrid3d 集成 | 2-3 天 | geometry3Sharp |
|
||
| Phase 2 | DMeshAABBTree3 优化 | 5-7 天 | Phase 1 |
|
||
| Phase 3 | 精确查询(可选)| 3-5 天 | Phase 2 |
|
||
| **总计** | | **10-15 天** | |
|
||
|
||
---
|
||
|
||
## 7. 技术挑战与缓解
|
||
|
||
### 7.1 Navisworks API 限制
|
||
|
||
**挑战**:Navisworks 的 `ModelItem` 不直接暴露三角网格数据。
|
||
|
||
**缓解措施**:
|
||
|
||
- ✅ **Phase 1**:使用包围盒代替精确网格(快速验证)
|
||
- ✅ **Phase 2**:研究 `GeometryExtractor` 提取实际几何
|
||
- ✅ **Phase 3**:实现 Navisworks Geometry API → DMesh3 转换器
|
||
|
||
### 7.2 坐标系转换
|
||
|
||
**挑战**:Navisworks API 和 geometry3Sharp 使用不同的类型系统。
|
||
|
||
**缓解措施**:
|
||
|
||
- ✅ 创建 `NavisworksGeometryAdapter` 适配层
|
||
- ✅ 统一使用模型单位(避免米制/英制混淆)
|
||
- ✅ 单元测试验证转换正确性
|
||
|
||
### 7.3 性能权衡
|
||
|
||
**挑战**:空间索引构建有初始开销。
|
||
|
||
**缓解措施**:
|
||
|
||
- ✅ 在动画开始前预构建(用户点击"生成动画"时)
|
||
- ✅ 缓存索引(场景不变时复用)
|
||
- ✅ 显示进度条(让用户知道正在构建)
|
||
|
||
---
|
||
|
||
## 8. 性能预期
|
||
|
||
### 8.1 性能对比(预估)
|
||
|
||
基于类似规模的空间索引系统经验:
|
||
|
||
| 操作 | 当前实现 | Phase 1 (哈希) | Phase 2 (AABB树) |
|
||
|------|---------|---------------|-----------------|
|
||
| **范围查询** | O(n) = 290次 | O(1) ≈ 10次 | O(log n) ≈ 8次 |
|
||
| **碰撞检测** | 32,400次/动画 | 3,000次/动画 | 2,400次/动画 |
|
||
| **构建开销** | 0 秒 | 0.1 秒 | 2-5 秒 |
|
||
| **内存占用** | 0 MB | +10 MB | +50 MB |
|
||
|
||
**场景假设**:
|
||
|
||
- 对象总数:290 个
|
||
- 动画帧数:300 帧
|
||
- 潜在碰撞对象:108 个(经过预过滤)
|
||
|
||
### 8.2 预期加速比
|
||
|
||
**Phase 1 完成后**:
|
||
|
||
- 每帧碰撞检测:108 对象 → 10 对象
|
||
- **加速比**:约 10 倍
|
||
|
||
**Phase 2 完成后**:
|
||
|
||
- 范围查询:O(n) → O(log n)
|
||
- **加速比**:约 30-50 倍(组合效果)
|
||
|
||
---
|
||
|
||
## 9. 与体素网格方案的关系
|
||
|
||
### 9.1 互补关系说明
|
||
|
||
| 方案 | 主要用途 | 数据结构 | 查询类型 |
|
||
|------|---------|---------|---------|
|
||
| **体素网格** | 路径规划 | 3D 网格 + SDF | 可通行性、路径搜索 |
|
||
| **空间索引** | 碰撞检测 | AABB树、哈希网格 | 范围查询、相交测试 |
|
||
|
||
### 9.2 共用基础设施
|
||
|
||
两个方案共享 geometry3Sharp 的基础组件:
|
||
|
||
- 数学类型(Vector3d, AxisAlignedBox3d)
|
||
- 坐标转换适配器(NavisworksGeometryAdapter)
|
||
- 几何提取工具
|
||
|
||
### 9.3 集成策略
|
||
|
||
```graph
|
||
geometry3Sharp 基础库
|
||
├── 体素网格模块 (用于路径规划)
|
||
│ ├── MeshSignedDistanceGrid
|
||
│ ├── Bitmap3 / DSparseGrid3
|
||
│ └── VoxelGrid (自定义)
|
||
│
|
||
└── 空间索引模块 (用于碰撞检测)
|
||
├── DMeshAABBTree3
|
||
├── PointHashGrid3d
|
||
└── Distance/Intersection 查询
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 不适用的模块
|
||
|
||
以下 geometry3Sharp 模块**不适用于本项目**:
|
||
|
||
### 10.1 网格编辑类
|
||
|
||
- **Remesher**, **Reducer**, **MeshEditor**
|
||
- **原因**:Navisworks 是只读查看器,不支持修改网格几何
|
||
|
||
### 10.2 曲线生成类
|
||
|
||
- **NURBSCurve2**, **BezierCurve**
|
||
- **原因**:项目使用折线路径,不需要复杂曲线拟合
|
||
|
||
### 10.3 网格修复类
|
||
|
||
- **MeshAutoRepair**, **RemoveDuplicateTriangles**
|
||
- **原因**:Navisworks 模型已优化,无法运行时修改
|
||
|
||
### 10.4 布尔运算类
|
||
|
||
- **MeshBoolean**
|
||
- **原因**:文档明确说"不是健壮的布尔运算",且 Navisworks API 不支持
|
||
|
||
---
|
||
|
||
## 11. 实施建议
|
||
|
||
### 11.1 推荐的实施优先级
|
||
|
||
**立即实施(高优先级)**:
|
||
|
||
1. ✅ **PointHashGrid3d** → 快速见效,大幅提升动画碰撞检测性能
|
||
|
||
**短期实施(中优先级)**:
|
||
2. ✅ **DMeshAABBTree3** → 进一步优化,支持精确相交检测
|
||
|
||
**长期考虑(低优先级)**:
|
||
3. ⚠️ **精确距离查询** → 功能增强,非关键路径
|
||
4. ⚠️ **射线相交查询** → 路径验证功能,可选
|
||
|
||
### 11.2 风险提示
|
||
|
||
**不要全盘集成 geometry3Sharp**,应该:
|
||
|
||
- ✅ 只提取需要的类(避免依赖膨胀)
|
||
- ✅ 创建适配层(隔离外部依赖)
|
||
- ✅ 渐进式验证(每个 Phase 独立验收)
|
||
|
||
---
|
||
|
||
## 12. 参考资料
|
||
|
||
### 12.1 geometry3Sharp 资源
|
||
|
||
- **GitHub(原版)**:<https://github.com/gradientspace/geometry3Sharp>
|
||
- **GitHub(fork)**:<https://github.com/NewWheelTech/geometry4Sharp>
|
||
- **NuGet**:geometry4Sharp 1.0.0
|
||
- **原作者**:Ryan Schmidt (@rms80)
|
||
- **算法来源**:WildMagic5/GTEngine (David Eberly)
|
||
|
||
### 12.2 理论基础
|
||
|
||
- **空间哈希网格**:
|
||
- "Spatial Hashing for 3D Grids" - Teschner et al., 2003
|
||
- "Optimized Spatial Hashing for Collision Detection of Deformable Objects"
|
||
|
||
- **AABB 树**:
|
||
- "OBBTree: A Hierarchical Structure for Rapid Interference Detection" (Gottschalk et al., 1996)
|
||
- "Fast Collision Detection Using AABB Trees" - Real-Time Rendering
|
||
|
||
- **距离查询**:
|
||
- "Distance Between Point and Triangle in 3D" - David Eberly, Geometric Tools
|
||
- "Fast Distance Queries for Triangles, Lines and Points using SSE"
|
||
|
||
### 12.3 相关文档
|
||
|
||
- 《体素网格路径规划方案.md》- 路径规划部分
|
||
- 《PATHFINDING_DESIGN.md》- A* 算法设计
|
||
- 《寻路算法的对比.md》- 算法选型依据
|
||
|
||
---
|
||
|
||
## 13. 总结
|
||
|
||
### 13.1 核心价值
|
||
|
||
1. ✅ **显著的性能提升**:碰撞检测加速 10-50 倍
|
||
2. ✅ **商业友好许可证**:Boost License,无需开源
|
||
3. ✅ **成熟稳定的代码**:源自经典几何库 WildMagic5
|
||
4. ✅ **渐进式集成**:分阶段实施,风险可控
|
||
5. ✅ **互补体素方案**:两者协同,各司其职
|
||
|
||
### 13.2 建议行动
|
||
|
||
**立即行动**:
|
||
|
||
1. 安装 geometry4Sharp NuGet 包
|
||
2. 实施 Phase 1(PointHashGrid3d)
|
||
3. 测试性能提升
|
||
|
||
**后续行动**:
|
||
|
||
- Phase 1 验证后,评估是否继续 Phase 2
|
||
- 与体素网格方案协同推进
|
||
|
||
### 13.3 成功标准
|
||
|
||
- ✅ 动画碰撞检测速度提升 10 倍以上
|
||
- ✅ 构建开销可接受(< 5 秒)
|
||
- ✅ 内存占用增加 < 100 MB
|
||
- ✅ 功能正确性保持不变
|
||
- ✅ 代码可维护性良好
|
||
|
||
---
|
||
|
||
**文档版本**: v1.0
|
||
**创建日期**: 2025-10-14
|
||
**最后更新**: 2025-10-14
|
||
**作者**: NavisworksTransport 开发团队
|
||
**相关文档**: 《体素网格路径规划方案.md》
|