How to update a model's texture in react-babylonjs?

I want to render a 3D gltf model using react-babylonjs. On a button click event, I want to change the model’s texture.

I have tried the code below, but new texture is not geting applied to the model:
<Model
rotation={modelRotation} position={modelPosition}
rootUrl={${baseUrl}car/} sceneFilename=“lowpolyCar.gltf” }
scaling={modelScaling}>
<standardMaterial
albedoTexture={${baseUrl}car/textures/carTex1.png}
/>

I want to specifically update index 1 of the loaded meshes in the scene array.
Same model, and texture application works in babylonjs like this:
myMesh.loadedMeshes[1].albedoTexture = new BABYLON.Texture(“textureURL”, scene, true, false);

Pinging @brianzinn who owns that space :wink:

Firstly, the texture I haven’t tried assigning directly - definitely it won’t work to assign a URL.
You can you try this:

<standardMaterial>
   <texture assignTo="albedoTexture" url={'${baseUrl}car/textures/carTex1.png'} />
</standardMaterial>

What you will find though is that it will assign it to the root mesh, which is a mesh that is not part of your original model and the texture assignment will not be visible. The root mesh is added by the <Model /> and allows you to scale and position the model as you have done (even though a model can have multiple meshes). The onModelLoaded is the handler you would need to assign the textures imperatively as you have done. I have been working on a new declarative way that looks like this:

import { useLoader } from 'react-babylonjs';
// this hook doesn't exist right now!
const [model] = useLoader(rootUrl, sceneFilename);

<mesh assignFrom={model.meshes[0]}>
    <standardMaterial>
       <texture assignTo="albedoTexture" url={'${baseUrl}car/textures/carTex1.png'} /> 
     </standardMaterial>
</mesh>

You may be able to do it right now though using assignTo - I can probably do that easily by adding the loaded model somewhere if not available. The assign to will walk the object heirarchy and assign dynamically. For example:

<texture assignTo="meshes[0].albedoTexture" ... />

I think there are going to be a lot of edge cases and issues with a declarative design - as here for example, it would remove the existing materials and textures.

I should be able to get the last example working for you by adding a property. I have a releases going out soon that fixes a bunch of github issues and can get that out quickly. Are you able to share a repo?

Shared the repo on personal message.

1 Like

You will need to wait for next NPM - this works and clicking your colour buttons changes the albedoTexture on your car :slight_smile: I was missing a couple of changes:

  1. Texture to be able to assign to model (was only able to assign directly to materials before)
  2. Update ‘assignTo’ to work with arrays, so we can do meshes[0].material.albedoTexture.
import React, { useState } from 'react';
import { Engine, Scene } from 'react-babylonjs'
import { Vector3 } from '@babylonjs/core';

// DefaultPlayground from your repo - changed it around a bit
export default (props) => {
    const [modelState, setModelState] = useState(null);
    return (<div style={{ height: '100%', width: '100%' }}>
        <Engine antialias adaptToDeviceRatio canvasId='babylonJS' style={{ height: '100%', width: '100%' }} >
            <Scene canvasId="scene_1" style={{ height: '100%', width: '100%' }} >
                <arcRotateCamera name="arc" target={new Vector3(0, 1, 0)} minZ={1} alpha={(-Math.PI / 2) + 0.5 } beta={(0.5+ (Math.PI / 4))} radius={10} lowerBetaLimit={(Math.PI / 2) - 1.5} upperBetaLimit={4}
                    lowerRadiusLimit={1.84} />
                <hemisphericLight name="light1" intensity={0.7} direction={Vector3.Up()} />
                <model
                    rotation={props.modelRotation} position={props.modelPosition}
                    rootUrl={`assets/car/`} sceneFilename={props.modelUrl}
                    onModelLoaded={model => setModelState(model)}
                    scaling={props.modelScaling}>
                    {modelState &&
                        <texture key={props.textureUrl} name={props.textureUrl} assignTo={'meshes[1].material.albedoTexture'} url={`assets/${props.textureUrl}`} />
                    }
                </model>
            </Scene>
        </Engine>
    </div>)
};

Your main app looks then like this:

<div className="App">
        <div style={{ height: '75%', width: '50%', padding: '5px' }}>
          <DefaultPlayground
            modelUrl='lowpolyCar.gltf'
            modelRotation={modelRotation}
            modelScaling={modelScaling}
            modelPosition={modelPosition}
            textureUrl={this.state.texturePath}
          />
        </div>
        <ColorPanel1 changeColor={this.changeColor} />
</div>

Cool idea what you are building! Cheers.

I published a beta NPM yesterday, if you want to try it out.

Hi @brianzinn,

I have tried the npm package version: 2.2.1-beta.2

It’s giving me an error:
./node_modules/react-babylonjs/dist/react-babylonjs.js
Module not found: Can’t resolve ‘@babylonjs/core/Materials/Node/nodeMaterial’ in node_modules\react-babylonjs\dist’

So, is the error related to the npm package or something else?

I hope not. I did change “type”:“module” in package.json, but I also tested it out on another CRA project. Is your repository up to date with a repro? I can check that if so. Otherwise also there is a 2.2.1 out that hopefully has no issues :slight_smile: