mergeGeometries static method

BufferGeometry? mergeGeometries(
  1. List<BufferGeometry> geometries, [
  2. bool useGroups = false
])

Method to merge multiple BufferGeometry objects into a single geometry.

All geometries must have:

  • The same set of attributes (e.g., position, normal, uv).
  • The same set of morph attributes (if any).
  • The same indexing type (either all indexed or all non-indexed).
  • The same morphTargetsRelative flag.

If useGroups is true, the resulting geometry will contain groups corresponding to each input geometry. This is useful for assigning different materials to different parts of the merged geometry.

Returns:

  • A new merged BufferGeometry if successful.
  • null if the geometries are incompatible or merging fails.

Implementation

static BufferGeometry? mergeGeometries(List<BufferGeometry> geometries,
    [bool useGroups = false]) {
  if (geometries.isEmpty) return null;

  // Check if the first geometry is indexed
  final bool isIndexed = geometries[0].getIndex() != null;

  // Store the set of attribute names and morph attribute names from the first geometry
  final attributesUsed = Set<String>.from(geometries[0].attributes.keys);
  final morphAttributesUsed =
      Set<String>.from(geometries[0].morphAttributes.keys);

  // Maps to collect attributes and morph attributes from all geometries
  final Map<String, List<BufferAttribute>> attributes = {};
  final Map<String, List<List<BufferAttribute>>> morphAttributes = {};

  // All geometries must have the same morphTargetsRelative flag
  final bool morphTargetsRelative = geometries[0].morphTargetsRelative;
  final mergedGeometry = BufferGeometry();

  int offset = 0;

  // --- Iterate over all geometries ---
  for (int i = 0; i < geometries.length; i++) {
    final geometry = geometries[i];
    int attributesCount = 0;

    // Ensure all geometries are either indexed or non-indexed
    if (isIndexed != (geometry.getIndex() != null)) return null;

    // Collect attributes
    for (final name in geometry.attributes.keys) {
      if (!attributesUsed.contains(name)) return null;

      attributes.putIfAbsent(name, () => []);
      attributes[name]!.add(geometry.getAttributeFromString(name));
      attributesCount++;
    }

    if (attributesCount != attributesUsed.length) return null;

    // Collect morph attributes
    if (morphTargetsRelative != geometry.morphTargetsRelative) return null;

    for (final name in geometry.morphAttributes.keys) {
      if (!morphAttributesUsed.contains(name)) return null;

      morphAttributes.putIfAbsent(name, () => []);
      morphAttributes[name]!.add(geometry.morphAttributes[name]!);
    }

    // Add groups if requested
    if (useGroups) {
      int count;
      if (isIndexed) {
        count = geometry.getIndex()!.count;
      } else if (geometry.getAttributeFromString('position') != null) {
        count = geometry.getAttributeFromString('position').count;
      } else {
        return null;
      }
      mergedGeometry.addGroup(offset, count, i);
      offset += count;
    }
  }

  // --- Merge indices ---
  if (isIndexed) {
    int indexOffset = 0;
    final List<int> mergedIndex = [];
    for (final geometry in geometries) {
      final index = geometry.getIndex()!;
      for (int j = 0; j < index.count; j++) {
        mergedIndex.add(index.getX(j)!.toInt() + indexOffset);
      }
      indexOffset =
          (indexOffset + geometry.getAttributeFromString('position').count)
              .toInt();
    }
    mergedGeometry.setIndex(mergedIndex);
  }

  // --- Merge attributes --
  for (final name in attributes.keys) {
    final mergedAttribute = _mergeAttributes(attributes[name]!);
    if (mergedAttribute == null) return null;

    mergedGeometry.setAttributeFromString(name, mergedAttribute);
  }

  // --- Merge morph attributes ---
  for (final name in morphAttributes.keys) {
    final numMorphTargets = morphAttributes[name]![0].length;
    if (numMorphTargets == 0) continue;

    mergedGeometry.morphAttributes[name] = [];

    for (int i = 0; i < numMorphTargets; i++) {
      final morphToMerge = <BufferAttribute>[];
      for (int j = 0; j < morphAttributes[name]!.length; j++) {
        morphToMerge.add(morphAttributes[name]![j][i]);
      }
      final mergedMorph = _mergeAttributes(morphToMerge);
      if (mergedMorph == null) return null;

      mergedGeometry.morphAttributes[name]!.add(mergedMorph);
    }
  }

  return mergedGeometry;
}