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]);
}
}
}