javascriptreactjswebpackthree.jsobjloader

import LOCAL .obj to load with Three.js OBJLoader using React


I'm trying to import a local .obj to be loaded with THREE.OBJLoader().load() function, I know it works because when I pass a non-local URL (for example 'http://downloadOBJfromHere.com') it loads it perfectly, my main problem is that that way everytime I load it, it has to download the obj and it takes ages.

I'm doing server side rendering with React.

import * as THREE from 'three';
import * as OBJLoader from 'three-obj-loader';
import obj from '../../../assets/obj/scene.obj';

In ComponentDidMount():

const loader = new THREE.OBJLoader();
        loader.load( obj,
            object => {
                scene.add(object);
            },
            xhr => {
                console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
            },
            error => {
                console.log("Error! ", error);
            }
        );

When I do this I get the error in the Chrome console:

Unexpected line: '<!doctype html><html lang="en" data-reactroot="" data-reactid="1" data-react-checksum="1415239080"><head data-reactid="2"><meta charset="utf-8" data-reactid="3"/><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" data-reactid="4"/><title data-reactid="5">TITLE</title><link rel="stylesheet" href="/client/style.css" data-reactid="6"/></head><body data-reactid="7"><div id="root" data-reactid="8"><!-- react-empty: 1 --></div><script type="text/javascript" src="/client/vendor.js" data-reactid="9"></script><script type="text/javascript" src="/client/client.js" data-reactid="10"></script></body></html>'

(Keep in mind that with the URL it loads fine). It shows me the root html that is served.

So I'm guessing it has to do with some webpack config for importing files, I'm using file-loader to load .obj files:

This is part of my webpack config:

const paths = {
  source: path.join(__dirname, '../source'),
  javascript: path.join(__dirname, '../source/js'),
  images: path.join(__dirname, '../source/assets/img'),
  svg: path.join(__dirname, '../source/assets/svg'),
  obj: path.join(__dirname, '../source/assets/obj'),
  build: path.join(__dirname, '../build'),
};

const rules = [
  {
    test: /\.mtl$/,
    loader: 'mtl-loader'
  },
  { test: /\.obj$/,
    loader: 'file-loader',
    include: paths.obj
  },
  {
    test: /\.(png|gif|jpg|svg)$/,
    include: paths.images,
    use: [{
      loader: 'file-loader',
      options: {
        name: 'client/assets/[name]-[hash].[ext]',
      }
    }]
  }
];

EDIT Added images loader to show how I serve images.

Here's part of my server config

const app = express();
const hostname = 'localhost';
const port = 8080;

app.use('/client', express.static('build/client'));

app.use((req, res) => {
  const { persistor, store } = configureStore();
  const context = {};

  const appHtml = ReactDOMServer.renderToString(
    <Provider store={ store }>
      <PersistGate persistor={ persistor }>
        <StaticRouter location={ req.url } context={ context }>
            <App />
        </StaticRouter>
      </PersistGate>
    </Provider>
  );

  const serverHtml = getServerHtml(appHtml, 'dehydratedState');
  if (context.url) {
    res.redirect(301, context.url);
  } else {
    res.status(context.status || 200).send(serverHtml);
  }
});

app.listen(port, err => callback);

Solution

  • I figured it out.

    In the webpack config I changed the .obj loader, from 'file-loader' to 'url-loader'

    const rules = [
      {
        test: /\.mtl$/,
        loader: 'mtl-loader'
      },
      { test: /\.obj$/,
    
    // CHANGE HERE
        loader: 'url-loader',
    
        include: paths.obj
      },
      {
        test: /\.(png|gif|jpg|svg)$/,
        include: paths.images,
        use: [{
          loader: 'file-loader',
          options: {
            name: 'client/assets/[name]-[hash].[ext]',
          }
        }]
      }
    ];
    

    Note: I also had a problem importing a script called OutlinePass (for THREE.js) that doesn't exist in npm, I managed to import it this way:

    const script = document.createElement("script");
            script.src = require('!!url-loader!../../../assets/webgl/js/OutlinePass.js');
            script.async = true;
            document.body.appendChild(script);
    

    the script itself didn't work but I managed to import it (what we are talking about here :) )