Search code examples
reactjsthree.jswebglreact-three-fiberreact-three-drei

How to dynamically render and edit 3D models using ReactJS?


I am new to Three.js, I am building a 3D product configurator using React-three-fiber. I have followed some tutorials and am able to render and edit textures/colors live, but still unable to find a way to dynamically load any 3D object and edit it on the go. Right now everything is hardcoded. Any piece of code or if anyone can point me in the right direction for how can I make it dynamic so it can handle any product and user can modify any element of the 3D object from the website. Right now this is how I am doing.

const Franklin = () => {
    const group = useRef();
    const { nodes, materials } = useGLTF('/wes/franklin/franklin.glb');
    const [
        backMap,
        backEmissiveMap,
        backDisplacementMap,
        backMetalnessMap,
        backNormalMap,
        backRoughnessMap,
    ] = useLoader(TextureLoader, [
        '/wes/franklin/textures/Franklin_back_BaseColor.png',
        '/wes/franklin/textures/Franklin_back_Emissive.png',
        '/wes/franklin/textures/Franklin_back_Height.png',
        '/wes/franklin/textures/Franklin_back_Metallic.png',
        '/wes/franklin/textures/Franklin_back_Normal.png',
        '/wes/franklin/textures/Franklin_back_Roughness.png',
    ]);

    const [
        frameMap,
        frameEmissiveMap,
        frameDisplacementMap,
        frameMetalnessMap,
        frameNormalMap,
        frameRoughnessMap,
    ] = useLoader(TextureLoader, [
        '/wes/franklin/textures/Franklin_frame_BaseColor.png',
        '/wes/franklin/textures/Franklin_frame_Emissive.png',
        '/wes/franklin/textures/Franklin_frame_Height.png',
        '/wes/franklin/textures/Franklin_frame_Metallic.png',
        '/wes/franklin/textures/Franklin_frame_Normal.png',
        '/wes/franklin/textures/Franklin_frame_Roughness.png',
    ]);
    const [
        capMap,
        capEmissiveMap,
        capDisplacementMap,
        capMetalnessMap,
        capNormalMap,
        capRoughnessMap,
    ] = useLoader(TextureLoader, [
        '/wes/franklin/textures/Franklin_cap_BaseColor.png',
        '/wes/franklin/textures/Franklin_cap_Emissive.png',
        '/wes/franklin/textures/Franklin_cap_Height.png',
        '/wes/franklin/textures/Franklin_cap_Metallic.png',
        '/wes/franklin/textures/Franklin_cap_Normal.png',
        '/wes/franklin/textures/Franklin_cap_Roughness.png',
    ]);
    const [
        seatMap,
        seatEmissiveMap,
        seatDisplacementMap,
        seatMetalnessMap,
        seatNormalMap,
        seatRoughnessMap,
    ] = useLoader(TextureLoader, [
        '/wes/franklin/textures/Franklin_seat_BaseColor.png',
        '/wes/franklin/textures/Franklin_seat_Emissive.png',
        '/wes/franklin/textures/Franklin_seat_Height.png',
        '/wes/franklin/textures/Franklin_seat_Metallic.png',
        '/wes/franklin/textures/Franklin_seat_Normal.png',
        '/wes/franklin/textures/Franklin_seat_Roughness.png',
    ]);

    return (
        <group ref={group} dispose={null}>
            <mesh
                geometry={nodes.Bottom_Bars001.geometry}
                material={nodes.Bottom_Bars001.material}
            >
                <meshStandardMaterial
                    map={frameMap}
                    emissiveMap={frameEmissiveMap}
                    displacementMap={frameDisplacementMap}
                    metalnessMap={frameMetalnessMap}
                    normalMap={frameNormalMap}
                    roughnessMap={frameRoughnessMap}
                />
            </mesh>
            <mesh
                geometry={nodes.Back_Seat002.geometry}
                material={nodes.Back_Seat002.material}
            >
                <meshStandardMaterial
                    map={backMap}
                    emissiveMap={backEmissiveMap}
                    displacementMap={backDisplacementMap}
                    metalnessMap={backMetalnessMap}
                    normalMap={backNormalMap}
                    roughnessMap={backRoughnessMap}
                />
            </mesh>
            <mesh geometry={nodes.Base001.geometry} material={nodes.Base001.material}>
                <meshStandardMaterial
                    map={frameMap}
                    emissiveMap={frameEmissiveMap}
                    displacementMap={frameDisplacementMap}
                    metalnessMap={frameMetalnessMap}
                    normalMap={frameNormalMap}
                    roughnessMap={frameRoughnessMap}
                />
            </mesh>
            <mesh geometry={nodes.Seat001.geometry} material={nodes.Seat001.material}>
                <meshStandardMaterial
                    map={seatMap}
                    emissiveMap={seatEmissiveMap}
                    displacementMap={seatDisplacementMap}
                    metalnessMap={seatMetalnessMap}
                    normalMap={seatNormalMap}
                    roughnessMap={seatRoughnessMap}
                />
            </mesh>
            <mesh geometry={nodes.Caps003.geometry} material={nodes.Caps003.material}>
                <meshStandardMaterial
                    map={capMap}
                    emissiveMap={capEmissiveMap}
                    displacementMap={capDisplacementMap}
                    metalnessMap={capMetalnessMap}
                    normalMap={capNormalMap}
                    roughnessMap={capRoughnessMap}
                />
            </mesh>
        </group>
    );
};

Solution

  • I did it this way, take the scene from GLTFLoader and traverse through it to apply textures to specific nodes and at the end pass this scene to primitive as object. Taken from pure three.js approach.

    scene.traverse((node) => {
        if (node.isMesh) {
            const checkItem = selectedItems.find((itm) =>
                node.name.includes(itm.value)
            );
    
            if (checkItem) {
                const fabric = items.filter(
                    (item) => item.name.value === checkItem.value
                );
                var tempMaterial = new MeshStandardMaterial({
                    ...node.material,
                    map: loadTexture(fabric[0].texture),
                });
                node.material = tempMaterial;
            }
        }
    });