最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

【世界热闻】three.js场景地形导出到物理引擎

来源:博客园

太长不看版

遍历场景地形里的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;}

关键词: 物理引擎 需要额外 参考手册