Reputation: 101
In my app I want to add texture to the loaded .obj model. I have the same situation with my .fbx loaded models. Below is my example code, but this works only with something like sphereGeometry not with a loaded model.
Thanks in Advance!
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { useTexture } from '@react-three/drei'
const OBJModel = ({ file }: { file: string }) => {
const obj = useLoader(OBJLoader, file)
const texture = useTexture(textureFile)
return (
<mesh>
<primitive object={obj} />
<meshStandardMaterial map={texture} attach="material" />
</mesh>
)
}
Upvotes: 5
Views: 6922
Reputation: 11258
I just came up with something like this, hopefully would be useful for you also,
const OBJModel = ({ file, textureFile, ...props }) => {
const obj = useLoader(OBJLoader, file)
const texture = useTexture(textureFile)
useEffect(() => {
obj.traverse(child => {
if (child.isMesh) child.material.map = texture
})
}, [obj])
return <primitive object={obj} {...props} />
}
Which can be used with something like
<OBJModel file='./a.obj' textureFile='./a.png' position-z={10} />
Upvotes: 2
Reputation: 1435
i believe obj loader returns a group, or a mesh, it wouldn't make sense to put that into a top-level mesh, and giving the top level a texture wont change the loaded obj mesh.
there are three possible solutions:
Upvotes: 2
Reputation: 4025
primitive
is not a subset of mesh. it can be a children of group
.primitive
requires both geometry and material as props. Mesh
requires both geometry and material as props. it's pretty obvious both cannot be used as subset of each other.Mesh
or primitive
. I'd suggest using Mesh
which has abundant documentations. primitive
is not documented enough.OBJ
acquired through useLoader
may have complex group inside it. Models usually contain larger sets such as group
or scene
. Group and Scenes can't have textures. Mesh
can.OBJ(result of useLoader) = scene => group => mesh => geometry, texture
traversing is required to acquire geometry from mesh.
// I've implemented this with Typescript,
// but it is not necessary to use 'as' for type conversion.
const obj = useLoader(OBJLoader, "/rock.obj");
const texture = useTexture("/guide.png");
const geometry = useMemo(() => {
let g;
obj.traverse((c) => {
if (c.type === "Mesh") {
const _c = c as Mesh;
g = _c.geometry;
}
});
return g;
}, [obj]);
// I've used meshPhysicalMaterial because the texture needs lights to be seen properly.
return (
<mesh geometry={geometry} scale={0.04}>
<meshPhysicalMaterial map={texture} />
</mesh>
);
I've implemented it in codesandbox. here's the working code:
https://codesandbox.io/s/crazy-dawn-i6vzb?file=/src/components/Rock.tsx:257-550
Upvotes: 8