3. 八叉树与胶囊Capsule交叉计算
# 八叉树与胶囊Capsule交叉计算
通过前面两小节介绍,大家对八叉树Octree.js
和胶囊Capsule.js
扩展库都有一定了解。
下面给大家讲解胶囊碰撞体Capsule
与八叉树Octree
交叉计算的应用场景。
# 胶囊与地面重合的计算问题
大家可以思考一个问题,假设场景中有一个胶囊网格Mesh,如何计算这个网格Mesh与地面地形的交叉重合问题?
或者换个说法,如何把与平移胶囊Mesh,确保他与地面刚刚不重合,而是相切。
这就要用到前面两节课讲到的八叉树Octree.js
和胶囊Capsule.js
。
用数学几何胶囊Capsule
表示胶囊网格Mesh,用八叉树Octree
表示胶囊周围的地面或物体Mesh。
const worldOctree = new Octree();
const gltf = await loader.loadAsync("../地形.glb");
worldOctree.fromGraphNode(gltf.scene);
const R = 0.4;//胶囊半径
const H = 1.7;//胶囊总高度
const start = new THREE.Vector3(0, 0, 0);//底部半球球心坐标
const end = new THREE.Vector3(0, H - 2*R, 0);//顶部半球球心坐标
const capsule = new Capsule(start, end, R);
# 交叉计算
- 胶囊碰撞体
Capsule
:代表胶囊网格模型capsuleMesh - 八叉树
Octree
:代表地形网格模型
Octree.capsuleIntersect(capsule)
可以计算Octree
表示的3D模型与胶囊几何体capsule
是否重合交叉,如果有重合交叉,返回交叉相关的信息,具体说就是在某个方向上交叉重合的深度是多少。
// 碰撞检测:几何体交叉计算
// Octree表示的3D模型和Capsule交叉计算
const result = worldOctree.capsuleIntersect(capsule);
console.log('碰撞检测结果', result);
.depth
交叉重合的深度.normal
深度对应的方向
计算原理:先与八叉树里面的包围盒子节点进行交叉计算,在与相交叉的包围盒包含的三角形进行交叉计算。借助八叉树,相比较,for循环所有模型所有三角形分别进行交叉计算,更节约时间。
# 根据交叉碰撞数据,平移碰撞体
根据交叉碰撞数据,平移碰撞体,让胶囊碰撞体不在于八叉树对应模型重合。
.depth
交叉重合的深度.normal
深度对应的方向
总结:.normal
数据的特点就是让胶囊沿着.normal
方向,平移.depth
距离,就能刚好确保交叉重合深度为0
//平移胶囊碰撞体Capsule,保证与八叉树对应网格没有交叉
capsule.translate(result.normal.multiplyScalar(result.depth));
// 获取胶囊碰撞体坐标,同步胶囊网格capsuleMesh
capsuleMesh.position.copy(capsule.start);
capsuleMesh.position.y -= R;
# 测试:胶囊放在斜面上
平移胶囊放在斜面上,查看交叉重合计算结果。
// 平移胶囊与斜坡交叉重合
capsule.translate(new THREE.Vector3(0, 0, -5))
capsuleMesh.position.z -= 5;
测试下面代码是否同样可以做到保证胶囊Mesh与八叉树对应的地形不交叉
capsule.translate(result.normal.multiplyScalar(result.depth));
capsuleMesh.position.copy(capsule.start);
capsuleMesh.position.y -= R;
# 测试:胶囊与楼梯交叉
// 平移胶囊与楼梯交叉重合
capsule.translate(new THREE.Vector3(0, 0, 6.5))
capsuleMesh.position.z += 6.5;
测试下面代码是否同样可以做到保证胶囊Mesh与八叉树对应的地形不交叉
capsule.translate(result.normal.multiplyScalar(result.depth));
capsuleMesh.position.copy(capsule.start);
capsuleMesh.position.y -= R;
# 胶囊向上平移,与地面没有接触,查看交叉计算结果
胶囊向上平移,与地面没有接触
// 平移胶囊与地形没有交叉重合
capsule.translate(new THREE.Vector3(0, 2*R, 0))
capsuleMesh.position.y += 2*R;
交叉计算结果返回值false
const result = worldOctree.capsuleIntersect(capsule);
console.log('碰撞检测结果', result);