Loading and manipulating a 3D model in react-three-fiber


Once I kicked the react-three-fiber tires it was time to start learning how to render 3D models in the project I started this week. I wanted to use a free submarine model I got from Turbo Squid. I just downloaded the .blend file and opened it in Blender, then File > Export > gITF 2.0. glTF is a file format for 3D models using the JSON standard.

Loading the submarine.gltf model

First I needed to create a new component for the model.

 import React, { useLayoutEffect }from 'react'; 
 import { useGLTF } from '@react-three/drei'; 
 import * as THREE from 'three'; 
 export const Submarine = ({currentColor}) => { 
   const { scene, nodes, materials } = useGLTF('./models/submarine.gltf'); 
   useLayoutEffect(() => { 
     Object.assign(materials.Material, {  
       roughness: 0,  
       metalness: 0.25, 
       emissive: new THREE.Color(0x000000), 
       color: currentColor, 
       envMapIntensity: 0.5 }) 
   }, [scene, nodes, materials, currentColor]); 
   return <primitive object={scene} /> 

I’m importing useGLTF from @react-three/drei (A collection of abstractions for react-three-fiber.) and pulling out the some variables to use with React’s useLayoutEffect hook. I’ll be using the currentColor state to change the color of the submarine.

Changing the submarine color

Now I needed to update the App.js file to store some colors and refactor the handleColorChange function:

  const orange = new THREE.Color(0xffa500);
  const crimson = new THREE.Color(0xdc143c);
  const teal = new THREE.Color(0x008080);
  const steelblue = new THREE.Color(0x4682b4);

  const [currentColor, setCurrentColor] = useState(orange);

  const handleColorChange = (event, color) => {
    if (color === 'crimson') {
    } else if (color === 'teal') {
    } else if (color === 'steelblue') {
    } else {

Moving the scene with mouse

Last thing to do was use the <OrbitControls> component from @react-three/drei so I could make the submarine rotate and respond to mouse events.

<OrbitControls autoRotate enableZoom={false} enablePan={false} minPolarAngle={Math.PI / 2.8} maxPolarAngle={Math.PI / 2.8} /> 

I have a branch with this code and here’s what the project looks like so far: