Quellcode durchsuchen

qol improvements

Darragh Burke vor 5 Jahren
Ursprung
Commit
a2baf58eb2

+ 118 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/canvasShader.ts

@@ -0,0 +1,118 @@
+export const canvasShader = {
+    path: {
+        vertexSource: `
+            precision highp float;
+
+            attribute vec3 position;
+            attribute vec2 uv;
+
+            uniform mat4 worldViewProjection;
+
+            varying vec2 vUV;
+
+            void main(void) {
+                gl_Position = worldViewProjection * vec4(position, 1.0);
+                vUV = uv;
+            }
+        `,
+        fragmentSource: `
+            precision highp float;
+    
+            uniform sampler2D textureSampler;
+    
+            uniform bool r;
+            uniform bool g;
+            uniform bool b;
+            uniform bool a;
+
+            uniform int x1;
+            uniform int y1;
+            uniform int x2;
+            uniform int y2;
+            uniform int w;
+            uniform int h;
+
+            uniform int time;
+            uniform bool showGrid;
+    
+            varying vec2 vUV;
+
+            float scl = 200.0;
+            float speed = 10.0 / 1000.0;
+            float smoothing = 0.2;
+    
+            void main(void) {
+                vec2 pos2 = vec2(gl_FragCoord.x, gl_FragCoord.y);
+                vec2 pos = floor(pos2 * 0.05);
+                float pattern = mod(pos.x + pos.y, 2.0); 
+                if (pattern == 0.0) {
+                    pattern = 0.7;
+                }
+                vec4 bg = vec4(pattern, pattern, pattern, 1.0);
+                vec4 col = texture(textureSampler, vUV);
+                if (!r && !g && !b) {
+                    if (a) {
+                        col = vec4(col.a, col.a, col.a, 1.0);
+                    } else {
+                        col = vec4(0.0,0.0,0.0,0.0);
+                    }
+                } else {
+                    if (!r) {
+                        col.r = 0.0;
+                        if (!b) {
+                            col.r = col.g;
+                        }
+                        else if (!g) {
+                            col.r = col.b;
+                        }
+                    }
+                    if (!g) {
+                        col.g = 0.0;
+                        if (!b) {
+                            col.g = col.r;
+                        }
+                        else if (!r) {
+                            col.g = col.b;
+                        }
+                    }
+                    if (!b) {
+                        col.b = 0.0;
+                        if (!r) {
+                            col.b = col.g;
+                        } else if (!g) {
+                            col.b = col.r;
+                        }
+                    }
+                    if (!a) {
+                        col.a = 1.0;
+                    }
+                }
+                gl_FragColor = col * (col.a) + bg * (1.0 - col.a);
+                float wF = float(w);
+                float hF = float(h);
+                int xPixel = int(floor(vUV.x * wF));
+                int yPixel = int(floor((1.0 - vUV.y) * hF));
+                int xDis = min(abs(xPixel - x1), abs(xPixel - x2));
+                int yDis = min(abs(yPixel - y1), abs(yPixel - y2));
+                if (showGrid) {
+                    vec2 frac = fract(vUV * vec2(wF,hF));
+                    float thickness = 0.1;
+                    if (abs(frac.x) < thickness || abs (frac.y) < thickness) {
+                        gl_FragColor = vec4(0.75,0.75,0.75,1.0);
+                    }
+                }
+                if (xPixel >= x1 && yPixel >= y1 && xPixel <= x2 && yPixel <= y2) {
+                    if (xDis <= 4 || yDis <= 4) {
+                        float c = sin(vUV.x * scl + vUV.y * scl + float(time) * speed);
+                        c = smoothstep(-smoothing,smoothing,c);
+                        float val = 1.0 - c;
+                        gl_FragColor = vec4(val, val, val, 1.0) * 0.7 + gl_FragColor * 0.3;
+                    }
+                }
+            }`
+    },
+    options: {
+        attributes: ['position', 'uv'],
+        uniforms: ['worldViewProjection', 'textureSampler', 'r', 'g', 'b', 'a', 'x1', 'y1', 'x2', 'y2', 'w', 'h', 'time', 'showGrid']
+    }
+}

+ 8 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/colorPicker.tsx

@@ -0,0 +1,8 @@
+import * as React from 'react';
+import { SketchPicker } from 'react-color';
+import { Color4 } from 'babylonjs';
+
+export function ColorPicker() {
+    const [color, setColor] = React.useState(new Color4());
+    return <SketchPicker color={color.toHexString(false)}  onChange={color => setColor(new Color4(color.rgb.r / 255,color.rgb.g / 255,color.rgb.b / 255,color.rgb.a))}/>;
+}

+ 17 - 19
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.ts

@@ -33,30 +33,28 @@ export const Paintbrush : IToolData = {
                 x -= metadata.select.x1;
                 y -= metadata.select.y1;
             }
+            const {ctx} = this;
+            let numSteps, stepVector;
+            stepVector = new Vector2();
             if (this.mousePos == null) {
                 this.mousePos = new Vector2(x, y);
+                numSteps = 1;
+            } else {
+                const maxDistance = this.width / 4;
+                const diffVector = new Vector2(x - this.mousePos.x, y - this.mousePos.y);
+                numSteps = Math.ceil(diffVector.length() / maxDistance);
+                const trueDistance = diffVector.length() / numSteps;
+                stepVector = diffVector.normalize().multiplyByFloats(trueDistance, trueDistance);
             }
-            const {ctx} = this;
-            let xx = this.mousePos.x;
-            let yy = this.mousePos.y;
-            let stepCount = 0;
-            const distance = this.width / 4;
-            const step = new Vector2(x - this.mousePos.x, y - this.mousePos.y).normalize().multiplyByFloats(distance, distance);
-            const numSteps = new Vector2(x - this.mousePos.x, y - this.mousePos.y).length() / distance;
-            while(stepCount < numSteps) {
+            let paintVector = this.mousePos.clone();
+            for(let stepCount = 0; stepCount < numSteps; stepCount++) {
                 ctx.globalAlpha = 1.0;
                 ctx.globalCompositeOperation = 'destination-out';
-                ctx.drawImage(this.circleCanvas, Math.floor(xx - this.width / 2), Math.floor(yy - this.width / 2));
+                ctx.drawImage(this.circleCanvas, Math.ceil(paintVector.x - this.width / 2), Math.ceil(paintVector.y - this.width / 2));
                 ctx.globalAlpha = metadata.alpha;
                 ctx.globalCompositeOperation = 'source-over';
-                ctx.drawImage(this.circleCanvas, Math.floor(xx - this.width / 2), Math.floor(yy - this.width / 2));
-                xx += step.x;
-                yy += step.y;
-                stepCount++;
-                if (numSteps - stepCount < 1) {
-                    xx = x;
-                    yy = y;
-                }
+                ctx.drawImage(this.circleCanvas, Math.ceil(paintVector.x - this.width / 2), Math.ceil(paintVector.y - this.width / 2));
+                paintVector.addInPlace(stepVector);
             }
             updatePainting();
             this.mousePos = new Vector2(x,y);
@@ -107,8 +105,8 @@ export const Paintbrush : IToolData = {
                             const g = Math.floor(rgb.g * 255);
                             const b = Math.floor(rgb.b * 255);
                             let idx = 0;
-                            for(let y = -Math.ceil(this.width / 2); y < Math.floor(this.width / 2); y++) {
-                                for (let x = -Math.ceil(this.width / 2); x < Math.floor(this.width / 2); x++) {
+                            for(let y = -Math.floor(this.width / 2); y < Math.ceil(this.width / 2); y++) {
+                                for (let x = -Math.floor(this.width / 2); x < Math.ceil(this.width / 2); x++) {
                                     pixels[idx++] = r;
                                     pixels[idx++] = g;
                                     pixels[idx++] = b;

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

@@ -1,9 +1,11 @@
 import * as React from 'react';
 import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
 import { IPixelData } from './textureCanvasManager';
+import { ISize } from 'babylonjs/Maths/math.size';
 
 interface IPropertiesBarProps {
     texture: BaseTexture;
+    size: ISize;
     saveTexture(): void;
     pixelData: IPixelData;
     face: number;
@@ -21,7 +23,7 @@ interface IPropertiesBarState {
 }
 
 interface IPixelDataProps {
-    name : string;
+    name: string;
     data: number | undefined;
 }
 
@@ -50,8 +52,8 @@ export class PropertiesBar extends React.Component<IPropertiesBarProps,IProperti
         super(props);
 
         this.state = {
-            width: props.texture.getSize().width,
-            height: props.texture.getSize().height
+            width: props.size.width,
+            height: props.size.height
         }
     }
 
@@ -69,6 +71,15 @@ export class PropertiesBar extends React.Component<IPropertiesBarProps,IProperti
         return oldDim;
     }
 
+    componentWillUpdate(nextProps: IPropertiesBarProps) {
+        if (nextProps.size.width != this.props.size.width || nextProps.size.height != this.props.size.height) {
+            this.setState({
+                width: nextProps.size.width,
+                height: nextProps.size.height
+            })
+        }
+    }
+
     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)));

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

@@ -2,10 +2,10 @@ import * as React from 'react';
 import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
 
 interface ITextureCanvasComponentProps {
-    canvasUI : React.RefObject<HTMLCanvasElement>;
-    canvas2D : React.RefObject<HTMLCanvasElement>;
-    canvas3D : React.RefObject<HTMLCanvasElement>;
-    texture : BaseTexture;
+    canvasUI: React.RefObject<HTMLCanvasElement>;
+    canvas2D: React.RefObject<HTMLCanvasElement>;
+    canvas3D: React.RefObject<HTMLCanvasElement>;
+    texture: BaseTexture;
 }
 
 export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {

+ 25 - 135
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts

@@ -4,7 +4,6 @@ import { Vector3, Vector2 } from 'babylonjs/Maths/math.vector';
 import { Color4, Color3 } from 'babylonjs/Maths/math.color';
 import { FreeCamera } from 'babylonjs/Cameras/freeCamera';
 import { Nullable } from 'babylonjs/types'
-
 import { PlaneBuilder } from 'babylonjs/Meshes/Builders/planeBuilder';
 import { Mesh } from 'babylonjs/Meshes/mesh';
 import { Camera } from 'babylonjs/Cameras/camera';
@@ -36,6 +35,8 @@ import { Style } from 'babylonjs-gui/2D/style';
 import { AdvancedDynamicTexture } from 'babylonjs-gui/2D/advancedDynamicTexture';
 import { IMetadata } from './textureEditorComponent';
 
+import { canvasShader } from './canvasShader';
+
 
 export interface IPixelData {
     x? : number;
@@ -100,8 +101,8 @@ export class TextureCanvasManager {
     /* Tracks which keys are currently pressed */
     private _keyMap : any = {};
 
-    private static ZOOM_MOUSE_SPEED : number = 0.0005;
-    private static ZOOM_KEYBOARD_SPEED : number = 0.2;
+    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 = '-';
 
@@ -109,6 +110,7 @@ export class TextureCanvasManager {
     private static 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 static SELECT_ALL_KEY = 'KeyA';
@@ -160,7 +162,7 @@ export class TextureCanvasManager {
         this._originalTexture = texture;
         this._originalInternalTexture = this._originalTexture._texture;
         this._engine = new Engine(this._UICanvas, true);
-        this._scene = new Scene(this._engine);
+        this._scene = new Scene(this._engine, {virtual: true});
         this._scene.clearColor = new Color4(0.11, 0.11, 0.11, 1.0);
 
         this._camera = new FreeCamera('camera', new Vector3(0, 0, -1), this._scene);
@@ -170,7 +172,7 @@ export class TextureCanvasManager {
         this._channelsTexture = new HtmlElementTexture('ct', this._2DCanvas, {engine: this._engine, scene: null, samplingMode: Texture.NEAREST_SAMPLINGMODE, generateMipMaps: true});
 
         this._3DEngine = new Engine(this._3DCanvas);
-        this._3DScene = new Scene(this._3DEngine);
+        this._3DScene = new Scene(this._3DEngine, {virtual: true});
         this._3DScene.clearColor = new Color4(0,0,0,0);
         this._3DCanvasTexture = new HtmlElementTexture('canvas', this._2DCanvas, {engine: this._3DEngine, scene: this._3DScene});
         this._3DCanvasTexture.hasAlpha = true;
@@ -186,122 +188,13 @@ export class TextureCanvasManager {
         mat.emissiveColor = Color3.White();
         this._3DPlane.material = mat;
 
+
         this._planeMaterial = new ShaderMaterial(
-            'shader',
+            'canvasShader',
             this._scene,
-            {
-                vertexSource: `
-                    precision highp float;
-
-                    attribute vec3 position;
-                    attribute vec2 uv;
-
-                    uniform mat4 worldViewProjection;
-
-                    varying vec2 vUV;
-
-                    void main(void) {
-                        gl_Position = worldViewProjection * vec4(position, 1.0);
-                        vUV = uv;
-                    }
-                `,
-                fragmentSource: `
-                    precision highp float;
-            
-                    uniform sampler2D textureSampler;
-            
-                    uniform bool r;
-                    uniform bool g;
-                    uniform bool b;
-                    uniform bool a;
-
-                    uniform int x1;
-                    uniform int y1;
-                    uniform int x2;
-                    uniform int y2;
-                    uniform int w;
-                    uniform int h;
-
-                    uniform int time;
-            
-                    varying vec2 vUV;
-
-                    float scl = 200.0;
-                    float speed = 10.0 / 1000.0;
-                    float smoothing = 0.2;
-            
-                    void main(void) {
-                        vec2 pos2 = vec2(gl_FragCoord.x, gl_FragCoord.y);
-                        vec2 pos = floor(pos2 * 0.05);
-                        float pattern = mod(pos.x + pos.y, 2.0); 
-                        if (pattern == 0.0) {
-                            pattern = 0.7;
-                        }
-                        vec4 bg = vec4(pattern, pattern, pattern, 1.0);
-                        vec4 col = texture(textureSampler, vUV);
-                        if (!r && !g && !b) {
-                            if (a) {
-                                col = vec4(col.a, col.a, col.a, 1.0);
-                            } else {
-                                col = vec4(0.0,0.0,0.0,0.0);
-                            }
-                        } else {
-                            if (!r) {
-                                col.r = 0.0;
-                                if (!b) {
-                                    col.r = col.g;
-                                }
-                                else if (!g) {
-                                    col.r = col.b;
-                                }
-                            }
-                            if (!g) {
-                                col.g = 0.0;
-                                if (!b) {
-                                    col.g = col.r;
-                                }
-                                else if (!r) {
-                                    col.g = col.b;
-                                }
-                            }
-                            if (!b) {
-                                col.b = 0.0;
-                                if (!r) {
-                                    col.b = col.g;
-                                } else if (!g) {
-                                    col.b = col.r;
-                                }
-                            }
-                            if (!a) {
-                                col.a = 1.0;
-                            }
-                        }
-                        gl_FragColor = col * (col.a) + bg * (1.0 - col.a);
-                        float wF = float(w);
-                        float hF = float(h);
-                        int xPixel = int(floor(vUV.x * wF));
-                        int yPixel = int(floor((1.0 - vUV.y) * hF));
-                        int xDis = min(abs(xPixel - x1), abs(xPixel - x2));
-                        int yDis = min(abs(yPixel - y1), abs(yPixel - y2));
-                        vec2 frac = fract(vUV * vec2(wF,hF));
-                        float thickness = 0.1;
-                        if (abs(frac.x) < thickness || abs (frac.y) < thickness) {
-                            gl_FragColor = vec4(0.5,0.5,0.5,1.0);
-                        }
-                        if (xPixel >= x1 && yPixel >= y1 && xPixel <= x2 && yPixel <= y2) {
-                            if (xDis <= 4 || yDis <= 4) {
-                                float c = sin(vUV.x * scl + vUV.y * scl + float(time) * speed);
-                                c = smoothstep(-smoothing,smoothing,c);
-                                float val = 1.0 - c;
-                                gl_FragColor = vec4(val, val, val, 1.0) * 0.7 + gl_FragColor * 0.3;
-                            }
-                        }
-                    }`
-            },
-        {
-            attributes: ['position', 'uv'],
-            uniforms: ['worldViewProjection', 'textureSampler', 'r', 'g', 'b', 'a', 'x1', 'y1', 'x2', 'y2', 'w', 'h', 'time']
-        });
+            canvasShader.path,
+            canvasShader.options
+        );
         
         this.grabOriginalTexture();
 
@@ -317,6 +210,7 @@ export class TextureCanvasManager {
         this._planeMaterial.setInt('w', this._size.width);
         this._planeMaterial.setInt('h', this._size.height);
         this._planeMaterial.setInt('time', 0);
+        this._planeMaterial.setFloat('showGrid', 0.0);
         this._plane.material = this._planeMaterial;
         
         const adt = AdvancedDynamicTexture.CreateFullscreenUI('gui', true, this._scene);
@@ -401,13 +295,19 @@ export class TextureCanvasManager {
             this.GUI.toolWindow.top = Math.min(Math.max(this._GUI.toolWindow.topInPixels, -this._UICanvas.height + this._GUI.toolWindow.heightInPixels), 0);
             this._scene.render();
             this._planeMaterial.setInt('time', new Date().getTime());
+            
         });
 
-        this._scale = 1.5;
+        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), TextureCanvasManager.MAX_SCALE * Math.log2(this._size.width));
+            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._planeMaterial.setFloat('showGrid', 1.0);
+            } else {
+                this._planeMaterial.setFloat('showGrid', 0.0);
+            }
             const ratio = this._UICanvas?.width / this._UICanvas?.height;
             const {x,y} = this._cameraPos;
             this._camera.orthoBottom = y - 1 / this._scale;
@@ -508,6 +408,7 @@ export class TextureCanvasManager {
     private queueTextureUpdate() {
         if (this._canUpdate) {
             (this._target as HtmlElementTexture).update((this._originalTexture as Texture).invertY);
+            this._target._texture?.updateSize(this._size.width, this._size.height);
             if (this._editing3D) {
                 this._imageData = this._3DEngine.readPixels(0, 0, this._size.width, this._size.height);
             } else {
@@ -728,9 +629,8 @@ export class TextureCanvasManager {
     }
 
     private makePlane() {
-        const textureRatio = this._size.width / this._size.height;
         if (this._plane) this._plane.dispose();
-        this._plane = PlaneBuilder.CreatePlane("plane", {width: textureRatio, height: 1}, this._scene);
+        this._plane = PlaneBuilder.CreatePlane("plane", {width: this._size.width, height: this._size.height}, this._scene);
         this._plane.enableEdgesRendering();
         this._plane.edgesWidth = 4.0;
         this._plane.edgesColor = new Color4(1,1,1,1);
@@ -769,7 +669,7 @@ export class TextureCanvasManager {
         if (oldSize.width != size.width || oldSize.height != size.height) {
             this._cameraPos.x = 0;
             this._cameraPos.y = 0;
-            this._scale = 1.5 / (this._size.width/this._size.height);
+            this._scale = 1.5 / Math.max(this._size.width, this._size.height);
         }
         this.makePlane();
     }
@@ -789,17 +689,7 @@ export class TextureCanvasManager {
                 let base64data = reader.result as string;     
 
                 if (extension === '.dds' || extension === '.env') {
-                    const texture = new CubeTexture(
-                        base64data,
-                        this._scene,
-                        [extension],
-                        this._originalTexture.noMipmap,                        
-                        null,
-                        () => {
-                            // TO-DO: implement cube loading
-                            texture.dispose();
-                        }
-                    );
+                    (this._originalTexture as CubeTexture).updateURL(base64data, extension, () => this.grabOriginalTexture());
                 } else {
                     const texture = new Texture(
                         base64data,

+ 21 - 3
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx

@@ -35,6 +35,7 @@ interface ITextureEditorComponentState {
     pixelData : IPixelData;
     face: number;
     mipLevel: number;
+    pickerOpen: boolean;
 }
 
 export interface IToolParameters {
@@ -116,6 +117,7 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
     private _UICanvas = React.createRef<HTMLCanvasElement>();
     private _2DCanvas = React.createRef<HTMLCanvasElement>();
     private _3DCanvas = React.createRef<HTMLCanvasElement>();
+    private _pickerRef = React.createRef<HTMLDivElement>();
     private _timer : number | null;
     private static PREVIEW_UPDATE_DELAY_MS = 160;
 
@@ -147,7 +149,8 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
             channels,
             pixelData: {},
             face: 0,
-            mipLevel: 0
+            mipLevel: 0,
+            pickerOpen: false
         }
         this.loadToolFromURL = this.loadToolFromURL.bind(this);
         this.changeTool = this.changeTool.bind(this);
@@ -156,7 +159,8 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
         this.resetTexture = this.resetTexture.bind(this);
         this.resizeTexture = this.resizeTexture.bind(this);
         this.uploadTexture = this.uploadTexture.bind(this);
-
+        this.setPickerOpen = this.setPickerOpen.bind(this);
+        this.onPointerDown = this.onPointerDown.bind(this);
     }
 
     componentDidMount() {
@@ -252,6 +256,16 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
         this._textureCanvasManager.metadata = data;
     }
 
+    setPickerOpen(open: boolean) {
+        this.setState({pickerOpen: open});
+    }
+
+    onPointerDown(evt: React.PointerEvent) {
+        if (!this._pickerRef.current?.contains(evt.target as Node)) {
+            this.setPickerOpen(false);
+        }
+    }
+
     saveTexture() {
         Tools.ToBlob(this._2DCanvas.current!, (blob) => {
             Tools.Download(blob!, this.props.url);
@@ -271,7 +285,7 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
     }
 
     render() {
-        return <div id="texture-editor">
+        return <div id="texture-editor" onPointerDown={this.onPointerDown}>
             <PropertiesBar
                 texture={this.props.texture}
                 saveTexture={this.saveTexture}
@@ -283,6 +297,7 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
                 uploadTexture={this.uploadTexture}
                 mipLevel={this.state.mipLevel}
                 setMipLevel={mipLevel => this.setState({mipLevel})}
+                size={this._textureCanvasManager?.size || this.props.texture.getSize()}
             />
             {!this.props.texture.isCube && <ToolBar
                 tools={this.state.tools}
@@ -291,6 +306,9 @@ export class TextureEditorComponent extends React.Component<ITextureEditorCompon
                 changeTool={this.changeTool}
                 metadata={this.state.metadata}
                 setMetadata={this.setMetadata}
+                pickerOpen={this.state.pickerOpen}
+                setPickerOpen={this.setPickerOpen}
+                pickerRef={this._pickerRef}
             />}
             <ChannelsBar channels={this.state.channels} setChannels={(channels) => {this.setState({channels})}}/>
             <TextureCanvasComponent canvas2D={this._2DCanvas} canvas3D={this._3DCanvas} canvasUI={this._UICanvas} texture={this.props.texture}/>

+ 17 - 20
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar.tsx

@@ -13,26 +13,23 @@ interface IToolBarProps {
     activeToolIndex : number;
     metadata: IMetadata;
     setMetadata(data : any): void;
+    pickerOpen: boolean;
+    setPickerOpen(open: boolean): void;
+    pickerRef: React.RefObject<HTMLDivElement>;
 }
 
 interface IToolBarState {
     toolURL : string;
-    pickerOpen : boolean;
     addOpen : boolean;
 }
 
-
 export class ToolBar extends React.Component<IToolBarProps, IToolBarState> {
-
-    private _pickerRef : React.RefObject<HTMLDivElement>;
     constructor(props : IToolBarProps) {
         super(props);
         this.state = {
             toolURL: "",
-            pickerOpen: false,
             addOpen: false
         };
-        this._pickerRef = React.createRef();
     }
 
     computeRGBAColor() {
@@ -40,6 +37,10 @@ export class ToolBar extends React.Component<IToolBarProps, IToolBarState> {
         const opacityHex = opacityInt.toString(16).padStart(2, '0');
         return `${this.props.metadata.color}${opacityHex}`;
     }
+    
+    shouldComponentUpdate(nextProps: IToolBarProps) {
+        return (nextProps.tools != this.props.tools || nextProps.activeToolIndex !== this.props.activeToolIndex || nextProps.metadata != this.props.metadata || nextProps.pickerOpen != this.props.pickerOpen);
+    }
 
     render() {
         return <div id='toolbar'>
@@ -61,25 +62,21 @@ export class ToolBar extends React.Component<IToolBarProps, IToolBarState> {
                     }
                 )}
             </div>
-            <div id='color' onClick={() => this.setState({pickerOpen: !this.state.pickerOpen})} title='Color' className={`icon button${this.state.pickerOpen ? ` active` : ``}`}>
+            <div
+                id='color'
+                onClick={() => {if (!this.props.pickerOpen) this.props.setPickerOpen(true);}}
+                title='Color'
+                className={`icon button${this.props.pickerOpen ? ` active` : ``}`}
+            >
                 <div id='active-color-bg'>
                     <div id='active-color' style={{backgroundColor: this.props.metadata.color, opacity: this.props.metadata.alpha}}></div>
                 </div>
             </div>
             {
-                this.state.pickerOpen &&
-                <>
-                    <div className='color-picker-cover' onClick={evt => {
-                        if (evt.target !== this._pickerRef.current?.ownerDocument.querySelector('.color-picker-cover')) {
-                            return;
-                        }
-                        this.setState({pickerOpen: false});
-                    }}>
-                    </div>
-                    <div className='color-picker' ref={this._pickerRef}>
-                            <SketchPicker color={this.computeRGBAColor()}  onChange={color => this.props.setMetadata({color: color.hex, alpha: color.rgb.a})}/>
-                    </div>
-                </>
+                this.props.pickerOpen &&
+                <div className='color-picker' ref={this.props.pickerRef}>
+                    <SketchPicker color={this.computeRGBAColor()}  onChange={color => this.props.setMetadata({color: color.hex, alpha: color.rgb.a})}/>
+                </div>
             }
         </div>;
     }