Reapplying same texture on different Mesh changes the previous Mesh as well

Here is the Code : Repeat value issue - CodeSandbox

image

Here I am loading the same Gltf twice and applying the Texture on just the first gltf mesh with a repeat of 1 on X and Y by doing this

gltf.scene.traverse(function (child) {
        if (child.isMesh) {
          child.material.color = new THREE.Color(0xffffff);
          child.material.map = map;
          child.material.metalness = 0.001;
          child.material.roughness = 1;
          child.material.map.wrapS = THREE.RepeatWrapping;
          child.material.map.wrapT = THREE.RepeatWrapping;
          child.material.map.repeat.x = 1;
          child.material.map.repeat.y = 1;
          child.material.needsUpdate = true;
        }
      });

now I have a button at top when clicked apply same texture of second mesh(gltf2) with different Repeat value as

handleClick = () => {
    const Item = this.Item;
    const map2 = this.map;

    Item.scene.traverse(function (child) {
      if (child.isMesh) {
        child.material.color = new THREE.Color(0xffffff);
        child.material.map = map2;
        child.material.map.repeat.x = 6;
        child.material.map.repeat.y = 6;
        child.material.needsUpdate = true;
      }
    });
  };

But it’s effecting the first gltf mesh as well.

I am not sure what am I doing wrong. Am I missing something ?

You are referencing to the original map with:

child.material.map = map2;

So basically you just have one map and when you change it’s repeat, all meshes that use this map are effected.

You can use map2.clone() to get an independent instance of that map to apply different repeats, offsets, …

You need to set map.needsUpdate = true if you do so.

Thank you for replying.
Just a quick question, is there anyway to know if the same texture is already applied on some mesh in the scene ?

Well I am not an expert. It depends on your use case.

You could collect your used maps in an array and check if a map is in that array to know that it is in use.

Somethin like this:

let myUsedMaps = [map1, map2, map3];
let mapToCheckIfUsed = someMap;

if (myUsedMaps.indexOf(mapToCheckIfUsed > -1)){
   console.log("map already in use")
}

Another option would be traversing through your scene and check all maps. Like this:

let mapToCheckIfUsed = someMap;
scene.traverse(function(node){
   if (node.material && node.material.map === mapToCheckIfUsed){
      console.log("map already in use")
   }
})

but this may not be as performant in a huge scene.

1 Like

There are many ways to keep track of your resources. I think THREEjs is already caching textures and some other things, but unfortunately there is not much information about it. There is a THREE.Cache Class that can be enabled by setting THREE.Cache.enabled = true.

You can also use a Helper class, described in this tutorial Its using a ResourceTracker Class for disposing unused resources. You could extend/modify the example to also keep track where a resource is in use, similar to @Drumstructor 's example.

You could also make a LoadingManager Class, that keeps track of everything that was loaded and that is cloning resources instead of reloading them, or is using Instancing instead. You could also keep track of the usage here by referencing the used Obj/Mesh id.

Here are some more infos I found about caching: Performance: Caching Material - Learning Three.js
And instancing Instancing with three.js — Part 1 | by Dusan Bosnjak | Medium

1 Like