OBJ w/MTL renderer example from docs not working

I am following the webgl_obj_mtl example from the three.js docs (https://threejs.org/examples/?q=obj#webgl_loader_obj_mtl). The first roadblock I ran into is that three.js (for some reason) does not distribute the loaders by default on npm, so I set up three.js in conjunction with “three-addons” as shown below.

Here is my code:

import * as THREEJS from 'three';
import * as THREE_ADDONS from 'three-addons';
const THREE = { ...THREEJS, ...THREE_ADDONS };

(function() {
  var elem = $('.area-test')[0];
  if(!elem) return;

  $(document).ready(function() {
    var container, stats;
    var camera, scene, renderer;
    var mouseX = 0, mouseY = 0;
    var windowHalfX = window.innerWidth / 2;
    var windowHalfY = window.innerHeight / 2;
    init();
    animate();
    function init() {
      container = $('.area-test')[0];
      camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
      camera.position.z = 250;
      // scene
      scene = new THREE.Scene();
      var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
      scene.add( ambientLight );
      var pointLight = new THREE.PointLight( 0xffffff, 0.8 );
      camera.add( pointLight );
      scene.add( camera );
      // model
      var onProgress = function ( xhr ) {
        if ( xhr.lengthComputable ) {
          var percentComplete = xhr.loaded / xhr.total * 100;
          console.log( Math.round( percentComplete, 2 ) + '% downloaded' );
        }
      };
      var onError = function ( xhr ) { };
      THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
      new THREE.MTLLoader()
        .setPath( 'models/test' )
        .load( 'lighting.mtl', function ( materials ) {
          materials.preload();
          new THREE.OBJLoader()
            .setMaterials( materials )
            .setPath( '/models/test/' )
            .load( 'lighting.obj', function ( object ) {
              object.position.y = - 95;
              scene.add( object );
            }, onProgress, onError );
        } );
      //
      renderer = new THREE.WebGLRenderer();
      renderer.setPixelRatio( window.devicePixelRatio );
      renderer.setSize( window.innerWidth, window.innerHeight );
      container.appendChild( renderer.domElement );
      document.addEventListener( 'mousemove', onDocumentMouseMove, false );
      //
      window.addEventListener( 'resize', onWindowResize, false );
    }
    function onWindowResize() {
      windowHalfX = window.innerWidth / 2;
      windowHalfY = window.innerHeight / 2;
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize( window.innerWidth, window.innerHeight );
    }
    function onDocumentMouseMove( event ) {
      mouseX = ( event.clientX - windowHalfX ) / 2;
      mouseY = ( event.clientY - windowHalfY ) / 2;
    }
    //
    function animate() {
      requestAnimationFrame( animate );
      render();
    }
    function render() {
      camera.position.x += ( mouseX - camera.position.x ) * .05;
      camera.position.y += ( - mouseY - camera.position.y ) * .05;
      camera.lookAt( scene.position );
      renderer.render( scene, camera );
    }
  });
})();

The error I am getting is:

application.js:43 Uncaught TypeError: Cannot read property 'load' of undefined
    at init (application.js:43)
    at application.js:21
    at Object.<anonymous> (application.js:86)
    at __webpack_require__ (bootstrap 1326183ce48070e27f4a:19)
    at Object.defineProperty.value (bootstrap 1326183ce48070e27f4a:62)
    at bootstrap 1326183ce48070e27f4a:62

The .load line the error is complaining about is this line:

  new THREE.MTLLoader()
    .setPath( 'models/test' )
    .load( 'lighting.mtl', function ( materials ) {

Note that I am using the webpacker gem in a Rails app to do modern javascript, so all of this gets run through webpack.

I have tried asking on stackoverflow but the question is being ignored so I would really appreciate an answer. Thanks so much.

how about referencing the loader to a variable and then call methods on it.
Seems like .setPath() is a void return,
therefore you are trying to call a method on an undefined object.

try instead:
var loader = new THREE.MTLLoader();
loader.setPath( … );
loader.load( … )

Thanks I will try that. Sucks that the official examples have errors like this.

The example is correct. The problem is that your used module three-addons is more than two years old. The official examples always use the latest code of the library. I suggest you use MTLLoader from the repo and turn it into a module.

1 Like

Thanks for the advice – I didn’t realize that.

On that note, why is it that these “addons” haven’t been merged into the main distribution, as they are commonly needed features?

It’s a goal of the project to keep the core of the library lean. If we start adding OBJLoader or MTLLoader to the core, we would also have to add many other files. This is something three.js wants to avoid.

1 Like

For background on that, see https://github.com/mrdoob/three.js/issues/9562. We would like to make these addons available through the usual distribution channels, even though they won’t be part of the core library, but this is technically and logistically difficult.

1 Like