aframe-physics-extras:动态实体与静态 gltf 模型的碰撞
aframe-physics-extras: collision of dynamic entity with static gltf model
我正在尝试捕捉动态球体和静态 gltf 模型之间的碰撞事件。我正在通过以下方式构建 gltf 实体:
const template = document.createElement('a-entity');
template.setAttribute('physics-collider', 'ignoreSleep: false');
template.setAttribute('collision-filter', 'collisionForces: false');
template.setAttribute('body', 'type:static; shape:hull');
// add position, scale, url etc
// ...
template.addEventListener('collisions', e => {
// debugger;
})
此代码创建了一个实体,但在调试模式下它周围没有金属丝网并且未触发 collisions
事件。
出于调试目的,我尝试了不同的形状。这会在实体周围创建一个圆柱体,但它看起来太大了。动态形状穿过圆柱体,但 collisions
事件并不总是被触发,相当罕见。
template.setAttribute('body', 'type:static; shape:cylinder');
然后我尝试手动构建形状:
template.setAttribute('body', 'type:static; shape:none');
template.setAttribute('shape', 'shape: cylinder; height: 5;');
在这种情况下,我收到以下错误:
Cannot read property 'bodyOverlapKeeper' of null at NewComponent.<anonymous> (physics-collider.js:34)
所以现在我卡住了。有人可以建议我做错了什么。我想使用 gltf 模型本身的形状。我用blender打开了,好像没问题,我不明白为什么shape:hull
不起作用。
P. S.:如果重要的话,我正在使用 webpack
0。使用 setAttribute
setAttribute
不会处理如下列表:
template.setAttribute('body', 'type:static; shape:none');
Rather提供对象的新属性:
element.setAttribute("name", {
"property 1": "value 1",
"property 2": "value 2"
});
1.动态 body/shape set-up
也就是说,您可以像这样创建自定义静态圆柱体:
element.setAttribute("body", {
"type": "static",
"shape": "none"
})
element.setAttribute("shape__cylinder", {
'shape': 'cylinder',
"height": 1.5,
"radiusTop": 0.1,
"radiusBottom": 0.2
})
在这个 fiddle
中查看
2。模型的动态形状
关于为 gltf 模型创建动态形状。就我个人而言,我没有使用 cannon
,尽管它与 ammo
驱动程序配合得很好。另一方面,我的 FPS 大幅下降(在~较旧的移动设备上),所以如果可能的话,为了性能起见,请尝试使用简单的碰撞网格。
你可以用一个简单的函数得到蒙皮模型的边界框I made:
let box = new THREE.Box3()
THREE.Box3Utils.fromSkinnedMesh(skinnedMesh, box);
// box should be the bounding box of the skinned mesh
3。动画模型
我强烈建议创建简单的碰撞形状并将它们附加到模型中的特定骨骼。起点可以是 this component:
<a-gltf-model bone-collider="bone: boneName; halfExtents: 0.15 0.15 0.15">
边界框方法将非常复杂,因为:
- 您必须为 non-rotated 网格计算边界框
- 对结果应用旋转(边界框是 world-aligned)
- 更新每个刻度的主体,甚至删除形状并重新添加它 (source)
您可以在 this example
中看到这两种方法
与此同时,我能够为 gltf 模型实现动画碰撞网格。
我使用了由@Piotr Adam Milewski here. See more details about the function in 回答实现的辅助函数。
感谢任何其他提高性能的建议。
AFRAME.registerComponent('animated-collision-shape', {
init: function () {
this.nodeMap = {};
this.el.setAttribute('body', 'type: static; shape: none;')
this.el.addEventListener('model-loaded', () => {
const size = new THREE.Vector3();
let box = new THREE.Box3().setFromObject(this.el.object3D);
box.getSize(size);
this.offset = new CANNON.Vec3(0, size.y / 2, 0);
let mesh = this.el.getObject3D("mesh");
mesh.traverse(node => {
if (node.isSkinnedMesh) {
this.skinnedMesh = node;
this.nodeMap[node.uuid] = {
mesh: node,
box: new THREE.Box3()
}
}
});
if (!Object.keys(this.nodeMap).length) {
this.nodeMap[0] = {
mesh: this.el.object3D,
box: new THREE.Box3()
};
}
this.el.components["body"].shouldUpdateBody = true;
})
},
remove: function () {
this.removeBoxes();
},
tick: (function () {
const size = new THREE.Vector3();
let common_box_uuid = null;
return function tick() {
if (
!Object.keys(this.nodeMap).length ||
!this.el.body) {
return;
}
let combine = this.data.combine === true
let i = 0;
for (let uuid in this.nodeMap) {
// Non - skinned case
if (!this.nodeMap[uuid].mesh.isSkinnedMesh) {
this.nodeMap[uuid].box.setFromObject(this.el.object3D);
return;
}
// skinned model. Either separate boxes, or combined
if (common_box_uuid && combine) {
utils.SkinnedMeshBBox.expandAABB(this.nodeMap[uuid].mesh, this.nodeMap[common_box_uuid].box);
} else {
utils.SkinnedMeshBBox.getAABB(this.nodeMap[uuid].mesh, this.nodeMap[uuid].box);
common_box_uuid = uuid
}
if (isFinite(this.nodeMap[common_box_uuid].box.max.x)) {
this.nodeMap[common_box_uuid].box.getSize(size);
if (this.el.body.shapes[i]) {
this.el.body.shapes[i].halfExtents = new CANNON.Vec3(size.x / 2, size.y / 2, size.z / 2);
this.el.body.shapes[i].updateConvexPolyhedronRepresentation();
} else {
let shape = new CANNON.Box(new CANNON.Vec3(size.x / 2, size.y / 2, size.z / 2))
this.el.body.addShape(shape, this.offset, shape.orientation);
}
i++;
}
}
this.el.components["body"].shouldUpdateWireframe = true;
};
})()
})
我正在尝试捕捉动态球体和静态 gltf 模型之间的碰撞事件。我正在通过以下方式构建 gltf 实体:
const template = document.createElement('a-entity');
template.setAttribute('physics-collider', 'ignoreSleep: false');
template.setAttribute('collision-filter', 'collisionForces: false');
template.setAttribute('body', 'type:static; shape:hull');
// add position, scale, url etc
// ...
template.addEventListener('collisions', e => {
// debugger;
})
此代码创建了一个实体,但在调试模式下它周围没有金属丝网并且未触发 collisions
事件。
出于调试目的,我尝试了不同的形状。这会在实体周围创建一个圆柱体,但它看起来太大了。动态形状穿过圆柱体,但 collisions
事件并不总是被触发,相当罕见。
template.setAttribute('body', 'type:static; shape:cylinder');
然后我尝试手动构建形状:
template.setAttribute('body', 'type:static; shape:none');
template.setAttribute('shape', 'shape: cylinder; height: 5;');
在这种情况下,我收到以下错误:
Cannot read property 'bodyOverlapKeeper' of null at NewComponent.<anonymous> (physics-collider.js:34)
所以现在我卡住了。有人可以建议我做错了什么。我想使用 gltf 模型本身的形状。我用blender打开了,好像没问题,我不明白为什么shape:hull
不起作用。
P. S.:如果重要的话,我正在使用 webpack
0。使用 setAttribute
setAttribute
不会处理如下列表:
template.setAttribute('body', 'type:static; shape:none');
Rather提供对象的新属性:
element.setAttribute("name", {
"property 1": "value 1",
"property 2": "value 2"
});
1.动态 body/shape set-up
也就是说,您可以像这样创建自定义静态圆柱体:
element.setAttribute("body", {
"type": "static",
"shape": "none"
})
element.setAttribute("shape__cylinder", {
'shape': 'cylinder',
"height": 1.5,
"radiusTop": 0.1,
"radiusBottom": 0.2
})
在这个 fiddle
中查看2。模型的动态形状
关于为 gltf 模型创建动态形状。就我个人而言,我没有使用 cannon
,尽管它与 ammo
驱动程序配合得很好。另一方面,我的 FPS 大幅下降(在~较旧的移动设备上),所以如果可能的话,为了性能起见,请尝试使用简单的碰撞网格。
你可以用一个简单的函数得到蒙皮模型的边界框I made:
let box = new THREE.Box3()
THREE.Box3Utils.fromSkinnedMesh(skinnedMesh, box);
// box should be the bounding box of the skinned mesh
3。动画模型
我强烈建议创建简单的碰撞形状并将它们附加到模型中的特定骨骼。起点可以是 this component:
<a-gltf-model bone-collider="bone: boneName; halfExtents: 0.15 0.15 0.15">
边界框方法将非常复杂,因为:
- 您必须为 non-rotated 网格计算边界框
- 对结果应用旋转(边界框是 world-aligned)
- 更新每个刻度的主体,甚至删除形状并重新添加它 (source)
您可以在 this example
中看到这两种方法与此同时,我能够为 gltf 模型实现动画碰撞网格。
我使用了由@Piotr Adam Milewski here. See more details about the function in
AFRAME.registerComponent('animated-collision-shape', {
init: function () {
this.nodeMap = {};
this.el.setAttribute('body', 'type: static; shape: none;')
this.el.addEventListener('model-loaded', () => {
const size = new THREE.Vector3();
let box = new THREE.Box3().setFromObject(this.el.object3D);
box.getSize(size);
this.offset = new CANNON.Vec3(0, size.y / 2, 0);
let mesh = this.el.getObject3D("mesh");
mesh.traverse(node => {
if (node.isSkinnedMesh) {
this.skinnedMesh = node;
this.nodeMap[node.uuid] = {
mesh: node,
box: new THREE.Box3()
}
}
});
if (!Object.keys(this.nodeMap).length) {
this.nodeMap[0] = {
mesh: this.el.object3D,
box: new THREE.Box3()
};
}
this.el.components["body"].shouldUpdateBody = true;
})
},
remove: function () {
this.removeBoxes();
},
tick: (function () {
const size = new THREE.Vector3();
let common_box_uuid = null;
return function tick() {
if (
!Object.keys(this.nodeMap).length ||
!this.el.body) {
return;
}
let combine = this.data.combine === true
let i = 0;
for (let uuid in this.nodeMap) {
// Non - skinned case
if (!this.nodeMap[uuid].mesh.isSkinnedMesh) {
this.nodeMap[uuid].box.setFromObject(this.el.object3D);
return;
}
// skinned model. Either separate boxes, or combined
if (common_box_uuid && combine) {
utils.SkinnedMeshBBox.expandAABB(this.nodeMap[uuid].mesh, this.nodeMap[common_box_uuid].box);
} else {
utils.SkinnedMeshBBox.getAABB(this.nodeMap[uuid].mesh, this.nodeMap[uuid].box);
common_box_uuid = uuid
}
if (isFinite(this.nodeMap[common_box_uuid].box.max.x)) {
this.nodeMap[common_box_uuid].box.getSize(size);
if (this.el.body.shapes[i]) {
this.el.body.shapes[i].halfExtents = new CANNON.Vec3(size.x / 2, size.y / 2, size.z / 2);
this.el.body.shapes[i].updateConvexPolyhedronRepresentation();
} else {
let shape = new CANNON.Box(new CANNON.Vec3(size.x / 2, size.y / 2, size.z / 2))
this.el.body.addShape(shape, this.offset, shape.orientation);
}
i++;
}
}
this.el.components["body"].shouldUpdateWireframe = true;
};
})()
})