"use strict";
const three_module = require("../../../../../../three@0.154.0/node_modules/three/build/three.module.js");
const RGBELoader = require("../../../../../../three@0.154.0/node_modules/three/examples/jsm/loaders/RGBELoader.js");
const utilities = require("../utilities.js");
const EnvironmentScene = require("./EnvironmentScene.js");
const EnvironmentSceneAlt = require("./EnvironmentSceneAlt.js");
/* @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const GENERATED_SIGMA = 0.04;
const MAX_SAMPLES = 20;
const HDR_FILE_RE = /\.hdr(\.js)?$/;
class TextureUtils extends three_module.EventDispatcher {
  constructor(threeRenderer) {
    super();
    this.threeRenderer = threeRenderer;
    this.lottieLoaderUrl = "";
    this.withCredentials = false;
    this._ldrLoader = null;
    this._hdrLoader = null;
    this._lottieLoader = null;
    this.generatedEnvironmentMap = null;
    this.generatedEnvironmentMapAlt = null;
    this.skyboxCache = /* @__PURE__ */ new Map();
    this.blurMaterial = null;
    this.blurScene = null;
  }
  get ldrLoader() {
    if (this._ldrLoader == null) {
      this._ldrLoader = new three_module.TextureLoader();
    }
    this._ldrLoader.setWithCredentials(this.withCredentials);
    return this._ldrLoader;
  }
  get hdrLoader() {
    if (this._hdrLoader == null) {
      this._hdrLoader = new RGBELoader.RGBELoader();
      this._hdrLoader.setDataType(three_module.HalfFloatType);
    }
    this._hdrLoader.setWithCredentials(this.withCredentials);
    return this._hdrLoader;
  }
  async getLottieLoader() {
    if (this._lottieLoader == null) {
      const { LottieLoader } = await import(
        /* webpackIgnore: true */
        this.lottieLoaderUrl
      );
      this._lottieLoader = new LottieLoader();
    }
    this._lottieLoader.setWithCredentials(this.withCredentials);
    return this._lottieLoader;
  }
  async loadImage(url) {
    const texture = await new Promise((resolve, reject) => this.ldrLoader.load(url, resolve, () => {
    }, reject));
    texture.name = url;
    texture.flipY = false;
    return texture;
  }
  async loadLottie(url, quality) {
    const loader = await this.getLottieLoader();
    loader.setQuality(quality);
    const texture = await new Promise((resolve, reject) => loader.load(url, resolve, () => {
    }, reject));
    texture.name = url;
    return texture;
  }
  async loadEquirect(url, progressCallback = () => {
  }) {
    try {
      const isHDR = HDR_FILE_RE.test(url);
      const loader = isHDR ? this.hdrLoader : this.ldrLoader;
      const texture = await new Promise((resolve, reject) => loader.load(url, resolve, (event) => {
        progressCallback(event.loaded / event.total * 0.9);
      }, reject));
      progressCallback(1);
      texture.name = url;
      texture.mapping = three_module.EquirectangularReflectionMapping;
      if (!isHDR) {
        texture.encoding = three_module.sRGBEncoding;
      }
      return texture;
    } finally {
      if (progressCallback) {
        progressCallback(1);
      }
    }
  }
  /**
   * Returns a { skybox, environmentMap } object with the targets/textures
   * accordingly. `skybox` is a WebGLRenderCubeTarget, and `environmentMap`
   * is a Texture from a WebGLRenderCubeTarget.
   */
  async generateEnvironmentMapAndSkybox(skyboxUrl = null, environmentMapUrl = null, progressCallback = () => {
  }) {
    const useAltEnvironment = environmentMapUrl !== "legacy";
    if (environmentMapUrl === "legacy" || environmentMapUrl === "neutral") {
      environmentMapUrl = null;
    }
    environmentMapUrl = utilities.deserializeUrl(environmentMapUrl);
    let skyboxLoads = Promise.resolve(null);
    let environmentMapLoads;
    if (!!skyboxUrl) {
      skyboxLoads = this.loadEquirectFromUrl(skyboxUrl, progressCallback);
    }
    if (!!environmentMapUrl) {
      environmentMapLoads = this.loadEquirectFromUrl(environmentMapUrl, progressCallback);
    } else if (!!skyboxUrl) {
      environmentMapLoads = this.loadEquirectFromUrl(skyboxUrl, progressCallback);
    } else {
      environmentMapLoads = useAltEnvironment ? this.loadGeneratedEnvironmentMapAlt() : this.loadGeneratedEnvironmentMap();
    }
    const [environmentMap, skybox] = await Promise.all([environmentMapLoads, skyboxLoads]);
    if (environmentMap == null) {
      throw new Error("Failed to load environment map.");
    }
    return { environmentMap, skybox };
  }
  /**
   * Loads an equirect Texture from a given URL, for use as a skybox.
   */
  async loadEquirectFromUrl(url, progressCallback) {
    if (!this.skyboxCache.has(url)) {
      const skyboxMapLoads = this.loadEquirect(url, progressCallback);
      this.skyboxCache.set(url, skyboxMapLoads);
    }
    return this.skyboxCache.get(url);
  }
  async GenerateEnvironmentMap(scene, name) {
    await utilities.timePasses();
    const renderer = this.threeRenderer;
    const cubeTarget = new three_module.WebGLCubeRenderTarget(256, {
      generateMipmaps: false,
      type: three_module.HalfFloatType,
      format: three_module.RGBAFormat,
      colorSpace: three_module.LinearSRGBColorSpace,
      depthBuffer: true
    });
    const cubeCamera = new three_module.CubeCamera(0.1, 100, cubeTarget);
    const generatedEnvironmentMap = cubeCamera.renderTarget.texture;
    generatedEnvironmentMap.name = name;
    const outputColorSpace = renderer.outputColorSpace;
    const toneMapping = renderer.toneMapping;
    renderer.toneMapping = three_module.NoToneMapping;
    renderer.outputColorSpace = three_module.LinearSRGBColorSpace;
    cubeCamera.update(renderer, scene);
    this.blurCubemap(cubeTarget, GENERATED_SIGMA);
    renderer.toneMapping = toneMapping;
    renderer.outputColorSpace = outputColorSpace;
    return generatedEnvironmentMap;
  }
  /**
   * Loads a dynamically generated environment map.
   */
  async loadGeneratedEnvironmentMap() {
    if (this.generatedEnvironmentMap == null) {
      this.generatedEnvironmentMap = this.GenerateEnvironmentMap(new EnvironmentScene(), "legacy");
    }
    return this.generatedEnvironmentMap;
  }
  /**
   * Loads a dynamically generated environment map, designed to be neutral and
   * color-preserving. Shows less contrast around the different sides of the
   * object.
   */
  async loadGeneratedEnvironmentMapAlt() {
    if (this.generatedEnvironmentMapAlt == null) {
      this.generatedEnvironmentMapAlt = this.GenerateEnvironmentMap(new EnvironmentSceneAlt(), "neutral");
    }
    return this.generatedEnvironmentMapAlt;
  }
  blurCubemap(cubeTarget, sigma) {
    if (this.blurMaterial == null) {
      this.blurMaterial = this.getBlurShader(MAX_SAMPLES);
      const box = new three_module.BoxGeometry();
      const blurMesh = new three_module.Mesh(box, this.blurMaterial);
      this.blurScene = new three_module.Scene();
      this.blurScene.add(blurMesh);
    }
    const tempTarget = cubeTarget.clone();
    this.halfblur(cubeTarget, tempTarget, sigma, "latitudinal");
    this.halfblur(tempTarget, cubeTarget, sigma, "longitudinal");
  }
  halfblur(targetIn, targetOut, sigmaRadians, direction) {
    const STANDARD_DEVIATIONS = 3;
    const pixels = targetIn.width;
    const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : 2 * Math.PI / (2 * MAX_SAMPLES - 1);
    const sigmaPixels = sigmaRadians / radiansPerPixel;
    const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES;
    if (samples > MAX_SAMPLES) {
      console.warn(`sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`);
    }
    const weights = [];
    let sum = 0;
    for (let i = 0; i < MAX_SAMPLES; ++i) {
      const x = i / sigmaPixels;
      const weight = Math.exp(-x * x / 2);
      weights.push(weight);
      if (i == 0) {
        sum += weight;
      } else if (i < samples) {
        sum += 2 * weight;
      }
    }
    for (let i = 0; i < weights.length; i++) {
      weights[i] = weights[i] / sum;
    }
    const blurUniforms = this.blurMaterial.uniforms;
    blurUniforms["envMap"].value = targetIn.texture;
    blurUniforms["samples"].value = samples;
    blurUniforms["weights"].value = weights;
    blurUniforms["latitudinal"].value = direction === "latitudinal";
    blurUniforms["dTheta"].value = radiansPerPixel;
    const cubeCamera = new three_module.CubeCamera(0.1, 100, targetOut);
    cubeCamera.update(this.threeRenderer, this.blurScene);
  }
  getBlurShader(maxSamples) {
    const weights = new Float32Array(maxSamples);
    const poleAxis = new three_module.Vector3(0, 1, 0);
    const shaderMaterial = new three_module.ShaderMaterial({
      name: "SphericalGaussianBlur",
      defines: { "n": maxSamples },
      uniforms: {
        "envMap": { value: null },
        "samples": { value: 1 },
        "weights": { value: weights },
        "latitudinal": { value: false },
        "dTheta": { value: 0 },
        "poleAxis": { value: poleAxis }
      },
      vertexShader: (
        /* glsl */
        `
      
      varying vec3 vOutputDirection;
  
      void main() {
  
        vOutputDirection = vec3( position );
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  
      }
    `
      ),
      fragmentShader: (
        /* glsl */
        `
        varying vec3 vOutputDirection;
  
        uniform samplerCube envMap;
        uniform int samples;
        uniform float weights[ n ];
        uniform bool latitudinal;
        uniform float dTheta;
        uniform vec3 poleAxis;
  
        vec3 getSample( float theta, vec3 axis ) {
  
          float cosTheta = cos( theta );
          // Rodrigues' axis-angle rotation
          vec3 sampleDirection = vOutputDirection * cosTheta
            + cross( axis, vOutputDirection ) * sin( theta )
            + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );
  
          return vec3( textureCube( envMap, sampleDirection ) );
  
        }
  
        void main() {
  
          vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );
  
          if ( all( equal( axis, vec3( 0.0 ) ) ) ) {
  
            axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );
  
          }
  
          axis = normalize( axis );
  
          gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );
          gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );
  
          for ( int i = 1; i < n; i++ ) {
  
            if ( i >= samples ) {
  
              break;
  
            }
  
            float theta = dTheta * float( i );
            gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );
            gl_FragColor.rgb += weights[ i ] * getSample( theta, axis );
  
          }
        }
      `
      ),
      blending: three_module.NoBlending,
      depthTest: false,
      depthWrite: false,
      side: three_module.BackSide
    });
    return shaderMaterial;
  }
  async dispose() {
    for (const [, promise] of this.skyboxCache) {
      const skybox = await promise;
      skybox.dispose();
    }
    if (this.generatedEnvironmentMap != null) {
      (await this.generatedEnvironmentMap).dispose();
      this.generatedEnvironmentMap = null;
    }
    if (this.generatedEnvironmentMapAlt != null) {
      (await this.generatedEnvironmentMapAlt).dispose();
      this.generatedEnvironmentMapAlt = null;
    }
    if (this.blurMaterial != null) {
      this.blurMaterial.dispose();
    }
  }
}
module.exports = TextureUtils;
//# sourceMappingURL=TextureUtils.js.map
