浏览代码

Merge pull request #8779 from DarraghBurkeMS/react-tools

Replace Babylon GUI with React for tool settings UI
David Catuhe 5 年之前
父节点
当前提交
a635894c9c
共有 14 个文件被更改,包括 302 次插入353 次删除
  1. 1 1
      dist/preview release/what's new.md
  2. 13 9
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  3. 2 2
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/bottomBar.tsx
  4. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar.tsx
  5. 0 108
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.ts
  6. 99 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.tsx
  7. 0 149
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.ts
  8. 144 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.tsx
  9. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/propertiesBar.tsx
  10. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasComponent.tsx
  11. 0 75
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts
  12. 17 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditor.scss
  13. 8 6
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx
  14. 15 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/toolSettings.tsx

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

@@ -84,7 +84,7 @@
 - Popup Window available (To be used in Curve Editor) ([pixelspace](https://github.com/devpixelspace))
 - 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))
 - 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))
 - Hex Component for Hex inputs on layer masks. ([msDestiny14](https://github.com/msDestiny14))
-- View & edit textures in pop out inspector using canvas and postprocesses. Supports region selection and individual channel editing. ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
+- View & edit textures in pop out inspector using 5 tools. Supports region selection and individual channel editing. ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
 
 
 ### Cameras
 ### Cameras
 
 

+ 13 - 9
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -48,6 +48,8 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
     private popoutWindowRef : React.RefObject<PopupComponent>;
     private popoutWindowRef : React.RefObject<PopupComponent>;
     private textureLineRef: React.RefObject<TextureLineComponent>;
     private textureLineRef: React.RefObject<TextureLineComponent>;
 
 
+    private _textureInspectorSize = {width: 1024, height: 490};
+
 
 
     constructor(props: ITexturePropertyGridComponentProps) {
     constructor(props: ITexturePropertyGridComponentProps) {
         super(props);
         super(props);
@@ -70,6 +72,9 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         this._adtInstrumentation = new AdvancedDynamicTextureInstrumentation(adt);
         this._adtInstrumentation = new AdvancedDynamicTextureInstrumentation(adt);
         this._adtInstrumentation!.captureRenderTime = true;
         this._adtInstrumentation!.captureRenderTime = true;
         this._adtInstrumentation!.captureLayoutTime = true;
         this._adtInstrumentation!.captureLayoutTime = true;
+        
+        this.onOpenTextureEditor.bind(this);
+        this.onCloseTextureEditor.bind(this);
     }
     }
 
 
     componentWillUnmount() {
     componentWillUnmount() {
@@ -106,9 +111,9 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         }, undefined, true);
         }, undefined, true);
     }
     }
 
 
-    onOpenTextureEditor() {
+    openTextureEditor() {
         if (this.state.isTextureEditorOpen && this.state.textureEditing !== this.props.texture) {
         if (this.state.isTextureEditorOpen && this.state.textureEditing !== this.props.texture) {
-            this.onCloseTextureEditor(null, () => this.onOpenTextureEditor());
+            this.onCloseTextureEditor(null, () => this.openTextureEditor());
             return;
             return;
         }
         }
         this.setState({
         this.setState({
@@ -116,6 +121,8 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
             textureEditing: this.props.texture
             textureEditing: this.props.texture
         });
         });
     }
     }
+
+    onOpenTextureEditor(window: Window) {}
     
     
     onCloseTextureEditor(window: Window | null, callback?: {() : void}) {
     onCloseTextureEditor(window: Window | null, callback?: {() : void}) {
         this.setState({
         this.setState({
@@ -169,7 +176,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                 <LineContainerComponent globalState={this.props.globalState} title="PREVIEW">
                 <LineContainerComponent globalState={this.props.globalState} title="PREVIEW">
                     <TextureLineComponent ref={this.textureLineRef} texture={texture} width={256} height={256} globalState={this.props.globalState} />
                     <TextureLineComponent ref={this.textureLineRef} texture={texture} width={256} height={256} globalState={this.props.globalState} />
                     <FileButtonLineComponent label="Load texture from file" onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
                     <FileButtonLineComponent label="Load texture from file" onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
-                    <ButtonLineComponent label="Edit" onClick={() => this.onOpenTextureEditor()} />
+                    <ButtonLineComponent label="Edit" onClick={() => this.openTextureEditor()} />
                     <TextInputLineComponent label="URL" value={textureUrl} lockObject={this.props.lockObject} onChange={url => {
                     <TextInputLineComponent label="URL" value={textureUrl} lockObject={this.props.lockObject} onChange={url => {
                         (texture as Texture).updateURL(url);
                         (texture as Texture).updateURL(url);
                         this.forceRefresh();
                         this.forceRefresh();
@@ -179,15 +186,12 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                 <PopupComponent
                 <PopupComponent
                   id='texture-editor'
                   id='texture-editor'
                   title='Texture Inspector'
                   title='Texture Inspector'
-                  size={{ width: 1024, height: 490 }}
-                  onOpen={(window: Window) => {}}
-                  onClose={(window: Window) =>
-                    this.onCloseTextureEditor(window)
-                  }
+                  size={this._textureInspectorSize}
+                  onOpen={this.onOpenTextureEditor}
+                  onClose={this.onCloseTextureEditor}
                   ref={this.popoutWindowRef}
                   ref={this.popoutWindowRef}
                 >
                 >
                     <TextureEditorComponent
                     <TextureEditorComponent
-                        globalState={this.props.globalState}
                         texture={this.props.texture}
                         texture={this.props.texture}
                         url={textureUrl}
                         url={textureUrl}
                         window={this.popoutWindowRef}
                         window={this.popoutWindowRef}

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

@@ -1,12 +1,12 @@
 import * as React from 'react';
 import * as React from 'react';
 
 
-interface BottomBarProps {
+interface IBottomBarProps {
     name: string;
     name: string;
     mipLevel: number;
     mipLevel: number;
     hasMips: boolean;
     hasMips: boolean;
 }
 }
 
 
-export class BottomBar extends React.Component<BottomBarProps> {
+export class BottomBar extends React.PureComponent<IBottomBarProps> {
     render() {
     render() {
         return <div id='bottom-bar'>
         return <div id='bottom-bar'>
             <span id='file-url'>{this.props.name}</span>
             <span id='file-url'>{this.props.name}</span>

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

@@ -16,7 +16,7 @@ interface IChannelsBarProps {
 const eyeOpen = require('./assets/eyeOpen.svg');
 const eyeOpen = require('./assets/eyeOpen.svg');
 const eyeClosed = require('./assets/eyeClosed.svg');
 const eyeClosed = require('./assets/eyeClosed.svg');
 
 
-export class ChannelsBar extends React.Component<IChannelsBarProps> {
+export class ChannelsBar extends React.PureComponent<IChannelsBarProps> {
     render() {
     render() {
         return <div id='channels-bar'>
         return <div id='channels-bar'>
             {this.props.channels.map(
             {this.props.channels.map(

+ 0 - 108
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.ts

@@ -1,108 +0,0 @@
-import { IToolData, IToolParameters, IToolType } from '../textureEditorComponent';
-import { TextBlock } from 'babylonjs-gui/2D/controls/textBlock';
-import { Slider } from 'babylonjs-gui/2D/controls/sliders/slider';
-
-export const Contrast : IToolData = {
-    name: 'Contrast/Exposure',
-    type: class implements IToolType {
-        getParameters: () => IToolParameters;
-        contrast : number = 1.0;
-        exposure : number = 1.0;
-        GUI: {
-            contrastLabel : TextBlock;
-            contrastSlider : Slider;
-            exposureLabel : TextBlock;
-            exposureSlider : Slider;
-        }
-        constructor(getParameters: () => IToolParameters) {
-            this.getParameters = getParameters;
-        }
-        setExposure(exposure : number) {
-            this.exposure = exposure;
-            this.GUI.exposureLabel.text = `Exposure: ${this.exposure}`;
-            const {scene3D, updateTexture} = this.getParameters();
-            scene3D.imageProcessingConfiguration.isEnabled = true;
-            scene3D.imageProcessingConfiguration.exposure = this.computeExposure(this.exposure);
-            updateTexture();
-        }
-        setContrast(contrast : number) {
-            this.contrast = contrast;
-            this.GUI.contrastLabel.text = `Contrast: ${this.contrast}`;
-            const {scene3D, updateTexture} = this.getParameters();
-            scene3D.imageProcessingConfiguration.isEnabled = true;
-            scene3D.imageProcessingConfiguration.contrast = this.computeContrast(contrast);
-            updateTexture();
-        }
-        /** Maps slider values to post processing values using an exponential regression */
-        computeExposure(sliderValue : number) {
-            if (sliderValue <= 0) {
-                return 1 - (-sliderValue / 100);
-            } else {
-                return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
-            }
-        }
-        /** Maps slider values to post processing values using an exponential regression */
-        computeContrast(sliderValue : number) {
-            if (sliderValue <= 0) {
-                return 1 - (-sliderValue / 100);
-            } else {
-                return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
-            }
-        }
-        setup() {
-            this.contrast = 0;
-            this.exposure = 0;
-            const {GUI} = this.getParameters();
-            const contrastLabel = new TextBlock();
-            contrastLabel.style = GUI.style;
-            contrastLabel.height = '20px';
-            contrastLabel.color = '#ffffff';
-            const contrastSlider = new Slider();
-            contrastSlider.value = this.contrast;
-            contrastSlider.minimum = -100;
-            contrastSlider.maximum = 100;
-            contrastSlider.height = '20px';
-            contrastSlider.isThumbCircle = true;
-            contrastSlider.background = '#a3a3a3';
-            contrastSlider.color = '#33648f';
-            contrastSlider.borderColor = '#33648f';
-            contrastSlider.onValueChangedObservable.add(evt => this.setContrast(evt.valueOf()));
-            const exposureLabel = new TextBlock();
-            exposureLabel.style = GUI.style;
-            exposureLabel.height = '20px';
-            exposureLabel.color = '#ffffff';
-            const exposureSlider = new Slider();
-            exposureSlider.value = this.exposure;
-            exposureSlider.minimum = -100;
-            exposureSlider.maximum = 100;
-            exposureSlider.height = '20px';
-            exposureSlider.isThumbCircle = true;
-            exposureSlider.background = '#a3a3a3';
-            exposureSlider.color = '#33648f';
-            exposureSlider.borderColor = '#33648f';
-            exposureSlider.onValueChangedObservable.add(evt => this.setExposure(evt.valueOf()));
-            GUI.toolWindow.addControl(contrastLabel);
-            GUI.toolWindow.addControl(contrastSlider);
-            GUI.toolWindow.addControl(exposureLabel);
-            GUI.toolWindow.addControl(exposureSlider);
-            this.GUI = {contrastLabel, contrastSlider, exposureLabel, exposureSlider};
-            this.setExposure(this.exposure);
-            this.setContrast(this.contrast);
-        }
-        cleanup() {
-            Object.entries(this.GUI).forEach(([key, value]) => value.dispose());
-        }
-        onReset() {
-            this.GUI.contrastSlider.value = 0;
-            this.GUI.exposureSlider.value = 0;
-        }
-    },
-    usesWindow: true,
-    is3D: true,
-    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0i
-    NDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMTcuNTUsMjYuNTVsOC41OS0zLjIxQTYuODYsNi44NiwwLDAsMSwyNCwyNS43NWwtMy4x
-    OSwxLjE5QTcsNywwLDAsMSwxNy41NSwyNi41NVpNMjAsMTEuNUE4LjUsOC41LDAsMSwwLDI4LjUsMjAsOC41MSw4LjUxLDAsMCwwLDIwLDExLjVNMjAsMTBBMTAsMTAs
-    MCwxLDEsMTAsMjAsMTAsMTAsMCwwLDEsMjAsMTBabS0yLjQ1LDUuMzQsNS0xLjg2QTcsNywwLDAsMCwxOS40NCwxM2wtMS44OS43MVptMCwzLjIsNy44OC0yLjk0YTYu
-    ODgsNi44OCwwLDAsMC0xLjE5LTEuMTZsLTYuNjksMi41Wm0wLDMuMiw5LjIzLTMuNDRhNy42OCw3LjY4LDAsMCwwLS41Mi0xLjQxbC04LjcxLDMuMjVabTAsMS42djEu
-    Nmw5LjI4LTMuNDZBNi42Nyw2LjY3LDAsMCwwLDI3LDE5LjgyWiIgc3R5bGU9ImZpbGw6I2ZmZiIvPjwvc3ZnPg==`
-}

+ 99 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.tsx

@@ -0,0 +1,99 @@
+import * as React from 'react';
+import { IToolData, IToolParameters, IToolType, IToolGUIProps } from '../textureEditorComponent';
+
+class contrastTool implements IToolType {
+    getParameters: () => IToolParameters;
+    contrast : number = 1.0;
+    exposure : number = 1.0;
+    constructor(getParameters: () => IToolParameters) {
+        this.getParameters = getParameters;
+    }
+    setExposure(exposure : number) {
+        this.exposure = exposure;
+        const {scene3D, updateTexture} = this.getParameters();
+        scene3D.imageProcessingConfiguration.isEnabled = true;
+        scene3D.imageProcessingConfiguration.exposure = this.computeExposure(this.exposure);
+        updateTexture();
+        
+    }
+    setContrast(contrast : number) {
+        this.contrast = contrast;
+        const {scene3D, updateTexture} = this.getParameters();
+        scene3D.imageProcessingConfiguration.isEnabled = true;
+        scene3D.imageProcessingConfiguration.contrast = this.computeContrast(contrast);
+        updateTexture();
+    }
+    /** Maps slider values to post processing values using an exponential regression */
+    computeExposure(sliderValue : number) {
+        if (sliderValue <= 0) {
+            return 1 - (-sliderValue / 100);
+        } else {
+            return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
+        }
+    }
+    /** Maps slider values to post processing values using an exponential regression */
+    computeContrast(sliderValue : number) {
+        if (sliderValue <= 0) {
+            return 1 - (-sliderValue / 100);
+        } else {
+            return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
+        }
+    }
+    setup() {
+        this.contrast = 0;
+        this.exposure = 0;
+        this.setExposure(this.exposure);
+        this.setContrast(this.contrast);
+    }
+    cleanup() {
+    }
+    onReset() {
+        this.contrast = 0;
+        this.exposure = 0;
+    }
+};
+
+class Settings extends React.Component<IToolGUIProps> {
+    render() {
+        const instance = this.props.instance as contrastTool;
+        return (
+            <div>
+                <div>
+                <label className='tool-slider-input'>
+                    <span>Contrast: {instance.contrast}</span>
+                    <input id='contrast-slider'
+                        type='range'
+                        min={-100}
+                        max={100}
+                        value={instance.contrast}
+                        onChange={evt => {instance.setContrast(evt.target.valueAsNumber); this.forceUpdate();}}/>
+                </label>
+                </div>
+                <div>
+                <label className='tool-slider-input'>
+                    <span>Exposure: {instance.exposure}</span>
+                    <input
+                        type='range'
+                        min={-100}
+                        max={100}
+                        value={instance.exposure}
+                        onChange={evt => {instance.setExposure(evt.target.valueAsNumber); this.forceUpdate();}}/>
+                </label>
+                </div>
+            </div>
+        )
+    }
+}
+
+export const Contrast : IToolData = {
+    name: 'Contrast/Exposure',
+    type: contrastTool,
+    is3D: true,
+    settingsComponent: Settings,
+    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0i
+    NDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMTcuNTUsMjYuNTVsOC41OS0zLjIxQTYuODYsNi44NiwwLDAsMSwyNCwyNS43NWwtMy4x
+    OSwxLjE5QTcsNywwLDAsMSwxNy41NSwyNi41NVpNMjAsMTEuNUE4LjUsOC41LDAsMSwwLDI4LjUsMjAsOC41MSw4LjUxLDAsMCwwLDIwLDExLjVNMjAsMTBBMTAsMTAs
+    MCwxLDEsMTAsMjAsMTAsMTAsMCwwLDEsMjAsMTBabS0yLjQ1LDUuMzQsNS0xLjg2QTcsNywwLDAsMCwxOS40NCwxM2wtMS44OS43MVptMCwzLjIsNy44OC0yLjk0YTYu
+    ODgsNi44OCwwLDAsMC0xLjE5LTEuMTZsLTYuNjksMi41Wm0wLDMuMiw5LjIzLTMuNDRhNy42OCw3LjY4LDAsMCwwLS41Mi0xLjQxbC04LjcxLDMuMjVabTAsMS42djEu
+    Nmw5LjI4LTMuNDZBNi42Nyw2LjY3LDAsMCwwLDI3LDE5LjgyWiIgc3R5bGU9ImZpbGw6I2ZmZiIvPjwvc3ZnPg==`
+}

文件差异内容过多而无法显示
+ 0 - 149
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.ts


文件差异内容过多而无法显示
+ 144 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.tsx


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

@@ -25,7 +25,7 @@ interface IPixelDataProps {
     data: number | undefined;
     data: number | undefined;
 }
 }
 
 
-export class PropertiesBar extends React.Component<IPropertiesBarProps,IPropertiesBarState> {
+export class PropertiesBar extends React.PureComponent<IPropertiesBarProps,IPropertiesBarState> {
     private _resetButton = require('./assets/reset.svg');
     private _resetButton = require('./assets/reset.svg');
     private _uploadButton = require('./assets/upload.svg');
     private _uploadButton = require('./assets/upload.svg');
     private _saveButton = require('./assets/save.svg');
     private _saveButton = require('./assets/save.svg');

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

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

+ 0 - 75
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts

@@ -28,12 +28,6 @@ import { TextureHelper } from '../../../../../../textureHelper';
 
 
 import { ITool } from './toolBar';
 import { ITool } from './toolBar';
 import { IChannel } from './channelsBar';
 import { IChannel } from './channelsBar';
-import { TextBlock } from 'babylonjs-gui/2D/controls/textBlock';
-import { Rectangle } from 'babylonjs-gui/2D/controls/rectangle';
-import { StackPanel } from 'babylonjs-gui/2D/controls/stackPanel';
-import { Control } from 'babylonjs-gui/2D/controls/control';
-import { Style } from 'babylonjs-gui/2D/style';
-import { AdvancedDynamicTexture } from 'babylonjs-gui/2D/advancedDynamicTexture';
 import { IMetadata } from './textureEditorComponent';
 import { IMetadata } from './textureEditorComponent';
 
 
 
 
@@ -46,14 +40,6 @@ export interface IPixelData {
     a? : number;
     a? : number;
 }
 }
 
 
-export interface IToolGUI {
-    adt: AdvancedDynamicTexture;
-    toolWindow: StackPanel;
-    isDragging: boolean;
-    dragCoords: Nullable<Vector2>;
-    style: Style;
-}
-
 export class TextureCanvasManager {
 export class TextureCanvasManager {
     private _engine: Engine;
     private _engine: Engine;
     private _scene: Scene;
     private _scene: Scene;
@@ -118,8 +104,6 @@ export class TextureCanvasManager {
 
 
     private _setPixelData : (pixelData : IPixelData) => void;
     private _setPixelData : (pixelData : IPixelData) => void;
 
 
-    private _GUI : IToolGUI;
-
     private _window : Window;
     private _window : Window;
 
 
     private _metadata : IMetadata;
     private _metadata : IMetadata;
@@ -314,53 +298,6 @@ export class TextureCanvasManager {
         this._planeMaterial.setInt('time', 0);
         this._planeMaterial.setInt('time', 0);
         this._plane.material = this._planeMaterial;
         this._plane.material = this._planeMaterial;
         
         
-        const adt = AdvancedDynamicTexture.CreateFullscreenUI('gui', true, this._scene);
-        const style = adt.createStyle();
-        style.fontFamily = 'acumin-pro-condensed';
-        style.fontSize = '15px';
-
-        const toolWindow = new StackPanel();
-        toolWindow.background = '#333333';
-        toolWindow.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_RIGHT;
-        toolWindow.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM;
-        toolWindow.left = '0px';
-        toolWindow.top = '-30px';
-        toolWindow.width = '200px';
-        toolWindow.isVisible = false;
-        toolWindow.isPointerBlocker = true;
-        adt.addControl(toolWindow);
-
-        this._GUI = {adt, style, toolWindow, isDragging: false, dragCoords: null};
-
-        const topBar = new Rectangle();
-        topBar.width = '100%';
-        topBar.height = '20px';
-        topBar.background = '#666666';
-        topBar.thickness = 0;
-        topBar.hoverCursor = 'grab';
-        topBar.onPointerDownObservable.add(() => {this._GUI.isDragging = true; topBar.hoverCursor = 'grabbing';});
-        topBar.onPointerUpObservable.add(() => {this._GUI.isDragging = false; this._GUI.dragCoords = null; topBar.hoverCursor = 'grab';});
-
-        const title = new TextBlock();
-        title.text = 'Tool Settings';
-        title.color = 'white';
-        title.height = '20px';
-        title.style = this._GUI.style;
-        topBar.addControl(title);
-        this._GUI.toolWindow.addControl(topBar);
-
-        this._window.addEventListener('pointermove', evt => {
-            if (!this._GUI.isDragging) return;
-            if (!this._GUI.dragCoords) {
-                this._GUI.dragCoords = new Vector2(evt.x, evt.y);
-                return;
-            }
-            this._GUI.toolWindow.leftInPixels += evt.x - this._GUI.dragCoords.x;
-            this._GUI.toolWindow.topInPixels += evt.y - this._GUI.dragCoords.y;
-            this._GUI.dragCoords.x = evt.x;
-            this._GUI.dragCoords.y = evt.y;
-        });
-
         this._window.addEventListener('keydown', evt => {
         this._window.addEventListener('keydown', evt => {
             this._keyMap[evt.code] = true;
             this._keyMap[evt.code] = true;
             if (evt.code === TextureCanvasManager.SELECT_ALL_KEY && evt.ctrlKey) {
             if (evt.code === TextureCanvasManager.SELECT_ALL_KEY && evt.ctrlKey) {
@@ -392,8 +329,6 @@ export class TextureCanvasManager {
 
 
         this._engine.runRenderLoop(() => {
         this._engine.runRenderLoop(() => {
             this._engine.resize();
             this._engine.resize();
-            this.GUI.toolWindow.left = Math.min(Math.max(this._GUI.toolWindow.leftInPixels, -this._UICanvas.width + this._GUI.toolWindow.widthInPixels), 0);
-            this.GUI.toolWindow.top = Math.min(Math.max(this._GUI.toolWindow.topInPixels, -this._UICanvas.height + this._GUI.toolWindow.heightInPixels), 0);
             this._scene.render();
             this._scene.render();
             this._planeMaterial.setInt('time', new Date().getTime());
             this._planeMaterial.setInt('time', new Date().getTime());
         });
         });
@@ -667,11 +602,6 @@ export class TextureCanvasManager {
         this._tool = tool;
         this._tool = tool;
         if (this._tool) {
         if (this._tool) {
             this._tool.instance.setup();
             this._tool.instance.setup();
-            if (this._tool.usesWindow) {
-                this._GUI.toolWindow.isVisible = true;
-            } else {
-                this._GUI.toolWindow.isVisible = false;
-            }
             if (this._editing3D && !this._tool.is3D) {
             if (this._editing3D && !this._tool.is3D) {
                 this._editing3D = false;
                 this._editing3D = false;
                 this._2DCanvas.getContext('2d')?.drawImage(this._3DCanvas, 0, 0);
                 this._2DCanvas.getContext('2d')?.drawImage(this._3DCanvas, 0, 0);
@@ -701,11 +631,6 @@ export class TextureCanvasManager {
         this.grabOriginalTexture(false);
         this.grabOriginalTexture(false);
     }
     }
 
 
-    /** Returns the tool GUI object, allowing tools to access the GUI */
-    public get GUI() {
-        return this._GUI;
-    }
-
     /** Returns the 3D scene used for postprocesses */
     /** Returns the 3D scene used for postprocesses */
     public get scene3D() {
     public get scene3D() {
         return this._3DScene;
         return this._3DScene;

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

@@ -231,6 +231,23 @@
         height: calc(100% - 70px);
         height: calc(100% - 70px);
         outline: none;
         outline: none;
     }
     }
+
+    #tool-ui {
+        background-color: #333;
+        position: absolute;
+        right: 0;
+        bottom: 30px;
+
+        label {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+        }
+
+        input[type='range'] {
+            background: #d3d3d3;
+        }
+    }
     
     
     #bottom-bar {
     #bottom-bar {
         height: 30px;
         height: 30px;

+ 8 - 6
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx

@@ -1,6 +1,5 @@
 import * as React from 'react';
 import * as React from 'react';
-import { GlobalState } from '../../../../../globalState';
-import { TextureCanvasManager, IPixelData, IToolGUI } from './textureCanvasManager';
+import { TextureCanvasManager, IPixelData } from './textureCanvasManager';
 import { ITool, ToolBar } from './toolBar';
 import { ITool, ToolBar } from './toolBar';
 import { PropertiesBar } from './propertiesBar';
 import { PropertiesBar } from './propertiesBar';
 import { IChannel, ChannelsBar } from './channelsBar';
 import { IChannel, ChannelsBar } from './channelsBar';
@@ -16,11 +15,11 @@ import { Vector2 } from 'babylonjs/Maths/math.vector';
 import { PointerInfo } from 'babylonjs/Events/pointerEvents';
 import { PointerInfo } from 'babylonjs/Events/pointerEvents';
 
 
 import { PopupComponent } from '../../../../../popupComponent';
 import { PopupComponent } from '../../../../../popupComponent';
+import { ToolSettings } from './toolSettings';
 
 
 require('./textureEditor.scss');
 require('./textureEditor.scss');
 
 
 interface ITextureEditorComponentProps {
 interface ITextureEditorComponentProps {
-    globalState: GlobalState;
     texture: BaseTexture;
     texture: BaseTexture;
     url: string;
     url: string;
     window: React.RefObject<PopupComponent>;
     window: React.RefObject<PopupComponent>;
@@ -54,8 +53,6 @@ export interface IToolParameters {
     setMetadata: (data : any) => void;
     setMetadata: (data : any) => void;
     /** Returns the texture coordinates under the cursor */
     /** Returns the texture coordinates under the cursor */
     getMouseCoordinates: (pointerInfo : PointerInfo) => Vector2;
     getMouseCoordinates: (pointerInfo : PointerInfo) => Vector2;
-    /** An object which holds the GUI's ADT as well as the tool window. */
-    GUI: IToolGUI;
     /** Provides access to the BABYLON namespace */
     /** Provides access to the BABYLON namespace */
     BABYLON: any;
     BABYLON: any;
     /** Provides a canvas that you can use the canvas API to paint on. */
     /** Provides a canvas that you can use the canvas API to paint on. */
@@ -66,6 +63,9 @@ export interface IToolParameters {
     stopPainting: () => void;
     stopPainting: () => void;
 }
 }
 
 
+export interface IToolGUIProps {
+    instance: IToolType
+}
 
 
 /** An interface representing the definition of a tool */
 /** An interface representing the definition of a tool */
 export interface IToolData {
 export interface IToolData {
@@ -79,6 +79,7 @@ export interface IToolData {
     usesWindow? : boolean;
     usesWindow? : boolean;
     /** Whether the tool uses postprocesses */
     /** Whether the tool uses postprocesses */
     is3D? : boolean;
     is3D? : boolean;
+    settingsComponent? : React.ComponentType<IToolGUIProps>;
 }
 }
 
 
 export interface IToolType {
 export interface IToolType {
@@ -229,7 +230,6 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
             metadata: this.state.metadata,
             metadata: this.state.metadata,
             setMetadata: (data : any) => this.setMetadata(data),
             setMetadata: (data : any) => this.setMetadata(data),
             getMouseCoordinates: (pointerInfo : PointerInfo) => this._textureCanvasManager.getMouseCoordinates(pointerInfo),
             getMouseCoordinates: (pointerInfo : PointerInfo) => this._textureCanvasManager.getMouseCoordinates(pointerInfo),
-            GUI: this._textureCanvasManager.GUI,
             BABYLON: BABYLON,
             BABYLON: BABYLON,
         };
         };
     }
     }
@@ -271,6 +271,7 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
     }
     }
 
 
     render() {
     render() {
+        const currentTool : ITool | undefined = this.state.tools[this.state.activeToolIndex];
         return <div id="texture-editor">
         return <div id="texture-editor">
             <PropertiesBar
             <PropertiesBar
                 texture={this.props.texture}
                 texture={this.props.texture}
@@ -294,6 +295,7 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
             />}
             />}
             <ChannelsBar channels={this.state.channels} setChannels={(channels) => {this.setState({channels})}}/>
             <ChannelsBar channels={this.state.channels} setChannels={(channels) => {this.setState({channels})}}/>
             <TextureCanvasComponent canvas2D={this._2DCanvas} canvas3D={this._3DCanvas} canvasUI={this._UICanvas} texture={this.props.texture}/>
             <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 name={this.props.url} mipLevel={this.state.mipLevel} hasMips={!this.props.texture.noMipmap}/>
         </div>
         </div>
     }
     }

+ 15 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/toolSettings.tsx

@@ -0,0 +1,15 @@
+import * as React from 'react';
+import { ITool } from './toolBar';
+
+interface IToolSettingsProps {
+    tool: ITool | undefined;
+}
+
+export class ToolSettings extends React.Component<IToolSettingsProps> {
+    render() {
+        if (!this.props.tool || !this.props.tool.settingsComponent) return <></>;
+        return <div id='tool-ui'>
+            {<this.props.tool.settingsComponent instance={this.props.tool.instance}/>}
+        </div>;
+    }
+}