edgeSplit static method

dynamic edgeSplit(
  1. dynamic geometry
)

////////////////////////////////////////////////////////////////////////////////// // Split Hypotenuse /////////////////

Applies one iteration of split subdivision. Splits all triangles at edges shared by coplanar triangles. Starts by splitting at longest shared edge, followed by splitting from that center edge point to the center of any other shared edges.

Implementation

///
/// Applies one iteration of split subdivision. Splits all triangles at edges shared by coplanar triangles.
/// Starts by splitting at longest shared edge, followed by splitting from that center edge point to the
/// center of any other shared edges.
///
static edgeSplit(geometry) {

    ///// Geometries
    if (! verifyGeometry(geometry)) return geometry;
    final existing = (geometry.index != null) ? geometry.toNonIndexed() : geometry.clone();
    final split = BufferGeometry();

    ///// Attributes
    final attributeList = gatherAttributes(existing);
    final vertexCount = existing.attributes['position'].count;
    final posAttribute = existing.getAttributeFromString('position');
    final norAttribute = existing.getAttributeFromString('normal');
    final edgeHashToTriangle = {};
    final triangleEdgeHashes = [];
    final edgeLength = {};
    final triangleExist = [];

    ///// Edges
    for (int i = 0; i < vertexCount; i += 3) {

        // Positions
        _vector0.fromBuffer(posAttribute, i + 0);
        _vector1.fromBuffer(posAttribute, i + 1);
        _vector2.fromBuffer(posAttribute, i + 2);
        _normal.fromBuffer(norAttribute, i);
        final vecHash0 = hashFromVector(_vector0);
        final vecHash1 = hashFromVector(_vector1);
        final vecHash2 = hashFromVector(_vector2);

        // Verify Area
        final triangleSize = _triangle.set(_vector0, _vector1, _vector2).getArea();
        triangleExist.add(! fuzzy(triangleSize, 0));
        if (! triangleExist[i ~/ 3]) {
            triangleEdgeHashes.add([]);
            continue;
        }

        // Calculate Normals
        calcNormal(_normal, _vector0, _vector1, _vector2);
        final normalHash = hashFromVector(_normal);

        // Vertex Hashes
        final hashes = [
            '${vecHash0}_${vecHash1}_$normalHash', // [0]: 0to1
            '${vecHash1}_${vecHash0}_$normalHash', // [1]: 1to0
            '${vecHash1}_${vecHash2}_$normalHash', // [2]: 1to2
            '${vecHash2}_${vecHash1}_$normalHash', // [3]: 2to1
            '${vecHash2}_${vecHash0}_$normalHash', // [4]: 2to0
            '${vecHash0}_${vecHash2}_$normalHash', // [5]: 0to2
        ];

        // Store Edge Hashes
        final index = i / 3;
        for (int j = 0; j < hashes.length; j++) {
            // Attach Triangle Index to Edge Hash
            if (edgeHashToTriangle[hashes[j]] == null) edgeHashToTriangle[hashes[j]] = [];
            edgeHashToTriangle[hashes[j]].add(index);

            // Edge Length
            if (edgeLength[hashes[j]] == null) {
                if (j == 0 || j == 1) edgeLength[hashes[j]] = _vector0.distanceTo(_vector1);
                if (j == 2 || j == 3) edgeLength[hashes[j]] = _vector1.distanceTo(_vector2);
                if (j == 4 || j == 5) edgeLength[hashes[j]] = _vector2.distanceTo(_vector0);
            }
        }

        // Triangle Edge Reference
        triangleEdgeHashes.add([ hashes[0], hashes[2], hashes[4] ]);
    }

    // Loop Subdivide Function
    List<double> splitAttribute(BufferAttribute attribute, String attributeName, [bool morph = false]) {
        const newTriangles = 4; /* maximum number of triangles */
        final arrayLength = (vertexCount * attribute.itemSize) * newTriangles;
        final List<num> floatArray = List.filled(arrayLength, 0, growable: true);//attribute.array.sublist(0,arrayLength);//.constructor(arrayLength);
        floatArray.replaceRange(0, attribute.array.length, attribute.array.sublist(0));
        final processGroups = (attributeName == 'position' && ! morph && existing.groups.length > 0);
        int? groupStart;
        int? groupMaterial;

        int index = 0;
        int skipped = 0;
        int step = attribute.itemSize;

        for (int i = 0; i < vertexCount; i += 3) {
            // Verify Triangle is Valid
            if (! triangleExist[i ~/ 3]) {
                skipped += 3;
                continue;
            }

            // Get Triangle Points
            _vector0.fromBuffer(attribute, i + 0);
            _vector1.fromBuffer(attribute, i + 1);
            _vector2.fromBuffer(attribute, i + 2);

            // Check for Shared Edges
            final existingIndex = i ~/ 3;
            final edgeHash0to1 = triangleEdgeHashes[existingIndex][0];
            final edgeHash1to2 = triangleEdgeHashes[existingIndex][1];
            final edgeHash2to0 = triangleEdgeHashes[existingIndex][2];

            final edgeCount0to1 = edgeHashToTriangle[edgeHash0to1].length;
            final edgeCount1to2 = edgeHashToTriangle[edgeHash1to2].length;
            final edgeCount2to0 = edgeHashToTriangle[edgeHash2to0].length;
            final sharedCount = (edgeCount0to1 + edgeCount1to2 + edgeCount2to0) - 3;

            // New Index (Before New Triangles, used for Groups)
            final int loopStartIndex = ((index * 3) / step) ~/ 3;

            // No Shared Edges
            if (sharedCount == 0) {
              setTriangle(floatArray, index, step, _vector0, _vector1, _vector2);
              index += (step * 3);
            }
            else {
                final length0to1 = edgeLength[edgeHash0to1];
                final length1to2 = edgeLength[edgeHash1to2];
                final length2to0 = edgeLength[edgeHash2to0];

                // Add New Triangle Positions
                if ((length0to1 > length1to2 || edgeCount1to2 <= 1) &&
                    (length0to1 > length2to0 || edgeCount2to0 <= 1) && edgeCount0to1 > 1) {
                    _center.setFrom(_vector0).add(_vector1).divideScalar(2.0);
                    if (edgeCount2to0 > 1) {
                        _midpoint.setFrom(_vector2).add(_vector0).divideScalar(2.0);
                        setTriangle(floatArray, index, step, _vector0, _center, _midpoint); index += (step * 3);
                        setTriangle(floatArray, index, step, _center, _vector2, _midpoint); index += (step * 3);
                    } else {
                        setTriangle(floatArray, index, step, _vector0, _center, _vector2); index += (step * 3);
                    }
                    if (edgeCount1to2 > 1) {
                        _midpoint.setFrom(_vector1).add(_vector2).divideScalar(2.0);
                        setTriangle(floatArray, index, step, _center, _vector1, _midpoint); index += (step * 3);
                        setTriangle(floatArray, index, step, _midpoint, _vector2, _center); index += (step * 3);
                    } else {
                        setTriangle(floatArray, index, step, _vector1, _vector2, _center); index += (step * 3);
                    }

                } else if ((length1to2 > length2to0 || edgeCount2to0 <= 1) && edgeCount1to2 > 1) {
                    _center.setFrom(_vector1).add(_vector2).divideScalar(2.0);
                    if (edgeCount0to1 > 1) {
                        _midpoint.setFrom(_vector0).add(_vector1).divideScalar(2.0);
                        setTriangle(floatArray, index, step, _center, _midpoint, _vector1); index += (step * 3);
                        setTriangle(floatArray, index, step, _midpoint, _center, _vector0); index += (step * 3);
                    } else {
                        setTriangle(floatArray, index, step, _vector1, _center, _vector0); index += (step * 3);
                    }
                    if (edgeCount2to0 > 1) {
                        _midpoint.setFrom(_vector2).add(_vector0).divideScalar(2.0);
                        setTriangle(floatArray, index, step, _center, _vector2, _midpoint); index += (step * 3);
                        setTriangle(floatArray, index, step, _midpoint, _vector0, _center); index += (step * 3);
                    } else {
                        setTriangle(floatArray, index, step, _vector2, _vector0, _center); index += (step * 3);
                    }

                } else if (edgeCount2to0 > 1) {
                    _center.setFrom(_vector2).add(_vector0).divideScalar(2.0);
                    if (edgeCount1to2 > 1) {
                        _midpoint.setFrom(_vector1).add(_vector2).divideScalar(2.0);
                        setTriangle(floatArray, index, step, _vector2, _center, _midpoint); index += (step * 3);
                        setTriangle(floatArray, index, step, _center, _vector1, _midpoint); index += (step * 3);
                    } else {
                        setTriangle(floatArray, index, step, _vector2, _center, _vector1); index += (step * 3);
                    }
                    if (edgeCount0to1 > 1) {
                        _midpoint.setFrom(_vector0).add(_vector1).divideScalar(2.0);
                        setTriangle(floatArray, index, step, _vector0, _midpoint, _center); index += (step * 3);
                        setTriangle(floatArray, index, step, _midpoint, _vector1, _center); index += (step * 3);
                    } else {
                        setTriangle(floatArray, index, step, _vector0, _vector1, _center); index += (step * 3);
                    }

                } else {
                    setTriangle(floatArray, index, step, _vector0, _vector1, _vector2); index += (step * 3);
                }
            }

            // Process Groups
            if (processGroups) {
                existing.groups.forEach((group){
                    if (group['start'] == (i - skipped)) {
                        if (groupStart != null && groupMaterial != null) {
                            split.addGroup(groupStart!, loopStartIndex - groupStart!, groupMaterial!);
                        }
                        groupStart = loopStartIndex;
                        groupMaterial = group['materialIndex'];
                    }
                });
            }

            // Reset Skipped Triangle Counter
            skipped = 0;
        }

        // Resize Array
        final reducedCount = (index * 3) ~/ step;
        final List<double> reducedArray = List.filled(reducedCount, 0.0);//attribute.array.sublist(0,reducedCount);//.constructor(reducedCount);
        final len = floatArray.length > reducedCount?reducedCount:floatArray.length;
        for (int i = 0; i < len; i++) {
          reducedArray[i] = floatArray[i].toDouble();
        }

        // Final Group
        if (processGroups && groupStart != null && groupMaterial != null) {
            split.addGroup(groupStart!, (((index * 3) / step) ~/ 3) - groupStart!, groupMaterial!);
        }

        return reducedArray;
    }

    ///// Build Geometry, Set Attributes
    attributeList.forEach((attributeName){
        final attribute = existing.getAttributeFromString(attributeName);
        if (attribute == null) return;
        final floatArray = splitAttribute(attribute, attributeName);
        split.setAttributeFromString(attributeName, Float32BufferAttribute.fromList(floatArray, attribute.itemSize));
    });

    ///// Morph Attributes
    final morphAttributes = existing.morphAttributes;
    for (final attributeName in morphAttributes.keys) {
        final List<BufferAttribute> array = [];
        final morphAttribute = morphAttributes[attributeName];

        // Process Array of Float32BufferAttributes
        for (int i = 0, l = morphAttribute.length; i < l; i++) {
            if (morphAttribute[i].count != vertexCount) continue;
            final floatArray = splitAttribute(morphAttribute[i], attributeName, true);
            array.add(Float32BufferAttribute.fromList(floatArray, morphAttribute[i].itemSize));
        }
        split.morphAttributes[attributeName] = array;
    }
    split.morphTargetsRelative = existing.morphTargetsRelative;

    // Clean Up, Return New Geometry
    existing.dispose();
    return split;
}