computeTangents method
void
computeTangents()
Calculates and adds a tangent attribute to this geometry.
The computation is only supported for indexed geometries and if position,
normal, and uv attributes are defined. When using a tangent space normal
map, prefer the MikkTSpace algorithm provided by
BufferGeometryUtils.computeMikkTSpaceTangents
instead.
Implementation
void computeTangents() {
final index = this.index;
final 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;
final positions = attributes["position"].array;
final normals = attributes["normal"].array;
final uvs = attributes["uv"].array;
int nVertices = positions.length ~/ 3;
if (attributes["tangent"] == null) {
setAttributeFromString('tangent', Float32BufferAttribute.fromList(Float32List(4 * nVertices), 4));
}
final tangents = attributes["tangent"].array;
final List<Vector3> tan1 = [], tan2 = [];
for (int i = 0; i < nVertices; i++) {
tan1.add(Vector3.zero());
tan2.add(Vector3.zero());
}
final vA = Vector3.zero(),
vB = Vector3.zero(),
vC = Vector3.zero(),
uvA = Vector2.zero(),
uvB = Vector2.zero(),
uvC = Vector2.zero(),
sdir = Vector3.zero(),
tdir = Vector3.zero();
void handleTriangle(int a, int b, int c) {
vA.fromNativeArray(positions, a * 3);
vB.fromNativeArray(positions, b * 3);
vC.fromNativeArray(positions, c * 3);
uvA.fromNativeArray(uvs, a * 2);
uvB.fromNativeArray(uvs, b * 2);
uvC.fromNativeArray(uvs, c * 2);
vB.sub(vA);
vC.sub(vA);
uvB.sub(uvA);
uvC.sub(uvA);
double 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.setFrom(vB);
sdir.scale(uvC.y);
sdir.addScaled(vC, -uvB.y);
sdir.scale(r);
tdir.setFrom(vC);
tdir.scale(uvB.x);
tdir.addScaled(vB, -uvC.x);
tdir.scale(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);
}
List<Map<String,dynamic>> groups = this.groups;
if (groups.isEmpty) {
groups = [
{"start": 0, "count": indices.length}
];
}
for (int i = 0, il = groups.length; i < il; ++i) {
final group = groups[i];
final start = group["start"];
final count = group["count"];
for (int j = start, jl = start + count; j < jl; j += 3) {
handleTriangle(
indices[j + 0].toInt(),
indices[j + 1].toInt(),
indices[j + 2].toInt(),
);
}
}
final tmp = Vector3.zero(), tmp2 = Vector3.zero();
final n = Vector3.zero(), n2 = Vector3.zero();
void handleVertex(int v) {
n.fromNativeArray(normals, v * 3);
n2.setFrom(n);
final t = tan1[v];
// Gram-Schmidt orthogonalize
tmp.setFrom(t);
n.scale(n.dot(t));
tmp.sub(n);
tmp.normalize();
// Calculate handedness
tmp2.cross2(n2, t);
final test = tmp2.dot(tan2[v]);
final 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 (int i = 0, il = groups.length; i < il; ++i) {
final group = groups[i];
final start = group["start"];
final count = group["count"];
for (int j = start, jl = start + count; j < jl; j += 3) {
handleVertex(indices[j + 0].toInt());
handleVertex(indices[j + 1].toInt());
handleVertex(indices[j + 2].toInt());
}
}
}