Closed evshiron closed 1 year ago
Looks like ternary doesn't work with conditionally applying texture. You could instead render like this.
{clicked && (
<meshStandardMaterial map={map}></meshStandardMaterial>
)}
{!clicked && (
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'}></meshStandardMaterial>
)}
It might be that when state changes, it's remounting the mesh without textures.
This sounds like a bug in React, mildly related to #2844. I'll see what R3F can do as it's ultimately responsible for the update.
@CodyJasonBennett @evshiron I was able to get it working by wrapping the material with the map prop in a Suspense, I think it just needs time to load / apply to the material to the mesh
hmm, i don't think this is a bug ... @CodyJasonBennett
{clicked ? (
<meshStandardMaterial map={map} />
) : (
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
)}
is basically one and the same material in reacts eyes, it creates it once and then just exchanges props when the click condition changes. the culprit is a limitation in threejs, you cannot change a texture after the material has been created without setting needsUpdate, see https://threejs.org/docs/#manual/en/introduction/How-to-update-things
presence or not of
- texture
- ... Changes in these require building of new shader program. You'll need to set
material.needsUpdate = true
the solution @evshiron @ashleymvanduzer
useEffect(() => {
ref.current.material.needsUpdate = true
}, [clicked])
ps the reason why it works in @ashleymvanduzer's example is because now it's not the same node and react will mount and unmount it, which imo is wasteful. better retain the single material and just call needsupdate, which indeed is a side effect.
i would also write it like this from the get go:
<meshStandardMaterial map={clicked && map} color={clicked ? 'white' : hovered ? 'hotpink' : 'orange'} />
makes it much clearer that we're operating on a single material.
@drcmda nice, I was thinking that would work too, and thanks for the context above, that's actually super good to know
I'm afraid there's nothing R3F can do to help here regardless. I don't see the need of three.js for explicitly flagging materials either, they aren't containers and their state should already be cached. I can look into this upstream, but in the meantime you'll have to work around this gotcha like above.
@drcmda Thanks, the solution works for me. But I have a question, Why can't react-three-fiber automatically flag which material needs to be updated?
@IWSR Sorry to jump in here but If I understand your question correctly, it's because react three/fiber is just using three js primitives as JSX (via react's reconciler), and threejs requires requires this, so we need to handle the needsUpdate declaration the same way when using React, but with react we can allow mutability via a ref, which means we have direct access to the existing node and we don't have to tear down the dom (and then rebuild it) in order to see the update.
@ashleymvanduzer Thanks for your response and I apologize for my previous response with incorrect grammar. I was confused earlier because I mistakenly treated tags like \<mesh \/> as React components, which led me to wonder why three/fiber couldn't logically flag the material's updates within the component.
@IWSR No need to apologize! I can see how that would be an easy thing to mix up.
Greetings.
I am new to
react-three-fiber
, and I have spent hours to figure out why it didn't work but without luck.Here is the demo:
https://codesandbox.io/s/nice-hopper-353nsf?file=/src/App.js
My real scenario is to add an attached displacement map node to the standard material when it is ready. React did call the render function, but the scene didn't reflect the change even if I replaced the whole material. It works if I remove the condition, but will produce warnings every frame before it is ready.
Any help is appreciated. Thanks in advance.