Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GLTF create instances

I am new in THREEJS. In the past, I have used AFRAME, CESIUM, XEOGL and BABYLONJS. But eventually, due to memory consumption and performance, I have realized that the best product for making a CAD visualizer is THREEJS.

BABYLONJS takes more than 4 minutes to load a big GLTF file (400MB) while THREEJS takes only 30 secs. The memory taken by BABYLONJS is 4 times that used by THREEJS.

I know that there are still some issues to be able to create instances (GPU) from a loaded GLTF file in THREEJS, but I only need to change the position and rotation in each instance, no need to animate anything.

I have tried with GLTF1.0 and GLTF2.0 and the issue is the same. When I load the GLTF model, I get a scene. From this scene I am trying to obtain the buffergeometry from children array. However when I try to créate an instance, it is not working. My objects are static (no animations at all).

Is there any way to create an instance of an Object3D or from its buffergeometry?

In BABYLONJS it is very simple to create instances from a loaded GLTF file. I really need to use instances to save RAM and use GPU instead CPU resources. My scene needs to load many copies of the same objects to compound the scene.

Some problems I see using GLFT loader:

    1. You have to identify all object3Ds which contain the valid geometry. In this example is on line 335:

    var geo = data.scene.children[0].children[0].children[0].children[0].geometry

    1. The merge example is not working. it doesn't work either in the original example.
    1. IT seems that instancing do not improve performance at all:
  • 10000 objects, multimaterial --> 4FPS

  • 10000 objects, singlematerial--> 4FPS

  • 10000 objects, instanced --> 4FPS

  • 10000 objects, merged --> Not working

    1. When instanced is selected the geometries are not properly rendered.

Here you have my code using Duck GLTF example:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>three.js webgl - interactive instances (gpu)</title>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
    />
    <style>
      body {
        font-family: Monospace;
        background-color: #f0f0f0;
        margin: 0px;
        overflow: hidden;
      }
      .info {
        position: absolute;
        background-color: black;
        opacity: 0.8;
        color: white;
        text-align: center;
        top: 0px;
        width: 100%;
      }
      .info a {
        color: #00ffff;
      }
      #notSupported {
        width: 50%;
        margin: auto;
        border: 2px red solid;
        margin-top: 20px;
        padding: 10px;
      }
    </style>
  </head>
  <body>
    <div class="info">
      <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a>
      webgl - gpu picking of geometry instances
      <div id="notSupported" style="display: none">
        Sorry your graphics card + browser does not support hardware instancing
      </div>
      <br /><br />
      <div>
        This demo compares different methods of constructing and rendering many
        instances of a single geometry.
      </div>
      <br />
      <div>
        <div style="display: inline-block">
          <span>number of<br />geometry instances</span>
          <br />
          <select id="instanceCount">
            <option>100</option>
            <option>500</option>
            <option selected>1000</option>
            <option>2000</option>
            <option>3000</option>
            <option>5000</option>
            <option>10000</option>
            <option>20000</option>
            <option>30000</option>
            <option>50000</option>
            <option>100000</option>
          </select>
        </div>
        &nbsp;&nbsp;&nbsp;
        <div style="display: inline-block">
          <span>method of<br />construction/rendering</span>
          <br />
          <select id="method">
            <option>instanced</option>
            <option>merged</option>
            <option selected>singleMaterial</option>
            <option>multiMaterial</option>
          </select>
        </div>
        &nbsp;&nbsp;&nbsp;
        <div style="display: inline-block">
          <span>render continuously<br />(to get fps reading)</span>
          <br />
          <input id="animate" type="checkbox" />
        </div>
        &nbsp;&nbsp;&nbsp;
        <div style="display: inline-block">
          <span
            >use override material<br />(only effects singleMaterial
            method)</span
          >
          <br />
          <input id="override" type="checkbox" checked />
        </div>
        &nbsp;&nbsp;&nbsp;
        <div style="display: inline-block">
          <span>construct anew<br />(to get additional timings)</span>
          <br />
          <button id="construct" type="button">do it</button>
        </div>
      </div>
      <br />
      <div>
        <span>Materials: #<span id="materialCount"></span></span>
        &nbsp;&nbsp;&nbsp;
        <span>Objects: #<span id="objectCount"></span></span>
        &nbsp;&nbsp;&nbsp;
        <span>Drawcalls: #<span id="drawcalls"></span></span>
        &nbsp;&nbsp;&nbsp;
        <span>Construction time: <span id="initTime"></span>&nbsp;ms</span>
        &nbsp;&nbsp;&nbsp;
      </div>
    </div>
    <div id="container"></div>
    <script src="../build/three.js"></script>
    <script src="js/controls/TrackballControls.js"></script>
    <script src="js/libs/stats.min.js"></script>
    <script src="js/loaders/GLTF2Loader.js"></script>
    <script id="vertMerged" type="x-shader/x-vertex">
      #define SHADER_NAME vertMerged
      precision highp float;
      uniform mat4 modelViewMatrix;
      uniform mat4 projectionMatrix;
      attribute vec3 position;
      #ifdef PICKING
      attribute vec3 pickingColor;
      #else
      attribute vec3 color;
      varying vec3 vPosition;
      #endif
      varying vec3 vColor;
      void main()   {
      vec3 positionEye = ( modelViewMatrix * vec4( position, 1.0 ) ).xyz;
      #ifdef PICKING
      vColor = pickingColor;
      #else
      vColor = color;
      vPosition = positionEye;
      #endif
      gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
      }
    </script>
    <script id="fragMerged" type="x-shader/x-fragment">
      #define SHADER_NAME fragMerged
      #extension GL_OES_standard_derivatives : enable
      precision highp float;
      varying vec3 vColor;
      #ifndef PICKING
      varying vec3 vPosition;
      #endif
      void main()   {
      #ifdef PICKING
      gl_FragColor = vec4( vColor, 1.0 );
      #else
      vec3 fdx = dFdx( vPosition );
      vec3 fdy = dFdy( vPosition );
      vec3 normal = normalize( cross( fdx, fdy ) );
      float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
      gl_FragColor = vec4( diffuse * vColor, 1.0 );
      #endif
      }
    </script>
    <script id="vertInstanced" type="x-shader/x-vertex">
      #define SHADER_NAME vertInstanced
      precision highp float;
      uniform mat4 modelViewMatrix;
      uniform mat4 projectionMatrix;
      attribute vec3 position;
      attribute vec3 mcol0;
      attribute vec3 mcol1;
      attribute vec3 mcol2;
      attribute vec3 mcol3;
      #ifdef PICKING
      attribute vec3 pickingColor;
      #else
      attribute vec3 color;
      varying vec3 vPosition;
      #endif
      varying vec3 vColor;
      void main()   {
      mat4 matrix = mat4(
      vec4( mcol0, 0 ),
      vec4( mcol1, 0 ),
      vec4( mcol2, 0 ),
      vec4( mcol3, 1 )
      );
      vec3 positionEye = ( modelViewMatrix * matrix * vec4( position, 1.0 ) ).xyz;
      #ifdef PICKING
      vColor = pickingColor;
      #else
      vColor = color;
      vPosition = positionEye;
      #endif
      gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
      }
    </script>
    <script id="fragInstanced" type="x-shader/x-fragment">
      #define SHADER_NAME fragInstanced
      #extension GL_OES_standard_derivatives : enable
      precision highp float;
      varying vec3 vColor;
      #ifndef PICKING
      varying vec3 vPosition;
      #endif
      void main()   {
      #ifdef PICKING
      gl_FragColor = vec4( vColor, 1.0 );
      #else
      vec3 fdx = dFdx( vPosition );
      vec3 fdy = dFdy( vPosition );
      vec3 normal = normalize( cross( fdx, fdy ) );
      float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
      gl_FragColor = vec4( diffuse * vColor, 1.0 );
      #endif
      }
    </script>
    <script id="vertMaterial" type="x-shader/x-vertex">
      #define SHADER_NAME vertMaterial
      precision highp float;
      uniform mat4 modelViewMatrix;
      uniform mat4 projectionMatrix;
      attribute vec3 position;
      #ifndef PICKING
      varying vec3 vPosition;
      #endif
      void main()   {
      vec3 positionEye = ( modelViewMatrix * vec4( position, 1.0 ) ).xyz;
      #ifndef PICKING
      vPosition = positionEye;
      #endif
      gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
      }
    </script>
    <script id="fragMaterial" type="x-shader/x-fragment">
      #define SHADER_NAME fragMaterial
      #extension GL_OES_standard_derivatives : enable
      precision highp float;
      #ifdef PICKING
      uniform vec3 pickingColor;
      #else
      uniform vec3 color;
      varying vec3 vPosition;
      #endif
      void main()   {
      #ifdef PICKING
      gl_FragColor = vec4( pickingColor, 1.0 );
      #else
      vec3 fdx = dFdx( vPosition );
      vec3 fdy = dFdy( vPosition );
      vec3 normal = normalize( cross( fdx, fdy ) );
      float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
      gl_FragColor = vec4( diffuse * color, 1.0 );
      #endif
      }
    </script>
    <script>
      var container, stats;
      var camera, controls, scene, renderer;
      var pickingData, pickingRenderTarget, pickingScene;
      var useOverrideMaterial = true;
      var singleMaterial, singlePickingMaterial;
      var highlightBox;
      var materialList = [];
      var geometryList = [];
      var objectCount = 0;
      var geometrySize;
      var mouse = new THREE.Vector2();
      var scale = 1.03;
      var loader = new THREE.GLTF2Loader();
      var pixelBuffer = new Uint8Array(4);
      var instanceCount, method, doAnimate;
      gui();
      init();
      initMesh();
      if (doAnimate) animate();
      function gui() {
        var instanceCountElm = document.getElementById("instanceCount");
        instanceCount = parseInt(instanceCountElm.value);
        instanceCountElm.addEventListener("change", function () {
          instanceCount = parseInt(instanceCountElm.value);
          initMesh();
        });
        var methodElm = document.getElementById("method");
        method = methodElm.value;
        methodElm.addEventListener("change", function () {
          method = methodElm.value;
          initMesh();
        });
        var animateElm = document.getElementById("animate");
        doAnimate = animateElm.checked;
        animateElm.addEventListener("click", function () {
          doAnimate = animateElm.checked;
          animate();
        });
        var overrideElm = document.getElementById("override");
        useOverrideMaterial = overrideElm.checked;
        overrideElm.addEventListener("click", function () {
          useOverrideMaterial = overrideElm.checked;
          initMesh();
        });
        var constructElm = document.getElementById("construct");
        constructElm.addEventListener("click", function () {
          initMesh();
        });
      }
      function clean() {
        THREE.Cache.clear();
        materialList.forEach(function (m) {
          m.dispose();
        });
        geometryList.forEach(function (g) {
          g.dispose();
        });
        scene = new THREE.Scene();
        scene.background = new THREE.Color(0xffffff);
        scene.add(camera);
        scene.add(highlightBox);
        pickingScene = new THREE.Scene();
        pickingData = {};
        materialList = [];
        geometryList = [];
        objectCount = 0;
        singleMaterial = undefined;
        singlePickingMaterial = undefined;
      }
      var randomizeMatrix = (function () {
        var position = new THREE.Vector3();
        var rotation = new THREE.Euler();
        var quaternion = new THREE.Quaternion();
        var scale = new THREE.Vector3();
        return function (matrix) {
          position.x = Math.random() * 40 - 20;
          position.y = Math.random() * 40 - 20;
          position.z = Math.random() * 40 - 20;
          rotation.x = Math.random() * 2 * Math.PI;
          rotation.y = Math.random() * 2 * Math.PI;
          rotation.z = Math.random() * 2 * Math.PI;
          quaternion.setFromEuler(rotation, false);
          scale.x = scale.y = scale.z = 0.001;
          matrix.compose(position, quaternion, scale);
        };
      })();
      function initMesh() {
        clean();
        loader.load("models/gltf/Duck/glTF-Binary/Duck.glb", function (data) {
          console.log(data);
          var geo =
            data.scene.children[0].children[0].children[0].children[0].geometry;
          console.log("geo:");
          console.log(geo);
          geo.computeBoundingBox();
          geometrySize = geo.boundingBox.getSize();
          geometryList.push(geo);
          var start = window.performance.now();
          switch (method) {
            case "merged":
              makeMerged(geo);
              break;
            case "instanced":
              makeInstanced(geo);
              break;
            case "singleMaterial":
              makeSingleMaterial(geo);
              break;
            case "multiMaterial":
              makeMultiMaterial(geo);
              break;
          }
          render();
          var end = window.performance.now();
          document.getElementById("materialCount").innerText =
            materialList.length;
          document.getElementById("objectCount").innerText = objectCount;
          document.getElementById("drawcalls").innerText =
            renderer.info.render.calls;
          document.getElementById("initTime").innerText = (end - start).toFixed(
            2
          );
        });
      }
      function makeMultiMaterial(geo) {
        var vert = document.getElementById("vertMaterial").textContent;
        var frag = document.getElementById("fragMaterial").textContent;
        var material = new THREE.RawShaderMaterial({
          vertexShader: vert,
          fragmentShader: frag,
          uniforms: {
            color: {
              value: new THREE.Color(),
            },
          },
        });
        var pickingMaterial = new THREE.RawShaderMaterial({
          vertexShader: "#define PICKING\n" + vert,
          fragmentShader: "#define PICKING\n" + frag,
          uniforms: {
            pickingColor: {
              value: new THREE.Color(),
            },
          },
        });
        var matrix = new THREE.Matrix4();
        for (var i = 0; i < instanceCount; i++) {
          var object = new THREE.Mesh(geo, material);
          objectCount++;
          randomizeMatrix(matrix);
          object.applyMatrix(matrix);
          var pickingObject = object.clone();
          objectCount++;
          object.material = material.clone();
          object.material.uniforms.color.value.setHex(Math.random() * 0xffffff);
          materialList.push(object.material);
          pickingObject.material = pickingMaterial.clone();
          pickingObject.material.uniforms.pickingColor.value.setHex(i + 1);
          materialList.push(pickingObject.material);
          pickingData[i + 1] = object;
          scene.add(object);
          pickingScene.add(pickingObject);
        }
        material.dispose();
        pickingMaterial.dispose();
      }
      function makeSingleMaterial(geo) {
        var vert = document.getElementById("vertMaterial").textContent;
        var frag = document.getElementById("fragMaterial").textContent;
        var material = new THREE.RawShaderMaterial({
          vertexShader: vert,
          fragmentShader: frag,
          uniforms: {
            color: {
              value: new THREE.Color(),
            },
          },
        });
        materialList.push(material);
        var pickingMaterial = new THREE.RawShaderMaterial({
          vertexShader: "#define PICKING\n" + vert,
          fragmentShader: "#define PICKING\n" + frag,
          uniforms: {
            pickingColor: {
              value: new THREE.Color(),
            },
          },
        });
        materialList.push(pickingMaterial);
        if (useOverrideMaterial) {
          singleMaterial = material;
          singlePickingMaterial = pickingMaterial;
        }
        var matrix = new THREE.Matrix4();
        function onBeforeRender(
          renderer,
          scene,
          camera,
          geometry,
          material,
          group
        ) {
          var updateList = [];
          var u = material.uniforms;
          var d = this.userData;
          if (u.pickingColor) {
            u.pickingColor.value.setHex(d.pickingColor);
            updateList.push("pickingColor");
          }
          if (u.color) {
            u.color.value.setHex(d.color);
            updateList.push("color");
          }
          if (updateList.length) {
            var materialProperties = renderer.properties.get(material);
            if (materialProperties.program) {
              var gl = renderer.getContext();
              var p = materialProperties.program;
              gl.useProgram(p.program);
              var pu = p.getUniforms();
              updateList.forEach(function (name) {
                pu.setValue(gl, name, u[name].value);
              });
            }
          }
        }
        for (var i = 0; i < instanceCount; i++) {
          var object = new THREE.Mesh(geo, material);
          objectCount++;
          randomizeMatrix(matrix);
          object.applyMatrix(matrix);
          var pickingObject;
          if (!useOverrideMaterial) {
            pickingObject = object.clone();
            objectCount++;
          }
          object.material = material;
          object.userData["color"] = Math.random() * 0xffffff;
          if (useOverrideMaterial) {
            object.userData["pickingColor"] = i + 1;
            object.onBeforeRender = onBeforeRender;
          } else {
            pickingObject.material = pickingMaterial;
            pickingObject.userData["pickingColor"] = i + 1;
            pickingObject.onBeforeRender = onBeforeRender;
          }
          pickingData[i + 1] = object;
          scene.add(object);
          if (!useOverrideMaterial) pickingScene.add(pickingObject);
        }
      }
      function makeMerged(geo) {
        var vert = document.getElementById("vertMerged").textContent;
        var frag = document.getElementById("fragMerged").textContent;
        var material = new THREE.RawShaderMaterial({
          vertexShader: vert,
          fragmentShader: frag,
        });
        materialList.push(material);
        var pickingMaterial = new THREE.RawShaderMaterial({
          vertexShader: "#define PICKING\n" + vert,
          fragmentShader: "#define PICKING\n" + frag,
        });
        materialList.push(pickingMaterial);
        var bgeo = geo.clone();
        geometryList.push(bgeo);
        var mgeo = new THREE.BufferGeometry();
        geometryList.push(mgeo);
        var pos = bgeo.attributes.position;
        var posLen = bgeo.attributes.position.count * 3;
        var vertices = new THREE.BufferAttribute(
          new Float32Array(instanceCount * posLen),
          3
        );

        var matrix = new THREE.Matrix4();
        for (var i = 0, ul = instanceCount; i < ul; i++) {
          randomizeMatrix(matrix);
          var object = new THREE.Object3D();
          objectCount++;
          object.applyMatrix(matrix);
          pickingData[i + 1] = object;
          vertices.set(pos.array, i * posLen);
          //matrix.applyToVector3Array( vertices.array, i * posLen, posLen )
        }
        mgeo.addAttribute("position", vertices);
        var colCount = posLen / 3;
        var colors = new THREE.BufferAttribute(
          new Float32Array(instanceCount * colCount * 3),
          3
        );

        var randCol = function () {
          return Math.random();
        };
        for (var i = 0, ul = instanceCount; i < ul; i++) {
          var r = randCol(),
            g = randCol(),
            b = randCol();
          for (var j = i * colCount, jl = (i + 1) * colCount; j < jl; j++) {
            colors.setXYZ(j, r, g, b);
          }
        }
        mgeo.addAttribute("color", colors);
        var col = new THREE.Color();
        var pickingColors = new THREE.BufferAttribute(
          new Float32Array(instanceCount * colCount * 3),
          3
        );
        for (var i = 0, ul = instanceCount; i < ul; i++) {
          col.setHex(i + 1);
          for (var j = i * colCount, jl = (i + 1) * colCount; j < jl; j++) {
            pickingColors.setXYZ(j, col.r, col.g, col.b);
          }
        }
        mgeo.addAttribute("pickingColor", pickingColors);
        var mesh = new THREE.Mesh(mgeo, material);
        scene.add(mesh);
        var pickingMesh = new THREE.Mesh(mgeo, pickingMaterial);
        pickingScene.add(pickingMesh);
      }
      function makeInstanced(geo) {
        var vert = document.getElementById("vertInstanced").textContent;
        var frag = document.getElementById("fragInstanced").textContent;
        var material = new THREE.RawShaderMaterial({
          vertexShader: vert,
          fragmentShader: frag,
        });
        materialList.push(material);
        var pickingMaterial = new THREE.RawShaderMaterial({
          vertexShader: "#define PICKING\n" + vert,
          fragmentShader: "#define PICKING\n" + frag,
        });
        materialList.push(pickingMaterial);
        var bgeo = geo.clone();
        geometryList.push(bgeo);
        var igeo = new THREE.InstancedBufferGeometry();
        geometryList.push(igeo);
        var vertices = bgeo.attributes.position.clone();
        igeo.addAttribute("position", vertices);
        var mcol0 = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        var mcol1 = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        var mcol2 = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        var mcol3 = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        var matrix = new THREE.Matrix4();
        var me = matrix.elements;
        for (var i = 0, ul = mcol0.count; i < ul; i++) {
          randomizeMatrix(matrix);
          var object = new THREE.Object3D();
          objectCount++;
          object.applyMatrix(matrix);
          pickingData[i + 1] = object;
          mcol0.setXYZ(i, me[0], me[1], me[2]);
          mcol1.setXYZ(i, me[4], me[5], me[6]);
          mcol2.setXYZ(i, me[8], me[9], me[10]);
          mcol3.setXYZ(i, me[12], me[13], me[14]);
        }
        igeo.addAttribute("mcol0", mcol0);
        igeo.addAttribute("mcol1", mcol1);
        igeo.addAttribute("mcol2", mcol2);
        igeo.addAttribute("mcol3", mcol3);
        var randCol = function () {
          return Math.random();
        };
        var colors = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        for (var i = 0, ul = colors.count; i < ul; i++) {
          colors.setXYZ(i, randCol(), randCol(), randCol());
        }
        igeo.addAttribute("color", colors);
        var col = new THREE.Color();
        var pickingColors = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        for (var i = 0, ul = pickingColors.count; i < ul; i++) {
          col.setHex(i + 1);
          pickingColors.setXYZ(i, col.r, col.g, col.b);
        }
        igeo.addAttribute("pickingColor", pickingColors);
        var mesh = new THREE.Mesh(igeo, material);
        scene.add(mesh);
        var pickingMesh = new THREE.Mesh(igeo, pickingMaterial);
        pickingScene.add(pickingMesh);
      }
      function init() {
        camera = new THREE.PerspectiveCamera(
          70,
          window.innerWidth / window.innerHeight,
          1,
          100
        );
        camera.position.z = 40;
        pickingRenderTarget = new THREE.WebGLRenderTarget(
          window.innerWidth,
          window.innerHeight
        );
        pickingRenderTarget.texture.generateMipmaps = false;
        pickingRenderTarget.texture.minFilter = THREE.NearestFilter;
        highlightBox = new THREE.Mesh(
          new THREE.BoxGeometry(1, 1, 1),
          new THREE.MeshLambertMaterial({
            emissive: 0xffff00,
            transparent: true,
            opacity: 0.5,
            side: THREE.FrontSide,
          })
        );
        container = document.getElementById("container");
        renderer = new THREE.WebGLRenderer({
          antialias: true,
          alpha: true,
        });
        if (renderer.extensions.get("ANGLE_instanced_arrays") === false) {
          document.getElementById("notSupported").style.display = "";
          return;
        }
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);
        container.appendChild(renderer.domElement);
        if (renderer.extensions.get("ANGLE_instanced_arrays") === false) {
          throw "ANGLE_instanced_arrays not supported";
        }
        controls = new THREE.TrackballControls(camera, renderer.domElement);
        controls.staticMoving = true;
        stats = new Stats();
        container.appendChild(stats.dom);
        renderer.domElement.addEventListener("mousemove", onMouseMove);
        window.addEventListener("resize", onWindowResize, false);
      }
      //
      function onMouseMove(e) {
        mouse.x = e.clientX;
        mouse.y = e.clientY;
        controls.update();
        requestAnimationFrame(render);
      }
      function onWindowResize(event) {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
        pickingRenderTarget.setSize(window.innerWidth, window.innerHeight);
      }
      function animate() {
        if (doAnimate) {
          requestAnimationFrame(animate);
        }
        controls.update();
        stats.update();
        document.getElementById("materialCount").innerText =
          materialList.length;
        document.getElementById("objectCount").innerText = objectCount;
        document.getElementById("drawcalls").innerText =
          renderer.info.render.calls;
        render();
      }
      function pick() {
        highlightBox.visible = false;
        if (singlePickingMaterial) {
          scene.overrideMaterial = singlePickingMaterial;
          renderer.render(scene, camera, pickingRenderTarget);
          scene.overrideMaterial = null;
        } else {
          renderer.render(pickingScene, camera, pickingRenderTarget);
        }
        renderer.readRenderTargetPixels(
          pickingRenderTarget,
          mouse.x,
          pickingRenderTarget.height - mouse.y,
          1,
          1,
          pixelBuffer
        );
        var id =
          (pixelBuffer[0] << 16) | (pixelBuffer[1] << 8) | pixelBuffer[2];
        var object = pickingData[id];
        if (object) {
          if (object.position && object.rotation && object.scale) {
            highlightBox.position.copy(object.position);
            highlightBox.rotation.copy(object.rotation);
            highlightBox.scale
              .copy(object.scale)
              .multiply(geometrySize)
              .multiplyScalar(scale);
            highlightBox.visible = true;
          }
        } else {
          highlightBox.visible = false;
        }
      }
      function render() {
        pick();
        renderer.render(scene, camera);
      }
    </script>
  </body>
</html>
like image 277
Alejandro Insúa Avatar asked Aug 14 '17 08:08

Alejandro Insúa


2 Answers

It seems like your question is primarily, how to do instancing in three.js? Once you've loaded a model, it doesn't really matter what format you used to create it.

That being the case, you probably just want to check out the three.js instancing examples or use one of the helpers like three-instanced-mesh.

The second link shows how to proceed, once you've grabbed the geometry from your model:

// Assumes your model only contains one mesh.
var geometry;
model.traverse(function (node) => {
  if (node.isMesh) {
    geometry = node.geometry;
  }
});

//material that the geometry will use 
var material = new THREE.MeshPhongMaterial();

//the instance group 
var cluster = new THREE.InstancedMesh( 
  geometry,
  material, 
  10000, //instance count 
  false, //is it dynamic 
  false  //does it have color 
  true,  //uniform scale
);

var _v3 = new THREE.Vector3();
var _q = new THREE.Quaternion();

for ( var i ; i < 10000 ; i ++ ) {

  cluster.setQuaternionAt( i , _q );
  cluster.setPositionAt( i , v3.set( Math.random() , Math.random(), Math.random() ) );
  cluster.setScaleAt( i , v3.set(1,1,1) );

}

scene.add( cluster );
like image 72
Don McCurdy Avatar answered Sep 22 '22 11:09

Don McCurdy


After some time of investigation I discovered why using instancedbuffergeometries were not working with the buffergeometries found in my GLTF files.

The problem is that GLTF format uses indexedbuffergeometries and the workaround is very simple, just convert them with toNonIndexed() method.

Issue fixed.

Best regards

like image 28
Alejandro Insúa Avatar answered Sep 22 '22 11:09

Alejandro Insúa