如何在框架中实现复杂模型
how to implement complex models in aframe
我对 aframe 和 ECS 建模技术还很陌生,所以我可能没有完全理解应该如何使用该架构。
我想模拟机械臂之类的东西:在简化版本中,它是一个底座,在底座之上是一个旋转器和手臂本身。该模型是从单个 json 文件加载的,由不同部分的多个嵌套对象组成。
如果我希望能够独立控制不同的自由度(这意味着在对象本身的不同子项上设置 object.rotation
值),如何在框架中实现这样的东西?
我想到的一件事是将模型文件的加载实现为一个组件,并将每个自由度实现为一个单独的组件。所以基本上是这样的:
<a-entity robot-model="..." base-rotation="123" arm-pitch="10" />
或者像这样使用 registerPrimitive
会是更好的方法吗?
我的第一印象是这样的:
registerComponent('robot', {
schema: {type: 'asset'},
update() {
// - load and parse model using THREE.ObjectLoader
// - once ready, assign property this.parts with the various
// parts of the robot-arm
}
});
registerComponent('dof-1', {
schema: {type: 'number'},
dependencies: ['robot'],
init() {
this.robot = this.el.components.robot;
},
tick(t, dt) {
if (!this.robot.parts) { return; } // not ready yet
// update values (left out here: acceleration etc)
this.robot.parts.dof1.rotation.x = this.data;
}
});
// more parts / dof implemented likewise
我假设您已经使用 Blender、Maya 或 Cinema4D 等软件创建并装配了 3D 模型。如果没有,文章 Animation from Blender to three.js 是一个很好的起点。
完成后,您可以使用支持 skinning/rigging 的任何格式将模型导入 A-Frame。 THREE.ObjectLoader (.json
) 或 THREE.GLTFLoader (.gltf
) 是不错的选择,并且已经有包装这些加载程序的 A-Frame 组件。假设您正在使用 JSON 和 object-model
component from A-Frame Extras,您可以这样做:
<a-entity object-model="src: url(my-model.json)"></a-entity>
此时您应该会在场景中看到一个模型,而无需编写任何内容 JavaScript,但它还没有动画效果。如果您预先知道想要什么动画,则可以使用相同的建模软件在关键帧或变形目标中创建动画:Blender、Maya 或 Cinema4D。假设您在导出模型时包含了动画,您可以使用 animation-mixer
组件(也来自 A-Frame Extras),如下所示:
<a-entity object-model="src: url(my-model.json)"
animation-mixer="clip: *;"></a-entity>
这将同时播放所有动画。您可以使用剪辑名称而不是 *
来播放特定动画。
如果您的动画需要在运行时计算,并且无法烘焙到模型中,则您需要编写一个自定义组件。这很快就会变得复杂,但基础知识还不错:
<a-entity object-model="src: url(my-model.json)"
custom-animation></a-entity>
和 JS:
AFRAME.registerComponent('custom-animation', {
tick: function (t, dt) {
var mesh = this.el.getObject3D('mesh');
// With complex models, you may need to loop over `mesh.children`
// in case the mesh you want to animate is a child of another
// object in your model file.
if (!mesh || !mesh.isSkinnedMesh) { return; }
mesh.traverse(function (node) {
if (node.isBone && node.name === 'arm') {
node.rotation.x += dt * Math.PI / 1000;
}
});
}
});
我对 aframe 和 ECS 建模技术还很陌生,所以我可能没有完全理解应该如何使用该架构。
我想模拟机械臂之类的东西:在简化版本中,它是一个底座,在底座之上是一个旋转器和手臂本身。该模型是从单个 json 文件加载的,由不同部分的多个嵌套对象组成。
如果我希望能够独立控制不同的自由度(这意味着在对象本身的不同子项上设置 object.rotation
值),如何在框架中实现这样的东西?
我想到的一件事是将模型文件的加载实现为一个组件,并将每个自由度实现为一个单独的组件。所以基本上是这样的:
<a-entity robot-model="..." base-rotation="123" arm-pitch="10" />
或者像这样使用 registerPrimitive
会是更好的方法吗?
我的第一印象是这样的:
registerComponent('robot', {
schema: {type: 'asset'},
update() {
// - load and parse model using THREE.ObjectLoader
// - once ready, assign property this.parts with the various
// parts of the robot-arm
}
});
registerComponent('dof-1', {
schema: {type: 'number'},
dependencies: ['robot'],
init() {
this.robot = this.el.components.robot;
},
tick(t, dt) {
if (!this.robot.parts) { return; } // not ready yet
// update values (left out here: acceleration etc)
this.robot.parts.dof1.rotation.x = this.data;
}
});
// more parts / dof implemented likewise
我假设您已经使用 Blender、Maya 或 Cinema4D 等软件创建并装配了 3D 模型。如果没有,文章 Animation from Blender to three.js 是一个很好的起点。
完成后,您可以使用支持 skinning/rigging 的任何格式将模型导入 A-Frame。 THREE.ObjectLoader (.json
) 或 THREE.GLTFLoader (.gltf
) 是不错的选择,并且已经有包装这些加载程序的 A-Frame 组件。假设您正在使用 JSON 和 object-model
component from A-Frame Extras,您可以这样做:
<a-entity object-model="src: url(my-model.json)"></a-entity>
此时您应该会在场景中看到一个模型,而无需编写任何内容 JavaScript,但它还没有动画效果。如果您预先知道想要什么动画,则可以使用相同的建模软件在关键帧或变形目标中创建动画:Blender、Maya 或 Cinema4D。假设您在导出模型时包含了动画,您可以使用 animation-mixer
组件(也来自 A-Frame Extras),如下所示:
<a-entity object-model="src: url(my-model.json)"
animation-mixer="clip: *;"></a-entity>
这将同时播放所有动画。您可以使用剪辑名称而不是 *
来播放特定动画。
如果您的动画需要在运行时计算,并且无法烘焙到模型中,则您需要编写一个自定义组件。这很快就会变得复杂,但基础知识还不错:
<a-entity object-model="src: url(my-model.json)"
custom-animation></a-entity>
和 JS:
AFRAME.registerComponent('custom-animation', {
tick: function (t, dt) {
var mesh = this.el.getObject3D('mesh');
// With complex models, you may need to loop over `mesh.children`
// in case the mesh you want to animate is a child of another
// object in your model file.
if (!mesh || !mesh.isSkinnedMesh) { return; }
mesh.traverse(function (node) {
if (node.isBone && node.name === 'arm') {
node.rotation.x += dt * Math.PI / 1000;
}
});
}
});