Procházet zdrojové kódy

Merge pull request #7001 from atg/master

Implement 2D Array textures
David Catuhe před 5 roky
rodič
revize
f287a1ea59

+ 1 - 0
dist/preview release/what's new.md

@@ -41,6 +41,7 @@
 - Added optional parameter to use Euler angles in planeRotationGizmo ([CedricGuillemet](https://github.com/CedricGuillemet))
 - Added optional parameter to use Euler angles in planeRotationGizmo ([CedricGuillemet](https://github.com/CedricGuillemet))
 - Added `AnimationGroup.onAnimationGroupLoopObservable` ([Deltakosh](https://github.com/deltakosh/))
 - Added `AnimationGroup.onAnimationGroupLoopObservable` ([Deltakosh](https://github.com/deltakosh/))
 - Supports custom materials to generate glow through ```referenceMeshToUseItsOwnMaterial``` in the ```GlowLayer``` ([sebavan](http://www.github.com/sebavan))
 - Supports custom materials to generate glow through ```referenceMeshToUseItsOwnMaterial``` in the ```GlowLayer``` ([sebavan](http://www.github.com/sebavan))
+- Added `RawTexture2DArray` to enable use of WebGL2 2D array textures by custom shaders ([atg](https://github.com/atg))
 
 
 ### Engine
 ### Engine
 
 

+ 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="Class" value={texture.getClassName()} />
                     <TextLineComponent label="Has alpha" value={texture.hasAlpha ? "Yes" : "No"} />
                     <TextLineComponent label="Has alpha" value={texture.hasAlpha ? "Yes" : "No"} />
                     <TextLineComponent label="Is 3D" value={texture.is3D ? "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 cube" value={texture.isCube ? "Yes" : "No"} />
                     <TextLineComponent label="Is render target" value={texture.isRenderTarget ? "Yes" : "No"} />
                     <TextLineComponent label="Is render target" value={texture.isRenderTarget ? "Yes" : "No"} />
                     <TextLineComponent label="Has mipmaps" value={!texture.noMipmap ? "Yes" : "No"} />
                     <TextLineComponent label="Has mipmaps" value={!texture.noMipmap ? "Yes" : "No"} />

+ 124 - 57
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...)
          * @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;
         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 2D array 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;
     }
     }
 }
 }
 
 
@@ -453,73 +489,104 @@ Engine.prototype.createRawCubeTextureFromUrl = function(url: string, scene: Scen
     return texture;
     return texture;
 };
 };
 
 
-Engine.prototype.createRawTexture3D = 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.Raw3D);
-    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.is3D = true;
-
-    if (!this._doNotHandleContextLost) {
-        texture._bufferView = data;
-    }
+/**
+ * Create a function for createRawTexture3D/createRawTexture2DArray
+ * @param is3D true for TEXTURE_3D and false for TEXTURE_2D_ARRAY
+ * @hidden
+ */
+function _makeCreateRawTextureFunction(is3D: boolean) {
+    return function(this: Engine, 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 target = is3D ? this._gl.TEXTURE_3D : this._gl.TEXTURE_2D_ARRAY;
+        var source = is3D ? InternalTextureSource.Raw3D : InternalTextureSource.Raw2DArray;
+        var texture = new InternalTexture(this, source);
+        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;
+        if (is3D) {
+            texture.is3D = true;
+        } else {
+            texture.is2DArray = true;
+        }
 
 
-    this.updateRawTexture3D(texture, data, format, invertY, compression, textureType);
-    this._bindTextureDirectly(this._gl.TEXTURE_3D, texture, true);
+        if (!this._doNotHandleContextLost) {
+            texture._bufferView = data;
+        }
 
 
-    // Filters
-    var filters = this._getSamplingParameters(samplingMode, generateMipMaps);
+        if (is3D) {
+            this.updateRawTexture3D(texture, data, format, invertY, compression, textureType);
+        } else {
+            this.updateRawTexture2DArray(texture, data, format, invertY, compression, textureType);
+        }
+        this._bindTextureDirectly(target, texture, true);
 
 
-    this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
-    this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MIN_FILTER, filters.min);
+        // Filters
+        var filters = this._getSamplingParameters(samplingMode, generateMipMaps);
 
 
-    if (generateMipMaps) {
-        this._gl.generateMipmap(this._gl.TEXTURE_3D);
-    }
+        this._gl.texParameteri(target, this._gl.TEXTURE_MAG_FILTER, filters.mag);
+        this._gl.texParameteri(target, this._gl.TEXTURE_MIN_FILTER, filters.min);
 
 
-    this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
+        if (generateMipMaps) {
+            this._gl.generateMipmap(target);
+        }
 
 
-    this._internalTexturesCache.push(texture);
+        this._bindTextureDirectly(target, null);
 
 
-    return texture;
-};
+        this._internalTexturesCache.push(texture);
 
 
-Engine.prototype.updateRawTexture3D = 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);
+        return texture;
+    };
+}
 
 
-    this._bindTextureDirectly(this._gl.TEXTURE_3D, texture, true);
-    this._unpackFlipY(invertY === undefined ? true : (invertY ? true : false));
+Engine.prototype.createRawTexture2DArray = _makeCreateRawTextureFunction(false);
+Engine.prototype.createRawTexture3D = _makeCreateRawTextureFunction(true);
+
+/**
+ * Create a function for updateRawTexture3D/updateRawTexture2DArray
+ * @param is3D true for TEXTURE_3D and false for TEXTURE_2D_ARRAY
+ * @hidden
+ */
+function _makeUpdateRawTextureFunction(is3D: boolean) {
+    return function(this: Engine, texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): void {
+        var target = is3D ? this._gl.TEXTURE_3D : this._gl.TEXTURE_2D_ARRAY;
+        var internalType = this._getWebGLTextureType(textureType);
+        var internalFormat = this._getInternalFormat(format);
+        var internalSizedFomat = this._getRGBABufferInternalSizedFormat(textureType, format);
+
+        this._bindTextureDirectly(target, 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 (!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 (texture.width % 4 !== 0) {
-        this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1);
-    }
+        if (compression && data) {
+            this._gl.compressedTexImage3D(target, 0, (<any>this.getCaps().s3tc)[compression], texture.width, texture.height, texture.depth, 0, data);
+        } else {
+            this._gl.texImage3D(target, 0, internalSizedFomat, texture.width, texture.height, texture.depth, 0, internalFormat, internalType, data);
+        }
 
 
-    if (compression && data) {
-        this._gl.compressedTexImage3D(this._gl.TEXTURE_3D, 0, (<any>this.getCaps().s3tc)[compression], texture.width, texture.height, texture.depth, 0, data);
-    } else {
-        this._gl.texImage3D(this._gl.TEXTURE_3D, 0, internalSizedFomat, texture.width, texture.height, texture.depth, 0, internalFormat, internalType, data);
-    }
+        if (texture.generateMipMaps) {
+            this._gl.generateMipmap(target);
+        }
+        this._bindTextureDirectly(target, null);
+        // this.resetTextureCache();
+        texture.isReady = true;
+    };
+}
 
 
-    if (texture.generateMipMaps) {
-        this._gl.generateMipmap(this._gl.TEXTURE_3D);
-    }
-    this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
-    // this.resetTextureCache();
-    texture.isReady = true;
-};
+Engine.prototype.updateRawTexture2DArray = _makeUpdateRawTextureFunction(false);
+Engine.prototype.updateRawTexture3D = _makeUpdateRawTextureFunction(true);

+ 2 - 0
src/Engines/nativeEngine.ts

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

+ 55 - 20
src/Engines/thinEngine.ts

@@ -353,6 +353,7 @@ export class ThinEngine {
     private _emptyTexture: Nullable<InternalTexture>;
     private _emptyTexture: Nullable<InternalTexture>;
     private _emptyCubeTexture: Nullable<InternalTexture>;
     private _emptyCubeTexture: Nullable<InternalTexture>;
     private _emptyTexture3D: Nullable<InternalTexture>;
     private _emptyTexture3D: Nullable<InternalTexture>;
+    private _emptyTexture2DArray: Nullable<InternalTexture>;
 
 
     /** @hidden */
     /** @hidden */
     public _frameHandler: number;
     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
      * Gets the default empty cube texture
      */
      */
     public get emptyCubeTexture(): InternalTexture {
     public get emptyCubeTexture(): InternalTexture {
@@ -2854,6 +2866,24 @@ export class ThinEngine {
         throw _DevTools.WarnImport("Engine.RawTexture");
         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 2D array 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;
     private _unpackFlipYCached: Nullable<boolean> = null;
 
 
     /**
     /**
@@ -2886,21 +2916,19 @@ export class ThinEngine {
      */
      */
     public updateTextureSamplingMode(samplingMode: number, texture: InternalTexture): void {
     public updateTextureSamplingMode(samplingMode: number, texture: InternalTexture): void {
         var filters = this._getSamplingParameters(samplingMode, texture.generateMipMaps);
         var filters = this._getSamplingParameters(samplingMode, texture.generateMipMaps);
-
+        var target = this._gl.TEXTURE_2D;
         if (texture.isCube) {
         if (texture.isCube) {
-            this._setTextureParameterInteger(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MAG_FILTER, filters.mag, texture);
-            this._setTextureParameterInteger(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MIN_FILTER, filters.min);
-            this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
+            target = this._gl.TEXTURE_CUBE_MAP;
         } else if (texture.is3D) {
         } else if (texture.is3D) {
-            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 {
-            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);
-            this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
+            target = this._gl.TEXTURE_3D;
+        } else if (texture.is2DArray) {
+            target = this._gl.TEXTURE_2D_ARRAY;
         }
         }
 
 
+        this._setTextureParameterInteger(target, this._gl.TEXTURE_MAG_FILTER, filters.mag, texture);
+        this._setTextureParameterInteger(target, this._gl.TEXTURE_MIN_FILTER, filters.min);
+        this._bindTextureDirectly(target, null);
+
         texture.samplingMode = samplingMode;
         texture.samplingMode = samplingMode;
     }
     }
 
 
@@ -3239,6 +3267,7 @@ export class ThinEngine {
             this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
             this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
             if (this.webGLVersion > 1) {
             if (this.webGLVersion > 1) {
                 this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
+                this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, null);
             }
             }
         }
         }
     }
     }
@@ -3291,6 +3320,7 @@ export class ThinEngine {
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
                 if (this.webGLVersion > 1) {
                 if (this.webGLVersion > 1) {
                     this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
                     this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
+                    this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, null);
                 }
                 }
             }
             }
             return false;
             return false;
@@ -3318,6 +3348,9 @@ export class ThinEngine {
         else if (texture.is3D) {
         else if (texture.is3D) {
             internalTexture = this.emptyTexture3D;
             internalTexture = this.emptyTexture3D;
         }
         }
+        else if (texture.is2DArray) {
+            internalTexture = this.emptyTexture2DArray;
+        }
         else {
         else {
             internalTexture = this.emptyTexture;
             internalTexture = this.emptyTexture;
         }
         }
@@ -3340,29 +3373,31 @@ export class ThinEngine {
             if (needToBind) {
             if (needToBind) {
                 this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, internalTexture, isPartOfTextureArray);
                 this._bindTextureDirectly(this._gl.TEXTURE_2D_ARRAY, internalTexture, isPartOfTextureArray);
             }
             }
-        } else if (internalTexture && internalTexture.is3D) {
+        } else if (internalTexture && (internalTexture.is3D || internalTexture.is2DArray)) {
+            let is3D = internalTexture.is3D;
+            let target = is3D ? this._gl.TEXTURE_3D : this._gl.TEXTURE_2D_ARRAY;
+
             if (needToBind) {
             if (needToBind) {
-                this._bindTextureDirectly(this._gl.TEXTURE_3D, internalTexture, isPartOfTextureArray);
+                this._bindTextureDirectly(target, internalTexture, isPartOfTextureArray);
             }
             }
 
 
             if (internalTexture && internalTexture._cachedWrapU !== texture.wrapU) {
             if (internalTexture && internalTexture._cachedWrapU !== texture.wrapU) {
                 internalTexture._cachedWrapU = texture.wrapU;
                 internalTexture._cachedWrapU = texture.wrapU;
-                this._setTextureParameterInteger(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_S, this._getTextureWrapMode(texture.wrapU), internalTexture);
+                this._setTextureParameterInteger(target, this._gl.TEXTURE_WRAP_S, this._getTextureWrapMode(texture.wrapU), internalTexture);
             }
             }
 
 
             if (internalTexture && internalTexture._cachedWrapV !== texture.wrapV) {
             if (internalTexture && internalTexture._cachedWrapV !== texture.wrapV) {
                 internalTexture._cachedWrapV = texture.wrapV;
                 internalTexture._cachedWrapV = texture.wrapV;
-                this._setTextureParameterInteger(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_T, this._getTextureWrapMode(texture.wrapV), internalTexture);
+                this._setTextureParameterInteger(target, this._gl.TEXTURE_WRAP_T, this._getTextureWrapMode(texture.wrapV), internalTexture);
             }
             }
 
 
-            if (internalTexture && internalTexture._cachedWrapR !== texture.wrapR) {
+            if (is3D && internalTexture && internalTexture._cachedWrapR !== texture.wrapR) {
                 internalTexture._cachedWrapR = texture.wrapR;
                 internalTexture._cachedWrapR = texture.wrapR;
-                this._setTextureParameterInteger(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_R, this._getTextureWrapMode(texture.wrapR), internalTexture);
+                this._setTextureParameterInteger(target, this._gl.TEXTURE_WRAP_R, this._getTextureWrapMode(texture.wrapR), internalTexture);
             }
             }
 
 
-            this._setAnisotropicLevel(this._gl.TEXTURE_3D, texture);
-        }
-        else if (internalTexture && internalTexture.isCube) {
+            this._setAnisotropicLevel(target, texture);
+        } else if (internalTexture && internalTexture.isCube) {
             if (needToBind) {
             if (needToBind) {
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, internalTexture, isPartOfTextureArray);
                 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).
      * Define if the texture contains data in gamma space (most of the png/jpg aside bump).
      * HDR texture are usually stored in linear space.
      * HDR texture are usually stored in linear space.
      * This only impacts the PBR and Background materials
      * 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 "./rawCubeTexture";
 export * from "./rawTexture";
 export * from "./rawTexture";
 export * from "./rawTexture3D";
 export * from "./rawTexture3D";
+export * from "./rawTexture2DArray";
 export * from "./refractionTexture";
 export * from "./refractionTexture";
 export * from "./renderTargetTexture";
 export * from "./renderTargetTexture";
 export * from "./texture";
 export * from "./texture";

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

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