Better documentation of indexed buffer geometry.

Currently the documentation for indexed buffer geometry is a bit confusing. The example you chose is literally the worst possible example as the triangles are all in random positions and none of them share edges…. soooo what is the point of indexing? Additionally you don’t even SET the index buffer! It would be nice to have some better documentation explaining the benefit to indexing your vertices, along with some example code that shows how to actually implement indexing…. I unfortunately don’t have the time to make a pull request, but if anyone wants to undertake this effort here is my code I used for one of my projects. Given an array with some ‘holes’ it creates a index buffer. Normals are currently commented out but work as well.

fab.addEventListener('click', () => {
  let array = [
    0, 1, 1, 1,
    1, 1, 0, 1,
    1, 1, 1, 1,
    1, 1, 0, 1
  ];
  //   array,dim,badval
  test(array, 4, 0)
});
function test(array, d, b) {
  let positions = [];
  let normals = [];
  for (let j = 0; j < d; j++) {
    for (let i = 0; i < d; i++) {
      let n = (j*d) + i;
      if (array[n] === b) {
        positions.push(null, null, null)
      }
      else {
        positions.push(i, -j, array[n]);
      }
    }
  }
  let indices = [];
  for (let j = 0; j < d-1; j++) {
    for (let i = 0; i < d-1; i++) {
      /*
      var pA = new THREE.Vector3();
      var pB = new THREE.Vector3();
      var pC = new THREE.Vector3();
      var cb = new THREE.Vector3();
      var ab = new THREE.Vector3();
      */
      let n = 0;
      let garbage;
      let i1 = j * d + i;
      let z1 = array[i1];
      if (z1 === b) {
        n += 1;
        garbage = 1;
      }
      let i2 = j * d + i + 1;
      let z2 = array[i2];
      if (z2 === b) {
        n += 1;
        garbage = 2;
      }
      let i3 = ((j+1) * d) + i;
      let z3 = array[i3];
      if (z3 === b) {
        n += 1;
        garbage = 3;
      }
      let i4 = ((j+1) * d) + (i+1);
      let z4 = array[i4];
      if (z4 === b) {
        n += 1;
        garbage = 4;
      }
      if (n === 1) {
        if (garbage === 1) {
          indices.push(i3, i4, i2);
          /*
          pA.set( positions[i3 * 3], positions[(i3 * 3) + 1], positions[(i3 * 3) + 2] );
          pB.set( positions[i4 * 3], positions[(i4 * 3) + 1], positions[(i4 * 3) + 2] );
          pC.set( positions[i2 * 3], positions[(i2 * 3) + 1], positions[(i2 * 3) + 2] );
          cb.subVectors( pC, pB );
          ab.subVectors( pA, pB );
          cb.cross( ab );
          cb.normalize();
          var nx = cb.x;
          var ny = cb.y;
          var nz = cb.z;
          normals.push( nx, ny, nz );
          normals.push( nx, ny, nz );
          normals.push( nx, ny, nz );
          */
        }
        if (garbage === 2) {
          indices.push(i1, i3, i4);
          /*
          pA.set( positions[i1 * 3], positions[(i1 * 3) + 1], positions[(i1 * 3) + 2] );
          pB.set( positions[i3 * 3], positions[(i3 * 3) + 1], positions[(i3 * 3) + 2] );
          pC.set( positions[i4 * 3], positions[(i4 * 3) + 1], positions[(i4 * 3) + 2] );
          cb.subVectors( pC, pB );
          ab.subVectors( pA, pB );
          cb.cross( ab );
          cb.normalize();
          var nx = cb.x;
          var ny = cb.y;
          var nz = cb.z;
          normals.push( nx, ny, nz );
          normals.push( nx, ny, nz );
          normals.push( nx, ny, nz );
          */
        }
        if (garbage === 3) {
          indices.push(i1, i4, i2);
          /*
          pA.set( positions[i1 * 3], positions[(i1 * 3) + 1], positions[(i1 * 3) + 2] );
          pB.set( positions[i4 * 3], positions[(i4 * 3) + 1], positions[(i4 * 3) + 2] );
          pC.set( positions[i2 * 3], positions[(i2 * 3) + 1], positions[(i2 * 3) + 2] );
          cb.subVectors( pC, pB );
          ab.subVectors( pA, pB );
          cb.cross( ab );
          cb.normalize();
          var nx = cb.x;
          var ny = cb.y;
          var nz = cb.z;
          normals.push( nx, ny, nz );
          normals.push( nx, ny, nz );
          normals.push( nx, ny, nz );
          */
        }
        if (garbage === 4) {
          indices.push(i1, i3, i2);
          /*
          pA.set( positions[i1 * 3], positions[(i1 * 3) + 1], positions[(i1 * 3) + 2] );
          pB.set( positions[i3 * 3], positions[(i3 * 3) + 1], positions[(i3 * 3) + 2] );
          pC.set( positions[i2 * 3], positions[(i2 * 3) + 1], positions[(i2 * 3) + 2] );
          cb.subVectors( pC, pB );
          ab.subVectors( pA, pB );
          cb.cross( ab );
          cb.normalize();
          var nx = cb.x;
          var ny = cb.y;
          var nz = cb.z;
          normals.push( nx, ny, nz );
          normals.push( nx, ny, nz );
          normals.push( nx, ny, nz );
          */
        }
      }
      else if (n === 0) {
        indices.push(i1, i3, i4, i1, i4, i2)
        /*
        pA.set( positions[i1 * 3], positions[(i1 * 3) + 1], positions[(i1 * 3) + 2] );
        pB.set( positions[i3 * 3], positions[(i3 * 3) + 1], positions[(i3 * 3) + 2] );
        pC.set( positions[i4 * 3], positions[(i4 * 3) + 1], positions[(i4 * 3) + 2] );
        cb.subVectors( pC, pB );
        ab.subVectors( pA, pB );
        cb.cross( ab );
        cb.normalize();
        var nx = cb.x;
        var ny = cb.y;
        var nz = cb.z;
        normals.push( nx, ny, nz );
        normals.push( nx, ny, nz );
        normals.push( nx, ny, nz );

        pA.set( positions[i1 * 3], positions[(i1 * 3) + 1], positions[(i1 * 3) + 2] );
        pB.set( positions[i4 * 3], positions[(i4 * 3) + 1], positions[(i4 * 3) + 2] );
        pC.set( positions[i2 * 3], positions[(i2 * 3) + 1], positions[(i2 * 3) + 2] );
        cb.subVectors( pC, pB );
        ab.subVectors( pA, pB );
        cb.cross( ab );
        cb.normalize();
        var nx = cb.x;
        var ny = cb.y;
        var nz = cb.z;
        normals.push( nx, ny, nz );
        normals.push( nx, ny, nz );
        normals.push( nx, ny, nz );
        */
      }
    }
  }

  let geometry = new THREE.BufferGeometry();
  let material = new THREE.MeshBasicMaterial({
    color: 0xff0000,
    wireframe:false,
  });

  var posAttribute = new Float32Array( positions );
  var indAttribute = new Uint8Array( indices );
  //var normAttribute = new Int16Array( normals );
  //normAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader

  geometry.addAttribute( 'position', new THREE.BufferAttribute( posAttribute, 3 ) );
  geometry.setIndex(new THREE.BufferAttribute(indAttribute, 1));
  //geometry.addAttribute( 'normal', new THREE.Int16BufferAttribute( normAttribute, 3 ) );
  geometry.computeBoundingSphere();
  geometry.computeBoundingBox();
  var mesh = new THREE.Mesh( geometry, material );
  scene.add(mesh)
}

Author: Fantashit

1 thought on “Better documentation of indexed buffer geometry.

  1. Yup thats the link I meant, I had to write an algorithm to generate a gigantic mesh from a geotiff (45 million + faces and over 300 million verts and could not downsample) and indexing VASTLY improved performance, went from 1fps to 60fps, so I plan on writing up a tutorial on rendering large geometries/tiffs like that w/ indexing, I will comment the link here to help people in the future.

Comments are closed.