# How to change texture of FBX model dynamically

**URL:** <https://discourse.threejs.org/t/how-to-change-texture-of-fbx-model-dynamically/41174>\
**Category:** Questions\
**Tags:** materials, textures\
**Created:** [August 7, 2022, 5:58pm UTC](https://discourse.threejs.org/t/how-to-change-texture-of-fbx-model-dynamically/41174 "2022-08-07T17:58:41Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![gfilkov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/gfilkov/32/25559_2.png) [@gfilkov](https://discourse.threejs.org/u/gfilkov)\
**Post date:** [August 7, 2022, 5:58pm UTC](https://discourse.threejs.org/t/how-to-change-texture-of-fbx-model-dynamically/41174/1 "2022-08-07T17:58:41Z")

</div>

My code:  
loads FBX,  
loads alternative texture (texture2),  
clones FBX 3D object (to object\_clone),  
clones material and material’s map of object\_clone,  
assigns map\_clone = texture2.  
Result: both object and object\_clone wear the same textures: texture2  
Why? How to fix that?  
Thanks

---

<div class="post-metadata">

**Author:** ![MejdiKazdarlli](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mejdikazdarlli/32/47891_2.png) [@MejdiKazdarlli](https://discourse.threejs.org/u/MejdiKazdarlli)\
**Post date:** [August 8, 2022, 12:58am UTC](https://discourse.threejs.org/t/how-to-change-texture-of-fbx-model-dynamically/41174/2 "2022-08-08T00:58:27Z")

</div>

Thé cloned material must be updated  
Set needupdate to true for the cloned material

[https://threejs.org/docs/#api/en/materials/Material.needsUpdate](https://threejs.org/docs/#api/en/materials/Material.needsUpdate)

---

<div class="post-metadata">

**Author:** ![gfilkov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/gfilkov/32/25559_2.png) [@gfilkov](https://discourse.threejs.org/u/gfilkov)\
**Post date:** [August 8, 2022, 4:17am UTC](https://discourse.threejs.org/t/how-to-change-texture-of-fbx-model-dynamically/41174/3 "2022-08-08T04:17:39Z")

</div>

Thank you for the prompt response.  
1.  
headMesh2.material[0].needsUpdate = true;  
headMesh1.material[0].needsUpdate = true;

- did not do any change.

1. 

Why both headMesh1.material[0] (original) and headMesh2.material[0] (clone) are modified?  
I have only assigned alternative texture to cloned material map:  
headMesh2.material[0].map = newTexture;

I expected (I am trying to) modify only clone of the original object.  
But they both get modified the same way.

Appreciate your advice.

---

<div class="post-metadata">

**Author:** ![MejdiKazdarlli](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mejdikazdarlli/32/47891_2.png) [@MejdiKazdarlli](https://discourse.threejs.org/u/MejdiKazdarlli)\
**Post date:** [August 8, 2022, 7:03am UTC](https://discourse.threejs.org/t/how-to-change-texture-of-fbx-model-dynamically/41174/4 "2022-08-08T07:03:24Z")

</div>

Can you share your code

---

<div class="post-metadata">

**Author:** ![gfilkov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/gfilkov/32/25559_2.png) [@gfilkov](https://discourse.threejs.org/u/gfilkov)\
**Post date:** [August 8, 2022, 4:12pm UTC](https://discourse.threejs.org/t/how-to-change-texture-of-fbx-model-dynamically/41174/5 "2022-08-08T16:12:12Z")

</div>

Sure. Just let know what is the best way to share.  
Trying to paste the whole JS in here:

* * *

let camera, scene, renderer, light, controls, fbxLoader, mixer;  
let directionalLight;  
let modelReady = false;  
let textureReady = false;  
let newTexture;  
let animationAction;

let marker; // short vertical line to mark step length

let player1, player2;  
const animGroup = new THREE.AnimationObjectGroup();

// gui.dat parameters  
const options = {  
continuous: false,  
speed: 1,  
step: 1.65, // optimal! No slips  
cameraX: 0.8,  
cameraY: 1.4,  
cameraZ: 1.0  
}

function init() {  
// Init scene  
scene = new THREE.Scene();  
scene.add(new THREE.AxesHelper(20));

```
light = new THREE.PointLight();
light.position.set(2.5, 7.5, -15);
scene.add(light);

// White directional light at half intensity shining from the top.
directionalLight = new THREE.DirectionalLight( 0xffffff); // , 0.5 );
directionalLight.position.set(10, 10, 10);
scene.add( directionalLight );

const ambientLight = new THREE.AmbientLight(0x333333);
scene.add(ambientLight);

// Init camera (PerspectiveCamera)
camera = new THREE.PerspectiveCamera(
	75,
	window.innerWidth / window.innerHeight,
	0.1,
	1000
);

camera.position.set(options.cameraX, options.cameraY, options.cameraZ);

// Init renderer
renderer = new THREE.WebGLRenderer({ antialias: true });

// Set size (whole window)
renderer.setSize(window.innerWidth, window.innerHeight);

// blach -> transparent
renderer.setClearColor( 0xffffff, 0);

// Render to canvas element
document.body.appendChild(renderer.domElement);

controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.target.set(0, 1, 0);

fbxLoader = new THREE.FBXLoader();
// NB: 404 - FBX not found!? Add FBX mime type in web.config!

fbxLoader.load(
    'models/swat-walking.fbx',
    (object) => {
        object.scale.set(0.01, 0.01, 0.01);
        clips = object.animations;

        modelReady = true;
        player1 = object;
		
		directionalLight.target = player1;
		
		tryClonePlayer();	// will check modelReady and textureReady flags.

        mixer = new THREE.AnimationMixer(animGroup);
        const clip = THREE.AnimationClip.findByName(clips, 'mixamo.com');

        const action = mixer.clipAction(clip);
        action.play();

        generateFloor();
        addMixerListeners();
        setMarker();
    },
    (xhr) => {
        console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
    },
    (error) => {
        console.log(error);
    }
);

// instantiate a loader
const loader = new THREE.TextureLoader();

// load a resource
loader.load(
	// resource URL
	'textures/Soldier_Body_diffuse_w.png',

	// onLoad callback
	function ( texture ) {
		// in this example we create the material when the texture is loaded
		//const material = new THREE.MeshBasicMaterial( {
		//	map: texture
		// } );

		textureReady = true;
		texture.name = "white_helmet";
		newTexture = texture;
		
		tryClonePlayer();	// will check modelReady and textureReady flags.
	},

	// onProgress callback currently not supported
	undefined,

	// onError callback
	function ( err ) {
		console.error( 'An error happened.' );
	}
);

function tryClonePlayer() {
	// will check modelReady and textureReady flags
	if(!modelReady)
		return;
	
	if(!textureReady)
		return;

	// clone player and add to scene
	player2 = THREE.SkeletonUtils.clone( player1 );

	player1.name = "player1";
	player2.name = "player2";
	
	let geoObject1 = search("Geo", player1.children);
	let headMesh1 = search("Soldier_head", geoObject1.children);

	let geoObject2 = search("Geo", player2.children).clone();
	let headMesh2 = search("Soldier_head", geoObject2.children).clone();
	geoObject2.name = geoObject2.name + "_cloned";
	headMesh2.name = headMesh2.name + "_cloned";
    headMesh2.parent = geoObject2; // why it is null?

	// headMesh has 2 materials: "Soldier_body1" and "Soldier_head6"
	// helmet is part of "Soldier_body1" material

	headMesh2.material[0] = headMesh2.material[0].clone();
	headMesh2.material[0].map = headMesh2.material[0].map.clone();

```

headMesh2.material[0].map = newTexture;

```
	headMesh2.material[0].name = headMesh2.material[0].name + "_cloned";
	headMesh2.material[0].map.name = headMesh2.material[0].map.name + "_cloned";
    headMesh2.material[0].needsUpdate = true;
    headMesh1.material[0].needsUpdate = true;

	player2.position.x = -1;
	animGroup.add(player1);
	animGroup.add(player2);
	
	scene.add( player1, player2 );
}

```

}

// change texture:  
// mesh.material.map = THREE.ImageUtils.loadTexture( src );  
// mesh.material.needsUpdate = true;

search = (key, inputArray) =\> {  
for (let i=0; i \< inputArray.length; i++) {  
if (inputArray[i].name === key) {  
return inputArray[i];  
}  
}  
}

function initGui() {  
var gui = new dat.GUI(); // for dat.gui CDN  
// new GUI(); // for dat.gui module

```
/*
// checkbox
gui.add(options, "continuous").onChange((val) => {
    // controls.enabled = !val;
    console.log("continuous", val);
});

// slider
gui.add(options, "speed", 0, 5, 0.1).onChange((val) => {
    animationAction.timeScale = val;
    console.log("speed", val);
});
gui.add(options, "step", 0, 5, 0.1).onChange((val) => {
    console.log("step", val);
});

```

\*/

```
const cameraFolder = gui.addFolder('Camera');
const playerFolder = gui.addFolder('Player');

cameraFolder.add(options, "cameraX", -10, 10, 0.1).onChange((val) => {
    camera.position.x = options.cameraX;
});
cameraFolder.add(options, "cameraY", -10, 10, 0.1).onChange((val) => {
    camera.position.y = options.cameraY;
});
cameraFolder.add(options, "cameraZ", -10, 10, 0.1).onChange((val) => {
    camera.position.z = options.cameraZ;
});

// checkbox
playerFolder.add(options, "continuous").onChange((val) => {
    // controls.enabled = !val;
    //console.log("continuous", val);
    if(!val) {
        // put player back
        // player.position = new THREE.Vector3(0, 0, 0); // no effect 1?
        player.translateZ(-player.position.z);
    }
});

// slider
playerFolder.add(options, "speed", 0, 5, 0.01).onChange((val) => {
    animationAction.timeScale = val;
    //console.log("speed", val);
});
playerFolder.add(options, "step", 0, 5, 0.01).onChange((val) => {
    marker.position.z = val;
    //console.log("step", val);
});

cameraFolder.open();
playerFolder.open();

```

}

//generateFloor(); // no scene yet!?

window.addEventListener(‘resize’, onWindowResize, false);  
function onWindowResize() {  
camera.aspect = window.innerWidth / window.innerHeight;  
camera.updateProjectionMatrix();  
renderer.setSize(window.innerWidth, window.innerHeight);  
render();  
}

function addMixerListeners() {  
// properties of e: type, action and loopDelta  
mixer.addEventListener( ‘loop’, function( e ) {  
// console.log(‘loop’);

```
    // move player one step ahead
    if(options.continuous) {
        let dbg = 0;
        // check animationAction, mixer
        player.translateZ(options.step);

        // if over floor edge, put back
        if(player.position.z > 24) {
            player.translateZ(-48);
        }
    }

} );

// properties of e: type, action and direction
// mixer.addEventListener( 'finished', function( e ) {
// console.log('finished');
// } );

```

}

function generateFloor() {  
// TEXTURE  
const textureLoader = new THREE.TextureLoader();  
const placeholder = textureLoader.load(“./textures/placeholder/placeholder.png”, function ( texture ) {

```
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
    texture.offset.set( 0, 0 );
    texture.repeat.set( 24, 24 );

} );

const WIDTH = 80;
const LENGTH = 80;

const geometry = new THREE.PlaneGeometry(WIDTH, LENGTH, 512, 512);
const material = new THREE.MeshPhongMaterial({ map: placeholder});

const floor = new THREE.Mesh(geometry, material);
floor.receiveShadow = true;
floor.rotation.x = - Math.PI / 2;
scene.add(floor);

```

}

// mark one step length along 0Z with blue line  
function setMarker() {  
//create a blue LineBasicMaterial  
const markerMat = new THREE.LineBasicMaterial( { color: 0x0000ff } );  
const points = ;  
points.push( new THREE.Vector3( 0, 0, options.step ) );  
points.push( new THREE.Vector3( 0, 0.1, options.step ) );  
const markerGeo = new THREE.BufferGeometry().setFromPoints( points );  
marker = new THREE.Line( markerGeo, markerMat ); // marker line  
scene.add( marker );  
}

onError = function (e) {  
console.error(e);  
};

const clock = new THREE.Clock();

function animate() {  
requestAnimationFrame(animate);

```
controls.update();

if (modelReady) mixer.update(clock.getDelta());

render();

```

}

function render() {  
renderer.render(scene, camera);  
}

init();  
initGui();

animate();
