Przeglądaj źródła

Merge pull request #8860 from BabylonJS/msDestiny14/inspector

Expose Node Gradient In the Inspector
David Catuhe 5 lat temu
rodzic
commit
002828abdc

+ 114 - 1
inspector/src/components/actionTabs/actionTabs.scss

@@ -127,7 +127,6 @@ $line-padding-left: 2px;
 
         .panes {
             grid-row: 2;
-
             display: grid;
             grid-template-rows: 100%;
 
@@ -140,6 +139,120 @@ $line-padding-left: 2px;
                                 
             }
 
+            .gradient-step {
+                display: grid;
+                grid-template-rows: 100%;
+                grid-template-columns: 25px 50px 55px 40px auto 20px 5px;
+                padding-top: 5px;
+                padding-left: 5px;
+                padding-bottom: 5px;    
+                align-items: center;
+        
+                .step {
+                    grid-row: 1;
+                    grid-column: 1;
+                }
+
+                .color1 {
+                    height: 100%;
+                }
+
+                .color2{
+                    height: 100%;
+                    padding-left: 5px;
+                }
+                    
+                .factor1 {
+                    grid-row: 1;
+                    grid-column: 2;
+                    cursor: pointer;
+                }
+
+                .factor2 {
+                    padding-left: 5px;
+                    grid-row: 1;
+                    grid-column: 3;
+                    cursor: pointer;
+
+                    .grayed {
+                        background: gray;
+                        border-color: gray;
+                    }
+                }
+
+                .numeric-input {
+                    width: calc(100% - 5px);
+                }
+
+                .icon{
+                    cursor: pointer;
+                }
+        
+                .step-value {       
+                    margin-left: 5px;     
+                    grid-row: 1;
+                    grid-column: 4;
+                    text-align: right;
+                    margin-right: 5px;
+                }
+        
+                .step-slider {            
+                    grid-row: 1;
+                    grid-column: 5;
+                    display: grid;
+                    justify-content: stretch;
+                    align-content: center;
+                    margin-right: 12px;
+        
+                    input {
+                        width: 100%;
+                    }
+                    .range:hover {
+                        opacity: 1;
+                    }
+
+                    .range {
+                        -webkit-appearance: none;
+                        height: 6px;
+                        background: #d3d3d3;
+                        border-radius: 5px;
+                        outline: none;
+                        opacity: 0.7;
+                        -webkit-transition: .2s;
+                        transition: opacity .2s;
+                    }
+                    .range::-webkit-slider-thumb {
+                        -webkit-appearance: none;
+                        appearance: none;
+                        width: 14px;
+                        height: 14px;
+                        border-radius: 50%;
+                        background: rgb(51, 122, 183);
+                        cursor: pointer;
+                    }
+                    
+                    .range::-moz-range-thumb {
+                        width: 14px;
+                        height: 14px;
+                        border-radius: 50%;
+                        background: rgb(51, 122, 183);
+                        cursor: pointer;
+                    }
+
+                  
+
+                }
+               
+        
+                .gradient-delete {            
+                    grid-row: 1;
+                    grid-column: 6;
+                    display: grid;
+                    align-content: center;
+                    justify-content: center;
+                }
+            }
+
             .pane {
                 color: white;
 

+ 63 - 0
inspector/src/components/actionTabs/tabs/gradientNodePropertyComponent.tsx

@@ -0,0 +1,63 @@
+
+import * as React from "react";
+import { GradientBlockColorStep, GradientBlock } from 'babylonjs/Materials/Node/Blocks/gradientBlock';
+import { GradientStepComponent } from './gradientStepComponent';
+import { Color3 } from 'babylonjs/Maths/math.color';
+import { ButtonLineComponent } from '../lines/buttonLineComponent';
+import { IPropertyComponentProps } from './propertyComponentProps';
+
+export class GradientPropertyTabComponent extends React.Component<IPropertyComponentProps> {
+
+    private _gradientBlock: GradientBlock;
+    constructor(props: IPropertyComponentProps) {
+        super(props)
+        this._gradientBlock =  this.props.block as GradientBlock;
+    }
+
+    forceRebuild() {
+        this._gradientBlock.colorStepsUpdated();
+        this.forceUpdate();
+    }
+
+    deleteStep(step: GradientBlockColorStep) {
+        let index =  this._gradientBlock.colorSteps.indexOf(step);
+
+        if (index > -1) {
+            this._gradientBlock.colorSteps.splice(index, 1);
+            this.forceRebuild();
+        }
+    }
+
+    addNewStep() {
+        let newStep = new GradientBlockColorStep( this._gradientBlock, 1.0, Color3.White());
+        this._gradientBlock.colorSteps.push(newStep);
+        this.forceRebuild();
+    }
+
+    checkForReOrder() {
+       
+        this._gradientBlock.colorSteps.sort((a, b) => {
+            return a.step - b.step;
+        });
+
+        this.forceUpdate();
+    }
+
+    render() {
+        return (
+            <div>
+                <ButtonLineComponent label="Add new step" onClick={() => this.addNewStep()} />
+                {
+                     this._gradientBlock.colorSteps.map((c, i) => {
+                        return (
+                            <GradientStepComponent globalState={this.props.globalState} 
+                            onCheckForReOrder={() => this.checkForReOrder()}
+                            onUpdateStep={() => this.forceRebuild()}
+                            key={"step-" + i} lineIndex={i} step={c} onDelete={() => this.deleteStep(c)}/>
+                        )
+                    })
+                    }
+            </div>
+        );
+    }
+}

+ 75 - 0
inspector/src/components/actionTabs/tabs/gradientStepComponent.tsx

@@ -0,0 +1,75 @@
+import * as React from 'react';
+import { GlobalState } from '../../globalState';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faTrash } from '@fortawesome/free-solid-svg-icons';
+import { Color3 } from 'babylonjs/Maths/math.color';
+import { GradientBlockColorStep } from 'babylonjs/Materials/Node/Blocks/gradientBlock';
+import { ColorPickerLineComponent } from '../lines/colorPickerComponent';
+
+
+interface IGradientStepComponentProps {
+    globalState: GlobalState;
+    step: GradientBlockColorStep;
+    lineIndex: number;
+    onDelete: () => void;
+    onUpdateStep: () => void;
+    onCheckForReOrder: () => void;
+}
+
+export class GradientStepComponent extends React.Component<IGradientStepComponentProps, {gradient: number}> {
+
+    constructor(props: IGradientStepComponentProps) {
+        super(props);
+
+        this.state={gradient: props.step.step};
+    }
+
+    updateColor(color: string) {
+        this.props.step.color = Color3.FromHexString(color);
+        
+        this.props.onUpdateStep();
+        this.forceUpdate();
+    }    
+    
+    updateStep(gradient: number) {
+        this.props.step.step = gradient;
+
+        this.setState({gradient: gradient});
+
+        this.props.onUpdateStep();
+    }
+
+    onPointerUp() {
+        this.props.onCheckForReOrder();
+    }
+
+    render() {
+        let step = this.props.step;
+
+        return (
+            <div className="gradient-step">
+                <div className="step">
+                    {`#${this.props.lineIndex}`}
+                </div>
+                <div className="color">
+                    <ColorPickerLineComponent value={step.color} disableAlpha={true}
+                            onColorChanged={color => {
+                                    this.updateColor(color);
+                            }} 
+                    />  
+                </div>
+                <div className="step-value">
+                    {step.step.toFixed(2)}
+                </div>
+                <div className="step-slider slider">
+                    <input className="range" type="range" step={0.01} min={0} max={1.0} value={step.step}
+                        onPointerUp={evt => this.onPointerUp()}
+                        onChange={evt => this.updateStep(parseFloat(evt.target.value))} />
+                </div>
+                <div className="gradient-delete" onClick={() => this.props.onDelete()}>
+                    <FontAwesomeIcon icon={faTrash} />
+                </div>
+            </div>
+        )
+    }
+}

+ 7 - 0
inspector/src/components/actionTabs/tabs/propertyComponentProps.ts

@@ -0,0 +1,7 @@
+import { GlobalState } from "../../globalState";
+import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
+
+export interface IPropertyComponentProps {
+    globalState: GlobalState;
+    block: NodeMaterialBlock;
+}

+ 29 - 13
inspector/src/components/actionTabs/tabs/propertyGrids/materials/nodeMaterialPropertyGridComponent.tsx

@@ -20,6 +20,7 @@ import { SliderLineComponent } from '../../../lines/sliderLineComponent';
 import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes';
 import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 import { Color4LineComponent } from '../../../lines/color4LineComponent';
+import { GradientPropertyTabComponent } from '../../gradientNodePropertyComponent';
 
 interface INodeMaterialPropertyGridComponentProps {
     globalState: GlobalState;
@@ -118,6 +119,7 @@ export class NodeMaterialPropertyGridComponent extends React.Component<INodeMate
                         onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 )
             }
+            
         return null;
     }
 
@@ -128,12 +130,7 @@ export class NodeMaterialPropertyGridComponent extends React.Component<INodeMate
             return a.name.localeCompare(b.name);
         });
 
-        if (configurableInputBlocks.length === 0) {
-            return null;
-        }
-
         let namedGroups: string[] = [];
-
         configurableInputBlocks.forEach(block => {
             if (!block.groupInInspector) {
                 return;
@@ -143,18 +140,26 @@ export class NodeMaterialPropertyGridComponent extends React.Component<INodeMate
                 namedGroups.push(block.groupInInspector);
             }
         });
-
         namedGroups.sort();
+        
+
+        let gradiantNodeMaterialBlocks = this.props.material.attachedBlocks.filter(block => {
+            return block.visibleInInspector && block.getClassName() === 'GradientBlock'
+        }).sort( (a, b) => {
+            return a.name.localeCompare(b.name);
+        });
+
+        let inputBlockContainer = configurableInputBlocks.length > 0 ?
+            <LineContainerComponent globalState={this.props.globalState} title="INPUTS"> {
+                configurableInputBlocks.filter(block => !block.groupInInspector).map(block => {
+                    return this.renderInputBlock(block);
+                })
+            }
+            </LineContainerComponent> : null;
 
         return (           
             <>
-                <LineContainerComponent globalState={this.props.globalState} title="INPUTS">
-                    {
-                        configurableInputBlocks.filter(block => !block.groupInInspector).map(block => {
-                            return this.renderInputBlock(block);
-                        })
-                    }           
-                </LineContainerComponent>
+                {inputBlockContainer}
                 {
                     namedGroups.map((name, i) => {
                         return (
@@ -166,6 +171,17 @@ export class NodeMaterialPropertyGridComponent extends React.Component<INodeMate
                             }
                             </LineContainerComponent>
                         )
+                    })   
+                }
+                {
+                    gradiantNodeMaterialBlocks.map((block,i) => {
+                        return (
+                            <LineContainerComponent key={block.name +i} globalState={this.props.globalState} title={block.name.toUpperCase()}>
+                            {
+                                <GradientPropertyTabComponent globalState={this.props.globalState} block={block}/>
+                            }
+                            </LineContainerComponent>
+                        )
                     })
                 }
           </>

+ 62 - 5
nodeEditor/src/diagram/properties/gradientNodePropertyComponent.tsx

@@ -7,11 +7,32 @@ import { ButtonLineComponent } from '../../sharedComponents/buttonLineComponent'
 import { Color3 } from 'babylonjs/Maths/math.color';
 import { IPropertyComponentProps } from './propertyComponentProps';
 import { GeneralPropertyTabComponent } from './genericNodePropertyComponent';
+import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponent';
+import { Nullable } from 'babylonjs/types';
+import { Observer } from 'babylonjs/Misc/observable';
 
 export class GradientPropertyTabComponent extends React.Component<IPropertyComponentProps> {
 
+    private onValueChangedObserver: Nullable<Observer<GradientBlock>>;
+
     constructor(props: IPropertyComponentProps) {
-        super(props)
+        super(props);
+    }
+
+    componentDidMount() {
+        let gradientBlock = this.props.block as GradientBlock;
+        this.onValueChangedObserver = gradientBlock.onValueChangedObservable.add(() => {
+            this.forceUpdate();
+            this.props.globalState.onUpdateRequiredObservable.notifyObservers();
+        });
+    }
+
+    componentWillUnmount() {
+        let gradientBlock = this.props.block as GradientBlock;
+        if (this.onValueChangedObserver) {
+            gradientBlock.onValueChangedObservable.remove(this.onValueChangedObserver);
+            this.onValueChangedObserver = null;
+        }
     }
 
     forceRebuild() {
@@ -26,6 +47,7 @@ export class GradientPropertyTabComponent extends React.Component<IPropertyCompo
 
         if (index > -1) {
             gradientBlock.colorSteps.splice(index, 1);
+            gradientBlock.colorStepsUpdated();
             this.forceRebuild();
             this.forceUpdate();
         }
@@ -34,8 +56,9 @@ export class GradientPropertyTabComponent extends React.Component<IPropertyCompo
     addNewStep() {
         let gradientBlock = this.props.block as GradientBlock;
 
-        let newStep = new GradientBlockColorStep(1.0, Color3.White());
+        let newStep = new GradientBlockColorStep(gradientBlock, 1.0, Color3.White());
         gradientBlock.colorSteps.push(newStep);
+        gradientBlock.colorStepsUpdated();
 
         this.forceRebuild();
         this.forceUpdate();
@@ -54,6 +77,7 @@ export class GradientPropertyTabComponent extends React.Component<IPropertyCompo
 
             return -1;
         });
+        gradientBlock.colorStepsUpdated();
 
         this.props.globalState.onUpdateRequiredObservable.notifyObservers();
         this.forceUpdate();
@@ -61,20 +85,53 @@ export class GradientPropertyTabComponent extends React.Component<IPropertyCompo
 
     render() {
         let gradientBlock = this.props.block as GradientBlock;
-      
+
+        var typeOptions = [
+            { label: "None", value: 0 },
+            { label: "Visible in the inspector", value: 1 },
+        ];
+
         return (
             <div>
                 <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+                <LineContainerComponent title="PROPERTIES">
+                <OptionsLineComponent label="Type" options={typeOptions} target={this.props.block}
+                            noDirectUpdate={true}
+                            getSelection={(block) => {
+                                if (block.visibleInInspector) {
+                                    return 1;
+                                }
+
+                                if (block.isConstant) {
+                                    return 2;
+                                }
+
+                                return 0;
+                            }}
+                            onSelect={(value: any) => {
+                                switch (value) {
+                                    case 0:
+                                        this.props.block.visibleInInspector = false;
+                                        break;
+                                    case 1:
+                                        this.props.block.visibleInInspector = true;
+                                        break;
+                                }
+                                this.forceUpdate();
+                                this.props.globalState.onUpdateRequiredObservable.notifyObservers();
+                                this.props.globalState.onRebuildRequiredObservable.notifyObservers();
+                            }} />
+                </LineContainerComponent>
                 <LineContainerComponent title="STEPS">
                     <ButtonLineComponent label="Add new step" onClick={() => this.addNewStep()} />
                     {
                         gradientBlock.colorSteps.map((c, i) => {
                             return (
-                                <GradientStepComponent globalState={this.props.globalState} 
+                                <GradientStepComponent globalState={this.props.globalState}
                                 onCheckForReOrder={() => this.checkForReOrder()}
                                 onUpdateStep={() => this.forceRebuild()}
                                 key={"step-" + i} lineIndex={i} step={c} onDelete={() => this.deleteStep(c)}/>
-                            )
+                            );
                         })
                     }
                 </LineContainerComponent>

+ 0 - 6
src/Materials/Node/Blocks/Input/inputBlock.ts

@@ -60,9 +60,6 @@ export class InputBlock extends NodeMaterialBlock {
     /** @hidden */
     public _systemValue: Nullable<NodeMaterialSystemValues> = null;
 
-    /** Gets or sets a boolean indicating that this input can be edited in the Inspector (false by default) */
-    public visibleInInspector = false;
-
     /** Gets or sets a boolean indicating that the value of this input will not change after a build */
     public isConstant = false;
 
@@ -676,7 +673,6 @@ export class InputBlock extends NodeMaterialBlock {
             // Common Property "Type"
             codes.push(
                 `${variableName}.isConstant = ${this.isConstant}`,
-                `${variableName}.visibleInInspector = ${this.visibleInInspector}`
             );
 
             codes.push('');
@@ -699,7 +695,6 @@ export class InputBlock extends NodeMaterialBlock {
         serializationObject.mode = this._mode;
         serializationObject.systemValue = this._systemValue;
         serializationObject.animationType = this._animationType;
-        serializationObject.visibleInInspector = this.visibleInInspector;
         serializationObject.min = this.min;
         serializationObject.max = this.max;
         serializationObject.isBoolean = this.isBoolean;
@@ -728,7 +723,6 @@ export class InputBlock extends NodeMaterialBlock {
 
         this._systemValue = serializationObject.systemValue || serializationObject.wellKnownValue;
         this._animationType = serializationObject.animationType;
-        this.visibleInInspector = serializationObject.visibleInInspector;
         this.min = serializationObject.min || 0;
         this.max = serializationObject.max || 0;
         this.isBoolean = !!serializationObject.isBoolean;

+ 63 - 13
src/Materials/Node/Blocks/gradientBlock.ts

@@ -6,25 +6,57 @@ import { NodeMaterialBlockTargets } from '../Enums/nodeMaterialBlockTargets';
 import { _TypeStore } from '../../../Misc/typeStore';
 import { Color3 } from '../../../Maths/math.color';
 import { Scene } from '../../../scene';
+import { Observable } from '../../../Misc/observable';
 
 /**
  * Class used to store a color step for the GradientBlock
  */
 export class GradientBlockColorStep {
+    private _parent: GradientBlock;
+
+    private _step: number;
+    /**
+     * Gets value indicating which step this color is associated with (between 0 and 1)
+     */
+    public get step(): number {
+        return this._step;
+    }
+
+    /**
+     * Sets a value indicating which step this color is associated with (between 0 and 1)
+    */
+    public set step(val: number) {
+        this._step = val;
+        this._parent.onValueChangedObservable?.notifyObservers(this._parent);
+    }
+
+    private _color: Color3;
+
+    /**
+     * Gets the color associated with this step
+     */
+    public get color(): Color3 {
+        return this._color;
+    }
+
+    /**
+     * Sets the color associated with this step
+     */
+    public set color(val: Color3) {
+        this._color = val;
+        this._parent.onValueChangedObservable?.notifyObservers(this._parent);
+    }
+
     /**
      * Creates a new GradientBlockColorStep
+     * @param parent defines the parent gradient for this block
      * @param step defines a value indicating which step this color is associated with (between 0 and 1)
      * @param color defines the color associated with this step
      */
-    public constructor(
-        /**
-         * Gets or sets a value indicating which step this color is associated with (between 0 and 1)
-         */
-        public step: number,
-        /**
-         * Gets or sets the color associated with this step
-         */
-        public color: Color3) {
+    public constructor(parent: GradientBlock, step: number, color: Color3) {
+        this._parent = parent;
+        this.step = step;
+        this.color = color;
     }
 }
 
@@ -37,10 +69,17 @@ export class GradientBlock extends NodeMaterialBlock {
      * Gets or sets the list of color steps
      */
     public colorSteps: GradientBlockColorStep[] = [
-        new GradientBlockColorStep(0, Color3.Black()),
-        new GradientBlockColorStep(1.0, Color3.White())
+        new GradientBlockColorStep(this, 0, Color3.Black()),
+        new GradientBlockColorStep(this, 1.0, Color3.White())
     ];
 
+    /** Gets an observable raised when the value is changed */
+    public onValueChangedObservable = new Observable<GradientBlock>();
+
+    /** calls observable when the value is changed*/
+    public colorStepsUpdated() {
+        this.onValueChangedObservable.notifyObservers(this);
+    }
     /**
      * Creates a new GradientBlock
      * @param name defines the block name
@@ -121,7 +160,18 @@ export class GradientBlock extends NodeMaterialBlock {
     public serialize(): any {
         let serializationObject = super.serialize();
 
-        serializationObject.colorSteps = this.colorSteps;
+        serializationObject.colorSteps = [];
+
+        for (var step of this.colorSteps) {
+            serializationObject.colorSteps.push({
+                step: step.step,
+                color: {
+                    r: step.color.r,
+                    g: step.color.g,
+                    b: step.color.b
+                }
+            });
+        }
 
         return serializationObject;
     }
@@ -132,7 +182,7 @@ export class GradientBlock extends NodeMaterialBlock {
         this.colorSteps = [];
 
         for (var step of serializationObject.colorSteps) {
-            this.colorSteps.push(new GradientBlockColorStep(step.step, new Color3(step.color.r, step.color.g, step.color.b)));
+            this.colorSteps.push(new GradientBlockColorStep(this, step.step, new Color3(step.color.r, step.color.g, step.color.b)));
         }
     }
 

+ 7 - 1
src/Materials/Node/nodeMaterialBlock.ts

@@ -156,6 +156,9 @@ export class NodeMaterialBlock {
         return null;
     }
 
+     /** Gets or sets a boolean indicating that this input can be edited in the Inspector (false by default) */
+     public visibleInInspector = false;
+
     /**
      * Creates a new NodeMaterialBlock
      * @param name defines the block name
@@ -583,7 +586,8 @@ export class NodeMaterialBlock {
     }
 
     protected _dumpPropertiesCode() {
-        return "";
+        let variableName = this._codeVariableName;
+        return `${variableName}.visibleInInspector = ${this.visibleInInspector}`;
     }
 
     /** @hidden */
@@ -703,6 +707,7 @@ export class NodeMaterialBlock {
         serializationObject.id = this.uniqueId;
         serializationObject.name = this.name;
         serializationObject.comments = this.comments;
+        serializationObject.visibleInInspector = this.visibleInInspector;
 
         serializationObject.inputs = [];
         serializationObject.outputs = [];
@@ -722,6 +727,7 @@ export class NodeMaterialBlock {
     public _deserialize(serializationObject: any, scene: Scene, rootUrl: string) {
         this.name = serializationObject.name;
         this.comments = serializationObject.comments;
+        this.visibleInInspector = !!serializationObject.visibleInInspector;
         this._deserializePortDisplayNamesAndExposedOnFrame(serializationObject);
     }