Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to load obj & mtl file with Three.js in React Native

Main objective : Load animated models exported from Maya into React Native app Exported files : obj, mtl & png file

I have setup https://github.com/react-community/react-native-webgl in my React Native project and it is working properly.

Now, when I am trying to load the MTL file using the MTLLoader, I am getting following error:

Can't find variable: document

Apparently, the MTLLoader is calling TextureLoader which internally calls some load function which has 'document' reference. So what could be the solution to this ?

Here are the two files that I am using:

three.js

const THREE = require("three");
global.THREE = THREE;
if (!window.addEventListener)
    window.addEventListener = () => { };
// require("three/examples/js/renderers/Projector");
require("three/examples/js/loaders/MTLLoader");
require("three/examples/js/loaders/OBJLoader");
export default THREE;

ThreeView.js

import React, { Component } from "react";
import { StyleSheet, View } from "react-native";
import { WebGLView } from "react-native-webgl";
import THREE from "./three";
import { image } from "src/res/image";

export default class ThreeView extends Component {
    requestId: *;
    componentWillUnmount() {
    cancelAnimationFrame(this.requestId);
}
onContextCreate = (gl: WebGLRenderingContext) => {
    const rngl = gl.getExtension("RN");

    const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
    const renderer = new THREE.WebGLRenderer({
        canvas: {
            width,
            height,
            style: {},
            addEventListener: () => { },
            removeEventListener: () => { },
            clientHeight: height
        },
        context: gl
    });
    renderer.setSize(width, height);
    renderer.setClearColor(0xffffff, 1);

    let camera, scene;
    let cube;

    function init() {
        camera = new THREE.PerspectiveCamera(75, width / height, 1, 1100);
        camera.position.y = 150;
        camera.position.z = 500;
        scene = new THREE.Scene();

        var mtlLoader = new THREE.MTLLoader();
        mtlLoader.load('female-croupier-2013-03-26.mtl', function (materials) {
            materials.preload();

            var objLoader = new THREE.OBJLoader();
            objLoader.setMaterials(materials);
            objLoader.load('female-croupier-2013-03-26.obj', function (object) {
                scene.add(object);
            }, onLoading, onErrorLoading);
        }, onLoading, onErrorLoading);
    }
    const onLoading = (xhr) => {
        console.log((xhr.loaded / xhr.total * 100) + '% loaded');
    };
    const onErrorLoading = (error) => {
        console.log('An error happened', error);
    };
    const animate = () => {
        this.requestId = requestAnimationFrame(animate);
        renderer.render(scene, camera);

        // cube.rotation.y += 0.05;

        gl.flush();
        rngl.endFrame();
    };

    init();
    animate();
};
render() {
    return (
        <View style={styles.container}>
            <WebGLView
                style={styles.webglView}
                onContextCreate={this.onContextCreate}
            />
        </View>
    );
}
}

const styles = StyleSheet.create({
container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center"
},
webglView: {
    width: 300,
    height: 300
}
});
like image 278
Aditya Avatar asked Mar 07 '23 06:03

Aditya


1 Answers

This error is as others have said caused by threejs trying to use features from a browser which react-native does not have.

I've gotten so far as to be able to load the textures (which is the stage you're getting the error from) by monkey patching the texture loader to use the loader in react-native-webgl. Add this in your init function (right near the top preferably).

//make sure you have defined renderer and rngl

/*

const renderer = new THREE.WebGLRenderer(...)

const rngl = gl.getExtension("RN");

*/

const loadTexture = async function(url, onLoad, onProgress, onError) {
      let textureObject = new THREE.Texture();
      console.log("loading",url,'with fancy texture loader');
      let properties = renderer.properties.get(textureObject);


      var texture = await rngl.loadTexture({yflip: false, image: url});

      /*
      rngl.loadTexture({ image: url })
        .then(({ texture }) => {
        */
          console.log("Texture [" + url + "] Loaded!")
          texture.needsUpdate = true;
          properties.__webglTexture = texture;
          properties.__webglInit = true;
          console.log(texture);
              
          
          if (onLoad !== undefined) {
            //console.warn('loaded tex', texture);
            onLoad(textureObject);
          }
          
  
      //});

    
      return textureObject;
  
  }
  
  THREE.TextureLoader.prototype.load = loadTexture;   

This solves the problem of loading textures and I can see them load in Charles but they still don't render on a model so I'm stuck past that point. Technically a correct answer but you'll be stuck as soon as you've implemented it. I'm hoping you can comment back and tell me you've gotten further.

like image 57
kbcool Avatar answered Mar 19 '23 05:03

kbcool