"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const three_module = require("../../../../../../../three@0.154.0/node_modules/three/build/three.module.js");
const $threeGLTF = Symbol("threeGLTF");
const $gltf = Symbol("gltf");
const $gltfElementMap = Symbol("gltfElementMap");
const $threeObjectMap = Symbol("threeObjectMap");
const $parallelTraverseThreeScene = Symbol("parallelTraverseThreeScene");
const $correlateOriginalThreeGLTF = Symbol("correlateOriginalThreeGLTF");
const $correlateCloneThreeGLTF = Symbol("correlateCloneThreeGLTF");
class CorrelatedSceneGraph {
  constructor(threeGLTF, gltf, threeObjectMap, gltfElementMap) {
    this[$threeGLTF] = threeGLTF;
    this[$gltf] = gltf;
    this[$gltfElementMap] = gltfElementMap;
    this[$threeObjectMap] = threeObjectMap;
  }
  /**
   * Produce a CorrelatedSceneGraph from a naturally generated Three.js GLTF.
   * Such GLTFs are produced by Three.js' GLTFLoader, and contain cached
   * details that expedite the correlation step.
   *
   * If a CorrelatedSceneGraph is provided as the second argument, re-correlates
   * a cloned Three.js GLTF with a clone of the glTF hierarchy used to produce
   * the upstream Three.js GLTF that the clone was created from. The result
   * CorrelatedSceneGraph is representative of the cloned hierarchy.
   */
  static from(threeGLTF, upstreamCorrelatedSceneGraph) {
    if (upstreamCorrelatedSceneGraph != null) {
      return this[$correlateCloneThreeGLTF](threeGLTF, upstreamCorrelatedSceneGraph);
    } else {
      return this[$correlateOriginalThreeGLTF](threeGLTF);
    }
  }
  static [$correlateOriginalThreeGLTF](threeGLTF) {
    const gltf = threeGLTF.parser.json;
    const associations = threeGLTF.parser.associations;
    const gltfElementMap = /* @__PURE__ */ new Map();
    const defaultMaterial = { name: "Default" };
    const defaultReference = { type: "materials", index: -1 };
    for (const threeMaterial of associations.keys()) {
      if (threeMaterial instanceof three_module.Material && associations.get(threeMaterial) == null) {
        if (defaultReference.index < 0) {
          if (gltf.materials == null) {
            gltf.materials = [];
          }
          defaultReference.index = gltf.materials.length;
          gltf.materials.push(defaultMaterial);
        }
        threeMaterial.name = defaultMaterial.name;
        associations.set(threeMaterial, { materials: defaultReference.index });
      }
    }
    for (const [threeObject, gltfMappings] of associations) {
      if (gltfMappings) {
        const objWithUserData = threeObject;
        objWithUserData.userData = objWithUserData.userData || {};
        objWithUserData.userData.associations = gltfMappings;
      }
      for (const mapping in gltfMappings) {
        if (mapping != null && mapping !== "primitives") {
          const type = mapping;
          const elementArray = gltf[type] || [];
          const gltfElement = elementArray[gltfMappings[type]];
          if (gltfElement == null) {
            continue;
          }
          let threeObjects = gltfElementMap.get(gltfElement);
          if (threeObjects == null) {
            threeObjects = /* @__PURE__ */ new Set();
            gltfElementMap.set(gltfElement, threeObjects);
          }
          threeObjects.add(threeObject);
        }
      }
    }
    return new CorrelatedSceneGraph(threeGLTF, gltf, associations, gltfElementMap);
  }
  /**
   * Transfers the association between a raw glTF and a Three.js scene graph
   * to a clone of the Three.js scene graph, resolved as a new
   * CorrelatedSceneGraph instance.
   */
  static [$correlateCloneThreeGLTF](cloneThreeGLTF, upstreamCorrelatedSceneGraph) {
    const originalThreeGLTF = upstreamCorrelatedSceneGraph.threeGLTF;
    const originalGLTF = upstreamCorrelatedSceneGraph.gltf;
    const cloneGLTF = JSON.parse(JSON.stringify(originalGLTF));
    const cloneThreeObjectMap = /* @__PURE__ */ new Map();
    const cloneGLTFElementMap = /* @__PURE__ */ new Map();
    for (let i = 0; i < originalThreeGLTF.scenes.length; i++) {
      this[$parallelTraverseThreeScene](originalThreeGLTF.scenes[i], cloneThreeGLTF.scenes[i], (object, cloneObject) => {
        const elementReference = upstreamCorrelatedSceneGraph.threeObjectMap.get(object);
        if (elementReference == null) {
          return;
        }
        for (const mapping in elementReference) {
          if (mapping != null && mapping !== "primitives") {
            const type = mapping;
            const index = elementReference[type];
            const cloneElement = cloneGLTF[type][index];
            const mappings = cloneThreeObjectMap.get(cloneObject) || {};
            mappings[type] = index;
            cloneThreeObjectMap.set(cloneObject, mappings);
            const cloneObjects = cloneGLTFElementMap.get(cloneElement) || /* @__PURE__ */ new Set();
            cloneObjects.add(cloneObject);
            cloneGLTFElementMap.set(cloneElement, cloneObjects);
          }
        }
      });
    }
    return new CorrelatedSceneGraph(cloneThreeGLTF, cloneGLTF, cloneThreeObjectMap, cloneGLTFElementMap);
  }
  /**
   * Traverses two presumably identical Three.js scenes, and invokes a
   * callback for each Object3D or Material encountered, including the initial
   * scene. Adapted from
   * https://github.com/mrdoob/three.js/blob/7c1424c5819ab622a346dd630ee4e6431388021e/examples/jsm/utils/SkeletonUtils.js#L586-L596
   */
  static [$parallelTraverseThreeScene](sceneOne, sceneTwo, callback) {
    const traverse = (a, b) => {
      callback(a, b);
      if (a.isObject3D) {
        const meshA = a;
        const meshB = b;
        if (meshA.material) {
          if (Array.isArray(meshA.material)) {
            for (let i = 0; i < meshA.material.length; ++i) {
              callback(meshA.material[i], meshB.material[i]);
            }
          } else {
            callback(meshA.material, meshB.material);
          }
        }
        for (let i = 0; i < a.children.length; ++i) {
          traverse(a.children[i], b.children[i]);
        }
      }
    };
    traverse(sceneOne, sceneTwo);
  }
  /**
   * The source Three.js GLTF result given to us by a Three.js GLTFLoader.
   */
  get threeGLTF() {
    return this[$threeGLTF];
  }
  /**
   * The in-memory deserialized source glTF.
   */
  get gltf() {
    return this[$gltf];
  }
  /**
   * A Map of glTF element references to arrays of corresponding Three.js
   * object references. Three.js objects are kept in arrays to account for
   * cases where more than one Three.js object corresponds to a single glTF
   * element.
   */
  get gltfElementMap() {
    return this[$gltfElementMap];
  }
  /**
   * A map of individual Three.js objects to corresponding elements in the
   * source glTF.
   */
  get threeObjectMap() {
    return this[$threeObjectMap];
  }
}
exports.CorrelatedSceneGraph = CorrelatedSceneGraph;
//# sourceMappingURL=correlated-scene-graph.js.map
