Is there a way to compute Tanget attribute for a mesh simiar to compute normals

I need to add tangent attribute to my mesh, but couldn’t find how to calculate it. Vertex data has compute normals method is there any similar helper for tangent?
I tried creating custom one but unable to get a proper results.

computeTangents() {
    const index = this.getIndices();
    const positionAttributes = this.getVerticesData(VertexBuffer.PositionKind);
    const normalAttributes = this.getVerticesData(VertexBuffer.NormalKind);
    const uvAttributes = this.getVerticesData(VertexBuffer.UVKind);

    const tangetAttributes = this.getVerticesData(VertexBuffer.TangentKind);

    if (!index || !positionAttributes || !normalAttributes || !uvAttributes) {
   
      return;
    }
    const vertexCount = positionAttributes.length / 3;

    const tangentBuffer = new Float32Array(vertexCount * 4);
    this.setVerticesData(VertexBuffer.TangentKind, tangentBuffer);

    const tan1: Vector3[] = [];
    const tan2: Vector3[] = [];

    for (let i = 0; i < vertexCount; i++) {
      tan1[i] = new Vector3();
      tan2[i] = new Vector3();
    }

    const vA = new Vector3();
    const vB = new Vector3();
    const vC = new Vector3();

    const uvA = new Vector2();
    const uvB = new Vector2();
    const uvC = new Vector2();

    const sdir = new Vector3();
    const tdir = new Vector3();

    function handleTriangle(a: number, b: number, c: number) {
      vA.fromArray(positionAttributes!, a * 3);
      vB.fromArray(positionAttributes!, b * 3);
      vC.fromArray(positionAttributes!, c * 3);

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

      vB.subtractToRef(vA, sdir);
      vC.subtractToRef(vA, tdir);

      uvB.subtractToRef(uvA, uvB);
      uvC.subtractToRef(uvA, uvC);

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

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

      if (!isFinite(r)) return;

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

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

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

    let groups = this._groups;

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

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

      const start = group.start;
      const count = group.count;

      for (let j = start, jl = start + count; j < jl; j += 3) {
        handleTriangle(index[j + 0], index[j + 1], index[j + 2]);
      }
    }

    const tmp = new Vector3();
    const tmp2 = new Vector3();
    const n = new Vector3();
    const n2 = new Vector3();

    function handleVertex(v: number) {
      n.fromArray(normalAttributes!, v);
      n2.copyFrom(n);

      const t = tan1[v];

      // Gram-Schmidt orthogonalize

      tmp.copyFrom(t);
      const dot = n.dot(t);
      tmp.subtractInPlace(n.multiplyByFloats(dot, dot, dot)).normalize();

      // Calculate handedness

      tmp2.copyFrom(n2.cross(t));

      const test = tmp2.dot(tan2[v]);
      const w = test < 0.0 ? -1.0 : 1.0;

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

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

      const start = group.start;
      const count = group.count;

      for (let j = start, jl = start + count; j < jl; j += 3) {
        handleVertex(index[j + 0]);
        handleVertex(index[j + 1]);
        handleVertex(index[j + 2]);
      }
    }
  }

Just cross the normal and up vector should get the tangent I think.

2 Likes