Selaa lähdekoodia

Merge pull request #8060 from BabylonJS/master

Nightly
mergify[bot] 5 vuotta sitten
vanhempi
commit
cca352cdc1
27 muutettua tiedostoa jossa 542 lisäystä ja 228 poistoa
  1. 1 1
      dist/preview release/babylon.d.ts
  2. 1 1
      dist/preview release/babylon.js
  3. 39 13
      dist/preview release/babylon.max.js
  4. 1 1
      dist/preview release/babylon.max.js.map
  5. 2 2
      dist/preview release/babylon.module.d.ts
  6. 1 1
      dist/preview release/documentation.d.ts
  7. 16 0
      dist/preview release/nodeEditor/babylon.nodeEditor.d.ts
  8. 6 6
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  9. 191 96
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  10. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  11. 32 0
      dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts
  12. 2 2
      dist/preview release/viewer/babylon.module.d.ts
  13. 3 3
      dist/preview release/viewer/babylon.viewer.js
  14. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  15. 2 0
      dist/preview release/what's new.md
  16. 16 0
      nodeEditor/src/components/propertyTab/propertyTab.scss
  17. 5 1
      nodeEditor/src/diagram/frameNodePort.ts
  18. 125 78
      nodeEditor/src/diagram/graphFrame.ts
  19. 15 1
      nodeEditor/src/diagram/graphNode.ts
  20. 2 0
      nodeEditor/src/diagram/nodeLink.ts
  21. 28 3
      nodeEditor/src/diagram/nodePort.ts
  22. 7 0
      nodeEditor/src/diagram/properties/nodePortPropertyComponent.tsx
  23. 1 0
      nodeEditor/src/globalState.ts
  24. 4 3
      nodeEditor/src/sharedComponents/checkBoxLineComponent.tsx
  25. 2 2
      src/Materials/Node/nodeMaterialBlockConnectionPoint.ts
  26. 1 1
      src/Offline/database.ts
  27. 37 11
      src/Particles/solidParticleSystem.ts

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

@@ -58896,7 +58896,7 @@ declare module BABYLON {
         get target(): NodeMaterialBlockTargets;
         set target(value: NodeMaterialBlockTargets);
         /**
-         * Gets a boolean indicating that the current point is connected
+         * Gets a boolean indicating that the current point is connected to another NodeMaterialBlock
          */
         get isConnected(): boolean;
         /**

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/babylon.js


+ 39 - 13
dist/preview release/babylon.max.js

@@ -76971,10 +76971,10 @@ var NodeMaterialConnectionPoint = /** @class */ (function () {
     });
     Object.defineProperty(NodeMaterialConnectionPoint.prototype, "isConnected", {
         /**
-         * Gets a boolean indicating that the current point is connected
+         * Gets a boolean indicating that the current point is connected to another NodeMaterialBlock
          */
         get: function () {
-            return this.connectedPoint !== null;
+            return this.connectedPoint !== null || this.hasEndpoints;
         },
         enumerable: true,
         configurable: true
@@ -142532,7 +142532,7 @@ var Database = /** @class */ (function () {
         return absLocation;
     };
     Database._ReturnFullUrlLocation = function (url) {
-        if (url.indexOf("http:/") === -1 && url.indexOf("https:/") === -1) {
+        if (url.indexOf("http:/") === -1 && url.indexOf("https:/") === -1 && typeof window !== "undefined") {
             return (Database._ParseURL(window.location.href) + url);
         }
         else {
@@ -151175,6 +151175,21 @@ var SolidParticleSystem = /** @class */ (function () {
         }
         vertexData.applyToMesh(this.mesh, this._updatable);
         this.mesh.isPickable = this._pickable;
+        if (this._pickable) {
+            var faceId = 0;
+            for (var p = 0; p < this.nbParticles; p++) {
+                var part = this.particles[p];
+                var lind = part._model._indicesLength;
+                for (var i = 0; i < lind; i++) {
+                    var f = i % 3;
+                    if (f == 0) {
+                        var pickedData = { idx: part.idx, faceId: faceId };
+                        this.pickedParticles[faceId] = pickedData;
+                        faceId++;
+                    }
+                }
+            }
+        }
         if (this._multimaterialEnabled) {
             this.setMultiMaterial(this._materials);
         }
@@ -151473,12 +151488,6 @@ var SolidParticleSystem = /** @class */ (function () {
                 this._needs32Bits = true;
             }
         }
-        if (this._pickable) {
-            var nbfaces = meshInd.length / 3;
-            for (i = 0; i < nbfaces; i++) {
-                this.pickedParticles.push({ idx: idx, faceId: i });
-            }
-        }
         if (this._depthSort || this._multimaterialEnabled) {
             var matIndex = (copy.materialIndex !== null) ? copy.materialIndex : 0;
             this.depthSortedParticles.push(new _solidParticle__WEBPACK_IMPORTED_MODULE_7__["DepthSortedParticle"](idx, ind, meshInd.length, matIndex));
@@ -152108,12 +152117,23 @@ var SolidParticleSystem = /** @class */ (function () {
                 depthSortedParticles.sort(this._depthSortFunction);
                 var dspl = depthSortedParticles.length;
                 var sid = 0;
+                var faceId = 0;
                 for (var sorted = 0; sorted < dspl; sorted++) {
-                    var lind = depthSortedParticles[sorted].indicesLength;
-                    var sind = depthSortedParticles[sorted].ind;
+                    var sortedParticle = depthSortedParticles[sorted];
+                    var lind = sortedParticle.indicesLength;
+                    var sind = sortedParticle.ind;
                     for (var i = 0; i < lind; i++) {
                         indices32[sid] = indices[sind + i];
                         sid++;
+                        if (this._pickable) {
+                            var f = i % 3;
+                            if (f == 0) {
+                                var pickedData = this.pickedParticles[faceId];
+                                pickedData.idx = sortedParticle.idx;
+                                pickedData.faceId = faceId;
+                                faceId++;
+                            }
+                        }
                     }
                 }
                 mesh.updateIndices(indices32);
@@ -152312,8 +152332,14 @@ var SolidParticleSystem = /** @class */ (function () {
                 if (this._pickable) {
                     var f = i % 3;
                     if (f == 0) {
-                        var pickedData = { idx: sortedPart.idx, faceId: faceId };
-                        this.pickedBySubMesh[subMeshIndex][subMeshFaceId] = pickedData;
+                        var pickedData = this.pickedBySubMesh[subMeshIndex][subMeshFaceId];
+                        if (pickedData) {
+                            pickedData.idx = sortedPart.idx;
+                            pickedData.faceId = faceId;
+                        }
+                        else {
+                            this.pickedBySubMesh[subMeshIndex][subMeshFaceId] = { idx: sortedPart.idx, faceId: faceId };
+                        }
                         subMeshFaceId++;
                         faceId++;
                     }

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/babylon.max.js.map


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

@@ -61653,7 +61653,7 @@ declare module "babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint" {
         get target(): NodeMaterialBlockTargets;
         set target(value: NodeMaterialBlockTargets);
         /**
-         * Gets a boolean indicating that the current point is connected
+         * Gets a boolean indicating that the current point is connected to another NodeMaterialBlock
          */
         get isConnected(): boolean;
         /**
@@ -134424,7 +134424,7 @@ declare module BABYLON {
         get target(): NodeMaterialBlockTargets;
         set target(value: NodeMaterialBlockTargets);
         /**
-         * Gets a boolean indicating that the current point is connected
+         * Gets a boolean indicating that the current point is connected to another NodeMaterialBlock
          */
         get isConnected(): boolean;
         /**

+ 1 - 1
dist/preview release/documentation.d.ts

@@ -58896,7 +58896,7 @@ declare module BABYLON {
         get target(): NodeMaterialBlockTargets;
         set target(value: NodeMaterialBlockTargets);
         /**
-         * Gets a boolean indicating that the current point is connected
+         * Gets a boolean indicating that the current point is connected to another NodeMaterialBlock
          */
         get isConnected(): boolean;
         /**

+ 16 - 0
dist/preview release/nodeEditor/babylon.nodeEditor.d.ts

@@ -149,6 +149,8 @@ declare module NODEEDITOR {
         private _mouseStartPointY;
         private _onSelectionChangedObserver;
         private _onGraphNodeRemovalObserver;
+        private _onExposePortOnFrameObserver;
+        private _onNodeLinkDisposedObservers;
         private _isCollapsed;
         private _frameInPorts;
         private _frameOutPorts;
@@ -167,6 +169,9 @@ declare module NODEEDITOR {
         get id(): number;
         get isCollapsed(): boolean;
         private _createInputPort;
+        private _markFramePortPositions;
+        private _createFramePorts;
+        private _redrawFramePorts;
         set isCollapsed(value: boolean);
         get nodes(): GraphNode[];
         get ports(): FrameNodePort[];
@@ -252,6 +257,7 @@ declare module NODEEDITOR {
 }
 declare module NODEEDITOR {
     export class NodePort {
+        private portContainer;
         connectionPoint: BABYLON.NodeMaterialConnectionPoint;
         node: GraphNode;
         protected _element: HTMLDivElement;
@@ -260,11 +266,15 @@ declare module NODEEDITOR {
         protected _portLabelElement: Element;
         protected _onCandidateLinkMovedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Nullable<BABYLON.Vector2>>>;
         protected _onSelectionChangedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
+        protected _exposedOnFrame: boolean;
         delegatedPort: BABYLON.Nullable<FrameNodePort>;
         get element(): HTMLDivElement;
         get portName(): string;
         set portName(newName: string);
         hasLabel(): boolean;
+        get exposedOnFrame(): boolean;
+        private _isConnectedToNodeInsideSameFrame;
+        set exposedOnFrame(value: boolean);
         refresh(): void;
         constructor(portContainer: HTMLElement, connectionPoint: BABYLON.NodeMaterialConnectionPoint, node: GraphNode, globalState: GlobalState);
         dispose(): void;
@@ -806,6 +816,7 @@ declare module NODEEDITOR {
         onSelect?: (value: boolean) => void;
         onValueChanged?: () => void;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+        disabled?: boolean;
     }
     export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponentProps, {
         isSelected: boolean;
@@ -1136,6 +1147,7 @@ declare module NODEEDITOR {
         private _isSelected;
         private _displayManager;
         private _isVisible;
+        private _enclosingFrameId;
         get isVisible(): boolean;
         set isVisible(value: boolean);
         private _upateNodePortNames;
@@ -1153,6 +1165,8 @@ declare module NODEEDITOR {
         get id(): number;
         get name(): string;
         get isSelected(): boolean;
+        get enclosingFrameId(): number;
+        set enclosingFrameId(value: number);
         set isSelected(value: boolean);
         constructor(block: BABYLON.NodeMaterialBlock, globalState: GlobalState);
         isOverlappingFrame(frame: GraphFrame): boolean;
@@ -1199,6 +1213,7 @@ declare module NODEEDITOR {
         onGraphNodeRemovalObservable: BABYLON.Observable<GraphNode>;
         onGetNodeFromBlock: (block: BABYLON.NodeMaterialBlock) => GraphNode;
         onGridSizeChanged: BABYLON.Observable<void>;
+        onExposePortOnFrameObservable: BABYLON.Observable<GraphNode>;
         previewMeshType: PreviewMeshType;
         previewMeshFile: File;
         listOfCustomPreviewMeshFiles: File[];
@@ -1280,6 +1295,7 @@ declare module NODEEDITOR {
         private _onSelectionChangedObserver;
         constructor(props: IFrameNodePortPropertyTabComponentProps);
         componentWillUnmount(): void;
+        toggleExposeOnFrame(value: boolean): void;
         render(): JSX.Element;
     }
 }

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 6 - 6
dist/preview release/nodeEditor/babylon.nodeEditor.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 191 - 96
dist/preview release/nodeEditor/babylon.nodeEditor.max.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map


+ 32 - 0
dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts

@@ -240,6 +240,8 @@ declare module "babylonjs-node-editor/diagram/graphFrame" {
         private _mouseStartPointY;
         private _onSelectionChangedObserver;
         private _onGraphNodeRemovalObserver;
+        private _onExposePortOnFrameObserver;
+        private _onNodeLinkDisposedObservers;
         private _isCollapsed;
         private _frameInPorts;
         private _frameOutPorts;
@@ -258,6 +260,9 @@ declare module "babylonjs-node-editor/diagram/graphFrame" {
         get id(): number;
         get isCollapsed(): boolean;
         private _createInputPort;
+        private _markFramePortPositions;
+        private _createFramePorts;
+        private _redrawFramePorts;
         set isCollapsed(value: boolean);
         get nodes(): GraphNode[];
         get ports(): FrameNodePort[];
@@ -354,6 +359,7 @@ declare module "babylonjs-node-editor/diagram/nodePort" {
     import { FrameNodePort } from "babylonjs-node-editor/diagram/frameNodePort";
     import { FramePortData } from "babylonjs-node-editor/diagram/graphCanvas";
     export class NodePort {
+        private portContainer;
         connectionPoint: NodeMaterialConnectionPoint;
         node: GraphNode;
         protected _element: HTMLDivElement;
@@ -362,11 +368,15 @@ declare module "babylonjs-node-editor/diagram/nodePort" {
         protected _portLabelElement: Element;
         protected _onCandidateLinkMovedObserver: Nullable<Observer<Nullable<Vector2>>>;
         protected _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
+        protected _exposedOnFrame: boolean;
         delegatedPort: Nullable<FrameNodePort>;
         get element(): HTMLDivElement;
         get portName(): string;
         set portName(newName: string);
         hasLabel(): boolean;
+        get exposedOnFrame(): boolean;
+        private _isConnectedToNodeInsideSameFrame;
+        set exposedOnFrame(value: boolean);
         refresh(): void;
         constructor(portContainer: HTMLElement, connectionPoint: NodeMaterialConnectionPoint, node: GraphNode, globalState: GlobalState);
         dispose(): void;
@@ -992,6 +1002,7 @@ declare module "babylonjs-node-editor/sharedComponents/checkBoxLineComponent" {
         onSelect?: (value: boolean) => void;
         onValueChanged?: () => void;
         onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+        disabled?: boolean;
     }
     export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponentProps, {
         isSelected: boolean;
@@ -1384,6 +1395,7 @@ declare module "babylonjs-node-editor/diagram/graphNode" {
         private _isSelected;
         private _displayManager;
         private _isVisible;
+        private _enclosingFrameId;
         get isVisible(): boolean;
         set isVisible(value: boolean);
         private _upateNodePortNames;
@@ -1401,6 +1413,8 @@ declare module "babylonjs-node-editor/diagram/graphNode" {
         get id(): number;
         get name(): string;
         get isSelected(): boolean;
+        get enclosingFrameId(): number;
+        set enclosingFrameId(value: number);
         set isSelected(value: boolean);
         constructor(block: NodeMaterialBlock, globalState: GlobalState);
         isOverlappingFrame(frame: GraphFrame): boolean;
@@ -1461,6 +1475,7 @@ declare module "babylonjs-node-editor/globalState" {
         onGraphNodeRemovalObservable: Observable<GraphNode>;
         onGetNodeFromBlock: (block: NodeMaterialBlock) => GraphNode;
         onGridSizeChanged: Observable<void>;
+        onExposePortOnFrameObservable: Observable<GraphNode>;
         previewMeshType: PreviewMeshType;
         previewMeshFile: File;
         listOfCustomPreviewMeshFiles: File[];
@@ -1555,6 +1570,7 @@ declare module "babylonjs-node-editor/diagram/properties/nodePortPropertyCompone
         private _onSelectionChangedObserver;
         constructor(props: IFrameNodePortPropertyTabComponentProps);
         componentWillUnmount(): void;
+        toggleExposeOnFrame(value: boolean): void;
         render(): JSX.Element;
     }
 }
@@ -1948,6 +1964,8 @@ declare module NODEEDITOR {
         private _mouseStartPointY;
         private _onSelectionChangedObserver;
         private _onGraphNodeRemovalObserver;
+        private _onExposePortOnFrameObserver;
+        private _onNodeLinkDisposedObservers;
         private _isCollapsed;
         private _frameInPorts;
         private _frameOutPorts;
@@ -1966,6 +1984,9 @@ declare module NODEEDITOR {
         get id(): number;
         get isCollapsed(): boolean;
         private _createInputPort;
+        private _markFramePortPositions;
+        private _createFramePorts;
+        private _redrawFramePorts;
         set isCollapsed(value: boolean);
         get nodes(): GraphNode[];
         get ports(): FrameNodePort[];
@@ -2051,6 +2072,7 @@ declare module NODEEDITOR {
 }
 declare module NODEEDITOR {
     export class NodePort {
+        private portContainer;
         connectionPoint: BABYLON.NodeMaterialConnectionPoint;
         node: GraphNode;
         protected _element: HTMLDivElement;
@@ -2059,11 +2081,15 @@ declare module NODEEDITOR {
         protected _portLabelElement: Element;
         protected _onCandidateLinkMovedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Nullable<BABYLON.Vector2>>>;
         protected _onSelectionChangedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
+        protected _exposedOnFrame: boolean;
         delegatedPort: BABYLON.Nullable<FrameNodePort>;
         get element(): HTMLDivElement;
         get portName(): string;
         set portName(newName: string);
         hasLabel(): boolean;
+        get exposedOnFrame(): boolean;
+        private _isConnectedToNodeInsideSameFrame;
+        set exposedOnFrame(value: boolean);
         refresh(): void;
         constructor(portContainer: HTMLElement, connectionPoint: BABYLON.NodeMaterialConnectionPoint, node: GraphNode, globalState: GlobalState);
         dispose(): void;
@@ -2605,6 +2631,7 @@ declare module NODEEDITOR {
         onSelect?: (value: boolean) => void;
         onValueChanged?: () => void;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+        disabled?: boolean;
     }
     export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponentProps, {
         isSelected: boolean;
@@ -2935,6 +2962,7 @@ declare module NODEEDITOR {
         private _isSelected;
         private _displayManager;
         private _isVisible;
+        private _enclosingFrameId;
         get isVisible(): boolean;
         set isVisible(value: boolean);
         private _upateNodePortNames;
@@ -2952,6 +2980,8 @@ declare module NODEEDITOR {
         get id(): number;
         get name(): string;
         get isSelected(): boolean;
+        get enclosingFrameId(): number;
+        set enclosingFrameId(value: number);
         set isSelected(value: boolean);
         constructor(block: BABYLON.NodeMaterialBlock, globalState: GlobalState);
         isOverlappingFrame(frame: GraphFrame): boolean;
@@ -2998,6 +3028,7 @@ declare module NODEEDITOR {
         onGraphNodeRemovalObservable: BABYLON.Observable<GraphNode>;
         onGetNodeFromBlock: (block: BABYLON.NodeMaterialBlock) => GraphNode;
         onGridSizeChanged: BABYLON.Observable<void>;
+        onExposePortOnFrameObservable: BABYLON.Observable<GraphNode>;
         previewMeshType: PreviewMeshType;
         previewMeshFile: File;
         listOfCustomPreviewMeshFiles: File[];
@@ -3079,6 +3110,7 @@ declare module NODEEDITOR {
         private _onSelectionChangedObserver;
         constructor(props: IFrameNodePortPropertyTabComponentProps);
         componentWillUnmount(): void;
+        toggleExposeOnFrame(value: boolean): void;
         render(): JSX.Element;
     }
 }

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

@@ -61653,7 +61653,7 @@ declare module "babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint" {
         get target(): NodeMaterialBlockTargets;
         set target(value: NodeMaterialBlockTargets);
         /**
-         * Gets a boolean indicating that the current point is connected
+         * Gets a boolean indicating that the current point is connected to another NodeMaterialBlock
          */
         get isConnected(): boolean;
         /**
@@ -134424,7 +134424,7 @@ declare module BABYLON {
         get target(): NodeMaterialBlockTargets;
         set target(value: NodeMaterialBlockTargets);
         /**
-         * Gets a boolean indicating that the current point is connected
+         * Gets a boolean indicating that the current point is connected to another NodeMaterialBlock
          */
         get isConnected(): boolean;
         /**

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 3 - 3
dist/preview release/viewer/babylon.viewer.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -27,6 +27,7 @@
 - Frames are now resizable from the corners ([belfortk](https://github.com/belfortk)
 - Can now rename and re-order frame inputs and outputs ([belfortk](https://github.com/belfortk)
 - Can now edit Node port names ([belfortk](https://github.com/belfortk)
+- Updated which node ports are shown on frames by default so that only node ports connected to outside nodes are by default exposed on the frame ([belfortk](https://github.com/belfortk)
 
 ### Inspector
 
@@ -117,6 +118,7 @@
 
 - Fix infinite loop in `GlowLayer.unReferenceMeshFromUsingItsOwnMaterial` ([Popov72](https://github.com/Popov72)
 - Fix picking issue in the Solid Particle System when MultiMaterial is enabled ([jerome](https://github.com/jbousquie))
+- Fix picking issue in the Solid Particle System when expandable ([jerome](https://github.com/jbousquie))
 - `QuadraticErrorSimplification` was not exported ([RaananW](https://github.com/Raananw)
 - Fix NME Frames bug where collapsing and moving a frame removed the nodes inside ([belfortk](https://github.com/belfortk)
 - Fix moving / disappearing controls when freezing/unfreezing the ScrollViewer ([Popov72](https://github.com/Popov72)

+ 16 - 0
nodeEditor/src/components/propertyTab/propertyTab.scss

@@ -486,6 +486,22 @@
                 background: rgb(22, 73, 117);
             }
 
+            .cbx:checked ~ label.disabled { 
+                background: rgb(22, 73, 117);
+                cursor: pointer;
+            }
+
+            .cbx:checked ~ label.disabled:after {
+                left: 20px;
+                background: rgb(85, 85, 85);
+                cursor: pointer;
+            }
+
+            .cbx ~ label.disabled {
+                background: rgb(85, 85, 85);
+                cursor: pointer;
+            }
+
             .hidden { 
                 display: none; 
             }               

+ 5 - 1
nodeEditor/src/diagram/frameNodePort.ts

@@ -72,7 +72,11 @@ export class FrameNodePort extends NodePort {
         if (!displayManager || displayManager.shouldDisplayPortLabels(block)) {
             let portLabel = root.ownerDocument!.createElement("div");
             portLabel.classList.add("port-label");
-            portLabel.innerHTML = connectionPoint.displayName || connectionPoint.name;        
+            let portName = connectionPoint.displayName || connectionPoint.name;
+            if (connectionPoint.ownerBlock.isInput) {
+                portName = node.name;
+            }
+            portLabel.innerHTML = portName;       
             portContainer.appendChild(portLabel);
         }
 

+ 125 - 78
nodeEditor/src/diagram/graphFrame.ts

@@ -53,6 +53,8 @@ export class GraphFrame {
     private _mouseStartPointY: Nullable<number> = null;
     private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
     private _onGraphNodeRemovalObserver: Nullable<Observer<GraphNode>>; 
+    private _onExposePortOnFrameObserver: Nullable<Observer<GraphNode>>;
+    private _onNodeLinkDisposedObservers: Nullable<Observer<NodeLink>>[] = [];
     private _isCollapsed = false;
     private _frameInPorts: FrameNodePort[] = [];
     private _frameOutPorts: FrameNodePort[] = [];
@@ -87,101 +89,133 @@ export class GraphFrame {
         this._controlledPorts.push(port);
     }
 
-    public set isCollapsed(value: boolean) {
-        if (this._isCollapsed === value) {
-            return;
+    // Mark ports with FramePortPosition for re-arrangement support
+    private _markFramePortPositions() {
+        // mark FrameInPorts 
+         if(this._frameInPorts.length == 2){
+            this._frameInPorts[0].framePortPosition = FramePortPosition.Top;
+            this._frameInPorts[1].framePortPosition = FramePortPosition.Bottom;
+        } else {
+            for(let i = 0; i < this._frameInPorts.length; i++) {
+                const port = this._frameInPorts[i];
+                if(i === 0){
+                    port.framePortPosition = FramePortPosition.Top;
+                } else if(i === this._frameInPorts.length -1){
+                    port.framePortPosition = FramePortPosition.Bottom;
+                } else {
+                    port.framePortPosition = FramePortPosition.Middle;
+                }
+            }
         }
 
-        this._isCollapsed = value;
-        this._ownerCanvas._frameIsMoving = true;
-
-        // Need to delegate the outside ports to the frame
-        if (value) {
-            this.element.classList.add("collapsed");
+        // mark FrameOutPorts
+        if(this._frameOutPorts.length == 2){
+            this._frameOutPorts[0].framePortPosition = FramePortPosition.Top;
+            this._frameOutPorts[1].framePortPosition = FramePortPosition.Bottom;
+        } else {
+            for(let i = 0; i < this._frameOutPorts.length; i++) {
+                const port = this._frameOutPorts[i];
+                if(i === 0){
+                    port.framePortPosition = FramePortPosition.Top
+                } else if(i === this._frameInPorts.length -1){
+                    port.framePortPosition = FramePortPosition.Bottom
+                } else {
+                    port.framePortPosition = FramePortPosition.Middle
+                }
+            }
+        }
+    }
 
-            this._moveFrame((this.width - this.CollapsedWidth) / 2, 0);
+    private _createFramePorts() {
+        for (var node of this._nodes) {
+            node.isVisible = false;
+            for (var port of node.outputPorts) { // Output
+                if (port.connectionPoint.hasEndpoints) {
+                    let portAdded = false;
 
-            for (var node of this._nodes) {
-                node.isVisible = false;
-                for (var port of node.outputPorts) { // Output
-                    if (port.connectionPoint.hasEndpoints) {
-                        let portAdded = false;
+                    for (var link of node.links) {
+                        if (link.portA === port && this.nodes.indexOf(link.nodeB!) === -1) {
+                            let localPort: FrameNodePort;
 
-                        for (var link of node.links) {
-                            if (link.portA === port && this.nodes.indexOf(link.nodeB!) === -1) {
-                                let localPort: FrameNodePort;
+                            if (!portAdded) {
+                                portAdded = true;
+                                localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, link.nodeB!, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
+                                this._frameOutPorts.push(localPort);
 
-                                if (!portAdded) {
-                                    portAdded = true;
-                                    localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, link.nodeB!, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
-                                    this._frameOutPorts.push(localPort);
+                                link.isVisible = true;
 
-                                    link.isVisible = true;
+                                const onLinkDisposedObserver = link.onDisposedObservable.add((nodeLink: NodeLink) => {
+                                    this._redrawFramePorts();
+                                });
 
-                                } else {
-                                    localPort = this.ports.filter(p => p.connectionPoint === port.connectionPoint)[0];
-                                }
+                                this._onNodeLinkDisposedObservers.push(onLinkDisposedObserver); 
 
-                                port.delegatedPort = localPort;
-                                this._controlledPorts.push(port);
+                            } else {
+                                localPort = this.ports.filter(p => p.connectionPoint === port.connectionPoint)[0];
                             }
-                        }
-                    } else {
-                        let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
-                        this._frameOutPorts.push(localPort);
-                        port.delegatedPort = localPort;
-                        this._controlledPorts.push(port);
-
-                    }
-                }
 
-                for (var port of node.inputPorts) { // Input
-                    if (port.connectionPoint.isConnected) {
-                        for (var link of node.links) {
-                            if (link.portB === port && this.nodes.indexOf(link.nodeA) === -1) {
-                                this._createInputPort(port, node);
-                                link.isVisible = true;
-                            }
+                            port.delegatedPort = localPort;
+                            this._controlledPorts.push(port);
                         }
-                    } else {
-                        this._createInputPort(port, node);
                     }
+                } else if(port.exposedOnFrame) {
+                    let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
+                    this._frameOutPorts.push(localPort);
+                    port.delegatedPort = localPort;
+                    this._controlledPorts.push(port);
                 }
             }
 
-            // mark FrameInPorts with position
-            if(this._frameInPorts.length == 2){
-                this._frameInPorts[0].framePortPosition = FramePortPosition.Top;
-                this._frameInPorts[1].framePortPosition = FramePortPosition.Bottom;
-            } else {
-                for(let i = 0; i < this._frameInPorts.length; i++) {
-                    const port = this._frameInPorts[i];
-                    if(i === 0){
-                        port.framePortPosition = FramePortPosition.Top;
-                    } else if(i === this._frameInPorts.length -1){
-                        port.framePortPosition = FramePortPosition.Bottom;
-                    } else {
-                        port.framePortPosition = FramePortPosition.Middle;
+            for (var port of node.inputPorts) { // Input
+                if (port.connectionPoint.isConnected) {
+                    for (var link of node.links) {
+                        if (link.portB === port && this.nodes.indexOf(link.nodeA) === -1) {
+                            this._createInputPort(port, node);
+                            link.isVisible = true;
+                            
+                            const onLinkDisposedObserver = link.onDisposedObservable.add((nodeLink: NodeLink) => {
+                                this._redrawFramePorts();
+                            });
+
+                            this._onNodeLinkDisposedObservers.push(onLinkDisposedObserver);
+                        }
                     }
+                } else if(port.exposedOnFrame) {
+                    this._createInputPort(port, node);
                 }
             }
+        }
+    }
+    
+    private _redrawFramePorts() {
+        if(!this.isCollapsed) {
+            return;
+        }
+        this.ports.forEach((framePort:FrameNodePort) => {
+            framePort.dispose()
+        });
 
-            // mark FrameOutPorts with position
-            if(this._frameOutPorts.length == 2){
-                this._frameOutPorts[0].framePortPosition = FramePortPosition.Top;
-                this._frameOutPorts[1].framePortPosition = FramePortPosition.Bottom;
-            } else {
-                for(let i = 0; i < this._frameOutPorts.length; i++) {
-                    const port = this._frameOutPorts[i];
-                    if(i === 0){
-                        port.framePortPosition = FramePortPosition.Top
-                    } else if(i === this._frameInPorts.length -1){
-                        port.framePortPosition = FramePortPosition.Bottom
-                    } else {
-                        port.framePortPosition = FramePortPosition.Middle
-                    }
-                }
-            }
+        this._createFramePorts();
+        this.ports.forEach((framePort: FrameNodePort) => framePort.node._refreshLinks());
+    }
+
+    public set isCollapsed(value: boolean) {
+        if (this._isCollapsed === value) {
+            return;
+        }
+
+        this._isCollapsed = value;
+        this._ownerCanvas._frameIsMoving = true;
+
+        // Need to delegate the outside ports to the frame
+        if (value) {
+            this.element.classList.add("collapsed");
+
+            this._moveFrame((this.width - this.CollapsedWidth) / 2, 0);
+
+            this._createFramePorts()
+
+            this._markFramePortPositions()
 
         } else {
             this.element.classList.remove("collapsed");
@@ -204,6 +238,7 @@ export class GraphFrame {
             this._frameInPorts = [];
             this._frameOutPorts = [];
             this._controlledPorts = [];
+            this._onNodeLinkDisposedObservers = [];
 
             for (var node of this._nodes) {
                 node.isVisible = true;
@@ -225,7 +260,7 @@ export class GraphFrame {
         }
 
         this.onExpandStateChanged.notifyObservers(this);
-    }
+    }     
 
     public get nodes() {
         return this._nodes;
@@ -487,6 +522,13 @@ export class GraphFrame {
             }
         });
 
+        this._onExposePortOnFrameObserver = canvas.globalState.onExposePortOnFrameObservable.add((node: GraphNode) => {
+            if (this.nodes.indexOf(node) === -1) {
+                return;
+            }
+            this._redrawFramePorts();
+        });
+
         this._commentsElement = document.createElement('div');
         this._commentsElement.className = 'frame-comments';
         this._commentsElement.style.color = 'white';
@@ -1209,11 +1251,15 @@ export class GraphFrame {
 
         if (this._onSelectionChangedObserver) {
             this._ownerCanvas.globalState.onSelectionChangedObservable.remove(this._onSelectionChangedObserver);
-        }
+        };
 
         if(this._onGraphNodeRemovalObserver) {
             this._ownerCanvas.globalState.onGraphNodeRemovalObservable.remove(this._onGraphNodeRemovalObserver);
-        }
+        };
+
+        if(this._onExposePortOnFrameObserver) {
+            this._ownerCanvas.globalState.onExposePortOnFrameObservable.remove(this._onExposePortOnFrameObserver);
+        };
 
         this.element.parentElement!.removeChild(this.element);
 
@@ -1261,6 +1307,7 @@ export class GraphFrame {
 
                 if (node.length) {
                     newFrame.nodes.push(node[0]);
+                    node[0].enclosingFrameId = newFrame.id;
                 }
             }
         } else {

+ 15 - 1
nodeEditor/src/diagram/graphNode.ts

@@ -39,6 +39,7 @@ export class GraphNode {
     private _isSelected: boolean;
     private _displayManager: Nullable<IDisplayManager> = null;
     private _isVisible = true;
+    private _enclosingFrameId: number;
 
     public get isVisible() {
         return this._isVisible;
@@ -144,6 +145,14 @@ export class GraphNode {
         return this._isSelected;
     }
 
+    public get enclosingFrameId() {
+        return this._enclosingFrameId;
+    }
+
+    public set enclosingFrameId(value: number) {
+        this._enclosingFrameId = value;
+    }
+
     public set isSelected(value: boolean) {
         if (this._isSelected === value) {
             return;            
@@ -211,10 +220,15 @@ export class GraphNode {
         rect1.width -= 5;
         rect1.height -= 5;
 
-        return !(rect1.right < rect2.left || 
+        const isOverlappingFrame = !(rect1.right < rect2.left || 
             rect1.left > rect2.right || 
             rect1.bottom < rect2.top || 
             rect1.top > rect2.bottom);
+
+        if (isOverlappingFrame) {
+            this.enclosingFrameId = frame.id;
+        }
+        return isOverlappingFrame;
     }
 
     public getPortForConnectionPoint(point: NodeMaterialConnectionPoint) {

+ 2 - 0
nodeEditor/src/diagram/nodeLink.ts

@@ -148,5 +148,7 @@ export class NodeLink {
         }
 
         this.onDisposedObservable.notifyObservers(this);
+
+        this.onDisposedObservable.clear();
     }
 }

+ 28 - 3
nodeEditor/src/diagram/nodePort.ts

@@ -18,7 +18,8 @@ export class NodePort {
     protected _globalState: GlobalState;
     protected _portLabelElement: Element;
     protected _onCandidateLinkMovedObserver: Nullable<Observer<Nullable<Vector2>>>;
-    protected _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;  
+    protected _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
+    protected _exposedOnFrame: boolean;
     
     public delegatedPort: Nullable<FrameNodePort> = null;
 
@@ -31,7 +32,11 @@ export class NodePort {
     }
 
     public get portName(){
-        return this.connectionPoint.displayName || this.connectionPoint.name;
+        let portName = this.connectionPoint.displayName || this.connectionPoint.name;
+        if (this.connectionPoint.ownerBlock.isInput) {
+            portName = this.node.name;
+        }
+        return portName
     }
 
     public set portName(newName: string){
@@ -45,6 +50,24 @@ export class NodePort {
         return !!this._portLabelElement;
     }
 
+    public get exposedOnFrame() {
+        return (this.connectionPoint.isConnected || !!this._exposedOnFrame) && !this._isConnectedToNodeInsideSameFrame() ;
+    }
+
+    private _isConnectedToNodeInsideSameFrame(){
+        const link = this.node.getLinksForConnectionPoint(this.connectionPoint)
+        if (link.length){
+            if (link[0].nodeA.enclosingFrameId == link[0].nodeB!.enclosingFrameId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public set exposedOnFrame(value: boolean) {
+        this._exposedOnFrame = value;
+    }
+
     public refresh() {
         this._element.style.background = BlockTools.GetColorFromConnectionNodeType(this.connectionPoint.type);
         switch (this.connectionPoint.type) {
@@ -72,7 +95,7 @@ export class NodePort {
         }
     }
 
-    public constructor(portContainer: HTMLElement, public connectionPoint: NodeMaterialConnectionPoint, public node: GraphNode, globalState: GlobalState) {
+    public constructor(private portContainer: HTMLElement, public connectionPoint: NodeMaterialConnectionPoint, public node: GraphNode, globalState: GlobalState) {
         this._element = portContainer.ownerDocument!.createElement("div");
         this._element.classList.add("port");
         portContainer.appendChild(this._element);
@@ -120,6 +143,8 @@ export class NodePort {
         if (this._onSelectionChangedObserver) {
             this._globalState.onSelectionChangedObservable.remove(this._onSelectionChangedObserver);
         }
+
+        this.portContainer.remove();
     }
 
     public static CreatePortElement(connectionPoint: NodeMaterialConnectionPoint, node: GraphNode, root: HTMLElement, 

+ 7 - 0
nodeEditor/src/diagram/properties/nodePortPropertyComponent.tsx

@@ -10,6 +10,7 @@ import { NodePort } from '../nodePort';
 import { GraphNode } from '../graphNode';
 import { NodeLink } from '../nodeLink';
 import { FramePortData } from '../graphCanvas';
+import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
 
 export interface IFrameNodePortPropertyTabComponentProps {
     globalState: GlobalState
@@ -27,6 +28,11 @@ export class NodePortPropertyTabComponent extends React.Component<IFrameNodePort
         this.props.globalState.onSelectionChangedObservable.remove(this._onSelectionChangedObserver);
     }
 
+    toggleExposeOnFrame(value: boolean){
+        this.props.nodePort.exposedOnFrame = value;
+        this.props.globalState.onExposePortOnFrameObservable.notifyObservers(this.props.nodePort.node);
+    }
+
     render() {
         return (
             <div id="propertyTab">
@@ -39,6 +45,7 @@ export class NodePortPropertyTabComponent extends React.Component<IFrameNodePort
                 <div>
                     <LineContainerComponent title="GENERAL">
                         {this.props.nodePort.hasLabel() && <TextInputLineComponent globalState={this.props.globalState} label="Port Label" propertyName="portName" target={this.props.nodePort} />}
+                        {this.props.nodePort.node.enclosingFrameId !== undefined && <CheckBoxLineComponent label= "Expose Port on Frame" target={this.props.nodePort} isSelected={() => this.props.nodePort.exposedOnFrame} onSelect={(value: boolean) => this.toggleExposeOnFrame(value)}  propertyName="exposedOnFrame" disabled={this.props.nodePort.connectionPoint.isConnected} />}
                     </LineContainerComponent>
                 </div>
             </div>

+ 1 - 0
nodeEditor/src/globalState.ts

@@ -42,6 +42,7 @@ export class GlobalState {
     onGraphNodeRemovalObservable = new Observable<GraphNode>();
     onGetNodeFromBlock: (block: NodeMaterialBlock) => GraphNode;
     onGridSizeChanged = new Observable<void>();
+    onExposePortOnFrameObservable = new Observable<GraphNode>();
     previewMeshType: PreviewMeshType;
     previewMeshFile: File;
     listOfCustomPreviewMeshFiles: File[] = [];

+ 4 - 3
nodeEditor/src/sharedComponents/checkBoxLineComponent.tsx

@@ -10,6 +10,7 @@ export interface ICheckBoxLineComponentProps {
     onSelect?: (value: boolean) => void;
     onValueChanged?: () => void;
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    disabled?: boolean;
 }
 
 export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponentProps, { isSelected: boolean }> {
@@ -42,7 +43,7 @@ export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponen
             this._localChange = false;
             return true;
         }
-        return nextProps.label !== this.props.label;
+        return nextProps.label !== this.props.label || nextProps.target !== this.props.target;
     }
 
     onChange() {
@@ -76,8 +77,8 @@ export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponen
                     {this.props.label}
                 </div>
                 <div className="checkBox">
-                    <input type="checkbox" id={"checkbox" + this._uniqueId} className="cbx hidden" checked={this.state.isSelected} onChange={() => this.onChange()} />
-                    <label htmlFor={"checkbox" + this._uniqueId} className="lbl"></label>
+                    <input type="checkbox" id={"checkbox" + this._uniqueId} className="cbx hidden" checked={this.state.isSelected} onChange={() => this.onChange()} disabled={!!this.props.disabled}/>
+                    <label htmlFor={"checkbox" + this._uniqueId} className={`lbl${!!this.props.disabled ? ' disabled' : ''}`}></label>
                 </div>
             </div>
         );

+ 2 - 2
src/Materials/Node/nodeMaterialBlockConnectionPoint.ts

@@ -178,10 +178,10 @@ export class NodeMaterialConnectionPoint {
     }
 
     /**
-     * Gets a boolean indicating that the current point is connected
+     * Gets a boolean indicating that the current point is connected to another NodeMaterialBlock
      */
     public get isConnected(): boolean {
-        return this.connectedPoint !== null;
+        return this.connectedPoint !== null || this.hasEndpoints;
     }
 
     /**

+ 1 - 1
src/Offline/database.ts

@@ -92,7 +92,7 @@ export class Database implements IOfflineProvider {
     }
 
     private static _ReturnFullUrlLocation = (url: string): string => {
-        if (url.indexOf("http:/") === -1 && url.indexOf("https:/") === -1) {
+        if (url.indexOf("http:/") === -1 && url.indexOf("https:/") === -1 && typeof window !== "undefined") {
             return (Database._ParseURL(window.location.href) + url);
         }
         else {

+ 37 - 11
src/Particles/solidParticleSystem.ts

@@ -248,6 +248,22 @@ export class SolidParticleSystem implements IDisposable {
         vertexData.applyToMesh(this.mesh, this._updatable);
         this.mesh.isPickable = this._pickable;
 
+        if (this._pickable) {
+            let faceId = 0;
+            for (let p = 0; p < this.nbParticles; p++) {
+                let part = this.particles[p];
+                let lind = part._model._indicesLength;
+                for (let i = 0; i < lind; i++) {
+                    let f = i % 3;
+                    if (f == 0) {
+                        const pickedData = {idx: part.idx, faceId: faceId};
+                        this.pickedParticles[faceId] = pickedData;
+                        faceId++;
+                    }
+                }
+            }
+        }
+
         if (this._multimaterialEnabled) {
             this.setMultiMaterial(this._materials);
         }
@@ -573,13 +589,6 @@ export class SolidParticleSystem implements IDisposable {
             }
         }
 
-        if (this._pickable) {
-            var nbfaces = meshInd.length / 3;
-            for (i = 0; i < nbfaces; i++) {
-                this.pickedParticles.push({ idx: idx, faceId: i });
-            }
-        }
-
         if (this._depthSort || this._multimaterialEnabled) {
             var matIndex = (copy.materialIndex !== null) ? copy.materialIndex : 0;
             this.depthSortedParticles.push(new DepthSortedParticle(idx, ind, meshInd.length, matIndex));
@@ -1266,12 +1275,23 @@ export class SolidParticleSystem implements IDisposable {
                 depthSortedParticles.sort(this._depthSortFunction);
                 const dspl = depthSortedParticles.length;
                 let sid = 0;
+                let faceId = 0;
                 for (let sorted = 0; sorted < dspl; sorted++) {
-                    const lind = depthSortedParticles[sorted].indicesLength;
-                    const sind = depthSortedParticles[sorted].ind;
+                    const sortedParticle = depthSortedParticles[sorted];
+                    const lind = sortedParticle.indicesLength;
+                    const sind = sortedParticle.ind;
                     for (var i = 0; i < lind; i++) {
                         indices32[sid] = indices[sind + i];
                         sid++;
+                        if (this._pickable) {
+                            let f = i % 3;
+                            if (f == 0) {
+                                let pickedData = this.pickedParticles[faceId];
+                                pickedData.idx = sortedParticle.idx;
+                                pickedData.faceId = faceId;
+                                faceId++;
+                            }
+                        }
                     }
                 }
                 mesh.updateIndices(indices32);
@@ -1475,8 +1495,14 @@ export class SolidParticleSystem implements IDisposable {
                 if (this._pickable) {
                     let f = i % 3;
                     if (f == 0) {
-                        const pickedData = {idx: sortedPart.idx, faceId: faceId};
-                        this.pickedBySubMesh[subMeshIndex][subMeshFaceId] = pickedData;
+                        let pickedData = this.pickedBySubMesh[subMeshIndex][subMeshFaceId];
+                        if (pickedData) {
+                            pickedData.idx = sortedPart.idx;
+                            pickedData.faceId = faceId;
+                        }
+                        else {
+                            this.pickedBySubMesh[subMeshIndex][subMeshFaceId] = {idx: sortedPart.idx, faceId: faceId};
+                        }
                         subMeshFaceId++;
                         faceId++;
                     }