3
votes

I'm trying to build a simple object-viewer in React with Meteor that can import .obj and .mtl Files using the following npm modules:

  • three(0.87.1)
  • react(15.6.1)
  • three-obj-loader(1.1.3)
  • three-mtl-loader(1.0.1)

So far i have managed to display an object using the OBJLoader. But when i try to render an object after applying a texture with MTLLoader, i get this error from console:

Uncaught TypeError: Cannot read property 'toString' of undefined at WebGLPrograms.getProgramCode (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:50707) at initMaterial (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54628) at setProgram (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54820) at WebGLRenderer.renderBufferDirect (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:53883) at renderObject (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54613) at renderObjects (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54586) at WebGLRenderer.render (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:54350) at WebGlDisplay.renderScene (WebGlDisplay.jsx:86) at onClick (WebGlDisplay.jsx:90) at HTMLUnknownElement.boundFunc (modules.js?hash=eae498e3ee56e21f967b663c5bed3444c66eaef2:8794)

Cause: material.onBeforeCompile in getProgramCode is undefined

My code looks like this:

import React, { Component } from 'react'
import THREE from 'three'
const MTLLoader = require('three-mtl-loader');
const OBJLoader = require('three-obj-loader')(THREE);

export default class WebGlDisplay extends Component {
  constructor(props) {
    super(props)
  }

      //init canvas
   init(){
    const width = this.mount.clientWidth;
    const height = this.mount.clientHeight;
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
    renderer.setClearColor('#000000', 0.2);
    renderer.setSize(width, height);
    camera.position.set(3,4,6);
    camera.lookAt(new THREE.Vector3());
    this.scene = scene;
    this.camera = camera;
    this.renderer = renderer;
    this.mount.appendChild(this.renderer.domElement);
  }

  //load & render object
  drawOBJ(){
    const mtlLoader = new MTLLoader();
    let onProgress = function(e){console.log("rendering:" + e)};
    let onError = function(e){console.log("error:" + e)};
    mtlLoader.load("eagle.mtl", materials => {
        materials.preload();
        // OBJ Loader
        const objLoader = new THREE.OBJLoader();
        this.materials = materials;
        objLoader.setMaterials(materials);
        objLoader.load("eagle.obj", object => {
              this.object = object;
            this.scene.add(object);
        }, onProgress, onError);
    }, onProgress,onError);
    this.renderScene();
  }

  componentDidMount() {
    this.init();
    this.drawOBJ();
  }

  renderScene() {
    this.renderer.render(this.scene, this.camera)
  }

  render() {
    return (
      <div onClick={(e) => this.renderScene()}
        style={{ width: '800px', height: '600px' }}
        ref={(mount) => { this.mount = mount }}
      />
    )
  }
}

Does anyone have an idea why i get this error? I've tried to use different .obj- and .mtl-files, but the error remains (whenever i try to call renderScene()).

By any chance, could it be a problem with the module versions, or maybe some timing problems while loading?

Any help would be appreciated.

2
Try to avoid this => "arrow function" and use more clear and explicit code... I don't know how this => precisely works, but this appear to me another false good-idea of the elastic Javascript syntax... mostly in a context where references must be consistants and reliable... I'am not a Javascript guru, but coming from C/C++, this kind of thing make to me the same effect than a match in a gunpowder magazine.user1501157
Hmmm, can you look at the source of three and see if it has onBeforeCompile defined on a material? Are you absolutely sure you are using 87?pailhead
is there any chance you could post this code, so we can track it down and see what is going on? This looks like a bug, but nothing stands out as obvious.pailhead
arrow functions are perfectly normal JavaScript nowadays. @Sedenion I suggest you learn them. They aren't going away. There is absolutely nothing wrong with them. They are the recommended way to do many things in modern JavaScript.gman
Yes the version is definitely 87 @pailhead . So far I couldn't really wrap my head around the three source. I didn't find any direct definition of onBeforeCompile except for an empty function in Object.assign in Material.js (i don't even understand if it is called somewhere or not). Sorry, it seems i am not very helpful here. Is there a method to put this node/react stuff online without hosting it yourself? (i don't know a fitting method, but i'm pretty new to this whole stuff anyway)NickG

2 Answers

5
votes

The problem seems to be that the three-mtl-loader NPM package is referencing an outdated three.js version in it's package.json, so even though you are using the latest version of three, the plugin isn't!

Obviously this isn't a viable long-term fix, but I changed the version for three in node_modules/three-mtl-loader/package.json to 0.87.1 and deleted the directory node_modules/three-mtl-loader/node_modules for good measure and ran my example and it worked straight away, textures and all.

Clearly the plugin needs to be updated. I also saw at least one functional difference between the source in the plugin and in the three examples folder ('Tr' vs 'tr' in a case statement), and it doesn't follow the same initialization behavior as other loader plugins (specifically it isn't initialized by calling require("three-mtl-loader")(THREE)), so there's a bit of work to get it ship-shape.

Alternately, it appears that the author has updated the version number to 0.86.0 in their repo (which is high enough), just hasn't done a deploy to NPM. So, if you feel brave, you can just change your package.json to have the line

"dependencies": { ... "three-mtl-loader": "git+https://git@github.com/nascherman/three-mtl-loader.git", ... }

0
votes

As a workaround, what i ended up doing is to get a local copy of the newest MTLLoader Version and slightly modifing it, as it seems to be a problem with the version pointed out by @user1691694. In case somebody needs this here my way of importing it:

In the MTLLoader file, add the following line at the top:

import THREE from 'three';

and at the bottom:

module.exports.default =  THREE.MTLLoader

Use it like in the drawOBJ function in the question post and import it in the target file like this:

import MTLLoader from './MTLLoader.js';