I am trying to understand how I can reuse models in ThreeJS / React Three Fiber
So let’s say I have a component Dude, where I load a model like so
import model from "../../assets/dude.glb";
and use it like so
const Dude = () => {
const dudeRef = useRef();
const groupRef = useRef();
const gltf = useLoader(GLTFLoader, model);
return (
<group ref={groupRef} position={[0, 6, 0]}>
<primitive
ref={dudeRef}
object={gltf.scene}
scale={[1, 1, 1]}
/>
</group>
);
If I load that model in other components, it will use the model that is already loaded the first time
I can make duplicate files and that works, so I know the problem isn’t the model itself
I tried cloning the scene and using clone method from SkeletonUtils, but it did not work
Does anyone know what might my problem be? Ty
NVM I was using clone method from SkeletonUtils incorrectly
This is what worked for
import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
const Dude = () => {
const dudeRef = useRef();
const groupRef = useRef();
const gltf = useLoader(GLTFLoader, model);
return (
<group ref={groupRef} position={[0, 6, 0]}>
<primitive
ref={dudeRef}
object={clone(gltf.scene)}
scale={[1, 1, 1]}
/>
</group>
);
I was previously doing
gltf.scene.clone()
Which did not work for me
drcmda
July 12, 2024, 7:26am
3
there is a misconception, both would be anti patterns.
your first example uses primitive, read this React Three Fiber Documentation
Scene objects can only ever be added once in Threejs. If you attempt to add one and the same object in two places Threejs will remove the first instance automatically. This will also happen with primitive! If you want to re-use an existing object, you must clone it first.
your second example does clone, but without usememo, so the whole model will be cloned when the component re-renders. btw skelleton clone is usually for skinned meshes.
function Dude(props) {
const { scene } = useGltf(model)
const clone = useMemo(() => scene.clone(), [scene])
return <primitive object={clone} {...props} />
}
<Dude position={[0, 6, 0]} scale={1} />
<Dude position={[6, 0, 0]} scale={2} />
there’s two better ways,
1. using drei/Gltf
import { Gltf } from '@react-three/drei'
<Gltf src={model} position={[0, 6, 0]} scale={1} />
<Gltf src={model} position={[6, 0, 0]} scale={2} />
2. using gltfjsx
1 Like
Thank you for the hint
Is there a big enough advantage to using gltfjsx as compated to Gltf from drei? Reading the description page the big advantage from it seems to be it’s ability to compress models?
I tried using gltfjsx but the Model
component it makes is not reusable for me for whatever reason
This is the command I used
npx gltfjsx@6.2.18 src/assets/dude.glb --name DudeModel --transform --simplify
And it made me a Model
component (I thought the named would be DudeModel) as well as a dude-transformed.glb
I moved that new glb file into src/assets/
and update path in Model
import React, { useRef } from 'react'
import { useGLTF, useAnimations } from '@react-three/drei'
import model from "../../assets/dude-transformed.glb";
export function Model(props) {
const group = useRef()
const { nodes, materials, animations } = useGLTF(model)
And I tried using it just like it was suggested in the docs
<Model key="model1" position={[10, 10, -60]}/>
<Model key="model2" position={[10, 10, -70]}/>
And it did not work, only let’s me place the 1 copy
Was there something I overlooked?
drcmda
July 12, 2024, 3:58pm
6
the purpose of gltfjsx is declarative re-usability
example 1 Re-using GLTFs - CodeSandbox
example 2 (skinned meshes cannot be re-used in threejs without skelletontools) https://codesandbox.io/p/sandbox/gltf-animations-re-used-k8phr ?
drcmda
July 12, 2024, 4:03pm
7
ps, i might have to make the skeletontools thing automatic, it’s too complex and hidden. i think the only place where it comes up is here three.js docs
I see so even after using gltfjsx you have to adjust the Model component to make use of skeletontools, guess I’ll stick with using Gltf from drei per your first suggestion ty ty
drcmda
July 12, 2024, 4:37pm
9
i’ll push a change in a few minutes, it’s already working locally. it will take care of it. <Gltf uses skeletontools also but you’d use declarativity.
Is skinned instancing an option here? Not r3f based but the source looks straight forward enough to port a workable component from…
drcmda
July 13, 2024, 5:16pm
11
@iseafish69 it’s in
npx gltfjsx stacy.glb --transform
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.4.0 stacy.glb --transform --console
Files: stacy.glb [1.87MB] > /Users/ph/Downloads/651190fd-3842-46db-b089-e27b20818530/public/stacy-transformed.glb [822.97KB] (56%)
*/
import React from 'react'
import { useGraph } from '@react-three/fiber'
import { useGLTF, useAnimations } from '@react-three/drei'
import { SkeletonUtils } from 'three-stdlib'
export function Model(props) {
const group = React.useRef()
const { scene } = useGLTF('/stacy-transformed.glb')
const clone = React.useMemo(() => SkeletonUtils.clone(scene), [scene])
const { nodes, materials, animations } = useGraph(clone)
const { actions } = useAnimations(animations, group)
return (
<group ref={group} {...props} dispose={null}>
<group name="Scene">
<group name="Stacy" rotation={[Math.PI / 2, 0, 0]} scale={0.01}>
<primitive object={nodes.mixamorigHips} />
</group>
<skinnedMesh name="stacy" geometry={nodes.stacy.geometry} material={nodes.stacy.material} skeleton={nodes.stacy.skeleton} />
</group>
</group>
)
}
useGLTF.preload('/stacy-transformed.glb')
1 Like