transformed method
This mesh placed by transform.
Positions move as points. normals are carried by the inverse
transpose, so a non-uniform scale leaves them perpendicular to the
surface, and tangents by transform itself; both are renormalized.
Every other attribute carries through untouched.
A mirroring transform reverses orientation, so it flips the tangent
handedness and reverses triangle winding to match. The renderer flips
winding per node for a mirrored node transform, which cannot help once
the mirror lives in the vertices. An unindexed triangle list comes back
indexed, since that is where its winding lives.
Implementation
MeshData transformed(vm.Matrix4 transform) {
final m = transform.storage;
final outPositions = Float32List(vertexCount * 3);
for (var v = 0; v < vertexCount; v++) {
final i = v * 3;
final x = positions[i];
final y = positions[i + 1];
final z = positions[i + 2];
outPositions[i] = m[0] * x + m[4] * y + m[8] * z + m[12];
outPositions[i + 1] = m[1] * x + m[5] * y + m[9] * z + m[13];
outPositions[i + 2] = m[2] * x + m[6] * y + m[10] * z + m[14];
}
final linear = transform.getRotation();
final mirrored = linear.determinant() < 0;
Float32List? outNormals;
final srcNormals = normals;
if (srcNormals != null) {
// A singular linear part has no inverse transpose; carrying normals
// through unchanged beats emitting NaNs.
final normalMatrix = vm.Matrix3.copy(linear);
if (normalMatrix.invert() == 0.0) {
normalMatrix.setFrom(linear);
} else {
normalMatrix.transpose();
}
outNormals = _transformVectors(srcNormals, normalMatrix, 3);
}
Float32List? outTangents;
final srcTangents = tangents;
if (srcTangents != null) {
outTangents = _transformVectors(srcTangents, linear, 4);
if (mirrored) {
for (var v = 0; v < vertexCount; v++) {
outTangents[v * 4 + 3] = -outTangents[v * 4 + 3];
}
}
}
return MeshData(
positions: outPositions,
vertexCount: vertexCount,
normals: outNormals,
texCoords: texCoords,
texCoords1: texCoords1,
colors: colors,
tangents: outTangents,
indices: mirrored ? _reversedWinding() : indices,
primitiveType: primitiveType,
customAttributes: customAttributes,
);
}