Hello I try to load multiples models inside a canvas in the background of my page, I was looking for an optimise way to load while customise it with css.
I came across the React-three/drei View.
Unfortunatly when I try to load a .glb model it wont show inside the canvas, while displaying the model without it work fine.
I tried to follow the multiple example but none of them worked for me.
Here is one of my model files (a smartphone model):
import { useGLTF } from "@react-three/drei";
import * as THREE from 'three';
import { GLTF } from 'three-stdlib';
import { ModelProps } from '@/constants/interface';
type GLTFResult = GLTF & {
nodes: {
Cube001: THREE.Mesh
Cube001_1: THREE.Mesh
Cube001_2: THREE.Mesh
}
materials: {
IPHONE: THREE.MeshStandardMaterial
['iphone-x-screenshot']: THREE.MeshStandardMaterial
glass: THREE.MeshStandardMaterial
}
}
type ContextType = Record<string, React.ForwardRefExoticComponent<JSX.IntrinsicElements['mesh']>>
// this represent a model of a smartphone
const SmartphoneModel = ({modelRef,position,rotation,scale} : ModelProps) => {
const { nodes, materials } = useGLTF("/3d/smartphone/smartphone.glb") as GLTFResult;
return (
<group
ref={modelRef}
position={position}
rotation={rotation}
dispose={null}
scale={scale ?? 1}
>
<spotLight
position={[-3, 5, 1]}
angle={1}
penumbra={1}
intensity={300}
castShadow
shadow-mapSize={1024}
/>
<mesh geometry={nodes.Cube001.geometry} material={materials.IPHONE} material-color="rgb(165, 255, 250)"/>
<mesh geometry={nodes.Cube001_1.geometry} material={materials['iphone-x-screenshot']}/>
<mesh geometry={nodes.Cube001_2.geometry} material={materials.glass} material-color="rgb(200, 200, 200)"/>
</group>
);
};
useGLTF.preload('/3d/smartphone/smartphone.glb');
export default SmartphoneModel;
And here is my main code where I try to use the component:
'use client'
import {Suspense, useRef} from 'react';
import {Canvas} from '@react-three/fiber';
import {Preload, View} from '@react-three/drei';
import {Group} from 'three';
import {SmartphoneModel, LaptopModel, CookieModel, PawnModel } from './models';
import {getNewPositionsArray, getNewPosition, getRandomNumber} from "@/utils/function";
import { ModelProps } from '@/constants/interface';
// animation: objet apparaissent et disparaissent à l'arrière plan de façon random
// TODO: rendre position z pas dépendant de la vue en utilisant view
// https://codesandbox.io/p/sandbox/view-skissor-2-forked-rvf3sz?file=%2Fsrc%2Fstyles.css%3A48%2C16
const componentModels = {
smartphone: SmartphoneModel,
laptop: LaptopModel,
cookie: CookieModel,
pawn: PawnModel
};
// this component is a wrapper to add new prop and control the animation of the model passed as prop
const ModelFadeWrapper = ({model, id, props, getNewPosition} : {model : string, id : string, props : ModelProps, getNewPosition : (id: string) => [number, number, number]}) => {
const refGroup = useRef<Group>(null); // we create a ref to handle the model rotation inside useFrame
// we create a new var as a component model in order to pass new prop to it like position, ref and the other props.
const SpecificModel = componentModels[model as keyof typeof componentModels];
return (
<View className='w-[200px] h-[200px] absolute'>
<fog attach="fog" color="black" near={0} far={10}/>
<directionalLight position={[10, 10, 5]} intensity={2} />
<directionalLight position={[-10, -10, -5]} intensity={1} />
<SpecificModel modelRef={refGroup} {...props}/>
</View>
);
};
// this component represent the background canvas of the about page
// here the main trick is to use ThreeElements.fog to make a kind of fadeIn fadeOut effect by only playing witht the z position of a model
const AboutBGCanvas = () => {
let modelList:{name:string,id:string,props:ModelProps}[] = [
{name:"pawn", id:"Pawn1", props:{scale:0.1}},
{name:"pawn", id:"Pawn2", props:{scale:0.1}},
{name:"laptop", id:"Laptop1",props:{ rotation:[0.8,0,0], scale:1}},
{name:"laptop", id:"Laptop2",props:{ rotation:[0.8,0,0], scale:1}},
{name:"smartphone", id:"Smartphone1",props:{ scale:2}},
{name:"smartphone", id:"Smartphone2",props:{ scale:2}},
{name:"cookie", id:"Cookie1",props:{ scale:1}}
]; // this array contain model component
let positionModelList:[number,number,number][] = getNewPositionsArray(7,{x:[-2.5,2.5],y:[-3,3]},1.6); // this array represent all the positions of the respective models in the canvas
modelList.forEach((modelObj,index)=>{ // for each model object of modelList we add the position value to the props object
modelObj.props.position = positionModelList[index];
});
// this function changed the position of a model by his id and update the positionModelList and return it
const changeModelPositions = (id: string) : [number,number,number] => {
const modelIndex = modelList.findIndex((obj)=>obj.id === id); // we get the model with the id
// we create a new position for the model but we make sure to remove the old position of the model from the list before generating a new one
const newPosition = getNewPosition(positionModelList.toSpliced(modelIndex,1),{x:[-2.5,2.5],y:[-3,3]},1.6);
positionModelList[modelIndex] = newPosition; // we replace the model's position with the new one in the list
return newPosition; // we return the new position
};
return (
<div className='w-full h-auto absolute inset-0 z-[-1]'>
<Canvas dpr={[1, 1.5]} shadows gl={{ alpha: false }}>
<Suspense fallback={null}>
{
modelList.map((model)=>
<ModelFadeWrapper key={model.id} model={model.name} id={model.id} props={model.props} getNewPosition={changeModelPositions}/>
)
}
</Suspense>
<Preload all/>
</Canvas>
</div>
);
};
export default AboutBGCanvas;
What I am doing wrong? Thank you in advance for your responses.