coiai Logo

This article is an English translation of the original Japanese post. Read the Japanese original →

Getting Stuck Displaying an OBJ or glTF in React Three.js

December 2, 2023

Categories: 未分類

What happened?

I tried to display my own model in Three.js, and the model just wouldn't show up, which had me stumped.

The fix turned out to be putting the object files under public.

The result

What I actually did

Directory structure

public
  |- object
  |  |- Monkey.mtl
  |  |_ Monkey.obj
  |_ etc...
src
package.json

The code

Here's the code for object3d.tsx.

import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';

const Object3D = () => {
  const mount = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!mount.current) return;

    const width = mount.current.clientWidth;
    const height = mount.current.clientHeight;

    // scene, camera, and renderer initialization
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer({ antialias: true });

    // controls for mouse rotation
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;

    // camera position
    camera.position.z = 2;

    // renderer settings
   const light = new THREE.PointLight(0xffffff, 1, 1000);
   light.position.set(0, 0, 10); // ライトの位置を設定します(x, y, z)

    // ライトをシーンに追加
    scene.add(light);

    // mount the renderer to the DOM
    mount.current.appendChild(renderer.domElement);

    // MTL and OBJ loader
    const mtlLoader = new MTLLoader();
    const objLoader = new OBJLoader();
    const MonkeyMtl = '/Objects/Monkey.mtl';
    const MonkeyObj = '/Objects/Monkey.obj';

    mtlLoader.load(
      MonkeyMtl,
      (materials) => {
        materials.preload();
        objLoader.setMaterials(materials);
        objLoader.load(
          MonkeyObj,
          (object) => {
            object.position.set(0, 0, 0); // set position to center
            object.scale.set(0.5, 0.5, 0.5); // scale down
            scene.add(object);
            console.log('Added object to scene');
          },
          (xhr) => {
            console.log((xhr.loaded / xhr.total * 100) + '% loaded');
          },
          (error) => {
            console.log('An error happened', error);
          }
        );
      },
      (xhr) => {
        console.log((xhr.loaded / xhr.total * 100) + '% loaded');
      },
      (error) => {
        console.log('An error happened', error);
      }
    );

    // animation
    const animate = () => {
      requestAnimationFrame(animate);

      // controls update
      controls.update();

      // rendering
      renderer.render(scene, camera);
    };

    // start animation
    animate();

    // handle resize
    const handleResize = () => {
      if (!mount.current) return;

      const width = mount.current.clientWidth;
      const height = mount.current.clientHeight;

      renderer.setSize(width, height);
      camera.aspect = width / height;
      camera.updateProjectionMatrix();
    };

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return (
    <div>
      <h2>モデルテスト</h2>
    <div
      ref={mount}
      style={{ width: '100%', height: '100vh' }}
    />
    </div>
  );
};

export default Object3D;