Browse Source

Finish step MVP

David Catuhe 5 years ago
parent
commit
45be6bc75b

+ 14 - 1
dist/preview release/babylon.d.ts

@@ -54741,6 +54741,15 @@ declare module BABYLON {
         TargetIncompatible = 2
         TargetIncompatible = 2
     }
     }
     /**
     /**
+     * Defines the direction of a connection point
+     */
+    export enum NodeMaterialConnectionPointDirection {
+        /** Input */
+        Input = 0,
+        /** Output */
+        Output = 1
+    }
+    /**
      * Defines a connection point for a block
      * Defines a connection point for a block
      */
      */
     export class NodeMaterialConnectionPoint {
     export class NodeMaterialConnectionPoint {
@@ -54750,6 +54759,7 @@ declare module BABYLON {
         _connectedPoint: Nullable<NodeMaterialConnectionPoint>;
         _connectedPoint: Nullable<NodeMaterialConnectionPoint>;
         private _endpoints;
         private _endpoints;
         private _associatedVariableName;
         private _associatedVariableName;
+        private _direction;
         /** @hidden */
         /** @hidden */
         _typeConnectionSource: Nullable<NodeMaterialConnectionPoint>;
         _typeConnectionSource: Nullable<NodeMaterialConnectionPoint>;
         /** @hidden */
         /** @hidden */
@@ -54757,6 +54767,8 @@ declare module BABYLON {
         private _type;
         private _type;
         /** @hidden */
         /** @hidden */
         _enforceAssociatedVariableName: boolean;
         _enforceAssociatedVariableName: boolean;
+        /** Gets the direction of the point */
+        readonly direction: NodeMaterialConnectionPointDirection;
         /**
         /**
          * Gets or sets the additional types supported by this connection point
          * Gets or sets the additional types supported by this connection point
          */
          */
@@ -54826,8 +54838,9 @@ declare module BABYLON {
          * Creates a new connection point
          * Creates a new connection point
          * @param name defines the connection point name
          * @param name defines the connection point name
          * @param ownerBlock defines the block hosting this connection point
          * @param ownerBlock defines the block hosting this connection point
+         * @param direction defines the direction of the connection point
          */
          */
-        constructor(name: string, ownerBlock: NodeMaterialBlock);
+        constructor(name: string, ownerBlock: NodeMaterialBlock, direction: NodeMaterialConnectionPointDirection);
         /**
         /**
          * Gets the current class name e.g. "NodeMaterialConnectionPoint"
          * Gets the current class name e.g. "NodeMaterialConnectionPoint"
          * @returns the class name
          * @returns the class name

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 100 - 71
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.max.js.map


+ 28 - 2
dist/preview release/babylon.module.d.ts

@@ -57340,6 +57340,15 @@ declare module "babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint" {
         TargetIncompatible = 2
         TargetIncompatible = 2
     }
     }
     /**
     /**
+     * Defines the direction of a connection point
+     */
+    export enum NodeMaterialConnectionPointDirection {
+        /** Input */
+        Input = 0,
+        /** Output */
+        Output = 1
+    }
+    /**
      * Defines a connection point for a block
      * Defines a connection point for a block
      */
      */
     export class NodeMaterialConnectionPoint {
     export class NodeMaterialConnectionPoint {
@@ -57349,6 +57358,7 @@ declare module "babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint" {
         _connectedPoint: Nullable<NodeMaterialConnectionPoint>;
         _connectedPoint: Nullable<NodeMaterialConnectionPoint>;
         private _endpoints;
         private _endpoints;
         private _associatedVariableName;
         private _associatedVariableName;
+        private _direction;
         /** @hidden */
         /** @hidden */
         _typeConnectionSource: Nullable<NodeMaterialConnectionPoint>;
         _typeConnectionSource: Nullable<NodeMaterialConnectionPoint>;
         /** @hidden */
         /** @hidden */
@@ -57356,6 +57366,8 @@ declare module "babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint" {
         private _type;
         private _type;
         /** @hidden */
         /** @hidden */
         _enforceAssociatedVariableName: boolean;
         _enforceAssociatedVariableName: boolean;
+        /** Gets the direction of the point */
+        readonly direction: NodeMaterialConnectionPointDirection;
         /**
         /**
          * Gets or sets the additional types supported by this connection point
          * Gets or sets the additional types supported by this connection point
          */
          */
@@ -57425,8 +57437,9 @@ declare module "babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint" {
          * Creates a new connection point
          * Creates a new connection point
          * @param name defines the connection point name
          * @param name defines the connection point name
          * @param ownerBlock defines the block hosting this connection point
          * @param ownerBlock defines the block hosting this connection point
+         * @param direction defines the direction of the connection point
          */
          */
-        constructor(name: string, ownerBlock: NodeMaterialBlock);
+        constructor(name: string, ownerBlock: NodeMaterialBlock, direction: NodeMaterialConnectionPointDirection);
         /**
         /**
          * Gets the current class name e.g. "NodeMaterialConnectionPoint"
          * Gets the current class name e.g. "NodeMaterialConnectionPoint"
          * @returns the class name
          * @returns the class name
@@ -123459,6 +123472,15 @@ declare module BABYLON {
         TargetIncompatible = 2
         TargetIncompatible = 2
     }
     }
     /**
     /**
+     * Defines the direction of a connection point
+     */
+    export enum NodeMaterialConnectionPointDirection {
+        /** Input */
+        Input = 0,
+        /** Output */
+        Output = 1
+    }
+    /**
      * Defines a connection point for a block
      * Defines a connection point for a block
      */
      */
     export class NodeMaterialConnectionPoint {
     export class NodeMaterialConnectionPoint {
@@ -123468,6 +123490,7 @@ declare module BABYLON {
         _connectedPoint: Nullable<NodeMaterialConnectionPoint>;
         _connectedPoint: Nullable<NodeMaterialConnectionPoint>;
         private _endpoints;
         private _endpoints;
         private _associatedVariableName;
         private _associatedVariableName;
+        private _direction;
         /** @hidden */
         /** @hidden */
         _typeConnectionSource: Nullable<NodeMaterialConnectionPoint>;
         _typeConnectionSource: Nullable<NodeMaterialConnectionPoint>;
         /** @hidden */
         /** @hidden */
@@ -123475,6 +123498,8 @@ declare module BABYLON {
         private _type;
         private _type;
         /** @hidden */
         /** @hidden */
         _enforceAssociatedVariableName: boolean;
         _enforceAssociatedVariableName: boolean;
+        /** Gets the direction of the point */
+        readonly direction: NodeMaterialConnectionPointDirection;
         /**
         /**
          * Gets or sets the additional types supported by this connection point
          * Gets or sets the additional types supported by this connection point
          */
          */
@@ -123544,8 +123569,9 @@ declare module BABYLON {
          * Creates a new connection point
          * Creates a new connection point
          * @param name defines the connection point name
          * @param name defines the connection point name
          * @param ownerBlock defines the block hosting this connection point
          * @param ownerBlock defines the block hosting this connection point
+         * @param direction defines the direction of the connection point
          */
          */
-        constructor(name: string, ownerBlock: NodeMaterialBlock);
+        constructor(name: string, ownerBlock: NodeMaterialBlock, direction: NodeMaterialConnectionPointDirection);
         /**
         /**
          * Gets the current class name e.g. "NodeMaterialConnectionPoint"
          * Gets the current class name e.g. "NodeMaterialConnectionPoint"
          * @returns the class name
          * @returns the class name

+ 1 - 1
nodeEditor/src/blockTools.ts

@@ -418,7 +418,7 @@ export class BlockTools {
     }
     }
 
 
     public static GetColorFromConnectionNodeType(type: NodeMaterialBlockConnectionPointTypes) {
     public static GetColorFromConnectionNodeType(type: NodeMaterialBlockConnectionPointTypes) {
-        let color = "Red";
+        let color = "#880000";
         switch (type) {
         switch (type) {
             case NodeMaterialBlockConnectionPointTypes.Float:
             case NodeMaterialBlockConnectionPointTypes.Float:
 				color = "#cb9e27";
 				color = "#cb9e27";

+ 6 - 2
nodeEditor/src/components/propertyTab/propertyTabComponent.tsx

@@ -26,8 +26,12 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
     }
     }
 
 
     componentDidMount() {
     componentDidMount() {
-        this.props.globalState.onSelectionChangedObservable.add(graphNode => {
-            this.setState({ currentNode: graphNode });
+        this.props.globalState.onSelectionChangedObservable.add(selection => {
+            if (selection instanceof GraphNode) {
+                this.setState({ currentNode: selection });
+            } else {
+                this.setState({ currentNode: null });
+            }
         });
         });
     }
     }
 
 

+ 16 - 3
nodeEditor/src/diagram/graphCanvas.scss

@@ -17,11 +17,23 @@
         transform-origin: left top;
         transform-origin: left top;
 
 
         #graph-svg-container {
         #graph-svg-container {
-            z-index: -10;
             position:absolute;
             position:absolute;
             width: 100%;
             width: 100%;
             height: 100%;  
             height: 100%;  
-            overflow: visible;  
+            overflow: visible; 
+            
+            .link {
+                stroke-width: 4px;                       
+            }
+
+            .selection-link {
+                stroke-width: 16px;
+                &:hover, &.selected {
+                    stroke: white !important;
+                    opacity: 0.4;
+                }
+                stroke: transparent;                        
+            }
         }
         }
 
 
         #graph-canvas-container {
         #graph-canvas-container {
@@ -32,6 +44,7 @@
             top: 0;
             top: 0;
 
 
             .visual {
             .visual {
+                z-index: 1;
                 width: 200px;
                 width: 200px;
                 position: absolute;
                 position: absolute;
                 left: 0;
                 left: 0;
@@ -91,7 +104,7 @@
                             width: 100%;
                             width: 100%;
                         }     
                         }     
                         
                         
-                        &:hover {
+                        &:hover, &.selected {
                             filter: brightness(2);
                             filter: brightness(2);
                         }
                         }
                     }
                     }

+ 199 - 7
nodeEditor/src/diagram/graphCanvas.tsx

@@ -4,8 +4,12 @@ import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
 import { GraphNode } from './graphNode';
 import { GraphNode } from './graphNode';
 import * as dagre from 'dagre';
 import * as dagre from 'dagre';
 import { Nullable } from 'babylonjs/types';
 import { Nullable } from 'babylonjs/types';
-import { NodeMaterialConnectionPoint } from 'babylonjs';
 import { NodeLink } from './nodeLink';
 import { NodeLink } from './nodeLink';
+import { NodePort } from './nodePort';
+import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection, NodeMaterialConnectionPointCompatibilityStates } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+import { Vector2 } from 'babylonjs/Maths/math.vector';
+import { FragmentOutputBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/fragmentOutputBlock';
+import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 
 
 require("./graphCanvas.scss");
 require("./graphCanvas.scss");
 
 
@@ -21,10 +25,23 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
     private _links: NodeLink[] = [];
     private _links: NodeLink[] = [];
     private _mouseStartPointX: Nullable<number> = null;
     private _mouseStartPointX: Nullable<number> = null;
     private _mouseStartPointY: Nullable<number> = null
     private _mouseStartPointY: Nullable<number> = null
+    private _dropPointX = 0;
+    private _dropPointY = 0;
     private _x = 0;
     private _x = 0;
     private _y = 0;
     private _y = 0;
     private _zoom = 1;
     private _zoom = 1;
     private _selectedNodes: GraphNode[] = [];
     private _selectedNodes: GraphNode[] = [];
+    private _selectedLink: Nullable<NodeLink> = null;
+    private _candidateLink: Nullable<NodeLink> = null;
+    private _candidatePort: Nullable<NodePort> = null;
+
+    private _altKeyIsPressed = false;
+    private _ctrlKeyIsPressed = false;
+    private _oldY = -1;
+
+    public get globalState(){
+        return this.props.globalState;
+    }
 
 
     public get nodes() {
     public get nodes() {
         return this._nodes;
         return this._nodes;
@@ -65,20 +82,58 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
         return this._selectedNodes;
         return this._selectedNodes;
     }
     }
 
 
+    public get selectedLink() {
+        return this._selectedLink;
+    }
+
     public get canvasContainer() {
     public get canvasContainer() {
         return this._graphCanvas;
         return this._graphCanvas;
     }
     }
 
 
+     public get svgCanvas() {
+        return this._svgCanvas;
+    }
+
     constructor(props: IGraphCanvasComponentProps) {
     constructor(props: IGraphCanvasComponentProps) {
         super(props);
         super(props);
 
 
-        props.globalState.onSelectionChangedObservable.add(node => {
-            if (!node) {
+        props.globalState.onSelectionChangedObservable.add(selection => {
+            
+            if (!selection) {
                 this._selectedNodes = [];
                 this._selectedNodes = [];
+                this._selectedLink = null;
             } else {
             } else {
-                this._selectedNodes = [node];
+                if (selection instanceof NodeLink) {
+                    this._selectedLink = selection;
+                } else {
+                    if (this._ctrlKeyIsPressed) {
+                        this._selectedNodes.push(selection);
+                    } else {                    
+                        this._selectedNodes = [selection];
+                    }
+                }
             }
             }
         });
         });
+
+        props.globalState.onCandidatePortSelected.add(port => {
+            this._candidatePort = port;
+        });
+
+        this.props.globalState.hostDocument!.addEventListener("keyup", () => this.onKeyUp(), false);
+        this.props.globalState.hostDocument!.addEventListener("keydown", evt => {
+            this._altKeyIsPressed = evt.altKey;            
+            this._ctrlKeyIsPressed = evt.ctrlKey;
+        }, false);
+        this.props.globalState.hostDocument!.defaultView!.addEventListener("blur", () => {
+            this._altKeyIsPressed = false;
+            this._ctrlKeyIsPressed = false;
+        }, false);        
+    }
+
+    onKeyUp() {        
+        this._altKeyIsPressed = false;
+        this._ctrlKeyIsPressed = false;
+        this._oldY = -1;
     }
     }
 
 
     findNodeFromBlock(block: NodeMaterialBlock) {
     findNodeFromBlock(block: NodeMaterialBlock) {
@@ -202,6 +257,34 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
     }    
     }    
 
 
     onMove(evt: React.PointerEvent) {        
     onMove(evt: React.PointerEvent) {        
+        // Candidate link
+        if (this._candidateLink) {        
+            const rootRect = this.canvasContainer.getBoundingClientRect();       
+            this._candidatePort = null; 
+            this.props.globalState.onCandidateLinkMoved.notifyObservers(new Vector2(evt.pageX, evt.pageY));
+            this._dropPointX = (evt.pageX - rootRect.left) / this.zoom;
+            this._dropPointY = (evt.pageY - rootRect.top) / this.zoom;
+
+            this._candidateLink.update(this._dropPointX, this._dropPointY, true);
+            
+            return;
+        }          
+
+        // Zoom with mouse + alt
+        if (this._altKeyIsPressed && evt.buttons === 1) {
+            if (this._oldY < 0) {
+                this._oldY = evt.pageY;
+            }
+
+            let zoomDelta = (evt.pageY - this._oldY) / 10;
+            if (Math.abs(zoomDelta) > 5) {
+                this.zoom += zoomDelta / 100;
+                this._oldY = evt.pageY;      
+            }
+            return;
+        }   
+
+        // Move canvas
         this._rootContainer.style.cursor = "move";
         this._rootContainer.style.cursor = "move";
 
 
         if (this._mouseStartPointX === null || this._mouseStartPointY === null) {
         if (this._mouseStartPointX === null || this._mouseStartPointY === null) {
@@ -214,17 +297,36 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
         this._mouseStartPointY = evt.clientY;
         this._mouseStartPointY = evt.clientY;
     }
     }
 
 
-    onDown(evt: React.PointerEvent) {
+    onDown(evt: React.PointerEvent<HTMLElement>) {
+        this._rootContainer.setPointerCapture(evt.pointerId);
+
+        if (evt.nativeEvent.srcElement && (evt.nativeEvent.srcElement as HTMLElement).nodeName === "IMG") {
+            if (!this._candidateLink) {
+                let portElement = ((evt.nativeEvent.srcElement as HTMLElement).parentElement as any).port as NodePort;
+                this._candidateLink = new NodeLink(this, portElement, portElement.node);
+            }  
+            return;
+        }
+
         this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
         this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
         this._mouseStartPointX = evt.clientX;
         this._mouseStartPointX = evt.clientX;
         this._mouseStartPointY = evt.clientY;
         this._mouseStartPointY = evt.clientY;
-        this._rootContainer.setPointerCapture(evt.pointerId);
+        
     }
     }
 
 
     onUp(evt: React.PointerEvent) {
     onUp(evt: React.PointerEvent) {
         this._mouseStartPointX = null;
         this._mouseStartPointX = null;
         this._mouseStartPointY = null;
         this._mouseStartPointY = null;
-        this._rootContainer.releasePointerCapture(evt.pointerId);
+        this._rootContainer.releasePointerCapture(evt.pointerId);   
+        this._oldY = -1; 
+
+        if (this._candidateLink) {        
+            this.processCandidatePort();          
+            this.props.globalState.onCandidateLinkMoved.notifyObservers(null);
+            this._candidateLink.dispose();
+            this._candidateLink = null;
+            this._candidatePort = null;
+        }
     }
     }
 
 
     onWheel(evt: React.WheelEvent) {
     onWheel(evt: React.WheelEvent) {
@@ -250,6 +352,96 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
         this.x = 0;
         this.x = 0;
         this.y = 0;
         this.y = 0;
     }
     }
+
+    processCandidatePort() {
+        let pointB = this._candidateLink!.portA.connectionPoint;
+        let nodeB = this._candidateLink!.portA.node;
+        let pointA: NodeMaterialConnectionPoint;
+        let nodeA: GraphNode;
+
+        if (this._candidatePort) {
+            pointA = this._candidatePort.connectionPoint;
+            nodeA = this._candidatePort.node;
+        } else {
+            if (pointB.direction === NodeMaterialConnectionPointDirection.Output) {
+                return;
+            }
+
+            // No destination so let's spin a new input block
+            let inputBlock = new InputBlock("", undefined, this._candidateLink!.portA.connectionPoint.type);
+            pointA = inputBlock.output;
+            nodeA = this.appendBlock(inputBlock);
+            
+            nodeA.x = this._dropPointX - 200;
+            nodeA.y = this._dropPointY - 50;    
+        }
+
+        if (pointA.direction === NodeMaterialConnectionPointDirection.Input) {
+            let temp = pointB;
+            pointB = pointA;
+            pointA = temp;
+
+            let tempNode = nodeA;
+            nodeA = nodeB;
+            nodeB = tempNode;
+        }
+
+        if (pointB.connectedPoint === pointA) {
+            return;
+        }
+
+        if (pointB === pointA) {
+            return;
+        }
+
+        if (pointB.direction === pointA.direction) {
+            return;
+        }
+
+        // Check compatibility
+        let isFragmentOutput = pointB.ownerBlock.getClassName() === "FragmentOutputBlock";
+        let compatibilityState = pointA.checkCompatibilityState(pointB);
+        if (compatibilityState === NodeMaterialConnectionPointCompatibilityStates.Compatible) {
+            if (isFragmentOutput) {
+                let fragmentBlock = pointB.ownerBlock as FragmentOutputBlock;
+
+                if (pointB.name === "rgb" && fragmentBlock.rgba.isConnected) {
+                    nodeB.getLinksForConnectionPoint(fragmentBlock.rgba)[0].dispose();
+                } else if (pointB.name === "rgba" && fragmentBlock.rgb.isConnected) {
+                    nodeB.getLinksForConnectionPoint(fragmentBlock.rgb)[0].dispose();
+                }                     
+            }
+        } else {
+            let message = "";
+
+            switch (compatibilityState) {
+                case NodeMaterialConnectionPointCompatibilityStates.TypeIncompatible:
+                    message = "Cannot connect two different connection types";
+                    break;
+                case NodeMaterialConnectionPointCompatibilityStates.TargetIncompatible:
+                    message = "Source block can only work in fragment shader whereas destination block is currently aimed for the vertex shader";
+                    break;
+            }
+
+            this.props.globalState.onErrorMessageDialogRequiredObservable.notifyObservers(message);             
+            return;
+        }
+
+        if (pointB.isConnected) {
+            let links = nodeB.getLinksForConnectionPoint(pointB);
+
+            links.forEach(link => {
+                link.dispose();
+            });
+        }
+
+        pointA.connectTo(pointB);
+        this.connectPorts(pointA, pointB);
+
+        nodeB.refresh();
+
+        this.props.globalState.onRebuildRequiredObservable.notifyObservers();
+    }
  
  
     render() {
     render() {
         return (
         return (

File diff suppressed because it is too large
+ 37 - 79
nodeEditor/src/diagram/graphNode.ts


+ 47 - 13
nodeEditor/src/diagram/nodeLink.ts

@@ -1,13 +1,18 @@
 import { GraphCanvasComponent } from './graphCanvas';
 import { GraphCanvasComponent } from './graphCanvas';
 import { GraphNode } from './graphNode';
 import { GraphNode } from './graphNode';
+import { NodePort } from './nodePort';
+import { Nullable } from 'babylonjs/types';
+import { Observer } from 'babylonjs/Misc/observable';
 
 
 export class NodeLink {   
 export class NodeLink {   
     private _graphCanvas: GraphCanvasComponent;
     private _graphCanvas: GraphCanvasComponent;
-    private _portA: HTMLDivElement;
-    private _portB?: HTMLDivElement;
+    private _portA: NodePort;
+    private _portB?: NodePort;
     private _nodeA: GraphNode;
     private _nodeA: GraphNode;
     private _nodeB?: GraphNode;
     private _nodeB?: GraphNode;
     private _path: SVGPathElement;
     private _path: SVGPathElement;
+    private _selectionPath: SVGPathElement;
+    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphNode | NodeLink>>>;
 
 
     public get portA() {
     public get portA() {
         return this._portA;
         return this._portA;
@@ -18,7 +23,7 @@ export class NodeLink {
     }
     }
 
 
     public update(endX = 0, endY = 0, straight = false) {   
     public update(endX = 0, endY = 0, straight = false) {   
-        const rectA = this._portA.getBoundingClientRect();
+        const rectA = this._portA.element.getBoundingClientRect();
         const rootRect = this._graphCanvas.canvasContainer.getBoundingClientRect();
         const rootRect = this._graphCanvas.canvasContainer.getBoundingClientRect();
         const zoom = this._graphCanvas.zoom;
         const zoom = this._graphCanvas.zoom;
         const xOffset = rootRect.left;
         const xOffset = rootRect.left;
@@ -28,11 +33,9 @@ export class NodeLink {
         var startY = (rectA.top - yOffset + 0.5 * rectA.height) / zoom;  
         var startY = (rectA.top - yOffset + 0.5 * rectA.height) / zoom;  
 
 
         if (this._portB) {
         if (this._portB) {
-            const rectB = this._portB.getBoundingClientRect();
+            const rectB = this._portB.element.getBoundingClientRect();
             endX = (rectB.left - xOffset + 0.5 * rectB.width) / zoom;
             endX = (rectB.left - xOffset + 0.5 * rectB.width) / zoom;
             endY = (rectB.top - yOffset + 0.5 * rectB.height) / zoom;  
             endY = (rectB.top - yOffset + 0.5 * rectB.height) / zoom;  
-        } else {
-
         }
         }
     
     
         if (straight) {
         if (straight) {
@@ -41,40 +44,71 @@ export class NodeLink {
             this._path.setAttribute("stroke-linecap", "round");
             this._path.setAttribute("stroke-linecap", "round");
         } else {
         } else {
             this._path.setAttribute("d",  `M${startX},${startY} C${startX + 80},${startY} ${endX - 80},${endY} ${endX},${endY}`);        
             this._path.setAttribute("d",  `M${startX},${startY} C${startX + 80},${startY} ${endX - 80},${endY} ${endX},${endY}`);        
+            this._selectionPath.setAttribute("d",  `M${startX},${startY} C${startX + 80},${startY} ${endX - 80},${endY} ${endX},${endY}`);
         }
         }
-        this._path.setAttribute("stroke", this._portA.style.backgroundColor!);
+        this._path.setAttribute("stroke", this._portA.element.style.backgroundColor!);
     }
     }
 
 
-    public constructor(graphCanvas: GraphCanvasComponent, portA: HTMLDivElement, nodeA: GraphNode, portB?: HTMLDivElement, nodeB?: GraphNode) {
+    public constructor(graphCanvas: GraphCanvasComponent, portA: NodePort, nodeA: GraphNode, portB?: NodePort, nodeB?: GraphNode) {
         this._portA = portA;
         this._portA = portA;
         this._portB = portB;
         this._portB = portB;
         this._nodeA = nodeA;
         this._nodeA = nodeA;
         this._nodeB = nodeB;
         this._nodeB = nodeB;
         this._graphCanvas = graphCanvas;
         this._graphCanvas = graphCanvas;
 
 
-        var document = portA.ownerDocument!;
-        var svg = document.getElementById("graph-svg-container")!;
+        var document = portA.element.ownerDocument!;
+        var svg = graphCanvas.svgCanvas;
 
 
         // Create path
         // Create path
         this._path = document.createElementNS('http://www.w3.org/2000/svg',"path"); 
         this._path = document.createElementNS('http://www.w3.org/2000/svg',"path"); 
         this._path.setAttribute("fill", "none");
         this._path.setAttribute("fill", "none");
-        this._path.setAttribute("stroke-width", "4px");
+        this._path.classList.add("link");
 
 
         svg.appendChild(this._path);
         svg.appendChild(this._path);
 
 
-        // Update
-        this.update();
+        this._selectionPath = document.createElementNS('http://www.w3.org/2000/svg',"path"); 
+        this._selectionPath.setAttribute("fill", "none");
+        this._selectionPath.classList.add("selection-link");
+
+        svg.appendChild(this._selectionPath);
+
+        this._selectionPath.onmousedown = ()=> this.onClick();
+
+        if (this._portB) {
+            // Update
+            this.update();
+        }
+
+        this._onSelectionChangedObserver = this._graphCanvas.globalState.onSelectionChangedObservable.add(selection => {
+            if (selection === this) {
+                this._selectionPath.classList.add("selected");
+            } else {
+                this._selectionPath.classList.remove("selected");
+            }
+        });
+    }
+
+    onClick() {
+        this._graphCanvas.globalState.onSelectionChangedObservable.notifyObservers(this);
     }
     }
 
 
     public dispose() {
     public dispose() {
+        this._graphCanvas.globalState.onSelectionChangedObservable.remove(this._onSelectionChangedObserver);
+
         if (this._path.parentElement) {
         if (this._path.parentElement) {
             this._path.parentElement.removeChild(this._path);
             this._path.parentElement.removeChild(this._path);
         }
         }
 
 
+        if (this._selectionPath.parentElement) {
+            this._selectionPath.parentElement.removeChild(this._selectionPath);
+        }        
+
         if (this._nodeB) {
         if (this._nodeB) {
             this._nodeA.links.splice(this._nodeA.links.indexOf(this), 1);
             this._nodeA.links.splice(this._nodeA.links.indexOf(this), 1);
             this._nodeB.links.splice(this._nodeB.links.indexOf(this), 1);
             this._nodeB.links.splice(this._nodeB.links.indexOf(this), 1);
             this._graphCanvas.links.splice(this._graphCanvas.links.indexOf(this), 1);
             this._graphCanvas.links.splice(this._graphCanvas.links.indexOf(this), 1);
+
+            this._portA.connectionPoint.disconnectFrom(this._portB!.connectionPoint);
         }
         }
     }   
     }   
 }
 }

File diff suppressed because it is too large
+ 77 - 0
nodeEditor/src/diagram/nodePort.ts


+ 6 - 1
nodeEditor/src/globalState.ts

@@ -7,12 +7,15 @@ import { PreviewMeshType } from './components/preview/previewMeshType';
 import { DataStorage } from './dataStorage';
 import { DataStorage } from './dataStorage';
 import { Color4 } from 'babylonjs/Maths/math.color';
 import { Color4 } from 'babylonjs/Maths/math.color';
 import { GraphNode } from './diagram/graphNode';
 import { GraphNode } from './diagram/graphNode';
+import { Vector2 } from 'babylonjs/Maths/math.vector';
+import { NodePort } from './diagram/nodePort';
+import { NodeLink } from './diagram/nodeLink';
 
 
 export class GlobalState {
 export class GlobalState {
     nodeMaterial: NodeMaterial;
     nodeMaterial: NodeMaterial;
     hostElement: HTMLElement;
     hostElement: HTMLElement;
     hostDocument: HTMLDocument;
     hostDocument: HTMLDocument;
-    onSelectionChangedObservable = new Observable<Nullable<GraphNode>>();
+    onSelectionChangedObservable = new Observable<Nullable<GraphNode | NodeLink>>();
     onRebuildRequiredObservable = new Observable<void>();
     onRebuildRequiredObservable = new Observable<void>();
     onResetRequiredObservable = new Observable<void>();
     onResetRequiredObservable = new Observable<void>();
     onUpdateRequiredObservable = new Observable<void>();
     onUpdateRequiredObservable = new Observable<void>();
@@ -27,6 +30,8 @@ export class GlobalState {
     onBackFaceCullingChanged = new Observable<void>();
     onBackFaceCullingChanged = new Observable<void>();
     onDepthPrePassChanged = new Observable<void>();
     onDepthPrePassChanged = new Observable<void>();
     onAnimationCommandActivated = new Observable<void>();
     onAnimationCommandActivated = new Observable<void>();
+    onCandidateLinkMoved = new Observable<Nullable<Vector2>>();    
+    onCandidatePortSelected = new Observable<Nullable<NodePort>>();
     onGetNodeFromBlock: (block: NodeMaterialBlock) => GraphNode;
     onGetNodeFromBlock: (block: NodeMaterialBlock) => GraphNode;
     previewMeshType: PreviewMeshType;
     previewMeshType: PreviewMeshType;
     previewMeshFile: File;
     previewMeshFile: File;

+ 8 - 97
nodeEditor/src/graphEditor.tsx

@@ -1,9 +1,3 @@
-import {
-    DiagramEngine,
-    DiagramWidget,
-    LinkModel
-} from "storm-react-diagrams";
-
 import * as React from "react";
 import * as React from "react";
 import { GlobalState } from './globalState';
 import { GlobalState } from './globalState';
 
 
@@ -35,7 +29,6 @@ interface IGraphEditorProps {
 export class GraphEditor extends React.Component<IGraphEditorProps> {
 export class GraphEditor extends React.Component<IGraphEditorProps> {
     private readonly NodeWidth = 100;
     private readonly NodeWidth = 100;
     private _graphCanvas: GraphCanvasComponent;
     private _graphCanvas: GraphCanvasComponent;
-    private _engine: DiagramEngine;
 
 
     private _startX: number;
     private _startX: number;
     private _moveInProgress: boolean;
     private _moveInProgress: boolean;
@@ -51,12 +44,6 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
     private _mouseLocationY = 0;
     private _mouseLocationY = 0;
     private _onWidgetKeyUpPointer: any;
     private _onWidgetKeyUpPointer: any;
 
 
-    private _altKeyIsPressed = false;
-    private _oldY = -1;
-
-    /** @hidden */
-    public _toAdd: LinkModel[] | null = [];
-
     /**
     /**
      * Creates a node and recursivly creates its parent nodes from it's input
      * Creates a node and recursivly creates its parent nodes from it's input
      * @param nodeMaterialBlock 
      * @param nodeMaterialBlock 
@@ -107,54 +94,9 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
         return this.createNodeFromObject(newInputBlock)
         return this.createNodeFromObject(newInputBlock)
     }
     }
 
 
-    onWidgetKeyUp(evt: any) {        
-        this._altKeyIsPressed = false;
-        this._oldY = -1;
-
-        var widget = (this.refs["test"] as DiagramWidget);
-
-        if (!widget || this.props.globalState.blockKeyboardEvents) {
-            return;
-        }
-
-        widget.onKeyUp(evt)
-    }
-
     componentDidMount() {
     componentDidMount() {
         if (this.props.globalState.hostDocument) {
         if (this.props.globalState.hostDocument) {
             this._graphCanvas = (this.refs["graphCanvas"] as GraphCanvasComponent);
             this._graphCanvas = (this.refs["graphCanvas"] as GraphCanvasComponent);
-
-
-            var widget = (this.refs["test"] as DiagramWidget);
-            widget.setState({ document: this.props.globalState.hostDocument })
-            this._onWidgetKeyUpPointer = this.onWidgetKeyUp.bind(this)
-            this.props.globalState.hostDocument!.addEventListener("keyup", this._onWidgetKeyUpPointer, false);
-            this.props.globalState.hostDocument!.defaultView!.addEventListener("blur", () => this._altKeyIsPressed = false, false);
-
-            let previousMouseMove = widget.onMouseMove;
-            widget.onMouseMove = (evt: any) => {
-                if (this._altKeyIsPressed && evt.buttons === 1) {
-                    if (this._oldY < 0) {
-                        this._oldY = evt.pageY;
-                    }
-
-                    let zoomDelta = (evt.pageY - this._oldY) / 10;
-                    if (Math.abs(zoomDelta) > 5) {
-                        this._engine.diagramModel.setZoomLevel(this._engine.diagramModel.getZoomLevel() + zoomDelta);
-                        this._engine.repaintCanvas();
-                        this._oldY = evt.pageY;      
-                    }
-                    return;
-                }
-                previousMouseMove(evt);
-            }
-
-            let previousMouseUp = widget.onMouseUp;
-            widget.onMouseUp = (evt: any) => {
-                this._oldY = -1;
-                previousMouseUp(evt);
-            }
-
             this._previewManager = new PreviewManager(this.props.globalState.hostDocument.getElementById("preview-canvas") as HTMLCanvasElement, this.props.globalState);
             this._previewManager = new PreviewManager(this.props.globalState.hostDocument.getElementById("preview-canvas") as HTMLCanvasElement, this.props.globalState);
         }
         }
 
 
@@ -178,10 +120,6 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
     constructor(props: IGraphEditorProps) {
     constructor(props: IGraphEditorProps) {
         super(props);
         super(props);
 
 
-        // setup the diagram engine
-        this._engine = new DiagramEngine();
-        this._engine.installDefaultFactories()
-
         this.props.globalState.onRebuildRequiredObservable.add(() => {
         this.props.globalState.onRebuildRequiredObservable.add(() => {
             if (this.props.globalState.nodeMaterial) {
             if (this.props.globalState.nodeMaterial) {
                 this.buildMaterial();
                 this.buildMaterial();
@@ -195,8 +133,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
             }
             }
         });
         });
 
 
-        this.props.globalState.onUpdateRequiredObservable.add(() => {          
-            this._engine.repaintCanvas();  
+        this.props.globalState.onUpdateRequiredObservable.add(() => {
+            // Do nothing for now
         });
         });
 
 
         this.props.globalState.onZoomToFitRequiredObservable.add(() => {
         this.props.globalState.onZoomToFitRequiredObservable.add(() => {
@@ -212,13 +150,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
         }
         }
 
 
         this.props.globalState.hostDocument!.addEventListener("keydown", evt => {
         this.props.globalState.hostDocument!.addEventListener("keydown", evt => {
-            this._altKeyIsPressed = evt.altKey;
-
             if (evt.keyCode === 46) { // Delete                
             if (evt.keyCode === 46) { // Delete                
                 let selectedItems = this._graphCanvas.selectedNodes;
                 let selectedItems = this._graphCanvas.selectedNodes;
-                if (!selectedItems.length) {
-                    return;
-                }
 
 
                 for (var selectedItem of selectedItems) {
                 for (var selectedItem of selectedItems) {
                     selectedItem.dispose();
                     selectedItem.dispose();
@@ -229,8 +162,11 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
 
 
                     if (blockIndex > -1) {
                     if (blockIndex > -1) {
                         this._blocks.splice(blockIndex, 1);
                         this._blocks.splice(blockIndex, 1);
-                    }
-                                  
+                    }                                  
+                }
+
+                if (this._graphCanvas.selectedLink) {
+                    this._graphCanvas.selectedLink.dispose();
                 }
                 }
 
 
                 this.props.globalState.onSelectionChangedObservable.notifyObservers(null);  
                 this.props.globalState.onSelectionChangedObservable.notifyObservers(null);  
@@ -294,8 +230,6 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                     newNode.x = x;
                     newNode.x = x;
                     newNode.y = y;
                     newNode.y = y;
                 }
                 }
-
-                this._engine.repaintCanvas();
             }
             }
 
 
         }, false);
         }, false);
@@ -321,23 +255,6 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
         SerializationTools.UpdateLocations(this.props.globalState.nodeMaterial, this.props.globalState);
         SerializationTools.UpdateLocations(this.props.globalState.nodeMaterial, this.props.globalState);
     }
     }
 
 
-    // applyFragmentOutputConstraints(rootInput: DefaultPortModel) {
-    //     var model = rootInput.parent as GenericNodeModel;
-    //     for (var inputKey in model.getPorts()) {                                       
-    //         let input = model.getPorts()[inputKey];
-
-    //         if (rootInput.name === "rgba" && (inputKey === "a" || inputKey === "rgb")
-    //             ||
-    //             (rootInput.name === "a" || rootInput.name === "rgb") && inputKey === "rgba") {
-    //                 for (var key in input.links) {
-    //                     let other = input.links[key];
-    //                     other.remove();
-    //                 }
-    //             continue;
-    //         }
-    //     }
-    // }
-
     build() {        
     build() {        
         let locations: Nullable<INodeLocationInfo[]> = this.props.globalState.nodeMaterial.editorData;
         let locations: Nullable<INodeLocationInfo[]> = this.props.globalState.nodeMaterial.editorData;
         // setup the diagram model
         // setup the diagram model
@@ -451,7 +368,6 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                 }
                 }
             } 
             } 
 
 
-            this._toAdd = [];
             block.autoConfigure(this.props.globalState.nodeMaterial);       
             block.autoConfigure(this.props.globalState.nodeMaterial);       
             newNode = this.createNodeFromObject(block);
             newNode = this.createNodeFromObject(block);
         };
         };
@@ -491,7 +407,7 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                     {
                     {
                         gridTemplateColumns: this.buildColumnLayout()
                         gridTemplateColumns: this.buildColumnLayout()
                     }}
                     }}
-                    onMouseMove={evt => {
+                    onMouseMove={evt => {                
                         this._mouseLocationX = evt.pageX;
                         this._mouseLocationX = evt.pageX;
                         this._mouseLocationY = evt.pageY;
                         this._mouseLocationY = evt.pageY;
                     }}
                     }}
@@ -520,11 +436,6 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                             event.preventDefault();
                             event.preventDefault();
                         }}
                         }}
                     >
                     >
-                        <DiagramWidget className="diagram" deleteKeys={[46]} ref={"test"} 
-                        allowLooseLinks={false}
-                        inverseZoom={true} 
-                        diagramEngine={this._engine} 
-                        maxNumberPointsPerLink={0} />
                         <GraphCanvasComponent ref={"graphCanvas"} globalState={this.props.globalState}/>
                         <GraphCanvasComponent ref={"graphCanvas"} globalState={this.props.globalState}/>
                     </div>
                     </div>
 
 

+ 0 - 1
package.json

@@ -91,7 +91,6 @@
         "shelljs": "^0.8.3",
         "shelljs": "^0.8.3",
         "sinon": "^6.1.4",
         "sinon": "^6.1.4",
         "split.js": "^1.5.9",
         "split.js": "^1.5.9",
-        "storm-react-diagrams": "^5.2.1",
         "style-loader": "^0.21.0",
         "style-loader": "^0.21.0",
         "through2": "~2.0.3",
         "through2": "~2.0.3",
         "ts-loader": "^5.2.1",
         "ts-loader": "^5.2.1",

+ 5 - 1
src/Materials/Node/Blocks/Fragment/fragmentOutputBlock.ts

@@ -61,7 +61,11 @@ export class FragmentOutputBlock extends NodeMaterialBlock {
         state.sharedData.hints.needAlphaBlending = rgba.isConnected || a.isConnected;
         state.sharedData.hints.needAlphaBlending = rgba.isConnected || a.isConnected;
 
 
         if (rgba.connectedPoint) {
         if (rgba.connectedPoint) {
-            state.compilationString += `gl_FragColor = ${rgba.associatedVariableName};\r\n`;
+            if (a.isConnected) {
+                state.compilationString += `gl_FragColor = vec4(${rgba.associatedVariableName}.rgb, ${a.associatedVariableName});\r\n`;
+            } else {
+                state.compilationString += `gl_FragColor = ${rgba.associatedVariableName};\r\n`;
+            }
         } else if (rgb.connectedPoint) {
         } else if (rgb.connectedPoint) {
             if (a.connectedPoint) {
             if (a.connectedPoint) {
                 state.compilationString += `gl_FragColor = vec4(${rgb.associatedVariableName}, ${a.associatedVariableName});\r\n`;
                 state.compilationString += `gl_FragColor = vec4(${rgb.associatedVariableName}, ${a.associatedVariableName});\r\n`;

+ 3 - 3
src/Materials/Node/nodeMaterialBlock.ts

@@ -1,7 +1,7 @@
 import { NodeMaterialBlockConnectionPointTypes } from './Enums/nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from './Enums/nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBuildState } from './nodeMaterialBuildState';
 import { NodeMaterialBuildState } from './nodeMaterialBuildState';
 import { Nullable } from '../../types';
 import { Nullable } from '../../types';
-import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
+import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection } from './nodeMaterialBlockConnectionPoint';
 import { NodeMaterialBlockTargets } from './Enums/nodeMaterialBlockTargets';
 import { NodeMaterialBlockTargets } from './Enums/nodeMaterialBlockTargets';
 import { Effect } from '../effect';
 import { Effect } from '../effect';
 import { AbstractMesh } from '../../Meshes/abstractMesh';
 import { AbstractMesh } from '../../Meshes/abstractMesh';
@@ -208,7 +208,7 @@ export class NodeMaterialBlock {
      * @returns the current block
      * @returns the current block
      */
      */
     public registerInput(name: string, type: NodeMaterialBlockConnectionPointTypes, isOptional: boolean = false, target?: NodeMaterialBlockTargets) {
     public registerInput(name: string, type: NodeMaterialBlockConnectionPointTypes, isOptional: boolean = false, target?: NodeMaterialBlockTargets) {
-        let point = new NodeMaterialConnectionPoint(name, this);
+        let point = new NodeMaterialConnectionPoint(name, this, NodeMaterialConnectionPointDirection.Input);
         point.type = type;
         point.type = type;
         point.isOptional = isOptional;
         point.isOptional = isOptional;
         if (target) {
         if (target) {
@@ -228,7 +228,7 @@ export class NodeMaterialBlock {
      * @returns the current block
      * @returns the current block
      */
      */
     public registerOutput(name: string, type: NodeMaterialBlockConnectionPointTypes, target?: NodeMaterialBlockTargets) {
     public registerOutput(name: string, type: NodeMaterialBlockConnectionPointTypes, target?: NodeMaterialBlockTargets) {
-        let point = new NodeMaterialConnectionPoint(name, this);
+        let point = new NodeMaterialConnectionPoint(name, this, NodeMaterialConnectionPointDirection.Output);
         point.type = type;
         point.type = type;
         if (target) {
         if (target) {
             point.target = target;
             point.target = target;

+ 19 - 1
src/Materials/Node/nodeMaterialBlockConnectionPoint.ts

@@ -19,6 +19,16 @@ export enum NodeMaterialConnectionPointCompatibilityStates {
 }
 }
 
 
 /**
 /**
+ * Defines the direction of a connection point
+ */
+export enum NodeMaterialConnectionPointDirection {
+    /** Input */
+    Input,
+    /** Output */
+    Output
+}
+
+/**
  * Defines a connection point for a block
  * Defines a connection point for a block
  */
  */
 export class NodeMaterialConnectionPoint {
 export class NodeMaterialConnectionPoint {
@@ -29,6 +39,7 @@ export class NodeMaterialConnectionPoint {
 
 
     private _endpoints = new Array<NodeMaterialConnectionPoint>();
     private _endpoints = new Array<NodeMaterialConnectionPoint>();
     private _associatedVariableName: string;
     private _associatedVariableName: string;
+    private _direction: NodeMaterialConnectionPointDirection;
 
 
     /** @hidden */
     /** @hidden */
     public _typeConnectionSource: Nullable<NodeMaterialConnectionPoint> = null;
     public _typeConnectionSource: Nullable<NodeMaterialConnectionPoint> = null;
@@ -41,6 +52,11 @@ export class NodeMaterialConnectionPoint {
     /** @hidden */
     /** @hidden */
     public _enforceAssociatedVariableName = false;
     public _enforceAssociatedVariableName = false;
 
 
+    /** Gets the direction of the point */
+    public get direction() {
+        return this._direction;
+    }
+
     /**
     /**
      * Gets or sets the additional types supported by this connection point
      * Gets or sets the additional types supported by this connection point
      */
      */
@@ -266,10 +282,12 @@ export class NodeMaterialConnectionPoint {
      * Creates a new connection point
      * Creates a new connection point
      * @param name defines the connection point name
      * @param name defines the connection point name
      * @param ownerBlock defines the block hosting this connection point
      * @param ownerBlock defines the block hosting this connection point
+     * @param direction defines the direction of the connection point
      */
      */
-    public constructor(name: string, ownerBlock: NodeMaterialBlock) {
+    public constructor(name: string, ownerBlock: NodeMaterialBlock, direction: NodeMaterialConnectionPointDirection) {
         this._ownerBlock = ownerBlock;
         this._ownerBlock = ownerBlock;
         this.name = name;
         this.name = name;
+        this._direction = direction;
     }
     }
 
 
     /**
     /**