unweld method
A flat-shaded triangle soup derived from this mesh.
Every triangle gets three unique vertices carrying its face normal
(oriented to agree with the source vertex normals when present), with
texture coordinates, colors, and custom attributes carried through
per corner. attributes attaches canned per-triangle streams under
the names in UnweldAttribute, ready for a custom material's
attributes list.
Triples the vertex data of an indexed mesh; run it on a background isolate for large inputs (the whole derivation is pure CPU work).
Implementation
MeshData unweld({Set<UnweldAttribute> attributes = const {}}) {
_requireTriangles('unweld');
final count = triangleCount;
final outCount = count * 3;
final srcTexCoords = texCoords;
final srcColors = colors;
final outPositions = Float32List(outCount * 3);
final outNormals = Float32List(outCount * 3);
final outTexCoords = srcTexCoords == null
? null
: Float32List(outCount * 2);
final outColors = srcColors == null ? null : Float32List(outCount * 4);
final outCustom = <String, MeshAttributeData>{
for (final entry in customAttributes.entries)
entry.key: MeshAttributeData(
Float32List(outCount * entry.value.components),
components: entry.value.components,
),
};
final wantCentroid = attributes.contains(UnweldAttribute.centroid);
final wantSeed = attributes.contains(UnweldAttribute.seed);
final wantIndex = attributes.contains(UnweldAttribute.triangleIndex);
final wantBarycentric = attributes.contains(UnweldAttribute.barycentric);
final centroids = wantCentroid ? Float32List(outCount * 3) : null;
final seeds = wantSeed ? Float32List(outCount) : null;
final triIndices = wantIndex ? Float32List(outCount) : null;
final barycentrics = wantBarycentric ? Float32List(outCount * 3) : null;
final srcNormals = normals;
var seedState = 0x9e3779b9;
for (var t = 0; t < count; t++) {
final i0 = _cornerIndex(t * 3);
final i1 = _cornerIndex(t * 3 + 1);
final i2 = _cornerIndex(t * 3 + 2);
// Face normal from the winding, sign-checked against the source
// vertex normals so mirrored or inconsistently wound data still
// points outward.
final ax = positions[i0 * 3],
ay = positions[i0 * 3 + 1],
az = positions[i0 * 3 + 2];
final e1x = positions[i1 * 3] - ax,
e1y = positions[i1 * 3 + 1] - ay,
e1z = positions[i1 * 3 + 2] - az;
final e2x = positions[i2 * 3] - ax,
e2y = positions[i2 * 3 + 1] - ay,
e2z = positions[i2 * 3 + 2] - az;
var nx = e1y * e2z - e1z * e2y;
var ny = e1z * e2x - e1x * e2z;
var nz = e1x * e2y - e1y * e2x;
final nLen = math.sqrt(nx * nx + ny * ny + nz * nz);
if (nLen > 0) {
nx /= nLen;
ny /= nLen;
nz /= nLen;
}
if (srcNormals != null) {
final sx = srcNormals[i0 * 3] + srcNormals[i1 * 3] + srcNormals[i2 * 3];
final sy =
srcNormals[i0 * 3 + 1] +
srcNormals[i1 * 3 + 1] +
srcNormals[i2 * 3 + 1];
final sz =
srcNormals[i0 * 3 + 2] +
srcNormals[i1 * 3 + 2] +
srcNormals[i2 * 3 + 2];
if (nx * sx + ny * sy + nz * sz < 0) {
nx = -nx;
ny = -ny;
nz = -nz;
}
}
final cx =
(positions[i0 * 3] + positions[i1 * 3] + positions[i2 * 3]) / 3;
final cy =
(positions[i0 * 3 + 1] +
positions[i1 * 3 + 1] +
positions[i2 * 3 + 1]) /
3;
final cz =
(positions[i0 * 3 + 2] +
positions[i1 * 3 + 2] +
positions[i2 * 3 + 2]) /
3;
// Deterministic per-triangle random in [0, 1).
seedState = 0x1fffffff & (seedState * 1103515245 + 12345);
final seed = (seedState & 0xffff) / 0x10000;
for (var corner = 0; corner < 3; corner++) {
final src = corner == 0 ? i0 : (corner == 1 ? i1 : i2);
final v = t * 3 + corner;
outPositions[v * 3] = positions[src * 3];
outPositions[v * 3 + 1] = positions[src * 3 + 1];
outPositions[v * 3 + 2] = positions[src * 3 + 2];
outNormals[v * 3] = nx;
outNormals[v * 3 + 1] = ny;
outNormals[v * 3 + 2] = nz;
if (outTexCoords != null) {
outTexCoords[v * 2] = srcTexCoords![src * 2];
outTexCoords[v * 2 + 1] = srcTexCoords[src * 2 + 1];
}
if (outColors != null) {
for (var i = 0; i < 4; i++) {
outColors[v * 4 + i] = srcColors![src * 4 + i];
}
}
for (final entry in customAttributes.entries) {
final components = entry.value.components;
final srcData = entry.value.data;
final dstData = outCustom[entry.key]!.data;
for (var i = 0; i < components; i++) {
dstData[v * components + i] = srcData[src * components + i];
}
}
if (centroids != null) {
centroids[v * 3] = cx;
centroids[v * 3 + 1] = cy;
centroids[v * 3 + 2] = cz;
}
if (seeds != null) seeds[v] = seed;
if (triIndices != null) triIndices[v] = t.toDouble();
if (barycentrics != null) {
barycentrics[v * 3 + corner] = 1.0;
}
}
}
if (centroids != null) {
outCustom['triangle_centroid'] = MeshAttributeData(
centroids,
components: 3,
);
}
if (seeds != null) {
outCustom['triangle_seed'] = MeshAttributeData(seeds, components: 1);
}
if (triIndices != null) {
outCustom['triangle_index'] = MeshAttributeData(
triIndices,
components: 1,
);
}
if (barycentrics != null) {
outCustom['barycentric'] = MeshAttributeData(barycentrics, components: 3);
}
return MeshData(
positions: outPositions,
vertexCount: outCount,
normals: outNormals,
texCoords: outTexCoords,
colors: outColors,
primitiveType: gpu.PrimitiveType.triangle,
customAttributes: outCustom,
);
}