| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- import { PanoramaToCubeMapTools } from '../../Misc/HighDynamicRange/panoramaToCubemap';
- import { Engine } from "../../Engines/engine";
- import { BaseTexture } from './baseTexture';
- import { Texture } from './texture';
- import { Scene } from "../../scene";
- import { Nullable } from "../../types";
- import { Tools } from '../../Misc/tools';
- /**
- * This represents a texture coming from an equirectangular image supported by the web browser canvas.
- */
- export class EquiRectangularCubeTexture extends BaseTexture {
- /** The six faces of the cube. */
- private static _FacesMapping = ['right', 'left', 'up', 'down', 'front', 'back'];
- private _noMipmap: boolean;
- private _onLoad: Nullable<() => void> = null;
- private _onError: Nullable<() => void> = null;
- /** The size of the cubemap. */
- private _size: number;
- /** The buffer of the image. */
- private _buffer: ArrayBuffer;
- /** The width of the input image. */
- private _width: number;
- /** The height of the input image. */
- private _height: number;
- /** The URL to the image. */
- public url: string;
- /** The texture coordinates mode. As this texture is stored in a cube format, please modify carefully. */
- public coordinatesMode = Texture.CUBIC_MODE;
- /**
- * Instantiates an EquiRectangularCubeTexture from the following parameters.
- * @param url The location of the image
- * @param scene The scene the texture will be used in
- * @param size The cubemap desired size (the more it increases the longer the generation will be)
- * @param noMipmap Forces to not generate the mipmap if true
- * @param gammaSpace Specifies if the texture will be used in gamma or linear space
- * (the PBR material requires those textures in linear space, but the standard material would require them in Gamma space)
- * @param onLoad — defines a callback called when texture is loaded
- * @param onError — defines a callback called if there is an error
- */
- constructor(
- url: string,
- scene: Scene,
- size: number,
- noMipmap: boolean = false,
- gammaSpace: boolean = true,
- onLoad: Nullable<() => void> = null,
- onError: Nullable<(message?: string, exception?: any) => void> = null
- ) {
- super(scene);
- if (!url) {
- throw new Error('Image url is not set');
- }
- this.name = url;
- this.url = url;
- this._size = size;
- this._noMipmap = noMipmap;
- this.gammaSpace = gammaSpace;
- this._onLoad = onLoad;
- this._onError = onError;
- this.hasAlpha = false;
- this.isCube = true;
- this._texture = this._getFromCache(url, this._noMipmap);
- if (!this._texture) {
- if (!scene.useDelayedTextureLoading) {
- this.loadImage(this.loadTexture.bind(this), this._onError);
- } else {
- this.delayLoadState = Engine.DELAYLOADSTATE_NOTLOADED;
- }
- } else if (onLoad) {
- if (this._texture.isReady) {
- Tools.SetImmediate(() => onLoad());
- } else {
- this._texture.onLoadedObservable.add(onLoad);
- }
- }
- }
- /**
- * Load the image data, by putting the image on a canvas and extracting its buffer.
- */
- private loadImage(loadTextureCallback: () => void, onError: Nullable<(message?: string, exception?: any) => void>): void {
- const canvas = document.createElement('canvas');
- const image = new Image();
- image.addEventListener('load', () => {
- this._width = image.width;
- this._height = image.height;
- canvas.width = this._width;
- canvas.height = this._height;
- const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
- ctx.drawImage(image, 0, 0);
- const imageData = ctx.getImageData(0, 0, image.width, image.height);
- this._buffer = imageData.data.buffer as ArrayBuffer;
- canvas.remove();
- loadTextureCallback();
- });
- image.addEventListener('error', (error) => {
- if (onError) {
- onError(`${this.getClassName()} could not be loaded`, error);
- }
- });
- image.src = this.url;
- }
- /**
- * Convert the image buffer into a cubemap and create a CubeTexture.
- */
- private loadTexture(): void {
- const scene = this.getScene();
- const callback = (): Nullable<ArrayBufferView[]> => {
- const imageData = this.getFloat32ArrayFromArrayBuffer(this._buffer);
- // Extract the raw linear data.
- const data = PanoramaToCubeMapTools.ConvertPanoramaToCubemap(imageData, this._width, this._height, this._size);
- const results = [];
- // Push each faces.
- for (let i = 0; i < 6; i++) {
- const dataFace = (data as any)[EquiRectangularCubeTexture._FacesMapping[i]];
- results.push(dataFace);
- }
- return results;
- };
- if (!scene) {
- return;
- }
- this._texture = scene
- .getEngine()
- .createRawCubeTextureFromUrl(
- this.url,
- scene,
- this._size,
- Engine.TEXTUREFORMAT_RGB,
- scene.getEngine().getCaps().textureFloat
- ? Engine.TEXTURETYPE_FLOAT
- : Engine.TEXTURETYPE_UNSIGNED_INTEGER,
- this._noMipmap,
- callback,
- null,
- this._onLoad,
- this._onError
- );
- }
- /**
- * Convert the ArrayBuffer into a Float32Array and drop the transparency channel.
- * @param buffer The ArrayBuffer that should be converted.
- * @returns The buffer as Float32Array.
- */
- private getFloat32ArrayFromArrayBuffer(buffer: ArrayBuffer): Float32Array {
- const dataView = new DataView(buffer);
- const floatImageData = new Float32Array((buffer.byteLength * 3) / 4);
- let k = 0;
- for (let i = 0; i < buffer.byteLength; i++) {
- // We drop the transparency channel, because we do not need/want it
- if ((i + 1) % 4 !== 0) {
- floatImageData[k++] = dataView.getUint8(i) / 255;
- }
- }
- return floatImageData;
- }
- /**
- * Get the current class name of the texture useful for serialization or dynamic coding.
- * @returns "EquiRectangularCubeTexture"
- */
- public getClassName(): string {
- return "EquiRectangularCubeTexture";
- }
- /**
- * Create a clone of the current EquiRectangularCubeTexture and return it.
- * @returns A clone of the current EquiRectangularCubeTexture.
- */
- public clone(): EquiRectangularCubeTexture {
- const scene = this.getScene();
- if (!scene) {
- return this;
- }
- const newTexture = new EquiRectangularCubeTexture(this.url, scene, this._size, this._noMipmap, this.gammaSpace);
- // Base texture
- newTexture.level = this.level;
- newTexture.wrapU = this.wrapU;
- newTexture.wrapV = this.wrapV;
- newTexture.coordinatesIndex = this.coordinatesIndex;
- newTexture.coordinatesMode = this.coordinatesMode;
- return newTexture;
- }
- }
|