Heightfield不属于Convex mesh,不能直接使用通用的gjk算法(*也可以通过扩充三角面实现,UE5 chaos使用了这种方法),需要单独拿出来看,从Sphere-Heightfield入手能更方便了解PhysX heightfield相关的碰撞实现(注:Sphere-Heightfield还有PCM的实现方式,遗弃的3.4版本考虑厚度的方式,这里分析最基本的)
Heightfield在Physx中存储可以理解为一个数组,和高度图类似,每个坐标点就对应一个高度,同时还有地形物理材质(两个三角形),因为可能有挖洞等。
Narrow Phase源码位置:GuContactSphereMesh.cpp contactSphereHeightfield
大致思路:利用sphere的AABB min和max索引到地形中对应的最小、最大方格坐标,然后再循环逐三角形判断是否相交,生成contacts。
Physx对于最终contacts的处理的方式面的碰撞一定保留,其余根据距离排序,然后移除一些顶点或边的索引在前面已经存在的。
1.利用Sphere的AABB获取所有的三角形
2.因为计算三角形与球的最近交点也有一定计算量,在遍历的过程中根据三角形高度做一个剔除,然后判断是否是地形中的洞。
3.callback->onEvent即是对三角形做出处理
closestPtPointTriangle就不详细展开了,有兴趣的同学可以仔细去看,会返回fc FeatureCode表示最近点是vertex,edge还是face,三角形上距离圆心最近的点就是penetration最深的点。
4.最后generateLastContacts,对于face碰撞,前面已经加入最终contact结果并cache,对于vertex和edge碰撞,根据距离进行排序,如果已经处理的index索引和前面没有重复则加入,同时也会将三角形索引cache用于后面的排除。
注意validate函数并不是严格去重,而是只要检查到索引相同就会排除,比如前面face已经加入三个索引,那么后面和这个三个索引相关的edge、vertex碰撞都会被排除。
Physx对于Contact的处理个人感觉可能有几个原因,出于堆叠稳定性(多个点-面碰撞)、尽量保留最大penetration,部分情况下去重。
5.最后加入contact结果
注:如果最近点和圆心重合,那么会使用三角形的法线作为碰撞法线
Physx的Sphere-HeightField碰撞检测没有使用mid-phase(比如BVH,四叉树等)对HeightField本身处理,而trianglemesh是有使用的,个人感觉可能是因为在实践中需要进行碰撞检测的物体相比于地形分辨率一般确实都不会太大,可以直接索引到地形中对应三角形,额外的结构反而是负担。
UE5的Chaos实现方式和Physx类似
Bullet的代码结构和Physx有一些区别,Sphere继承自ConvexShape,Heightfield继承自ConcaveShape,代码位于ConvexConcave中