太长不看版
遍历场景地形里的Mesh,从geometry里抽取index和position,通过这两个数组构建物理引擎里的Trimesh。
背景
最近在试制网页MMORPG,内核用最顺手的three.js,资产使用glTF Binary(.glb)文件载入场景。
需求
three.js虽然自带了OimoPhysics和包装,还包含了Ammo.js的包装,但两种包装都只能对三种特定几何体(BoxGeometry、SphereGeometry和IcosahedronGeometry)构造碰撞体,而GLTFLoader导入的是BufferGeometry,完全对不上。需要找到一种方法,在物理引擎中构造对应碰撞体。
环境
three.js r147
物理引擎(我用了cannon-es 0.20.0和@dimforge/rapier3d-compat 0.10.0)
过程
1. 基本几何体组合
……然后被美术否决了。嗯,我自己也不想这么搞_(:з」∠)_
2. Heightfield
在找到的物理引擎示例和演示里,除了构造基本几何体当做地面,剩下的都使用了Heightfield作为地形的构造方式。
然而这需要额外的高度图数据,生成、储存和读取都是需要解决的问题。
同时,考虑到将来有可能使用多层室内地形,这种方式需要额外工作才能支持多层结构。
最后没有使用。
3. Trimesh
看起来是唯一符合条件的方式,然而从哪里获取构造Trimesh的参数这个问题卡了很久。最后还是读three.js官方文档和glTF参考手册找到了线索。
物理引擎的Trimesh构造需要顶点坐标数组(vertices)和顶点索引数组(indices)。three.js的BufferGeometry里包含了这两项,只不过不怎么直观……
从glTF文件里读取的BufferGeometry,BufferGeometry.attributes里都有名为position的BufferAttribute,这个就是顶点坐标数组,通过BufferGeometry.getAttribute("position")就能拿到。
而顶点索引数组不在BufferGeometry.attributes里,就在BufferGeometry.index属性,也是BufferAttribute类型。
位置、旋转和缩放信息可以通过BufferGeometry所属Mesh的.getWorldPosition()、.getWorldQuaternion()和.getWorldScale()获得。(准确的说,这三个方法是Object3D的方法)
import { Quaternion, Vector3 } from "three"; let terrainModelRoot; // 地形资产根节点 // 读取模型资产...... terrainModelRoot.traverse(node => { if (node.isMesh && node.geometry.index && node.geometry.hasAttribute("position")) { // 几何体信息 const geometry = node.geometry; // 顶点索引数组 const indices = geometry.index.array; // 顶点坐标数组 const vertices = geometry.getAttribute("position").array; // 缩放 const scale = node.getWorldScale(new Vector3()); // 位置 const position = node.getWorldPosition(new Vector3()); // 旋转(四元数) const quaternion = node.getWorldQuaternion(new Quaternion()); // 构造物理世界Trimesh碰撞体...... } });
需要额外注意的是,一些物理引擎不支持缩放,比如Rapier3D。这种情况下就需要先对顶点坐标数组应用缩放,然后再构造Trimesh。
const positionAttribute = geometry.getAttribute("position"); const vertices = new Float32Array(positionAttribute.array.length); const scale = node.getWorldScale(new Vector3()); for (let i = 0; i < positionAttribute.count; i++) { vertices[3 * i] = positionAttribute.array[3 * i] * scale.x; vertices[(3 * i) + 1] = positionAttribute.array[(3 * i) + 1] * scale.y; vertices[(3 * i) + 2] = positionAttribute.array[(3 * i) + 2] * scale.z; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」