react-babylonjs icon indicating copy to clipboard operation
react-babylonjs copied to clipboard

Changing texture url prop does not change the image used in texture

Open nene opened this issue 11 months ago • 7 comments

Created a playground example with a toggle button that switches between two URL-s:

https://brianzinn.github.io/react-babylonjs/playground?snippetId=CTqBHrbd

But the image used by the texture is not updating.

This possibly happens because setting the url prop of a Texture does nothing. Instead one needs to call updateURL().

When I update the playground example with a code that calls updateURL() inside an effect, it works:

function MyMaterial({url}: {url: string}) {
  const ref = useRef<StandardMaterial>(null);

  useEffect(() => {
    if (ref.current) {
      ref.current.diffuseTexture.updateURL(url);
    }
  }, [url]);

  return (
    <standardMaterial ref={ref} name="mat">
      <texture url={url} />
    </standardMaterial>
  )
}

PS. I tried copying the whole code from playground to here as well, but for some reason the copy functionality doesn't quite work there.

nene avatar Jan 21 '25 12:01 nene

it's kind of a known issue. it likely is a bug on the one hand. this will work:

<standardMaterial ...>
   <texture key={url} url={url} />
</standardMaterial>

The "bug" is that the updateUrl isn't mapped properly - it's not a known property, since it's a function and those aren't usually mapped. I think only observables are by default. The "feature" might be to detect constructor properties that aren't properties of an object that are updated after instantiation to create a new instance. That might be confusing for some people, especially when we have in some instances like what you have another way to update the url.

I did at one point a bunch of investigation on the mesh shapes like 'box', 'sphere', 'ground', etc. That if the construction/builder props were updated that it would update the underlying mesh. It started to get a bit involved and I was wondering where to make the distinction between a lighter Renderer vs. a full framework with knowledge of all the components. I can detect which properties are constructor only through the code generation, but haven't made any decisions to use it yet.

brianzinn avatar Jan 21 '25 17:01 brianzinn

I wanted to add as well - what a declarative solution would look like. So, the updateUrl would be supplied when the texture is created, but then altered as a prop? There would need to be some convention for props that would not call the underlying function as well. Not sure what that would look like...

brianzinn avatar Jan 22 '25 00:01 brianzinn

As a user I really would just expect to be able to write <texture url={dynamicUrl} />. Having to write <texture updateUrl={dynamicUrl} /> would be really confusing. Though I might be misunderstanding what you're saying.

One thought I have is that mutating a prop that is BabylonJS constructor parameter and doesn't have a setter could just trigger recreation of the whole BabylonJS object.

nene avatar Jan 22 '25 08:01 nene

I agree with everything you said. url isn't a property that seems to do anything, but I would expect that as well: https://github.com/BabylonJS/Babylon.js/blob/master/packages/dev/core/src/Materials/Textures/texture.ts#L211

whereas the function actually does something: https://github.com/BabylonJS/Babylon.js/blob/master/packages/dev/core/src/Materials/Textures/texture.ts#L575

I am trying to balance between writing custom code for every object in Babylon.js with escape hatches. Here, for example, I would even think the real change belongs upstream...

edit: so, despite this "constructor" arg having a matching url property, even if I coded it to create a new Texture you would actually not do so here, since an instance property of the same name exists! 😄 i think url should have a get() that calls updateUrl() in the main library when it changes, but it would need a backed prop.

brianzinn avatar Jan 22 '25 21:01 brianzinn

I really appreciate you creating these issues. It's useful to reconsider some design decisions, especially ones that make the library harder to use than it needs to be. Hopefully I have some time in ~10 days to address a couple of them.

brianzinn avatar Jan 23 '25 02:01 brianzinn

Hey so I'm currently looking at the example on https://brianzinn.github.io/react-babylonjs/examples/textures/image-textures and the <texture> tag here doesn't have an url in it, but the current version requires an url attribute to be given. What should I put in the url attribute here?

I hope you could help me on this!

trietng avatar Feb 28 '25 14:02 trietng

hi @trietng - that's because it's using fromInstance, which means that instead of asking react-babylonjs to instantiate a Texture class for you that you will instead be supplying your own instance. That example was done to load the textures with a loading screen, so there's no glitch pre-load on the meshes.

There are only a small number of custom properties that deviate from the babylon.js API - that is one of them: https://brianzinn.github.io/react-babylonjs/examples/basic/from-instance

Behind the scenes the "typings" are such that you either need fromInstance or the required constructor args. So, that's why current (and going pretty far back) require an url attribute. You can just put a path or link link the "rootUrl" here - I don't think I have a <texture ..> example anymore: https://brianzinn.github.io/react-babylonjs/examples/textures/pbr-configuration

if that doesn't solve it for you, please open a separate issue. thx.

brianzinn avatar Feb 28 '25 17:02 brianzinn