0

I'm trying to load a 3D model of a cube onto a 3D Force Directed Graph using GLTFLoader in three.js. The project is built using Angular.

The model is loaded, showing GLTFLoader: 23.507080078125ms but the object is not displayed. It further gives an error showing,

ERROR TypeError: Cannot read property 'center' of undefined
at Sphere.copy (three.module.js:5347)
at Mesh.raycast (three.module.js:14240)
at intersectObject (three.js:42091)
at Raycaster.intersectObjects (three.js:42164)
at animate (3d-force-graph.module.js:386)
at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:398)
at Object.onInvokeTask (core.es5.js:4136)
at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:397)
at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask (zone.js:165)
at ZoneTask.invoke (zone.js:460)
at timer (zone.js:1732)

The code to load the model is as below:

    var manager = new THREE.LoadingManager();
    var loader = new GLTFLoader(manager);
    loader.load(
      // resource URL
      'assets/model/cube.gltf',
      // called when the resource is loaded
      function ( gltf ) {           
        gltf.scene.traverse( function ( child ) {
          if ( child.isMesh ) {
            child.material = gltf.materials;
          }
        } );               
        var cube = gltf.scene; // Object        
        self.showGraph(gData,cube,themeNum);
        console.log("graph drawn");
      },
      // called when loading is in progresses
      function ( xhr ) {    
        console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );    
      },
      // called when loading has errors
      function ( error ) {    
        console.log( 'An error happened -- ' + error );    
      }
    );
    
// function to draw the graph -- to be executed on 3D model load
showGraph(gData:any, cube:any, themeNum: any){
    const Graph = ForceGraph3D()
      (document.getElementById('3d-graph'))
      .nodeThreeObject(({ group }) => new THREE.Mesh(
        [
          new THREE.BoxGeometry(Math.random() * 20, Math.random() * 20, Math.random() * 20),          
          cube,
          new THREE.CylinderGeometry(Math.random() * 10, Math.random() * 10, Math.random() * 20),
          new THREE.DodecahedronGeometry(Math.random() * 10),
          new THREE.SphereGeometry(Math.random() * 10),
          new THREE.TorusGeometry(Math.random() * 10, Math.random() * 2),
          new THREE.TorusKnotGeometry(Math.random() * 10, Math.random() * 2)
        ][group],
        new THREE.MeshLambertMaterial({
          color: this.themes[themeNum][group],
          transparent: true,
          opacity: 0.75
        })
      ))
        .nodeAutoColorBy('group')  
        .onNodeClick(node => {    
          this.attach3DNodeClickEvent(node);
        })      
        .graphData(gData); 
  }

The cube.gltf file exported from Blender is as below:

{
    "accessors" : [{
        "bufferView" : 0, 
        "componentType" : 5121, 
        "count" : 36, 
        "max" : [23], 
        "min" : [0], 
        "type" : "SCALAR"
    }, {
        "bufferView" : 1, 
        "componentType" : 5126, 
        "count" : 24, 
        "max" : [1, 1, 1], 
        "min" : [-1, -1, -1], 
        "type" : "VEC3"
    }, {
        "bufferView" : 2, 
        "componentType" : 5126, 
        "count" : 24, 
        "max" : [1, 1, 1], 
        "min" : [-1, -1, -1], 
        "type" : "VEC3"
    }], 
    "asset" : {
        "generator" : "Khronos Blender glTF 2.0 exporter", 
        "version" : "2.0"
    }, 
    "bufferViews" : [{
        "buffer" : 0, 
        "byteLength" : 36, 
        "byteOffset" : 0, 
        "target" : 34963
    }, {
        "buffer" : 0, 
        "byteLength" : 288, 
        "byteOffset" : 36, 
        "target" : 34962
    }, {
        "buffer" : 0, 
        "byteLength" : 288, 
        "byteOffset" : 324, 
        "target" : 34962
    }], 
    "buffers" : [{
        "byteLength" : 612, 
        "uri" : "cube.bin"
    }], 
    "materials" : [{
        "name" : "Material", 
        "pbrMetallicRoughness" : {
            "baseColorFactor" : [0.114473, 0.362915, 0.64, 1], 
            "metallicFactor" : 0
        }
    }], 
    "meshes" : [{
        "name" : "Cube", 
        "primitives" : [{
            "attributes" : {
                "NORMAL" : 2, 
                "POSITION" : 1
            }, 
            "indices" : 0, 
            "material" : 0
        }]
    }], 
    "nodes" : [{
        "name" : "Camera", 
        "rotation" : [0.483536, 0.336872, -0.208704, 0.780483], 
        "translation" : [7.48113, 5.34367, 6.50764]
    }, {
        "mesh" : 0, 
        "name" : "Cube"
    }, {
        "name" : "Lamp", 
        "rotation" : [0.169076, 0.75588, -0.272171, 0.570948], 
        "scale" : [1, 1, 1], 
        "translation" : [4.07625, 5.90386, -1.00545]
    }], 
    "scene" : 0, 
    "scenes" : [{
        "name" : "Scene", 
        "nodes" : [1, 2, 0]
    }]
}

What am I doing wrong here? The examples on three.js docs seem to be the same.

Any help is much appreciated.

Fleur
  • 666
  • 1
  • 8
  • 29
  • Please include the full error. It should be telling you exactly where and why the problem is happening. – TheJim01 Mar 22 '18 at 12:47
  • Can you load the cube in the following `glTF` viewer? https://gltf-viewer.donmccurdy.com/ – Mugen87 Mar 22 '18 at 13:31
  • @Mugen87 can load in the `glTF` viewer but have to provide both `gltf` and `bin` files generated by `Blender`. – Fleur Mar 23 '18 at 03:42
  • @TheJim01 I have updated the question with the complete error. However it does not give much information as to where the error is occurring. – Fleur Mar 23 '18 at 03:45
  • @fleur That's okay. Your glTF asset consists of both files. If you place `.gltf` and `.bin` in the same folder, you should be able to load the file via `GLTFLoader`. – Mugen87 Mar 23 '18 at 10:02

1 Answers1

0

According to the error, you're performing a raycast in your animate function, which is where the error is occurring. Specifically, the error is happening when the raycast process is trying to copy an object's bounding sphere.

// three.js r91, Mesh.js, line 237
sphere.copy( geometry.boundingSphere );

It's saying it can't find property center on geometry.boundingSphere, because geometry.boundingSphere is undefined.

Unless GLTFLoader automatically computes bounding spheres, this information isn't automatically populated. You'll need to call geometry.computeBoundingSphere() before calling raycast on an object. If GLTFLoader isn't doing this job, then you'll likely need to do it for the whole scene.

Running this code after the data is loaded (and before starting animate) will populate all of the objects' bounding spheres:

scene.traverse(function(node){
    if(node.geometry){
        node.geometry.computeBoundingSphere();
    }
});
TheJim01
  • 8,411
  • 1
  • 30
  • 54
  • Can confirm that GLTFLoader does not pre-compute boundingSphere or boundingBox, [default for both is null](https://threejs.org/docs/#api/core/BufferGeometry.boundingBox). – Don McCurdy Mar 23 '18 at 15:31