David Stein
David Stein

Reputation: 399

How to use Three.InstancedMesh in Aframe

I'm trying to implement instancing in Aframe using the ThreeJs InstancedMesh based on the example here: https://github.com/mrdoob/three.js/blob/master/examples/webgl_instancing_dynamic.html

Relevant section of code here:

    init: function() {
        const {count, radius, scale, colors, positions} = this.data;  
        this.start = true;
        this.dummy = new THREE.Object3D();
        this.count = count;
        this.startObject = new THREE.Object3D();
        this.endObject = new THREE.Object3D();
        this.instanceColors = new Float32Array(count * 3);
        this.instanceColorsBase = new Float32Array(this.instanceColors.length);

        this.vertices = [];
        this.rotations = [];
        for ( var i = 0; i < this.data.count; i ++ ) {
            var x = this.data.positions[i][0] * this.data.scale;
            var y = this.data.positions[i][1] * this.data.scale;
            var z = this.data.positions[i][2] * this.data.scale;

            var xEnd = x + this.data.endPositions[i][0] * this.data.scale;
            var yEnd = y + this.data.endPositions[i][1] * this.data.scale;
            var zEnd = z + this.data.endPositions[i][2] * this.data.scale;

            this.vertices.push( x, y, z );
            const rotation = this.getDirection({'x':x,'y':y,'z':z}, 
                                          {'x':xEnd,'y':yEnd,'z':zEnd});
            this.rotations.push(rotation.x, rotation.y, rotation.z);
        }

        let mesh;
        let geometry;
        let material;
        const loader = new THREE.GLTFLoader();
        const el = this.el;
        loader.load("/assets/arrow/arrow.gltf", function ( model ) {
            geometry = model.scene.children[0].children[0].geometry;
           
            geometry.computeVertexNormals();
            geometry.scale( 0.03, 0.03, 0.03 );

            material = new THREE.MeshNormalMaterial();

            mesh = new THREE.InstancedMesh( geometry, material, count );
            mesh.instanceMatrix.setUsage( THREE.DynamicDrawUsage );
            el.object3D.add(mesh);
        } );
        this.el.setAttribute("id", "cells");
    },
    setMatrix: function (start) {
        if (this.mesh) {
            for ( let i = 0; i < this.count; i ++ ) {
                var x = this.data.positions[i][0] * this.data.scale;
                var y = this.data.positions[i][1] * this.data.scale;
                var z = this.data.positions[i][2] * this.data.scale;

                var xEnd = x + this.data.endPositions[i][0] * this.data.scale;
                var yEnd = y + this.data.endPositions[i][1] * this.data.scale;
                var zEnd = z + this.data.endPositions[i][2] * this.data.scale;
                if (start) {
                    this.dummy.position.set(xEnd, yEnd, zEnd);
                } else {
                    this.dummy.position.set(x, y, z);
                }
                this.dummy.rotation.x = this.rotations[i][0];
                this.dummy.rotation.y = this.rotations[i][1];
                this.dummy.rotation.z = this.rotations[i][2];
                this.dummy.updateMatrix();
                this.mesh.setMatrixAt( i, this.dummy.matrix );
            }
            this.mesh.instanceMatrix.needsUpdate = true;
        }
    }
    tick: function() {
        this.setMatrix(this.start);
        this.start = !this.start;
    },

No errors or relevant messages that I can see, but none of the instanced objects are rendering. I don't really have a good way to post an example unfortunately. Anyone know what I'm doing wrong? Thanks in advance!

Note: It seems that the objects are being rendered because the number of triangles being drawn increases drastically when I add this component. However, they are not visible anywhere and I can't find them in the aframe inspector either

Upvotes: 1

Views: 1869

Answers (1)

Piotr Adam Milewski
Piotr Adam Milewski

Reputation: 14655

It's a very case specific question with a quite extensive topic, so:

In general, using THREE.InstancedMeshes is simple, and you got it right:

// create an instanced mesh
let iMesh = new THREE.InstancedMesh(geometry, material, count)
element.object3D.add(iMesh)

// manipulate the instances
let mtx = new Matrix4()
// set the position, rotation, scale of the matrix
// ...
// update the instance
iMesh.setMatrixAt(index, mtx);
iMesh.instanceMatrix.needsUpdate = true;

Example of an instanced gltf model here


Your code is doing a lot, and it would be easier if it could be stripped to a bare minimum. Yet I think there is only one major issue - this.model isn't set anywhere, so the setMatrix function does nothing. Other than that you may need to disable frustum culling (mesh.frustumCulling = false), or set a bounding sphere - otherwise the objects may dissapear when the base object is out of sight.

Once it's set, your code seems to be working

Upvotes: 3

Related Questions