فهرست منبع

Preliminary implementation of 2D array textures

Alex Gordon 5 سال پیش
والد
کامیت
bc74b57893

+ 1 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -169,6 +169,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                     <TextLineComponent label="Class" value={texture.getClassName()} />
                     <TextLineComponent label="Has alpha" value={texture.hasAlpha ? "Yes" : "No"} />
                     <TextLineComponent label="Is 3D" value={texture.is3D ? "Yes" : "No"} />
+                    <TextLineComponent label="Is 2D array" value={texture.is2DArray ? "Yes" : "No"} />
                     <TextLineComponent label="Is cube" value={texture.isCube ? "Yes" : "No"} />
                     <TextLineComponent label="Is render target" value={texture.isRenderTarget ? "Yes" : "No"} />
                     <TextLineComponent label="Has mipmaps" value={!texture.noMipmap ? "Yes" : "No"} />

+ 107 - 0
src/Engines/Extensions/engine.rawTexture.ts

@@ -171,6 +171,42 @@ declare module "../../Engines/engine" {
          * @param textureType defines the texture Type (Engine.TEXTURETYPE_UNSIGNED_INT, Engine.TEXTURETYPE_FLOAT...)
          */
         updateRawTexture3D(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string>, textureType: number): void;
+
+        /**
+         * Creates a new raw 2D array texture
+         * @param data defines the data used to create the texture
+         * @param width defines the width of the texture
+         * @param height defines the height of the texture
+         * @param depth defines the number of layers of the texture
+         * @param format defines the format of the texture
+         * @param generateMipMaps defines if the engine must generate mip levels
+         * @param invertY defines if data must be stored with Y axis inverted
+         * @param samplingMode defines the required sampling mode (like Texture.NEAREST_SAMPLINGMODE)
+         * @param compression defines the compressed used (can be null)
+         * @param textureType defines the compressed used (can be null)
+         * @returns a new raw 3D texture (stored in an InternalTexture)
+         */
+        createRawTexture2DArray(data: Nullable<ArrayBufferView>, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable<string>, textureType: number): InternalTexture;
+
+        /**
+         * Update a raw 2D array texture
+         * @param texture defines the texture to update
+         * @param data defines the data to store
+         * @param format defines the data format
+         * @param invertY defines if data must be stored with Y axis inverted
+         */
+        updateRawTexture2DArray(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean): void;
+
+        /**
+         * Update a raw 2D array texture
+         * @param texture defines the texture to update
+         * @param data defines the data to store
+         * @param format defines the data format
+         * @param invertY defines if data must be stored with Y axis inverted
+         * @param compression defines the used compression (can be null)
+         * @param textureType defines the texture Type (Engine.TEXTURETYPE_UNSIGNED_INT, Engine.TEXTURETYPE_FLOAT...)
+         */
+        updateRawTexture2DArray(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string>, textureType: number): void;
     }
 }
 
@@ -523,3 +559,74 @@ Engine.prototype.updateRawTexture3D = function(texture: InternalTexture, data: N
     // this.resetTextureCache();
     texture.isReady = true;
 };
+
+Engine.prototype.createRawTexture2DArray = function(data: Nullable<ArrayBufferView>, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture {
+    var texture = new InternalTexture(this, InternalTextureSource.Raw2DArray);
+    texture.baseWidth = width;
+    texture.baseHeight = height;
+    texture.baseDepth = depth;
+    texture.width = width;
+    texture.height = height;
+    texture.depth = depth;
+    texture.format = format;
+    texture.type = textureType;
+    texture.generateMipMaps = generateMipMaps;
+    texture.samplingMode = samplingMode;
+    texture.is2DArray = true;
+
+    if (!this._doNotHandleContextLost) {
+        texture._bufferView = data;
+    }
+
+    this.updateRawTexture2DArray(texture, data, format, invertY, compression, textureType);
+    this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, texture, true);
+
+    // Filters
+    var filters = this._getSamplingParameters(samplingMode, generateMipMaps);
+
+    this._gl.texParameteri(this._gl.TEXTURE_2D_ARRAY, this._gl.TEXTURE_MAG_FILTER, filters.mag);
+    this._gl.texParameteri(this._gl.TEXTURE_2D_ARRAY, this._gl.TEXTURE_MIN_FILTER, filters.min);
+
+    if (generateMipMaps) {
+        this._gl.generateMipmap(this._gl.TEXTURE_2D_ARRAY);
+    }
+
+    this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, null);
+
+    this._internalTexturesCache.push(texture);
+
+    return texture;
+};
+
+Engine.prototype.updateRawTexture2DArray = function(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): void {
+    var internalType = this._getWebGLTextureType(textureType);
+    var internalFormat = this._getInternalFormat(format);
+    var internalSizedFomat = this._getRGBABufferInternalSizedFormat(textureType, format);
+
+    this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, texture, true);
+    this._unpackFlipY(invertY === undefined ? true : (invertY ? true : false));
+
+    if (!this._doNotHandleContextLost) {
+        texture._bufferView = data;
+        texture.format = format;
+        texture.invertY = invertY;
+        texture._compression = compression;
+    }
+
+    if (texture.width % 4 !== 0) {
+        this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1);
+    }
+
+    if (compression && data) {
+        this._gl.compressedTexImage3D(this._gl.TEXTURE_2D_ARRAY, 0, (<any>this.getCaps().s3tc)[compression], texture.width, texture.height, texture.depth, 0, data);
+    } else {
+        this._gl.texImage3D(this._gl.TEXTURE_2D_ARRAY, 0, internalSizedFomat, texture.width, texture.height, texture.depth, 0, internalFormat, internalType, data);
+    }
+
+    if (texture.generateMipMaps) {
+        this._gl.generateMipmap(this._gl.TEXTURE_2D_ARRAY);
+    }
+    this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, null);
+    // this.resetTextureCache();
+    texture.isReady = true;
+};

+ 2 - 0
src/Engines/nativeEngine.ts

@@ -1292,6 +1292,8 @@ export class NativeEngine extends Engine {
             internalTexture = this.emptyCubeTexture;
         } else if (texture.is3D) {
             internalTexture = this.emptyTexture3D;
+        } else if (texture.is2DArray) {
+            internalTexture = this.emptyTexture2DArray;
         } else {
             internalTexture = this.emptyTexture;
         }

+ 54 - 2
src/Engines/thinEngine.ts

@@ -353,6 +353,7 @@ export class ThinEngine {
     private _emptyTexture: Nullable<InternalTexture>;
     private _emptyCubeTexture: Nullable<InternalTexture>;
     private _emptyTexture3D: Nullable<InternalTexture>;
+    private _emptyTexture2DArray: Nullable<InternalTexture>;
 
     /** @hidden */
     public _frameHandler: number;
@@ -416,6 +417,17 @@ export class ThinEngine {
     }
 
     /**
+     * Gets the default empty 2D array texture
+     */
+    public get emptyTexture2DArray(): InternalTexture {
+        if (!this._emptyTexture2DArray) {
+            this._emptyTexture2DArray = this.createRawTexture2DArray(new Uint8Array(4), 1, 1, 1, Constants.TEXTUREFORMAT_RGBA, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE);
+        }
+
+        return this._emptyTexture2DArray;
+    }
+
+    /**
      * Gets the default empty cube texture
      */
     public get emptyCubeTexture(): InternalTexture {
@@ -2854,6 +2866,24 @@ export class ThinEngine {
         throw _DevTools.WarnImport("Engine.RawTexture");
     }
 
+    /**
+     * Creates a new raw 2D array texture
+     * @param data defines the data used to create the texture
+     * @param width defines the width of the texture
+     * @param height defines the height of the texture
+     * @param depth defines the number of layers of the texture
+     * @param format defines the format of the texture
+     * @param generateMipMaps defines if the engine must generate mip levels
+     * @param invertY defines if data must be stored with Y axis inverted
+     * @param samplingMode defines the required sampling mode (like Texture.NEAREST_SAMPLINGMODE)
+     * @param compression defines the compressed used (can be null)
+     * @param textureType defines the compressed used (can be null)
+     * @returns a new raw 3D texture (stored in an InternalTexture)
+     */
+    public createRawTexture2DArray(data: Nullable<ArrayBufferView>, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number, compression: Nullable<string> = null, textureType = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture {
+        throw _DevTools.WarnImport("Engine.RawTexture");
+    }
+
     private _unpackFlipYCached: Nullable<boolean> = null;
 
     /**
@@ -2895,6 +2925,10 @@ export class ThinEngine {
             this._setTextureParameterInteger(this._gl.TEXTURE_3D, this._gl.TEXTURE_MAG_FILTER, filters.mag, texture);
             this._setTextureParameterInteger(this._gl.TEXTURE_3D, this._gl.TEXTURE_MIN_FILTER, filters.min);
             this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
+        } else if (texture.is2DArray) {
+            this._setTextureParameterInteger(this._gl.TEXTURE_2D_ARRAY, this._gl.TEXTURE_MAG_FILTER, filters.mag, texture);
+            this._setTextureParameterInteger(this._gl.TEXTURE_2D_ARRAY, this._gl.TEXTURE_MIN_FILTER, filters.min);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, null);
         } else {
             this._setTextureParameterInteger(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, filters.mag, texture);
             this._setTextureParameterInteger(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, filters.min);
@@ -3318,6 +3352,9 @@ export class ThinEngine {
         else if (texture.is3D) {
             internalTexture = this.emptyTexture3D;
         }
+        else if (texture.is2DArray) {
+            internalTexture = this.emptyTexture2DArray;
+        }
         else {
             internalTexture = this.emptyTexture;
         }
@@ -3361,8 +3398,23 @@ export class ThinEngine {
             }
 
             this._setAnisotropicLevel(this._gl.TEXTURE_3D, texture);
-        }
-        else if (internalTexture && internalTexture.isCube) {
+        } else if (internalTexture && internalTexture.is2DArray) {
+            if (needToBind) {
+                this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, internalTexture, isPartOfTextureArray);
+            }
+
+            if (internalTexture && internalTexture._cachedWrapU !== texture.wrapU) {
+                internalTexture._cachedWrapU = texture.wrapU;
+                this._setTextureParameterInteger(this._gl.TEXTURE_2D_ARRAY, this._gl.TEXTURE_WRAP_S, this._getTextureWrapMode(texture.wrapU), internalTexture);
+            }
+
+            if (internalTexture && internalTexture._cachedWrapV !== texture.wrapV) {
+                internalTexture._cachedWrapV = texture.wrapV;
+                this._setTextureParameterInteger(this._gl.TEXTURE_2D_ARRAY, this._gl.TEXTURE_WRAP_T, this._getTextureWrapMode(texture.wrapV), internalTexture);
+            }
+
+            this._setAnisotropicLevel(this._gl.TEXTURE_2D_ARRAY, texture);
+        } else if (internalTexture && internalTexture.isCube) {
             if (needToBind) {
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, internalTexture, isPartOfTextureArray);
             }

+ 20 - 0
src/Materials/Textures/baseTexture.ts

@@ -198,6 +198,26 @@ export class BaseTexture implements IAnimatable {
     }
 
     /**
+     * Define if the texture is a 2d array texture (webgl 2) or if false a 2d texture.
+     */
+    @serialize()
+    public get is2DArray(): boolean {
+        if (!this._texture) {
+            return false;
+        }
+
+        return this._texture.is2DArray;
+    }
+
+    public set is2DArray(value: boolean) {
+        if (!this._texture) {
+            return;
+        }
+
+        this._texture.is2DArray = value;
+    }
+
+    /**
      * Define if the texture contains data in gamma space (most of the png/jpg aside bump).
      * HDR texture are usually stored in linear space.
      * This only impacts the PBR and Background materials

+ 1 - 0
src/Materials/Textures/index.ts

@@ -14,6 +14,7 @@ export * from "./Procedurals/index";
 export * from "./rawCubeTexture";
 export * from "./rawTexture";
 export * from "./rawTexture3D";
+export * from "./rawTexture2DArray";
 export * from "./refractionTexture";
 export * from "./renderTargetTexture";
 export * from "./texture";

+ 16 - 0
src/Materials/Textures/internalTexture.ts

@@ -59,6 +59,10 @@ export enum InternalTextureSource {
      */
     Raw3D,
     /**
+     * Texture content is raw 2D array data
+     */
+    Raw2DArray,
+    /**
      * Texture content is a depth texture
      */
     Depth,
@@ -92,6 +96,10 @@ export class InternalTexture {
      */
     public is3D: boolean = false;
     /**
+     * Defines if the texture contains 2D array data
+     */
+    public is2DArray: boolean = false;
+    /**
      * Defines if the texture contains multiview data
      */
     public isMultiview: boolean = false;
@@ -335,6 +343,14 @@ export class InternalTexture {
                 this.isReady = true;
                 return;
 
+            case InternalTextureSource.Raw2DArray:
+                proxy = this._engine.createRawTexture2DArray(this._bufferView, this.baseWidth, this.baseHeight, this.baseDepth, this.format, this.generateMipMaps,
+                    this.invertY, this.samplingMode, this._compression);
+                proxy._swapAndDie(this);
+
+                this.isReady = true;
+                return;
+
             case InternalTextureSource.Dynamic:
                 proxy = this._engine.createDynamicTexture(this.baseWidth, this.baseHeight, this.generateMipMaps, this.samplingMode);
                 proxy._swapAndDie(this);

+ 61 - 0
src/Materials/Textures/rawTexture2DArray.ts

@@ -0,0 +1,61 @@
+import { Scene } from "../../scene";
+import { Engine } from "../../Engines/engine";
+import { Texture } from "./texture";
+import { Constants } from "../../Engines/constants";
+import "../../Engines/Extensions/engine.rawTexture";
+/**
+ * Class used to store 2D array textures containing user data
+ */
+export class RawTexture2DArray extends Texture {
+    private _engine: Engine;
+
+    /**
+     * Create a new RawTexture2DArray
+     * @param data defines the data of the texture
+     * @param width defines the width of the texture
+     * @param height defines the height of the texture
+     * @param depth defines the number of layers of the texture
+     * @param format defines the texture format to use
+     * @param scene defines the hosting scene
+     * @param generateMipMaps defines a boolean indicating if mip levels should be generated (true by default)
+     * @param invertY defines if texture must be stored with Y axis inverted
+     * @param samplingMode defines the sampling mode to use (Texture.TRILINEAR_SAMPLINGMODE by default)
+     * @param textureType defines the texture Type (Engine.TEXTURETYPE_UNSIGNED_INT, Engine.TEXTURETYPE_FLOAT...)
+     */
+    constructor(data: ArrayBufferView, width: number, height: number, depth: number,
+        /** Gets or sets the texture format to use */
+        public format: number, scene: Scene,
+        generateMipMaps: boolean = true,
+        invertY: boolean = false,
+        samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE,
+        textureType = Constants.TEXTURETYPE_UNSIGNED_INT) {
+        super(null, scene, !generateMipMaps, invertY);
+        this._engine = scene.getEngine();
+
+        this._texture = scene.getEngine().createRawTexture2DArray(
+            data,
+            width,
+            height,
+            depth,
+            format,
+            generateMipMaps,
+            invertY,
+            samplingMode,
+            null,
+            textureType
+        );
+
+        this.is2DArray = true;
+    }
+
+    /**
+     * Update the texture with new data
+     * @param data defines the data to store in the texture
+     */
+    public update(data: ArrayBufferView): void {
+        if (!this._texture) {
+            return;
+        }
+        this._engine.updateRawTexture2DArray(this._texture, data, this._texture.format, this._texture!.invertY, null, this._texture.type);
+    }
+}