computeBounds function

void computeBounds(
  1. BufferGeometry geometry,
  2. Map<String, dynamic> primitiveDef,
  3. GLTFParser parser
)

@param {BufferGeometry} geometry @param {GLTF.Primitive} primitiveDef @param {GLTFParser} parser

Implementation

void computeBounds(BufferGeometry geometry, Map<String, dynamic> primitiveDef, GLTFParser parser) {
  Map<String, dynamic> attributes = primitiveDef["attributes"];

  final box = BoundingBox();

  if (attributes["POSITION"] != null) {
    final accessor = parser.json["accessors"][attributes["POSITION"]];

    final min = accessor["min"];
    final max = accessor["max"];

    // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.

    if (min != null && max != null) {
      box.set(Vector3(min[0].toDouble(), min[1].toDouble(), min[2].toDouble()),
          Vector3(max[0].toDouble(), max[1].toDouble(), max[2].toDouble()));

      // todo normalized is bool ? int ?
      if (accessor["normalized"] != null &&
          accessor["normalized"] != false &&
          accessor["normalized"] != 0) {
        final boxScale = getNormalizedComponentScale( webglComponentTypes[accessor.componentType]);
        box.min.scale(boxScale);
        box.max.scale(boxScale);
      }
    }
    else {
      console.warning('GLTFLoader: Missing min/max properties for accessor POSITION.');

      return;
    }
  } else {
    return;
  }

  final targets = primitiveDef["targets"];

  if (targets != null) {
    final maxDisplacement = Vector3();
    final vector = Vector3();

    for (int i = 0, il = targets.length; i < il; i++) {
      final target = targets[i];

      if (target["POSITION"] != null) {
        final accessor = parser.json["accessors"][target["POSITION"]];
        final min = accessor["min"];
        final max = accessor["max"];

        // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.

        if (min != null && max != null) {
          // we need to get max of absolute components because target weight is [-1,1]
          vector.setX(math.max<double>(min[0].abs().toDouble(), max[0].abs().toDouble()));
          vector.setY(math.max<double>(min[1].abs().toDouble(), max[1].abs().toDouble()));
          vector.setZ(math.max<double>(min[2].abs().toDouble(), max[2].abs().toDouble()));

          if (accessor["normalized"] == true) {
            final boxScale = getNormalizedComponentScale(webglComponentTypes[accessor.componentType]);
            vector.scale(boxScale);
          }

          // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative
          // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets
          // are used to implement key-frame animations and as such only two are active at a time - this results in very large
          // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size.
          maxDisplacement.max(vector);
        }
        else {
          console.warning('GLTFLoader: Missing min/max properties for accessor POSITION.');
        }
      }
    }

    // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets.
    box.expandByVector(maxDisplacement);
  }

  geometry.boundingBox = box;

  final sphere = BoundingSphere();

  box.getCenter(sphere.center);
  sphere.radius = box.min.distanceTo(box.max) / 2;

  geometry.boundingSphere = sphere;
}