Browse Source

Merge pull request #8867 from DarraghBurkeMS/patches

Texture Inspector Mipmap Improvements
sebavan 5 years ago
parent
commit
f1cd21d371
20 changed files with 151 additions and 130 deletions
  1. 1 1
      dist/preview release/what's new.md
  2. 5 4
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  3. 0 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/addTool.svg
  4. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/brushCursor.svg
  5. 0 11
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/editIcon.svg
  6. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/marqueeCursor.svg
  7. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/pickerCursor.svg
  8. 7 4
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/bottomBar.tsx
  9. 2 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/eyedropper.ts
  10. 2 2
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/floodfill.ts
  11. 4 3
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.tsx
  12. 2 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/rectangleSelect.ts
  13. 4 2
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/propertiesBar.tsx
  14. 1 4
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasComponent.tsx
  15. 76 67
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts
  16. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditor.scss
  17. 11 7
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx
  18. 6 1
      inspector/src/lod.ts
  19. 21 19
      inspector/src/lodCube.ts
  20. 5 2
      inspector/src/textureHelper.ts

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

@@ -88,7 +88,7 @@
 - Popup Window available (To be used in Curve Editor) ([pixelspace](https://github.com/devpixelspace))
 - Add support to update inspector when switching to a new scene ([belfortk](https://github.com/belfortk))
 - Hex Component for Hex inputs on layer masks. ([msDestiny14](https://github.com/msDestiny14))
-- View & edit textures in pop out inspector using tools such as brush and floodfill. Supports region selection, individual channel editing, and resizing. ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
+- View & edit textures in pop out inspector using tools such as brush and floodfill. Supports region selection, individual channel editing, mipmap previews, and resizing. ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
 
 ### Cameras
 

+ 5 - 4
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -75,6 +75,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         
         this.onOpenTextureEditor.bind(this);
         this.onCloseTextureEditor.bind(this);
+        this.openTextureEditor.bind(this);
     }
 
     componentWillUnmount() {
@@ -112,8 +113,8 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
     }
 
     openTextureEditor() {
-        if (this.state.isTextureEditorOpen && this.state.textureEditing !== this.props.texture) {
-            this.onCloseTextureEditor(null, () => this.openTextureEditor());
+        if (this.state.isTextureEditorOpen) {
+            this.onCloseTextureEditor(() => this.openTextureEditor());
             return;
         }
         this.setState({
@@ -124,7 +125,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
 
     onOpenTextureEditor(window: Window) {}
     
-    onCloseTextureEditor(window: Window | null, callback?: {() : void}) {
+    onCloseTextureEditor(callback?: {() : void}) {
         this.setState({
             isTextureEditorOpen: false,
             textureEditing: null
@@ -188,7 +189,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                   title='Texture Inspector'
                   size={this._textureInspectorSize}
                   onOpen={this.onOpenTextureEditor}
-                  onClose={this.onCloseTextureEditor}
+                  onClose={() => this.onCloseTextureEditor}
                   ref={this.popoutWindowRef}
                 >
                     <TextureEditorComponent

+ 0 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/addTool.svg

@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><rect width="40" height="40" style="fill:none"/><path d="M20,10A10,10,0,1,1,10,20,10,10,0,0,1,20,10Zm0,1.5A8.5,8.5,0,1,0,28.5,20,8.51,8.51,0,0,0,20,11.5ZM20,15a.76.76,0,0,1,.75.75v3.5h3.5a.75.75,0,0,1,0,1.5h-3.5v3.5a.75.75,0,0,1-1.5,0v-3.5h-3.5a.75.75,0,0,1,0-1.5h3.5v-3.5A.76.76,0,0,1,20,15Z" style="fill:#fff"/></svg>

File diff suppressed because it is too large
+ 1 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/brushCursor.svg


File diff suppressed because it is too large
+ 0 - 11
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/editIcon.svg


File diff suppressed because it is too large
+ 1 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/marqueeCursor.svg


File diff suppressed because it is too large
+ 1 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/assets/pickerCursor.svg


+ 7 - 4
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/bottomBar.tsx

@@ -1,16 +1,19 @@
 import * as React from 'react';
+import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
 
 interface IBottomBarProps {
-    name: string;
+    texture: BaseTexture;
     mipLevel: number;
-    hasMips: boolean;
 }
 
 export class BottomBar extends React.PureComponent<IBottomBarProps> {
     render() {
+        const factor = Math.pow(2, this.props.mipLevel);
+        const width = Math.ceil(this.props.texture.getSize().width / factor);
+        const height = Math.ceil(this.props.texture.getSize().height / factor);
         return <div id='bottom-bar'>
-            <span id='file-url'>{this.props.name}</span>
-            {this.props.hasMips && <span id='mip-level'>MIP Preview: {this.props.mipLevel}</span>}
+            <span id='file-url'>{this.props.texture.name}</span>
+            {!this.props.texture.noMipmap && <span id='mip-level'>MIP Preview: {this.props.mipLevel} {width}x{height}</span>}
         </div>;
     }
 }

File diff suppressed because it is too large
+ 2 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/eyedropper.ts


+ 2 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/floodfill.ts

@@ -12,9 +12,9 @@ export const Floodfill : IToolData = {
             this.getParameters = getParameters;
         }
 
-        fill() {
+        async fill() {
             const {metadata, startPainting, updatePainting, stopPainting} = this.getParameters();
-            const ctx = startPainting();
+            const ctx = await startPainting();
             ctx.fillStyle = metadata.color;
             ctx.globalAlpha = metadata.alpha;
             ctx.globalCompositeOperation = 'copy';

File diff suppressed because it is too large
+ 4 - 3
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.tsx


File diff suppressed because it is too large
+ 2 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/rectangleSelect.ts


+ 4 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/propertiesBar.tsx

@@ -82,7 +82,9 @@ export class PropertiesBar extends React.PureComponent<IPropertiesBarProps,IProp
 
     render() {
         const {mipLevel, setMipLevel, pixelData, resizeTexture, texture, face, setFace, saveTexture, resetTexture, uploadTexture} = this.props;
-        const maxLevels = 1 + Math.floor(Math.log2(Math.max(texture.getSize().width, texture.getSize().height)));
+        const maxLevels = Math.floor(Math.log2(Math.max(texture.getSize().width, texture.getSize().height)));
+        const engine = texture.getScene()!.getEngine();
+        const mipsEnabled = (!texture.noMipmap && (engine.webGLVersion == 2 || engine._gl.getExtension('EXT_shader_texture_lod')));
         return <div id='properties'>
                 <div className='tab' id='logo-tab'>
                     <img className='icon' src={this._babylonLogo}/>
@@ -123,7 +125,7 @@ export class PropertiesBar extends React.PureComponent<IPropertiesBarProps,IProp
                             />)}
                         </div>
                     }
-                    {!texture.noMipmap &&
+                    {mipsEnabled &&
                         <div className='tab' id='mip-tab'>
                             <img title='Mip Preview Up' className='icon button' src={this._mipUp} onClick={() => mipLevel > 0 && setMipLevel(mipLevel - 1)} />
                             <img title='Mip Preview Down' className='icon button' src={this._mipDown} onClick={() => mipLevel < maxLevels && setMipLevel(mipLevel + 1)} />

+ 1 - 4
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasComponent.tsx

@@ -8,10 +8,7 @@ interface ITextureCanvasComponentProps {
     texture: BaseTexture;
 }
 
-export class TextureCanvasComponent extends React.PureComponent<ITextureCanvasComponentProps> {
-    shouldComponentUpdate(nextProps : ITextureCanvasComponentProps) {
-        return (nextProps.texture !== this.props.texture);
-    }
+export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {
 
     render() {
         return <div>

+ 76 - 67
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts

@@ -56,11 +56,11 @@ export class TextureCanvasManager {
 
     private _size : ISize;
 
-    /* The canvas we paint onto using the canvas API */
+    /** The canvas we paint onto using the canvas API */
     private _2DCanvas : HTMLCanvasElement;
-    /* The canvas we apply post processes to */
+    /** The canvas we apply post processes to */
     private _3DCanvas : HTMLCanvasElement;
-    /* The canvas which handles channel filtering */
+    /** The canvas which handles channel filtering */
     private _channelsTexture : HtmlElementTexture;
 
     private _3DEngine : Engine;
@@ -72,41 +72,45 @@ export class TextureCanvasManager {
     private _face : number = 0;
     private _mipLevel : number = 0;
 
-    /* The texture from the original engine that we invoked the editor on */
+    /** The texture from the original engine that we invoked the editor on */
     private _originalTexture: BaseTexture;
-    /* This is a hidden texture which is only responsible for holding the actual texture memory in the original engine */
+    /** This is a hidden texture which is only responsible for holding the actual texture memory in the original engine */
     private _target : HtmlElementTexture | RawCubeTexture;
-    /* The internal texture representation of the original texture */
+    /** The internal texture representation of the original texture */
     private _originalInternalTexture : Nullable<InternalTexture> = null;
-    /* Keeps track of whether we have modified the texture */
+    /** Keeps track of whether we have modified the texture */
     private _didEdit : boolean = false;
 
     private _plane : Mesh;
     private _planeMaterial : ShaderMaterial;
 
-    /* Tracks which keys are currently pressed */
+    /** Tracks which keys are currently pressed */
     private _keyMap : any = {};
 
-    private static ZOOM_MOUSE_SPEED : number = 0.001;
-    private static ZOOM_KEYBOARD_SPEED : number = 0.4;
-    private static ZOOM_IN_KEY : string = '+';
-    private static ZOOM_OUT_KEY : string = '-';
+    private readonly ZOOM_MOUSE_SPEED : number = 0.001;
+    private readonly ZOOM_KEYBOARD_SPEED : number = 0.4;
+    private readonly ZOOM_IN_KEY : string = '+';
+    private readonly ZOOM_OUT_KEY : string = '-';
 
-    private static PAN_SPEED : number = 0.002;
-    private static PAN_MOUSE_BUTTON : number = 1; // MMB
+    private readonly PAN_SPEED : number = 0.003;
+    private readonly PAN_MOUSE_BUTTON : number = 1; // MMB
 
-    private static MIN_SCALE : number = 0.01;
-    private static GRID_SCALE : number = 0.047;
-    private static MAX_SCALE : number = 10;
+    private readonly MIN_SCALE : number = 0.01;
+    private readonly GRID_SCALE : number = 0.047;
+    private readonly MAX_SCALE : number = 10;
 
-    private static SELECT_ALL_KEY = 'KeyA';
-    private static SAVE_KEY ='KeyS';
-    private static RESET_KEY = 'KeyR';
-    private static DESELECT_KEY = 'Escape'
+    private readonly SELECT_ALL_KEY = 'KeyA';
+    private readonly SAVE_KEY ='KeyS';
+    private readonly RESET_KEY = 'KeyR';
+    private readonly DESELECT_KEY = 'Escape'
+
+    /** The number of milliseconds between texture updates */
+    private readonly PUSH_FREQUENCY = 32;
 
     private _tool : Nullable<ITool>;
 
     private _setPixelData : (pixelData : IPixelData) => void;
+    private _setMipLevel: (mipLevel: number) => void;
 
     private _window : Window;
 
@@ -118,8 +122,8 @@ export class TextureCanvasManager {
     private _setMetadata : (metadata: any) => void;
 
     private _imageData : Uint8Array | Uint8ClampedArray;
-    private _canUpdate : boolean = true;
-    private _shouldUpdate : boolean = false;
+    private _canPush : boolean = true;
+    private _shouldPush : boolean = false;
     private _paintCanvas: HTMLCanvasElement;
 
     public constructor(
@@ -131,7 +135,8 @@ export class TextureCanvasManager {
         setPixelData: (pixelData : IPixelData) => void,
         metadata: IMetadata,
         onUpdate: () => void,
-        setMetadata: (metadata: any) => void
+        setMetadata: (metadata: any) => void,
+        setMipLevel: (level: number) => void
     ) {
         this._window = window;
 
@@ -143,6 +148,7 @@ export class TextureCanvasManager {
         this._metadata = metadata;
         this._onUpdate = onUpdate;
         this._setMetadata = setMetadata;
+        this._setMipLevel = setMipLevel;
 
         this._size = texture.getSize();
         this._originalTexture = texture;
@@ -201,7 +207,7 @@ export class TextureCanvasManager {
         
         this._window.addEventListener('keydown', evt => {
             this._keyMap[evt.code] = true;
-            if (evt.code === TextureCanvasManager.SELECT_ALL_KEY && evt.ctrlKey) {
+            if (evt.code === this.SELECT_ALL_KEY && evt.ctrlKey) {
                 this._setMetadata({
                     select: {
                         x1: 0,
@@ -212,15 +218,15 @@ export class TextureCanvasManager {
                 });
                 evt.preventDefault();
             }
-            if (evt.code === TextureCanvasManager.SAVE_KEY && evt.ctrlKey) {
+            if (evt.code === this.SAVE_KEY && evt.ctrlKey) {
                 this.saveTexture();
                 evt.preventDefault();
             }
-            if (evt.code === TextureCanvasManager.RESET_KEY && evt.ctrlKey) {
+            if (evt.code === this.RESET_KEY && evt.ctrlKey) {
                 this.reset();
                 evt.preventDefault();
             }
-            if (evt.code === TextureCanvasManager.DESELECT_KEY) {
+            if (evt.code === this.DESELECT_KEY) {
                 this._setMetadata({
                     select: {
                         x1: -1,
@@ -240,15 +246,14 @@ export class TextureCanvasManager {
             this._engine.resize();
             this._scene.render();
             this._planeMaterial.setInt('time', new Date().getTime());
-            
         });
 
         this._scale =  1.5 / Math.max(this._size.width, this._size.height);
         this._isPanning = false;
 
         this._scene.onBeforeRenderObservable.add(() => {
-            this._scale = Math.min(Math.max(this._scale, TextureCanvasManager.MIN_SCALE / Math.log2(Math.min(this._size.width, this._size.height))), TextureCanvasManager.MAX_SCALE);
-            if (this._scale > TextureCanvasManager.GRID_SCALE) {
+            this._scale = Math.min(Math.max(this._scale, this.MIN_SCALE / Math.log2(Math.min(this._size.width, this._size.height))), this.MAX_SCALE);
+            if (this._scale > this.GRID_SCALE) {
                 this._planeMaterial.setFloat('showGrid', 1.0);
             } else {
                 this._planeMaterial.setFloat('showGrid', 0.0);
@@ -265,10 +270,10 @@ export class TextureCanvasManager {
             switch (pointerInfo.type) {
                 case PointerEventTypes.POINTERWHEEL:
                     const event = pointerInfo.event as MouseWheelEvent;
-                    this._scale -= (event.deltaY * TextureCanvasManager.ZOOM_MOUSE_SPEED * this._scale);
+                    this._scale -= (event.deltaY * this.ZOOM_MOUSE_SPEED * this._scale);
                     break;
                 case PointerEventTypes.POINTERDOWN:
-                    if (pointerInfo.event.button === TextureCanvasManager.PAN_MOUSE_BUTTON) {
+                    if (pointerInfo.event.button === this.PAN_MOUSE_BUTTON) {
                         this._isPanning = true;
                         this._mouseX = pointerInfo.event.x;
                         this._mouseY = pointerInfo.event.y;
@@ -276,14 +281,14 @@ export class TextureCanvasManager {
                     }
                     break;
                 case PointerEventTypes.POINTERUP:
-                    if (pointerInfo.event.button === TextureCanvasManager.PAN_MOUSE_BUTTON) {
+                    if (pointerInfo.event.button === this.PAN_MOUSE_BUTTON) {
                         this._isPanning = false;
                     }
                     break;
                 case PointerEventTypes.POINTERMOVE:
                     if (this._isPanning) {
-                        this._cameraPos.x -= (pointerInfo.event.x - this._mouseX) / this._scale * TextureCanvasManager.PAN_SPEED;
-                        this._cameraPos.y += (pointerInfo.event.y - this._mouseY) / this._scale * TextureCanvasManager.PAN_SPEED;
+                        this._cameraPos.x -= (pointerInfo.event.x - this._mouseX) * this.PAN_SPEED / this._scale;
+                        this._cameraPos.y += (pointerInfo.event.y - this._mouseY) * this.PAN_SPEED / this._scale;
                         this._mouseX = pointerInfo.event.x;
                         this._mouseY = pointerInfo.event.y;
                     }
@@ -303,11 +308,11 @@ export class TextureCanvasManager {
                 case KeyboardEventTypes.KEYDOWN:
                     this._keyMap[kbInfo.event.key] = true;
                     switch (kbInfo.event.key) {
-                        case TextureCanvasManager.ZOOM_IN_KEY:
-                            this._scale += TextureCanvasManager.ZOOM_KEYBOARD_SPEED * this._scale;
+                        case this.ZOOM_IN_KEY:
+                            this._scale += this.ZOOM_KEYBOARD_SPEED * this._scale;
                             break;
-                        case TextureCanvasManager.ZOOM_OUT_KEY:
-                            this._scale -= TextureCanvasManager.ZOOM_KEYBOARD_SPEED * this._scale;
+                        case this.ZOOM_OUT_KEY:
+                            this._scale -= this.ZOOM_KEYBOARD_SPEED * this._scale;
                             break;
                     }
                     break;
@@ -320,6 +325,7 @@ export class TextureCanvasManager {
 
 
     public async updateTexture() {
+        if (this._mipLevel !== 0) await this._setMipLevel(0);
         this._didEdit = true;
         const element = this._editing3D ? this._3DCanvas : this._2DCanvas;
         if (this._editing3D) {
@@ -336,13 +342,14 @@ export class TextureCanvasManager {
                     {
                         engine: this._originalTexture.getScene()?.getEngine()!,
                         scene: null,
-                        samplingMode: (this._originalTexture as Texture).samplingMode
+                        samplingMode: (this._originalTexture as Texture).samplingMode,
+                        generateMipMaps: this._originalInternalTexture?.generateMipMaps
                     }
                 );
             } else {
                 (this._target as HtmlElementTexture).element = element;
             }
-            this.queueTextureUpdate();
+            this.pushTexture();
         }
         this._originalTexture._texture = this._target._texture;
         this._channelsTexture.element = element;
@@ -350,8 +357,8 @@ export class TextureCanvasManager {
         this._onUpdate();
     }
 
-    private queueTextureUpdate() {
-        if (this._canUpdate) {
+    private pushTexture() {
+        if (this._canPush) {
             (this._target as HtmlElementTexture).update((this._originalTexture as Texture).invertY);
             this._target._texture?.updateSize(this._size.width, this._size.height);
             if (this._editing3D) {
@@ -359,20 +366,21 @@ export class TextureCanvasManager {
             } else {
                 this._imageData = this._2DCanvas.getContext('2d')!.getImageData(0, 0, this._size.width, this._size.height).data;
             }
-            this._canUpdate = false;
-            this._shouldUpdate = false;
+            this._canPush = false;
+            this._shouldPush = false;
             setTimeout(() => {
-                this._canUpdate = true;
-                if (this._shouldUpdate) {
-                    this.queueTextureUpdate();
+                this._canPush = true;
+                if (this._shouldPush) {
+                    this.pushTexture();
                 }
-            }, 32);
+            }, this.PUSH_FREQUENCY);
         } else {
-            this._shouldUpdate = true;
+            this._shouldPush = true;
         }
     }
 
-    public startPainting() : CanvasRenderingContext2D {
+    public async startPainting() : Promise<CanvasRenderingContext2D> {
+        if (this._mipLevel != 0) await this._setMipLevel(0);
         let x = 0, y = 0, w = this._size.width, h = this._size.height;
         if (this._metadata.select.x1 != -1) {
             x = this._metadata.select.x1;
@@ -465,17 +473,17 @@ export class TextureCanvasManager {
         }
     }
 
-    public static paintPixelsOnCanvas(pixelData : Uint8Array, canvas: HTMLCanvasElement) {
+    public paintPixelsOnCanvas(pixelData : Uint8Array, canvas: HTMLCanvasElement) {
         const ctx = canvas.getContext('2d')!;
         const imgData = ctx.createImageData(canvas.width, canvas.height);
         imgData.data.set(pixelData);
         ctx.putImageData(imgData, 0, 0);
     }
 
-    public grabOriginalTexture() {
+    public async grabOriginalTexture() {
         // Grab image data from original texture and paint it onto the context of a DynamicTexture
         this.setSize(this._originalTexture.getSize());
-        TextureHelper.GetTextureDataAsync(
+        const data = await TextureHelper.GetTextureDataAsync(
             this._originalTexture,
             this._size.width,
             this._size.height,
@@ -483,13 +491,13 @@ export class TextureCanvasManager {
             {R:true, G:true, B:true, A:true},
             undefined,
             this._mipLevel
-        ).then(data => {
-            this._imageData = data;
-            TextureCanvasManager.paintPixelsOnCanvas(data, this._2DCanvas);
-            this._3DCanvasTexture.update();
-            this.updateDisplay();
-        })
-    }
+        );
+        this._imageData = data;
+        this.paintPixelsOnCanvas(data, this._2DCanvas);
+        this._3DCanvasTexture.update();
+        this.updateDisplay();
+        return data;
+}
 
     public getMouseCoordinates(pointerInfo : PointerInfo) {
         if (pointerInfo.pickInfo?.hit) {
@@ -587,7 +595,7 @@ export class TextureCanvasManager {
     public async resize(newSize : ISize) {
         const data = await TextureHelper.GetTextureDataAsync(this._originalTexture, newSize.width, newSize.height, this._face, {R: true,G: true,B: true,A: true});
         this.setSize(newSize);
-        TextureCanvasManager.paintPixelsOnCanvas(data, this._2DCanvas);
+        this.paintPixelsOnCanvas(data, this._2DCanvas);
         this.updateTexture();
         this._didEdit = true;
     }
@@ -634,14 +642,15 @@ export class TextureCanvasManager {
                         Texture.NEAREST_SAMPLINGMODE,
                         () => {
                             TextureHelper.GetTextureDataAsync(texture, texture.getSize().width, texture.getSize().height, 0, {R: true, G: true, B: true, A: true})
-                                .then((pixels) => {
+                                .then(async (pixels) => {
                                     if (this._tool && this._tool.instance.onReset) {
                                         this._tool.instance.onReset();
                                     }
-                                    this.setSize(texture.getSize());
-                                    TextureCanvasManager.paintPixelsOnCanvas(pixels, this._2DCanvas);
-                                    this.updateTexture();
                                     texture.dispose();
+                                    this.setSize(texture.getSize());
+                                    this.paintPixelsOnCanvas(pixels, this._2DCanvas);
+                                    await this.updateTexture();
+                                    this._setMipLevel(0);
                                 });
                         }
                     );

+ 1 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditor.scss

@@ -91,6 +91,7 @@
                         text-align: 'left';
                         font-family: 'acumin-pro-condensed';
                         font-size: 15px;
+                        padding-left: 8px;
                     }
         
                     &:last-of-type {

+ 11 - 7
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx

@@ -57,7 +57,7 @@ export interface IToolParameters {
     /** Provides access to the BABYLON namespace */
     BABYLON: any;
     /** Provides a canvas that you can use the canvas API to paint on. */
-    startPainting: () => CanvasRenderingContext2D;
+    startPainting: () => Promise<CanvasRenderingContext2D>;
     /** After you have painted on your canvas, call this method to push the updates back to the texture. */
     updatePainting: () => void;
     /** Call this when you are finished painting. */
@@ -76,10 +76,9 @@ export interface IToolData {
     type: IToolConstructable;
     /**  An SVG icon encoded in Base64 */
     icon: string;
-    /** Whether the tool uses the draggable GUI window */
-    usesWindow? : boolean;
     /** Whether the tool uses postprocesses */
-    is3D? : boolean;
+    is3D?: boolean;
+    cursor?: string;
     settingsComponent? : React.ComponentType<IToolGUIProps>;
 }
 
@@ -174,7 +173,8 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
             (data : IPixelData) => {this.setState({pixelData: data})},
             this.state.metadata,
             () => this.textureDidUpdate(),
-            data => this.setMetadata(data)
+            data => this.setMetadata(data),
+            mipLevel => this.setState({mipLevel})
         );
         this.addTools(defaultTools);
     }
@@ -284,8 +284,12 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
 
     render() {
         const currentTool : ITool | undefined = this.state.tools[this.state.activeToolIndex];
+        let cursor = `initial`;
+        if (currentTool && currentTool.cursor) {
+            cursor = `url(data:image/png;base64,${currentTool.cursor}) 10 10, auto`;
+        }
 
-        return <div id="texture-editor" onPointerDown={this.onPointerDown}>
+        return <div id="texture-editor" onPointerDown={this.onPointerDown} style={{cursor}}>
             <PropertiesBar
                 texture={this.props.texture}
                 saveTexture={this.saveTexture}
@@ -314,7 +318,7 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
             <ChannelsBar channels={this.state.channels} setChannels={(channels) => {this.setState({channels})}}/>
             <TextureCanvasComponent canvas2D={this._2DCanvas} canvas3D={this._3DCanvas} canvasUI={this._UICanvas} texture={this.props.texture}/>
             <ToolSettings tool={currentTool} />
-            <BottomBar name={this.props.url} mipLevel={this.state.mipLevel} hasMips={!this.props.texture.noMipmap}/>
+            <BottomBar texture={this.props.texture} mipLevel={this.state.mipLevel}/>
         </div>
     }
 }

+ 6 - 1
inspector/src/lod.ts

@@ -2,12 +2,17 @@ import { Effect } from "babylonjs/Materials/effect";
 
 let name = 'lodPixelShader';
 let shader = `
+#extension GL_EXT_shader_texture_lod : enable
+
+precision highp float;
+
 varying vec2 vUV;
 uniform sampler2D textureSampler;
 uniform float lod;
+uniform vec2 texSize;
 void main(void)
 {
-gl_FragColor=textureLod(textureSampler,vUV,lod);
+    gl_FragColor = textureLod(textureSampler,vUV,lod);
 }`;
 
 Effect.ShadersStore[name] = shader;

+ 21 - 19
inspector/src/lodCube.ts

@@ -2,30 +2,32 @@ import { Effect } from "babylonjs/Materials/effect";
 
 let name = 'lodCubePixelShader';
 let shader = `
+precision highp float;
+
 varying vec2 vUV;
 uniform samplerCube textureSampler;
 uniform float lod;
 void main(void)
 {
-vec2 uv=vUV*2.0-1.0;
-#ifdef POSITIVEX
-gl_FragColor=textureCube(textureSampler,vec3(1.001,uv.y,uv.x),lod);
-#endif
-#ifdef NEGATIVEX
-gl_FragColor=textureCube(textureSampler,vec3(-1.001,uv.y,uv.x),lod);
-#endif
-#ifdef POSITIVEY
-gl_FragColor=textureCube(textureSampler,vec3(uv.y,1.001,uv.x),lod);
-#endif
-#ifdef NEGATIVEY
-gl_FragColor=textureCube(textureSampler,vec3(uv.y,-1.001,uv.x),lod);
-#endif
-#ifdef POSITIVEZ
-gl_FragColor=textureCube(textureSampler,vec3(uv,1.001),lod);
-#endif
-#ifdef NEGATIVEZ
-gl_FragColor=textureCube(textureSampler,vec3(uv,-1.001),lod);
-#endif
+    vec2 uv=vUV*2.0-1.0;
+    #ifdef POSITIVEX
+    gl_FragColor=textureCube(textureSampler,vec3(1.001,uv.y,uv.x),lod);
+    #endif
+    #ifdef NEGATIVEX
+    gl_FragColor=textureCube(textureSampler,vec3(-1.001,uv.y,uv.x),lod);
+    #endif
+    #ifdef POSITIVEY
+    gl_FragColor=textureCube(textureSampler,vec3(uv.y,1.001,uv.x),lod);
+    #endif
+    #ifdef NEGATIVEY
+    gl_FragColor=textureCube(textureSampler,vec3(uv.y,-1.001,uv.x),lod);
+    #endif
+    #ifdef POSITIVEZ
+    gl_FragColor=textureCube(textureSampler,vec3(uv,1.001),lod);
+    #endif
+    #ifdef NEGATIVEZ
+    gl_FragColor=textureCube(textureSampler,vec3(uv,-1.001),lod);
+    #endif
 }`;
 
 Effect.ShadersStore[name] = shader;

+ 5 - 2
inspector/src/textureHelper.ts

@@ -25,7 +25,7 @@ export class TextureHelper {
         let lodPostProcess: PostProcess;
 
         if (!texture.isCube) {
-            lodPostProcess = new PostProcess("lod", "lod", ["lod"], null, 1.0, null, Texture.NEAREST_SAMPLINGMODE, engine);
+            lodPostProcess = new PostProcess("lod", "lod", ["lod"], null, 1.0, null, Texture.NEAREST_NEAREST_MIPNEAREST, engine);
         } else {
             const faceDefines = [
                 "#define POSITIVEX",
@@ -35,7 +35,7 @@ export class TextureHelper {
                 "#define POSITIVEZ",
                 "#define NEGATIVEZ",
             ];
-            lodPostProcess = new PostProcess("lodCube", "lodCube", ["lod"], null, 1.0, null, Texture.NEAREST_SAMPLINGMODE, engine, false, faceDefines[face]);
+            lodPostProcess = new PostProcess("lodCube", "lodCube", ["lod"], null, 1.0, null, Texture.NEAREST_NEAREST_MIPNEAREST, engine, false, faceDefines[face]);
         }
 
         
@@ -68,7 +68,10 @@ export class TextureHelper {
         let internalTexture = rtt.getInternalTexture();
 
         if (internalTexture) {
+            const samplingMode = (texture as Texture).samplingMode;
+            texture.updateSamplingMode(Texture.NEAREST_NEAREST_MIPNEAREST);
             scene.postProcessManager.directRender([lodPostProcess], internalTexture);
+            texture.updateSamplingMode(samplingMode);
 
             // Read the contents of the framebuffer
             var numberOfChannelsByLine = width * 4;