How to change texture of FBX model dynamically

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

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

https://threejs.org/docs/#api/en/materials/Material.needsUpdate

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

  • did not do any change.

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.

Can you share your code

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();