How to add objects (3D models) to Scene on button click?

Hi all!

I am trying to add 3D models (converted from .gltf to .jsx file) to Scene on Button click. I am using Vite, React Three Fiber, Drei and leva. For now I have got 4 objects and I want to make them appear on scene when I click on button for each individually, for example:

<button type="submit">Add chair</button>
<button type="submit">Add table</button>
<button type="submit">Add barrier</button>
<button type="submit">Add tree</button>

My code is spaghetti code for now, later I am going to refactor it (seperate files, loop over etc.) I have tried to make it works like adding task in simple todolist but it does not work. Do you know how to solve this issue?

import {
  DragControls,
  Grid,
  OrbitControls,
  PivotControls,
} from "@react-three/drei";
import Table from "../../public/temporary/Table";
import Barrier from "../../public/temporary/Barrier";
import Chair from "../../public/temporary/Chair";
import Tree from "../../public/temporary/Tree";
import { useControls } from "leva";

function Scene() {
  const { scale, Pivot } = useControls("models", {
    scale: { options: [0.2, 0.4, 0.6, 0.8] },
    Pivot: true,
  });

  return (
    <>
      <ambientLight intensity={2} />
      <OrbitControls
        makeDefault
        maxPolarAngle={Math.PI / 2.5}
        minDistance={5}
        maxDistance={25}
      />
      <directionalLight />
      <Grid
        args={[64, 64]}
        fadeDistance={32}
        fadeStrength={1.5}
        cellThickness={0.75}
        sectionThickness={1.5}
        cellSize={0.25}
      />

      <DragControls axisLock="y">
        <PivotControls
          activeAxes={[true, false, true]}
          anchor={[0, 1, 0]}
          depthTest={false}
          axisColors={["red", "green", "blue"]}
          lineWidth={7}
          scale={2}
          visible={Pivot}
        >
          <Table position={[-3, 0, -2]} scale={scale} />
        </PivotControls>
      </DragControls>
      <DragControls axisLock="y">
        <PivotControls
          activeAxes={[true, false, true]}
          anchor={[0, -0.9, 0]}
          depthTest={false}
          axisColors={["red", "green", "blue"]}
          lineWidth={7}
          scale={2}
          visible={Pivot}
        >
          <Tree position={[2, 0.075, -2]} scale={scale} />
        </PivotControls>
      </DragControls>
      <DragControls axisLock="y">
        <PivotControls
          activeAxes={[true, false, true]}
          anchor={[0, 1, 0]}
          depthTest={false}
          axisColors={["red", "green", "blue"]}
          lineWidth={7}
          scale={1.5}
          visible={Pivot}
        >
          <Chair position={[-3, 0, 4]} scale={scale} />
        </PivotControls>
      </DragControls>
      <DragControls axisLock="y">
        <PivotControls
          activeAxes={[true, false, true]}
          anchor={[0, 1, 0]}
          depthTest={false}
          axisColors={["red", "green", "blue"]}
          lineWidth={7}
          scale={2}
          visible={Pivot}
        >
          <Barrier position={[1, 0, 4]} scale={scale} pos />
        </PivotControls>
      </DragControls>
    </>
  );
}

export default Scene;

hello! You can use a event listener

inside the button, you can do something like that to create a event:

    var addEvent= new CustomEvent('on_click_to_add');
    document.dispatchEvent(addEvent);

then… on your scene file, you can create a listener for this event, like this:

    document.addEventListener('on_click_to_add', (event) => {
      const glftLoader = new GLTFLoader();

      glftLoader.load('path/gltf.gltf', (gltfScene) => {
        gltfScene.scene.position.copy(position);
        gltfScene.scene.name = name;
        gltfScene.scene.scale.set(0.3, 0.3, 0.3);

        scene.add(gltfScene.scene);
      });
    });

you can send params via event using detail, like this:

    var event = new CustomEvent('exemple_event', {
      detail: { data: something here },
    });

Thank you for quick answer! But I have got another question. Can I call add, remove etc. in React Three Fiber imperatively? If so, I am surprised because whole point of React is be declarative.

Well, i tried to use the react version and I didn’t find in any documentation showing how I could use add() or remove()… so I had to change my code to use three js

export const threeJsExample = () => {

  useEffect(() => {
    const container = document.getElementById('threejsdiv');

    const renderer = new THREE.WebGLRenderer({ antialias: true });
    container.appendChild(renderer.domElement);

    // all what you want to do here ^-^

  }, []);

  return (
    <>
      <div id='threejsdiv'></div>
    </>
  );
};

export default threeJsExample;

1 Like

Thank you for code! I have explored a lot of examples of R3F (official site Three.js and more) and I could not find any example of adding objects (or similiar). Do you have idea, where I can find examples?

there’s a lot of examples on internet, mainly in youtube, but this video helps a lot to understand some concepts and build my own project: https://www.youtube.com/watch?v=oQbfy8QP8Lc
Maybe this tutorial can help you too, just link it with the useEffect() example that i sent to you before, and you can build awesome things using React and ThreeJS.

1 Like