2
votes

Using a-frame, I load in an obj model which contains about 114 people meshes using <a-assets>. I then want to find a specific mesh within that array which I will then clone() many times throughout the scene. Using plain old three.js I am able to do this with minimal effort, but I can't seem to find a way to do this in the <a-entity> structure. I have tried this.el.sceneEl.appendChild() but that errors out saying parameter 1 is not a node; I also tried this.el.sceneEl.add(), but that errors out with Trying to add an element that doesn't have an object3D. Any thoughts on the best approach to solving this issue?

people.js

AFRAME.registerComponent('people', {
    schema: {
        samplePerson: {default: {}}
    },

    init: function () {
        var el = this.el;

        // Listen for when the model is loaded
        el.sceneEl.addEventListener('model-loaded', this.loadPersonModel.bind(this));
    },

    loadPersonModel: function () {

        // Update all meshes from the object3D which is made up with 114 child meshes
        this.el.object3DMap.mesh.children.forEach(function (obj) {
            obj.material.color = new THREE.Color(0xff0000);
            obj.visible = false;
        });

        // Get a sample person for which we will instantiate all over the place
        this.data.samplePerson = this.el.object3D.getObjectByName("people_silhouette73");

        // Clone ten people throughout the scene
        for (var i = 0; i < 10; i++) {
            console.log(this.data.samplePerson);
            if (this.data.samplePerson) {
                var clone = this.data.samplePerson.clone();
                clone.visible = true;

                // Randomly position the object
                clone.position.x += Math.random() * 10;
                clone.position.y += 0.01;
                clone.position.z = -300 + Math.random() * 25;

                // Add the object to the scene
                this.el.sceneEl.appendChild(clone);
            }
        }
   }
});

index.html

<a-scene>
    <a-assets>
        <a-asset-item id="people-obj" src="./assets/obj/silhouette_people_lowpoly_obj.obj"
                  name="testname"></a-asset-item>
    </a-assets>
    <a-entity obj-model="obj: #people-obj;" obj-name="person" people></a-entity>
    </a-entity>
</a-scene>
1

1 Answers

3
votes

I would approach this in two steps. First, load the model and hide it (or ideally remove it from the scene entirely). Second, have one or more extra entities that extract parts of the object that they need:

<a-entity obj-model="obj: #people-obj;" id="group" visible="false"></a-entity>
<a-entity position="0 0 0"
          model-subset="target: #group;
                        name: person1;"></a-entity>
<a-entity position="3 0 0"
          model-subset="target: #group;
                        name: person2;"></a-entity>
<a-entity position="6 0 0"
          model-subset="target: #group;
                        name: person3;"></a-entity>

Then defining the model-subset component:

AFRAME.registerComponent('model-subset', {
  schema: {
    target: {default: '', type: 'selector'},
    name: {default: ''}
  },
  init: function () {
    var el = this.el;
    var data = this.data;
    data.target.addEventListener('model-loaded', function (e) {
      var model = e.detail.model;
      var subset = model.getObjectByName(data.name);
      el.setObject3D('mesh', subset.clone());
    });
  },
  update: function () { /* ... */ },
  remove: function () { /* ... */ }
});