text: import { GltfState } from '../GltfState/gltf_state.js';
  import { gltfRenderer } from '../Renderer/renderer.js';
  import { GL } from '../Renderer/webgl.js';
  import { ResourceLoader } from '../ResourceLoader/resource_loader.js';
  
  /**
   * GltfView represents a view on a gltf, e.g. in a canvas
   */
  class GltfView
  {
      /**
       * GltfView representing one WebGl 2.0 context or in other words one
       * 3D rendering of the Gltf.
       * You can create multiple views for example when multiple canvases should
       * be shown on the same webpage.
       * @param {*} context WebGl 2.0 context. Get it from a canvas with `canvas.getContext("webgl2")`
       */
      constructor(context)
      {
          this.context = context;
          this.renderer = new gltfRenderer(this.context);
      }
  
      /**
       * createState constructs a new GltfState for the GltfView. The resources
       * referenced in a gltf state can directly be stored as resources on the WebGL
       * context of GltfView, therefore GltfStates cannot not be shared between
       * GltfViews.
       * @returns {GltfState} GltfState
       */
      createState()
      {
          return new GltfState(this);
      }
  
      /**
       * createResourceLoader creates a resource loader with which glTFs and
       * environments can be loaded for the view
       * @param {Object} [externalDracoLib] optional object of an external Draco library, e.g. from a CDN
       * @param {Object} [externalKtxLib] optional object of an external KTX library, e.g. from a CDN
       * @returns {ResourceLoader} ResourceLoader
       */
      createResourceLoader(externalDracoLib = undefined, externalKtxLib = undefined)
      {
          let resourceLoader = new ResourceLoader(this);
          resourceLoader.initKtxLib(externalKtxLib);
          resourceLoader.initDracoLib(externalDracoLib);
          return resourceLoader;
      }
  
      /**
       * renderFrame to the context's default frame buffer
       * Call this function in the javascript animation update loop for continuous rendering to a canvas
       * @param {*} state GltfState that is be used for rendering
       * @param {*} width of the viewport
       * @param {*} height of the viewport
       */
      renderFrame(state, width, height)
      {
          this.renderer.init(state);
          this._animate(state);
  
          this.renderer.resize(width, height);
  
          this.renderer.clearFrame(state.renderingParameters.clearColor);
  
          if(state.gltf === undefined)
          {
              return;
          }
  
          const scene = state.gltf.scenes[state.sceneIndex];
  
          if(scene === undefined)
          {
              return;
          }
  
          scene.applyTransformHierarchy(state.gltf);
  
          this.renderer.drawScene(state, scene);
      }
  
      /**
       * gatherStatistics collects information about the GltfState such as the number of
       * rendered meshes or triangles
       * @param {*} state GltfState about which the statistics should be collected
       * @returns {Object} an object containing statistics information
       */
      gatherStatistics(state)
      {
          if(state.gltf === undefined)
          {
              return;
          }
  
          // gather information from the active scene
          const scene = state.gltf.scenes[state.sceneIndex];
          if (scene === undefined)
          {
              return {
                  meshCount: 0,
                  faceCount: 0,
                  opaqueMaterialsCount: 0,
                  transparentMaterialsCount: 0};
          }
          const nodes = scene.gatherNodes(state.gltf);
          const activeMeshes = nodes.filter(node => node.mesh !== undefined).map(node => state.gltf.meshes[node.mesh]);
          const activePrimitives = activeMeshes
              .reduce((acc, mesh) => acc.concat(mesh.primitives), [])
              .filter(primitive => primitive.material !== undefined);
          const activeMaterials = [... new Set(activePrimitives.map(primitive => state.gltf.materials[primitive.material]))];
          const opaqueMaterials = activeMaterials.filter(material => material.alphaMode !== "BLEND");
          const transparentMaterials = activeMaterials.filter(material => material.alphaMode === "BLEND");
          const faceCount = activePrimitives
              .map(primitive => {
                  let verticesCount = 0;
                  if(primitive.indices !== undefined)
                  {
                      verticesCount = state.gltf.accessors[primitive.indices].count;
                  }
                  if (verticesCount === 0)
                  {
                      return 0;
                  }
  
                  // convert vertex count to point, line or triangle count
                  switch (primitive.mode) {
                  case GL.POINTS:
                      return verticesCount;
                  case GL.LINES:
                      return verticesCount / 2;
                  case GL.LINE_LOOP:
                      return verticesCount;
                  case GL.LINE_STRIP:
                      return verticesCount - 1;
                  case GL.TRIANGLES:
                      return verticesCount / 3;
                  case GL.TRIANGLE_STRIP:
                  case GL.TRIANGLE_FAN:
                      return verticesCount - 2;
                  }
              })
              .reduce((acc, faceCount) => acc += faceCount);
  
          // assemble statistics object
          return {
              meshCount: activeMeshes.length,
              faceCount: faceCount,
              opaqueMaterialsCount: opaqueMaterials.length,
              transparentMaterialsCount: transparentMaterials.length
          };
      }
  
      _animate(state)
      {
          if(state.gltf === undefined)
          {
              return;
          }
  
          if(state.gltf.animations !== undefined && state.animationIndices !== undefined)
          {
              const disabledAnimations = state.gltf.animations.filter( (anim, index) => {
                  return false === state.animationIndices.includes(index);
              });
  
              for(const disabledAnimation of disabledAnimations)
              {
                  disabledAnimation.advance(state.gltf, undefined);
              }
  
              const t = state.animationTimer.elapsedSec();
  
              const animations = state.animationIndices.map(index => {
                  return state.gltf.animations[index];
              }).filter(animation => animation !== undefined);
  
              for(const animation of animations)
              {
                  animation.advance(state.gltf, t);
              }
          }
      }
  }
  
  export { GltfView };
  