1

This follows on from my previous question here. I'm just trying to apply a different texture to each side of this obj but nothing appears. There are no console errors and I believe I'm applying things in the right order.

This should be quite simple to do but I've been struggling with it for the past hour. Here's a code sample below:

(function onLoad() {
  var canvasElement;
  var width, height;
  var scene, camera;
  var renderer;
  var controls;

  var pivot;
  var bagMesh;
  var planeMesh;
  
  const objLoader = new THREE.OBJLoader2();
  const fileLoader = new THREE.FileLoader();
  const textureLoader = new THREE.TextureLoader();
 
  init();

  function init() {
    container = document.getElementById('container');
    initScene();
    addGridHelper();
    addCamera();
    addLighting();
    addRenderer();
    addOrbitControls();

    loadPlaneObj();
    
    // Logic
  var update = function() {};

  // Draw scene
  var render = function() {
   renderer.render(scene, camera);
  };

  // Run game logic (update, render, repeat)
  var gameLoop = function() {
   requestAnimationFrame(gameLoop);
   update();
   render();
  };
  gameLoop();
  }

  /**** Basic Scene Setup ****/
  function initScene() {
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xd3d3d3);
    var axis = new THREE.AxesHelper();
    scene.add(axis);
  }

  function addCamera() {
    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.set(3,3,3);
    scene.add(camera);
  }

  function addGridHelper() {
    var planeGeometry = new THREE.PlaneGeometry(2000, 2000);
    planeGeometry.rotateX(-Math.PI / 2);

    var planeMaterial = new THREE.ShadowMaterial({
      opacity: 0.2
    });
    var plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.position.y = -200;
    plane.receiveShadow = true;
    scene.add(plane);

    var helper = new THREE.GridHelper(2000, 100);
    helper.material.opacity = 0.25;
    helper.material.transparent = true;
    scene.add(helper);

    var axis = new THREE.AxesHelper();
    scene.add(axis);
  }

  // *********** Lighting settings **********************
  function addLighting() {
    var light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
    scene.add(light);
  }

  // ************** Material settings **************
  function setMaterial(materialName) {
    // get the object from the scene
    var bagMesh = scene.getObjectByName('bag');
    var material;

    if (!materialName) {
      materialName = materials.material;
    }

    if (bagMesh) {
      var colour = parseInt(materials.colour);
      switch (materialName) {
        case 'MeshBasicMaterial':
          material = new THREE.MeshBasicMaterial({
            color: colour
          });
          break;
        case 'MeshDepthMaterial':
          material = new THREE.MeshDepthMaterial();
          break;
        case 'MeshLambertMaterial':
          material = new THREE.MeshLambertMaterial({
            color: colour
          });
          break;
        case 'MeshNormalMaterial':
          material = new THREE.MeshNormalMaterial();
          break;
        case 'MeshPhongMaterial':
          material = new THREE.MeshPhongMaterial({
            color: colour
          });
          break;
        case 'MeshPhysicalMaterial':
          material = new THREE.MeshPhysicalMaterial({
            color: colour
          });
          break;
        case 'MeshStandardMaterial':
          material = new THREE.MeshStandardMaterial({
            color: colour
          });
          break;
        case 'MeshToonMaterial':
          material = new THREE.MeshToonMaterial({
            color: colour
          });
          break;
      }
      bagMesh.children.forEach(function(c) {
        c.material = material;
      });
    }
  }

  function setMaterialColour(colour) {
    materials.colour = colour;
    setMaterial(null);
  }
  // ************** End of materials ***************

  function addRenderer() {
    renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    container.appendChild(renderer.domElement);
  }

  function addOrbitControls() {
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
  }

  function addPivot() {
    var cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5);
    var cubeMat = new THREE.MeshBasicMaterial();
    pivot = new THREE.Mesh(cubeGeo, cubeMat);
    bagMesh.position.x -= 15;
    bagMesh.position.z -= 55;

    pivot.add(bagMesh);
    pivot.add(handle);
    scene.add(pivot);
  }
  
  function loadPlaneObj() {
  loadObjWithMtl('PlaneWithMaterial', 
      'https://rawgit.com/Katana24/threejs-experimentation/material-test/models/PlaneWithMaterial.obj', 
      'https://rawgit.com/Katana24/threejs-experimentation/material-test/models/PlaneWithMaterial.mtl')
   .then(function(mesh) {
        loadTexture(
          'https://rawgit.com/Katana24/threejs-experimentation/material-test/img/1.jpg', scene, 0xff0000, THREE.FrontSide, mesh);
   });
 }
  
  function loadObjWithMtl(modelName, modelUrl, mtlUrl) {
  return new Promise(function(resolve, reject) {
   var callbackOnLoad = function ( event ) { resolve(event.detail.loaderRootNode); };
   var onLoadMtl = function ( materials ) {
    objLoader.setModelName( modelName );
    objLoader.setMaterials( materials );
    objLoader.getLogger().setDebug( true );
    objLoader.load( modelUrl, callbackOnLoad, null, null, null, false );
   };
   objLoader.loadMtl(mtlUrl, null, onLoadMtl );
  });
 }

 function loadTexture(imgPath, scene, color, side, mesh) {
  var loadedMesh = mesh;
   textureLoader.load(imgPath,
   function (texture) { 
        var geometry = new THREE.Geometry().fromBufferGeometry( loadedMesh.children[0].geometry );
    mesh.children[0].material.map = texture;
    mesh.children[0].material.needsUpdate = true;
    scene.add(mesh);
   },
   undefined,
   function ( err ) {
    console.error( 'An error occurred...' );
   }
  );
 }
  
  function addPlaneToSceneSOAnswer(mesh) {
  var frontMaterial = new THREE.MeshBasicMaterial( { color : 0xff0000, side: THREE.FrontSide } );
  var backMaterial  = new THREE.MeshBasicMaterial( { color : 0x00ff00, side: THREE.BackSide } );
  
  var geometry = new THREE.Geometry().fromBufferGeometry( mesh.children[0].geometry );
  var length = geometry.faces.length;
  geometry.faces.splice(14, 1);

  for (var i = 0; i < geometry.faces.length; i ++ ) {
   var face = geometry.faces[i];
   face.color.setHex(Math.random() * 0xffffff);
  }
  mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors, side: THREE.DoubleSide }) );
  mesh.material.side = THREE.FrontSide; 
  
  var mesh2 = new THREE.Mesh( geometry, mesh.material.clone() ); 
  mesh2.material.side = THREE.BackSide; 
  // mesh2.material.vertexColors = THREE.NoColors; 
  mesh2.material.vertexColors = [new THREE.Color(0xff0000), new THREE.Color(0x00ff00), new THREE.Color(0x0000ff)];

  mesh.add( mesh2 );
  scene.add(mesh);
 }
})();
body {
  background: transparent;
  padding: 0;
  margin: 0;
  font-family: sans-serif;
}

#canvas {
  margin: 10px auto;
  width: 800px;
  height: 350px;
  margin-top: -44px;
}
<body>
  <div id="container"></div>
  <script src="https://threejs.org/build/three.js"></script>
  <script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
  <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  <script src="https://threejs.org/examples/js/loaders/MTLLoader.js"></script>
  <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/LoaderSupport.js"></script>
  <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/OBJLoader2.js"></script>
</body>

So what simple thing am I doing wrong here? I think the problem is in this function:

  function loadTexture(imgPath, scene, color, side, mesh) {
    var loadedMesh = mesh;
    textureLoader.load(imgPath,
      function (texture) {  
        var geometry = new THREE.Geometry().fromBufferGeometry( loadedMesh.children[0].geometry );
        mesh.children[0].material.map = texture;
        mesh.children[0].material.needsUpdate = true;
        scene.add(mesh);
      },
      undefined,
      function ( err ) {
        console.error( 'An error occurred...' );
      }
    );
  }

This function gets the mesh then loads the required texture and applies it to the material in the mesh. It then tells the material to update. Is this the correct approach?

This answer here actually only calls render once everything is set it but I want to be-able to change textures, materials etc at run time.

I thought that mesh.children[0].material.needsUpdate = true would signal the shader program to update...

Thanks

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Katana24
  • 8,706
  • 19
  • 76
  • 118

1 Answers1

1

The texture is loaded correctly, but it is not shown correctly, because the blender object does not provide any texture coordinates:

# Blender v2.77 (sub 0) OBJ File: 'Plane.blend'
# www.blender.org
mtllib PlaneWithMaterial.mtl
o Plane
v -1.000000 0.000000 1.000000
v 1.000000 0.000000 1.000000
v -1.000000 0.000000 -1.000000
v 1.000000 0.000000 -1.000000
vn 0.0000 1.0000 0.0000
usemtl Material
s off
f 1//1 2//1 4//1 3//1

Use the following object instead:

# Blender v2.77 (sub 0) OBJ File: 'Plane.blend'
# www.blender.org
mtllib PlaneWithMaterial.mtl
o Plane

v -1.000000 0.000000 1.000000
v 1.000000 0.000000 1.000000
v -1.000000 0.000000 -1.000000
v 1.000000 0.000000 -1.000000

vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000

vn 0.0000 1.0000 0.0000

usemtl Material
s off
f 1/1/1 2/2/1 4/4/1 3/3/1

See the code snippet:

(function onLoad() {
  var canvasElement;
  var width, height;
  var scene, camera;
  var renderer;
  var controls;

  var pivot;
  var bagMesh;
  var planeMesh;
  
  const objLoader = new THREE.OBJLoader2();
  const fileLoader = new THREE.FileLoader();
  const textureLoader = new THREE.TextureLoader();
  textureLoader.setCrossOrigin("");
 
  init();

  function init() {
    container = document.getElementById('container');
    initScene();
    addGridHelper();
    addCamera();
    addLighting();
    addRenderer();
    addOrbitControls();

    loadPlaneObj();
    
    // Logic
  var update = function() {};

  // Draw scene
  var render = function() {
   renderer.render(scene, camera);
  };
    
    function resize() {
      var aspect = window.innerWidth / window.innerHeight;
      renderer.setSize(window.innerWidth, window.innerHeight);
      camera.aspect = aspect;
      camera.updateProjectionMatrix();
    }
    window.onresize = resize;

  // Run game logic (update, render, repeat)
  var gameLoop = function() {
   requestAnimationFrame(gameLoop);
   update();
   render();
  };
  gameLoop();
  }

  /**** Basic Scene Setup ****/
  function initScene() {
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xd3d3d3);
    var axis = new THREE.AxesHelper();
    scene.add(axis);
  }

  function addCamera() {
    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.set(1,1,1);
    scene.add(camera);
  }

  function addGridHelper() {
    var planeGeometry = new THREE.PlaneGeometry(2000, 2000);
    planeGeometry.rotateX(-Math.PI / 2);

    var planeMaterial = new THREE.ShadowMaterial({
      opacity: 0.2
    });
    var plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.position.y = -200;
    plane.receiveShadow = true;
    scene.add(plane);

    var helper = new THREE.GridHelper(2000, 100);
    helper.material.opacity = 0.25;
    helper.material.transparent = true;
    scene.add(helper);

    var axis = new THREE.AxesHelper();
    scene.add(axis);
  }

  // *********** Lighting settings **********************
  function addLighting() {
    var light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
    scene.add(light);
  }

  // ************** Material settings **************
  function setMaterial(materialName) {
    // get the object from the scene
    var bagMesh = scene.getObjectByName('bag');
    var material;

    if (!materialName) {
      materialName = materials.material;
    }

    if (bagMesh) {
      var colour = parseInt(materials.colour);
      switch (materialName) {
        case 'MeshBasicMaterial':
          material = new THREE.MeshBasicMaterial({
            color: colour
          });
          break;
        case 'MeshDepthMaterial':
          material = new THREE.MeshDepthMaterial();
          break;
        case 'MeshLambertMaterial':
          material = new THREE.MeshLambertMaterial({
            color: colour
          });
          break;
        case 'MeshNormalMaterial':
          material = new THREE.MeshNormalMaterial();
          break;
        case 'MeshPhongMaterial':
          material = new THREE.MeshPhongMaterial({
            color: colour
          });
          break;
        case 'MeshPhysicalMaterial':
          material = new THREE.MeshPhysicalMaterial({
            color: colour
          });
          break;
        case 'MeshStandardMaterial':
          material = new THREE.MeshStandardMaterial({
            color: colour
          });
          break;
        case 'MeshToonMaterial':
          material = new THREE.MeshToonMaterial({
            color: colour
          });
          break;
      }
      bagMesh.children.forEach(function(c) {
        c.material = material;
      });
    }
  }

  function setMaterialColour(colour) {
    materials.colour = colour;
    setMaterial(null);
  }
  // ************** End of materials ***************

  function addRenderer() {
    renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    container.appendChild(renderer.domElement);
  }

  function addOrbitControls() {
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
  }

  function addPivot() {
    var cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5);
    var cubeMat = new THREE.MeshBasicMaterial();
    pivot = new THREE.Mesh(cubeGeo, cubeMat);
    bagMesh.position.x -= 15;
    bagMesh.position.z -= 55;

    pivot.add(bagMesh);
    pivot.add(handle);
    scene.add(pivot);
  }
  
  function loadPlaneObj() {
    makeTextFile = function (text) {
      var data = new Blob([text], {type: 'text/plain'});
      var textFile = window.URL.createObjectURL(data);
      return textFile;   
    }
    var textbox_obj = document.getElementById('plane_obj');
    var obj_url = makeTextFile(textbox_obj.value);
    var textbox_mtl = document.getElementById('plane_mtl');
    var mtl_url = makeTextFile(textbox_mtl.value);

    loadObjWithMtl('plane1', 
      obj_url, mtl_url)
   .then(function(mesh) {
        loadTexture(
         'https://rawgit.com/Katana24/threejs-experimentation/material-test/img/1.jpg',
         scene, 0xff0000, THREE.FrontSide, mesh);
   });
 }
  
  function loadObjWithMtl(modelName, modelUrl, mtlUrl) {
  return new Promise(function(resolve, reject) {
   var callbackOnLoad = function ( event ) { resolve(event.detail.loaderRootNode); };
   var onLoadMtl = function ( materials ) {
    objLoader.setModelName( modelName );
    objLoader.setMaterials( materials );
    objLoader.getLogger().setDebug( true );
    objLoader.load( modelUrl, callbackOnLoad, null, null, null, false );
   };
   objLoader.loadMtl(mtlUrl, null, onLoadMtl );
  });
 }

 function loadTexture(imgPath, scene, color, side, mesh) {
  var loadedMesh = mesh;
   textureLoader.load(imgPath,
   function (texture) { 
        var geometry = new THREE.Geometry().fromBufferGeometry( loadedMesh.children[0].geometry );
    mesh.children[0].material.map = texture;
    mesh.children[0].material.needsUpdate = true;
    scene.add(mesh);
   },
   undefined,
   function ( err ) {
    console.error( 'An error occurred...' );
   }
  );
 }
  
  function addPlaneToSceneSOAnswer(mesh) {
  var frontMaterial = new THREE.MeshBasicMaterial( { color : 0xff0000, side: THREE.FrontSide } );
  var backMaterial  = new THREE.MeshBasicMaterial( { color : 0x00ff00, side: THREE.BackSide } );
  
  var geometry = new THREE.Geometry().fromBufferGeometry( mesh.children[0].geometry );
  var length = geometry.faces.length;
  geometry.faces.splice(14, 1);

  for (var i = 0; i < geometry.faces.length; i ++ ) {
   var face = geometry.faces[i];
   face.color.setHex(Math.random() * 0xffffff);
  }
  mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors, side: THREE.DoubleSide }) );
  mesh.material.side = THREE.FrontSide; 
  
  var mesh2 = new THREE.Mesh( geometry, mesh.material.clone() ); 
  mesh2.material.side = THREE.BackSide; 
  // mesh2.material.vertexColors = THREE.NoColors; 
  mesh2.material.vertexColors = [new THREE.Color(0xff0000), new THREE.Color(0x00ff00), new THREE.Color(0x0000ff)];

  mesh.add( mesh2 );
  scene.add(mesh);
 }
})();
body {
  background: transparent;
  padding: 0;
  margin: 0;
  font-family: sans-serif;
}

#canvas {
  margin: 10px auto;
  width: 800px;
  height: 350px;
  margin-top: -44px;
}
<body>
  <div id="container"></div>
  <script src="https://threejs.org/build/three.js"></script>
  <script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
  <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  <script src="https://threejs.org/examples/js/loaders/MTLLoader.js"></script>
  <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/LoaderSupport.js"></script>
  <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/OBJLoader2.js"></script>

  <textarea id="plane_obj" style="display:none;">
    # Blender v2.77 (sub 0) OBJ File: 'Plane.blend'
    # www.blender.org
    mtllib Plane.mtl
    o Plane
    
    v -1.000000 0.000000 1.000000
    v 1.000000 0.000000 1.000000
    v -1.000000 0.000000 -1.000000
    v 1.000000 0.000000 -1.000000
    
    vt 0.000000 0.000000
    vt 1.000000 0.000000
    vt 0.000000 1.000000
    vt 1.000000 1.000000
    
    vn 0.0000 1.0000 0.0000
    
    usemtl None
    s off
    f 1/1/1 2/2/1 4/4/1 3/3/1
  </textarea>

  <textarea id="plane_mtl" style="display:none;">
    # Blender MTL File: 'Plane.blend'
    # Material Count: 1
    
    newmtl None
    Ns 0
    Ka 0.000000 0.000000 0.000000
    Kd 0.8 0.8 0.8
    Ks 0.8 0.8 0.8
    d 1
    illum 2
  </textarea>

</body>
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thanks great answer – Katana24 Jan 12 '18 at 10:28
  • I replaced the obj data with what you outlined above but when it hits the loadTexture function and looks for the geometry on the passed in mesh it complains - turns out the mesh doesn't have any children. – Katana24 Jan 12 '18 at 10:59
  • Actually it's strange - I updated my jsfiddle to what you have and it works but the same move doesn't work on my local - still returns to children.. – Katana24 Jan 12 '18 at 11:18