text: /// <reference types="./ColladaLoader.d.ts" />
  /// <reference lib="dom" />
  import {
    AmbientLight,
    AnimationClip,
    Bone,
    BufferGeometry,
    ClampToEdgeWrapping,
    Color,
    DirectionalLight,
    DoubleSide,
    Euler,
    FileLoader,
    Float32BufferAttribute,
    Group,
    Line,
    LineBasicMaterial,
    LineSegments,
    Loader,
    LoaderUtils,
    MathUtils,
    Matrix4,
    Mesh,
    MeshBasicMaterial,
    MeshLambertMaterial,
    MeshPhongMaterial,
    OrthographicCamera,
    PerspectiveCamera,
    PointLight,
    Quaternion,
    QuaternionKeyframeTrack,
    RepeatWrapping,
    Scene,
    Skeleton,
    SkinnedMesh,
    SpotLight,
    TextureLoader,
    Vector3,
    VectorKeyframeTrack,
  } from "../../../src/Three.js";
  import { TGALoader } from "../loaders/TGALoader.js";
  
  var ColladaLoader = function (manager) {
    Loader.call(this, manager);
  };
  
  ColladaLoader.prototype = Object.assign(Object.create(Loader.prototype), {
    constructor: ColladaLoader,
  
    load: function (url, onLoad, onProgress, onError) {
      var scope = this;
  
      var path = (scope.path === "")
        ? LoaderUtils.extractUrlBase(url)
        : scope.path;
  
      var loader = new FileLoader(scope.manager);
      loader.setPath(scope.path);
      loader.setRequestHeader(scope.requestHeader);
      loader.setWithCredentials(scope.withCredentials);
      loader.load(
        url,
        function (text) {
          try {
            onLoad(scope.parse(text, path));
          } catch (e) {
            if (onError) {
              onError(e);
            } else {
              console.error(e);
            }
  
            scope.manager.itemError(url);
          }
        },
        onProgress,
        onError,
      );
    },
  
    options: {
      set convertUpAxis(value) {
        console.warn(
          "THREE.ColladaLoader: options.convertUpAxis() has been removed. Up axis is converted automatically.",
        );
      },
    },
  
    parse: function (text, path) {
      function getElementsByTagName(xml, name) {
        // Non recursive xml.getElementsByTagName() ...
  
        var array = [];
        var childNodes = xml.childNodes;
  
        for (var i = 0, l = childNodes.length; i < l; i++) {
          var child = childNodes[i];
  
          if (child.nodeName === name) {
            array.push(child);
          }
        }
  
        return array;
      }
  
      function parseStrings(text) {
        if (text.length === 0) return [];
  
        var parts = text.trim().split(/\s+/);
        var array = new Array(parts.length);
  
        for (var i = 0, l = parts.length; i < l; i++) {
          array[i] = parts[i];
        }
  
        return array;
      }
  
      function parseFloats(text) {
        if (text.length === 0) return [];
  
        var parts = text.trim().split(/\s+/);
        var array = new Array(parts.length);
  
        for (var i = 0, l = parts.length; i < l; i++) {
          array[i] = parseFloat(parts[i]);
        }
  
        return array;
      }
  
      function parseInts(text) {
        if (text.length === 0) return [];
  
        var parts = text.trim().split(/\s+/);
        var array = new Array(parts.length);
  
        for (var i = 0, l = parts.length; i < l; i++) {
          array[i] = parseInt(parts[i]);
        }
  
        return array;
      }
  
      function parseId(text) {
        return text.substring(1);
      }
  
      function generateId() {
        return "three_default_" + (count++);
      }
  
      function isEmpty(object) {
        return Object.keys(object).length === 0;
      }
  
      // asset
  
      function parseAsset(xml) {
        return {
          unit: parseAssetUnit(getElementsByTagName(xml, "unit")[0]),
          upAxis: parseAssetUpAxis(getElementsByTagName(xml, "up_axis")[0]),
        };
      }
  
      function parseAssetUnit(xml) {
        if ((xml !== undefined) && (xml.hasAttribute("meter") === true)) {
          return parseFloat(xml.getAttribute("meter"));
        } else {
          return 1; // default 1 meter
        }
      }
  
      function parseAssetUpAxis(xml) {
        return xml !== undefined ? xml.textContent : "Y_UP";
      }
  
      // library
  
      function parseLibrary(xml, libraryName, nodeName, parser) {
        var library = getElementsByTagName(xml, libraryName)[0];
  
        if (library !== undefined) {
          var elements = getElementsByTagName(library, nodeName);
  
          for (var i = 0; i < elements.length; i++) {
            parser(elements[i]);
          }
        }
      }
  
      function buildLibrary(data, builder) {
        for (var name in data) {
          var object = data[name];
          object.build = builder(data[name]);
        }
      }
  
      // get
  
      function getBuild(data, builder) {
        if (data.build !== undefined) return data.build;
  
        data.build = builder(data);
  
        return data.build;
      }
  
      // animation
  
      function parseAnimation(xml) {
        var data = {
          sources: {},
          samplers: {},
          channels: {},
        };
  
        var hasChildren = false;
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          var id;
  
          switch (child.nodeName) {
            case "source":
              id = child.getAttribute("id");
              data.sources[id] = parseSource(child);
              break;
  
            case "sampler":
              id = child.getAttribute("id");
              data.samplers[id] = parseAnimationSampler(child);
              break;
  
            case "channel":
              id = child.getAttribute("target");
              data.channels[id] = parseAnimationChannel(child);
              break;
  
            case "animation":
              // hierarchy of related animations
              parseAnimation(child);
              hasChildren = true;
              break;
  
            default:
              console.log(child);
          }
        }
  
        if (hasChildren === false) {
          // since 'id' attributes can be optional, it's necessary to generate a UUID for unqiue assignment
  
          library.animations[xml.getAttribute("id") || MathUtils.generateUUID()] =
            data;
        }
      }
  
      function parseAnimationSampler(xml) {
        var data = {
          inputs: {},
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "input":
              var id = parseId(child.getAttribute("source"));
              var semantic = child.getAttribute("semantic");
              data.inputs[semantic] = id;
              break;
          }
        }
  
        return data;
      }
  
      function parseAnimationChannel(xml) {
        var data = {};
  
        var target = xml.getAttribute("target");
  
        // parsing SID Addressing Syntax
  
        var parts = target.split("/");
  
        var id = parts.shift();
        var sid = parts.shift();
  
        // check selection syntax
  
        var arraySyntax = (sid.indexOf("(") !== -1);
        var memberSyntax = (sid.indexOf(".") !== -1);
  
        if (memberSyntax) {
          //  member selection access
  
          parts = sid.split(".");
          sid = parts.shift();
          data.member = parts.shift();
        } else if (arraySyntax) {
          // array-access syntax. can be used to express fields in one-dimensional vectors or two-dimensional matrices.
  
          var indices = sid.split("(");
          sid = indices.shift();
  
          for (var i = 0; i < indices.length; i++) {
            indices[i] = parseInt(indices[i].replace(/\)/, ""));
          }
  
          data.indices = indices;
        }
  
        data.id = id;
        data.sid = sid;
  
        data.arraySyntax = arraySyntax;
        data.memberSyntax = memberSyntax;
  
        data.sampler = parseId(xml.getAttribute("source"));
  
        return data;
      }
  
      function buildAnimation(data) {
        var tracks = [];
  
        var channels = data.channels;
        var samplers = data.samplers;
        var sources = data.sources;
  
        for (var target in channels) {
          if (channels.hasOwnProperty(target)) {
            var channel = channels[target];
            var sampler = samplers[channel.sampler];
  
            var inputId = sampler.inputs.INPUT;
            var outputId = sampler.inputs.OUTPUT;
  
            var inputSource = sources[inputId];
            var outputSource = sources[outputId];
  
            var animation = buildAnimationChannel(
              channel,
              inputSource,
              outputSource,
            );
  
            createKeyframeTracks(animation, tracks);
          }
        }
  
        return tracks;
      }
  
      function getAnimation(id) {
        return getBuild(library.animations[id], buildAnimation);
      }
  
      function buildAnimationChannel(channel, inputSource, outputSource) {
        var node = library.nodes[channel.id];
        var object3D = getNode(node.id);
  
        var transform = node.transforms[channel.sid];
        var defaultMatrix = node.matrix.clone().transpose();
  
        var time, stride;
        var i, il, j, jl;
  
        var data = {};
  
        // the collada spec allows the animation of data in various ways.
        // depending on the transform type (matrix, translate, rotate, scale), we execute different logic
  
        switch (transform) {
          case "matrix":
            for (i = 0, il = inputSource.array.length; i < il; i++) {
              time = inputSource.array[i];
              stride = i * outputSource.stride;
  
              if (data[time] === undefined) data[time] = {};
  
              if (channel.arraySyntax === true) {
                var value = outputSource.array[stride];
                var index = channel.indices[0] + 4 * channel.indices[1];
  
                data[time][index] = value;
              } else {
                for (j = 0, jl = outputSource.stride; j < jl; j++) {
                  data[time][j] = outputSource.array[stride + j];
                }
              }
            }
  
            break;
  
          case "translate":
            console.warn(
              'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.',
              transform,
            );
            break;
  
          case "rotate":
            console.warn(
              'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.',
              transform,
            );
            break;
  
          case "scale":
            console.warn(
              'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.',
              transform,
            );
            break;
        }
  
        var keyframes = prepareAnimationData(data, defaultMatrix);
  
        var animation = {
          name: object3D.uuid,
          keyframes: keyframes,
        };
  
        return animation;
      }
  
      function prepareAnimationData(data, defaultMatrix) {
        var keyframes = [];
  
        // transfer data into a sortable array
  
        for (var time in data) {
          keyframes.push({ time: parseFloat(time), value: data[time] });
        }
  
        // ensure keyframes are sorted by time
  
        keyframes.sort(ascending);
  
        // now we clean up all animation data, so we can use them for keyframe tracks
  
        for (var i = 0; i < 16; i++) {
          transformAnimationData(keyframes, i, defaultMatrix.elements[i]);
        }
  
        return keyframes;
  
        // array sort function
  
        function ascending(a, b) {
          return a.time - b.time;
        }
      }
  
      var position = new Vector3();
      var scale = new Vector3();
      var quaternion = new Quaternion();
  
      function createKeyframeTracks(animation, tracks) {
        var keyframes = animation.keyframes;
        var name = animation.name;
  
        var times = [];
        var positionData = [];
        var quaternionData = [];
        var scaleData = [];
  
        for (var i = 0, l = keyframes.length; i < l; i++) {
          var keyframe = keyframes[i];
  
          var time = keyframe.time;
          var value = keyframe.value;
  
          matrix.fromArray(value).transpose();
          matrix.decompose(position, quaternion, scale);
  
          times.push(time);
          positionData.push(position.x, position.y, position.z);
          quaternionData.push(
            quaternion.x,
            quaternion.y,
            quaternion.z,
            quaternion.w,
          );
          scaleData.push(scale.x, scale.y, scale.z);
        }
  
        if (positionData.length > 0) {
          tracks.push(
            new VectorKeyframeTrack(name + ".position", times, positionData),
          );
        }
        if (quaternionData.length > 0) {
          tracks.push(
            new QuaternionKeyframeTrack(
              name + ".quaternion",
              times,
              quaternionData,
            ),
          );
        }
        if (scaleData.length > 0) {
          tracks.push(new VectorKeyframeTrack(name + ".scale", times, scaleData));
        }
  
        return tracks;
      }
  
      function transformAnimationData(keyframes, property, defaultValue) {
        var keyframe;
  
        var empty = true;
        var i, l;
  
        // check, if values of a property are missing in our keyframes
  
        for (i = 0, l = keyframes.length; i < l; i++) {
          keyframe = keyframes[i];
  
          if (keyframe.value[property] === undefined) {
            keyframe.value[property] = null; // mark as missing
          } else {
            empty = false;
          }
        }
  
        if (empty === true) {
          // no values at all, so we set a default value
  
          for (i = 0, l = keyframes.length; i < l; i++) {
            keyframe = keyframes[i];
  
            keyframe.value[property] = defaultValue;
          }
        } else {
          // filling gaps
  
          createMissingKeyframes(keyframes, property);
        }
      }
  
      function createMissingKeyframes(keyframes, property) {
        var prev, next;
  
        for (var i = 0, l = keyframes.length; i < l; i++) {
          var keyframe = keyframes[i];
  
          if (keyframe.value[property] === null) {
            prev = getPrev(keyframes, i, property);
            next = getNext(keyframes, i, property);
  
            if (prev === null) {
              keyframe.value[property] = next.value[property];
              continue;
            }
  
            if (next === null) {
              keyframe.value[property] = prev.value[property];
              continue;
            }
  
            interpolate(keyframe, prev, next, property);
          }
        }
      }
  
      function getPrev(keyframes, i, property) {
        while (i >= 0) {
          var keyframe = keyframes[i];
  
          if (keyframe.value[property] !== null) return keyframe;
  
          i--;
        }
  
        return null;
      }
  
      function getNext(keyframes, i, property) {
        while (i < keyframes.length) {
          var keyframe = keyframes[i];
  
          if (keyframe.value[property] !== null) return keyframe;
  
          i++;
        }
  
        return null;
      }
  
      function interpolate(key, prev, next, property) {
        if ((next.time - prev.time) === 0) {
          key.value[property] = prev.value[property];
          return;
        }
  
        key.value[property] =
          ((key.time - prev.time) *
            (next.value[property] - prev.value[property]) /
            (next.time - prev.time)) + prev.value[property];
      }
  
      // animation clips
  
      function parseAnimationClip(xml) {
        var data = {
          name: xml.getAttribute("id") || "default",
          start: parseFloat(xml.getAttribute("start") || 0),
          end: parseFloat(xml.getAttribute("end") || 0),
          animations: [],
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "instance_animation":
              data.animations.push(parseId(child.getAttribute("url")));
              break;
          }
        }
  
        library.clips[xml.getAttribute("id")] = data;
      }
  
      function buildAnimationClip(data) {
        var tracks = [];
  
        var name = data.name;
        var duration = (data.end - data.start) || -1;
        var animations = data.animations;
  
        for (var i = 0, il = animations.length; i < il; i++) {
          var animationTracks = getAnimation(animations[i]);
  
          for (var j = 0, jl = animationTracks.length; j < jl; j++) {
            tracks.push(animationTracks[j]);
          }
        }
  
        return new AnimationClip(name, duration, tracks);
      }
  
      function getAnimationClip(id) {
        return getBuild(library.clips[id], buildAnimationClip);
      }
  
      // controller
  
      function parseController(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "skin":
              // there is exactly one skin per controller
              data.id = parseId(child.getAttribute("source"));
              data.skin = parseSkin(child);
              break;
  
            case "morph":
              data.id = parseId(child.getAttribute("source"));
              console.warn(
                "THREE.ColladaLoader: Morph target animation not supported yet.",
              );
              break;
          }
        }
  
        library.controllers[xml.getAttribute("id")] = data;
      }
  
      function parseSkin(xml) {
        var data = {
          sources: {},
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "bind_shape_matrix":
              data.bindShapeMatrix = parseFloats(child.textContent);
              break;
  
            case "source":
              var id = child.getAttribute("id");
              data.sources[id] = parseSource(child);
              break;
  
            case "joints":
              data.joints = parseJoints(child);
              break;
  
            case "vertex_weights":
              data.vertexWeights = parseVertexWeights(child);
              break;
          }
        }
  
        return data;
      }
  
      function parseJoints(xml) {
        var data = {
          inputs: {},
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "input":
              var semantic = child.getAttribute("semantic");
              var id = parseId(child.getAttribute("source"));
              data.inputs[semantic] = id;
              break;
          }
        }
  
        return data;
      }
  
      function parseVertexWeights(xml) {
        var data = {
          inputs: {},
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "input":
              var semantic = child.getAttribute("semantic");
              var id = parseId(child.getAttribute("source"));
              var offset = parseInt(child.getAttribute("offset"));
              data.inputs[semantic] = { id: id, offset: offset };
              break;
  
            case "vcount":
              data.vcount = parseInts(child.textContent);
              break;
  
            case "v":
              data.v = parseInts(child.textContent);
              break;
          }
        }
  
        return data;
      }
  
      function buildController(data) {
        var build = {
          id: data.id,
        };
  
        var geometry = library.geometries[build.id];
  
        if (data.skin !== undefined) {
          build.skin = buildSkin(data.skin);
  
          // we enhance the 'sources' property of the corresponding geometry with our skin data
  
          geometry.sources.skinIndices = build.skin.indices;
          geometry.sources.skinWeights = build.skin.weights;
        }
  
        return build;
      }
  
      function buildSkin(data) {
        var BONE_LIMIT = 4;
  
        var build = {
          joints: [], // this must be an array to preserve the joint order
          indices: {
            array: [],
            stride: BONE_LIMIT,
          },
          weights: {
            array: [],
            stride: BONE_LIMIT,
          },
        };
  
        var sources = data.sources;
        var vertexWeights = data.vertexWeights;
  
        var vcount = vertexWeights.vcount;
        var v = vertexWeights.v;
        var jointOffset = vertexWeights.inputs.JOINT.offset;
        var weightOffset = vertexWeights.inputs.WEIGHT.offset;
  
        var jointSource = data.sources[data.joints.inputs.JOINT];
        var inverseSource = data.sources[data.joints.inputs.INV_BIND_MATRIX];
  
        var weights = sources[vertexWeights.inputs.WEIGHT.id].array;
        var stride = 0;
  
        var i, j, l;
  
        // procces skin data for each vertex
  
        for (i = 0, l = vcount.length; i < l; i++) {
          var jointCount = vcount[i]; // this is the amount of joints that affect a single vertex
          var vertexSkinData = [];
  
          for (j = 0; j < jointCount; j++) {
            var skinIndex = v[stride + jointOffset];
            var weightId = v[stride + weightOffset];
            var skinWeight = weights[weightId];
  
            vertexSkinData.push({ index: skinIndex, weight: skinWeight });
  
            stride += 2;
          }
  
          // we sort the joints in descending order based on the weights.
          // this ensures, we only procced the most important joints of the vertex
  
          vertexSkinData.sort(descending);
  
          // now we provide for each vertex a set of four index and weight values.
          // the order of the skin data matches the order of vertices
  
          for (j = 0; j < BONE_LIMIT; j++) {
            var d = vertexSkinData[j];
  
            if (d !== undefined) {
              build.indices.array.push(d.index);
              build.weights.array.push(d.weight);
            } else {
              build.indices.array.push(0);
              build.weights.array.push(0);
            }
          }
        }
  
        // setup bind matrix
  
        if (data.bindShapeMatrix) {
          build.bindMatrix = new Matrix4().fromArray(data.bindShapeMatrix)
            .transpose();
        } else {
          build.bindMatrix = new Matrix4().identity();
        }
  
        // process bones and inverse bind matrix data
  
        for (i = 0, l = jointSource.array.length; i < l; i++) {
          var name = jointSource.array[i];
          var boneInverse = new Matrix4().fromArray(
            inverseSource.array,
            i * inverseSource.stride,
          ).transpose();
  
          build.joints.push({ name: name, boneInverse: boneInverse });
        }
  
        return build;
  
        // array sort function
  
        function descending(a, b) {
          return b.weight - a.weight;
        }
      }
  
      function getController(id) {
        return getBuild(library.controllers[id], buildController);
      }
  
      // image
  
      function parseImage(xml) {
        var data = {
          init_from: getElementsByTagName(xml, "init_from")[0].textContent,
        };
  
        library.images[xml.getAttribute("id")] = data;
      }
  
      function buildImage(data) {
        if (data.build !== undefined) return data.build;
  
        return data.init_from;
      }
  
      function getImage(id) {
        var data = library.images[id];
  
        if (data !== undefined) {
          return getBuild(data, buildImage);
        }
  
        console.warn("THREE.ColladaLoader: Couldn't find image with ID:", id);
  
        return null;
      }
  
      // effect
  
      function parseEffect(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "profile_COMMON":
              data.profile = parseEffectProfileCOMMON(child);
              break;
          }
        }
  
        library.effects[xml.getAttribute("id")] = data;
      }
  
      function parseEffectProfileCOMMON(xml) {
        var data = {
          surfaces: {},
          samplers: {},
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "newparam":
              parseEffectNewparam(child, data);
              break;
  
            case "technique":
              data.technique = parseEffectTechnique(child);
              break;
  
            case "extra":
              data.extra = parseEffectExtra(child);
              break;
          }
        }
  
        return data;
      }
  
      function parseEffectNewparam(xml, data) {
        var sid = xml.getAttribute("sid");
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "surface":
              data.surfaces[sid] = parseEffectSurface(child);
              break;
  
            case "sampler2D":
              data.samplers[sid] = parseEffectSampler(child);
              break;
          }
        }
      }
  
      function parseEffectSurface(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "init_from":
              data.init_from = child.textContent;
              break;
          }
        }
  
        return data;
      }
  
      function parseEffectSampler(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "source":
              data.source = child.textContent;
              break;
          }
        }
  
        return data;
      }
  
      function parseEffectTechnique(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "constant":
            case "lambert":
            case "blinn":
            case "phong":
              data.type = child.nodeName;
              data.parameters = parseEffectParameters(child);
              break;
          }
        }
  
        return data;
      }
  
      function parseEffectParameters(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "emission":
            case "diffuse":
            case "specular":
            case "bump":
            case "ambient":
            case "shininess":
            case "transparency":
              data[child.nodeName] = parseEffectParameter(child);
              break;
            case "transparent":
              data[child.nodeName] = {
                opaque: child.getAttribute("opaque"),
                data: parseEffectParameter(child),
              };
              break;
          }
        }
  
        return data;
      }
  
      function parseEffectParameter(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "color":
              data[child.nodeName] = parseFloats(child.textContent);
              break;
  
            case "float":
              data[child.nodeName] = parseFloat(child.textContent);
              break;
  
            case "texture":
              data[child.nodeName] = {
                id: child.getAttribute("texture"),
                extra: parseEffectParameterTexture(child),
              };
              break;
          }
        }
  
        return data;
      }
  
      function parseEffectParameterTexture(xml) {
        var data = {
          technique: {},
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "extra":
              parseEffectParameterTextureExtra(child, data);
              break;
          }
        }
  
        return data;
      }
  
      function parseEffectParameterTextureExtra(xml, data) {
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "technique":
              parseEffectParameterTextureExtraTechnique(child, data);
              break;
          }
        }
      }
  
      function parseEffectParameterTextureExtraTechnique(xml, data) {
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "repeatU":
            case "repeatV":
            case "offsetU":
            case "offsetV":
              data.technique[child.nodeName] = parseFloat(child.textContent);
              break;
  
            case "wrapU":
            case "wrapV":
              // some files have values for wrapU/wrapV which become NaN via parseInt
  
              if (child.textContent.toUpperCase() === "TRUE") {
                data.technique[child.nodeName] = 1;
              } else if (child.textContent.toUpperCase() === "FALSE") {
                data.technique[child.nodeName] = 0;
              } else {
                data.technique[child.nodeName] = parseInt(child.textContent);
              }
  
              break;
          }
        }
      }
  
      function parseEffectExtra(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "technique":
              data.technique = parseEffectExtraTechnique(child);
              break;
          }
        }
  
        return data;
      }
  
      function parseEffectExtraTechnique(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "double_sided":
              data[child.nodeName] = parseInt(child.textContent);
              break;
          }
        }
  
        return data;
      }
  
      function buildEffect(data) {
        return data;
      }
  
      function getEffect(id) {
        return getBuild(library.effects[id], buildEffect);
      }
  
      // material
  
      function parseMaterial(xml) {
        var data = {
          name: xml.getAttribute("name"),
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "instance_effect":
              data.url = parseId(child.getAttribute("url"));
              break;
          }
        }
  
        library.materials[xml.getAttribute("id")] = data;
      }
  
      function getTextureLoader(image) {
        var loader;
  
        var extension = image.slice((image.lastIndexOf(".") - 1 >>> 0) + 2); // http://www.jstips.co/en/javascript/get-file-extension/
        extension = extension.toLowerCase();
  
        switch (extension) {
          case "tga":
            loader = tgaLoader;
            break;
  
          default:
            loader = textureLoader;
        }
  
        return loader;
      }
  
      function buildMaterial(data) {
        var effect = getEffect(data.url);
        var technique = effect.profile.technique;
        var extra = effect.profile.extra;
  
        var material;
  
        switch (technique.type) {
          case "phong":
          case "blinn":
            material = new MeshPhongMaterial();
            break;
  
          case "lambert":
            material = new MeshLambertMaterial();
            break;
  
          default:
            material = new MeshBasicMaterial();
            break;
        }
  
        material.name = data.name || "";
  
        function getTexture(textureObject) {
          var sampler = effect.profile.samplers[textureObject.id];
          var image = null;
  
          // get image
  
          if (sampler !== undefined) {
            var surface = effect.profile.surfaces[sampler.source];
            image = getImage(surface.init_from);
          } else {
            console.warn(
              "THREE.ColladaLoader: Undefined sampler. Access image directly (see #12530).",
            );
            image = getImage(textureObject.id);
          }
  
          // create texture if image is avaiable
  
          if (image !== null) {
            var loader = getTextureLoader(image);
  
            if (loader !== undefined) {
              var texture = loader.load(image);
  
              var extra = textureObject.extra;
  
              if (
                extra !== undefined && extra.technique !== undefined &&
                isEmpty(extra.technique) === false
              ) {
                var technique = extra.technique;
  
                texture.wrapS = technique.wrapU
                  ? RepeatWrapping
                  : ClampToEdgeWrapping;
                texture.wrapT = technique.wrapV
                  ? RepeatWrapping
                  : ClampToEdgeWrapping;
  
                texture.offset.set(
                  technique.offsetU || 0,
                  technique.offsetV || 0,
                );
                texture.repeat.set(
                  technique.repeatU || 1,
                  technique.repeatV || 1,
                );
              } else {
                texture.wrapS = RepeatWrapping;
                texture.wrapT = RepeatWrapping;
              }
  
              return texture;
            } else {
              console.warn(
                "THREE.ColladaLoader: Loader for texture %s not found.",
                image,
              );
  
              return null;
            }
          } else {
            console.warn(
              "THREE.ColladaLoader: Couldn't create texture with ID:",
              textureObject.id,
            );
  
            return null;
          }
        }
  
        var parameters = technique.parameters;
  
        for (var key in parameters) {
          var parameter = parameters[key];
  
          switch (key) {
            case "diffuse":
              if (parameter.color) material.color.fromArray(parameter.color);
              if (parameter.texture) material.map = getTexture(parameter.texture);
              break;
            case "specular":
              if (parameter.color && material.specular) {
                material.specular.fromArray(parameter.color);
              }
              if (parameter.texture) {
                material.specularMap = getTexture(parameter.texture);
              }
              break;
            case "bump":
              if (parameter.texture) {
                material.normalMap = getTexture(parameter.texture);
              }
              break;
            case "ambient":
              if (parameter.texture) {
                material.lightMap = getTexture(parameter.texture);
              }
              break;
            case "shininess":
              if (parameter.float && material.shininess) {
                material.shininess = parameter.float;
              }
              break;
            case "emission":
              if (parameter.color && material.emissive) {
                material.emissive.fromArray(parameter.color);
              }
              if (parameter.texture) {
                material.emissiveMap = getTexture(parameter.texture);
              }
              break;
          }
        }
  
        //
  
        var transparent = parameters["transparent"];
        var transparency = parameters["transparency"];
  
        // <transparency> does not exist but <transparent>
  
        if (transparency === undefined && transparent) {
          transparency = {
            float: 1,
          };
        }
  
        // <transparent> does not exist but <transparency>
  
        if (transparent === undefined && transparency) {
          transparent = {
            opaque: "A_ONE",
            data: {
              color: [1, 1, 1, 1],
            },
          };
        }
  
        if (transparent && transparency) {
          // handle case if a texture exists but no color
  
          if (transparent.data.texture) {
            // we do not set an alpha map (see #13792)
  
            material.transparent = true;
          } else {
            var color = transparent.data.color;
  
            switch (transparent.opaque) {
              case "A_ONE":
                material.opacity = color[3] * transparency.float;
                break;
              case "RGB_ZERO":
                material.opacity = 1 - (color[0] * transparency.float);
                break;
              case "A_ZERO":
                material.opacity = 1 - (color[3] * transparency.float);
                break;
              case "RGB_ONE":
                material.opacity = color[0] * transparency.float;
                break;
              default:
                console.warn(
                  'THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.',
                  transparent.opaque,
                );
            }
  
            if (material.opacity < 1) material.transparent = true;
          }
        }
  
        //
  
        if (
          extra !== undefined && extra.technique !== undefined &&
          extra.technique.double_sided === 1
        ) {
          material.side = DoubleSide;
        }
  
        return material;
      }
  
      function getMaterial(id) {
        return getBuild(library.materials[id], buildMaterial);
      }
  
      // camera
  
      function parseCamera(xml) {
        var data = {
          name: xml.getAttribute("name"),
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "optics":
              data.optics = parseCameraOptics(child);
              break;
          }
        }
  
        library.cameras[xml.getAttribute("id")] = data;
      }
  
      function parseCameraOptics(xml) {
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          switch (child.nodeName) {
            case "technique_common":
              return parseCameraTechnique(child);
          }
        }
  
        return {};
      }
  
      function parseCameraTechnique(xml) {
        var data = {};
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          switch (child.nodeName) {
            case "perspective":
            case "orthographic":
              data.technique = child.nodeName;
              data.parameters = parseCameraParameters(child);
  
              break;
          }
        }
  
        return data;
      }
  
      function parseCameraParameters(xml) {
        var data = {};
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          switch (child.nodeName) {
            case "xfov":
            case "yfov":
            case "xmag":
            case "ymag":
            case "znear":
            case "zfar":
            case "aspect_ratio":
              data[child.nodeName] = parseFloat(child.textContent);
              break;
          }
        }
  
        return data;
      }
  
      function buildCamera(data) {
        var camera;
  
        switch (data.optics.technique) {
          case "perspective":
            camera = new PerspectiveCamera(
              data.optics.parameters.yfov,
              data.optics.parameters.aspect_ratio,
              data.optics.parameters.znear,
              data.optics.parameters.zfar,
            );
            break;
  
          case "orthographic":
            var ymag = data.optics.parameters.ymag;
            var xmag = data.optics.parameters.xmag;
            var aspectRatio = data.optics.parameters.aspect_ratio;
  
            xmag = (xmag === undefined) ? (ymag * aspectRatio) : xmag;
            ymag = (ymag === undefined) ? (xmag / aspectRatio) : ymag;
  
            xmag *= 0.5;
            ymag *= 0.5;
  
            camera = new OrthographicCamera(
              -xmag,
              xmag,
              ymag,
              -ymag, // left, right, top, bottom
              data.optics.parameters.znear,
              data.optics.parameters.zfar,
            );
            break;
  
          default:
            camera = new PerspectiveCamera();
            break;
        }
  
        camera.name = data.name || "";
  
        return camera;
      }
  
      function getCamera(id) {
        var data = library.cameras[id];
  
        if (data !== undefined) {
          return getBuild(data, buildCamera);
        }
  
        console.warn("THREE.ColladaLoader: Couldn't find camera with ID:", id);
  
        return null;
      }
  
      // light
  
      function parseLight(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "technique_common":
              data = parseLightTechnique(child);
              break;
          }
        }
  
        library.lights[xml.getAttribute("id")] = data;
      }
  
      function parseLightTechnique(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "directional":
            case "point":
            case "spot":
            case "ambient":
              data.technique = child.nodeName;
              data.parameters = parseLightParameters(child);
          }
        }
  
        return data;
      }
  
      function parseLightParameters(xml) {
        var data = {};
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "color":
              var array = parseFloats(child.textContent);
              data.color = new Color().fromArray(array);
              break;
  
            case "falloff_angle":
              data.falloffAngle = parseFloat(child.textContent);
              break;
  
            case "quadratic_attenuation":
              var f = parseFloat(child.textContent);
              data.distance = f ? Math.sqrt(1 / f) : 0;
              break;
          }
        }
  
        return data;
      }
  
      function buildLight(data) {
        var light;
  
        switch (data.technique) {
          case "directional":
            light = new DirectionalLight();
            break;
  
          case "point":
            light = new PointLight();
            break;
  
          case "spot":
            light = new SpotLight();
            break;
  
          case "ambient":
            light = new AmbientLight();
            break;
        }
  
        if (data.parameters.color) light.color.copy(data.parameters.color);
        if (data.parameters.distance) light.distance = data.parameters.distance;
  
        return light;
      }
  
      function getLight(id) {
        var data = library.lights[id];
  
        if (data !== undefined) {
          return getBuild(data, buildLight);
        }
  
        console.warn("THREE.ColladaLoader: Couldn't find light with ID:", id);
  
        return null;
      }
  
      // geometry
  
      function parseGeometry(xml) {
        var data = {
          name: xml.getAttribute("name"),
          sources: {},
          vertices: {},
          primitives: [],
        };
  
        var mesh = getElementsByTagName(xml, "mesh")[0];
  
        // the following tags inside geometry are not supported yet (see https://github.com/mrdoob/three.js/pull/12606): convex_mesh, spline, brep
        if (mesh === undefined) return;
  
        for (var i = 0; i < mesh.childNodes.length; i++) {
          var child = mesh.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          var id = child.getAttribute("id");
  
          switch (child.nodeName) {
            case "source":
              data.sources[id] = parseSource(child);
              break;
  
            case "vertices":
              // data.sources[ id ] = data.sources[ parseId( getElementsByTagName( child, 'input' )[ 0 ].getAttribute( 'source' ) ) ];
              data.vertices = parseGeometryVertices(child);
              break;
  
            case "polygons":
              console.warn(
                "THREE.ColladaLoader: Unsupported primitive type: ",
                child.nodeName,
              );
              break;
  
            case "lines":
            case "linestrips":
            case "polylist":
            case "triangles":
              data.primitives.push(parseGeometryPrimitive(child));
              break;
  
            default:
              console.log(child);
          }
        }
  
        library.geometries[xml.getAttribute("id")] = data;
      }
  
      function parseSource(xml) {
        var data = {
          array: [],
          stride: 3,
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "float_array":
              data.array = parseFloats(child.textContent);
              break;
  
            case "Name_array":
              data.array = parseStrings(child.textContent);
              break;
  
            case "technique_common":
              var accessor = getElementsByTagName(child, "accessor")[0];
  
              if (accessor !== undefined) {
                data.stride = parseInt(accessor.getAttribute("stride"));
              }
  
              break;
          }
        }
  
        return data;
      }
  
      function parseGeometryVertices(xml) {
        var data = {};
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          data[child.getAttribute("semantic")] = parseId(
            child.getAttribute("source"),
          );
        }
  
        return data;
      }
  
      function parseGeometryPrimitive(xml) {
        var primitive = {
          type: xml.nodeName,
          material: xml.getAttribute("material"),
          count: parseInt(xml.getAttribute("count")),
          inputs: {},
          stride: 0,
          hasUV: false,
        };
  
        for (var i = 0, l = xml.childNodes.length; i < l; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "input":
              var id = parseId(child.getAttribute("source"));
              var semantic = child.getAttribute("semantic");
              var offset = parseInt(child.getAttribute("offset"));
              var set = parseInt(child.getAttribute("set"));
              var inputname = (set > 0 ? semantic + set : semantic);
              primitive.inputs[inputname] = { id: id, offset: offset };
              primitive.stride = Math.max(primitive.stride, offset + 1);
              if (semantic === "TEXCOORD") primitive.hasUV = true;
              break;
  
            case "vcount":
              primitive.vcount = parseInts(child.textContent);
              break;
  
            case "p":
              primitive.p = parseInts(child.textContent);
              break;
          }
        }
  
        return primitive;
      }
  
      function groupPrimitives(primitives) {
        var build = {};
  
        for (var i = 0; i < primitives.length; i++) {
          var primitive = primitives[i];
  
          if (build[primitive.type] === undefined) build[primitive.type] = [];
  
          build[primitive.type].push(primitive);
        }
  
        return build;
      }
  
      function checkUVCoordinates(primitives) {
        var count = 0;
  
        for (var i = 0, l = primitives.length; i < l; i++) {
          var primitive = primitives[i];
  
          if (primitive.hasUV === true) {
            count++;
          }
        }
  
        if (count > 0 && count < primitives.length) {
          primitives.uvsNeedsFix = true;
        }
      }
  
      function buildGeometry(data) {
        var build = {};
  
        var sources = data.sources;
        var vertices = data.vertices;
        var primitives = data.primitives;
  
        if (primitives.length === 0) return {};
  
        // our goal is to create one buffer geometry for a single type of primitives
        // first, we group all primitives by their type
  
        var groupedPrimitives = groupPrimitives(primitives);
  
        for (var type in groupedPrimitives) {
          var primitiveType = groupedPrimitives[type];
  
          // second, ensure consistent uv coordinates for each type of primitives (polylist,triangles or lines)
  
          checkUVCoordinates(primitiveType);
  
          // third, create a buffer geometry for each type of primitives
  
          build[type] = buildGeometryType(primitiveType, sources, vertices);
        }
  
        return build;
      }
  
      function buildGeometryType(primitives, sources, vertices) {
        var build = {};
  
        var position = { array: [], stride: 0 };
        var normal = { array: [], stride: 0 };
        var uv = { array: [], stride: 0 };
        var uv2 = { array: [], stride: 0 };
        var color = { array: [], stride: 0 };
  
        var skinIndex = { array: [], stride: 4 };
        var skinWeight = { array: [], stride: 4 };
  
        var geometry = new BufferGeometry();
  
        var materialKeys = [];
  
        var start = 0;
  
        for (var p = 0; p < primitives.length; p++) {
          var primitive = primitives[p];
          var inputs = primitive.inputs;
  
          // groups
  
          var count = 0;
  
          switch (primitive.type) {
            case "lines":
            case "linestrips":
              count = primitive.count * 2;
              break;
  
            case "triangles":
              count = primitive.count * 3;
              break;
  
            case "polylist":
              for (var g = 0; g < primitive.count; g++) {
                var vc = primitive.vcount[g];
  
                switch (vc) {
                  case 3:
                    count += 3; // single triangle
                    break;
  
                  case 4:
                    count += 6; // quad, subdivided into two triangles
                    break;
  
                  default:
                    count += (vc - 2) * 3; // polylist with more than four vertices
                    break;
                }
              }
  
              break;
  
            default:
              console.warn(
                "THREE.ColladaLoader: Unknow primitive type:",
                primitive.type,
              );
          }
  
          geometry.addGroup(start, count, p);
          start += count;
  
          // material
  
          if (primitive.material) {
            materialKeys.push(primitive.material);
          }
  
          // geometry data
  
          for (var name in inputs) {
            var input = inputs[name];
  
            switch (name) {
              case "VERTEX":
                for (var key in vertices) {
                  var id = vertices[key];
  
                  switch (key) {
                    case "POSITION":
                      var prevLength = position.array.length;
                      buildGeometryData(
                        primitive,
                        sources[id],
                        input.offset,
                        position.array,
                      );
                      position.stride = sources[id].stride;
  
                      if (sources.skinWeights && sources.skinIndices) {
                        buildGeometryData(
                          primitive,
                          sources.skinIndices,
                          input.offset,
                          skinIndex.array,
                        );
                        buildGeometryData(
                          primitive,
                          sources.skinWeights,
                          input.offset,
                          skinWeight.array,
                        );
                      }
  
                      // see #3803
  
                      if (
                        primitive.hasUV === false &&
                        primitives.uvsNeedsFix === true
                      ) {
                        var count = (position.array.length - prevLength) /
                          position.stride;
  
                        for (var i = 0; i < count; i++) {
                          // fill missing uv coordinates
  
                          uv.array.push(0, 0);
                        }
                      }
  
                      break;
  
                    case "NORMAL":
                      buildGeometryData(
                        primitive,
                        sources[id],
                        input.offset,
                        normal.array,
                      );
                      normal.stride = sources[id].stride;
                      break;
  
                    case "COLOR":
                      buildGeometryData(
                        primitive,
                        sources[id],
                        input.offset,
                        color.array,
                      );
                      color.stride = sources[id].stride;
                      break;
  
                    case "TEXCOORD":
                      buildGeometryData(
                        primitive,
                        sources[id],
                        input.offset,
                        uv.array,
                      );
                      uv.stride = sources[id].stride;
                      break;
  
                    case "TEXCOORD1":
                      buildGeometryData(
                        primitive,
                        sources[id],
                        input.offset,
                        uv2.array,
                      );
                      uv.stride = sources[id].stride;
                      break;
  
                    default:
                      console.warn(
                        'THREE.ColladaLoader: Semantic "%s" not handled in geometry build process.',
                        key,
                      );
                  }
                }
  
                break;
  
              case "NORMAL":
                buildGeometryData(
                  primitive,
                  sources[input.id],
                  input.offset,
                  normal.array,
                );
                normal.stride = sources[input.id].stride;
                break;
  
              case "COLOR":
                buildGeometryData(
                  primitive,
                  sources[input.id],
                  input.offset,
                  color.array,
                );
                color.stride = sources[input.id].stride;
                break;
  
              case "TEXCOORD":
                buildGeometryData(
                  primitive,
                  sources[input.id],
                  input.offset,
                  uv.array,
                );
                uv.stride = sources[input.id].stride;
                break;
  
              case "TEXCOORD1":
                buildGeometryData(
                  primitive,
                  sources[input.id],
                  input.offset,
                  uv2.array,
                );
                uv2.stride = sources[input.id].stride;
                break;
            }
          }
        }
  
        // build geometry
  
        if (position.array.length > 0) {
          geometry.setAttribute(
            "position",
            new Float32BufferAttribute(position.array, position.stride),
          );
        }
        if (normal.array.length > 0) {
          geometry.setAttribute(
            "normal",
            new Float32BufferAttribute(normal.array, normal.stride),
          );
        }
        if (color.array.length > 0) {
          geometry.setAttribute(
            "color",
            new Float32BufferAttribute(color.array, color.stride),
          );
        }
        if (uv.array.length > 0) {
          geometry.setAttribute(
            "uv",
            new Float32BufferAttribute(uv.array, uv.stride),
          );
        }
        if (uv2.array.length > 0) {
          geometry.setAttribute(
            "uv2",
            new Float32BufferAttribute(uv2.array, uv2.stride),
          );
        }
  
        if (skinIndex.array.length > 0) {
          geometry.setAttribute(
            "skinIndex",
            new Float32BufferAttribute(skinIndex.array, skinIndex.stride),
          );
        }
        if (skinWeight.array.length > 0) {
          geometry.setAttribute(
            "skinWeight",
            new Float32BufferAttribute(skinWeight.array, skinWeight.stride),
          );
        }
  
        build.data = geometry;
        build.type = primitives[0].type;
        build.materialKeys = materialKeys;
  
        return build;
      }
  
      function buildGeometryData(primitive, source, offset, array) {
        var indices = primitive.p;
        var stride = primitive.stride;
        var vcount = primitive.vcount;
  
        function pushVector(i) {
          var index = indices[i + offset] * sourceStride;
          var length = index + sourceStride;
  
          for (; index < length; index++) {
            array.push(sourceArray[index]);
          }
        }
  
        var sourceArray = source.array;
        var sourceStride = source.stride;
  
        if (primitive.vcount !== undefined) {
          var index = 0;
  
          for (var i = 0, l = vcount.length; i < l; i++) {
            var count = vcount[i];
  
            if (count === 4) {
              var a = index + stride * 0;
              var b = index + stride * 1;
              var c = index + stride * 2;
              var d = index + stride * 3;
  
              pushVector(a);
              pushVector(b);
              pushVector(d);
              pushVector(b);
              pushVector(c);
              pushVector(d);
            } else if (count === 3) {
              var a = index + stride * 0;
              var b = index + stride * 1;
              var c = index + stride * 2;
  
              pushVector(a);
              pushVector(b);
              pushVector(c);
            } else if (count > 4) {
              for (var k = 1, kl = (count - 2); k <= kl; k++) {
                var a = index + stride * 0;
                var b = index + stride * k;
                var c = index + stride * (k + 1);
  
                pushVector(a);
                pushVector(b);
                pushVector(c);
              }
            }
  
            index += stride * count;
          }
        } else {
          for (var i = 0, l = indices.length; i < l; i += stride) {
            pushVector(i);
          }
        }
      }
  
      function getGeometry(id) {
        return getBuild(library.geometries[id], buildGeometry);
      }
  
      // kinematics
  
      function parseKinematicsModel(xml) {
        var data = {
          name: xml.getAttribute("name") || "",
          joints: {},
          links: [],
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "technique_common":
              parseKinematicsTechniqueCommon(child, data);
              break;
          }
        }
  
        library.kinematicsModels[xml.getAttribute("id")] = data;
      }
  
      function buildKinematicsModel(data) {
        if (data.build !== undefined) return data.build;
  
        return data;
      }
  
      function getKinematicsModel(id) {
        return getBuild(library.kinematicsModels[id], buildKinematicsModel);
      }
  
      function parseKinematicsTechniqueCommon(xml, data) {
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "joint":
              data.joints[child.getAttribute("sid")] = parseKinematicsJoint(
                child,
              );
              break;
  
            case "link":
              data.links.push(parseKinematicsLink(child));
              break;
          }
        }
      }
  
      function parseKinematicsJoint(xml) {
        var data;
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "prismatic":
            case "revolute":
              data = parseKinematicsJointParameter(child);
              break;
          }
        }
  
        return data;
      }
  
      function parseKinematicsJointParameter(xml, data) {
        var data = {
          sid: xml.getAttribute("sid"),
          name: xml.getAttribute("name") || "",
          axis: new Vector3(),
          limits: {
            min: 0,
            max: 0,
          },
          type: xml.nodeName,
          static: false,
          zeroPosition: 0,
          middlePosition: 0,
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "axis":
              var array = parseFloats(child.textContent);
              data.axis.fromArray(array);
              break;
            case "limits":
              var max = child.getElementsByTagName("max")[0];
              var min = child.getElementsByTagName("min")[0];
  
              data.limits.max = parseFloat(max.textContent);
              data.limits.min = parseFloat(min.textContent);
              break;
          }
        }
  
        // if min is equal to or greater than max, consider the joint static
  
        if (data.limits.min >= data.limits.max) {
          data.static = true;
        }
  
        // calculate middle position
  
        data.middlePosition = (data.limits.min + data.limits.max) / 2.0;
  
        return data;
      }
  
      function parseKinematicsLink(xml) {
        var data = {
          sid: xml.getAttribute("sid"),
          name: xml.getAttribute("name") || "",
          attachments: [],
          transforms: [],
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "attachment_full":
              data.attachments.push(parseKinematicsAttachment(child));
              break;
  
            case "matrix":
            case "translate":
            case "rotate":
              data.transforms.push(parseKinematicsTransform(child));
              break;
          }
        }
  
        return data;
      }
  
      function parseKinematicsAttachment(xml) {
        var data = {
          joint: xml.getAttribute("joint").split("/").pop(),
          transforms: [],
          links: [],
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "link":
              data.links.push(parseKinematicsLink(child));
              break;
  
            case "matrix":
            case "translate":
            case "rotate":
              data.transforms.push(parseKinematicsTransform(child));
              break;
          }
        }
  
        return data;
      }
  
      function parseKinematicsTransform(xml) {
        var data = {
          type: xml.nodeName,
        };
  
        var array = parseFloats(xml.textContent);
  
        switch (data.type) {
          case "matrix":
            data.obj = new Matrix4();
            data.obj.fromArray(array).transpose();
            break;
  
          case "translate":
            data.obj = new Vector3();
            data.obj.fromArray(array);
            break;
  
          case "rotate":
            data.obj = new Vector3();
            data.obj.fromArray(array);
            data.angle = MathUtils.degToRad(array[3]);
            break;
        }
  
        return data;
      }
  
      // physics
  
      function parsePhysicsModel(xml) {
        var data = {
          name: xml.getAttribute("name") || "",
          rigidBodies: {},
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "rigid_body":
              data.rigidBodies[child.getAttribute("name")] = {};
              parsePhysicsRigidBody(
                child,
                data.rigidBodies[child.getAttribute("name")],
              );
              break;
          }
        }
  
        library.physicsModels[xml.getAttribute("id")] = data;
      }
  
      function parsePhysicsRigidBody(xml, data) {
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "technique_common":
              parsePhysicsTechniqueCommon(child, data);
              break;
          }
        }
      }
  
      function parsePhysicsTechniqueCommon(xml, data) {
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "inertia":
              data.inertia = parseFloats(child.textContent);
              break;
  
            case "mass":
              data.mass = parseFloats(child.textContent)[0];
              break;
          }
        }
      }
  
      // scene
  
      function parseKinematicsScene(xml) {
        var data = {
          bindJointAxis: [],
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "bind_joint_axis":
              data.bindJointAxis.push(parseKinematicsBindJointAxis(child));
              break;
          }
        }
  
        library.kinematicsScenes[parseId(xml.getAttribute("url"))] = data;
      }
  
      function parseKinematicsBindJointAxis(xml) {
        var data = {
          target: xml.getAttribute("target").split("/").pop(),
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "axis":
              var param = child.getElementsByTagName("param")[0];
              data.axis = param.textContent;
              var tmpJointIndex = data.axis.split("inst_").pop().split("axis")[0];
              data.jointIndex = tmpJointIndex.substr(0, tmpJointIndex.length - 1);
              break;
          }
        }
  
        return data;
      }
  
      function buildKinematicsScene(data) {
        if (data.build !== undefined) return data.build;
  
        return data;
      }
  
      function getKinematicsScene(id) {
        return getBuild(library.kinematicsScenes[id], buildKinematicsScene);
      }
  
      function setupKinematics() {
        var kinematicsModelId = Object.keys(library.kinematicsModels)[0];
        var kinematicsSceneId = Object.keys(library.kinematicsScenes)[0];
        var visualSceneId = Object.keys(library.visualScenes)[0];
  
        if (
          kinematicsModelId === undefined || kinematicsSceneId === undefined
        ) {
          return;
        }
  
        var kinematicsModel = getKinematicsModel(kinematicsModelId);
        var kinematicsScene = getKinematicsScene(kinematicsSceneId);
        var visualScene = getVisualScene(visualSceneId);
  
        var bindJointAxis = kinematicsScene.bindJointAxis;
        var jointMap = {};
  
        for (var i = 0, l = bindJointAxis.length; i < l; i++) {
          var axis = bindJointAxis[i];
  
          // the result of the following query is an element of type 'translate', 'rotate','scale' or 'matrix'
  
          var targetElement = collada.querySelector(
            '[sid="' + axis.target + '"]',
          );
  
          if (targetElement) {
            // get the parent of the transform element
  
            var parentVisualElement = targetElement.parentElement;
  
            // connect the joint of the kinematics model with the element in the visual scene
  
            connect(axis.jointIndex, parentVisualElement);
          }
        }
  
        function connect(jointIndex, visualElement) {
          var visualElementName = visualElement.getAttribute("name");
          var joint = kinematicsModel.joints[jointIndex];
  
          visualScene.traverse(function (object) {
            if (object.name === visualElementName) {
              jointMap[jointIndex] = {
                object: object,
                transforms: buildTransformList(visualElement),
                joint: joint,
                position: joint.zeroPosition,
              };
            }
          });
        }
  
        var m0 = new Matrix4();
  
        kinematics = {
          joints: kinematicsModel && kinematicsModel.joints,
  
          getJointValue: function (jointIndex) {
            var jointData = jointMap[jointIndex];
  
            if (jointData) {
              return jointData.position;
            } else {
              console.warn(
                "THREE.ColladaLoader: Joint " + jointIndex + " doesn't exist.",
              );
            }
          },
  
          setJointValue: function (jointIndex, value) {
            var jointData = jointMap[jointIndex];
  
            if (jointData) {
              var joint = jointData.joint;
  
              if (value > joint.limits.max || value < joint.limits.min) {
                console.warn(
                  "THREE.ColladaLoader: Joint " + jointIndex + " value " + value +
                    " outside of limits (min: " + joint.limits.min + ", max: " +
                    joint.limits.max + ").",
                );
              } else if (joint.static) {
                console.warn(
                  "THREE.ColladaLoader: Joint " + jointIndex + " is static.",
                );
              } else {
                var object = jointData.object;
                var axis = joint.axis;
                var transforms = jointData.transforms;
  
                matrix.identity();
  
                // each update, we have to apply all transforms in the correct order
  
                for (var i = 0; i < transforms.length; i++) {
                  var transform = transforms[i];
  
                  // if there is a connection of the transform node with a joint, apply the joint value
  
                  if (transform.sid && transform.sid.indexOf(jointIndex) !== -1) {
                    switch (joint.type) {
                      case "revolute":
                        matrix.multiply(
                          m0.makeRotationAxis(axis, MathUtils.degToRad(value)),
                        );
                        break;
  
                      case "prismatic":
                        matrix.multiply(
                          m0.makeTranslation(
                            axis.x * value,
                            axis.y * value,
                            axis.z * value,
                          ),
                        );
                        break;
  
                      default:
                        console.warn(
                          "THREE.ColladaLoader: Unknown joint type: " +
                            joint.type,
                        );
                        break;
                    }
                  } else {
                    switch (transform.type) {
                      case "matrix":
                        matrix.multiply(transform.obj);
                        break;
  
                      case "translate":
                        matrix.multiply(
                          m0.makeTranslation(
                            transform.obj.x,
                            transform.obj.y,
                            transform.obj.z,
                          ),
                        );
                        break;
  
                      case "scale":
                        matrix.scale(transform.obj);
                        break;
  
                      case "rotate":
                        matrix.multiply(
                          m0.makeRotationAxis(transform.obj, transform.angle),
                        );
                        break;
                    }
                  }
                }
  
                object.matrix.copy(matrix);
                object.matrix.decompose(
                  object.position,
                  object.quaternion,
                  object.scale,
                );
  
                jointMap[jointIndex].position = value;
              }
            } else {
              console.log(
                "THREE.ColladaLoader: " + jointIndex + " does not exist.",
              );
            }
          },
        };
      }
  
      function buildTransformList(node) {
        var transforms = [];
  
        var xml = collada.querySelector('[id="' + node.id + '"]');
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "matrix":
              var array = parseFloats(child.textContent);
              var matrix = new Matrix4().fromArray(array).transpose();
              transforms.push({
                sid: child.getAttribute("sid"),
                type: child.nodeName,
                obj: matrix,
              });
              break;
  
            case "translate":
            case "scale":
              var array = parseFloats(child.textContent);
              var vector = new Vector3().fromArray(array);
              transforms.push({
                sid: child.getAttribute("sid"),
                type: child.nodeName,
                obj: vector,
              });
              break;
  
            case "rotate":
              var array = parseFloats(child.textContent);
              var vector = new Vector3().fromArray(array);
              var angle = MathUtils.degToRad(array[3]);
              transforms.push({
                sid: child.getAttribute("sid"),
                type: child.nodeName,
                obj: vector,
                angle: angle,
              });
              break;
          }
        }
  
        return transforms;
      }
  
      // nodes
  
      function prepareNodes(xml) {
        var elements = xml.getElementsByTagName("node");
  
        // ensure all node elements have id attributes
  
        for (var i = 0; i < elements.length; i++) {
          var element = elements[i];
  
          if (element.hasAttribute("id") === false) {
            element.setAttribute("id", generateId());
          }
        }
      }
  
      var matrix = new Matrix4();
      var vector = new Vector3();
  
      function parseNode(xml) {
        var data = {
          name: xml.getAttribute("name") || "",
          type: xml.getAttribute("type"),
          id: xml.getAttribute("id"),
          sid: xml.getAttribute("sid"),
          matrix: new Matrix4(),
          nodes: [],
          instanceCameras: [],
          instanceControllers: [],
          instanceLights: [],
          instanceGeometries: [],
          instanceNodes: [],
          transforms: {},
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          if (child.nodeType !== 1) continue;
  
          switch (child.nodeName) {
            case "node":
              data.nodes.push(child.getAttribute("id"));
              parseNode(child);
              break;
  
            case "instance_camera":
              data.instanceCameras.push(parseId(child.getAttribute("url")));
              break;
  
            case "instance_controller":
              data.instanceControllers.push(parseNodeInstance(child));
              break;
  
            case "instance_light":
              data.instanceLights.push(parseId(child.getAttribute("url")));
              break;
  
            case "instance_geometry":
              data.instanceGeometries.push(parseNodeInstance(child));
              break;
  
            case "instance_node":
              data.instanceNodes.push(parseId(child.getAttribute("url")));
              break;
  
            case "matrix":
              var array = parseFloats(child.textContent);
              data.matrix.multiply(matrix.fromArray(array).transpose());
              data.transforms[child.getAttribute("sid")] = child.nodeName;
              break;
  
            case "translate":
              var array = parseFloats(child.textContent);
              vector.fromArray(array);
              data.matrix.multiply(
                matrix.makeTranslation(vector.x, vector.y, vector.z),
              );
              data.transforms[child.getAttribute("sid")] = child.nodeName;
              break;
  
            case "rotate":
              var array = parseFloats(child.textContent);
              var angle = MathUtils.degToRad(array[3]);
              data.matrix.multiply(
                matrix.makeRotationAxis(vector.fromArray(array), angle),
              );
              data.transforms[child.getAttribute("sid")] = child.nodeName;
              break;
  
            case "scale":
              var array = parseFloats(child.textContent);
              data.matrix.scale(vector.fromArray(array));
              data.transforms[child.getAttribute("sid")] = child.nodeName;
              break;
  
            case "extra":
              break;
  
            default:
              console.log(child);
          }
        }
  
        if (hasNode(data.id)) {
          console.warn(
            "THREE.ColladaLoader: There is already a node with ID %s. Exclude current node from further processing.",
            data.id,
          );
        } else {
          library.nodes[data.id] = data;
        }
  
        return data;
      }
  
      function parseNodeInstance(xml) {
        var data = {
          id: parseId(xml.getAttribute("url")),
          materials: {},
          skeletons: [],
        };
  
        for (var i = 0; i < xml.childNodes.length; i++) {
          var child = xml.childNodes[i];
  
          switch (child.nodeName) {
            case "bind_material":
              var instances = child.getElementsByTagName("instance_material");
  
              for (var j = 0; j < instances.length; j++) {
                var instance = instances[j];
                var symbol = instance.getAttribute("symbol");
                var target = instance.getAttribute("target");
  
                data.materials[symbol] = parseId(target);
              }
  
              break;
  
            case "skeleton":
              data.skeletons.push(parseId(child.textContent));
              break;
  
            default:
              break;
          }
        }
  
        return data;
      }
  
      function buildSkeleton(skeletons, joints) {
        var boneData = [];
        var sortedBoneData = [];
  
        var i, j, data;
  
        // a skeleton can have multiple root bones. collada expresses this
        // situtation with multiple "skeleton" tags per controller instance
  
        for (i = 0; i < skeletons.length; i++) {
          var skeleton = skeletons[i];
  
          var root;
  
          if (hasNode(skeleton)) {
            root = getNode(skeleton);
            buildBoneHierarchy(root, joints, boneData);
          } else if (hasVisualScene(skeleton)) {
            // handle case where the skeleton refers to the visual scene (#13335)
  
            var visualScene = library.visualScenes[skeleton];
            var children = visualScene.children;
  
            for (var j = 0; j < children.length; j++) {
              var child = children[j];
  
              if (child.type === "JOINT") {
                var root = getNode(child.id);
                buildBoneHierarchy(root, joints, boneData);
              }
            }
          } else {
            console.error(
              "THREE.ColladaLoader: Unable to find root bone of skeleton with ID:",
              skeleton,
            );
          }
        }
  
        // sort bone data (the order is defined in the corresponding controller)
  
        for (i = 0; i < joints.length; i++) {
          for (j = 0; j < boneData.length; j++) {
            data = boneData[j];
  
            if (data.bone.name === joints[i].name) {
              sortedBoneData[i] = data;
              data.processed = true;
              break;
            }
          }
        }
  
        // add unprocessed bone data at the end of the list
  
        for (i = 0; i < boneData.length; i++) {
          data = boneData[i];
  
          if (data.processed === false) {
            sortedBoneData.push(data);
            data.processed = true;
          }
        }
  
        // setup arrays for skeleton creation
  
        var bones = [];
        var boneInverses = [];
  
        for (i = 0; i < sortedBoneData.length; i++) {
          data = sortedBoneData[i];
  
          bones.push(data.bone);
          boneInverses.push(data.boneInverse);
        }
  
        return new Skeleton(bones, boneInverses);
      }
  
      function buildBoneHierarchy(root, joints, boneData) {
        // setup bone data from visual scene
  
        root.traverse(function (object) {
          if (object.isBone === true) {
            var boneInverse;
  
            // retrieve the boneInverse from the controller data
  
            for (var i = 0; i < joints.length; i++) {
              var joint = joints[i];
  
              if (joint.name === object.name) {
                boneInverse = joint.boneInverse;
                break;
              }
            }
  
            if (boneInverse === undefined) {
              // Unfortunately, there can be joints in the visual scene that are not part of the
              // corresponding controller. In this case, we have to create a dummy boneInverse matrix
              // for the respective bone. This bone won't affect any vertices, because there are no skin indices
              // and weights defined for it. But we still have to add the bone to the sorted bone list in order to
              // ensure a correct animation of the model.
  
              boneInverse = new Matrix4();
            }
  
            boneData.push(
              { bone: object, boneInverse: boneInverse, processed: false },
            );
          }
        });
      }
  
      function buildNode(data) {
        var objects = [];
  
        var matrix = data.matrix;
        var nodes = data.nodes;
        var type = data.type;
        var instanceCameras = data.instanceCameras;
        var instanceControllers = data.instanceControllers;
        var instanceLights = data.instanceLights;
        var instanceGeometries = data.instanceGeometries;
        var instanceNodes = data.instanceNodes;
  
        // nodes
  
        for (var i = 0, l = nodes.length; i < l; i++) {
          objects.push(getNode(nodes[i]));
        }
  
        // instance cameras
  
        for (var i = 0, l = instanceCameras.length; i < l; i++) {
          var instanceCamera = getCamera(instanceCameras[i]);
  
          if (instanceCamera !== null) {
            objects.push(instanceCamera.clone());
          }
        }
  
        // instance controllers
  
        for (var i = 0, l = instanceControllers.length; i < l; i++) {
          var instance = instanceControllers[i];
          var controller = getController(instance.id);
          var geometries = getGeometry(controller.id);
          var newObjects = buildObjects(geometries, instance.materials);
  
          var skeletons = instance.skeletons;
          var joints = controller.skin.joints;
  
          var skeleton = buildSkeleton(skeletons, joints);
  
          for (var j = 0, jl = newObjects.length; j < jl; j++) {
            var object = newObjects[j];
  
            if (object.isSkinnedMesh) {
              object.bind(skeleton, controller.skin.bindMatrix);
              object.normalizeSkinWeights();
            }
  
            objects.push(object);
          }
        }
  
        // instance lights
  
        for (var i = 0, l = instanceLights.length; i < l; i++) {
          var instanceLight = getLight(instanceLights[i]);
  
          if (instanceLight !== null) {
            objects.push(instanceLight.clone());
          }
        }
  
        // instance geometries
  
        for (var i = 0, l = instanceGeometries.length; i < l; i++) {
          var instance = instanceGeometries[i];
  
          // a single geometry instance in collada can lead to multiple object3Ds.
          // this is the case when primitives are combined like triangles and lines
  
          var geometries = getGeometry(instance.id);
          var newObjects = buildObjects(geometries, instance.materials);
  
          for (var j = 0, jl = newObjects.length; j < jl; j++) {
            objects.push(newObjects[j]);
          }
        }
  
        // instance nodes
  
        for (var i = 0, l = instanceNodes.length; i < l; i++) {
          objects.push(getNode(instanceNodes[i]).clone());
        }
  
        var object;
  
        if (nodes.length === 0 && objects.length === 1) {
          object = objects[0];
        } else {
          object = (type === "JOINT") ? new Bone() : new Group();
  
          for (var i = 0; i < objects.length; i++) {
            object.add(objects[i]);
          }
        }
  
        object.name = (type === "JOINT") ? data.sid : data.name;
        object.matrix.copy(matrix);
        object.matrix.decompose(object.position, object.quaternion, object.scale);
  
        return object;
      }
  
      var fallbackMaterial = new MeshBasicMaterial({ color: 0xff00ff });
  
      function resolveMaterialBinding(keys, instanceMaterials) {
        var materials = [];
  
        for (var i = 0, l = keys.length; i < l; i++) {
          var id = instanceMaterials[keys[i]];
  
          if (id === undefined) {
            console.warn(
              "THREE.ColladaLoader: Material with key %s not found. Apply fallback material.",
              keys[i],
            );
            materials.push(fallbackMaterial);
          } else {
            materials.push(getMaterial(id));
          }
        }
  
        return materials;
      }
  
      function buildObjects(geometries, instanceMaterials) {
        var objects = [];
  
        for (var type in geometries) {
          var geometry = geometries[type];
  
          var materials = resolveMaterialBinding(
            geometry.materialKeys,
            instanceMaterials,
          );
  
          // handle case if no materials are defined
  
          if (materials.length === 0) {
            if (type === "lines" || type === "linestrips") {
              materials.push(new LineBasicMaterial());
            } else {
              materials.push(new MeshPhongMaterial());
            }
          }
  
          // regard skinning
  
          var skinning = (geometry.data.attributes.skinIndex !== undefined);
  
          if (skinning) {
            for (var i = 0, l = materials.length; i < l; i++) {
              materials[i].skinning = true;
            }
          }
  
          // choose between a single or multi materials (material array)
  
          var material = (materials.length === 1) ? materials[0] : materials;
  
          // now create a specific 3D object
  
          var object;
  
          switch (type) {
            case "lines":
              object = new LineSegments(geometry.data, material);
              break;
  
            case "linestrips":
              object = new Line(geometry.data, material);
              break;
  
            case "triangles":
            case "polylist":
              if (skinning) {
                object = new SkinnedMesh(geometry.data, material);
              } else {
                object = new Mesh(geometry.data, material);
              }
  
              break;
          }
  
          objects.push(object);
        }
  
        return objects;
      }
  
      function hasNode(id) {
        return library.nodes[id] !== undefined;
      }
  
      function getNode(id) {
        return getBuild(library.nodes[id], buildNode);
      }
  
      // visual scenes
  
      function parseVisualScene(xml) {
        var data = {
          name: xml.getAttribute("name"),
          children: [],
        };
  
        prepareNodes(xml);
  
        var elements = getElementsByTagName(xml, "node");
  
        for (var i = 0; i < elements.length; i++) {
          data.children.push(parseNode(elements[i]));
        }
  
        library.visualScenes[xml.getAttribute("id")] = data;
      }
  
      function buildVisualScene(data) {
        var group = new Group();
        group.name = data.name;
  
        var children = data.children;
  
        for (var i = 0; i < children.length; i++) {
          var child = children[i];
  
          group.add(getNode(child.id));
        }
  
        return group;
      }
  
      function hasVisualScene(id) {
        return library.visualScenes[id] !== undefined;
      }
  
      function getVisualScene(id) {
        return getBuild(library.visualScenes[id], buildVisualScene);
      }
  
      // scenes
  
      function parseScene(xml) {
        var instance = getElementsByTagName(xml, "instance_visual_scene")[0];
        return getVisualScene(parseId(instance.getAttribute("url")));
      }
  
      function setupAnimations() {
        var clips = library.clips;
  
        if (isEmpty(clips) === true) {
          if (isEmpty(library.animations) === false) {
            // if there are animations but no clips, we create a default clip for playback
  
            var tracks = [];
  
            for (var id in library.animations) {
              var animationTracks = getAnimation(id);
  
              for (var i = 0, l = animationTracks.length; i < l; i++) {
                tracks.push(animationTracks[i]);
              }
            }
  
            animations.push(new AnimationClip("default", -1, tracks));
          }
        } else {
          for (var id in clips) {
            animations.push(getAnimationClip(id));
          }
        }
      }
  
      // convert the parser error element into text with each child elements text
      // separated by new lines.
  
      function parserErrorToText(parserError) {
        var result = "";
        var stack = [parserError];
  
        while (stack.length) {
          var node = stack.shift();
  
          if (node.nodeType === Node.TEXT_NODE) {
            result += node.textContent;
          } else {
            result += "\n";
            stack.push.apply(stack, node.childNodes);
          }
        }
  
        return result.trim();
      }
  
      if (text.length === 0) {
        return { scene: new Scene() };
      }
  
      var xml = new DOMParser().parseFromString(text, "application/xml");
  
      var collada = getElementsByTagName(xml, "COLLADA")[0];
  
      var parserError = xml.getElementsByTagName("parsererror")[0];
      if (parserError !== undefined) {
        // Chrome will return parser error with a div in it
  
        var errorElement = getElementsByTagName(parserError, "div")[0];
        var errorText;
  
        if (errorElement) {
          errorText = errorElement.textContent;
        } else {
          errorText = parserErrorToText(parserError);
        }
  
        console.error(
          "THREE.ColladaLoader: Failed to parse collada file.\n",
          errorText,
        );
  
        return null;
      }
  
      // metadata
  
      var version = collada.getAttribute("version");
      console.log("THREE.ColladaLoader: File version", version);
  
      var asset = parseAsset(getElementsByTagName(collada, "asset")[0]);
      var textureLoader = new TextureLoader(this.manager);
      textureLoader.setPath(this.resourcePath || path).setCrossOrigin(
        this.crossOrigin,
      );
  
      var tgaLoader;
  
      if (TGALoader) {
        tgaLoader = new TGALoader(this.manager);
        tgaLoader.setPath(this.resourcePath || path);
      }
  
      //
  
      var animations = [];
      var kinematics = {};
      var count = 0;
  
      //
  
      var library = {
        animations: {},
        clips: {},
        controllers: {},
        images: {},
        effects: {},
        materials: {},
        cameras: {},
        lights: {},
        geometries: {},
        nodes: {},
        visualScenes: {},
        kinematicsModels: {},
        physicsModels: {},
        kinematicsScenes: {},
      };
  
      parseLibrary(collada, "library_animations", "animation", parseAnimation);
      parseLibrary(
        collada,
        "library_animation_clips",
        "animation_clip",
        parseAnimationClip,
      );
      parseLibrary(collada, "library_controllers", "controller", parseController);
      parseLibrary(collada, "library_images", "image", parseImage);
      parseLibrary(collada, "library_effects", "effect", parseEffect);
      parseLibrary(collada, "library_materials", "material", parseMaterial);
      parseLibrary(collada, "library_cameras", "camera", parseCamera);
      parseLibrary(collada, "library_lights", "light", parseLight);
      parseLibrary(collada, "library_geometries", "geometry", parseGeometry);
      parseLibrary(collada, "library_nodes", "node", parseNode);
      parseLibrary(
        collada,
        "library_visual_scenes",
        "visual_scene",
        parseVisualScene,
      );
      parseLibrary(
        collada,
        "library_kinematics_models",
        "kinematics_model",
        parseKinematicsModel,
      );
      parseLibrary(
        collada,
        "library_physics_models",
        "physics_model",
        parsePhysicsModel,
      );
      parseLibrary(
        collada,
        "scene",
        "instance_kinematics_scene",
        parseKinematicsScene,
      );
  
      buildLibrary(library.animations, buildAnimation);
      buildLibrary(library.clips, buildAnimationClip);
      buildLibrary(library.controllers, buildController);
      buildLibrary(library.images, buildImage);
      buildLibrary(library.effects, buildEffect);
      buildLibrary(library.materials, buildMaterial);
      buildLibrary(library.cameras, buildCamera);
      buildLibrary(library.lights, buildLight);
      buildLibrary(library.geometries, buildGeometry);
      buildLibrary(library.visualScenes, buildVisualScene);
  
      setupAnimations();
      setupKinematics();
  
      var scene = parseScene(getElementsByTagName(collada, "scene")[0]);
  
      if (asset.upAxis === "Z_UP") {
        scene.quaternion.setFromEuler(new Euler(-Math.PI / 2, 0, 0));
      }
  
      scene.scale.multiplyScalar(asset.unit);
  
      return {
        animations: animations,
        kinematics: kinematics,
        library: library,
        scene: scene,
      };
    },
  });
  
  export { ColladaLoader };
  