Benjamin Guignabert před 5 roky
rodič
revize
469ab10373
100 změnil soubory, kde provedl 14891 přidání a 2258 odebrání
  1. 3 4
      Playground/js/main.js
  2. 25 0
      Playground/js/monacoCreator.js
  3. 44 0
      Playground/templates.json
  4. 2 1
      Tools/Config/config.json
  5. 11 0
      Tools/Publisher/tasks/processEs6Packages.js
  6. 11 11
      dist/preview release/ammo.js
  7. 1 1
      dist/preview release/ammo.wasm.js
  8. binární
      dist/preview release/ammo.wasm.wasm
  9. 1298 105
      dist/preview release/babylon.d.ts
  10. 2 2
      dist/preview release/babylon.js
  11. 4540 589
      dist/preview release/babylon.max.js
  12. 1 1
      dist/preview release/babylon.max.js.map
  13. 2687 195
      dist/preview release/babylon.module.d.ts
  14. 1302 105
      dist/preview release/documentation.d.ts
  15. 1 1
      dist/preview release/glTF2Interface/package.json
  16. 4 0
      dist/preview release/gui/babylon.gui.d.ts
  17. 7 0
      dist/preview release/gui/babylon.gui.js
  18. 1 1
      dist/preview release/gui/babylon.gui.js.map
  19. 2 2
      dist/preview release/gui/babylon.gui.min.js
  20. 8 0
      dist/preview release/gui/babylon.gui.module.d.ts
  21. 2 2
      dist/preview release/gui/package.json
  22. 7 7
      dist/preview release/inspector/babylon.inspector.bundle.js
  23. 157 3
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  24. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  25. 42 0
      dist/preview release/inspector/babylon.inspector.d.ts
  26. 86 0
      dist/preview release/inspector/babylon.inspector.module.d.ts
  27. 7 7
      dist/preview release/inspector/package.json
  28. 3 3
      dist/preview release/loaders/package.json
  29. 9 2
      dist/preview release/materialsLibrary/babylon.customMaterial.js
  30. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.js.map
  31. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  32. 9 2
      dist/preview release/materialsLibrary/babylonjs.materials.js
  33. 1 1
      dist/preview release/materialsLibrary/babylonjs.materials.js.map
  34. 1 1
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  35. 2 2
      dist/preview release/materialsLibrary/package.json
  36. 60 39
      dist/preview release/nodeEditor/babylon.nodeEditor.d.ts
  37. 7 7
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  38. 513 320
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  39. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  40. 137 88
      dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts
  41. 2 2
      dist/preview release/nodeEditor/package.json
  42. 1 1
      dist/preview release/package.json
  43. 1 1
      dist/preview release/packagesSizeBaseLine.json
  44. 2 2
      dist/preview release/postProcessesLibrary/package.json
  45. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  46. 3 3
      dist/preview release/serializers/package.json
  47. 2687 195
      dist/preview release/viewer/babylon.module.d.ts
  48. 190 126
      dist/preview release/viewer/babylon.viewer.js
  49. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  50. 51 22
      dist/preview release/what's new.md
  51. 1 0
      dist/what's new.md
  52. 1 0
      gui/src/2D/advancedDynamicTexture.ts
  53. 9 0
      gui/src/2D/controls/control.ts
  54. 2 0
      inspector/src/components/actionTabs/actionTabs.scss
  55. 1 0
      inspector/src/components/actionTabs/tabs/debugTabComponent.tsx
  56. 28 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx
  57. 55 26
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent.tsx
  58. 84 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/popupComponent.tsx
  59. 1 1
      inspector/src/components/globalState.ts
  60. 10 0
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  61. 5 0
      inspector/src/inspector.ts
  62. 4 1
      materialsLibrary/src/custom/customMaterial.ts
  63. 8 1
      materialsLibrary/src/custom/pbrCustomMaterial.ts
  64. 104 77
      nodeEditor/src/blockTools.ts
  65. 23 13
      nodeEditor/src/components/nodeList/nodeListComponent.tsx
  66. 16 0
      nodeEditor/src/components/propertyTab/propertyTab.scss
  67. 24 7
      nodeEditor/src/components/propertyTab/propertyTabComponent.tsx
  68. 2 1
      nodeEditor/src/diagram/display/textureDisplayManager.ts
  69. 2 0
      nodeEditor/src/diagram/displayLedger.ts
  70. 5 12
      nodeEditor/src/diagram/frameNodePort.ts
  71. 15 2
      nodeEditor/src/diagram/graphCanvas.tsx
  72. 140 78
      nodeEditor/src/diagram/graphFrame.ts
  73. 26 3
      nodeEditor/src/diagram/graphNode.ts
  74. 2 0
      nodeEditor/src/diagram/nodeLink.ts
  75. 81 2
      nodeEditor/src/diagram/nodePort.ts
  76. 0 24
      nodeEditor/src/diagram/properties/PerturbNormalNodePropertyComponent.tsx
  77. 0 33
      nodeEditor/src/diagram/properties/clampNodePropertyComponent.tsx
  78. 1 1
      nodeEditor/src/diagram/properties/frameNodePortPropertyComponent.tsx
  79. 113 5
      nodeEditor/src/diagram/properties/genericNodePropertyComponent.tsx
  80. 2 2
      nodeEditor/src/diagram/properties/gradientNodePropertyComponent.tsx
  81. 2 2
      nodeEditor/src/diagram/properties/inputNodePropertyComponent.tsx
  82. 2 2
      nodeEditor/src/diagram/properties/lightInformationPropertyTabComponent.tsx
  83. 2 2
      nodeEditor/src/diagram/properties/lightPropertyTabComponent.tsx
  84. 54 0
      nodeEditor/src/diagram/properties/nodePortPropertyComponent.tsx
  85. 0 33
      nodeEditor/src/diagram/properties/remapNodePropertyComponent.tsx
  86. 19 8
      nodeEditor/src/diagram/properties/texturePropertyTabComponent.tsx
  87. 2 2
      nodeEditor/src/diagram/properties/transformNodePropertyComponent.tsx
  88. 2 2
      nodeEditor/src/diagram/properties/trigonometryNodePropertyComponent.tsx
  89. 0 23
      nodeEditor/src/diagram/properties/worleyNoise3DNodePropertyComponent.tsx
  90. 2 8
      nodeEditor/src/diagram/propertyLedger.ts
  91. 1 0
      nodeEditor/src/globalState.ts
  92. 15 5
      nodeEditor/src/sharedComponents/checkBoxLineComponent.tsx
  93. 1 1
      package.json
  94. 13 13
      src/Audio/sound.ts
  95. 9 0
      src/Debug/debugLayer.ts
  96. 7 7
      src/Engines/Extensions/engine.rawTexture.ts
  97. 52 0
      src/Engines/Extensions/engine.readTexture.ts
  98. 5 0
      src/Engines/Extensions/engine.views.ts
  99. 1 0
      src/Engines/Extensions/index.ts
  100. 0 0
      src/Engines/engine.ts

+ 3 - 4
Playground/js/main.js

@@ -566,7 +566,7 @@ class Main {
                         exampleList.appendChild(noResultContainer);
                     }
 
-                    if (!location.hash && restoreVersionResult == false) {
+                    if (!location.hash && restoreVersionResult == false && location.pathname.indexOf('pg/') === -1) {
                         // Query string
                         var queryString = window.location.search;
 
@@ -1016,12 +1016,11 @@ class Main {
     };
     checkHash() {
         let pgHash = "";
-        if (location.search) {
+        if (location.search && (!location.pathname  || location.pathname === '/') && !location.hash) {
             var query = this.parseQuery(location.search);
             if (query.pg) {
                 pgHash = "#" + query.pg + "#" + (query.revision || "0")
             }
-
         } else if (location.hash) {
             if (this.previousHash !== location.hash) {
                 this.cleanHash();
@@ -1156,4 +1155,4 @@ class Main {
         }
         return query;
     }
-}
+}

+ 25 - 0
Playground/js/monacoCreator.js

@@ -21,6 +21,7 @@ class MonacoCreator {
         this.blockEditorChange = false;
         this.definitionWorker = null;
         this.deprecatedCandidates = [];
+        this.templates = [];
 
         this.compilerTriggerTimeoutID = null;
 
@@ -113,6 +114,12 @@ class MonacoCreator {
 
         this.setupDefinitionWorker(libContent);
 
+        // Load code templates
+        response = await fetch("/templates.json");
+        if (response.ok) {
+            this.templates = await response.json();
+        }
+
         // WARNING !!! We need the 'dev' version of Monaco, as we use monkey-patching to hook into the suggestion adapter
         require.config({
             paths: {
@@ -127,6 +134,13 @@ class MonacoCreator {
             // This is used for a vscode-like color preview for ColorX types
             this.setupMonacoColorProvider();
 
+            // enhance templates with extra properties
+            for (const template of this.templates) {
+                template.kind = monaco.languages.CompletionItemKind.Snippet,
+                template.sortText = "!" + template.label; // make sure templates are on top of the completion window
+                template.insertTextRules = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet;
+            }
+
             // As explained above, we need the 'dev' version of Monaco to access this adapter!
             require(['vs/language/typescript/languageFeatures'], module => {
                 this.hookMonacoCompletionProvider(module.SuggestAdapter);
@@ -289,6 +303,17 @@ class MonacoCreator {
                 }
             }
 
+            // add our own templates when invoked without context
+            if (context.triggerKind == monaco.languages.CompletionTriggerKind.Invoke) {
+                for (const template of owner.templates) {
+                    if (template.language && owner.monacoMode != template.language)
+                        continue;
+
+                    template.range = undefined;
+                    suggestions.push(template);
+                }
+            }
+
             // preserve incomplete flag or force it when the definition is not yet analyzed
             const incomplete = (result.incomplete && result.incomplete == true) || owner.deprecatedCandidates.length == 0;
 

+ 44 - 0
Playground/templates.json

@@ -0,0 +1,44 @@
+[
+  {
+    "label" : "Create a sphere",
+    "documentation" : "https://doc.babylonjs.com/how_to/set_shapes",
+    "insertText" : "var sphere = BABYLON.MeshBuilder.CreateSphere(\"${1:sphere}\", {diameter: ${2:1}}, scene);",
+    "language" : "javascript"
+  },
+  {
+    "label" : "Create a box",
+    "documentation" : "https://doc.babylonjs.com/how_to/set_shapes",
+    "insertText" : "var box = BABYLON.MeshBuilder.CreateBox(\"${1:box}\", {size: ${2:1}}, scene);",
+    "language" : "javascript"
+  },
+  {
+    "label" : "Create a cylinder",
+    "documentation" : "https://doc.babylonjs.com/how_to/set_shapes",
+    "insertText" : "var cylinder = BABYLON.MeshBuilder.CreateCylinder(\"${1:cylinder}\", {height: ${2:2}, diameter: ${3:1}}, scene);",
+    "language" : "javascript"
+  },
+  {
+    "label" : "Create a ground plane",
+    "documentation" : "https://doc.babylonjs.com/how_to/set_shapes",
+    "insertText" : "var ground = BABYLON.MeshBuilder.CreateGround(\"${1:ground}\", {width: ${2:6}, height: ${3:6}}, scene);",
+    "language" : "javascript"
+  },
+  {
+    "label" : "Create an Arc Rotate Camera w/Degrees",
+    "documentation" : "https://doc.babylonjs.com/babylon101/cameras#arc-rotate-camera",
+    "insertText" : "var camera = new BABYLON.ArcRotateCamera(\"${1:camera}\", BABYLON.Tools.ToRadians(${2:90}), BABYLON.Tools.ToRadians(${3:65}), ${4:10}, ${5:BABYLON.Vector3.Zero()}, scene);",
+    "language" : "javascript"
+  },
+  {
+  "label" : "Create an Arc Rotate Camera w/Radians",
+  "documentation" : "https://doc.babylonjs.com/babylon101/cameras#arc-rotate-camera",
+  "insertText" : "var camera = new BABYLON.ArcRotateCamera(\"${1:camera}\", ${2:0}, ${3:Math.PI/2}, ${4:10}, ${5:BABYLON.Vector3.Zero()}, scene);",
+  "language" : "javascript"
+  },
+  {
+  "label" : "Import a Mesh w/callback",
+  "documentation" : "https://doc.babylonjs.com/resources/external_pg_assets",
+  "insertText" : "BABYLON.SceneLoader.ImportMesh(\"${1:meshName}\", \"${2:url to the mesh parent directory}\", \"${3:Mesh filename.fileextension}\", scene, function(newMeshes){\n\n});",
+  "language" : "javascript"
+  }
+]

+ 2 - 1
Tools/Config/config.json

@@ -142,7 +142,8 @@
             },
             "es6": {
                 "packageName": "@babylonjs/core",
-                "readme": "readme-es6.md"
+                "readme": "readme-es6.md",
+                "license": "license.md"
             }
         }
     },

+ 11 - 0
Tools/Publisher/tasks/processEs6Packages.js

@@ -51,6 +51,13 @@ function processEs6Packages(version) {
             fs.copySync(source, destination);
         }
 
+        if (es6Config.license) {
+            let source = path.join(config.computed.rootFolder, es6Config.readme);
+            let destination = path.join(packagePath, "license.md");
+            colorConsole.log("    Copy es6 license file: ", source.cyan, destination.cyan);
+            fs.copySync(source, destination);
+        }
+
         umdPackageJson.name = es6Config.packageName;
         umdPackageJson.version = version;
         umdPackageJson.main = es6Config.index || "index.js";
@@ -68,6 +75,10 @@ function processEs6Packages(version) {
             umdPackageJson.files = files;
         }
 
+        if (es6Config.license && umdPackageJson.files.indexOf("license.md") === -1) {
+            umdPackageJson.files.push("license.md");
+        }
+
         ["dependencies", "peerDependencies", "devDependencies"].forEach(key => {
             if (umdPackageJson[key]) {
                 let dependencies = umdPackageJson[key];

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 11 - 11
dist/preview release/ammo.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/ammo.wasm.js


binární
dist/preview release/ammo.wasm.wasm


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1298 - 105
dist/preview release/babylon.d.ts


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 2
dist/preview release/babylon.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 4540 - 589
dist/preview release/babylon.max.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/babylon.max.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2687 - 195
dist/preview release/babylon.module.d.ts


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1302 - 105
dist/preview release/documentation.d.ts


+ 1 - 1
dist/preview release/glTF2Interface/package.json

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 4 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -822,6 +822,10 @@ declare module BABYLON.GUI {
          */
         onAfterDrawObservable: BABYLON.Observable<Control>;
         /**
+        * An event triggered when the control has been disposed
+        */
+        onDisposeObservable: BABYLON.Observable<Control>;
+        /**
          * Get the hosting AdvancedDynamicTexture
          */
         get host(): AdvancedDynamicTexture;

+ 7 - 0
dist/preview release/gui/babylon.gui.js

@@ -3870,6 +3870,10 @@ var Control = /** @class */ (function () {
          * An event triggered after the control was drawn
          */
         this.onAfterDrawObservable = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]();
+        /**
+        * An event triggered when the control has been disposed
+        */
+        this.onDisposeObservable = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["Observable"]();
         this._tmpMeasureA = new _measure__WEBPACK_IMPORTED_MODULE_2__["Measure"](0, 0, 0, 0);
     }
     Object.defineProperty(Control.prototype, "shadowOffsetX", {
@@ -5485,6 +5489,9 @@ var Control = /** @class */ (function () {
                 this.linkWithMesh(null);
             }
         }
+        // Callback
+        this.onDisposeObservable.notifyObservers(this);
+        this.onDisposeObservable.clear();
     };
     Object.defineProperty(Control, "HORIZONTAL_ALIGNMENT_LEFT", {
         /** HORIZONTAL_ALIGNMENT_LEFT */

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/gui/babylon.gui.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 2
dist/preview release/gui/babylon.gui.min.js


+ 8 - 0
dist/preview release/gui/babylon.gui.module.d.ts

@@ -853,6 +853,10 @@ declare module "babylonjs-gui/2D/controls/control" {
          */
         onAfterDrawObservable: Observable<Control>;
         /**
+        * An event triggered when the control has been disposed
+        */
+        onDisposeObservable: Observable<Control>;
+        /**
          * Get the hosting AdvancedDynamicTexture
          */
         get host(): AdvancedDynamicTexture;
@@ -5193,6 +5197,10 @@ declare module BABYLON.GUI {
          */
         onAfterDrawObservable: BABYLON.Observable<Control>;
         /**
+        * An event triggered when the control has been disposed
+        */
+        onDisposeObservable: BABYLON.Observable<Control>;
+        /**
          * Get the hosting AdvancedDynamicTexture
          */
         get host(): AdvancedDynamicTexture;

+ 2 - 2
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.11"
+        "babylonjs": "4.2.0-alpha.12"
     },
     "engines": {
         "node": "*"

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 7 - 7
dist/preview release/inspector/babylon.inspector.bundle.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 157 - 3
dist/preview release/inspector/babylon.inspector.bundle.max.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js.map


+ 42 - 0
dist/preview release/inspector/babylon.inspector.d.ts

@@ -35,6 +35,7 @@ declare module INSPECTOR {
         onTabChangedObservable: BABYLON.Observable<number>;
         onSelectionRenamedObservable: BABYLON.Observable<void>;
         onPluginActivatedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync>>;
+        onNewSceneObservable: BABYLON.Observable<BABYLON.Scene>;
         sceneImportDefaults: {
             [key: string]: any;
         };
@@ -510,6 +511,42 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface IAnimationCurveEditorComponentProps {
+        close: (event: any) => void;
+        title: string;
+    }
+    export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
+        isOpen: boolean;
+    }> {
+        constructor(props: IAnimationCurveEditorComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface IPopupComponentProps {
+        id: string;
+        title: string;
+        size: {
+            width: number;
+            height: number;
+        };
+        onOpen: (window: Window) => void;
+        onClose: (window: Window) => void;
+    }
+    export class PopupComponent extends React.Component<IPopupComponentProps, {
+        isComponentMounted: boolean;
+        blockedByBrowser: boolean;
+    }> {
+        private _container;
+        private _window;
+        constructor(props: IPopupComponentProps);
+        componentDidMount(): void;
+        openPopup(): void;
+        componentWillUnmount(): void;
+        render(): React.ReactPortal | null;
+    }
+}
+declare module INSPECTOR {
     interface IAnimationGridComponentProps {
         globalState: GlobalState;
         animatable: BABYLON.IAnimatable;
@@ -526,6 +563,7 @@ declare module INSPECTOR {
         private _onBeforeRenderObserver;
         private _isPlaying;
         private timelineRef;
+        private _isCurveEditorOpen;
         private _animationControl;
         constructor(props: IAnimationGridComponentProps);
         playOrPause(): void;
@@ -533,6 +571,8 @@ declare module INSPECTOR {
         componentWillUnmount(): void;
         onCurrentFrameChange(value: number): void;
         onChangeFromOrTo(): void;
+        onOpenAnimationCurveEditor(): void;
+        onCloseAnimationCurveEditor(window: Window | null): void;
         render(): JSX.Element;
     }
 }
@@ -2126,6 +2166,7 @@ declare module INSPECTOR {
         private _onSelectionChangeObserver;
         private _onSelectionRenamedObserver;
         private _onNewSceneAddedObserver;
+        private _onNewSceneObserver;
         private sceneExplorerRef;
         private _once;
         private _hooked;
@@ -2193,6 +2234,7 @@ declare module INSPECTOR {
         static get IsVisible(): boolean;
         static EarlyAttachToLoader(): void;
         static Show(scene: BABYLON.Scene, userOptions: Partial<BABYLON.IInspectorOptions>): void;
+        static _SetNewScene(scene: BABYLON.Scene): void;
         static _CreateCanvasContainer(parentControl: HTMLElement): void;
         private static _DestroyCanvasContainer;
         private static _Cleanup;

+ 86 - 0
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -48,6 +48,7 @@ declare module "babylonjs-inspector/components/globalState" {
         onTabChangedObservable: Observable<number>;
         onSelectionRenamedObservable: Observable<void>;
         onPluginActivatedObserver: Nullable<Observer<ISceneLoaderPlugin | ISceneLoaderPluginAsync>>;
+        onNewSceneObservable: Observable<Scene>;
         sceneImportDefaults: {
             [key: string]: any;
         };
@@ -578,6 +579,44 @@ declare module "babylonjs-inspector/components/actionTabs/lines/floatLineCompone
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent" {
+    import * as React from "react";
+    interface IAnimationCurveEditorComponentProps {
+        close: (event: any) => void;
+        title: string;
+    }
+    export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
+        isOpen: boolean;
+    }> {
+        constructor(props: IAnimationCurveEditorComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/popupComponent" {
+    import * as React from "react";
+    interface IPopupComponentProps {
+        id: string;
+        title: string;
+        size: {
+            width: number;
+            height: number;
+        };
+        onOpen: (window: Window) => void;
+        onClose: (window: Window) => void;
+    }
+    export class PopupComponent extends React.Component<IPopupComponentProps, {
+        isComponentMounted: boolean;
+        blockedByBrowser: boolean;
+    }> {
+        private _container;
+        private _window;
+        constructor(props: IPopupComponentProps);
+        componentDidMount(): void;
+        openPopup(): void;
+        componentWillUnmount(): void;
+        render(): React.ReactPortal | null;
+    }
+}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent" {
     import * as React from "react";
     import { Observable } from "babylonjs/Misc/observable";
@@ -602,6 +641,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         private _onBeforeRenderObserver;
         private _isPlaying;
         private timelineRef;
+        private _isCurveEditorOpen;
         private _animationControl;
         constructor(props: IAnimationGridComponentProps);
         playOrPause(): void;
@@ -609,6 +649,8 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         componentWillUnmount(): void;
         onCurrentFrameChange(value: number): void;
         onChangeFromOrTo(): void;
+        onOpenAnimationCurveEditor(): void;
+        onCloseAnimationCurveEditor(window: Window | null): void;
         render(): JSX.Element;
     }
 }
@@ -2683,6 +2725,7 @@ declare module "babylonjs-inspector/components/sceneExplorer/sceneExplorerCompon
         private _onSelectionChangeObserver;
         private _onSelectionRenamedObserver;
         private _onNewSceneAddedObserver;
+        private _onNewSceneObserver;
         private sceneExplorerRef;
         private _once;
         private _hooked;
@@ -2758,6 +2801,7 @@ declare module "babylonjs-inspector/inspector" {
         static get IsVisible(): boolean;
         static EarlyAttachToLoader(): void;
         static Show(scene: Scene, userOptions: Partial<IInspectorOptions>): void;
+        static _SetNewScene(scene: Scene): void;
         static _CreateCanvasContainer(parentControl: HTMLElement): void;
         private static _DestroyCanvasContainer;
         private static _Cleanup;
@@ -2811,6 +2855,7 @@ declare module INSPECTOR {
         onTabChangedObservable: BABYLON.Observable<number>;
         onSelectionRenamedObservable: BABYLON.Observable<void>;
         onPluginActivatedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync>>;
+        onNewSceneObservable: BABYLON.Observable<BABYLON.Scene>;
         sceneImportDefaults: {
             [key: string]: any;
         };
@@ -3286,6 +3331,42 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface IAnimationCurveEditorComponentProps {
+        close: (event: any) => void;
+        title: string;
+    }
+    export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
+        isOpen: boolean;
+    }> {
+        constructor(props: IAnimationCurveEditorComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface IPopupComponentProps {
+        id: string;
+        title: string;
+        size: {
+            width: number;
+            height: number;
+        };
+        onOpen: (window: Window) => void;
+        onClose: (window: Window) => void;
+    }
+    export class PopupComponent extends React.Component<IPopupComponentProps, {
+        isComponentMounted: boolean;
+        blockedByBrowser: boolean;
+    }> {
+        private _container;
+        private _window;
+        constructor(props: IPopupComponentProps);
+        componentDidMount(): void;
+        openPopup(): void;
+        componentWillUnmount(): void;
+        render(): React.ReactPortal | null;
+    }
+}
+declare module INSPECTOR {
     interface IAnimationGridComponentProps {
         globalState: GlobalState;
         animatable: BABYLON.IAnimatable;
@@ -3302,6 +3383,7 @@ declare module INSPECTOR {
         private _onBeforeRenderObserver;
         private _isPlaying;
         private timelineRef;
+        private _isCurveEditorOpen;
         private _animationControl;
         constructor(props: IAnimationGridComponentProps);
         playOrPause(): void;
@@ -3309,6 +3391,8 @@ declare module INSPECTOR {
         componentWillUnmount(): void;
         onCurrentFrameChange(value: number): void;
         onChangeFromOrTo(): void;
+        onOpenAnimationCurveEditor(): void;
+        onCloseAnimationCurveEditor(window: Window | null): void;
         render(): JSX.Element;
     }
 }
@@ -4902,6 +4986,7 @@ declare module INSPECTOR {
         private _onSelectionChangeObserver;
         private _onSelectionRenamedObserver;
         private _onNewSceneAddedObserver;
+        private _onNewSceneObserver;
         private sceneExplorerRef;
         private _once;
         private _hooked;
@@ -4969,6 +5054,7 @@ declare module INSPECTOR {
         static get IsVisible(): boolean;
         static EarlyAttachToLoader(): void;
         static Show(scene: BABYLON.Scene, userOptions: Partial<BABYLON.IInspectorOptions>): void;
+        static _SetNewScene(scene: BABYLON.Scene): void;
         static _CreateCanvasContainer(parentControl: HTMLElement): void;
         private static _DestroyCanvasContainer;
         private static _Cleanup;

+ 7 - 7
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -29,12 +29,12 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.11",
-        "babylonjs-gui": "4.2.0-alpha.11",
-        "babylonjs-loaders": "4.2.0-alpha.11",
-        "babylonjs-materials": "4.2.0-alpha.11",
-        "babylonjs-serializers": "4.2.0-alpha.11",
-        "babylonjs-gltf2interface": "4.2.0-alpha.11"
+        "babylonjs": "4.2.0-alpha.12",
+        "babylonjs-gui": "4.2.0-alpha.12",
+        "babylonjs-loaders": "4.2.0-alpha.12",
+        "babylonjs-materials": "4.2.0-alpha.12",
+        "babylonjs-serializers": "4.2.0-alpha.12",
+        "babylonjs-gltf2interface": "4.2.0-alpha.12"
     },
     "devDependencies": {
         "@types/react": "~16.7.3",

+ 3 - 3
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.2.0-alpha.11",
-        "babylonjs": "4.2.0-alpha.11"
+        "babylonjs-gltf2interface": "4.2.0-alpha.12",
+        "babylonjs": "4.2.0-alpha.12"
     },
     "engines": {
         "node": "*"

+ 9 - 2
dist/preview release/materialsLibrary/babylon.customMaterial.js

@@ -485,8 +485,10 @@ var CustomMaterial = /** @class */ (function (_super) {
             .replace('#define CUSTOM_FRAGMENT_UPDATE_DIFFUSE', (this.CustomParts.Fragment_Custom_Diffuse ? this.CustomParts.Fragment_Custom_Diffuse : ""))
             .replace('#define CUSTOM_FRAGMENT_UPDATE_ALPHA', (this.CustomParts.Fragment_Custom_Alpha ? this.CustomParts.Fragment_Custom_Alpha : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_LIGHTS', (this.CustomParts.Fragment_Before_Lights ? this.CustomParts.Fragment_Before_Lights : ""))
-            .replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', (this.CustomParts.Fragment_Before_Fog ? this.CustomParts.Fragment_Before_Fog : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR', (this.CustomParts.Fragment_Before_FragColor ? this.CustomParts.Fragment_Before_FragColor : ""));
+        if (this.CustomParts.Fragment_Before_Fog) {
+            babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore[name + "PixelShader"] = babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore[name + "PixelShader"].replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', this.CustomParts.Fragment_Before_Fog);
+        }
         this._isCreatedShader = true;
         this._createdShaderName = name;
         return name;
@@ -645,6 +647,9 @@ var PBRCustomMaterial = /** @class */ (function (_super) {
         _this.customShaderNameResolve = _this.Builder;
         _this.FragmentShader = babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore["pbrPixelShader"];
         _this.VertexShader = babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore["pbrVertexShader"];
+        _this.FragmentShader = _this.FragmentShader.replace(/#include<pbrBlockAlbedoOpacity>/g, babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].IncludesShadersStore["pbrBlockAlbedoOpacity"]);
+        _this.FragmentShader = _this.FragmentShader.replace(/#include<pbrBlockReflectivity>/g, babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].IncludesShadersStore["pbrBlockReflectivity"]);
+        _this.FragmentShader = _this.FragmentShader.replace(/#include<pbrBlockFinalColorComposition>/g, babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].IncludesShadersStore["pbrBlockFinalColorComposition"]);
         return _this;
     }
     PBRCustomMaterial.prototype.AttachAfterBind = function (mesh, effect) {
@@ -737,8 +742,10 @@ var PBRCustomMaterial = /** @class */ (function (_super) {
             .replace('#define CUSTOM_FRAGMENT_BEFORE_LIGHTS', (this.CustomParts.Fragment_Before_Lights ? this.CustomParts.Fragment_Before_Lights : ""))
             .replace('#define CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS', (this.CustomParts.Fragment_Custom_MetallicRoughness ? this.CustomParts.Fragment_Custom_MetallicRoughness : ""))
             .replace('#define CUSTOM_FRAGMENT_UPDATE_MICROSURFACE', (this.CustomParts.Fragment_Custom_MicroSurface ? this.CustomParts.Fragment_Custom_MicroSurface : ""))
-            .replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', (this.CustomParts.Fragment_Before_Fog ? this.CustomParts.Fragment_Before_Fog : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR', (this.CustomParts.Fragment_Before_FragColor ? this.CustomParts.Fragment_Before_FragColor : ""));
+        if (this.CustomParts.Fragment_Before_Fog) {
+            babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore[name + "PixelShader"] = babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore[name + "PixelShader"].replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', this.CustomParts.Fragment_Before_Fog);
+        }
         this._isCreatedShader = true;
         this._createdShaderName = name;
         return name;

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


+ 9 - 2
dist/preview release/materialsLibrary/babylonjs.materials.js

@@ -873,8 +873,10 @@ var CustomMaterial = /** @class */ (function (_super) {
             .replace('#define CUSTOM_FRAGMENT_UPDATE_DIFFUSE', (this.CustomParts.Fragment_Custom_Diffuse ? this.CustomParts.Fragment_Custom_Diffuse : ""))
             .replace('#define CUSTOM_FRAGMENT_UPDATE_ALPHA', (this.CustomParts.Fragment_Custom_Alpha ? this.CustomParts.Fragment_Custom_Alpha : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_LIGHTS', (this.CustomParts.Fragment_Before_Lights ? this.CustomParts.Fragment_Before_Lights : ""))
-            .replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', (this.CustomParts.Fragment_Before_Fog ? this.CustomParts.Fragment_Before_Fog : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR', (this.CustomParts.Fragment_Before_FragColor ? this.CustomParts.Fragment_Before_FragColor : ""));
+        if (this.CustomParts.Fragment_Before_Fog) {
+            babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore[name + "PixelShader"] = babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore[name + "PixelShader"].replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', this.CustomParts.Fragment_Before_Fog);
+        }
         this._isCreatedShader = true;
         this._createdShaderName = name;
         return name;
@@ -1033,6 +1035,9 @@ var PBRCustomMaterial = /** @class */ (function (_super) {
         _this.customShaderNameResolve = _this.Builder;
         _this.FragmentShader = babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore["pbrPixelShader"];
         _this.VertexShader = babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore["pbrVertexShader"];
+        _this.FragmentShader = _this.FragmentShader.replace(/#include<pbrBlockAlbedoOpacity>/g, babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].IncludesShadersStore["pbrBlockAlbedoOpacity"]);
+        _this.FragmentShader = _this.FragmentShader.replace(/#include<pbrBlockReflectivity>/g, babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].IncludesShadersStore["pbrBlockReflectivity"]);
+        _this.FragmentShader = _this.FragmentShader.replace(/#include<pbrBlockFinalColorComposition>/g, babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].IncludesShadersStore["pbrBlockFinalColorComposition"]);
         return _this;
     }
     PBRCustomMaterial.prototype.AttachAfterBind = function (mesh, effect) {
@@ -1125,8 +1130,10 @@ var PBRCustomMaterial = /** @class */ (function (_super) {
             .replace('#define CUSTOM_FRAGMENT_BEFORE_LIGHTS', (this.CustomParts.Fragment_Before_Lights ? this.CustomParts.Fragment_Before_Lights : ""))
             .replace('#define CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS', (this.CustomParts.Fragment_Custom_MetallicRoughness ? this.CustomParts.Fragment_Custom_MetallicRoughness : ""))
             .replace('#define CUSTOM_FRAGMENT_UPDATE_MICROSURFACE', (this.CustomParts.Fragment_Custom_MicroSurface ? this.CustomParts.Fragment_Custom_MicroSurface : ""))
-            .replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', (this.CustomParts.Fragment_Before_Fog ? this.CustomParts.Fragment_Before_Fog : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR', (this.CustomParts.Fragment_Before_FragColor ? this.CustomParts.Fragment_Before_FragColor : ""));
+        if (this.CustomParts.Fragment_Before_Fog) {
+            babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore[name + "PixelShader"] = babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_1__["Effect"].ShadersStore[name + "PixelShader"].replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', this.CustomParts.Fragment_Before_Fog);
+        }
         this._isCreatedShader = true;
         this._createdShaderName = name;
         return name;

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/materialsLibrary/babylonjs.materials.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/materialsLibrary/babylonjs.materials.min.js


+ 2 - 2
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.11"
+        "babylonjs": "4.2.0-alpha.12"
     },
     "engines": {
         "node": "*"

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 60 - 39
dist/preview release/nodeEditor/babylon.nodeEditor.d.ts


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 7 - 7
dist/preview release/nodeEditor/babylon.nodeEditor.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 513 - 320
dist/preview release/nodeEditor/babylon.nodeEditor.max.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 137 - 88
dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts


+ 2 - 2
dist/preview release/nodeEditor/package.json

@@ -4,14 +4,14 @@
     },
     "name": "babylonjs-node-editor",
     "description": "The Babylon.js node material editor.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
     },
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.11"
+        "babylonjs": "4.2.0-alpha.12"
     },
     "files": [
         "babylon.nodeEditor.max.js.map",

+ 1 - 1
dist/preview release/package.json

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/packagesSizeBaseLine.json

@@ -1 +1 @@
-{"thinEngineOnly":115611,"engineOnly":152019,"sceneOnly":510942,"minGridMaterial":643640,"minStandardMaterial":785002}
+{"thinEngineOnly":115670,"engineOnly":152078,"sceneOnly":511105,"minGridMaterial":644201,"minStandardMaterial":785874}

+ 2 - 2
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.11"
+        "babylonjs": "4.2.0-alpha.12"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.11"
+        "babylonjs": "4.2.0-alpha.12"
     },
     "engines": {
         "node": "*"

+ 3 - 3
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.11",
-        "babylonjs-gltf2interface": "4.2.0-alpha.11"
+        "babylonjs": "4.2.0-alpha.12",
+        "babylonjs-gltf2interface": "4.2.0-alpha.12"
     },
     "engines": {
         "node": "*"

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2687 - 195
dist/preview release/viewer/babylon.module.d.ts


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 190 - 126
dist/preview release/viewer/babylon.viewer.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


+ 51 - 22
dist/preview release/what's new.md

@@ -2,19 +2,22 @@
 
 ## Major updates
 
-- Added particle editor to the Inspector ([Deltakosh](https://github.com/deltakosh)
+- Added particle editor to the Inspector ([Deltakosh](https://github.com/deltakosh))
 - Added the `ShadowDepthWrapper` class to support accurate shadow generation for custom as well as node material shaders. [Doc](https://doc.babylonjs.com/babylon101/shadows#custom-shadow-map-shaders) ([Popov72](https://github.com/Popov72))
 
 ## Updates
 
 ### General
 
-- Refactored React refs from old string API to React.createRef() API ([belfortk](https://github.com/belfortk)
+- Refactored React refs from old string API to React.createRef() API ([belfortk](https://github.com/belfortk))
 - Scale on one axis for `BoundingBoxGizmo` ([cedricguillemet](https://github.com/cedricguillemet))
 - Simplified code contributions by fully automating the dev setup with gitpod ([nisarhassan12](https://github.com/nisarhassan12))
 - Add a `CascadedShadowMap.IsSupported` method and log an error instead of throwing an exception when CSM is not supported ([Popov72](https://github.com/Popov72))
 - Added initial code for DeviceInputSystem ([PolygonalSun](https://github.com/PolygonalSun))
 - Added support for `material.disableColorWrite` ([Deltakosh](https://github.com/deltakosh))
+- The Mesh Asset Task also accepts File as sceneInput ([RaananW](https://github.com/RaananW))
+- Added support preserving vert colors for CSG objects ([PirateJC](https://github.com/PirateJC))
+- Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 
 ### Engine
 
@@ -24,8 +27,10 @@
 
 ### NME
 
-- 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)
+- 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
 
@@ -34,6 +39,8 @@
 - Edit all textures (anisotropic, clear coat, sheen, ...) for the PBR materials ([Popov72](https://github.com/Popov72))
 - Added right click options to create PBR and Standard Materials ([Deltakosh](https://github.com/deltakosh))
 - Added support for recording GIF ([Deltakosh](https://github.com/deltakosh))
+- Popup Window available (To be used in Curve Editor) ([pixelspace](https://github.com/devpixelspace))
+- Add support to update inspector when switching to new scene ([belfortk](https://github.com/belfortk))
 
 ### Cameras
 
@@ -48,8 +55,10 @@
 ### Physics
 
 - Fixed time steps or delta time with sub time step for Oimo.js and Cannon.js ([cedricguillemet](https://github.com/cedricguillemet))
+- Ammo.js collision group and mask supported by impostor parameters ([cedricguillemet](https://github.com/cedricguillemet))
 - Ammo.js IDL exposed property update and raycast vehicle stablization support ([MackeyK24](https://github.com/MackeyK24))
 - Recast.js plugin nav mesh and crowd agent to ref performance optimizations. ([MackeyK24](https://github.com/MackeyK24))
+- Added `scene.physicsEnabled` boolean ([Deltakosh](https://github.com/deltakosh))
 
 ### Loaders
 
@@ -83,6 +92,10 @@
 - Support for pointer selection and teleportation in right handed systems ([#7967](https://github.com/BabylonJS/Babylon.js/issues/7967)) ([RaananW](https://github.com/RaananW))
 - Pointer Selection feature now uses `selectstart` and `selectend` events when gamepad and motion controller are not present ([#7989](https://github.com/BabylonJS/Babylon.js/issues/7989)) ([RaananW](https://github.com/RaananW))
 - Removed forced `autoClear` = false settings ([RaananW](https://github.com/RaananW))
+- Added a warning that WebXR can only be served over HTTPS ([RaananW](https://github.com/RaananW))
+- Default (XR-global) rendering group ID can be defined when initializing a default experience ([RaananW](https://github.com/RaananW))
+- Added support for (experimental) haptic actuators ([#8068](https://github.com/BabylonJS/Babylon.js/issues/8068)) ([RaananW](https://github.com/RaananW))
+- It is now possible to enable experimental (AR) features using the options of the default xr helper ([RaananW](https://github.com/RaananW))
 
 ### Collisions
 
@@ -93,38 +106,48 @@
 - Added support for Additive Animation Blending. Existing animations can be converted to additive using the new MakeAnimationAdditive method for Skeletons, AnimationGroups and Animations. Animations can be played additively using the new isAdditive input parameter to the begin animation methods. ([c-morten](https://github.com/c-morten))
 
 ### Maths
+
 - Added `Vector3.projectOnPlaneToRef` ([Deltakosh](https://github.com/deltakosh))
 
+### Particles
+
+- Added local space support for GPU particles ([CraigFeldpsar](https://github.com/craigfeldspar))
+- Added ability to update also colors and uvs of solid particle vertices ([jerome](https://github.com/jbousquie))
+
 ### Build
 
 - Fixed an issue with gulp webpack, webpack stream and the viewer ([RaananW](https://github.com/RaananW))
 
+### Playground
+
+- Added support for code templates in the playground ([sailro](http://www.github.com/sailro))
+
 ## Bugs
 
-- Fix infinite loop in `GlowLayer.unReferenceMeshFromUsingItsOwnMaterial` ([Popov72](https://github.com/Popov72)
+- 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))
-- `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)
-- Fix: when using instances, master mesh (if displayed) does not have correct instance buffer values ([Popov72](https://github.com/Popov72)
+- 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))
+- Fix: when using instances, master mesh (if displayed) does not have correct instance buffer values ([Popov72](https://github.com/Popov72))
 - Exit XR will only trigger only if state is IN_XR ([RaananW](https://github.com/RaananW))
 - Fix improper baking of transformed textures in `KHR_texture_transform` serializer. ([drigax](https://github.com/Drigax))
 - Fixed NME codegen: missing common properties for float-value input block. ([ycw](https://github.com/ycw))
 - Fixed missing options for MeshBuilder.CreateBox. ([ycw](https://github.com/ycw))
-- Fix bug in `Plane.transform` when matrix passed in is not a pure rotation ([Popov72](https://github.com/Popov72)
-- Fix bug in PBR when anisotropy is enabled and no bump texture is provided ([Popov72](https://github.com/Popov72)
-- Fix horizon occlusion in PBR materials ([Popov72](https://github.com/Popov72)
+- Fix bug in `Plane.transform` when matrix passed in is not a pure rotation ([Popov72](https://github.com/Popov72))
+- Fix bug in PBR when anisotropy is enabled and no bump texture is provided ([Popov72](https://github.com/Popov72))
+- Fix horizon occlusion in PBR materials ([Popov72](https://github.com/Popov72))
 - Fix wrong relative position in applyImpulse/applyForce for ammojs plugin ([cedricguillemet](https://github.com/cedricguillemet))
-- Fixed delay calculation in Animatable.goToFrame when speedRatio != 1 ([Reimund Järnfors](https://github.com/reimund)
-- Fix bug in PBR when translucency is enabled and an irradiance texture is provided ([Popov72](https://github.com/Popov72)
-- Fix bug in PBR with translucency when irradiance texture is 2D ([Popov72](https://github.com/Popov72)
-- Fix bug in PBR when specific combinations of parameters are used ([Popov72](https://github.com/Popov72)
+- Fixed delay calculation in Animatable.goToFrame when speedRatio != 1 ([Reimund Järnfors](https://github.com/reimund))
+- Fix bug in PBR when translucency is enabled and an irradiance texture is provided ([Popov72](https://github.com/Popov72))
+- Fix bug in PBR with translucency when irradiance texture is 2D ([Popov72](https://github.com/Popov72))
+- Fix bug in PBR when specific combinations of parameters are used ([Popov72](https://github.com/Popov72))
 - Fix texture being inverted on the Y axis by default when using TextureAsset or AssetManager ([broederj](https://github.com/broederj))
 - Fix `TexturePacker` cross-origin image requests, fix falsy default options ([ludevik](https://github.com/ludevik))
-- Fix freeze (infinite loop) when disposing a scene that loaded some specific gLTF files ([Popov72](https://github.com/Popov72)
-
-- Fix submesh recreation when it should not ([Popov72](https://github.com/Popov72)
-- Fix `CustomMaterial` and `PBRCustomMaterial` not setting uniforms / samplers / attributes ([Popov72](https://github.com/Popov72)
+- Fix freeze (infinite loop) when disposing a scene that loaded some specific gLTF files ([Popov72](https://github.com/Popov72))
+- Fix submesh recreation when it should not ([Popov72](https://github.com/Popov72))
+- Fix `CustomMaterial` and `PBRCustomMaterial` not setting uniforms / samplers / attributes ([Popov72](https://github.com/Popov72))
 - Fix bug in NME where deleting a node from a frame would not remove its ports on the outside of a frame
 - Fix mesh winding order inversion when merging meshes with overridden side orientation ([drigax](https://github.com/Drigax))
 - Fixed a rendering issue with GearVR in WebXR mode ([RaananW](https://github.com/RaananW))
@@ -135,10 +158,16 @@
 - Fix bug in NME where collapsed frames didn't redraw output links to outside nodes ([belfortk](https://github.com/belfortk))
 - Fix bug in NME where links were not redrawn after moving frame port ([belfortk](https://github.com/belfortk))
 - Fix bugs in NME that were causing inconsistent behavior displaying Move Node Up and Down buttons on frame ports ([belfortk](https://github.com/belfortk))
-- Fix bug in `ShaderMaterial` when using morph targets ([Popov72](https://github.com/Popov72)
-- Fix bug in playground where child NME windows would not close before page unload events ([belfortk](https://github.com/belfortk)
+- Fix bug in `ShaderMaterial` when using morph targets ([Popov72](https://github.com/Popov72))
+- Fix bug in playground where child NME windows would not close before page unload events ([belfortk](https://github.com/belfortk))
 - Fixed an issue with stereoscopic rendering ([#8000](https://github.com/BabylonJS/Babylon.js/issues/8000)) ([RaananW](https://github.com/RaananW))
+- Fix bug with multiple scenes when resizing the screen and there's a glow or highlight layer active ([Popov72](https://github.com/Popov72))
+- Fix an error when compiling with the closure compiler ([ageneau](https://github.com/ageneau/))
+- Fix an error in applying texture to sides of `extrudePolygon` using faceUV[1] ([JohnK](https://github.com/BabylonJSGuide/))
+- Playground didn't work if query params were added to the URL ([RaananW](https://github.com/RaananW))
+- Fixed Path3D `_distances` / length computation ([Poolminer](https://github.com/Poolminer))
 
 ## Breaking changes
 
 - `EffectRenderer.render` now takes a `RenderTargetTexture` or an `InternalTexture` as the output texture and only a single `EffectWrapper` for its first argument ([Popov72](https://github.com/Popov72))
+- Sound's `updateOptions` takes `options.length` and `options.offset` as seconds and not milliseconds ([RaananW](https://github.com/RaananW))

+ 1 - 0
dist/what's new.md

@@ -353,6 +353,7 @@
 - Fix for bug where comments would break out of frames and break resizing of frames ([Kyle Belfort](https://github.com/belfortk))
 - Fix for bug where frames without comments would display undefined at the bottom right corner ([Kyle Belfort](https://github.com/belfortk))
 - Fixed an issue in XR where one of the cameras used for rendering got the wrong framebuffer dimensions ([RaananW](https://github.com/RaananW/))
+- Fix bug in `StandardMaterial` and `PBRMaterial` where the mesh visibility value is not applied correctly when the material is frozen ([Popov72](https://github.com/Popov72))
 
 ## Breaking changes
 

+ 1 - 0
gui/src/2D/advancedDynamicTexture.ts

@@ -504,6 +504,7 @@ export class AdvancedDynamicTexture extends DynamicTexture {
         var textureSize = this.getSize();
         var renderWidth = engine.getRenderWidth() * this._renderScale;
         var renderHeight = engine.getRenderHeight() * this._renderScale;
+
         if (this._renderAtIdealSize) {
             if (this._idealWidth) {
                 renderHeight = (renderHeight * this._idealWidth) / renderWidth;

+ 9 - 0
gui/src/2D/controls/control.ts

@@ -281,6 +281,11 @@ export class Control {
     public onAfterDrawObservable = new Observable<Control>();
 
     /**
+    * An event triggered when the control has been disposed
+    */
+   public onDisposeObservable = new Observable<Control>();
+
+    /**
      * Get the hosting AdvancedDynamicTexture
      */
     public get host(): AdvancedDynamicTexture {
@@ -1914,6 +1919,10 @@ export class Control {
                 this.linkWithMesh(null);
             }
         }
+
+        // Callback
+        this.onDisposeObservable.notifyObservers(this);
+        this.onDisposeObservable.clear();
     }
 
     // Statics

+ 2 - 0
inspector/src/components/actionTabs/actionTabs.scss

@@ -1009,6 +1009,8 @@ $line-padding-left: 2px;
                         grid-column: 1;
                         opacity: 1;
                         border: 3px solid red;
+                        margin-bottom: -5px;
+                        z-index: 100;
                         transition: opacity 250ms;
                         pointer-events: none;
                         

+ 1 - 0
inspector/src/components/actionTabs/tabs/debugTabComponent.tsx

@@ -79,6 +79,7 @@ export class DebugTabComponent extends PaneComponent {
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="FEATURES">
                     <CheckBoxLineComponent label="Animations" isSelected={() => scene.animationsEnabled} onSelect={() => scene.animationsEnabled = !scene.animationsEnabled} />
+                    <CheckBoxLineComponent label="Physics" isSelected={() => scene.physicsEnabled} onSelect={() => scene.physicsEnabled = !scene.physicsEnabled} />
                     <CheckBoxLineComponent label="Collisions" isSelected={() => scene.collisionsEnabled} onSelect={() => scene.collisionsEnabled = !scene.collisionsEnabled} />
                     <CheckBoxLineComponent label="Fog" isSelected={() => scene.fogEnabled} onSelect={() => scene.fogEnabled = !scene.fogEnabled} />
                     <CheckBoxLineComponent label="Lens flares" isSelected={() => scene.lensFlaresEnabled} onSelect={() => scene.lensFlaresEnabled = !scene.lensFlaresEnabled} />

+ 28 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -0,0 +1,28 @@
+import * as React from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faTimes } from "@fortawesome/free-solid-svg-icons";
+
+interface IAnimationCurveEditorComponentProps {
+    close: (event: any) => void;
+    title: string;
+}
+
+export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, { isOpen: boolean }> {
+
+    constructor(props: IAnimationCurveEditorComponentProps) {
+        super(props);
+    }
+
+    render() {
+        return (
+            <div>
+                <div className="header">
+                    <div>{this.props.title}</div>
+                    <div style={{width:48, height:48}} className="close" onClick={(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => this.props.close(event)}>
+                        <FontAwesomeIcon icon={faTimes} />
+                    </div>
+                </div>
+            </div>
+        );
+    }
+}

+ 55 - 26
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent.tsx

@@ -18,6 +18,8 @@ import { Nullable } from 'babylonjs/types';
 import { FloatLineComponent } from '../../../lines/floatLineComponent';
 import { TextLineComponent } from '../../../lines/textLineComponent';
 import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
+import { AnimationCurveEditorComponent } from '../animations/animationCurveEditorComponent';
+import { PopupComponent } from '../animations/popupComponent';
 
 interface IAnimationGridComponentProps {
     globalState: GlobalState;
@@ -34,6 +36,7 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
     private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
     private _isPlaying = false;
     private timelineRef: React.RefObject<SliderLineComponent>;
+    private _isCurveEditorOpen = false;
     private _animationControl = {
         from: 0,
         to: 0,
@@ -58,7 +61,7 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                 }
             });
 
-            if (animatableAsAny.animations) {                
+            if (animatableAsAny.animations) {
                 this._animations!.push(...animatableAsAny.animations);
             }
 
@@ -128,6 +131,19 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
         }
     }
 
+    onOpenAnimationCurveEditor() {
+        this._isCurveEditorOpen = true;
+    }
+
+    onCloseAnimationCurveEditor(window: Window | null) {
+        this._isCurveEditorOpen = false;
+        if (window === null) {
+            console.log("Window already closed");
+        } else {
+            window.close();
+        }
+    }
+
     render() {
         const animatable = this.props.animatable;
         const animatableAsAny = this.props.animatable as any;
@@ -170,7 +186,7 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                     <>
                         <LineContainerComponent globalState={this.props.globalState} title="ANIMATIONS">
                             <TextLineComponent label="Count" value={animations.length.toString()} />
-                            <ButtonLineComponent label="Edit" onClick={() => {}} />
+                            <ButtonLineComponent label="Edit" onClick={() => this.onOpenAnimationCurveEditor()} />
                             {
                                 animations.map((anim, i) => {
                                     return (
@@ -178,10 +194,23 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                                     )
                                 })
                             }
+
+                            {
+
+                                this._isCurveEditorOpen && <PopupComponent
+                                    id="curve-editor"
+                                    title="Curve Animation Editor"
+                                    size={{ width: 800, height: 600 }}
+                                    onOpen={(window: Window) => { window.console.log("Window opened!!") }}
+                                    onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}>
+
+                                    <AnimationCurveEditorComponent title="Animations Curve Editor" close={(event) => this.onCloseAnimationCurveEditor(event.view)}/>
+                                </PopupComponent>
+                            }
                         </LineContainerComponent>
                         <LineContainerComponent globalState={this.props.globalState} title="ANIMATION GENERAL CONTROL">
-                            <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="From" target={this._animationControl} propertyName="from" onChange={()=> this.onChangeFromOrTo()} />
-                            <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="To" target={this._animationControl} propertyName="to" onChange={()=> this.onChangeFromOrTo()}/>
+                            <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="From" target={this._animationControl} propertyName="from" onChange={() => this.onChangeFromOrTo()} />
+                            <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="To" target={this._animationControl} propertyName="to" onChange={() => this.onChangeFromOrTo()} />
                             <CheckBoxLineComponent label="Loop" onSelect={value => this._animationControl.loop = value} isSelected={() => this._animationControl.loop} />
                             {
                                 this._isPlaying &&
@@ -189,32 +218,32 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                                     step={(this._animationControl.to - this._animationControl.from) / 1000.0} directValue={this.state.currentFrame}
                                     onInput={value => this.onCurrentFrameChange(value)}
                                 />
-                            }                         
-                            <ButtonLineComponent label={this._isPlaying ? "Stop" : "Play"} onClick={() => this.playOrPause()} />                        
+                            }
+                            <ButtonLineComponent label={this._isPlaying ? "Stop" : "Play"} onClick={() => this.playOrPause()} />
                             {
                                 (this._ranges.length > 0 || this._animations && this._animations.length > 0) &&
-                                    <>
-                                        <CheckBoxLineComponent label="Enable override" onSelect={value => {
-                                            if (value) {
-                                                animatableAsAny.animationPropertiesOverride = new AnimationPropertiesOverride();
-                                                animatableAsAny.animationPropertiesOverride.blendingSpeed = 0.05;
-                                            } else {
-                                                animatableAsAny.animationPropertiesOverride = null;
-                                            }
-                                            this.forceUpdate();
-                                        }} isSelected={() => animatableAsAny.animationPropertiesOverride != null}
-                                            onValueChanged={() => this.forceUpdate()}
-                                        />
-                                        {
-                                            animatableAsAny.animationPropertiesOverride != null &&
-                                            <div>
-                                                <CheckBoxLineComponent label="Enable blending" target={animatableAsAny.animationPropertiesOverride} propertyName="enableBlending" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                                                <SliderLineComponent label="Blending speed" target={animatableAsAny.animationPropertiesOverride} propertyName="blendingSpeed" minimum={0} maximum={0.1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                                            </div>
+                                <>
+                                    <CheckBoxLineComponent label="Enable override" onSelect={value => {
+                                        if (value) {
+                                            animatableAsAny.animationPropertiesOverride = new AnimationPropertiesOverride();
+                                            animatableAsAny.animationPropertiesOverride.blendingSpeed = 0.05;
+                                        } else {
+                                            animatableAsAny.animationPropertiesOverride = null;
                                         }
-                                    </>
+                                        this.forceUpdate();
+                                    }} isSelected={() => animatableAsAny.animationPropertiesOverride != null}
+                                        onValueChanged={() => this.forceUpdate()}
+                                    />
+                                    {
+                                        animatableAsAny.animationPropertiesOverride != null &&
+                                        <div>
+                                            <CheckBoxLineComponent label="Enable blending" target={animatableAsAny.animationPropertiesOverride} propertyName="enableBlending" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                                            <SliderLineComponent label="Blending speed" target={animatableAsAny.animationPropertiesOverride} propertyName="blendingSpeed" minimum={0} maximum={0.1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                                        </div>
+                                    }
+                                </>
                             }
-                        </LineContainerComponent>    
+                        </LineContainerComponent>
                     </>
                 }
             </div>

+ 84 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/popupComponent.tsx

@@ -0,0 +1,84 @@
+import * as React from "react";
+import * as ReactDOM from 'react-dom';
+
+interface IPopupComponentProps {
+    id: string,
+    title: string,
+    size: { width: number, height: number },
+    onOpen: (window: Window) => void,
+    onClose: (window: Window) => void,
+}
+
+export class PopupComponent extends React.Component<IPopupComponentProps, { isComponentMounted: boolean, blockedByBrowser: boolean }> {
+
+    private _container: HTMLDivElement;
+    private _window: Window | null;
+
+    constructor(props: IPopupComponentProps) {
+        super(props);
+
+        this._container = document.createElement('div')
+        this._container.id = this.props.id;
+        this._window;
+
+        this.state = {
+            isComponentMounted: false,
+            blockedByBrowser: false,
+        }
+    }
+
+    componentDidMount() {
+        this.openPopup()
+        this.setState({ isComponentMounted: true });
+    }
+
+    openPopup() {
+        const { title, size, onOpen, onClose } = this.props
+
+        const windowCreationOptionsList = {
+            width: size.width,
+            height: size.height,
+            top: (window.innerHeight - size.width) / 2 + window.screenY,
+            left: (window.innerWidth - size.height) / 2 + window.screenX
+        };
+
+        var windowCreationOptions = Object.keys(windowCreationOptionsList)
+            .map(
+                (key) => key + '=' + (windowCreationOptionsList as any)[key]
+            )
+            .join(',');
+
+        this._window = window.open("", title, windowCreationOptions);
+
+        if (this._window) {
+            this._window.document.title = title;
+            this._window.document.body.appendChild(this._container);
+            onOpen(this._window);
+            this._window.addEventListener('beforeunload', () => this._window && onClose(this._window));
+
+        } else {
+
+            if (!this._window) {
+                this.setState({ blockedByBrowser: true }, () => {
+                    if (this.state.blockedByBrowser) {
+                        alert("You might have blocked popups in your browser");
+                        console.warn("Popup window couldn't be created");
+                    }
+                });
+            }
+        }
+
+    }
+
+    componentWillUnmount() {
+        if (this._window) {
+            this._window.close()
+        }
+    }
+
+    render() {
+        if (!this.state.isComponentMounted) return null
+        return ReactDOM.createPortal(this.props.children, this._container)
+    }
+
+}

+ 1 - 1
inspector/src/components/globalState.ts

@@ -20,7 +20,7 @@ export class GlobalState {
     public onTabChangedObservable = new Observable<number>();
     public onSelectionRenamedObservable = new Observable<void>();
     public onPluginActivatedObserver: Nullable<Observer<ISceneLoaderPlugin | ISceneLoaderPluginAsync>>;
-
+    public onNewSceneObservable = new Observable<Scene>();
     public sceneImportDefaults: { [key: string]: any } = {};
 
     public validationResults: Nullable<IGLTFValidationResults> = null;

+ 10 - 0
inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx

@@ -63,6 +63,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
     private _onSelectionChangeObserver: Nullable<Observer<any>>;
     private _onSelectionRenamedObserver: Nullable<Observer<void>>;
     private _onNewSceneAddedObserver: Nullable<Observer<Scene>>;
+    private _onNewSceneObserver: Nullable<Observer<Scene>>;
     private sceneExplorerRef: React.RefObject<Resizable>;
 
 
@@ -79,6 +80,11 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
         this.sceneMutationFunc = this.processMutation.bind(this);
 
         this.sceneExplorerRef = React.createRef();
+        this._onNewSceneObserver = this.props.globalState.onNewSceneObservable.add((scene: Scene) => {
+            this.setState({
+                scene
+            });
+        })
     }
 
     processMutation() {
@@ -114,6 +120,10 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             EngineStore.LastCreatedEngine!.onNewSceneAddedObservable.remove(this._onNewSceneAddedObserver);
         }
 
+        if(this._onNewSceneObserver){
+            this.props.globalState.onNewSceneObservable.remove(this._onNewSceneObserver);
+        }
+
         const scene = this.state.scene;
 
         scene.onNewSkeletonAddedObservable.removeCallback(this.sceneMutationFunc);

+ 5 - 0
inspector/src/inspector.ts

@@ -417,6 +417,11 @@ export class Inspector {
         }
     }
 
+    public static _SetNewScene(scene: Scene) {
+        this._Scene = scene;
+        this._GlobalState.onNewSceneObservable.notifyObservers(scene);
+    }
+
     public static _CreateCanvasContainer(parentControl: HTMLElement) {
         // Create a container for previous elements
         this._NewCanvasContainer = parentControl.ownerDocument!.createElement("div");

+ 4 - 1
materialsLibrary/src/custom/customMaterial.ts

@@ -159,9 +159,12 @@ export class CustomMaterial extends StandardMaterial {
             .replace('#define CUSTOM_FRAGMENT_UPDATE_DIFFUSE', (this.CustomParts.Fragment_Custom_Diffuse ? this.CustomParts.Fragment_Custom_Diffuse : ""))
             .replace('#define CUSTOM_FRAGMENT_UPDATE_ALPHA', (this.CustomParts.Fragment_Custom_Alpha ? this.CustomParts.Fragment_Custom_Alpha : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_LIGHTS', (this.CustomParts.Fragment_Before_Lights ? this.CustomParts.Fragment_Before_Lights : ""))
-            .replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', (this.CustomParts.Fragment_Before_Fog ? this.CustomParts.Fragment_Before_Fog : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR', (this.CustomParts.Fragment_Before_FragColor ? this.CustomParts.Fragment_Before_FragColor : ""));
 
+        if (this.CustomParts.Fragment_Before_Fog) {
+            Effect.ShadersStore[name + "PixelShader"] = Effect.ShadersStore[name + "PixelShader"].replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', this.CustomParts.Fragment_Before_Fog);
+        }
+
         this._isCreatedShader = true;
         this._createdShaderName = name;
 

+ 8 - 1
materialsLibrary/src/custom/pbrCustomMaterial.ts

@@ -157,9 +157,12 @@ export class PBRCustomMaterial extends PBRMaterial {
             .replace('#define CUSTOM_FRAGMENT_BEFORE_LIGHTS', (this.CustomParts.Fragment_Before_Lights ? this.CustomParts.Fragment_Before_Lights : ""))
             .replace('#define CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS', (this.CustomParts.Fragment_Custom_MetallicRoughness ? this.CustomParts.Fragment_Custom_MetallicRoughness : ""))
             .replace('#define CUSTOM_FRAGMENT_UPDATE_MICROSURFACE', (this.CustomParts.Fragment_Custom_MicroSurface ? this.CustomParts.Fragment_Custom_MicroSurface : ""))
-            .replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', (this.CustomParts.Fragment_Before_Fog ? this.CustomParts.Fragment_Before_Fog : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR', (this.CustomParts.Fragment_Before_FragColor ? this.CustomParts.Fragment_Before_FragColor : ""));
 
+        if (this.CustomParts.Fragment_Before_Fog) {
+            Effect.ShadersStore[name + "PixelShader"] = Effect.ShadersStore[name + "PixelShader"].replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', this.CustomParts.Fragment_Before_Fog);
+        }
+
         this._isCreatedShader = true;
         this._createdShaderName = name;
 
@@ -173,6 +176,10 @@ export class PBRCustomMaterial extends PBRMaterial {
 
         this.FragmentShader = Effect.ShadersStore["pbrPixelShader"];
         this.VertexShader = Effect.ShadersStore["pbrVertexShader"];
+
+        this.FragmentShader = this.FragmentShader.replace(/#include<pbrBlockAlbedoOpacity>/g, Effect.IncludesShadersStore["pbrBlockAlbedoOpacity"]);
+        this.FragmentShader = this.FragmentShader.replace(/#include<pbrBlockReflectivity>/g, Effect.IncludesShadersStore["pbrBlockReflectivity"]);
+        this.FragmentShader = this.FragmentShader.replace(/#include<pbrBlockFinalColorComposition>/g, Effect.IncludesShadersStore["pbrBlockFinalColorComposition"]);
     }
 
     public AddUniform(name: string, kind: string, param: any): PBRCustomMaterial {

+ 104 - 77
nodeEditor/src/blockTools.ts

@@ -62,20 +62,29 @@ import { DerivativeBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/deriva
 import { RefractBlock } from 'babylonjs/Materials/Node/Blocks/refractBlock';
 import { ReflectBlock } from 'babylonjs/Materials/Node/Blocks/reflectBlock';
 import { DesaturateBlock } from 'babylonjs/Materials/Node/Blocks/desaturateBlock';
+import { PBRMetallicRoughnessBlock } from 'babylonjs/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock';
+import { SheenBlock } from 'babylonjs/Materials/Node/Blocks/PBR/sheenBlock';
+import { AmbientOcclusionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/ambientOcclusionBlock';
+import { ReflectivityBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectivityBlock';
+import { AnisotropyBlock } from 'babylonjs/Materials/Node/Blocks/PBR/anisotropyBlock';
+import { ReflectionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectionBlock';
+import { ClearCoatBlock } from 'babylonjs/Materials/Node/Blocks/PBR/clearCoatBlock';
+import { RefractionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/refractionBlock';
+import { SubSurfaceBlock } from 'babylonjs/Materials/Node/Blocks/PBR/subSurfaceBlock';
 
 export class BlockTools {
     public static GetBlockFromString(data: string, scene: Scene, nodeMaterial: NodeMaterial) {
         switch (data) {
             case "DesaturateBlock":
-                return new DesaturateBlock("Desaturate");                  
+                return new DesaturateBlock("Desaturate");
             case "RefractBlock":
-                return new RefractBlock("Refract");               
+                return new RefractBlock("Refract");
             case "ReflectBlock":
-                return new ReflectBlock("Reflect");              
+                return new ReflectBlock("Reflect");
             case "DerivativeBlock":
-                return new DerivativeBlock("Derivative");               
+                return new DerivativeBlock("Derivative");
             case "Rotate2dBlock":
-                return new Rotate2dBlock("Rotate2d");            
+                return new Rotate2dBlock("Rotate2d");
             case "NormalBlendBlock":
                 return new NormalBlendBlock("NormalBlend");
             case "WorleyNoise3DBlock":
@@ -95,7 +104,7 @@ export class BlockTools {
             case "ColorMergerBlock":
                 return new ColorMergerBlock("ColorMerger");
             case "VectorMergerBlock":
-                return new VectorMergerBlock("VectorMerger");                
+                return new VectorMergerBlock("VectorMerger");
             case "ColorSplitterBlock":
                 return new ColorSplitterBlock("ColorSplitter");
             case "VectorSplitterBlock":
@@ -103,7 +112,7 @@ export class BlockTools {
             case "TextureBlock":
                 return new TextureBlock("Texture");
             case "ReflectionTextureBlock":
-                return new ReflectionTextureBlock("Reflection texture");                
+                return new ReflectionTextureBlock("Reflection texture");
             case "LightBlock":
                 return new LightBlock("Lights");
             case "FogBlock":
@@ -143,45 +152,45 @@ export class BlockTools {
             case "DivideBlock":
                 return new DivideBlock("Divide");
             case "SubtractBlock":
-                return new SubtractBlock("Subtract"); 
+                return new SubtractBlock("Subtract");
             case "StepBlock":
-                return new StepBlock("Step");        
+                return new StepBlock("Step");
             case "SmoothStepBlock":
-                return new SmoothStepBlock("Smooth step");        
+                return new SmoothStepBlock("Smooth step");
             case "OneMinusBlock":
-                return new OneMinusBlock("One minus");          
+                return new OneMinusBlock("One minus");
             case "ReciprocalBlock":
-                return new ReciprocalBlock("Reciprocal");    
+                return new ReciprocalBlock("Reciprocal");
             case "ViewDirectionBlock":
-                return new ViewDirectionBlock("View direction");    
+                return new ViewDirectionBlock("View direction");
             case "LightInformationBlock":
                 let lightInformationBlock = new LightInformationBlock("Light information");
                 lightInformationBlock.light = scene.lights.length ? scene.lights[0] : null;
                 return lightInformationBlock;
             case "MaxBlock":
-                return new MaxBlock("Max");       
+                return new MaxBlock("Max");
             case "MinBlock":
-                return new MinBlock("Min");      
+                return new MinBlock("Min");
             case "LengthBlock":
-                return new LengthBlock("Length");   
+                return new LengthBlock("Length");
             case "DistanceBlock":
-                return new DistanceBlock("Distance");     
+                return new DistanceBlock("Distance");
             case "NegateBlock":
-                return new NegateBlock("Negate");                                     
-            case "PerturbNormalBlock":                                          
-                return new PerturbNormalBlock("Perturb normal");                     
-            case "RandomNumberBlock":                                          
-                return new RandomNumberBlock("Random number");         
-            case "ReplaceColorBlock":                                          
-                return new ReplaceColorBlock("Replace color");      
-            case "PosterizeBlock":                                          
-                return new PosterizeBlock("Posterize");                              
-            case "ArcTan2Block":                                          
-                return new ArcTan2Block("ArcTan2");                            
-            case "GradientBlock":                                          
-                return new GradientBlock("Gradient");                             
-            case "FrontFacingBlock":                                          
-                return new FrontFacingBlock("Front facing");            
+                return new NegateBlock("Negate");
+            case "PerturbNormalBlock":
+                return new PerturbNormalBlock("Perturb normal");
+            case "RandomNumberBlock":
+                return new RandomNumberBlock("Random number");
+            case "ReplaceColorBlock":
+                return new ReplaceColorBlock("Replace color");
+            case "PosterizeBlock":
+                return new PosterizeBlock("Posterize");
+            case "ArcTan2Block":
+                return new ArcTan2Block("ArcTan2");
+            case "GradientBlock":
+                return new GradientBlock("Gradient");
+            case "FrontFacingBlock":
+                return new FrontFacingBlock("Front facing");
             case "CosBlock": {
                 let cosBlock = new TrigonometryBlock("Cos");
                 cosBlock.operation = TrigonometryBlockOperations.Cos;
@@ -196,7 +205,7 @@ export class BlockTools {
                 let absBlock = new TrigonometryBlock("Abs");
                 absBlock.operation = TrigonometryBlockOperations.Abs;
                 return absBlock;
-            }            
+            }
             case "SqrtBlock": {
                 let sqrtBlock = new TrigonometryBlock("Sqrt");
                 sqrtBlock.operation = TrigonometryBlockOperations.Sqrt;
@@ -231,12 +240,12 @@ export class BlockTools {
                 let signBlock = new TrigonometryBlock("Sign");
                 signBlock.operation = TrigonometryBlockOperations.Sign;
                 return signBlock;
-            }            
+            }
             case "LogBlock": {
                 let logBlock = new TrigonometryBlock("Log");
                 logBlock.operation = TrigonometryBlockOperations.Log;
                 return logBlock;
-            }                                                            
+            }
             case "ExpBlock": {
                 let expBlock = new TrigonometryBlock("Exp");
                 expBlock.operation = TrigonometryBlockOperations.Exp;
@@ -256,7 +265,7 @@ export class BlockTools {
                 let radiansToDegreesBlock = new TrigonometryBlock("Radians to degrees");
                 radiansToDegreesBlock.operation = TrigonometryBlockOperations.Degrees;
                 return radiansToDegreesBlock;
-            }                        
+            }
             case "RoundBlock": {
                 let roundBlock = new TrigonometryBlock("Round");
                 roundBlock.operation = TrigonometryBlockOperations.Round;
@@ -266,22 +275,22 @@ export class BlockTools {
                 let ceilingBlock = new TrigonometryBlock("Ceiling");
                 ceilingBlock.operation = TrigonometryBlockOperations.Ceiling;
                 return ceilingBlock;
-            }     
+            }
             case "FloorBlock": {
                 let floorBlock = new TrigonometryBlock("Floor");
                 floorBlock.operation = TrigonometryBlockOperations.Floor;
                 return floorBlock;
-            }       
+            }
             case "SawToothWaveBlock": {
                 let sawToothWaveBlock = new WaveBlock("SawTooth wave");
                 sawToothWaveBlock.kind = WaveBlockKind.SawTooth;
                 return sawToothWaveBlock;
-            }     
+            }
             case "SquareWaveBlock": {
                 let squareWaveBlock = new WaveBlock("Square wave");
                 squareWaveBlock.kind = WaveBlockKind.Square;
                 return squareWaveBlock;
-            }     
+            }
             case "TriangleWaveBlock": {
                 let triangleWaveBlock = new WaveBlock("Triangle wave");
                 triangleWaveBlock.kind = WaveBlockKind.Triangle;
@@ -291,95 +300,95 @@ export class BlockTools {
                 let worldMatrixBlock = new InputBlock("World");
                 worldMatrixBlock.setAsSystemValue(NodeMaterialSystemValues.World);
                 return worldMatrixBlock;
-            }             
+            }
             case "WorldViewMatrixBlock": {
                 let worldViewMatrixBlock = new InputBlock("World x View");
                 worldViewMatrixBlock.setAsSystemValue(NodeMaterialSystemValues.WorldView);
                 return worldViewMatrixBlock;
-            }             
+            }
             case "WorldViewProjectionMatrixBlock": {
                 let worldViewProjectionMatrixBlock = new InputBlock("World x View x Projection");
                 worldViewProjectionMatrixBlock.setAsSystemValue(NodeMaterialSystemValues.WorldViewProjection);
                 return worldViewProjectionMatrixBlock;
-            }                    
+            }
             case "ViewMatrixBlock": {
                 let viewMatrixBlock = new InputBlock("View");
                 viewMatrixBlock.setAsSystemValue(NodeMaterialSystemValues.View);
                 return viewMatrixBlock;
-            }                          
+            }
             case "ViewProjectionMatrixBlock": {
                 let viewProjectionMatrixBlock = new InputBlock("View x Projection");
                 viewProjectionMatrixBlock.setAsSystemValue(NodeMaterialSystemValues.ViewProjection);
                 return viewProjectionMatrixBlock;
-            }                              
+            }
             case "ProjectionMatrixBlock": {
                 let projectionMatrixBlock = new InputBlock("Projection");
                 projectionMatrixBlock.setAsSystemValue(NodeMaterialSystemValues.Projection);
                 return projectionMatrixBlock;
-            }                                 
+            }
             case "CameraPositionBlock": {
                 let cameraPosition = new InputBlock("Camera position");
                 cameraPosition.setAsSystemValue(NodeMaterialSystemValues.CameraPosition);
                 return cameraPosition;
-            }                              
+            }
             case "FogColorBlock": {
                 let FogColor = new InputBlock("Fog color");
                 FogColor.setAsSystemValue(NodeMaterialSystemValues.FogColor);
                 return FogColor;
-            }                                   
+            }
             case "PositionBlock": {
                 let meshPosition = new InputBlock("position");
                 meshPosition.setAsAttribute("position");
                 return meshPosition;
-            }                                        
+            }
             case "UVBlock": {
                 let meshUV = new InputBlock("uv");
                 meshUV.setAsAttribute("uv");
                 return meshUV;
-            }                                         
+            }
             case "ColorBlock": {
                 let meshColor = new InputBlock("color");
                 meshColor.setAsAttribute("color");
                 return meshColor;
-            }                                              
+            }
             case "NormalBlock": {
                 let meshNormal = new InputBlock("normal");
                 meshNormal.setAsAttribute("normal");
                 return meshNormal;
-            }                                                 
+            }
             case "TangentBlock": {
                 let meshTangent = new InputBlock("tangent");
                 meshTangent.setAsAttribute("tangent");
                 return meshTangent;
-            }                                                  
+            }
             case "MatrixIndicesBlock": {
                 let meshMatrixIndices = new InputBlock("matricesIndices");
                 meshMatrixIndices.setAsAttribute("matricesIndices");
                 return meshMatrixIndices;
-            }                                                    
+            }
             case "MatrixWeightsBlock": {
                 let meshMatrixWeights = new InputBlock("matricesWeights");
                 meshMatrixWeights.setAsAttribute("matricesWeights");
                 return meshMatrixWeights;
-            }                                                     
+            }
             case "TimeBlock": {
                 let timeBlock = new InputBlock("Time", undefined, NodeMaterialBlockConnectionPointTypes.Float);
                 timeBlock.animationType = AnimatedInputBlockTypes.Time;
                 return timeBlock;
-            }   
+            }
             case "DeltaTimeBlock": {
-                let deltaTimeBlock = new InputBlock("Delta time");                
+                let deltaTimeBlock = new InputBlock("Delta time");
                 deltaTimeBlock.setAsSystemValue(NodeMaterialSystemValues.DeltaTime);
                 return deltaTimeBlock;
-            }      
+            }
             case "WorldPositionBlock": {
-                let worldPositionBlock = nodeMaterial.getInputBlockByPredicate(b => b.isAttribute && b.name === "position");                
+                let worldPositionBlock = nodeMaterial.getInputBlockByPredicate((b) => b.isAttribute && b.name === "position");
                 if (!worldPositionBlock) {
                     worldPositionBlock = new InputBlock("position");
                     worldPositionBlock.setAsAttribute("position");
                 }
 
-                let worldMatrixBlock = nodeMaterial.getInputBlockByPredicate(b => b.isSystemValue && b.systemValue === NodeMaterialSystemValues.World);  
+                let worldMatrixBlock = nodeMaterial.getInputBlockByPredicate((b) => b.isSystemValue && b.systemValue === NodeMaterialSystemValues.World);
 
                 if (!worldMatrixBlock) {
                     worldMatrixBlock = new InputBlock("World");
@@ -391,15 +400,15 @@ export class BlockTools {
                 worldMatrixBlock.connectTo(transformBlock);
 
                 return transformBlock;
-            }        
+            }
             case "WorldNormalBlock": {
-                let worldNormalBlock = nodeMaterial.getInputBlockByPredicate(b => b.isAttribute && b.name === "normal");                
+                let worldNormalBlock = nodeMaterial.getInputBlockByPredicate((b) => b.isAttribute && b.name === "normal");
                 if (!worldNormalBlock) {
                     worldNormalBlock = new InputBlock("normal");
                     worldNormalBlock.setAsAttribute("normal");
                 }
 
-                let worldMatrixBlock = nodeMaterial.getInputBlockByPredicate(b => b.isSystemValue && b.systemValue === NodeMaterialSystemValues.World);  
+                let worldMatrixBlock = nodeMaterial.getInputBlockByPredicate((b) => b.isSystemValue && b.systemValue === NodeMaterialSystemValues.World);
 
                 if (!worldMatrixBlock) {
                     worldMatrixBlock = new InputBlock("World");
@@ -411,15 +420,15 @@ export class BlockTools {
                 worldMatrixBlock.connectTo(transformBlock);
 
                 return transformBlock;
-            }     
+            }
             case "WorldTangentBlock": {
-                let worldTangentBlock = nodeMaterial.getInputBlockByPredicate(b => b.isAttribute && b.name === "tangent");                
+                let worldTangentBlock = nodeMaterial.getInputBlockByPredicate((b) => b.isAttribute && b.name === "tangent");
                 if (!worldTangentBlock) {
                     worldTangentBlock = new InputBlock("tangent");
                     worldTangentBlock.setAsAttribute("tangent");
                 }
 
-                let worldMatrixBlock = nodeMaterial.getInputBlockByPredicate(b => b.isSystemValue && b.systemValue === NodeMaterialSystemValues.World);  
+                let worldMatrixBlock = nodeMaterial.getInputBlockByPredicate((b) => b.isSystemValue && b.systemValue === NodeMaterialSystemValues.World);
 
                 if (!worldMatrixBlock) {
                     worldMatrixBlock = new InputBlock("World");
@@ -431,7 +440,25 @@ export class BlockTools {
                 worldMatrixBlock.connectTo(transformBlock);
 
                 return transformBlock;
-            }              
+            }
+            case "PBRMetallicRoughnessBlock":
+                return new PBRMetallicRoughnessBlock("PBRMetallicRoughness");
+            case "SheenBlock":
+                return new SheenBlock("Sheen");
+            case "AmbientOcclusionBlock":
+                return new AmbientOcclusionBlock("AmbientOcclusion");
+            case "ReflectivityBlock":
+                return new ReflectivityBlock("Reflectivity");
+            case "AnisotropyBlock":
+                return new AnisotropyBlock("Anisotropy");
+            case "ReflectionBlock":
+                return new ReflectionBlock("Reflection");
+            case "ClearCoatBlock":
+                return new ClearCoatBlock("ClearCoat");
+            case "RefractionBlock":
+                return new RefractionBlock("Refraction");
+            case "SubSurfaceBlock":
+                return new SubSurfaceBlock("SubSurface");
         }
 
         return null;
@@ -441,21 +468,21 @@ export class BlockTools {
         let color = "#880000";
         switch (type) {
             case NodeMaterialBlockConnectionPointTypes.Float:
-				color = "#cb9e27";
+                color = "#cb9e27";
                 break;
-            case NodeMaterialBlockConnectionPointTypes.Vector2:                
-				color = "#16bcb1";
+            case NodeMaterialBlockConnectionPointTypes.Vector2:
+                color = "#16bcb1";
                 break;
-            case NodeMaterialBlockConnectionPointTypes.Vector3:                
-            case NodeMaterialBlockConnectionPointTypes.Color3:                
+            case NodeMaterialBlockConnectionPointTypes.Vector3:
+            case NodeMaterialBlockConnectionPointTypes.Color3:
                 color = "#b786cb";
                 break;
-            case NodeMaterialBlockConnectionPointTypes.Vector4:                
-            case NodeMaterialBlockConnectionPointTypes.Color4:                
-				color = "#be5126";
+            case NodeMaterialBlockConnectionPointTypes.Vector4:
+            case NodeMaterialBlockConnectionPointTypes.Color4:
+                color = "#be5126";
                 break;
-            case NodeMaterialBlockConnectionPointTypes.Matrix:                
-				color = "#591990";
+            case NodeMaterialBlockConnectionPointTypes.Matrix:
+                color = "#591990";
                 break;
         }
 

+ 23 - 13
nodeEditor/src/components/nodeList/nodeListComponent.tsx

@@ -12,7 +12,7 @@ interface INodeListComponentProps {
 
 export class NodeListComponent extends React.Component<INodeListComponentProps, {filter: string}> {
 
-    private static _Tooltips:{[key: string]: string} = {
+    private static _Tooltips: {[key: string]: string} = {
         "BonesBlock": "Provides a world matrix for each vertex, based on skeletal (bone/joint) animation",
         "MorphTargetsBlock": "Provides the final positions, normals, tangents, and uvs based on morph targets in a mesh",
         "AddBlock": "Adds the left and right inputs of the same type together",
@@ -79,7 +79,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
         "CameraPositionBlock": "Outputs a Vector3 position of the active scene camera",
         "FogBlock": "Applies fog to the scene with an increasing opacity based on distance from the camera",
         "FogColorBlock": "The system value for fog color pulled from the scene",
-        "ImageProcessingBlock": "Provides access to all of the Babylon image processing properties",        
+        "ImageProcessingBlock": "Provides access to all of the Babylon image processing properties",
         "LightBlock": "Outputs diffuse and specular contributions from one or more scene lights",
         "LightInformationBlock": "Provides the direction, color and intensity of a selected light based on its world position",
         "ReflectionTextureBlock": "Creates a reflection from the input texture",
@@ -115,9 +115,18 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
         "SimplexPerlin3DBlock": "Creates a type of gradient noise with few directional artifacts.",
         "WorleyNoise3DBlock": "Creates a random pattern resembling cells.",
         "ReflectBlock": "Outputs the direction of the input vector reflected across the surface normal.",
-        "RefractBlock": "Outputs a direction simulating a deflection of the input vector.", 
-        "Rotate2dBlock": "Rotates UV coordinates around the W axis."
-    }
+        "RefractBlock": "Outputs a direction simulating a deflection of the input vector.",
+        "Rotate2dBlock": "Rotates UV coordinates around the W axis.",
+        "PBRMetallicRoughnessBlock": "PBR metallic/roughness material",
+        "SheenBlock": "PBR Sheen block",
+        "AmbientOcclusionBlock": "PBR Ambient occlusion block",
+        "ReflectivityBlock": "PBR Reflectivity block",
+        "AnisotropyBlock": "PBR Anisotropy block",
+        "ReflectionBlock": "PBR Reflection block",
+        "ClearCoatBlock": "PBR ClearCoat block",
+        "RefractionBlock": "PBR Refraction block",
+        "SubSurfaceBlock": "PBR SubSurface block",
+    };
 
     constructor(props: INodeListComponentProps) {
         super(props);
@@ -132,33 +141,34 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
     render() {
         // Block types used to create the menu from
         const allBlocks = {
-            
+
             Animation: ["BonesBlock", "MorphTargetsBlock"],
             Color_Management: ["ReplaceColorBlock", "PosterizeBlock", "GradientBlock", "DesaturateBlock"],
             Conversion_Blocks: ["ColorMergerBlock", "ColorSplitterBlock", "VectorMergerBlock", "VectorSplitterBlock"],
             Inputs: ["Float", "Vector2", "Vector3", "Vector4", "Color3", "Color4", "TextureBlock", "ReflectionTextureBlock", "TimeBlock", "DeltaTimeBlock"],
             Interpolation: ["LerpBlock", "StepBlock", "SmoothStepBlock", "NLerpBlock"],
-            Math__Standard: ["AddBlock", "DivideBlock", "MaxBlock", "MinBlock", "MultiplyBlock", "NegateBlock", "OneMinusBlock", "ReciprocalBlock", "ScaleBlock", "SignBlock", "SqrtBlock", "SubtractBlock"], 
+            Math__Standard: ["AddBlock", "DivideBlock", "MaxBlock", "MinBlock", "MultiplyBlock", "NegateBlock", "OneMinusBlock", "ReciprocalBlock", "ScaleBlock", "SignBlock", "SqrtBlock", "SubtractBlock"],
             Math__Scientific: ["AbsBlock", "ArcCosBlock", "ArcSinBlock", "ArcTanBlock", "ArcTan2Block", "CosBlock", "DegreesToRadiansBlock", "ExpBlock", "Exp2Block", "FractBlock", "LogBlock", "PowBlock", "RadiansToDegreesBlock", "SawToothWaveBlock", "SinBlock", "SquareWaveBlock", "TanBlock", "TriangleWaveBlock"],
             Math__Vector: ["CrossBlock", "DerivativeBlock", "DistanceBlock", "DotBlock", "FresnelBlock", "LengthBlock", "ReflectBlock", "RefractBlock", "Rotate2dBlock", "TransformBlock", ],
             Matrices: ["Matrix", "WorldMatrixBlock", "WorldViewMatrixBlock", "WorldViewProjectionMatrixBlock", "ViewMatrixBlock", "ViewProjectionMatrixBlock", "ProjectionMatrixBlock"],
-            Mesh: ["InstancesBlock", "PositionBlock", "UVBlock", "ColorBlock", "NormalBlock", "PerturbNormalBlock", "NormalBlendBlock" , "TangentBlock", "MatrixIndicesBlock", "MatrixWeightsBlock", "WorldPositionBlock", "WorldNormalBlock", "WorldTangentBlock", "FrontFacingBlock"], 
+            Mesh: ["InstancesBlock", "PositionBlock", "UVBlock", "ColorBlock", "NormalBlock", "PerturbNormalBlock", "NormalBlendBlock" , "TangentBlock", "MatrixIndicesBlock", "MatrixWeightsBlock", "WorldPositionBlock", "WorldNormalBlock", "WorldTangentBlock", "FrontFacingBlock"],
             Noises: ["RandomNumberBlock", "SimplexPerlin3DBlock", "WorleyNoise3DBlock"],
             Output_Nodes: ["VertexOutputBlock", "FragmentOutputBlock", "DiscardBlock"],
+            PBR: ["PBRMetallicRoughnessBlock", "AmbientOcclusionBlock", "AnisotropyBlock", "ClearCoatBlock", "ReflectionBlock", "ReflectivityBlock", "RefractionBlock", "SheenBlock", "SubSurfaceBlock"],
             Range: ["ClampBlock", "RemapBlock", "NormalizeBlock"],
             Round: ["RoundBlock", "CeilingBlock", "FloorBlock"],
             Scene: ["FogBlock", "CameraPositionBlock", "FogColorBlock", "ImageProcessingBlock", "LightBlock", "LightInformationBlock", "ViewDirectionBlock"],
-        }
+        };
 
         // Create node menu
-        var blockMenu = []
+        var blockMenu = [];
         for (var key in allBlocks) {
             var blockList = (allBlocks as any)[key].filter((b: string) => !this.state.filter || b.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1)
             .sort((a: string, b: string) => a.localeCompare(b))
             .map((block: any, i: number) => {
                 let tooltip = NodeListComponent._Tooltips[block] || "";
 
-                return <DraggableLineComponent key={block} data={block} tooltip={tooltip}/>
+                return <DraggableLineComponent key={block} data={block} tooltip={tooltip}/>;
             });
 
             if (blockList.length) {
@@ -175,9 +185,9 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
                 <div className="panes">
                     <div className="pane">
                         <div className="filter">
-                            <input type="text" placeholder="Filter" 
+                            <input type="text" placeholder="Filter"
                                 onFocus={() => this.props.globalState.blockKeyboardEvents = true}
-                                onBlur={evt => {
+                                onBlur={(evt) => {
                                     this.props.globalState.blockKeyboardEvents = false;
                                 }}
                                 onChange={(evt) => this.filterContent(evt.target.value)} />

+ 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; 
             }               

+ 24 - 7
nodeEditor/src/components/propertyTab/propertyTabComponent.tsx

@@ -17,6 +17,7 @@ import { TextLineComponent } from '../../sharedComponents/textLineComponent';
 import { Engine } from 'babylonjs/Engines/engine';
 import { FramePropertyTabComponent } from '../../diagram/properties/framePropertyComponent';
 import { FrameNodePortPropertyTabComponent } from '../../diagram/properties/frameNodePortPropertyComponent';
+import { NodePortPropertyTabComponent } from '../../diagram/properties/nodePortPropertyComponent';
 import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes';
 import { Color3LineComponent } from '../../sharedComponents/color3LineComponent';
@@ -28,6 +29,7 @@ import { Vector4LineComponent } from '../../sharedComponents/vector4LineComponen
 import { Observer } from 'babylonjs/Misc/observable';
 import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
 import { FrameNodePort } from '../../diagram/frameNodePort';
+import { NodePort } from '../../diagram/nodePort';
 import { isFramePortData } from '../../diagram/graphCanvas';
 require("./propertyTab.scss");
 
@@ -35,25 +37,34 @@ interface IPropertyTabComponentProps {
     globalState: GlobalState;
 }
 
-export class PropertyTabComponent extends React.Component<IPropertyTabComponentProps, { currentNode: Nullable<GraphNode>, currentFrame: Nullable<GraphFrame>, currentFrameNodePort: Nullable<FrameNodePort> }> {
+interface IPropertyTabComponentState { 
+    currentNode: Nullable<GraphNode>, 
+    currentFrame: Nullable<GraphFrame>, 
+    currentFrameNodePort: Nullable<FrameNodePort>,
+    currentNodePort: Nullable<NodePort>
+ }
+
+export class PropertyTabComponent extends React.Component<IPropertyTabComponentProps, IPropertyTabComponentState> {
     private _onBuiltObserver: Nullable<Observer<void>>;
 
     constructor(props: IPropertyTabComponentProps) {
         super(props)
 
-        this.state = { currentNode: null, currentFrame: null, currentFrameNodePort: null };
+        this.state = { currentNode: null, currentFrame: null, currentFrameNodePort: null, currentNodePort: null };
     }
 
     componentDidMount() {
         this.props.globalState.onSelectionChangedObservable.add(selection => {
             if (selection instanceof GraphNode) {
-                this.setState({ currentNode: selection, currentFrame: null, currentFrameNodePort: null });
+                this.setState({ currentNode: selection, currentFrame: null, currentFrameNodePort: null, currentNodePort: null });
             } else if (selection instanceof GraphFrame) {
-                this.setState({ currentNode: null, currentFrame: selection, currentFrameNodePort: null });
+                this.setState({ currentNode: null, currentFrame: selection, currentFrameNodePort: null, currentNodePort: null });
             } else if(isFramePortData(selection)) {
-                this.setState({ currentNode: null, currentFrame: selection.frame, currentFrameNodePort: selection.port });
+                this.setState({ currentNode: null, currentFrame: selection.frame, currentFrameNodePort: selection.port, currentNodePort: null });
+            } else if (selection instanceof NodePort && selection.hasLabel()) {
+                this.setState({ currentNode: null, currentFrame: null, currentFrameNodePort: null, currentNodePort: selection})
             } else {
-                this.setState({ currentNode: null, currentFrame: null, currentFrameNodePort: null });
+                this.setState({ currentNode: null, currentFrame: null, currentFrameNodePort: null, currentNodePort: null });
             }
         });
 
@@ -240,7 +251,7 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                             NODE MATERIAL EDITOR
                         </div>
                     </div>
-                    {this.state.currentNode.renderProperties()}
+                    {this.state.currentNode?.renderProperties() || this.state.currentNodePort?.node.renderProperties()}
                 </div>
             );
         }
@@ -251,6 +262,12 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
             );
         }
 
+        if (this.state.currentNodePort) {
+            return (
+                <NodePortPropertyTabComponent globalState={this.props.globalState} nodePort={this.state.currentNodePort}/>
+            );
+        }
+
         if (this.state.currentFrame) {
             return (
                 <FramePropertyTabComponent globalState={this.props.globalState} frame={this.state.currentFrame}/>

+ 2 - 1
nodeEditor/src/diagram/display/textureDisplayManager.ts

@@ -1,6 +1,7 @@
 import { IDisplayManager } from './displayManager';
 import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
 import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
+import { RefractionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/refractionBlock';
 import { ReflectionTextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock';
 import { TextureLineComponent } from '../../sharedComponents/textureLineComponent';
 
@@ -25,7 +26,7 @@ export class TextureDisplayManager implements IDisplayManager {
     }
 
     public updatePreviewContent(block: NodeMaterialBlock, contentArea: HTMLDivElement): void {       
-        const textureBlock = block as TextureBlock | ReflectionTextureBlock;
+        const textureBlock = block as TextureBlock | ReflectionTextureBlock | RefractionBlock;
 
         if (!this._previewCanvas) {
             contentArea.classList.add("texture-block");

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

@@ -20,4 +20,6 @@ DisplayLedger.RegisteredControls["RemapBlock"] = RemapDisplayManager;
 DisplayLedger.RegisteredControls["TrigonometryBlock"] = TrigonometryDisplayManager;
 DisplayLedger.RegisteredControls["TextureBlock"] = TextureDisplayManager;
 DisplayLedger.RegisteredControls["ReflectionTextureBlock"] = TextureDisplayManager;
+DisplayLedger.RegisteredControls["ReflectionBlock"] = TextureDisplayManager;
+DisplayLedger.RegisteredControls["RefractionBlock"] = TextureDisplayManager;
 DisplayLedger.RegisteredControls["DiscardBlock"] = DiscardDisplayManager;

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

@@ -9,7 +9,6 @@ import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMateri
 import { FramePortData, isFramePortData } from './graphCanvas';
 
 export class FrameNodePort extends NodePort {
-    private _portLabel: Element;
     private _parentFrameId: number;
     private _isInput: boolean;
     private _framePortPosition: FramePortPosition
@@ -28,25 +27,16 @@ export class FrameNodePort extends NodePort {
         return this._isInput;
     }
 
-    public get portLabel() {
-        return this._portLabel.innerHTML;
-    }
-
     public get framePortId() {
         return this._framePortId;
     }
 
-    public set portLabel(newLabel: string) {
-        this._portLabel.innerHTML = newLabel;
-    }
-
     public get framePortPosition() {
         return this._framePortPosition;
     }
 
     public set framePortPosition(position: FramePortPosition) {
         this._framePortPosition = position;
-        console.log(this.onFramePortPositionChangedObservable.observers);
         this.onFramePortPositionChangedObservable.notifyObservers(this);
     }
 
@@ -54,7 +44,6 @@ export class FrameNodePort extends NodePort {
         super(portContainer, connectionPoint,node, globalState);
 
         this._parentFrameId = parentFrameId;
-        this._portLabel = portContainer.children[0];
         this._isInput = isInput;
         this._framePortId = framePortId;
 
@@ -83,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.name;        
+            let portName = connectionPoint.displayName || connectionPoint.name;
+            if (connectionPoint.ownerBlock.isInput) {
+                portName = node.name;
+            }
+            portLabel.innerHTML = portName;       
             portContainer.appendChild(portLabel);
         }
 

+ 15 - 2
nodeEditor/src/diagram/graphCanvas.tsx

@@ -198,6 +198,8 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
                     } else {                    
                         this._selectedNodes = [selection];
                     }
+                } else if(selection instanceof NodePort && !selection.hasLabel()){ // if node port is uneditable, select graphNode instead
+                    props.globalState.onSelectionChangedObservable.notifyObservers(selection.node)
                 } else if(selection instanceof NodePort){
                     this._selectedNodes = [];
                     this._selectedFrame = null;
@@ -625,6 +627,8 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
                         }
                         this.props.globalState.onSelectionChangedObservable.notifyObservers(data);
                     }
+                } else if(this._candidateLink.portA instanceof NodePort){
+                    this.props.globalState.onSelectionChangedObservable.notifyObservers(this._candidateLink.portA );
                 }
             }
             this._candidateLink.dispose();
@@ -735,9 +739,15 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
             }
 
             // No destination so let's spin a new input block
-            let inputBlock = new InputBlock(NodeMaterialBlockConnectionPointTypes[this._candidateLink!.portA.connectionPoint.type], undefined, this._candidateLink!.portA.connectionPoint.type);
+            let pointName = "output", inputBlock;
+            let customInputBlock = this._candidateLink!.portA.connectionPoint.createCustomInputBlock();
+            if (!customInputBlock) {
+                inputBlock = new InputBlock(NodeMaterialBlockConnectionPointTypes[this._candidateLink!.portA.connectionPoint.type], undefined, this._candidateLink!.portA.connectionPoint.type);
+            } else {
+                [inputBlock, pointName] = customInputBlock;
+            }
             this.props.globalState.nodeMaterial.attachedBlocks.push(inputBlock);
-            pointA = inputBlock.output;
+            pointA = (inputBlock as any)[pointName];
             nodeA = this.appendBlock(inputBlock);
             
             nodeA.x = this._dropPointX - 200;
@@ -773,6 +783,9 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
         // Check compatibility
         let isFragmentOutput = pointB.ownerBlock.getClassName() === "FragmentOutputBlock";
         let compatibilityState = pointA.checkCompatibilityState(pointB);
+        if ((pointA.needDualDirectionValidation || pointB.needDualDirectionValidation) && compatibilityState === NodeMaterialConnectionPointCompatibilityStates.Compatible && !(pointA instanceof InputBlock)) {
+            compatibilityState = pointB.checkCompatibilityState(pointA);
+        }
         if (compatibilityState === NodeMaterialConnectionPointCompatibilityStates.Compatible) {
             if (isFragmentOutput) {
                 let fragmentBlock = pointB.ownerBlock as FragmentOutputBlock;

+ 140 - 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,148 @@ 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");
-
-            this._moveFrame((this.width - this.CollapsedWidth) / 2, 0);
-
-            for (var node of this._nodes) {
-                node.isVisible = false;
-                for (var port of node.outputPorts) { // Output
-                    if (port.connectionPoint.hasEndpoints) {
-                        let portAdded = false;
+        // 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
+                }
+            }
+        }
+    }
 
-                        for (var link of node.links) {
-                            if (link.portA === port && this.nodes.indexOf(link.nodeB!) === -1) {
-                                let localPort: FrameNodePort;
+    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;
 
-                                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);
+                    for (var link of node.links) {
+                        if (link.portA === port && this.nodes.indexOf(link.nodeB!) === -1 || (link.portA === port && port.exposedOnFrame)) {
+                            let localPort: FrameNodePort;
 
-                                    link.isVisible = true;
+                            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);
 
-                                } else {
-                                    localPort = this.ports.filter(p => p.connectionPoint === port.connectionPoint)[0];
-                                }
+                                link.isVisible = true;
 
-                                port.delegatedPort = localPort;
-                                this._controlledPorts.push(port);
-                            }
-                        }
-                    } 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);
+                                const onLinkDisposedObserver = link.onDisposedObservable.add((nodeLink: NodeLink) => {
+                                    this._redrawFramePorts();
+                                });
 
-                    }
-                }
+                                this._onNodeLinkDisposedObservers.push(onLinkDisposedObserver); 
 
-                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);
+                            } else if (this.nodes.indexOf(link.nodeB!) === -1) {
                                 link.isVisible = true;
+                                localPort = this.ports.filter(p => p.connectionPoint === port.connectionPoint)[0];
+                            } else {
+                                localPort = this.ports.filter(p => p.connectionPoint === port.connectionPoint)[0];
                             }
+
+                            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;
+        }
 
-            // 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._outputPortContainer.innerHTML = "";
+        this._inputPortContainer.innerHTML = "";
+        this.ports.forEach((framePort:FrameNodePort) => {
+            framePort.dispose();
+        });
+
+        this._controlledPorts.forEach(port => {
+            port.delegatedPort = null;
+            port.refresh();
+        })
+
+        this._frameInPorts = [];
+        this._frameOutPorts = [];
+        this._controlledPorts = [];
+
+        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 +253,7 @@ export class GraphFrame {
             this._frameInPorts = [];
             this._frameOutPorts = [];
             this._controlledPorts = [];
+            this._onNodeLinkDisposedObservers = [];
 
             for (var node of this._nodes) {
                 node.isVisible = true;
@@ -225,7 +275,7 @@ export class GraphFrame {
         }
 
         this.onExpandStateChanged.notifyObservers(this);
-    }
+    }     
 
     public get nodes() {
         return this._nodes;
@@ -487,6 +537,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 +1266,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 +1322,7 @@ export class GraphFrame {
 
                 if (node.length) {
                     newFrame.nodes.push(node[0]);
+                    node[0].enclosingFrameId = newFrame.id;
                 }
             }
         } else {

+ 26 - 3
nodeEditor/src/diagram/graphNode.ts

@@ -6,7 +6,7 @@ import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMateri
 import { GraphCanvasComponent, FramePortData } from './graphCanvas';
 import { PropertyLedger } from './propertyLedger';
 import * as React from 'react';
-import { GenericPropertyTabComponent } from './properties/genericNodePropertyComponent';
+import { GenericPropertyComponent } from './properties/genericNodePropertyComponent';
 import { DisplayLedger } from './displayLedger';
 import { IDisplayManager } from './display/displayManager';
 import { NodeLink } from './nodeLink';
@@ -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;
@@ -51,6 +52,7 @@ export class GraphNode {
             this._visual.classList.add("hidden");
         } else {
             this._visual.classList.remove("hidden");
+            this._upateNodePortNames();
         }
 
         for (var link of this._links) {
@@ -60,6 +62,14 @@ export class GraphNode {
         this._refreshLinks();
     }
 
+    private _upateNodePortNames(){
+        for (var port of this._inputPorts.concat(this._outputPorts)) {
+            if(port.hasLabel()){
+                port.portName = port.connectionPoint.displayName || port.connectionPoint.name;
+            }
+        }
+    }
+
     public get outputPorts() {
         return this._outputPorts;
     }
@@ -135,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;            
@@ -202,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) {
@@ -344,7 +367,7 @@ export class GraphNode {
         let control = PropertyLedger.RegisteredControls[this.block.getClassName()];
 
         if (!control) {
-            control = GenericPropertyTabComponent;
+            control = GenericPropertyComponent;
         }
 
         return React.createElement(control, {

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

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

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 81 - 2
nodeEditor/src/diagram/nodePort.ts


+ 0 - 24
nodeEditor/src/diagram/properties/PerturbNormalNodePropertyComponent.tsx

@@ -1,24 +0,0 @@
-
-import * as React from "react";
-import { LineContainerComponent } from '../../sharedComponents/lineContainerComponent';
-import { IPropertyComponentProps } from './propertyComponentProps';
-import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
-
-export class PerturbNormalPropertyTabComponent extends React.Component<IPropertyComponentProps> {
-    constructor(props: IPropertyComponentProps) {
-        super(props)
-    }
-
-    render() {
-        return (
-            <>                
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
-                <LineContainerComponent title="PROPERTIES">
-                    <CheckBoxLineComponent label="Invert X axis" target={this.props.block} propertyName="invertX" onValueChanged={() => this.props.globalState.onRebuildRequiredObservable.notifyObservers()} />
-                    <CheckBoxLineComponent label="Invert Y axis" target={this.props.block} propertyName="invertY" onValueChanged={() => this.props.globalState.onRebuildRequiredObservable.notifyObservers()}/>                    
-                </LineContainerComponent>        
-            </>
-        );
-    }
-}

+ 0 - 33
nodeEditor/src/diagram/properties/clampNodePropertyComponent.tsx

@@ -1,33 +0,0 @@
-
-import * as React from "react";
-import { LineContainerComponent } from '../../sharedComponents/lineContainerComponent';
-import { FloatLineComponent } from '../../sharedComponents/floatLineComponent';
-import { IPropertyComponentProps } from './propertyComponentProps';
-import { ClampBlock } from 'babylonjs/Materials/Node/Blocks/clampBlock';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
-
-export class ClampPropertyTabComponent extends React.Component<IPropertyComponentProps> {
-
-    constructor(props: IPropertyComponentProps) {
-        super(props)
-    }
-
-    forceRebuild() {
-        this.props.globalState.onUpdateRequiredObservable.notifyObservers();
-        this.props.globalState.onRebuildRequiredObservable.notifyObservers();
-    }
-
-    render() {
-        let clampBlock = this.props.block as ClampBlock
-      
-        return (
-            <div>
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
-                <LineContainerComponent title="PROPERTIES">
-                  <FloatLineComponent globalState={this.props.globalState} label="Minimum" propertyName="minimum" target={clampBlock} onChange={() => this.forceRebuild()} />
-                  <FloatLineComponent globalState={this.props.globalState} label="Maximum" propertyName="maximum" target={clampBlock} onChange={() => this.forceRebuild()} />
-                </LineContainerComponent>
-            </div>
-        );
-    }
-}

+ 1 - 1
nodeEditor/src/diagram/properties/frameNodePortPropertyComponent.tsx

@@ -62,7 +62,7 @@ export class FrameNodePortPropertyTabComponent extends React.Component<IFrameNod
                 </div>
                 <div>
                     <LineContainerComponent title="GENERAL">
-                        <TextInputLineComponent globalState={this.props.globalState} label="Port Label" propertyName="portLabel" target={this.props.frameNodePort} />
+                        <TextInputLineComponent globalState={this.props.globalState} label="Port Name" propertyName="portName" target={this.props.frameNodePort} />
                         {this.props.frameNodePort.framePortPosition !== FramePortPosition.Top && <ButtonLineComponent label="Move Port Up" onClick={() => {
                             this.props.frame.moveFramePortUp(this.props.frameNodePort);
                         }} />}

+ 113 - 5
nodeEditor/src/diagram/properties/genericNodePropertyComponent.tsx

@@ -4,11 +4,32 @@ import { LineContainerComponent } from '../../sharedComponents/lineContainerComp
 import { IPropertyComponentProps } from './propertyComponentProps';
 import { TextInputLineComponent } from '../../sharedComponents/textInputLineComponent';
 import { TextLineComponent } from '../../sharedComponents/textLineComponent';
+import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
+import { FloatLineComponent } from '../../sharedComponents/floatLineComponent';
+import { SliderLineComponent } from '../../sharedComponents/sliderLineComponent';
+import { Vector2LineComponent } from '../../sharedComponents/vector2LineComponent';
+import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponent';
 import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
+import { PropertyTypeForEdition, IPropertyDescriptionForEdition, IEditablePropertyListOption } from 'babylonjs/Materials/Node/nodeMaterialDecorator';
 
-export class GenericPropertyTabComponent extends React.Component<IPropertyComponentProps> {
+export class GenericPropertyComponent extends React.Component<IPropertyComponentProps> {
+    constructor(props: IPropertyComponentProps) {
+        super(props);
+    }
+
+    render() {
+        return (
+            <>
+                <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+            </>
+        );
+    }
+}
+
+export class GeneralPropertyTabComponent extends React.Component<IPropertyComponentProps> {
     constructor(props: IPropertyComponentProps) {
-        super(props)
+        super(props);
     }
 
     render() {
@@ -17,13 +38,100 @@ export class GenericPropertyTabComponent extends React.Component<IPropertyCompon
                 <LineContainerComponent title="GENERAL">
                     {
                         (!this.props.block.isInput || !(this.props.block as InputBlock).isAttribute) &&
-                        <TextInputLineComponent globalState={this.props.globalState} label="Name" propertyName="name" target={this.props.block} 
+                        <TextInputLineComponent globalState={this.props.globalState} label="Name" propertyName="name" target={this.props.block}
                             onChange={() => this.props.globalState.onUpdateRequiredObservable.notifyObservers()} />
                     }
                     <TextLineComponent label="Type" value={this.props.block.getClassName()} />
-                    <TextInputLineComponent globalState={this.props.globalState} label="Comments" propertyName="comments" target={this.props.block} 
+                    <TextInputLineComponent globalState={this.props.globalState} label="Comments" propertyName="comments" target={this.props.block}
                             onChange={() => this.props.globalState.onUpdateRequiredObservable.notifyObservers()} />
-                </LineContainerComponent>         
+                </LineContainerComponent>
+            </>
+        );
+    }
+}
+
+export class GenericPropertyTabComponent extends React.Component<IPropertyComponentProps> {
+    constructor(props: IPropertyComponentProps) {
+        super(props);
+    }
+
+    forceRebuild(notifiers?: { "rebuild"?: boolean; "update"?: boolean; }) {
+        if (!notifiers || notifiers.update) {
+            this.props.globalState.onUpdateRequiredObservable.notifyObservers();
+        }
+
+        if (!notifiers || notifiers.rebuild) {
+            this.props.globalState.onRebuildRequiredObservable.notifyObservers();
+        }
+    }
+
+    render() {
+        const block = this.props.block,
+              propStore: IPropertyDescriptionForEdition[] = (block as any)._propStore;
+
+        if (!propStore) {
+            return (
+                <>
+                </>
+            );
+        }
+
+        const componentList: { [groupName: string]: JSX.Element[]} = {},
+              groups: string[] = [];
+
+        for (const { propertyName, displayName, type, groupName, options } of propStore) {
+            let components = componentList[groupName];
+
+            if (!components) {
+                components = [];
+                componentList[groupName] = components;
+                groups.push(groupName);
+            }
+
+            switch (type) {
+                case PropertyTypeForEdition.Boolean: {
+                    components.push(
+                        <CheckBoxLineComponent label={displayName} target={this.props.block} propertyName={propertyName} onValueChanged={() => this.forceRebuild(options.notifiers)} />
+                    );
+                    break;
+                }
+                case PropertyTypeForEdition.Float: {
+                    let cantDisplaySlider = (isNaN(options.min as number) || isNaN(options.max as number) || options.min === options.max);
+                    if (cantDisplaySlider) {
+                        components.push(
+                            <FloatLineComponent globalState={this.props.globalState} label={displayName} propertyName={propertyName} target={this.props.block} onChange={() => this.forceRebuild(options.notifiers)} />
+                        );
+                    } else {
+                        components.push(
+                            <SliderLineComponent label={displayName} target={this.props.block} propertyName={propertyName} step={Math.abs((options.max as number) - (options.min as number)) / 100.0} minimum={Math.min(options.min as number, options.max as number)} maximum={options.max as number} onChange={() => this.forceRebuild(options.notifiers)}/>
+                        );
+                    }
+                    break;
+                }
+                case PropertyTypeForEdition.Vector2: {
+                    components.push(
+                        <Vector2LineComponent globalState={this.props.globalState} label={displayName} propertyName={propertyName} target={this.props.block} onChange={() => this.forceRebuild(options.notifiers)} />
+                    );
+                    break;
+                }
+                case PropertyTypeForEdition.List: {
+                    components.push(
+                        <OptionsLineComponent label={displayName} options={options.options as IEditablePropertyListOption[]} target={this.props.block} propertyName={propertyName} onSelect={() => this.forceRebuild(options.notifiers)} />
+                    );
+                    break;
+                }
+            }
+        }
+
+        return (
+            <>
+            {
+                groups.map((group) =>
+                    <LineContainerComponent title={group}>
+                        {componentList[group]}
+                    </LineContainerComponent>
+                )
+            }
             </>
         );
     }

+ 2 - 2
nodeEditor/src/diagram/properties/gradientNodePropertyComponent.tsx

@@ -6,7 +6,7 @@ import { GradientStepComponent } from './gradientStepComponent';
 import { ButtonLineComponent } from '../../sharedComponents/buttonLineComponent';
 import { Color3 } from 'babylonjs/Maths/math.color';
 import { IPropertyComponentProps } from './propertyComponentProps';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
+import { GeneralPropertyTabComponent } from './genericNodePropertyComponent';
 
 export class GradientPropertyTabComponent extends React.Component<IPropertyComponentProps> {
 
@@ -64,7 +64,7 @@ export class GradientPropertyTabComponent extends React.Component<IPropertyCompo
       
         return (
             <div>
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+                <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
                 <LineContainerComponent title="STEPS">
                     <ButtonLineComponent label="Add new step" onClick={() => this.addNewStep()} />
                     {

+ 2 - 2
nodeEditor/src/diagram/properties/inputNodePropertyComponent.tsx

@@ -16,7 +16,7 @@ import { NodeMaterialSystemValues } from 'babylonjs/Materials/Node/Enums/nodeMat
 import { AnimatedInputBlockTypes } from 'babylonjs/Materials/Node/Blocks/Input/animatedInputBlockTypes';
 import { IPropertyComponentProps } from './propertyComponentProps';
 import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
+import { GeneralPropertyTabComponent } from './genericNodePropertyComponent';
 import { TextInputLineComponent } from '../../sharedComponents/textInputLineComponent';
 import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
 import { Color4PropertyTabComponent } from '../../components/propertyTab/properties/color4PropertyTabComponent';
@@ -206,7 +206,7 @@ export class InputPropertyTabComponent extends React.Component<IPropertyComponen
 
         return (
             <div>
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+                <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
                 <LineContainerComponent title="PROPERTIES">
                     {
                         inputBlock.isUniform && !inputBlock.isSystemValue && inputBlock.animationType === AnimatedInputBlockTypes.None &&

+ 2 - 2
nodeEditor/src/diagram/properties/lightInformationPropertyTabComponent.tsx

@@ -4,7 +4,7 @@ import { LineContainerComponent } from '../../sharedComponents/lineContainerComp
 import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponent';
 import { IPropertyComponentProps } from './propertyComponentProps';
 import { LightInformationBlock } from 'babylonjs/Materials/Node/Blocks/Vertex/lightInformationBlock';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
+import { GeneralPropertyTabComponent } from './genericNodePropertyComponent';
 
 export class LightInformationPropertyTabComponent extends React.Component<IPropertyComponentProps> {
 
@@ -18,7 +18,7 @@ export class LightInformationPropertyTabComponent extends React.Component<IPrope
 
         return (
             <div>               
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+                <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
                 <LineContainerComponent title="PROPERTIES">
                     <OptionsLineComponent label="Light" noDirectUpdate={true} valuesAreStrings={true} options={lightOptions} target={lightInformationBlock} propertyName="name" onSelect={(name: any) => {
                         lightInformationBlock.light = scene.getLightByName(name);

+ 2 - 2
nodeEditor/src/diagram/properties/lightPropertyTabComponent.tsx

@@ -4,7 +4,7 @@ import { LineContainerComponent } from '../../sharedComponents/lineContainerComp
 import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponent';
 import { IPropertyComponentProps } from './propertyComponentProps';
 import { LightBlock } from 'babylonjs/Materials/Node/Blocks/Dual/lightBlock';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
+import { GeneralPropertyTabComponent } from './genericNodePropertyComponent';
 
 export class LightPropertyTabComponent extends React.Component<IPropertyComponentProps> {
 
@@ -20,7 +20,7 @@ export class LightPropertyTabComponent extends React.Component<IPropertyComponen
 
         return (
             <div>                
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+                <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
                 <LineContainerComponent title="PROPERTIES">
                     <OptionsLineComponent label="Light" defaultIfNull={0} noDirectUpdate={true} valuesAreStrings={true} options={lightOptions} target={lightBlock} propertyName="name" onSelect={(name: any) => {
                         if (name === "") {

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

@@ -0,0 +1,54 @@
+
+import * as React from "react";
+import { LineContainerComponent } from '../../sharedComponents/lineContainerComponent';
+import { GlobalState } from '../../globalState';
+import { TextInputLineComponent } from '../../sharedComponents/textInputLineComponent';
+import {  GraphFrame } from '../graphFrame';
+import { Nullable } from 'babylonjs/types';
+import { Observer } from 'babylonjs/Misc/observable';
+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
+    nodePort: NodePort;
+}
+
+export class NodePortPropertyTabComponent extends React.Component<IFrameNodePortPropertyTabComponentProps> {
+    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | NodePort | GraphNode | NodeLink | FramePortData>>>;
+
+    constructor(props: IFrameNodePortPropertyTabComponentProps) {
+        super(props);
+    }
+
+    componentWillUnmount() {
+        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">
+                <div id="header">
+                    <img id="logo" src="https://www.babylonjs.com/Assets/logo-babylonjs-social-twitter.png" />
+                    <div id="title">
+                        NODE MATERIAL EDITOR
+                </div>
+                </div>
+                <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.disabled} />}
+                    </LineContainerComponent>
+                </div>
+            </div>
+        );
+    }
+}

+ 0 - 33
nodeEditor/src/diagram/properties/remapNodePropertyComponent.tsx

@@ -1,33 +0,0 @@
-
-import * as React from "react";
-import { LineContainerComponent } from '../../sharedComponents/lineContainerComponent';
-import { Vector2LineComponent } from '../../sharedComponents/vector2LineComponent';
-import { IPropertyComponentProps } from './propertyComponentProps';
-import { RemapBlock } from 'babylonjs/Materials/Node/Blocks/remapBlock';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
-
-export class RemapPropertyTabComponent extends React.Component<IPropertyComponentProps> {
-
-    constructor(props: IPropertyComponentProps) {
-        super(props)
-    }
-
-    forceRebuild() {
-        this.props.globalState.onUpdateRequiredObservable.notifyObservers();
-        this.props.globalState.onRebuildRequiredObservable.notifyObservers();
-    }
-
-    render() {
-        let remapBlock = this.props.block as RemapBlock;
-      
-        return (
-            <div>                
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
-                <LineContainerComponent title="PROPERTIES">
-                  <Vector2LineComponent globalState={this.props.globalState} label="From" propertyName="sourceRange" target={remapBlock} onChange={() => this.forceRebuild()} />
-                  <Vector2LineComponent globalState={this.props.globalState} label="To" propertyName="targetRange" target={remapBlock} onChange={() => this.forceRebuild()} />
-                </LineContainerComponent>
-            </div>
-        );
-    }
-}

+ 19 - 8
nodeEditor/src/diagram/properties/texturePropertyTabComponent.tsx

@@ -14,13 +14,17 @@ import { CubeTexture } from 'babylonjs/Materials/Textures/cubeTexture';
 import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponent';
 import { IPropertyComponentProps } from './propertyComponentProps';
 import { ReflectionTextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock';
+import { ReflectionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectionBlock';
+import { RefractionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/refractionBlock';
 import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
+import { GeneralPropertyTabComponent, GenericPropertyTabComponent } from './genericNodePropertyComponent';
+
+type ReflectionTexture = ReflectionTextureBlock | ReflectionBlock | RefractionBlock;
 
 export class TexturePropertyTabComponent extends React.Component<IPropertyComponentProps, {isEmbedded: boolean, loadAsCubeTexture: boolean}> {
 
-    get textureBlock(): TextureBlock | ReflectionTextureBlock {
-        return this.props.block as TextureBlock | ReflectionTextureBlock;
+    get textureBlock(): TextureBlock | ReflectionTexture {
+        return this.props.block as TextureBlock | ReflectionTexture;
     }
 
     constructor(props: IPropertyComponentProps) {
@@ -33,7 +37,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
 
     UNSAFE_componentWillUpdate(nextProps: IPropertyComponentProps, nextState: {isEmbedded: boolean, loadAsCubeTexture: boolean}) {
         if (nextProps.block !== this.props.block) {
-            let texture = (nextProps.block as TextureBlock | ReflectionTextureBlock).texture as BaseTexture;
+            let texture = (nextProps.block as TextureBlock | ReflectionTexture).texture as BaseTexture;
 
             nextState.isEmbedded = !texture || texture.name.substring(0, 4) === "data";
             nextState.loadAsCubeTexture = texture && texture.isCube;
@@ -76,7 +80,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
 
         if (!texture) {
             if (!this.state.loadAsCubeTexture) {
-                this.textureBlock.texture = new Texture(null, this.props.globalState.nodeMaterial.getScene(), false, this.textureBlock instanceof ReflectionTextureBlock);
+                this.textureBlock.texture = new Texture(null, this.props.globalState.nodeMaterial.getScene(), false, this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock || this.textureBlock instanceof RefractionBlock);
                 texture = this.textureBlock.texture;
                 texture.coordinatesMode = Texture.EQUIRECTANGULAR_MODE;
             } else {
@@ -119,7 +123,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
         this._prepareTexture();
 
         let texture = this.textureBlock.texture as BaseTexture;       
-        if (texture.isCube || this.textureBlock instanceof ReflectionTextureBlock) {
+        if (texture.isCube || this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock || this.textureBlock instanceof RefractionBlock) {
             let extension: string | undefined = undefined;
             if (url.toLowerCase().indexOf(".dds") > 0) {
                 extension = ".dds";
@@ -143,7 +147,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
 
         url = url.replace(/\?nocache=\d+/, "");
 
-        let isInReflectionMode = this.textureBlock instanceof ReflectionTextureBlock;
+        let isInReflectionMode = this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock || this.textureBlock instanceof RefractionBlock;
 
         var reflectionModeOptions: {label: string, value: number}[] = [
             {
@@ -177,7 +181,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
         
         return (
             <div>                
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+                <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
                 <LineContainerComponent title="PROPERTIES">
                     <CheckBoxLineComponent label="Auto select UV" propertyName="autoSelectUV" target={this.props.block} onValueChanged={() => {                        
                         this.props.globalState.onUpdateRequiredObservable.notifyObservers();
@@ -189,6 +193,12 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
                         }}/>
                     }
                     {
+                        texture && !isInReflectionMode &&
+                        <CheckBoxLineComponent label="Convert to linear space" propertyName="convertToLinearSpace" target={this.props.block} onValueChanged={() => {                        
+                            this.props.globalState.onUpdateRequiredObservable.notifyObservers();
+                        }}/>
+                    }
+                    {
                         texture && isInReflectionMode &&
                         <OptionsLineComponent label="Reflection mode" options={reflectionModeOptions} target={texture} propertyName="coordinatesMode" onSelect={(value: any) => {
                             texture.coordinatesMode = value;
@@ -293,6 +303,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
                         <ButtonLineComponent label="Remove" onClick={() => this.removeTexture()}/>
                     }
                 </LineContainerComponent>
+                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
             </div>
         );
     }

+ 2 - 2
nodeEditor/src/diagram/properties/transformNodePropertyComponent.tsx

@@ -4,7 +4,7 @@ import { LineContainerComponent } from '../../sharedComponents/lineContainerComp
 import { IPropertyComponentProps } from './propertyComponentProps';
 import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
 import { TransformBlock } from 'babylonjs/Materials/Node/Blocks/transformBlock';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
+import { GeneralPropertyTabComponent } from './genericNodePropertyComponent';
 
 export class TransformPropertyTabComponent extends React.Component<IPropertyComponentProps> {
     constructor(props: IPropertyComponentProps) {
@@ -14,7 +14,7 @@ export class TransformPropertyTabComponent extends React.Component<IPropertyComp
     render() {
         return (
             <>                
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+                <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
                 <LineContainerComponent title="PROPERTIES">
                     <CheckBoxLineComponent label="Transform as direction" onSelect={value => {
                         let transformBlock = this.props.block as TransformBlock;

+ 2 - 2
nodeEditor/src/diagram/properties/trigonometryNodePropertyComponent.tsx

@@ -4,7 +4,7 @@ import { LineContainerComponent } from '../../sharedComponents/lineContainerComp
 import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponent';
 import { TrigonometryBlockOperations, TrigonometryBlock } from 'babylonjs/Materials/Node/Blocks/trigonometryBlock';
 import { IPropertyComponentProps } from './propertyComponentProps';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
+import { GeneralPropertyTabComponent } from './genericNodePropertyComponent';
 
 export class TrigonometryPropertyTabComponent extends React.Component<IPropertyComponentProps> {
 
@@ -41,7 +41,7 @@ export class TrigonometryPropertyTabComponent extends React.Component<IPropertyC
         
         return (
             <div>                
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
+                <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
                 <LineContainerComponent title="PROPERTIES">  
                     <OptionsLineComponent label="Operation" options={operationOptions} target={trigonometryBlock} propertyName="operation" onSelect={(value: any) => {
                         this.props.globalState.onUpdateRequiredObservable.notifyObservers();

+ 0 - 23
nodeEditor/src/diagram/properties/worleyNoise3DNodePropertyComponent.tsx

@@ -1,23 +0,0 @@
-
-import * as React from "react";
-import { LineContainerComponent } from '../../sharedComponents/lineContainerComponent';
-import { IPropertyComponentProps } from './propertyComponentProps';
-import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
-import { GenericPropertyTabComponent } from './genericNodePropertyComponent';
-
-export class WorleyNoise3DNodePropertyComponent extends React.Component<IPropertyComponentProps> {
-    constructor(props: IPropertyComponentProps) {
-        super(props)
-    }
-
-    render() {
-        return (
-            <>
-                <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
-                <LineContainerComponent title="PROPERTIES">
-                    <CheckBoxLineComponent label="Use Manhattan Distance" target={this.props.block} propertyName="manhattanDistance" onValueChanged={() => this.props.globalState.onRebuildRequiredObservable.notifyObservers()} />              
-                </LineContainerComponent>        
-            </>
-        );
-    }
-}

+ 2 - 8
nodeEditor/src/diagram/propertyLedger.ts

@@ -2,13 +2,9 @@ import { ComponentClass } from 'react';
 import { InputPropertyTabComponent } from './properties/inputNodePropertyComponent';
 import { IPropertyComponentProps } from './properties/propertyComponentProps';
 import { TransformPropertyTabComponent } from './properties/transformNodePropertyComponent';
-import { PerturbNormalPropertyTabComponent } from './properties/PerturbNormalNodePropertyComponent';
-import { WorleyNoise3DNodePropertyComponent } from './properties/worleyNoise3DNodePropertyComponent';
-import { ClampPropertyTabComponent } from './properties/clampNodePropertyComponent';
 import { GradientPropertyTabComponent } from './properties/gradientNodePropertyComponent';
 import { LightPropertyTabComponent } from './properties/lightPropertyTabComponent';
 import { LightInformationPropertyTabComponent } from './properties/lightInformationPropertyTabComponent';
-import { RemapPropertyTabComponent } from './properties/remapNodePropertyComponent';
 import { TexturePropertyTabComponent } from './properties/texturePropertyTabComponent';
 import { TrigonometryPropertyTabComponent } from './properties/trigonometryNodePropertyComponent';
 
@@ -18,13 +14,11 @@ export class PropertyLedger {
 
 PropertyLedger.RegisteredControls["TransformBlock"] = TransformPropertyTabComponent;
 PropertyLedger.RegisteredControls["InputBlock"] = InputPropertyTabComponent;
-PropertyLedger.RegisteredControls["PerturbNormalBlock"] = PerturbNormalPropertyTabComponent;
-PropertyLedger.RegisteredControls["WorleyNoise3DBlock"] = WorleyNoise3DNodePropertyComponent;
-PropertyLedger.RegisteredControls["ClampBlock"] = ClampPropertyTabComponent;
 PropertyLedger.RegisteredControls["GradientBlock"] = GradientPropertyTabComponent;
 PropertyLedger.RegisteredControls["LightBlock"] = LightPropertyTabComponent;
 PropertyLedger.RegisteredControls["LightInformationBlock"] = LightInformationPropertyTabComponent;
-PropertyLedger.RegisteredControls["RemapBlock"] = RemapPropertyTabComponent;
 PropertyLedger.RegisteredControls["TextureBlock"] = TexturePropertyTabComponent;
 PropertyLedger.RegisteredControls["ReflectionTextureBlock"] = TexturePropertyTabComponent;
+PropertyLedger.RegisteredControls["ReflectionBlock"] = TexturePropertyTabComponent;
+PropertyLedger.RegisteredControls["RefractionBlock"] = TexturePropertyTabComponent;
 PropertyLedger.RegisteredControls["TrigonometryBlock"] = TrigonometryPropertyTabComponent;

+ 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[] = [];

+ 15 - 5
nodeEditor/src/sharedComponents/checkBoxLineComponent.tsx

@@ -10,9 +10,10 @@ export interface ICheckBoxLineComponentProps {
     onSelect?: (value: boolean) => void;
     onValueChanged?: () => void;
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    disabled?: boolean;
 }
 
-export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponentProps, { isSelected: boolean }> {
+export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponentProps, { isSelected: boolean, isDisabled?: boolean }> {
     private static _UniqueIdSeed = 0;
     private _uniqueId: number;
     private _localChange = false;
@@ -26,9 +27,13 @@ export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponen
         } else {
             this.state = { isSelected: this.props.target[this.props.propertyName!] == true };
         }
+
+        if (this.props.disabled) {
+            this.state = { ...this.state, isDisabled: this.props.disabled };
+        }
     }
 
-    shouldComponentUpdate(nextProps: ICheckBoxLineComponentProps, nextState: { isSelected: boolean }) {
+    shouldComponentUpdate(nextProps: ICheckBoxLineComponentProps, nextState: { isSelected: boolean, isDisabled: boolean }) {
         var currentState: boolean;
 
         if (nextProps.isSelected) {
@@ -42,7 +47,12 @@ export class CheckBoxLineComponent extends React.Component<ICheckBoxLineComponen
             this._localChange = false;
             return true;
         }
-        return nextProps.label !== this.props.label;
+
+        if(nextProps.disabled !== !!nextState.isDisabled){
+            return true;
+        }
+        
+        return nextProps.label !== this.props.label || nextProps.target !== this.props.target;
     }
 
     onChange() {
@@ -76,8 +86,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>
         );

+ 1 - 1
package.json

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.2.0-alpha.11",
+    "version": "4.2.0-alpha.12",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 13 - 13
src/Audio/sound.ts

@@ -216,14 +216,14 @@ export class Sound {
             if (options.volume !== undefined) {
                 this._volume = options.volume;
             }
-            this.spatialSound = options.spatialSound || false;
-            this.maxDistance = options.maxDistance || 100;
-            this.useCustomAttenuation = options.useCustomAttenuation || false;
+            this.spatialSound = options.spatialSound ?? false;
+            this.maxDistance = options.maxDistance ?? 100;
+            this.useCustomAttenuation = options.useCustomAttenuation ?? false;
             this.rolloffFactor = options.rolloffFactor || 1;
             this.refDistance = options.refDistance || 1;
             this.distanceModel = options.distanceModel || "linear";
             this._playbackRate = options.playbackRate || 1;
-            this._streaming = options.streaming || false;
+            this._streaming = options.streaming ?? false;
             this._length = options.length;
             this._offset = options.offset;
         }
@@ -452,15 +452,15 @@ export class Sound {
      */
     public updateOptions(options: ISoundOptions): void {
         if (options) {
-            this.loop = options.loop || this.loop;
-            this.maxDistance = options.maxDistance || this.maxDistance;
-            this.useCustomAttenuation = options.useCustomAttenuation || this.useCustomAttenuation;
-            this.rolloffFactor = options.rolloffFactor || this.rolloffFactor;
-            this.refDistance = options.refDistance || this.refDistance;
-            this.distanceModel = options.distanceModel || this.distanceModel;
-            this._playbackRate = options.playbackRate || this._playbackRate;
-            this._length = options.length ? options.length / 1000 : undefined;
-            this._offset = options.offset ? options.offset / 1000 : undefined;
+            this.loop = options.loop ?? this.loop;
+            this.maxDistance = options.maxDistance ?? this.maxDistance;
+            this.useCustomAttenuation = options.useCustomAttenuation ?? this.useCustomAttenuation;
+            this.rolloffFactor = options.rolloffFactor ?? this.rolloffFactor;
+            this.refDistance = options.refDistance ?? this.refDistance;
+            this.distanceModel = options.distanceModel ?? this.distanceModel;
+            this._playbackRate = options.playbackRate ?? this._playbackRate;
+            this._length = options.length ?? undefined;
+            this._offset = options.offset ?? undefined;
             this._updateSpatialParameters();
             if (this.isPlaying) {
                 if (this._streaming && this._htmlAudioElement) {

+ 9 - 0
src/Debug/debugLayer.ts

@@ -263,6 +263,15 @@ export class DebugLayer {
     }
 
     /**
+     * Update the scene in the inspector
+     */
+    public setAsActiveScene() {
+        if (this.BJSINSPECTOR) {
+            this.BJSINSPECTOR.Inspector._SetNewScene(this._scene);
+        }
+    }
+
+    /**
       * Launch the debugLayer.
       * @param config Define the configuration of the inspector
       * @return a promise fulfilled when the debug layer is visible

+ 7 - 7
src/Engines/Extensions/engine.rawTexture.ts

@@ -105,7 +105,7 @@ declare module "../../Engines/thinEngine" {
          * @param onError defines a callback called if there is an error
          * @returns the cube texture as an InternalTexture
          */
-        createRawCubeTextureFromUrl(url: string, scene: Scene, size: number, format: number, type: number, noMipmap: boolean,
+        createRawCubeTextureFromUrl(url: string, scene: Nullable<Scene>, size: number, format: number, type: number, noMipmap: boolean,
             callback: (ArrayBuffer: ArrayBuffer) => Nullable<ArrayBufferView[]>,
             mipmapGenerator: Nullable<((faces: ArrayBufferView[]) => ArrayBufferView[][])>,
             onLoad: Nullable<() => void>,
@@ -127,7 +127,7 @@ declare module "../../Engines/thinEngine" {
          * @param invertY defines if data must be stored with Y axis inverted
          * @returns the cube texture as an InternalTexture
          */
-        createRawCubeTextureFromUrl(url: string, scene: Scene, size: number, format: number, type: number, noMipmap: boolean,
+        createRawCubeTextureFromUrl(url: string, scene: Nullable<Scene>, size: number, format: number, type: number, noMipmap: boolean,
             callback: (ArrayBuffer: ArrayBuffer) => Nullable<ArrayBufferView[]>,
             mipmapGenerator: Nullable<((faces: ArrayBufferView[]) => ArrayBufferView[][])>,
             onLoad: Nullable<() => void>,
@@ -410,7 +410,7 @@ ThinEngine.prototype.updateRawCubeTexture = function(texture: InternalTexture, d
     texture.isReady = true;
 };
 
-ThinEngine.prototype.createRawCubeTextureFromUrl = function(url: string, scene: Scene, size: number, format: number, type: number, noMipmap: boolean,
+ThinEngine.prototype.createRawCubeTextureFromUrl = function(url: string, scene: Nullable<Scene>, size: number, format: number, type: number, noMipmap: boolean,
     callback: (ArrayBuffer: ArrayBuffer) => Nullable<ArrayBufferView[]>,
     mipmapGenerator: Nullable<((faces: ArrayBufferView[]) => ArrayBufferView[][])>,
     onLoad: Nullable<() => void> = null,
@@ -420,12 +420,12 @@ ThinEngine.prototype.createRawCubeTextureFromUrl = function(url: string, scene:
 
     var gl = this._gl;
     var texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode, null);
-    scene._addPendingData(texture);
+    scene?._addPendingData(texture);
     texture.url = url;
     this._internalTexturesCache.push(texture);
 
     var onerror = (request?: IWebRequest, exception?: any) => {
-        scene._removePendingData(texture);
+        scene?._removePendingData(texture);
         if (onError && request) {
             onError(request.status + " " + request.statusText, exception);
         }
@@ -474,7 +474,7 @@ ThinEngine.prototype.createRawCubeTextureFromUrl = function(url: string, scene:
 
         texture.isReady = true;
         // this.resetTextureCache();
-        scene._removePendingData(texture);
+        scene?._removePendingData(texture);
 
         if (onLoad) {
             onLoad();
@@ -483,7 +483,7 @@ ThinEngine.prototype.createRawCubeTextureFromUrl = function(url: string, scene:
 
     this._loadFile(url, (data) => {
         internalCallback(data);
-    }, undefined, scene.offlineProvider, true, onerror);
+    }, undefined, scene?.offlineProvider, true, onerror);
 
     return texture;
 };

+ 52 - 0
src/Engines/Extensions/engine.readTexture.ts

@@ -0,0 +1,52 @@
+import { ThinEngine } from "../../Engines/thinEngine";
+import { InternalTexture } from '../../Materials/Textures/internalTexture';
+import { Nullable } from '../../types';
+
+declare module "../../Engines/thinEngine" {
+    export interface ThinEngine {
+        /** @hidden */
+        _readTexturePixels(texture: InternalTexture, width: number, height: number, faceIndex?: number, level?: number, buffer?: Nullable<ArrayBufferView>): ArrayBufferView;
+    }
+}
+
+ThinEngine.prototype._readTexturePixels = function(texture: InternalTexture, width: number, height: number, faceIndex = -1, level = 0, buffer: Nullable<ArrayBufferView> = null): ArrayBufferView {
+    let gl = this._gl;
+    if (!this._dummyFramebuffer) {
+        let dummy = gl.createFramebuffer();
+
+        if (!dummy) {
+            throw new Error("Unable to create dummy framebuffer");
+        }
+
+        this._dummyFramebuffer = dummy;
+    }
+    gl.bindFramebuffer(gl.FRAMEBUFFER, this._dummyFramebuffer);
+
+    if (faceIndex > -1) {
+        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture._webGLTexture, level);
+    } else {
+        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture._webGLTexture, level);
+    }
+
+    let readType = (texture.type !== undefined) ? this._getWebGLTextureType(texture.type) : gl.UNSIGNED_BYTE;
+
+    switch (readType) {
+        case gl.UNSIGNED_BYTE:
+            if (!buffer) {
+                buffer = new Uint8Array(4 * width * height);
+            }
+            readType = gl.UNSIGNED_BYTE;
+            break;
+        default:
+            if (!buffer) {
+                buffer = new Float32Array(4 * width * height);
+            }
+            readType = gl.FLOAT;
+            break;
+    }
+
+    gl.readPixels(0, 0, width, height, gl.RGBA, readType, <DataView>buffer);
+    gl.bindFramebuffer(gl.FRAMEBUFFER, this._currentFramebuffer);
+
+    return buffer;
+};

+ 5 - 0
src/Engines/Extensions/engine.views.ts

@@ -139,6 +139,11 @@ Engine.prototype._renderViews = function() {
             canvas.height = canvas.clientHeight;
             parent.width = canvas.clientWidth;
             parent.height = canvas.clientHeight;
+            this.resize();
+        }
+
+        if (!parent.width || !parent.height) {
+            return false;
         }
 
         // Render the frame

+ 1 - 0
src/Engines/Extensions/index.ts

@@ -12,6 +12,7 @@ export * from "./engine.renderTargetCube";
 export * from "./engine.webVR";
 export * from "./engine.uniformBuffer";
 export * from "./engine.views";
+export * from "./engine.readTexture";
 
 // must import first since nothing references the exports
 import "./engine.textureSelector";

+ 0 - 0
src/Engines/engine.ts


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů