David Catuhe hace 5 años
padre
commit
9a63b02a4d

+ 4 - 9
loaders/src/OBJ/objFileLoader.ts

@@ -650,8 +650,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
         };
 
         /**
-         * Create triangles from polygons by recursion
-         * The best to understand how it works is to draw it in the same time you get the recursion.
+         * Create triangles from polygons
          * It is important to notice that a triangle is a polygon
          * We get 5 patterns of face defined in OBJ File :
          * facePattern1 = ["1","2","3","4","5","6"]
@@ -663,15 +662,11 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
          * @param face Array[String] The indices of elements
          * @param v Integer The variable to increment
          */
-        var getTriangles = (face: Array<string>, v: number) => {
+        var getTriangles = (faces: Array<string>, v: number) => {
             //Work for each element of the array
-            if (v + 1 < face.length) {
+            for (var faceIndex = v; faceIndex < faces.length - 1; faceIndex++) {
                 //Add on the triangle variable the indexes to obtain triangles
-                triangles.push(face[0], face[v], face[v + 1]);
-                //Incrementation for recursion
-                v += 1;
-                //Recursion
-                getTriangles(face, v);
+                triangles.push(faces[0], faces[faceIndex], faces[faceIndex + 1]);
             }
 
             //Result obtained after 2 iterations:

+ 7 - 0
nodeEditor/src/components/diagram/diagram.scss

@@ -76,6 +76,13 @@
         }        
     }
 
+    &.gradient {
+        .outputs, .inputs {
+            transform: translateY(5px);
+        }        
+        height: 70px;
+    }
+
     &.attribute {
         background: #40866E;
     }

+ 23 - 0
nodeEditor/src/components/diagram/gradient/gradientNodeFactory.tsx

@@ -0,0 +1,23 @@
+import * as SRD from "storm-react-diagrams";
+import * as React from "react";
+import { GlobalState } from '../../../globalState';
+import { GradientNodeWidget } from './gradientNodeWidget';
+import { GradientNodeModel } from './gradientNodeModel';
+
+export class GradientNodeFactory extends SRD.AbstractNodeFactory {
+    private _globalState: GlobalState;
+
+    constructor(globalState: GlobalState) {
+        super("gradient");
+
+        this._globalState = globalState;
+    }
+
+    generateReactWidget(diagramEngine: SRD.DiagramEngine, node: GradientNodeModel): JSX.Element {
+        return <GradientNodeWidget node={node} globalState={this._globalState} />;
+    }
+
+    getNewInstance() {
+        return new GradientNodeModel();
+    }
+}

+ 25 - 0
nodeEditor/src/components/diagram/gradient/gradientNodeModel.tsx

@@ -0,0 +1,25 @@
+import * as React from "react";
+import { DefaultNodeModel } from '../defaultNodeModel';
+import { GlobalState } from '../../../globalState';
+import { GradientBlock } from 'babylonjs/Materials/Node/Blocks/gradientBlock';
+import { GradientPropertyTabComponentProps } from './gradientNodePropertyComponent';
+
+export class GradientNodeModel extends DefaultNodeModel {
+
+    public get gradientBlock(): GradientBlock {
+        return this.block as GradientBlock;
+    }
+
+	/**
+	 * Constructs the node model
+	 */
+    constructor() {
+        super("gradient");
+    }
+
+    renderProperties(globalState: GlobalState) {
+        return (
+            <GradientPropertyTabComponentProps globalState={globalState} gradientNode={this} />
+        );
+    }
+}

+ 75 - 0
nodeEditor/src/components/diagram/gradient/gradientNodePropertyComponent.tsx

@@ -0,0 +1,75 @@
+
+import * as React from "react";
+import { GlobalState } from '../../../globalState';
+import { LineContainerComponent } from '../../../sharedComponents/lineContainerComponent';
+import { TextInputLineComponent } from '../../../sharedComponents/textInputLineComponent';
+import { TextLineComponent } from '../../../sharedComponents/textLineComponent';
+import { GradientNodeModel } from './gradientNodeModel';
+import { GradientBlockColorStep } from 'babylonjs/Materials/Node/Blocks/gradientBlock';
+import { GradientStepComponent } from './gradientStepComponent';
+import { ButtonLineComponent } from '../../../sharedComponents/buttonLineComponent';
+import { Color3 } from 'babylonjs/Maths/math.color';
+
+interface IGradientPropertyTabComponentProps {
+    globalState: GlobalState;
+    gradientNode: GradientNodeModel;
+}
+
+export class GradientPropertyTabComponentProps extends React.Component<IGradientPropertyTabComponentProps> {
+
+    constructor(props: IGradientPropertyTabComponentProps) {
+        super(props)
+    }
+
+    forceRebuild() {
+        this.props.globalState.onUpdateRequiredObservable.notifyObservers();
+        this.props.globalState.onRebuildRequiredObservable.notifyObservers();
+    }
+
+    deleteStep(step: GradientBlockColorStep) {
+        let gradientBlock = this.props.gradientNode.gradientBlock;
+
+        let index = gradientBlock.colorSteps.indexOf(step);
+
+        if (index > -1) {
+            gradientBlock.colorSteps.splice(index, 1);
+            this.forceRebuild();
+            this.forceUpdate();
+        }
+    }
+
+    addNewStep() {
+        let gradientBlock = this.props.gradientNode.gradientBlock;
+
+        let newStep = new GradientBlockColorStep(1.0, Color3.White());
+        gradientBlock.colorSteps.push(newStep);
+
+        this.forceRebuild();
+        this.forceUpdate();
+    }
+
+    render() {
+        let gradientBlock = this.props.gradientNode.gradientBlock;
+      
+        return (
+            <div>
+                <LineContainerComponent title="GENERAL">
+                    <TextInputLineComponent globalState={this.props.globalState} label="Name" propertyName="name" target={gradientBlock} onChange={() => this.props.globalState.onUpdateRequiredObservable.notifyObservers()} />
+                    <TextLineComponent label="Type" value={gradientBlock.getClassName()} />
+                </LineContainerComponent>
+                <LineContainerComponent title="STEPS">
+                    <ButtonLineComponent label="Add new step" onClick={() => this.addNewStep()} />
+                    {
+                        gradientBlock.colorSteps.map((c, i) => {
+                            return (
+                                <GradientStepComponent globalState={this.props.globalState} 
+                                onUpdateStep={() => this.forceRebuild()}
+                                key={c.step} lineIndex={i} step={c} onDelete={() => this.deleteStep(c)}/>
+                            )
+                        })
+                    }
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 67 - 0
nodeEditor/src/components/diagram/gradient/gradientNodeWidget.tsx

@@ -0,0 +1,67 @@
+import * as React from "react";
+import { GradientNodeModel } from './gradientNodeModel';
+import { Nullable } from 'babylonjs/types';
+import { GlobalState } from '../../../globalState';
+import { PortHelper } from '../portHelper';
+import { GradientBlock } from 'babylonjs/Materials/Node/Blocks/gradientBlock';
+
+export interface IGradientNodeWidgetProps {
+    node: Nullable<GradientNodeModel>;
+    globalState: GlobalState;
+}
+
+export class GradientNodeWidget extends React.Component<IGradientNodeWidgetProps> {
+    constructor(props: IGradientNodeWidgetProps) {
+        super(props);
+        this.state = {};
+
+        if (this.props.node) {
+            this.props.node.addListener({
+                selectionChanged: () => {
+                    let selected = (this.props.node as any).selected;
+                    this.props.globalState.onSelectionChangedObservable.notifyObservers(selected ? this.props.node : null);
+                }
+            });
+        }
+    }
+
+    renderValue(value: string) {
+        if (value) {
+            return (
+                <div className="value-text">
+                    {value}
+                </div>
+            )
+        }
+
+        return null;
+    }
+
+    render() {
+        var inputPorts = PortHelper.GenerateInputPorts(this.props.node, undefined, true);
+        var outputPorts = PortHelper.GenerateOutputPorts(this.props.node, true);
+        let gradientBlock = this.props.node!.block! as GradientBlock;
+
+        let gradients = gradientBlock.colorSteps.map(c => `rgb(${c.color.r * 255}, ${c.color.g * 255}, ${c.color.b * 255}) ${c.step * 100}%`);
+
+        let style = {
+            background: gradients.length ? `linear-gradient(90deg, ${gradients.join(", ")})` : 'black'
+        };
+
+        return (
+            <div className={"diagramBlock gradient"} style={style}>
+                <div className="header">
+                    {gradientBlock.name}
+                </div>
+                <div className="inputs">
+                    {inputPorts}
+                </div>
+                <div className="outputs">
+                    {outputPorts}
+                </div>
+                <div className="value">                
+                </div>
+            </div>
+        );
+    }
+}

+ 61 - 0
nodeEditor/src/components/diagram/gradient/gradientStepComponent.tsx

@@ -0,0 +1,61 @@
+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';
+
+interface IGradientStepComponentProps {
+    globalState: GlobalState;
+    step: GradientBlockColorStep;
+    lineIndex: number;
+    onDelete: () => void;
+    onUpdateStep: () => 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();
+    }
+
+    render() {
+        let step = this.props.step;
+
+        return (
+            <div className="gradient-step">
+                <div className="step">
+                    {`#${this.props.lineIndex}`}
+                </div>
+                <input type="color" value={step.color.toHexString()} onChange={(evt) => this.updateColor(evt.target.value)} />
+                <div className="step-value">
+                    {step.step.toFixed(2)}
+                </div>
+                <div className="step-slider">
+                    <input className="range" type="range" step={0.01} min={0} max={1.0} value={step.step}
+                        onChange={evt => this.updateStep(parseFloat(evt.target.value))} />
+                </div>
+                <div className="gradient-delete" onClick={() => this.props.onDelete()}>
+                    <FontAwesomeIcon icon={faTrash} />
+                </div>
+            </div>
+        )
+    }
+}

+ 3 - 1
nodeEditor/src/components/diagram/portHelper.tsx

@@ -48,7 +48,9 @@ export class PortHelper {
                 let style = this._GetPortStyle(port.connection!.type);
 
                 outputPorts.push(
-                    <div key={key} className="output-port">
+                    <div key={key}                                    
+				        title={port.name}
+                        className="output-port">
                         {
                             !ignoreLabel &&
                             <div className="output-port-label">

+ 97 - 49
nodeEditor/src/components/propertyTab/propertyTab.scss

@@ -36,6 +36,54 @@
         }
     }
 
+    .range {
+        -webkit-appearance: none;
+        width: 120px;
+        height: 6px;
+        background: #d3d3d3;
+        border-radius: 5px;
+        outline: none;
+        opacity: 0.7;
+        -webkit-transition: .2s;
+        transition: opacity .2s;
+    }
+    
+    .range:hover {
+        opacity: 1;
+    }
+    
+    .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;
+    }
+
+    input[type="color"] {
+        -webkit-appearance: none;
+        border: 1px solid rgba(255, 255, 255, 0.5);
+        padding: 0;
+        width: 30px;
+        height: 20px;
+    }
+    input[type="color"]::-webkit-color-swatch-wrapper {
+        padding: 0;
+    }
+    input[type="color"]::-webkit-color-swatch {
+        border: none;
+    }
+
     .sliderLine {
         padding-left: $line-padding-left;
         height: 30px;
@@ -54,40 +102,6 @@
             
             display: flex;
             align-items: center;
-
-            .range {
-                -webkit-appearance: none;
-                width: 120px;
-                height: 6px;
-                background: #d3d3d3;
-                border-radius: 5px;
-                outline: none;
-                opacity: 0.7;
-                -webkit-transition: .2s;
-                transition: opacity .2s;
-            }
-            
-            .range:hover {
-                opacity: 1;
-            }
-            
-            .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;
-            }
         }                    
     }     
 
@@ -194,6 +208,54 @@
         }
     }
 
+    .gradient-step {
+        display: grid;
+        grid-template-rows: 100%;
+        grid-template-columns: 30px 30px 40px auto 20px 5px;
+        margin-top: 5px;
+        margin-left: 5px;
+
+        .step {
+            grid-row: 1;
+            grid-column: 1;
+        }
+            
+        .color {
+            grid-row: 1;
+            grid-column: 2;
+            cursor: pointer;
+        }
+
+        .step-value {       
+            margin-left: 5px;     
+            grid-row: 1;
+            grid-column: 3;
+            text-align: right;
+            margin-right: 5px;
+        }
+
+        .step-slider {            
+            grid-row: 1;
+            grid-column: 4;
+            display: grid;
+            justify-content: stretch;
+            align-content: center;
+            margin-right: 5px;
+
+            input {
+                width: unset;
+            }
+        }
+
+        .gradient-delete {            
+            grid-row: 1;
+            grid-column: 5;
+            display: grid;
+            align-content: center;
+            justify-content: center;
+        }
+    }
+
     .floatLine {
         padding-left: $line-padding-left;
         height: 30px;
@@ -448,21 +510,7 @@
                 grid-column: 2;
                 
                 display: flex;
-                align-items: center;   
-
-                input[type="color"] {
-                    -webkit-appearance: none;
-                    border: 1px solid rgba(255, 255, 255, 0.5);
-                    padding: 0;
-                    width: 30px;
-                    height: 20px;
-                }
-                input[type="color"]::-webkit-color-swatch-wrapper {
-                    padding: 0;
-                }
-                input[type="color"]::-webkit-color-swatch {
-                    border: none;
-                }
+                align-items: center;            
                 
                 input {
                     margin-right: 5px;

+ 8 - 3
nodeEditor/src/graphEditor.tsx

@@ -51,6 +51,9 @@ import { LightInformationNodeFactory } from './components/diagram/lightInformati
 import { LightInformationNodeModel } from './components/diagram/lightInformation/lightInformationNodeModel';
 import { LightInformationBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/lightInformationBlock';
 import { PreviewAreaComponent } from './components/preview/previewAreaComponent';
+import { GradientBlock } from 'babylonjs/Materials/Node/Blocks/gradientBlock';
+import { GradientNodeModel } from './components/diagram/gradient/gradientNodeModel';
+import { GradientNodeFactory } from './components/diagram/gradient/gradientNodeFactory';
 
 require("storm-react-diagrams/dist/style.min.css");
 require("./main.scss");
@@ -121,6 +124,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
             newNode = new ClampNodeModel();        
         } else if (options.nodeMaterialBlock instanceof LightInformationBlock) {
             newNode = new LightInformationNodeModel();
+        } else if (options.nodeMaterialBlock instanceof GradientBlock) {
+            newNode = new GradientNodeModel();
         } else {
             newNode = new GenericNodeModel();
         }
@@ -197,13 +202,13 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
         this._engine.registerNodeFactory(new TrigonometryNodeFactory(this.props.globalState));
         this._engine.registerNodeFactory(new ClampNodeFactory(this.props.globalState));
         this._engine.registerNodeFactory(new LightInformationNodeFactory(this.props.globalState));
+        this._engine.registerNodeFactory(new GradientNodeFactory(this.props.globalState));
         this._engine.registerLinkFactory(new AdvancedLinkFactory());
 
         this.props.globalState.onRebuildRequiredObservable.add(() => {
             if (this.props.globalState.nodeMaterial) {
                 this.buildMaterial();
             }
-            this.forceUpdate();
         });
 
         this.props.globalState.onResetRequiredObservable.add((locations) => {
@@ -213,8 +218,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
             }
         });
 
-        this.props.globalState.onUpdateRequiredObservable.add(() => {
-            this.forceUpdate();
+        this.props.globalState.onUpdateRequiredObservable.add(() => {          
+            this._engine.repaintCanvas();  
         });
 
         this.props.globalState.onZoomToFitRequiredObservable.add(() => {

+ 3 - 3
src/Materials/Node/Blocks/Input/inputBlock.ts

@@ -347,10 +347,10 @@ export class InputBlock extends NodeMaterialBlock {
         }
     }
 
-    private _emitConstant() {
+    private _emitConstant(state: NodeMaterialBuildState) {
         switch (this.type) {
             case NodeMaterialBlockConnectionPointTypes.Float:
-                return `${this.value}`;
+                return `${state._emitFloat(this.value)}`;
             case NodeMaterialBlockConnectionPointTypes.Vector2:
                 return `vec2(${this.value.x}, ${this.value.y})`;
             case NodeMaterialBlockConnectionPointTypes.Vector3:
@@ -378,7 +378,7 @@ export class InputBlock extends NodeMaterialBlock {
                     return;
                 }
                 state.constants.push(this.associatedVariableName);
-                state._constantDeclaration += this._declareOutput(this.output, state) + ` = ${this._emitConstant()};\r\n`;
+                state._constantDeclaration += this._declareOutput(this.output, state) + ` = ${this._emitConstant(state)};\r\n`;
                 return;
             }
 

+ 24 - 7
src/Materials/Node/Blocks/gradientBlock.ts

@@ -34,9 +34,8 @@ export class GradientBlockColorStep {
 export class GradientBlock extends NodeMaterialBlock {
 
     public colorSteps: GradientBlockColorStep[] = [
-        new GradientBlockColorStep(0, Color3.Red()),
-        new GradientBlockColorStep(0.5, Color3.Teal()),
-        new GradientBlockColorStep(1.0, Color3.Red())
+        new GradientBlockColorStep(0, Color3.White()),
+        new GradientBlockColorStep(1.0, Color3.Black())
     ];
 
     /**
@@ -48,6 +47,12 @@ export class GradientBlock extends NodeMaterialBlock {
 
         this.registerInput("gradient", NodeMaterialBlockConnectionPointTypes.Float);
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color3);
+
+        this._inputs[0].acceptedConnectionPointTypes.push(NodeMaterialBlockConnectionPointTypes.Vector2);
+        this._inputs[0].acceptedConnectionPointTypes.push(NodeMaterialBlockConnectionPointTypes.Vector3);
+        this._inputs[0].acceptedConnectionPointTypes.push(NodeMaterialBlockConnectionPointTypes.Vector4);
+        this._inputs[0].acceptedConnectionPointTypes.push(NodeMaterialBlockConnectionPointTypes.Color3);
+        this._inputs[0].acceptedConnectionPointTypes.push(NodeMaterialBlockConnectionPointTypes.Color4);
     }
 
     /**
@@ -80,21 +85,29 @@ export class GradientBlock extends NodeMaterialBlock {
     protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
-        if (!this.colorSteps.length) {
+        let output = this._outputs[0];
+
+        if (!this.colorSteps.length || !this.gradient.connectedPoint) {
+            state.compilationString += this._declareOutput(output, state) + ` = vec3(0., 0., 0.);\r\n`;
             return;
         }
 
-        let output = this._outputs[0];
         let tempColor = state._getFreeVariableName("gradientTempColor");
         let tempPosition = state._getFreeVariableName("gradientTempPosition");
         
         state.compilationString += `vec3 ${tempColor} = ${this._writeColorConstant(0)};\r\n`;        
         state.compilationString += `float ${tempPosition};\r\n`;
 
+        let gradientSource = this.gradient.associatedVariableName;
+
+        if (this.gradient.connectedPoint!.type !== NodeMaterialBlockConnectionPointTypes.Float) {
+            gradientSource += ".x";
+        }
+
         for (var index = 1; index < this.colorSteps.length; index++) {
             let step = this.colorSteps[index];
             let previousStep = this.colorSteps[index - 1];
-            state.compilationString += `${tempPosition} = clamp((${this.gradient.associatedVariableName} - ${state._emitFloat(previousStep.step)}) / (${state._emitFloat(step.step)} -  ${state._emitFloat(previousStep.step)}), 0.0, 1.0) * step(${state._emitFloat(index)}, ${state._emitFloat(this.colorSteps.length - 1)});\r\n`;
+            state.compilationString += `${tempPosition} = clamp((${gradientSource} - ${state._emitFloat(previousStep.step)}) / (${state._emitFloat(step.step)} -  ${state._emitFloat(previousStep.step)}), 0.0, 1.0) * step(${state._emitFloat(index)}, ${state._emitFloat(this.colorSteps.length - 1)});\r\n`;
             state.compilationString += `${tempColor} = mix(${tempColor}, ${this._writeColorConstant(index)}, ${tempPosition});\r\n`;
         }
         state.compilationString += this._declareOutput(output, state) + ` = ${tempColor};\r\n`;
@@ -113,7 +126,11 @@ export class GradientBlock extends NodeMaterialBlock {
     public _deserialize(serializationObject: any, scene: Scene, rootUrl: string) {
         super._deserialize(serializationObject, scene, rootUrl);
 
-        this.colorSteps = serializationObject.colorSteps;
+        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)))
+        }
     }
 
     protected _dumpPropertiesCode() {