1

Is it possible to load texture to specific faces of PlaneGeometry object? What I want to do is to map separate texture to specific 2 triangles of PlaneGeometry object.

Here is some example that color each 2 trianles of PlaneGeometry object with some color and it works:

<!DOCTYPE html>
<html lang="en">
 <head>
  <title>title</title>
  <meta charset="utf-8">
  <style>
   body {
    font-family: Monospace;
    background-color: #f0f0f0;
    margin: 0px;
    overflow: hidden;
   }
  </style>
 </head>
 
<body>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
 <script>
  
  var camera, scene, renderer, geometry, material, mesh;

  init();
  animate();
  
  function init() {

   scene = new THREE.Scene();

   camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
   camera.position.z = 500;
   scene.add(camera);

   // geometry
   var thumbnail_width = 32;
   var thumbnail_height = 32;
   var width_segments = 10;
   var height_segments = 10;
   var plane_geometry_width = width_segments * thumbnail_width;
   var plane_geometry_height = height_segments * thumbnail_height;
   var geometry = new THREE.PlaneGeometry(plane_geometry_width, plane_geometry_height, width_segments, height_segments); // faces.length = widthSegments*heightSegments*2
   
   //Draw grid with static colors
   // materials
   var materials = [];
   materials.push(new THREE.MeshBasicMaterial({
    color: 0xff0000, side:THREE.DoubleSide
   }));
   materials.push(new THREE.MeshBasicMaterial({
    color: 0x00ff00, side:THREE.DoubleSide
   }));
   materials.push(new THREE.MeshBasicMaterial({
    color: 0x0000ff, side:THREE.DoubleSide
   }));
   // Add materialIndex to face
   var l = geometry.faces.length / 2;
   for (var i = 0; i < l; i++) {
    var j = 2 * i;
    geometry.faces[j].materialIndex = i % 3;
    geometry.faces[j + 1].materialIndex = i % 3;
   }

   // mesh
   mesh = new THREE.Mesh(geometry, materials);
   scene.add(mesh);
   
   // WebGL renderer
   renderer = new THREE.WebGLRenderer();
   renderer.setSize(window.innerWidth, window.innerHeight); 
   document.body.appendChild(renderer.domElement);
  }

  function animate() {
   requestAnimationFrame(animate);
   render();
  }

  function render() {
   renderer.render(scene, camera);
  }
  
 </script>
</body>

Here is result: enter image description here

However when I try to map texture the same way, it maps to all triangles, which is not what I want:

<!DOCTYPE html>
<html lang="en">
 <head>
  <title>title</title>
  <meta charset="utf-8">
  <style>
   body {
    font-family: Monospace;
    background-color: #f0f0f0;
    margin: 0px;
    overflow: hidden;
   }
  </style>
 </head>
 
<body>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
 <script>
  
  var camera, scene, renderer, geometry, material, mesh;

  init();
  animate();
  
  function init() {

   scene = new THREE.Scene();

   camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
   camera.position.z = 500;
   scene.add(camera);

   // geometry
   var thumbnail_width = 32;
   var thumbnail_height = 32;
   var width_segments = 10;
   var height_segments = 10;
   var plane_geometry_width = width_segments * thumbnail_width;
   var plane_geometry_height = height_segments * thumbnail_height;
   var geometry = new THREE.PlaneGeometry(plane_geometry_width, plane_geometry_height, width_segments, height_segments); // faces.length = widthSegments*heightSegments*2
   
   var texture = new THREE.TextureLoader().load("image_small/item_1.png")
   
   var materials = [];
   materials.push(new THREE.MeshBasicMaterial({map : texture, side: THREE.DoubleSide}));
   materials.push(new THREE.MeshBasicMaterial({color: 0xff0000, side:THREE.DoubleSide}));
   materials.push(new THREE.MeshBasicMaterial({color: 0x00ff00, side:THREE.DoubleSide}));
   
   // Add materialIndex to face
   var l = geometry.faces.length / 2;
   for (var i = 0; i < l; i++) {
    var j = 2 * i;
    geometry.faces[j].materialIndex = i % 3;
    geometry.faces[j + 1].materialIndex = i % 3;
   }
   
   // mesh
   mesh = new THREE.Mesh(geometry, materials);
   scene.add(mesh);
   
   // WebGL renderer
   renderer = new THREE.WebGLRenderer();
   renderer.setSize(window.innerWidth, window.innerHeight);
   document.body.appendChild(renderer.domElement);
  }

  function animate() {
   requestAnimationFrame(animate);
   render();
  }

  function render() {
   renderer.render(scene, camera);
  }
  
 </script>
</body>

Here is result: enter image description here

UPDATE:

Part of code for 2nd solution suggested by @WestLangley

    var texture = new THREE.TextureLoader().load("image_small/item_1.png")
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set( 10, 10 );
    var materials = [];
    materials.push(new THREE.MeshBasicMaterial({map : texture, side: THREE.DoubleSide}));
    materials.push(new THREE.MeshBasicMaterial({color: 0xff0000, side:THREE.DoubleSide}));
    materials.push(new THREE.MeshBasicMaterial({color: 0x00ff00, side:THREE.DoubleSide}));
    // Add materialIndex to face
    var l = geometry.faces.length / 2;
    for (var i = 0; i < l; i++) {
        var j = 2 * i;
        geometry.faces[j].materialIndex = i % 3;
        geometry.faces[j + 1].materialIndex = i % 3;
    }
    geometry.sortFacesByMaterialIndex();
    // Mesh
    mesh = new THREE.Mesh(geometry, materials);
    scene.add(mesh);
    // WebGL renderer
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

Result: enter image description here

UPDATE2:

    var texture = new THREE.TextureLoader().load("image_small/item_1.png")
    texture.flipY = true
    var materials = [];
    materials.push(new THREE.MeshBasicMaterial({map : texture, side: THREE.DoubleSide}));
    for (var i = 0; i < geometry.faces.length; i++) {
        geometry.faces[i].materialIndex = 0;
    }
    var uvs = geometry.faceVertexUvs[ 0 ]; // widthSegments*heightSegments*2

    //UV
    // (0,1) (1,1)
    // (0,0) (1,0)
    // For THREE.PlaneGeometry, UV ( 0, 0 ) is located at the bottom left, and ( 1, 1 ) the top right.
    for (var i = 0; i < uvs.length; i++) {
        if(i % 2 == 0)
        {
            // (0,1)[2] x
            // (0,0)[1] (1,0)[3]
            uvs[i][0].x = 0.0;
            uvs[i][0].y = 0.0;
            uvs[i][1].x = 0.0;
            uvs[i][1].y = 1.0;
            uvs[i][2].x = 1.0;
            uvs[i][2].y = 0.0;

        }
        else
        {   
            // (0,1)[1] (1,1)[2]
            //     x    (1,0)[3]
            uvs[i][0].x = 0.0;
            uvs[i][0].y = 1.0;
            uvs[i][1].x = 1.0;
            uvs[i][1].y = 1.0;
            uvs[i][2].x = 1.0;
            uvs[i][2].y = 0.0;

        }
    }

    var mesh = new THREE.Mesh(geometry, materials);
    scene.add(mesh);
    // WebGL renderer
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

Result: enter image description here

mrgloom
  • 20,061
  • 36
  • 171
  • 301
  • It would probably be better to generate a [Texture Atlas](https://en.wikipedia.org/wiki/Texture_atlas), and adjust your UVs to map to specific images within the atlas. – TheJim01 Jan 22 '18 at 17:45

1 Answers1

4

You have two options.

One is to change the UVs of your geometry so each square has UVs that range from ( 0, 0 ) lower-left to ( 1, 1 ) upper-right.

The other solution is to just set the repeat of your texture:

texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 10, 10 );

BTW, type renderer.info into the console, and you will see you are generating 100 draw calls.

Add the following to your code after you set the material indices:

geometry.sortFacesByMaterialIndex();

You should now see just 3 draw calls.

three.js r.89

WestLangley
  • 102,557
  • 10
  • 276
  • 276
  • 2nd solution works, but actually I need to put different textures in different 'cells' of this grid. For 1st solution can you show example how to fill UVs for example for 'cell' (2,3) of grid? – mrgloom Jan 22 '18 at 17:17
  • Please do me a favor, and try first. – WestLangley Jan 22 '18 at 17:26
  • I found your example here https://stackoverflow.com/a/19892947/1179925 howewer texture Y flipped and for some reason `texture.flipY = true` doesn't help, see Update2. – mrgloom Jan 23 '18 at 13:00
  • From your inline comments, you do not appear to be laying out your geometry/uvs the way `PlaneGeometry` does. Look at https://threejs.org/examples/misc_uv_tests.html. You have the right idea. :) – WestLangley Jan 23 '18 at 15:45