.obj will randomly inherit multiple textures from other objects

I have three.js rendering a simple combo of objects, each with its own texture. It usually works fine, however, about 50% of the time, one of the objects will get it’s assigned texture, as well as a texture from the other object.

This happens on all devices, for all users. I know that the script is receiving all the right information, nothing changes on the script when the unexpected behavior happens.

Expected result:
image

Unexpected result, where it gets other object’s texture as well:
image
(Please keep in mind that this happens about 50% with no changes to code)

Code:

var scene = new THREE.Scene();
//scene.background = new THREE.Color( 0x121212 );

var camera = new THREE.PerspectiveCamera( 60, 512/512, 0.1, 1000 );

const alight = new THREE.AmbientLight( 0x919191 ); // soft white light
scene.add( alight );
const plight = new THREE.PointLight( 0xf2f2f2, 1, 200 );
plight.position.set( 50, 50, 50 );
scene.add( plight );

var renderer = new THREE.WebGLRenderer({canvas: document.getElementById('avatarCanvas'), preserveDrawingBuffer: true, alpha: true, antialias: true});
renderer.setSize( 512, 512 );
renderer.setClearColor( 0x000000, 0 );

var objLoader = new THREE.OBJLoader();
objLoader.setPath('');

// ADD HEAD
objLoader.load( 
    "../../assets/head.obj",
    function (geometry) {
      geometry.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.material.color.setHex(0xffffff);
        }
      });
      geometry.position.y = 1.5;
      geometry.rotation.y = 3.14159;
      scene.add(geometry);
});
// ADD FACE
objLoader.load( 
    "../../assets/head.obj",
    function (geometry) {
      var textureLoader = new THREE.TextureLoader();
      var texture = textureLoader.load("../../assets/default_face.png");
      var material = new THREE.MeshLambertMaterial({
        transparent: true,
        map: texture
      });
      texture.minFilter = THREE.LinearFilter;
      texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
      geometry.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.material = material; 
        }
      });
      geometry.position.y = 1.5;
      geometry.rotation.y = 3.14159;
      geometry.scale.set(1.0001,1.0001,1.0001);
      scene.add(geometry);
});

// ADD TORSO
objLoader.load( 
    "../../assets/male_torso.obj",
    function (geometry) {
      geometry.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.material.color.setHex(0xffffff);
        }
      });
      geometry.rotation.y = 3.14159;
      scene.add(geometry);
});

// ADD LEFT ARM
objLoader.load( 
    "../../assets/left_arm.obj",
    function (geometry) {
      geometry.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.material.color.setHex(0xffffff);
        }
      });
      geometry.position.x = 1.5;
      geometry.rotation.y = 3.14159;
      scene.add(geometry);
});

// ADD RIGHT ARM ARM
objLoader.load( 
    "../../assets/right_arm.obj",
    function (geometry) {
      geometry.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.material.color.setHex(0xffffff);
        }
      });
      geometry.position.x = -1.5;
      geometry.rotation.y = 3.14159;
      scene.add(geometry);
});

// ADD LEFT LEG
objLoader.load( 
    "../../assets/left_leg.obj",
    function (geometry) {
      geometry.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.material.color.setHex(0xffffff);
        }
      });
      geometry.position.y = -2;
      geometry.position.x = 0.5;
      geometry.rotation.y = 3.14159;
      scene.add(geometry);
});

// ADD RIGHT LEG
objLoader.load( 
    "../../assets/right_leg.obj",
    function (geometry) {
      geometry.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.material.color.setHex(0xffffff);
        }
      });
      geometry.position.y = -2;
      geometry.position.x = -0.5;
      geometry.rotation.y = 3.14159;
      scene.add(geometry);
});

// ADD HAT 1
objLoader.load( 
    "../../assets/items/1.obj",
    function (geometry) {
      var textureLoader = new THREE.TextureLoader();
      var texture = textureLoader.load("../../assets/items/1.png");
      var material = new THREE.MeshLambertMaterial({
        transparent: true,
        map: texture
      });
      texture.minFilter = THREE.LinearFilter;
      texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
      geometry.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.material = material; 
        }
      });
      geometry.position.y = 1.5;
      geometry.rotation.y = 3.14159;
      scene.add(geometry);
});

THREE.DefaultLoadingManager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
    render();
};

camera.position.x = -2; camera.position.y = 2; camera.position.z = 6;
camera.lookAt(new THREE.Vector3(0,-0.3,0));
camera.zoom = 1.1;
camera.updateProjectionMatrix();

function render() {
	//requestAnimationFrame( render );
	renderer.render(scene, camera);
    // get result as image after a bit
    setTimeout(function(){
    capture();
    }, 5000);
    
    //renderer.clear();
};

function capture(){
		  var canvas = document.getElementById("avatarCanvas")
		  potatoeDataImg = canvas.toDataURL("image/png");
			dataImg = potatoeDataImg.substr(22);
		  document.getElementById('dataSend').value = dataImg; 
           document.forms[0].submit();
		//$("#main").ajaxSubmit({url: 'savei.php', type: 'post'})
}
</script>
</body>

Just curious: why do you load head.obj twice – once as a head and once as a face?

// ADD HEAD
objLoader.load( 
    "../../assets/head.obj",

// ADD FACE
objLoader.load( 
    "../../assets/head.obj",

@PavelBoytchev

After searching for a while the only solution for simply putting the face image on, while still having the head whichever color the user chooses, is to have 1 head for color, and another that is transparent just for the face image

I see. That’s why it is slightly bigger.

Your code is not shared in some online system like CodePen, JSFiddle, CodeSandBox, so it is impossible for me to debug the issue of the OBJ models and the textures. Maybe some of the more experienced people here could give a nice advice by looking at the code.

If I were you, I will try the following things:

  • I will try a separate loader for each object. Now you use one loader, and I do not know what happens with a loader that is not finished, and a new load is requested. If each OBJ file is loaded by its own loader, this should eliminate interfering between them. As the problem appears randomly, it might be related with how fast OBJ files are loaded.
  • If the problem persists, I’d make a copy of the project, and then I will start to remove parts. For example, if I remove the arms and the problem still appears, then the arms are not a problem. Then I’ll remove other parts and keep removing things until I get the minimal code that makes the problem. This might help to find the problem by trial and error.

@PavelBoytchev

I will say that it worked for about 5 weeks, only yesterday it started doing this with no changes made to the code in that entire time