浏览代码

Initial texture support changes, incomplete

Scott Ramsby 7 年之前
父节点
当前提交
ac0df47ea5
共有 2 个文件被更改,包括 231 次插入29 次删除
  1. 7 5
      src/Engine/babylon.engine.ts
  2. 224 24
      src/Engine/babylon.nativeEngineWrapper.ts

+ 7 - 5
src/Engine/babylon.engine.ts

@@ -5998,7 +5998,7 @@
             return texture;
         }
 
-        private _prepareWebGLTextureContinuation(texture: InternalTexture, scene: Nullable<Scene>, noMipmap: boolean, isCompressed: boolean, samplingMode: number): void {
+        protected _prepareWebGLTextureContinuation(texture: InternalTexture, scene: Nullable<Scene>, noMipmap: boolean, isCompressed: boolean, samplingMode: number): void {
             var gl = this._gl;
             if (!gl) {
                 return;
@@ -6026,7 +6026,7 @@
 
         private _prepareWebGLTexture(texture: InternalTexture, scene: Nullable<Scene>, width: number, height: number, invertY: boolean, noMipmap: boolean, isCompressed: boolean,
             processFunction: (width: number, height: number, continuationCallback: () => void) => boolean, samplingMode: number = Engine.TEXTURE_TRILINEAR_SAMPLINGMODE): void {
-            var maxTextureSize = this.getCaps().maxTextureSize;    
+            var maxTextureSize = this.getCaps().maxTextureSize;
             var potWidth = Math.min(maxTextureSize, this.needPOTTextures ? Tools.GetExponentOfTwo(width, maxTextureSize) : width);
             var potHeight = Math.min(maxTextureSize, this.needPOTTextures ? Tools.GetExponentOfTwo(height, maxTextureSize) : height);
 
@@ -6119,11 +6119,9 @@
 
         /** @hidden */
         public _releaseTexture(texture: InternalTexture): void {
-            var gl = this._gl;
-
             this._releaseFramebufferObjects(texture);
 
-            gl.deleteTexture(texture._webGLTexture);
+            this._deleteTexture(texture._webGLTexture);
 
             // Unbind channels
             this.unbindAllTextures();
@@ -6163,6 +6161,10 @@
             })
         }
 
+        protected _deleteTexture(texture: Nullable<WebGLTexture>): void {
+            this._gl.deleteTexture(texture);
+        }
+
         private setProgram(program: WebGLProgram): void {
             if (this._currentProgram !== program) {
                 this._gl.useProgram(program);

+ 224 - 24
src/Engine/babylon.nativeEngineWrapper.ts

@@ -4,7 +4,7 @@
         requestAnimationFrame(callback: () => void): void;
 
         createIndexBuffer(indices: ArrayBuffer, is32Bits: boolean): WebGLBuffer;
-        bindIndexBuffer(buffer: WebGLBuffer) : void;
+        bindIndexBuffer(buffer: WebGLBuffer): void;
         
         createVertexBuffer(vertices: Float32Array): WebGLBuffer;
         bindVertexBuffer(buffer: WebGLBuffer, indx: number, size: number, type: number, normalized: boolean, stride: number, offset: number): void;
@@ -32,12 +32,35 @@
         setBool(uniform: WebGLUniformLocation, bool: number): void;
         setFloat4(uniform: WebGLUniformLocation, x: number, y: number, z: number, w: number): void;
 
+        createTexture(): WebGLTexture;
+        loadTexture(texture: WebGLTexture, buffer: ArrayBuffer, mipMap: boolean): void;
+        deleteTexture(texture: Nullable<WebGLTexture>): void;
+
         drawIndexed(fillMode: number, indexStart: number, indexCount: number): void;
         draw(fillMode: number, vertexStart: number, vertexCount: number): void;
 
         clear(r: number, g: number, b: number, a: number, backBuffer: boolean, depth: boolean, stencil: boolean): void;
     }
 
+    class NativeSamplingMode {
+        // Must match Filter enum in SpectreEngine.h.
+        public static readonly POINT = 0;
+        public static readonly MINPOINT_MAGPOINT_MIPPOINT = 1;
+        public static readonly BILINEAR = 2;
+        public static readonly MINLINEAR_MAGLINEAR_MIPPOINT = 3;
+        public static readonly TRILINEAR = 4;
+        public static readonly MINLINEAR_MAGLINEAR_MIPLINEAR = 5;
+        public static readonly ANISOTROPIC = 6;
+        public static readonly POINT_COMPARE = 7;
+        public static readonly TRILINEAR_COMPARE = 8;
+        public static readonly MINBILINEAR_MAGPOINT = 9;
+        public static readonly MINLINEAR_MAGPOINT_MIPLINEAR = 10;
+        public static readonly MINPOINT_MAGPOINT_MIPLINEAR = 11;
+        public static readonly MINPOINT_MAGLINEAR_MIPPOINT = 12;
+        public static readonly MINPOINT_MAGLINEAR_MIPLINEAR = 13;
+        public static readonly MINLINEAR_MAGPOINT_MIPPOINT = 14;
+    }
+
     /** @hidden */
     declare var nativeEngineInterop: INativeEngineInterop;
 
@@ -83,6 +106,7 @@
 
             this._options = options;
 
+            // TODO: Initialize this more correctly based on the hardware capabilities reported by Spectre.
             // Init caps
             // We consider we are on a webgl1 capable device
 
@@ -237,6 +261,8 @@
 
             this._drawCalls.addCount(1, false);
 
+            // TODO: Make this implementation more robust like core Engine version.
+
             // Render
             //var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             
@@ -260,6 +286,8 @@
             // this.applyStates();
             this._drawCalls.addCount(1, false);
 
+            // TODO: Make this implementation more robust like core Engine version.
+
             // if (instancesCount) {
             //     this._gl.drawArraysInstanced(drawMode, verticesStart, verticesCount, instancesCount);
             // } else {
@@ -550,39 +578,196 @@
         }
 
         public _createTexture(): WebGLTexture {
-            return {};
+            return this._interop.createTexture();
         }
 
-        public _releaseTexture(texture: InternalTexture): void {
+        protected _deleteTexture(texture: Nullable<WebGLTexture>): void {
+            this._interop.deleteTexture(texture);
         }
 
-        public createTexture(urlArg: string, noMipmap: boolean, invertY: boolean, scene: Scene, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null, buffer: Nullable<ArrayBuffer | HTMLImageElement> = null, fallBack?: InternalTexture, format?: number): InternalTexture {
-            var texture = new InternalTexture(this, InternalTexture.DATASOURCE_URL);
-            var url = String(urlArg);
+        // TODO: Refactor to share more logic with babylon.engine.ts version.
+        /**
+         * Usually called from BABYLON.Texture.ts.
+         * Passed information to create a WebGLTexture
+         * @param urlArg defines a value which contains one of the following:
+         * * A conventional http URL, e.g. 'http://...' or 'file://...'
+         * * A base64 string of in-line texture data, e.g. 'data:image/jpg;base64,/...'
+         * * An indicator that data being passed using the buffer parameter, e.g. 'data:mytexture.jpg'
+         * @param noMipmap defines a boolean indicating that no mipmaps shall be generated.  Ignored for compressed textures.  They must be in the file
+         * @param invertY when true, image is flipped when loaded.  You probably want true. Ignored for compressed textures.  Must be flipped in the file
+         * @param scene needed for loading to the correct scene
+         * @param samplingMode mode with should be used sample / access the texture (Default: BABYLON.Texture.TRILINEAR_SAMPLINGMODE)
+         * @param onLoad optional callback to be called upon successful completion
+         * @param onError optional callback to be called upon failure
+         * @param buffer a source of a file previously fetched as either a base64 string, an ArrayBuffer (compressed or image format), or a Blob
+         * @param fallback an internal argument in case the function must be called again, due to etc1 not having alpha capabilities
+         * @param format internal format.  Default: RGB when extension is '.jpg' else RGBA.  Ignored for compressed textures
+         * @param forcedExtension defines the extension to use to pick the right loader
+         * @returns a InternalTexture for assignment back into BABYLON.Texture
+         */
+        public createTexture(urlArg: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<Scene>, samplingMode: number = Engine.TEXTURE_TRILINEAR_SAMPLINGMODE,
+            onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null,
+            buffer: Nullable<string | ArrayBuffer | Blob> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null,
+            forcedExtension: Nullable<string> = null): InternalTexture {
+            var url = String(urlArg); // assign a new string, so that the original is still available in case of fallback
+            var fromData = url.substr(0, 5) === "data:";
+            var fromBlob = url.substr(0, 5) === "blob:";
+            var isBase64 = fromData && url.indexOf("base64") !== -1;
+
+            let texture = fallback ? fallback : new InternalTexture(this, InternalTexture.DATASOURCE_URL);
+
+            // establish the file extension, if possible
+            var lastDot = url.lastIndexOf('.');
+            var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? url.substring(lastDot).toLowerCase() : "");
+
+            // TODO: Add support for compressed texture formats.
+            var textureFormatInUse: Nullable<string> = null;
+
+            let loader: Nullable<IInternalTextureLoader> = null;
+            for (let availableLoader of Engine._TextureLoaders) {
+                if (availableLoader.canLoad(extension, textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
+                    loader = availableLoader;
+                    break;
+                }
+            }
+
+            if (loader) {
+                url = loader.transformUrl(url, textureFormatInUse);
+            }
 
+            if (scene) {
+                scene._addPendingData(texture);
+            }
             texture.url = url;
             texture.generateMipMaps = !noMipmap;
             texture.samplingMode = samplingMode;
             texture.invertY = invertY;
-            texture.baseWidth = this._options.textureSize;
-            texture.baseHeight = this._options.textureSize;
-            texture.width = this._options.textureSize;
-            texture.height = this._options.textureSize;
-            if (format) {
-                texture.format = format;
+
+            if (!this.doNotHandleContextLost) {
+                // Keep a link to the buffer only if we plan to handle context lost
+                texture._buffer = buffer;
             }
 
-            texture.isReady = true;
+            let onLoadObserver: Nullable<Observer<InternalTexture>> = null;
+            if (onLoad && !fallback) {
+                onLoadObserver = texture.onLoadedObservable.add(onLoad);
+            }
 
-            if (onLoad) {
-                onLoad();
+            if (!fallback) this._internalTexturesCache.push(texture);
+
+            let onInternalError = (message?: string, exception?: any) => {
+                if (scene) {
+                    scene._removePendingData(texture);
+                }
+
+                let customFallback = false;
+                if (loader) {
+                    const fallbackUrl = loader.getFallbackTextureUrl(url, textureFormatInUse);
+                    if (fallbackUrl) {
+                        // Add Back
+                        customFallback = true;
+                        this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                    }
+                }
+
+                if (!customFallback) {
+                    if (onLoadObserver) {
+                        texture.onLoadedObservable.remove(onLoadObserver);
+                    }
+                    if (Tools.UseFallbackTexture) {
+                        this.createTexture(Tools.fallbackTexture, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                    }
+                }
+
+                if (onError) {
+                    onError(message || "Unknown error", exception);
+                }
             }
 
-            this._internalTexturesCache.push(texture);
+            // processing for non-image formats
+            if (loader) {
+                throw new Error("Loading textures from loader not yet implemented.");
+                // var callback = (data: string | ArrayBuffer) => {
+                //     loader!.loadData(data as ArrayBuffer, texture, (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => {
+                //         this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, () => {
+                //                 done();
+                //                 return false;
+                //             }, 
+                //             samplingMode);
+                //     });
+                // }
+
+                // if (!buffer) {
+                //     this._loadFile(url, callback, undefined, scene ? scene.database : undefined, true, (request?: XMLHttpRequest, exception?: any) => {
+                //         onInternalError("Unable to load " + (request ? request.responseURL : url, exception));
+                //     });
+                // } else {
+                //     callback(buffer as ArrayBuffer);
+                // }
+            } else {
+                var onload = (data: string | ArrayBuffer, responseURL?: string) => {
+                    let arrayBuffer = data as ArrayBuffer;
+
+                    if (fromBlob && !this.doNotHandleContextLost) {
+                        // We need to store the image if we need to rebuild the texture
+                        // in case of a webgl context lost
+                        texture._buffer = data;
+                    }
+
+                    if (!texture._webGLTexture) {
+                        //  this.resetTextureCache();
+                        if (scene) {
+                            scene._removePendingData(texture);
+                        }
+
+                        return;
+                    }
+
+                    this._interop.loadTexture(texture._webGLTexture, arrayBuffer, !noMipmap);
+
+                    this._unpackFlipY(invertY === undefined ? true : (invertY ? true : false));
+
+                    texture.baseWidth = width;
+                    texture.baseHeight = height;
+                    texture.width = width;
+                    texture.height = height;
+                    texture.isReady = true;
+        
+                    var filters = this._getSamplingParameters(samplingMode, !noMipmap);
+
+                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filters.mag);
+                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filters.min);
+
+                    // this.resetTextureCache();
+                    if (scene) {
+                        scene._removePendingData(texture);
+                    }
+        
+                    texture.onLoadedObservable.notifyObservers(texture);
+                    texture.onLoadedObservable.clear();
+                };
+
+                if (!fromData || isBase64) {
+                    let onLoadFileError = (request?: XMLHttpRequest, exception?: any) => {
+                        onInternalError("Failed to retrieve " + url + ".", exception);
+                    }
+                    Tools.LoadFile(url, onload, undefined, undefined, /*useArrayBuffer*/true, onLoadFileError);
+                } else if (buffer instanceof ArrayBuffer) {
+                    onload(buffer);
+                } else if (typeof buffer === "string" || buffer instanceof Blob) {
+                    throw new Error("Loading texture from string/Blob not yet implemented.");
+                    //Tools.LoadImage(buffer, onload, onInternalError, null);
+                } else {
+                    throw new Error("Unexpected texture source type.");
+                }
+            }
 
             return texture;
         }
 
+        private _get(samplingMode: number, generateMipMaps: boolean): { min: number; mag: number } {
+        }
+
         public createRenderTargetTexture(size: any, options: boolean | RenderTargetCreationOptions): InternalTexture {
             let fullOptions = new RenderTargetCreationOptions();
 
@@ -661,6 +846,7 @@
         }
 
         public updateDynamicIndexBuffer(indexBuffer: WebGLBuffer, indices: IndicesArray, offset: number = 0): void {
+            throw new Error("updateDynamicIndexBuffer not yet implemented.")
         }
 
         /**
@@ -671,6 +857,7 @@
          * @param byteLength the byte length of the data (optional)
          */
         public updateDynamicVertexBuffer(vertexBuffer: WebGLBuffer, vertices: FloatArray, byteOffset?: number, byteLength?: number): void {
+            throw new Error("updateDynamicVertexBuffer not yet implemented.")
         }
 
         protected _bindTextureDirectly(target: number, texture: Nullable<InternalTexture>, forTextureDataUpdate = false, force = false): boolean {
@@ -682,18 +869,11 @@
             }
         }
 
-        public _bindTexture(channel: number, texture: InternalTexture): void {
-            if (channel < 0) {
-                return;
-            }
-
-            this._bindTextureDirectly(0, texture);
-        }
-
         public _releaseBuffer(buffer: WebGLBuffer): boolean {
             buffer.references--;
 
             if (buffer.references === 0) {
+                // TODO: Proactively delete the underlying buffer here in Spectre.
                 return true;
             }
 
@@ -702,5 +882,25 @@
 
         public releaseEffects() {
         }
+
+        /** @hidden */
+        public _uploadCompressedDataToTextureDirectly(texture: InternalTexture, internalFormat: number, width: number, height: number, data: ArrayBufferView, faceIndex: number = 0, lod: number = 0) {
+            throw new Error("_uploadCompressedDataToTextureDirectly not implemented.");
+        }
+
+        /** @hidden */
+        public _uploadDataToTextureDirectly(texture: InternalTexture, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0): void {
+            throw new Error("_uploadDataToTextureDirectly not implemented.");
+        }
+
+        /** @hidden */
+        public _uploadArrayBufferViewToTexture(texture: InternalTexture, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0): void {
+            throw new Error("_uploadArrayBufferViewToTexture not implemented.");
+        }
+
+        /** @hidden */
+        public _uploadImageToTexture(texture: InternalTexture, image: HTMLImageElement, faceIndex: number = 0, lod: number = 0) {
+            throw new Error("_uploadArrayBufferViewToTexture not implemented.");
+        }
     }
 }