如何在框架中实现复杂模型

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;
      }
    });
  }
});