computeTangents method

void computeTangents()

Implementation

void computeTangents() {
  var index = this.index;
  var attributes = this.attributes;

  // based on http://www.terathon.com/code/tangent.html
  // (per vertex tangents)

  if (index == null ||
      attributes["position"] == null ||
      attributes["normal"] == null ||
      attributes["uv"] == null) {
    console.error(
        'THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)');
    return;
  }

  final indices = index.array;
  var positions = attributes["position"].array;
  var normals = attributes["normal"].array;
  var uvs = attributes["uv"].array;

  int nVertices = positions.length ~/ 3;

  if (attributes["tangent"] == null) {
    setAttribute(
        'tangent', Float32BufferAttribute(Float32Array(4 * nVertices), 4));
  }

  var tangents = attributes["tangent"].array;

  var tan1 = [], tan2 = [];

  for (var i = 0; i < nVertices; i++) {
    tan1.add(Vector3());
    tan2.add(Vector3());
  }

  var vA = Vector3(),
      vB = Vector3(),
      vC = Vector3(),
      uvA = Vector2(),
      uvB = Vector2(),
      uvC = Vector2(),
      sdir = Vector3(),
      tdir = Vector3();

  void handleTriangle(int a, int b, int c) {
    vA.fromArray(positions, a * 3);
    vB.fromArray(positions, b * 3);
    vC.fromArray(positions, c * 3);

    uvA.fromArray(uvs, a * 2);
    uvB.fromArray(uvs, b * 2);
    uvC.fromArray(uvs, c * 2);

    vB.sub(vA);
    vC.sub(vA);

    uvB.sub(uvA);
    uvC.sub(uvA);

    num r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y);

    // silently ignore degenerate uv triangles having coincident or colinear vertices

    if (!r.isFinite) return;

    sdir
        .copy(vB)
        .multiplyScalar(uvC.y)
        .addScaledVector(vC, -uvB.y)
        .multiplyScalar(r);
    tdir
        .copy(vC)
        .multiplyScalar(uvB.x)
        .addScaledVector(vB, -uvC.x)
        .multiplyScalar(r);

    tan1[a].add(sdir);
    tan1[b].add(sdir);
    tan1[c].add(sdir);

    tan2[a].add(tdir);
    tan2[b].add(tdir);
    tan2[c].add(tdir);
  }

  var groups = this.groups;

  if (groups.isEmpty) {
    groups = [
      {"start": 0, "count": indices.length}
    ];
  }

  for (var i = 0, il = groups.length; i < il; ++i) {
    var group = groups[i];

    var start = group["start"];
    var count = group["count"];

    for (var j = start, jl = start + count; j < jl; j += 3) {
      handleTriangle(
        indices[j + 0].toInt(),
        indices[j + 1].toInt(),
        indices[j + 2].toInt(),
      );
    }
  }

  var tmp = Vector3(), tmp2 = Vector3();
  var n = Vector3(), n2 = Vector3();

  void handleVertex(int v) {
    n.fromArray(normals, v * 3);
    n2.copy(n);

    var t = tan1[v];

    // Gram-Schmidt orthogonalize

    tmp.copy(t);
    tmp.sub(n.multiplyScalar(n.dot(t))).normalize();

    // Calculate handedness

    tmp2.crossVectors(n2, t);
    var test = tmp2.dot(tan2[v]);
    var w = (test < 0.0) ? -1.0 : 1.0;

    tangents[v * 4] = tmp.x;
    tangents[v * 4 + 1] = tmp.y;
    tangents[v * 4 + 2] = tmp.z;
    tangents[v * 4 + 3] = w;
  }

  for (var i = 0, il = groups.length; i < il; ++i) {
    var group = groups[i];

    var start = group["start"];
    var count = group["count"];

    for (var j = start, jl = start + count; j < jl; j += 3) {
      handleVertex(indices[j + 0].toInt());
      handleVertex(indices[j + 1].toInt());
      handleVertex(indices[j + 2].toInt());
    }
  }
}