Browse Source

Merge remote-tracking branch 'upstream/master' into feature_points

Alex Tran 5 years ago
parent
commit
9dfad7cf76
100 changed files with 7982 additions and 2966 deletions
  1. 433 87
      dist/preview release/babylon.d.ts
  2. 2 2
      dist/preview release/babylon.js
  3. 1866 725
      dist/preview release/babylon.max.js
  4. 1 1
      dist/preview release/babylon.max.js.map
  5. 916 186
      dist/preview release/babylon.module.d.ts
  6. 440 90
      dist/preview release/documentation.d.ts
  7. 1 1
      dist/preview release/glTF2Interface/package.json
  8. 2 2
      dist/preview release/gui/package.json
  9. 6 6
      dist/preview release/inspector/babylon.inspector.bundle.js
  10. 679 288
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  11. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  12. 135 64
      dist/preview release/inspector/babylon.inspector.d.ts
  13. 294 140
      dist/preview release/inspector/babylon.inspector.module.d.ts
  14. 7 7
      dist/preview release/inspector/package.json
  15. 4 0
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  16. 1 1
      dist/preview release/loaders/babylon.glTF1FileLoader.js.map
  17. 1 1
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  18. 22 7
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  19. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js.map
  20. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  21. 22 7
      dist/preview release/loaders/babylon.glTFFileLoader.js
  22. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js.map
  23. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  24. 7 3
      dist/preview release/loaders/babylonjs.loaders.d.ts
  25. 22 7
      dist/preview release/loaders/babylonjs.loaders.js
  26. 1 1
      dist/preview release/loaders/babylonjs.loaders.js.map
  27. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  28. 14 6
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  29. 3 3
      dist/preview release/loaders/package.json
  30. 2 2
      dist/preview release/materialsLibrary/package.json
  31. 2 2
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  32. 2 3
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  33. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  34. 2 2
      dist/preview release/nodeEditor/package.json
  35. 1 1
      dist/preview release/package.json
  36. 1 1
      dist/preview release/packagesSizeBaseLine.json
  37. 2 2
      dist/preview release/postProcessesLibrary/package.json
  38. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  39. 3 3
      dist/preview release/serializers/package.json
  40. 916 186
      dist/preview release/viewer/babylon.module.d.ts
  41. 130 122
      dist/preview release/viewer/babylon.viewer.js
  42. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  43. 14 6
      dist/preview release/viewer/babylonjs.loaders.module.d.ts
  44. 8 3
      dist/preview release/what's new.md
  45. 10 1
      inspector/src/components/actionTabs/lines/vector3LineComponent.tsx
  46. 2 0
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/freeCameraPropertyGridComponent.tsx
  47. 4 3
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  48. 5 5
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar.tsx
  49. 0 59
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/Paintbrush.ts
  50. 100 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.ts
  51. 6 5
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/defaultTools.ts
  52. 59 60
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/Eyedropper.ts
  53. 44 44
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/Floodfill.ts
  54. 97 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.ts
  55. 10 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/rectangleSelect.ts
  56. 143 143
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/propertiesBar.tsx
  57. 5 5
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasComponent.tsx
  58. 293 107
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts
  59. 8 2
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditor.scss
  60. 83 35
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx
  61. 100 101
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar.tsx
  62. 193 124
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/skeletonPropertyGridComponent.tsx
  63. 9 5
      inspector/src/components/actionTabs/tabs/propertyGrids/particleSystems/particleSystemPropertyGridComponent.tsx
  64. 21 0
      inspector/src/components/globalState.ts
  65. 4 0
      inspector/src/components/popupComponent.tsx
  66. 24 5
      inspector/src/components/sceneExplorer/entities/cameraTreeItemComponent.tsx
  67. 82 23
      inspector/src/components/sceneExplorer/entities/sceneTreeItemComponent.tsx
  68. 6 2
      inspector/src/components/sceneExplorer/sceneExplorer.scss
  69. 6 4
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  70. 1 1
      inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx
  71. 5 0
      inspector/src/inspector.ts
  72. 1 1
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  73. 23 9
      loaders/src/glTF/2.0/glTFLoader.ts
  74. 1 1
      loaders/src/glTF/2.0/glTFLoaderExtension.ts
  75. 5 0
      loaders/src/glTF/glTFFileLoader.ts
  76. 1 3
      nodeEditor/src/components/preview/previewMeshControlComponent.tsx
  77. 11 1
      nodeEditor/src/main.scss
  78. 1 1
      package.json
  79. 15 1
      sandbox/src/components/renderingZone.tsx
  80. 18 0
      src/Bones/bone.ts
  81. 2 1
      src/Cameras/Inputs/freeCameraTouchInput.ts
  82. 1 0
      src/Cameras/arcRotateCamera.ts
  83. 7 3
      src/Cameras/followCamera.ts
  84. 11 0
      src/Cameras/targetCamera.ts
  85. 18 12
      src/Debug/rayHelper.ts
  86. 225 156
      src/Debug/skeletonViewer.ts
  87. 2 2
      src/Engines/engine.ts
  88. 18 21
      src/Engines/nativeEngine.ts
  89. 6 3
      src/Engines/thinEngine.ts
  90. 190 0
      src/Gizmos/cameraGizmo.ts
  91. 72 10
      src/Gizmos/gizmoManager.ts
  92. 1 0
      src/Gizmos/index.ts
  93. 9 3
      src/Inputs/scene.inputManager.ts
  94. 14 0
      src/Loading/Plugins/babylonFileLoader.ts
  95. 3 0
      src/Materials/Textures/Procedurals/proceduralTexture.ts
  96. 2 2
      src/Materials/Textures/baseTexture.ts
  97. 30 23
      src/Materials/Textures/rawTexture.ts
  98. 1 0
      src/Materials/Textures/texture.ts
  99. 1 0
      src/Materials/index.ts
  100. 0 0
      src/Materials/material.detailMapConfiguration.ts

File diff suppressed because it is too large
+ 433 - 87
dist/preview release/babylon.d.ts


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


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


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


File diff suppressed because it is too large
+ 916 - 186
dist/preview release/babylon.module.d.ts


File diff suppressed because it is too large
+ 440 - 90
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.28",
+    "version": "4.2.0-alpha.31",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 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.28",
+    "version": "4.2.0-alpha.31",
     "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.28"
+        "babylonjs": "4.2.0-alpha.31"
     },
     "engines": {
         "node": "*"

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


File diff suppressed because it is too large
+ 679 - 288
dist/preview release/inspector/babylon.inspector.bundle.max.js


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


+ 135 - 64
dist/preview release/inspector/babylon.inspector.d.ts

@@ -59,6 +59,8 @@ declare module INSPECTOR {
         prepareGLTFPlugin(loader: BABYLON.GLTFFileLoader): void;
         lightGizmos: Array<BABYLON.LightGizmo>;
         enableLightGizmo(light: BABYLON.Light, enable?: boolean): void;
+        cameraGizmos: Array<BABYLON.CameraGizmo>;
+        enableCameraGizmo(camera: BABYLON.Camera, enable?: boolean): void;
     }
 }
 declare module INSPECTOR {
@@ -365,6 +367,7 @@ declare module INSPECTOR {
         onChange?: (newvalue: BABYLON.Vector3) => void;
         useEuler?: boolean;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+        noSlider?: boolean;
     }
     export class Vector3LineComponent extends React.Component<IVector3LineComponentProps, {
         isExpanded: boolean;
@@ -1179,6 +1182,7 @@ declare module INSPECTOR {
         componentDidMount(): void;
         openPopup(): void;
         componentWillUnmount(): void;
+        getWindow(): Window | null;
         render(): React.ReactPortal | null;
     }
 }
@@ -1315,50 +1319,48 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    export interface Tool {
-        type: any;
-        name: string;
-        instance: any;
-        icon: string;
+    export interface ITool extends IToolData {
+        instance: IToolType;
     }
-    interface ToolBarProps {
-        tools: Tool[];
+    interface IToolBarProps {
+        tools: ITool[];
         addTool(url: string): void;
         changeTool(toolIndex: number): void;
         activeToolIndex: number;
         metadata: any;
         setMetadata(data: any): void;
     }
-    interface ToolBarState {
+    interface IToolBarState {
         toolURL: string;
         pickerOpen: boolean;
         addOpen: boolean;
     }
-    export class ToolBar extends React.Component<ToolBarProps, ToolBarState> {
-        private pickerRef;
-        constructor(props: ToolBarProps);
+    export class ToolBar extends React.Component<IToolBarProps, IToolBarState> {
+        private _addTool;
+        private _pickerRef;
+        constructor(props: IToolBarProps);
         computeRGBAColor(): string;
         render(): JSX.Element;
     }
 }
 declare module INSPECTOR {
-    export interface Channel {
+    export interface IChannel {
         visible: boolean;
         editable: boolean;
         name: string;
         id: 'R' | 'G' | 'B' | 'A';
         icon: any;
     }
-    interface ChannelsBarProps {
-        channels: Channel[];
-        setChannels(channelState: Channel[]): void;
+    interface IChannelsBarProps {
+        channels: IChannel[];
+        setChannels(channelState: IChannel[]): void;
     }
-    export class ChannelsBar extends React.Component<ChannelsBarProps> {
+    export class ChannelsBar extends React.Component<IChannelsBarProps> {
         render(): JSX.Element;
     }
 }
 declare module INSPECTOR {
-    export interface PixelData {
+    export interface IPixelData {
         x?: number;
         y?: number;
         r?: number;
@@ -1366,6 +1368,13 @@ declare module INSPECTOR {
         b?: number;
         a?: number;
     }
+    export interface IToolGUI {
+        adt: BABYLON.GUI.AdvancedDynamicTexture;
+        toolWindow: BABYLON.GUI.StackPanel;
+        isDragging: boolean;
+        dragCoords: BABYLON.Nullable<BABYLON.Vector2>;
+        style: BABYLON.GUI.Style;
+    }
     export class TextureCanvasManager {
         private _engine;
         private _scene;
@@ -1377,17 +1386,20 @@ declare module INSPECTOR {
         private _UICanvas;
         private _size;
         private _2DCanvas;
-        private _displayCanvas;
+        private _3DCanvas;
+        private _channelsTexture;
+        private _3DEngine;
+        private _3DPlane;
+        private _3DCanvasTexture;
+        private _3DScene;
         private _channels;
         private _face;
-        private _displayTexture;
         private _originalTexture;
         private _target;
         private _originalInternalTexture;
         private _didEdit;
         private _plane;
         private _planeMaterial;
-        private _planeFallbackMaterial;
         private _keyMap;
         private static ZOOM_MOUSE_SPEED;
         private static ZOOM_KEYBOARD_SPEED;
@@ -1395,55 +1407,66 @@ declare module INSPECTOR {
         private static ZOOM_OUT_KEY;
         private static PAN_SPEED;
         private static PAN_MOUSE_BUTTON;
-        private static PAN_KEY;
         private static MIN_SCALE;
         private static MAX_SCALE;
         private _tool;
         private _setPixelData;
+        private _GUI;
+        private _window;
         metadata: any;
-        constructor(texture: BABYLON.BaseTexture, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvasDisplay: HTMLCanvasElement, setPixelData: (pixelData: PixelData) => void);
+        private _editing3D;
+        constructor(texture: BABYLON.BaseTexture, window: Window, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvas3D: HTMLCanvasElement, setPixelData: (pixelData: IPixelData) => void);
         updateTexture(): Promise<void>;
-        private copyTextureToDisplayTexture;
-        set channels(channels: Channel[]);
+        private updateDisplay;
+        set channels(channels: IChannel[]);
         static paintPixelsOnCanvas(pixelData: Uint8Array, canvas: HTMLCanvasElement): void;
-        grabOriginalTexture(): void;
+        grabOriginalTexture(adjustZoom?: boolean): void;
         getMouseCoordinates(pointerInfo: BABYLON.PointerInfo): BABYLON.Vector2;
         get scene(): BABYLON.Scene;
         get canvas2D(): HTMLCanvasElement;
         get size(): BABYLON.ISize;
-        set tool(tool: BABYLON.Nullable<Tool>);
-        get tool(): BABYLON.Nullable<Tool>;
+        set tool(tool: BABYLON.Nullable<ITool>);
+        get tool(): BABYLON.Nullable<ITool>;
         set face(face: number);
+        /** Returns the tool GUI object, allowing tools to access the GUI */
+        get GUI(): IToolGUI;
+        /** Returns the 3D scene used for postprocesses */
+        get scene3D(): BABYLON.Scene;
         private makePlane;
         reset(): void;
         resize(newSize: BABYLON.ISize): Promise<void>;
-        private updateSize;
+        setSize(size: BABYLON.ISize, adjustZoom?: boolean): void;
         upload(file: File): void;
         dispose(): void;
     }
 }
 declare module INSPECTOR {
-    interface PropertiesBarProps {
+    interface IPropertiesBarProps {
         texture: BABYLON.BaseTexture;
         saveTexture(): void;
-        pixelData: PixelData;
+        pixelData: IPixelData;
         face: number;
         setFace(face: number): void;
         resetTexture(): void;
         resizeTexture(width: number, height: number): void;
         uploadTexture(file: File): void;
     }
-    interface PropertiesBarState {
+    interface IPropertiesBarState {
         width: number;
         height: number;
     }
-    interface PixelDataProps {
-        name: string;
-        data?: number;
-    }
-    function PixelData(props: PixelDataProps): JSX.Element;
-    export class PropertiesBar extends React.Component<PropertiesBarProps, PropertiesBarState> {
-        constructor(props: PropertiesBarProps);
+    export class PropertiesBar extends React.Component<IPropertiesBarProps, IPropertiesBarState> {
+        private _resetButton;
+        private _uploadButton;
+        private _saveButton;
+        private _babylonLogo;
+        private _resizeButton;
+        private _mipUp;
+        private _mipDown;
+        private _faces;
+        constructor(props: IPropertiesBarProps);
+        private pixelData;
+        private getNewDimension;
         render(): JSX.Element;
     }
 }
@@ -1456,72 +1479,110 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    interface TextureCanvasComponentProps {
+    interface ITextureCanvasComponentProps {
         canvasUI: React.RefObject<HTMLCanvasElement>;
         canvas2D: React.RefObject<HTMLCanvasElement>;
-        canvasDisplay: React.RefObject<HTMLCanvasElement>;
+        canvas3D: React.RefObject<HTMLCanvasElement>;
         texture: BABYLON.BaseTexture;
     }
-    export class TextureCanvasComponent extends React.Component<TextureCanvasComponentProps> {
-        shouldComponentUpdate(nextProps: TextureCanvasComponentProps): boolean;
+    export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {
+        shouldComponentUpdate(nextProps: ITextureCanvasComponentProps): boolean;
         render(): JSX.Element;
     }
 }
 declare module INSPECTOR {
-    export const Paintbrush: ToolData;
+    export const Paintbrush: IToolData;
+}
+declare module INSPECTOR {
+    export const Eyedropper: IToolData;
 }
 declare module INSPECTOR {
-    export const Eyedropper: ToolData;
+    export const Floodfill: IToolData;
 }
 declare module INSPECTOR {
-    export const Floodfill: ToolData;
+    export const Contrast: IToolData;
 }
 declare module INSPECTOR {
-    const _default: import("babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent").ToolData[];
+    const _default: import("babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent").IToolData[];
     export default _default;
 }
 declare module INSPECTOR {
-    interface TextureEditorComponentProps {
+    interface ITextureEditorComponentProps {
         globalState: GlobalState;
         texture: BABYLON.BaseTexture;
         url: string;
+        window: React.RefObject<PopupComponent>;
     }
-    interface TextureEditorComponentState {
-        tools: Tool[];
+    interface ITextureEditorComponentState {
+        tools: ITool[];
         activeToolIndex: number;
         metadata: any;
-        channels: Channel[];
-        pixelData: PixelData;
+        channels: IChannel[];
+        pixelData: IPixelData;
         face: number;
     }
-    export interface ToolParameters {
+    export interface IToolParameters {
+        /** The visible scene in the editor. Useful for adding pointer and keyboard events. */
         scene: BABYLON.Scene;
+        /** The 2D canvas which tools can paint on using the canvas API. */
         canvas2D: HTMLCanvasElement;
+        /** The 3D scene which tools can add post processes to. */
+        scene3D: BABYLON.Scene;
+        /** The size of the texture. */
         size: BABYLON.ISize;
+        /** Pushes the editor texture back to the original scene. This should be called every time a tool makes any modification to a texture. */
         updateTexture: () => void;
-        getMetadata: () => any;
+        /** The metadata object which is shared between all tools. Feel free to store any information here. Do not set this directly: instead call setMetadata. */
+        metadata: any;
+        /** Call this when you want to mutate the metadata. */
         setMetadata: (data: any) => void;
-    }
-    export interface ToolData {
+        /** Returns the texture coordinates under the cursor */
+        getMouseCoordinates: (pointerInfo: BABYLON.PointerInfo) => BABYLON.Vector2;
+        /** An object which holds the GUI's ADT as well as the tool window. */
+        GUI: IToolGUI;
+        /** Provides access to the BABYLON namespace */
+        BABYLON: any;
+    }
+    /** An interface representing the definition of a tool */
+    export interface IToolData {
+        /** Name to display on the toolbar */
         name: string;
-        type: any;
+        /** A class definition for the tool including setup and cleanup methods */
+        type: IToolConstructable;
+        /**  An SVG icon encoded in Base64 */
         icon: string;
+        /** Whether the tool uses the draggable GUI window */
+        usesWindow?: boolean;
+        /** Whether the tool uses postprocesses */
+        is3D?: boolean;
+    }
+    export interface IToolType {
+        /** Called when the tool is selected. */
+        setup: () => void;
+        /** Called when the tool is deselected. */
+        cleanup: () => void;
+        /** Optional. Called when the user resets the texture or uploads a new texture. Tools may want to reset their state when this happens. */
+        onReset?: () => void;
+    }
+    /** For constructable types, TS requires that you define a seperate interface which constructs your actual interface */
+    interface IToolConstructable {
+        new (getParameters: () => IToolParameters): IToolType;
     }
     global {
-        var _TOOL_DATA_: ToolData;
+        var _TOOL_DATA_: IToolData;
     }
-    export class TextureEditorComponent extends React.Component<TextureEditorComponentProps, TextureEditorComponentState> {
+    export class TextureEditorComponent extends React.Component<ITextureEditorComponentProps, ITextureEditorComponentState> {
         private _textureCanvasManager;
-        private canvasUI;
-        private canvas2D;
-        private canvasDisplay;
-        constructor(props: TextureEditorComponentProps);
+        private _UICanvas;
+        private _2DCanvas;
+        private _3DCanvas;
+        constructor(props: ITextureEditorComponentProps);
         componentDidMount(): void;
         componentDidUpdate(): void;
         componentWillUnmount(): void;
         loadToolFromURL(url: string): void;
-        addTools(tools: ToolData[]): void;
-        getToolParameters(): ToolParameters;
+        addTools(tools: IToolData[]): void;
+        getToolParameters(): IToolParameters;
         changeTool(index: number): void;
         setMetadata(newMetadata: any): void;
         setFace(face: number): void;
@@ -1545,6 +1606,7 @@ declare module INSPECTOR {
     }
     export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps, ITexturePropertyGridComponentState> {
         private _adtInstrumentation;
+        private popoutWindowRef;
         private textureLineRef;
         constructor(props: ITexturePropertyGridComponentProps);
         componentWillUnmount(): void;
@@ -2223,10 +2285,13 @@ declare module INSPECTOR {
     }
     export class SkeletonPropertyGridComponent extends React.Component<ISkeletonPropertyGridComponentProps> {
         private _skeletonViewersEnabled;
+        private _skeletonViewerDisplayOptions;
         private _skeletonViewers;
         constructor(props: ISkeletonPropertyGridComponentProps);
         switchSkeletonViewers(): void;
         checkSkeletonViewerState(props: ISkeletonPropertyGridComponentProps): void;
+        changeDisplayMode(): void;
+        changeDisplayOptions(option: string, value: number): void;
         shouldComponentUpdate(nextProps: ISkeletonPropertyGridComponentProps): boolean;
         onOverrideMeshLink(): void;
         render(): JSX.Element;
@@ -2852,15 +2917,18 @@ declare module INSPECTOR {
         camera: BABYLON.Camera;
         extensibilityGroups?: BABYLON.IExplorerExtensibilityGroup[];
         onClick: () => void;
+        globalState: GlobalState;
     }
     export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComponentProps, {
         isActive: boolean;
+        isGizmoEnabled: boolean;
     }> {
         private _onBeforeRenderObserver;
         constructor(props: ICameraTreeItemComponentProps);
         setActive(): void;
         componentDidMount(): void;
         componentWillUnmount(): void;
+        toggleGizmo(): void;
         render(): JSX.Element;
     }
 }
@@ -3285,4 +3353,7 @@ declare module INSPECTOR {
         calculateMove(): string;
         render(): JSX.Element;
     }
+}
+declare module INSPECTOR {
+    export const RectangleSelect: IToolData;
 }

+ 294 - 140
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -28,7 +28,9 @@ declare module "babylonjs-inspector/components/globalState" {
     import { ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs/Loading/sceneLoader";
     import { Scene } from "babylonjs/scene";
     import { Light } from "babylonjs/Lights/light";
+    import { Camera } from "babylonjs/Cameras/camera";
     import { LightGizmo } from "babylonjs/Gizmos/lightGizmo";
+    import { CameraGizmo } from "babylonjs/Gizmos/cameraGizmo";
     import { PropertyChangedEvent } from "babylonjs-inspector/components/propertyChangedEvent";
     import { ReplayRecorder } from "babylonjs-inspector/components/replayRecorder";
     export class GlobalState {
@@ -70,6 +72,8 @@ declare module "babylonjs-inspector/components/globalState" {
         prepareGLTFPlugin(loader: GLTFFileLoader): void;
         lightGizmos: Array<LightGizmo>;
         enableLightGizmo(light: Light, enable?: boolean): void;
+        cameraGizmos: Array<CameraGizmo>;
+        enableCameraGizmo(camera: Camera, enable?: boolean): void;
     }
 }
 declare module "babylonjs-inspector/components/actionTabs/paneComponent" {
@@ -413,6 +417,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/vector3LineCompo
         onChange?: (newvalue: Vector3) => void;
         useEuler?: boolean;
         onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+        noSlider?: boolean;
     }
     export class Vector3LineComponent extends React.Component<IVector3LineComponentProps, {
         isExpanded: boolean;
@@ -1314,6 +1319,7 @@ declare module "babylonjs-inspector/components/popupComponent" {
         componentDidMount(): void;
         openPopup(): void;
         componentWillUnmount(): void;
+        getWindow(): Window | null;
         render(): React.ReactPortal | null;
     }
 }
@@ -1487,46 +1493,45 @@ declare module "babylonjs-inspector/components/actionTabs/lines/textureLineCompo
 }
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar" {
     import * as React from 'react';
-    export interface Tool {
-        type: any;
-        name: string;
-        instance: any;
-        icon: string;
+    import { IToolData, IToolType } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
+    export interface ITool extends IToolData {
+        instance: IToolType;
     }
-    interface ToolBarProps {
-        tools: Tool[];
+    interface IToolBarProps {
+        tools: ITool[];
         addTool(url: string): void;
         changeTool(toolIndex: number): void;
         activeToolIndex: number;
         metadata: any;
         setMetadata(data: any): void;
     }
-    interface ToolBarState {
+    interface IToolBarState {
         toolURL: string;
         pickerOpen: boolean;
         addOpen: boolean;
     }
-    export class ToolBar extends React.Component<ToolBarProps, ToolBarState> {
-        private pickerRef;
-        constructor(props: ToolBarProps);
+    export class ToolBar extends React.Component<IToolBarProps, IToolBarState> {
+        private _addTool;
+        private _pickerRef;
+        constructor(props: IToolBarProps);
         computeRGBAColor(): string;
         render(): JSX.Element;
     }
 }
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar" {
     import * as React from 'react';
-    export interface Channel {
+    export interface IChannel {
         visible: boolean;
         editable: boolean;
         name: string;
         id: 'R' | 'G' | 'B' | 'A';
         icon: any;
     }
-    interface ChannelsBarProps {
-        channels: Channel[];
-        setChannels(channelState: Channel[]): void;
+    interface IChannelsBarProps {
+        channels: IChannel[];
+        setChannels(channelState: IChannel[]): void;
     }
-    export class ChannelsBar extends React.Component<ChannelsBarProps> {
+    export class ChannelsBar extends React.Component<IChannelsBarProps> {
         render(): JSX.Element;
     }
 }
@@ -1537,9 +1542,12 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
     import { ISize } from 'babylonjs/Maths/math.size';
     import { PointerInfo } from 'babylonjs/Events/pointerEvents';
-    import { Tool } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar";
-    import { Channel } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar";
-    export interface PixelData {
+    import { ITool } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar";
+    import { IChannel } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar";
+    import { StackPanel } from 'babylonjs-gui/2D/controls/stackPanel';
+    import { Style } from 'babylonjs-gui/2D/style';
+    import { AdvancedDynamicTexture } from 'babylonjs-gui/2D/advancedDynamicTexture';
+    export interface IPixelData {
         x?: number;
         y?: number;
         r?: number;
@@ -1547,6 +1555,13 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         b?: number;
         a?: number;
     }
+    export interface IToolGUI {
+        adt: AdvancedDynamicTexture;
+        toolWindow: StackPanel;
+        isDragging: boolean;
+        dragCoords: Nullable<Vector2>;
+        style: Style;
+    }
     export class TextureCanvasManager {
         private _engine;
         private _scene;
@@ -1558,17 +1573,20 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         private _UICanvas;
         private _size;
         private _2DCanvas;
-        private _displayCanvas;
+        private _3DCanvas;
+        private _channelsTexture;
+        private _3DEngine;
+        private _3DPlane;
+        private _3DCanvasTexture;
+        private _3DScene;
         private _channels;
         private _face;
-        private _displayTexture;
         private _originalTexture;
         private _target;
         private _originalInternalTexture;
         private _didEdit;
         private _plane;
         private _planeMaterial;
-        private _planeFallbackMaterial;
         private _keyMap;
         private static ZOOM_MOUSE_SPEED;
         private static ZOOM_KEYBOARD_SPEED;
@@ -1576,29 +1594,35 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
         private static ZOOM_OUT_KEY;
         private static PAN_SPEED;
         private static PAN_MOUSE_BUTTON;
-        private static PAN_KEY;
         private static MIN_SCALE;
         private static MAX_SCALE;
         private _tool;
         private _setPixelData;
+        private _GUI;
+        private _window;
         metadata: any;
-        constructor(texture: BaseTexture, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvasDisplay: HTMLCanvasElement, setPixelData: (pixelData: PixelData) => void);
+        private _editing3D;
+        constructor(texture: BaseTexture, window: Window, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvas3D: HTMLCanvasElement, setPixelData: (pixelData: IPixelData) => void);
         updateTexture(): Promise<void>;
-        private copyTextureToDisplayTexture;
-        set channels(channels: Channel[]);
+        private updateDisplay;
+        set channels(channels: IChannel[]);
         static paintPixelsOnCanvas(pixelData: Uint8Array, canvas: HTMLCanvasElement): void;
-        grabOriginalTexture(): void;
+        grabOriginalTexture(adjustZoom?: boolean): void;
         getMouseCoordinates(pointerInfo: PointerInfo): Vector2;
         get scene(): Scene;
         get canvas2D(): HTMLCanvasElement;
         get size(): ISize;
-        set tool(tool: Nullable<Tool>);
-        get tool(): Nullable<Tool>;
+        set tool(tool: Nullable<ITool>);
+        get tool(): Nullable<ITool>;
         set face(face: number);
+        /** Returns the tool GUI object, allowing tools to access the GUI */
+        get GUI(): IToolGUI;
+        /** Returns the 3D scene used for postprocesses */
+        get scene3D(): Scene;
         private makePlane;
         reset(): void;
         resize(newSize: ISize): Promise<void>;
-        private updateSize;
+        setSize(size: ISize, adjustZoom?: boolean): void;
         upload(file: File): void;
         dispose(): void;
     }
@@ -1606,28 +1630,33 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/propertiesBar" {
     import * as React from 'react';
     import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
-    import { PixelData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager";
-    interface PropertiesBarProps {
+    import { IPixelData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager";
+    interface IPropertiesBarProps {
         texture: BaseTexture;
         saveTexture(): void;
-        pixelData: PixelData;
+        pixelData: IPixelData;
         face: number;
         setFace(face: number): void;
         resetTexture(): void;
         resizeTexture(width: number, height: number): void;
         uploadTexture(file: File): void;
     }
-    interface PropertiesBarState {
+    interface IPropertiesBarState {
         width: number;
         height: number;
     }
-    interface PixelDataProps {
-        name: string;
-        data?: number;
-    }
-    function PixelData(props: PixelDataProps): JSX.Element;
-    export class PropertiesBar extends React.Component<PropertiesBarProps, PropertiesBarState> {
-        constructor(props: PropertiesBarProps);
+    export class PropertiesBar extends React.Component<IPropertiesBarProps, IPropertiesBarState> {
+        private _resetButton;
+        private _uploadButton;
+        private _saveButton;
+        private _babylonLogo;
+        private _resizeButton;
+        private _mipUp;
+        private _mipDown;
+        private _faces;
+        constructor(props: IPropertiesBarProps);
+        private pixelData;
+        private getNewDimension;
         render(): JSX.Element;
     }
 }
@@ -1643,83 +1672,125 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasComponent" {
     import * as React from 'react';
     import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
-    interface TextureCanvasComponentProps {
+    interface ITextureCanvasComponentProps {
         canvasUI: React.RefObject<HTMLCanvasElement>;
         canvas2D: React.RefObject<HTMLCanvasElement>;
-        canvasDisplay: React.RefObject<HTMLCanvasElement>;
+        canvas3D: React.RefObject<HTMLCanvasElement>;
         texture: BaseTexture;
     }
-    export class TextureCanvasComponent extends React.Component<TextureCanvasComponentProps> {
-        shouldComponentUpdate(nextProps: TextureCanvasComponentProps): boolean;
+    export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {
+        shouldComponentUpdate(nextProps: ITextureCanvasComponentProps): boolean;
         render(): JSX.Element;
     }
 }
-declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/Paintbrush" {
-    import { ToolData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
-    export const Paintbrush: ToolData;
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush" {
+    import { IToolData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
+    export const Paintbrush: IToolData;
 }
-declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/Eyedropper" {
-    import { ToolData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
-    export const Eyedropper: ToolData;
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/eyedropper" {
+    import { IToolData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
+    export const Eyedropper: IToolData;
 }
-declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/Floodfill" {
-    import { ToolData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
-    export const Floodfill: ToolData;
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/floodfill" {
+    import { IToolData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
+    export const Floodfill: IToolData;
+}
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast" {
+    import { IToolData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
+    export const Contrast: IToolData;
 }
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/defaultTools" {
-    const _default: import("babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent").ToolData[];
+    const _default: import("babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent").IToolData[];
     export default _default;
 }
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent" {
     import * as React from 'react';
     import { GlobalState } from "babylonjs-inspector/components/globalState";
-    import { PixelData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager";
-    import { Tool } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar";
-    import { Channel } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar";
+    import { IPixelData, IToolGUI } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager";
+    import { ITool } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar";
+    import { IChannel } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar";
     import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
     import { Scene } from 'babylonjs/scene';
     import { ISize } from 'babylonjs/Maths/math.size';
-    interface TextureEditorComponentProps {
+    import { Vector2 } from 'babylonjs/Maths/math.vector';
+    import { PointerInfo } from 'babylonjs/Events/pointerEvents';
+    import { PopupComponent } from "babylonjs-inspector/components/popupComponent";
+    interface ITextureEditorComponentProps {
         globalState: GlobalState;
         texture: BaseTexture;
         url: string;
+        window: React.RefObject<PopupComponent>;
     }
-    interface TextureEditorComponentState {
-        tools: Tool[];
+    interface ITextureEditorComponentState {
+        tools: ITool[];
         activeToolIndex: number;
         metadata: any;
-        channels: Channel[];
-        pixelData: PixelData;
+        channels: IChannel[];
+        pixelData: IPixelData;
         face: number;
     }
-    export interface ToolParameters {
+    export interface IToolParameters {
+        /** The visible scene in the editor. Useful for adding pointer and keyboard events. */
         scene: Scene;
+        /** The 2D canvas which tools can paint on using the canvas API. */
         canvas2D: HTMLCanvasElement;
+        /** The 3D scene which tools can add post processes to. */
+        scene3D: Scene;
+        /** The size of the texture. */
         size: ISize;
+        /** Pushes the editor texture back to the original scene. This should be called every time a tool makes any modification to a texture. */
         updateTexture: () => void;
-        getMetadata: () => any;
+        /** The metadata object which is shared between all tools. Feel free to store any information here. Do not set this directly: instead call setMetadata. */
+        metadata: any;
+        /** Call this when you want to mutate the metadata. */
         setMetadata: (data: any) => void;
-    }
-    export interface ToolData {
+        /** Returns the texture coordinates under the cursor */
+        getMouseCoordinates: (pointerInfo: PointerInfo) => Vector2;
+        /** An object which holds the GUI's ADT as well as the tool window. */
+        GUI: IToolGUI;
+        /** Provides access to the BABYLON namespace */
+        BABYLON: any;
+    }
+    /** An interface representing the definition of a tool */
+    export interface IToolData {
+        /** Name to display on the toolbar */
         name: string;
-        type: any;
+        /** A class definition for the tool including setup and cleanup methods */
+        type: IToolConstructable;
+        /**  An SVG icon encoded in Base64 */
         icon: string;
+        /** Whether the tool uses the draggable GUI window */
+        usesWindow?: boolean;
+        /** Whether the tool uses postprocesses */
+        is3D?: boolean;
+    }
+    export interface IToolType {
+        /** Called when the tool is selected. */
+        setup: () => void;
+        /** Called when the tool is deselected. */
+        cleanup: () => void;
+        /** Optional. Called when the user resets the texture or uploads a new texture. Tools may want to reset their state when this happens. */
+        onReset?: () => void;
+    }
+    /** For constructable types, TS requires that you define a seperate interface which constructs your actual interface */
+    interface IToolConstructable {
+        new (getParameters: () => IToolParameters): IToolType;
     }
     global {
-        var _TOOL_DATA_: ToolData;
+        var _TOOL_DATA_: IToolData;
     }
-    export class TextureEditorComponent extends React.Component<TextureEditorComponentProps, TextureEditorComponentState> {
+    export class TextureEditorComponent extends React.Component<ITextureEditorComponentProps, ITextureEditorComponentState> {
         private _textureCanvasManager;
-        private canvasUI;
-        private canvas2D;
-        private canvasDisplay;
-        constructor(props: TextureEditorComponentProps);
+        private _UICanvas;
+        private _2DCanvas;
+        private _3DCanvas;
+        constructor(props: ITextureEditorComponentProps);
         componentDidMount(): void;
         componentDidUpdate(): void;
         componentWillUnmount(): void;
         loadToolFromURL(url: string): void;
-        addTools(tools: ToolData[]): void;
-        getToolParameters(): ToolParameters;
+        addTools(tools: IToolData[]): void;
+        getToolParameters(): IToolParameters;
         changeTool(index: number): void;
         setMetadata(newMetadata: any): void;
         setFace(face: number): void;
@@ -1750,6 +1821,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     }
     export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps, ITexturePropertyGridComponentState> {
         private _adtInstrumentation;
+        private popoutWindowRef;
         private textureLineRef;
         constructor(props: ITexturePropertyGridComponentProps);
         componentWillUnmount(): void;
@@ -2692,10 +2764,13 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mes
     }
     export class SkeletonPropertyGridComponent extends React.Component<ISkeletonPropertyGridComponentProps> {
         private _skeletonViewersEnabled;
+        private _skeletonViewerDisplayOptions;
         private _skeletonViewers;
         constructor(props: ISkeletonPropertyGridComponentProps);
         switchSkeletonViewers(): void;
         checkSkeletonViewerState(props: ISkeletonPropertyGridComponentProps): void;
+        changeDisplayMode(): void;
+        changeDisplayOptions(option: string, value: number): void;
         shouldComponentUpdate(nextProps: ISkeletonPropertyGridComponentProps): boolean;
         onOverrideMeshLink(): void;
         render(): JSX.Element;
@@ -3476,19 +3551,23 @@ declare module "babylonjs-inspector/components/sceneExplorer/entities/cameraTree
     import { IExplorerExtensibilityGroup } from "babylonjs/Debug/debugLayer";
     import { Camera } from "babylonjs/Cameras/camera";
     import * as React from "react";
+    import { GlobalState } from "babylonjs-inspector/components/globalState";
     interface ICameraTreeItemComponentProps {
         camera: Camera;
         extensibilityGroups?: IExplorerExtensibilityGroup[];
         onClick: () => void;
+        globalState: GlobalState;
     }
     export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComponentProps, {
         isActive: boolean;
+        isGizmoEnabled: boolean;
     }> {
         private _onBeforeRenderObserver;
         constructor(props: ICameraTreeItemComponentProps);
         setActive(): void;
         componentDidMount(): void;
         componentWillUnmount(): void;
+        toggleGizmo(): void;
         render(): JSX.Element;
     }
 }
@@ -3994,6 +4073,10 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/rectangleSelect" {
+    import { IToolData } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent";
+    export const RectangleSelect: IToolData;
+}
 declare module "babylonjs-inspector/legacy/legacy" {
     export * from "babylonjs-inspector/index";
 }
@@ -4061,6 +4144,8 @@ declare module INSPECTOR {
         prepareGLTFPlugin(loader: BABYLON.GLTFFileLoader): void;
         lightGizmos: Array<BABYLON.LightGizmo>;
         enableLightGizmo(light: BABYLON.Light, enable?: boolean): void;
+        cameraGizmos: Array<BABYLON.CameraGizmo>;
+        enableCameraGizmo(camera: BABYLON.Camera, enable?: boolean): void;
     }
 }
 declare module INSPECTOR {
@@ -4367,6 +4452,7 @@ declare module INSPECTOR {
         onChange?: (newvalue: BABYLON.Vector3) => void;
         useEuler?: boolean;
         onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+        noSlider?: boolean;
     }
     export class Vector3LineComponent extends React.Component<IVector3LineComponentProps, {
         isExpanded: boolean;
@@ -5181,6 +5267,7 @@ declare module INSPECTOR {
         componentDidMount(): void;
         openPopup(): void;
         componentWillUnmount(): void;
+        getWindow(): Window | null;
         render(): React.ReactPortal | null;
     }
 }
@@ -5317,50 +5404,48 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    export interface Tool {
-        type: any;
-        name: string;
-        instance: any;
-        icon: string;
+    export interface ITool extends IToolData {
+        instance: IToolType;
     }
-    interface ToolBarProps {
-        tools: Tool[];
+    interface IToolBarProps {
+        tools: ITool[];
         addTool(url: string): void;
         changeTool(toolIndex: number): void;
         activeToolIndex: number;
         metadata: any;
         setMetadata(data: any): void;
     }
-    interface ToolBarState {
+    interface IToolBarState {
         toolURL: string;
         pickerOpen: boolean;
         addOpen: boolean;
     }
-    export class ToolBar extends React.Component<ToolBarProps, ToolBarState> {
-        private pickerRef;
-        constructor(props: ToolBarProps);
+    export class ToolBar extends React.Component<IToolBarProps, IToolBarState> {
+        private _addTool;
+        private _pickerRef;
+        constructor(props: IToolBarProps);
         computeRGBAColor(): string;
         render(): JSX.Element;
     }
 }
 declare module INSPECTOR {
-    export interface Channel {
+    export interface IChannel {
         visible: boolean;
         editable: boolean;
         name: string;
         id: 'R' | 'G' | 'B' | 'A';
         icon: any;
     }
-    interface ChannelsBarProps {
-        channels: Channel[];
-        setChannels(channelState: Channel[]): void;
+    interface IChannelsBarProps {
+        channels: IChannel[];
+        setChannels(channelState: IChannel[]): void;
     }
-    export class ChannelsBar extends React.Component<ChannelsBarProps> {
+    export class ChannelsBar extends React.Component<IChannelsBarProps> {
         render(): JSX.Element;
     }
 }
 declare module INSPECTOR {
-    export interface PixelData {
+    export interface IPixelData {
         x?: number;
         y?: number;
         r?: number;
@@ -5368,6 +5453,13 @@ declare module INSPECTOR {
         b?: number;
         a?: number;
     }
+    export interface IToolGUI {
+        adt: BABYLON.GUI.AdvancedDynamicTexture;
+        toolWindow: BABYLON.GUI.StackPanel;
+        isDragging: boolean;
+        dragCoords: BABYLON.Nullable<BABYLON.Vector2>;
+        style: BABYLON.GUI.Style;
+    }
     export class TextureCanvasManager {
         private _engine;
         private _scene;
@@ -5379,17 +5471,20 @@ declare module INSPECTOR {
         private _UICanvas;
         private _size;
         private _2DCanvas;
-        private _displayCanvas;
+        private _3DCanvas;
+        private _channelsTexture;
+        private _3DEngine;
+        private _3DPlane;
+        private _3DCanvasTexture;
+        private _3DScene;
         private _channels;
         private _face;
-        private _displayTexture;
         private _originalTexture;
         private _target;
         private _originalInternalTexture;
         private _didEdit;
         private _plane;
         private _planeMaterial;
-        private _planeFallbackMaterial;
         private _keyMap;
         private static ZOOM_MOUSE_SPEED;
         private static ZOOM_KEYBOARD_SPEED;
@@ -5397,55 +5492,66 @@ declare module INSPECTOR {
         private static ZOOM_OUT_KEY;
         private static PAN_SPEED;
         private static PAN_MOUSE_BUTTON;
-        private static PAN_KEY;
         private static MIN_SCALE;
         private static MAX_SCALE;
         private _tool;
         private _setPixelData;
+        private _GUI;
+        private _window;
         metadata: any;
-        constructor(texture: BABYLON.BaseTexture, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvasDisplay: HTMLCanvasElement, setPixelData: (pixelData: PixelData) => void);
+        private _editing3D;
+        constructor(texture: BABYLON.BaseTexture, window: Window, canvasUI: HTMLCanvasElement, canvas2D: HTMLCanvasElement, canvas3D: HTMLCanvasElement, setPixelData: (pixelData: IPixelData) => void);
         updateTexture(): Promise<void>;
-        private copyTextureToDisplayTexture;
-        set channels(channels: Channel[]);
+        private updateDisplay;
+        set channels(channels: IChannel[]);
         static paintPixelsOnCanvas(pixelData: Uint8Array, canvas: HTMLCanvasElement): void;
-        grabOriginalTexture(): void;
+        grabOriginalTexture(adjustZoom?: boolean): void;
         getMouseCoordinates(pointerInfo: BABYLON.PointerInfo): BABYLON.Vector2;
         get scene(): BABYLON.Scene;
         get canvas2D(): HTMLCanvasElement;
         get size(): BABYLON.ISize;
-        set tool(tool: BABYLON.Nullable<Tool>);
-        get tool(): BABYLON.Nullable<Tool>;
+        set tool(tool: BABYLON.Nullable<ITool>);
+        get tool(): BABYLON.Nullable<ITool>;
         set face(face: number);
+        /** Returns the tool GUI object, allowing tools to access the GUI */
+        get GUI(): IToolGUI;
+        /** Returns the 3D scene used for postprocesses */
+        get scene3D(): BABYLON.Scene;
         private makePlane;
         reset(): void;
         resize(newSize: BABYLON.ISize): Promise<void>;
-        private updateSize;
+        setSize(size: BABYLON.ISize, adjustZoom?: boolean): void;
         upload(file: File): void;
         dispose(): void;
     }
 }
 declare module INSPECTOR {
-    interface PropertiesBarProps {
+    interface IPropertiesBarProps {
         texture: BABYLON.BaseTexture;
         saveTexture(): void;
-        pixelData: PixelData;
+        pixelData: IPixelData;
         face: number;
         setFace(face: number): void;
         resetTexture(): void;
         resizeTexture(width: number, height: number): void;
         uploadTexture(file: File): void;
     }
-    interface PropertiesBarState {
+    interface IPropertiesBarState {
         width: number;
         height: number;
     }
-    interface PixelDataProps {
-        name: string;
-        data?: number;
-    }
-    function PixelData(props: PixelDataProps): JSX.Element;
-    export class PropertiesBar extends React.Component<PropertiesBarProps, PropertiesBarState> {
-        constructor(props: PropertiesBarProps);
+    export class PropertiesBar extends React.Component<IPropertiesBarProps, IPropertiesBarState> {
+        private _resetButton;
+        private _uploadButton;
+        private _saveButton;
+        private _babylonLogo;
+        private _resizeButton;
+        private _mipUp;
+        private _mipDown;
+        private _faces;
+        constructor(props: IPropertiesBarProps);
+        private pixelData;
+        private getNewDimension;
         render(): JSX.Element;
     }
 }
@@ -5458,72 +5564,110 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    interface TextureCanvasComponentProps {
+    interface ITextureCanvasComponentProps {
         canvasUI: React.RefObject<HTMLCanvasElement>;
         canvas2D: React.RefObject<HTMLCanvasElement>;
-        canvasDisplay: React.RefObject<HTMLCanvasElement>;
+        canvas3D: React.RefObject<HTMLCanvasElement>;
         texture: BABYLON.BaseTexture;
     }
-    export class TextureCanvasComponent extends React.Component<TextureCanvasComponentProps> {
-        shouldComponentUpdate(nextProps: TextureCanvasComponentProps): boolean;
+    export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {
+        shouldComponentUpdate(nextProps: ITextureCanvasComponentProps): boolean;
         render(): JSX.Element;
     }
 }
 declare module INSPECTOR {
-    export const Paintbrush: ToolData;
+    export const Paintbrush: IToolData;
+}
+declare module INSPECTOR {
+    export const Eyedropper: IToolData;
 }
 declare module INSPECTOR {
-    export const Eyedropper: ToolData;
+    export const Floodfill: IToolData;
 }
 declare module INSPECTOR {
-    export const Floodfill: ToolData;
+    export const Contrast: IToolData;
 }
 declare module INSPECTOR {
-    const _default: import("babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent").ToolData[];
+    const _default: import("babylonjs-inspector/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent").IToolData[];
     export default _default;
 }
 declare module INSPECTOR {
-    interface TextureEditorComponentProps {
+    interface ITextureEditorComponentProps {
         globalState: GlobalState;
         texture: BABYLON.BaseTexture;
         url: string;
+        window: React.RefObject<PopupComponent>;
     }
-    interface TextureEditorComponentState {
-        tools: Tool[];
+    interface ITextureEditorComponentState {
+        tools: ITool[];
         activeToolIndex: number;
         metadata: any;
-        channels: Channel[];
-        pixelData: PixelData;
+        channels: IChannel[];
+        pixelData: IPixelData;
         face: number;
     }
-    export interface ToolParameters {
+    export interface IToolParameters {
+        /** The visible scene in the editor. Useful for adding pointer and keyboard events. */
         scene: BABYLON.Scene;
+        /** The 2D canvas which tools can paint on using the canvas API. */
         canvas2D: HTMLCanvasElement;
+        /** The 3D scene which tools can add post processes to. */
+        scene3D: BABYLON.Scene;
+        /** The size of the texture. */
         size: BABYLON.ISize;
+        /** Pushes the editor texture back to the original scene. This should be called every time a tool makes any modification to a texture. */
         updateTexture: () => void;
-        getMetadata: () => any;
+        /** The metadata object which is shared between all tools. Feel free to store any information here. Do not set this directly: instead call setMetadata. */
+        metadata: any;
+        /** Call this when you want to mutate the metadata. */
         setMetadata: (data: any) => void;
-    }
-    export interface ToolData {
+        /** Returns the texture coordinates under the cursor */
+        getMouseCoordinates: (pointerInfo: BABYLON.PointerInfo) => BABYLON.Vector2;
+        /** An object which holds the GUI's ADT as well as the tool window. */
+        GUI: IToolGUI;
+        /** Provides access to the BABYLON namespace */
+        BABYLON: any;
+    }
+    /** An interface representing the definition of a tool */
+    export interface IToolData {
+        /** Name to display on the toolbar */
         name: string;
-        type: any;
+        /** A class definition for the tool including setup and cleanup methods */
+        type: IToolConstructable;
+        /**  An SVG icon encoded in Base64 */
         icon: string;
+        /** Whether the tool uses the draggable GUI window */
+        usesWindow?: boolean;
+        /** Whether the tool uses postprocesses */
+        is3D?: boolean;
+    }
+    export interface IToolType {
+        /** Called when the tool is selected. */
+        setup: () => void;
+        /** Called when the tool is deselected. */
+        cleanup: () => void;
+        /** Optional. Called when the user resets the texture or uploads a new texture. Tools may want to reset their state when this happens. */
+        onReset?: () => void;
+    }
+    /** For constructable types, TS requires that you define a seperate interface which constructs your actual interface */
+    interface IToolConstructable {
+        new (getParameters: () => IToolParameters): IToolType;
     }
     global {
-        var _TOOL_DATA_: ToolData;
+        var _TOOL_DATA_: IToolData;
     }
-    export class TextureEditorComponent extends React.Component<TextureEditorComponentProps, TextureEditorComponentState> {
+    export class TextureEditorComponent extends React.Component<ITextureEditorComponentProps, ITextureEditorComponentState> {
         private _textureCanvasManager;
-        private canvasUI;
-        private canvas2D;
-        private canvasDisplay;
-        constructor(props: TextureEditorComponentProps);
+        private _UICanvas;
+        private _2DCanvas;
+        private _3DCanvas;
+        constructor(props: ITextureEditorComponentProps);
         componentDidMount(): void;
         componentDidUpdate(): void;
         componentWillUnmount(): void;
         loadToolFromURL(url: string): void;
-        addTools(tools: ToolData[]): void;
-        getToolParameters(): ToolParameters;
+        addTools(tools: IToolData[]): void;
+        getToolParameters(): IToolParameters;
         changeTool(index: number): void;
         setMetadata(newMetadata: any): void;
         setFace(face: number): void;
@@ -5547,6 +5691,7 @@ declare module INSPECTOR {
     }
     export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps, ITexturePropertyGridComponentState> {
         private _adtInstrumentation;
+        private popoutWindowRef;
         private textureLineRef;
         constructor(props: ITexturePropertyGridComponentProps);
         componentWillUnmount(): void;
@@ -6225,10 +6370,13 @@ declare module INSPECTOR {
     }
     export class SkeletonPropertyGridComponent extends React.Component<ISkeletonPropertyGridComponentProps> {
         private _skeletonViewersEnabled;
+        private _skeletonViewerDisplayOptions;
         private _skeletonViewers;
         constructor(props: ISkeletonPropertyGridComponentProps);
         switchSkeletonViewers(): void;
         checkSkeletonViewerState(props: ISkeletonPropertyGridComponentProps): void;
+        changeDisplayMode(): void;
+        changeDisplayOptions(option: string, value: number): void;
         shouldComponentUpdate(nextProps: ISkeletonPropertyGridComponentProps): boolean;
         onOverrideMeshLink(): void;
         render(): JSX.Element;
@@ -6854,15 +7002,18 @@ declare module INSPECTOR {
         camera: BABYLON.Camera;
         extensibilityGroups?: BABYLON.IExplorerExtensibilityGroup[];
         onClick: () => void;
+        globalState: GlobalState;
     }
     export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComponentProps, {
         isActive: boolean;
+        isGizmoEnabled: boolean;
     }> {
         private _onBeforeRenderObserver;
         constructor(props: ICameraTreeItemComponentProps);
         setActive(): void;
         componentDidMount(): void;
         componentWillUnmount(): void;
+        toggleGizmo(): void;
         render(): JSX.Element;
     }
 }
@@ -7287,4 +7438,7 @@ declare module INSPECTOR {
         calculateMove(): string;
         render(): JSX.Element;
     }
+}
+declare module INSPECTOR {
+    export const RectangleSelect: IToolData;
 }

+ 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.28",
+    "version": "4.2.0-alpha.31",
     "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.28",
-        "babylonjs-gui": "4.2.0-alpha.28",
-        "babylonjs-loaders": "4.2.0-alpha.28",
-        "babylonjs-materials": "4.2.0-alpha.28",
-        "babylonjs-serializers": "4.2.0-alpha.28",
-        "babylonjs-gltf2interface": "4.2.0-alpha.28"
+        "babylonjs": "4.2.0-alpha.31",
+        "babylonjs-gui": "4.2.0-alpha.31",
+        "babylonjs-loaders": "4.2.0-alpha.31",
+        "babylonjs-materials": "4.2.0-alpha.31",
+        "babylonjs-serializers": "4.2.0-alpha.31",
+        "babylonjs-gltf2interface": "4.2.0-alpha.31"
     },
     "peerDependencies": {
         "@types/react": ">=16.7.3",

+ 4 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -2871,6 +2871,10 @@ var GLTFFileLoader = /** @class */ (function () {
          */
         this.alwaysComputeBoundingBox = false;
         /**
+         * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+         */
+        this.loadAllMaterials = false;
+        /**
          * Function called before loading a url referenced by the asset.
          */
         this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };

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


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


+ 22 - 7
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -2708,6 +2708,14 @@ var GLTFLoader = /** @class */ (function () {
                 var scene = ArrayItem.Get("/scene", _this._gltf.scenes, _this._gltf.scene || 0);
                 promises.push(_this.loadSceneAsync("/scenes/" + scene.index, scene));
             }
+            if (_this.parent.loadAllMaterials && _this._gltf.materials) {
+                for (var m = 0; m < _this._gltf.materials.length; ++m) {
+                    var material = _this._gltf.materials[m];
+                    var context_1 = "/materials/" + m;
+                    var babylonDrawMode = babylonjs_Misc_deferred__WEBPACK_IMPORTED_MODULE_0__["Material"].TriangleFillMode;
+                    promises.push(_this._loadMaterialAsync(context_1, material, null, babylonDrawMode, function (material) { }));
+                }
+            }
             // Restore the blocking of material dirty.
             _this._babylonScene.blockMaterialDirtyMechanism = oldBlockMaterialDirtyMechanism;
             if (_this._parent.compileMaterials) {
@@ -3415,6 +3423,7 @@ var GLTFLoader = /** @class */ (function () {
             if (babylonParentBone) {
                 baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
             }
+            babylonBone.setBindPose(baseMatrix);
             babylonBone.updateMatrix(baseMatrix, false, false);
             babylonBone._updateDifferenceMatrix(undefined, false);
         }
@@ -3938,13 +3947,15 @@ var GLTFLoader = /** @class */ (function () {
             this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
             this.logClose();
         }
-        babylonData.babylonMeshes.push(babylonMesh);
-        babylonMesh.onDisposeObservable.addOnce(function () {
-            var index = babylonData.babylonMeshes.indexOf(babylonMesh);
-            if (index !== -1) {
-                babylonData.babylonMeshes.splice(index, 1);
-            }
-        });
+        if (babylonMesh) {
+            babylonData.babylonMeshes.push(babylonMesh);
+            babylonMesh.onDisposeObservable.addOnce(function () {
+                var index = babylonData.babylonMeshes.indexOf(babylonMesh);
+                if (index !== -1) {
+                    babylonData.babylonMeshes.splice(index, 1);
+                }
+            });
+        }
         assign(babylonData.babylonMaterial);
         return babylonData.promise.then(function () {
             return babylonData.babylonMaterial;
@@ -4781,6 +4792,10 @@ var GLTFFileLoader = /** @class */ (function () {
          */
         this.alwaysComputeBoundingBox = false;
         /**
+         * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+         */
+        this.loadAllMaterials = false;
+        /**
          * Function called before loading a url referenced by the asset.
          */
         this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };

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


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


+ 22 - 7
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -5322,6 +5322,14 @@ var GLTFLoader = /** @class */ (function () {
                 var scene = ArrayItem.Get("/scene", _this._gltf.scenes, _this._gltf.scene || 0);
                 promises.push(_this.loadSceneAsync("/scenes/" + scene.index, scene));
             }
+            if (_this.parent.loadAllMaterials && _this._gltf.materials) {
+                for (var m = 0; m < _this._gltf.materials.length; ++m) {
+                    var material = _this._gltf.materials[m];
+                    var context_1 = "/materials/" + m;
+                    var babylonDrawMode = babylonjs_Misc_deferred__WEBPACK_IMPORTED_MODULE_0__["Material"].TriangleFillMode;
+                    promises.push(_this._loadMaterialAsync(context_1, material, null, babylonDrawMode, function (material) { }));
+                }
+            }
             // Restore the blocking of material dirty.
             _this._babylonScene.blockMaterialDirtyMechanism = oldBlockMaterialDirtyMechanism;
             if (_this._parent.compileMaterials) {
@@ -6029,6 +6037,7 @@ var GLTFLoader = /** @class */ (function () {
             if (babylonParentBone) {
                 baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
             }
+            babylonBone.setBindPose(baseMatrix);
             babylonBone.updateMatrix(baseMatrix, false, false);
             babylonBone._updateDifferenceMatrix(undefined, false);
         }
@@ -6552,13 +6561,15 @@ var GLTFLoader = /** @class */ (function () {
             this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
             this.logClose();
         }
-        babylonData.babylonMeshes.push(babylonMesh);
-        babylonMesh.onDisposeObservable.addOnce(function () {
-            var index = babylonData.babylonMeshes.indexOf(babylonMesh);
-            if (index !== -1) {
-                babylonData.babylonMeshes.splice(index, 1);
-            }
-        });
+        if (babylonMesh) {
+            babylonData.babylonMeshes.push(babylonMesh);
+            babylonMesh.onDisposeObservable.addOnce(function () {
+                var index = babylonData.babylonMeshes.indexOf(babylonMesh);
+                if (index !== -1) {
+                    babylonData.babylonMeshes.splice(index, 1);
+                }
+            });
+        }
         assign(babylonData.babylonMaterial);
         return babylonData.promise.then(function () {
             return babylonData.babylonMaterial;
@@ -7395,6 +7406,10 @@ var GLTFFileLoader = /** @class */ (function () {
          */
         this.alwaysComputeBoundingBox = false;
         /**
+         * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+         */
+        this.loadAllMaterials = false;
+        /**
          * Function called before loading a url referenced by the asset.
          */
         this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };

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


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


+ 7 - 3
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -193,6 +193,10 @@ declare module BABYLON {
          */
         alwaysComputeBoundingBox: boolean;
         /**
+         * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+         */
+        loadAllMaterials: boolean;
+        /**
          * Function called before loading a url referenced by the asset.
          */
         preprocessUrlAsync: (url: string) => Promise<string>;
@@ -1232,7 +1236,7 @@ declare module BABYLON.GLTF2 {
          * @param assign A function called synchronously after parsing the glTF properties
          * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
          */
-        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /**
          * Define this method to modify the default behavior when creating materials.
          * @param context The context when loading the asset
@@ -1496,7 +1500,7 @@ declare module BABYLON.GLTF2 {
         private _loadVertexAccessorAsync;
         private _loadMaterialMetallicRoughnessPropertiesAsync;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
         private _createDefaultMaterial;
         /**
          * Creates a Babylon material from a glTF material.
@@ -2216,7 +2220,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /** @hidden */
         _loadUriAsync(context: string, property: IProperty, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** @hidden */

+ 22 - 7
dist/preview release/loaders/babylonjs.loaders.js

@@ -6702,6 +6702,14 @@ var GLTFLoader = /** @class */ (function () {
                 var scene = ArrayItem.Get("/scene", _this._gltf.scenes, _this._gltf.scene || 0);
                 promises.push(_this.loadSceneAsync("/scenes/" + scene.index, scene));
             }
+            if (_this.parent.loadAllMaterials && _this._gltf.materials) {
+                for (var m = 0; m < _this._gltf.materials.length; ++m) {
+                    var material = _this._gltf.materials[m];
+                    var context_1 = "/materials/" + m;
+                    var babylonDrawMode = babylonjs_Misc_deferred__WEBPACK_IMPORTED_MODULE_0__["Material"].TriangleFillMode;
+                    promises.push(_this._loadMaterialAsync(context_1, material, null, babylonDrawMode, function (material) { }));
+                }
+            }
             // Restore the blocking of material dirty.
             _this._babylonScene.blockMaterialDirtyMechanism = oldBlockMaterialDirtyMechanism;
             if (_this._parent.compileMaterials) {
@@ -7409,6 +7417,7 @@ var GLTFLoader = /** @class */ (function () {
             if (babylonParentBone) {
                 baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
             }
+            babylonBone.setBindPose(baseMatrix);
             babylonBone.updateMatrix(baseMatrix, false, false);
             babylonBone._updateDifferenceMatrix(undefined, false);
         }
@@ -7932,13 +7941,15 @@ var GLTFLoader = /** @class */ (function () {
             this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
             this.logClose();
         }
-        babylonData.babylonMeshes.push(babylonMesh);
-        babylonMesh.onDisposeObservable.addOnce(function () {
-            var index = babylonData.babylonMeshes.indexOf(babylonMesh);
-            if (index !== -1) {
-                babylonData.babylonMeshes.splice(index, 1);
-            }
-        });
+        if (babylonMesh) {
+            babylonData.babylonMeshes.push(babylonMesh);
+            babylonMesh.onDisposeObservable.addOnce(function () {
+                var index = babylonData.babylonMeshes.indexOf(babylonMesh);
+                if (index !== -1) {
+                    babylonData.babylonMeshes.splice(index, 1);
+                }
+            });
+        }
         assign(babylonData.babylonMaterial);
         return babylonData.promise.then(function () {
             return babylonData.babylonMaterial;
@@ -8775,6 +8786,10 @@ var GLTFFileLoader = /** @class */ (function () {
          */
         this.alwaysComputeBoundingBox = false;
         /**
+         * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+         */
+        this.loadAllMaterials = false;
+        /**
          * Function called before loading a url referenced by the asset.
          */
         this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };

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


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


+ 14 - 6
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -213,6 +213,10 @@ declare module "babylonjs-loaders/glTF/glTFFileLoader" {
          */
         alwaysComputeBoundingBox: boolean;
         /**
+         * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+         */
+        loadAllMaterials: boolean;
+        /**
          * Function called before loading a url referenced by the asset.
          */
         preprocessUrlAsync: (url: string) => Promise<string>;
@@ -1305,7 +1309,7 @@ declare module "babylonjs-loaders/glTF/2.0/glTFLoaderExtension" {
          * @param assign A function called synchronously after parsing the glTF properties
          * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
          */
-        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /**
          * Define this method to modify the default behavior when creating materials.
          * @param context The context when loading the asset
@@ -1586,7 +1590,7 @@ declare module "babylonjs-loaders/glTF/2.0/glTFLoader" {
         private _loadVertexAccessorAsync;
         private _loadMaterialMetallicRoughnessPropertiesAsync;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
         private _createDefaultMaterial;
         /**
          * Creates a Babylon material from a glTF material.
@@ -2405,7 +2409,7 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/MSFT_lod" {
         /** @hidden */
         loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /** @hidden */
         _loadUriAsync(context: string, property: IProperty, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** @hidden */
@@ -3082,6 +3086,10 @@ declare module BABYLON {
          */
         alwaysComputeBoundingBox: boolean;
         /**
+         * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+         */
+        loadAllMaterials: boolean;
+        /**
          * Function called before loading a url referenced by the asset.
          */
         preprocessUrlAsync: (url: string) => Promise<string>;
@@ -4121,7 +4129,7 @@ declare module BABYLON.GLTF2 {
          * @param assign A function called synchronously after parsing the glTF properties
          * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
          */
-        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /**
          * Define this method to modify the default behavior when creating materials.
          * @param context The context when loading the asset
@@ -4385,7 +4393,7 @@ declare module BABYLON.GLTF2 {
         private _loadVertexAccessorAsync;
         private _loadMaterialMetallicRoughnessPropertiesAsync;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
         private _createDefaultMaterial;
         /**
          * Creates a Babylon material from a glTF material.
@@ -5105,7 +5113,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /** @hidden */
         _loadUriAsync(context: string, property: IProperty, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** @hidden */

+ 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.28",
+    "version": "4.2.0-alpha.31",
     "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.28",
-        "babylonjs": "4.2.0-alpha.28"
+        "babylonjs-gltf2interface": "4.2.0-alpha.31",
+        "babylonjs": "4.2.0-alpha.31"
     },
     "engines": {
         "node": "*"

+ 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.28",
+    "version": "4.2.0-alpha.31",
     "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.28"
+        "babylonjs": "4.2.0-alpha.31"
     },
     "engines": {
         "node": "*"

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


File diff suppressed because it is too large
+ 2 - 3
dist/preview release/nodeEditor/babylon.nodeEditor.max.js


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


+ 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.28",
+    "version": "4.2.0-alpha.31",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
     },
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.28"
+        "babylonjs": "4.2.0-alpha.31"
     },
     "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.28",
+    "version": "4.2.0-alpha.31",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -1 +1 @@
-{"thinEngineOnly":117102,"engineOnly":153538,"sceneOnly":517646,"minGridMaterial":655625,"minStandardMaterial":805033}
+{"thinEngineOnly":117135,"engineOnly":153571,"sceneOnly":517838,"minGridMaterial":656171,"minStandardMaterial":805596}

+ 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.28",
+    "version": "4.2.0-alpha.31",
     "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.28"
+        "babylonjs": "4.2.0-alpha.31"
     },
     "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.28",
+    "version": "4.2.0-alpha.31",
     "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.28"
+        "babylonjs": "4.2.0-alpha.31"
     },
     "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.28",
+    "version": "4.2.0-alpha.31",
     "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.28",
-        "babylonjs-gltf2interface": "4.2.0-alpha.28"
+        "babylonjs": "4.2.0-alpha.31",
+        "babylonjs-gltf2interface": "4.2.0-alpha.31"
     },
     "engines": {
         "node": "*"

File diff suppressed because it is too large
+ 916 - 186
dist/preview release/viewer/babylon.module.d.ts


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


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


+ 14 - 6
dist/preview release/viewer/babylonjs.loaders.module.d.ts

@@ -213,6 +213,10 @@ declare module "babylonjs-loaders/glTF/glTFFileLoader" {
          */
         alwaysComputeBoundingBox: boolean;
         /**
+         * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+         */
+        loadAllMaterials: boolean;
+        /**
          * Function called before loading a url referenced by the asset.
          */
         preprocessUrlAsync: (url: string) => Promise<string>;
@@ -1305,7 +1309,7 @@ declare module "babylonjs-loaders/glTF/2.0/glTFLoaderExtension" {
          * @param assign A function called synchronously after parsing the glTF properties
          * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
          */
-        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /**
          * Define this method to modify the default behavior when creating materials.
          * @param context The context when loading the asset
@@ -1586,7 +1590,7 @@ declare module "babylonjs-loaders/glTF/2.0/glTFLoader" {
         private _loadVertexAccessorAsync;
         private _loadMaterialMetallicRoughnessPropertiesAsync;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
         private _createDefaultMaterial;
         /**
          * Creates a Babylon material from a glTF material.
@@ -2405,7 +2409,7 @@ declare module "babylonjs-loaders/glTF/2.0/Extensions/MSFT_lod" {
         /** @hidden */
         loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /** @hidden */
         _loadUriAsync(context: string, property: IProperty, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** @hidden */
@@ -3082,6 +3086,10 @@ declare module BABYLON {
          */
         alwaysComputeBoundingBox: boolean;
         /**
+         * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+         */
+        loadAllMaterials: boolean;
+        /**
          * Function called before loading a url referenced by the asset.
          */
         preprocessUrlAsync: (url: string) => Promise<string>;
@@ -4121,7 +4129,7 @@ declare module BABYLON.GLTF2 {
          * @param assign A function called synchronously after parsing the glTF properties
          * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
          */
-        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /**
          * Define this method to modify the default behavior when creating materials.
          * @param context The context when loading the asset
@@ -4385,7 +4393,7 @@ declare module BABYLON.GLTF2 {
         private _loadVertexAccessorAsync;
         private _loadMaterialMetallicRoughnessPropertiesAsync;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign?: (babylonMaterial: Material) => void): Promise<Material>;
         private _createDefaultMaterial;
         /**
          * Creates a Babylon material from a glTF material.
@@ -5105,7 +5113,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
-        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /** @hidden */
         _loadUriAsync(context: string, property: IProperty, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** @hidden */

+ 8 - 3
dist/preview release/what's new.md

@@ -17,9 +17,11 @@
 
 ### General
 
-- Added support for querystrings on KTX file URLs ([abogartz](https://github.com/abogartz/Babylon.js)
+- Added support for postproces serialization ([Deltakosh](https://github.com/deltakosh))
+- Added support for querystrings on KTX file URLs ([abogartz](https://github.com/abogartz)
 - 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))
+- Camera gizmo ([cedricguillemet](https://github.com/cedricguillemet))
 - Node support (Transform, Bone) for gizmos ([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))
@@ -79,7 +81,7 @@
 - Popup Window available (To be used in Curve Editor) ([pixelspace](https://github.com/devpixelspace))
 - Add support to update inspector when switching to a new scene ([belfortk](https://github.com/belfortk))
 - Hex Component for Hex inputs on layer masks. ([msDestiny14](https://github.com/msDestiny14))
-- View & edit textures in pop out inspector with zoom & pan and individual channels, and save to local machine. ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
+- View & edit textures in pop out inspector using canvas and postprocesses. ([DarraghBurkeMS](https://github.com/DarraghBurkeMS))
 
 ### Cameras
 
@@ -93,6 +95,7 @@
 ### Sprites
 
 - Added support for 'sprite.useAlphaForPicking` to enable precise picking using sprite alpha ([Deltakosh](https://github.com/deltakosh))
+- Improved rendering engine by using instancing when available ([Deltakosh](https://github.com/deltakosh))
 
 ### Physics
 
@@ -118,6 +121,7 @@
 - Added missing "pluginExtension" parameter to SceneLoader.ImportAnimations. ([phenry20](https://github.com/phenry20))
 - Added support for .glb file loading through a base64 encoded filename ([Popov72](https://github.com/Popov72))
 - Fixed issue with loading screen hiding too early when loading multiple assets concurrently. ([bghgary](https://github.com/bghgary))
+- Added the `loadAllMaterials` property to the gLTF loader to load materials even if not used by any mesh ([Popov72](https://github.com/Popov72))
 
 ### Serializers
 
@@ -276,9 +280,10 @@
 - Fix an issue causing views to render blank when scene rendering is skipped for a given iteration of the render loop ([elInfidel](https://github.com/elInfidel))
 - Fix docs Matrix.RotationYawPitchRoll and Matrix.RotationYawPitchRollToRef ([VSerain](https://github.com/VSerain))
 - Fix issue in `GLTFLoader._updateBoneMatrices()` where bone rest position was not set. ([drigax](https://github.com/drigax))
+- Fix the bounding box of instances that does not match the instance position / rotation / scaling ([Popov72](https://github.com/Popov72))
 
 ## Breaking changes
-
+- `FollowCamera.target` was renamed to `FollowCamera.meshTarget` to not be in conflict with `TargetCamera.target` ([Deltakosh](https://github.com/deltakosh))
 - `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))
 - HDRCubeTexture default rotation is now similar to the industry one. You might need to add a rotation on y of 90 degrees if you scene changes ([Sebavan](https://github.com/sebavan/))

+ 10 - 1
inspector/src/components/actionTabs/lines/vector3LineComponent.tsx

@@ -17,6 +17,7 @@ interface IVector3LineComponentProps {
     onChange?: (newvalue: Vector3) => void;
     useEuler?: boolean,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    noSlider?: boolean;
 }
 
 export class Vector3LineComponent extends React.Component<IVector3LineComponentProps, { isExpanded: boolean, value: Vector3 }> {
@@ -131,13 +132,21 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
                     </div>
                 }
                 {
-                    this.state.isExpanded && this.props.useEuler &&
+                    this.state.isExpanded && this.props.useEuler && !this.props.noSlider &&
                     <div className="secondLine">
                         <SliderLineComponent label="x" minimum={0} maximum={360} step={0.1} directValue={Tools.ToDegrees(this.state.value.x)} onChange={value => this.updateStateX(Tools.ToRadians(value))} />
                         <SliderLineComponent label="y" minimum={0} maximum={360} step={0.1} directValue={Tools.ToDegrees(this.state.value.y)} onChange={value => this.updateStateY(Tools.ToRadians(value))} />
                         <SliderLineComponent label="z" minimum={0} maximum={360} step={0.1} directValue={Tools.ToDegrees(this.state.value.z)} onChange={value => this.updateStateZ(Tools.ToRadians(value))} />
                     </div>
                 }
+                {
+                    this.state.isExpanded && this.props.useEuler && this.props.noSlider &&
+                    <div className="secondLine">
+                        <NumericInputComponent label="x" step={this.props.step} value={Tools.ToDegrees(this.state.value.x)} onChange={value => this.updateStateX(Tools.ToRadians(value))} />
+                        <NumericInputComponent label="y" step={this.props.step} value={Tools.ToDegrees(this.state.value.y)} onChange={value => this.updateStateY(Tools.ToRadians(value))} />
+                        <NumericInputComponent label="z" step={this.props.step} value={Tools.ToDegrees(this.state.value.z)} onChange={value => this.updateStateZ(Tools.ToRadians(value))} />
+                    </div>
+                }
             </div>
         );
     }

+ 2 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/cameras/freeCameraPropertyGridComponent.tsx

@@ -29,7 +29,9 @@ export class FreeCameraPropertyGridComponent extends React.Component<IFreeCamera
             <div className="pane">
                 <CommonCameraPropertyGridComponent globalState={this.props.globalState} lockObject={this.props.lockObject} camera={camera} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent globalState={this.props.globalState} title="TRANSFORMS">
+                    <Vector3LineComponent label="Target" target={camera} propertyName="target" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Position" target={camera} propertyName="position" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <Vector3LineComponent label="Rotation" noSlider={true} useEuler={this.props.globalState.onlyUseEulers} target={camera} propertyName="rotation" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="CONTROLS" closed={true}>
                     <FloatLineComponent lockObject={this.props.lockObject} label="Angular sensitivity" target={camera} propertyName="angularSensibility" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 4 - 3
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -45,6 +45,7 @@ interface ITexturePropertyGridComponentState {
 export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps,ITexturePropertyGridComponentState> {
 
     private _adtInstrumentation: Nullable<AdvancedDynamicTextureInstrumentation>;
+    private popoutWindowRef : React.RefObject<PopupComponent>;
     private textureLineRef: React.RefObject<TextureLineComponent>;
 
 
@@ -58,6 +59,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         const texture = this.props.texture;
 
         this.textureLineRef = React.createRef();
+        this.popoutWindowRef = React.createRef();
 
         if (!texture || !(texture as any).rootContainer) {
             return;
@@ -120,9 +122,6 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
             isTextureEditorOpen: false,
             textureEditing: null
         }, callback);
-        if (window !== null) {
-            window.close();
-        }
     }
 
     forceRefresh() {
@@ -185,11 +184,13 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                   onClose={(window: Window) =>
                     this.onCloseTextureEditor(window)
                   }
+                  ref={this.popoutWindowRef}
                 >
                     <TextureEditorComponent
                         globalState={this.props.globalState}
                         texture={this.props.texture}
                         url={textureUrl}
+                        window={this.popoutWindowRef}
                     />
                 </PopupComponent>)}
                 <CustomPropertyGridComponent globalState={this.props.globalState} target={texture}

+ 5 - 5
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/channelsBar.tsx

@@ -1,6 +1,6 @@
 import * as React from 'react';
 
-export interface Channel {
+export interface IChannel {
     visible: boolean;
     editable: boolean;
     name: string;
@@ -8,15 +8,15 @@ export interface Channel {
     icon: any;
 }
 
-interface ChannelsBarProps {
-    channels: Channel[];
-    setChannels(channelState : Channel[]) : void;
+interface IChannelsBarProps {
+    channels: IChannel[];
+    setChannels(channelState : IChannel[]) : void;
 }
 
 const eyeOpen = require('./assets/eyeOpen.svg');
 const eyeClosed = require('./assets/eyeClosed.svg');
 
-export class ChannelsBar extends React.Component<ChannelsBarProps> {
+export class ChannelsBar extends React.Component<IChannelsBarProps> {
     render() {
         return <div id='channels-bar'>
             {this.props.channels.map(

+ 0 - 59
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/Paintbrush.ts

@@ -1,59 +0,0 @@
-import { ToolParameters, ToolData } from '../textureEditorComponent';
-import { PointerEventTypes, PointerInfo } from 'babylonjs/Events/pointerEvents';
-
-export const Paintbrush : ToolData = {
-    name: "Paintbrush",
-    type: class {
-        getParameters: () => ToolParameters;
-        pointerObservable: any;
-        isPainting: boolean;
-
-        constructor(getParameters: () => ToolParameters) {
-            this.getParameters = getParameters;
-        }
-
-        paint(pointerInfo : PointerInfo) {
-            const p = this.getParameters();
-            const ctx = p.canvas2D.getContext('2d')!;
-            const x = pointerInfo.pickInfo!.getTextureCoordinates()!.x * p.size.width;
-            const y = (1 - pointerInfo.pickInfo!.getTextureCoordinates()!.y) * p.size.height;
-            ctx.globalCompositeOperation = 'source-over';
-            ctx.fillStyle = p.getMetadata().color;
-            ctx.globalAlpha = p.getMetadata().opacity;
-            ctx.beginPath();
-            ctx.ellipse(x, y, 15, 15, 0, 0, Math.PI * 2);
-            ctx.fill();
-            p.updateTexture();
-        }
-        
-        setup () {
-            this.pointerObservable = this.getParameters().scene.onPointerObservable.add((pointerInfo) => {
-                if (pointerInfo.pickInfo?.hit) {
-                    if (pointerInfo.type === PointerEventTypes.POINTERDOWN) {
-                        if (pointerInfo.event.button == 0) {
-                            this.isPainting = true;
-                          }
-                    }
-                    if (pointerInfo.type === PointerEventTypes.POINTERMOVE && this.isPainting) {
-                        this.paint(pointerInfo);
-                    }
-                }
-                if (pointerInfo.type === PointerEventTypes.POINTERUP) {
-                    if (pointerInfo.event.button == 0) {
-                        this.isPainting = false;
-                      }
-                }
-            });
-            this.isPainting = false;
-        }
-        cleanup () {
-            this.isPainting = false;
-            if (this.pointerObservable) {
-                this.getParameters().scene.onPointerObservable.remove(this.pointerObservable);
-            }
-        }
-    },
-    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMjksMTFhMy41
-    NywzLjU3LDAsMCwxLDAsNS4wNkwxNywyOGEyLjM0LDIuMzQsMCwwLDEtMSwuNThMMTAuOTEsMzBhLjc1Ljc1LDAsMCwxLS45Mi0uOTJMMTEuMzgsMjRBMi4zNCwyLjM0LDAsMCwxLDEyLDIzbDEyLTEyQTMuNTcsMy41NywwLDAsMSwyOSwxMVpNMjMsMTQuMSwxMywy
-    NGEuNjkuNjksMCwwLDAtLjE5LjMzbC0xLjA1LDMuODUsMy44NS0xQS42OS42OSwwLDAsMCwxNiwyN0wyNS45LDE3Wm0yLTItMSwxTDI3LDE2bDEtMUEyLjA4LDIuMDgsMCwxLDAsMjUsMTIuMDdaIiBzdHlsZT0iZmlsbDojZmZmIi8+PC9zdmc+`
-};

+ 100 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/contrast.ts

@@ -0,0 +1,100 @@
+import { IToolData, IToolParameters, IToolType } from '../textureEditorComponent';
+import { TextBlock } from 'babylonjs-gui/2D/controls/textBlock';
+import { Slider } from 'babylonjs-gui/2D/controls/sliders/slider';
+
+export const Contrast : IToolData = {
+    name: 'Contrast/Exposure',
+    type: class implements IToolType {
+        getParameters: () => IToolParameters;
+        contrast : number = 1.0;
+        exposure : number = 1.0;
+        GUI: {
+            contrastLabel : TextBlock;
+            contrastSlider : Slider;
+            exposureLabel : TextBlock;
+            exposureSlider : Slider;
+        }
+        constructor(getParameters: () => IToolParameters) {
+            this.getParameters = getParameters;
+        }
+        setExposure(exposure : number) {
+            this.exposure = exposure;
+            this.GUI.exposureLabel.text = `Exposure: ${this.exposure}`;
+            const {scene3D, updateTexture} = this.getParameters();
+            scene3D.imageProcessingConfiguration.isEnabled = true;
+            scene3D.imageProcessingConfiguration.exposure = this.computeExposure(this.exposure);
+            updateTexture();
+        }
+        setContrast(contrast : number) {
+            this.contrast = contrast;
+            this.GUI.contrastLabel.text = `Contrast: ${this.contrast}`;
+            const {scene3D, updateTexture} = this.getParameters();
+            scene3D.imageProcessingConfiguration.isEnabled = true;
+            scene3D.imageProcessingConfiguration.contrast = this.computeContrast(contrast);
+            updateTexture();
+        }
+        /** Maps slider values to post processing values using an exponential regression */
+        computeExposure(sliderValue : number) {
+            return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
+        }
+        /** Maps slider values to post processing values using an exponential regression */
+        computeContrast(sliderValue : number) {
+            return Math.pow(1.05698, sliderValue) + 0.0000392163 * sliderValue;
+        }
+        setup() {
+            this.contrast = 0;
+            this.exposure = 0;
+            const {GUI} = this.getParameters();
+            const contrastLabel = new TextBlock();
+            contrastLabel.style = GUI.style;
+            contrastLabel.height = '20px';
+            contrastLabel.color = '#ffffff';
+            const contrastSlider = new Slider();
+            contrastSlider.value = this.contrast;
+            contrastSlider.minimum = -100;
+            contrastSlider.maximum = 100;
+            contrastSlider.height = '20px';
+            contrastSlider.isThumbCircle = true;
+            contrastSlider.background = '#a3a3a3';
+            contrastSlider.color = '#33648f';
+            contrastSlider.borderColor = '#33648f';
+            contrastSlider.onValueChangedObservable.add(evt => this.setContrast(evt.valueOf()));
+            const exposureLabel = new TextBlock();
+            exposureLabel.style = GUI.style;
+            exposureLabel.height = '20px';
+            exposureLabel.color = '#ffffff';
+            const exposureSlider = new Slider();
+            exposureSlider.value = this.exposure;
+            exposureSlider.minimum = -100;
+            exposureSlider.maximum = 100;
+            exposureSlider.height = '20px';
+            exposureSlider.isThumbCircle = true;
+            exposureSlider.background = '#a3a3a3';
+            exposureSlider.color = '#33648f';
+            exposureSlider.borderColor = '#33648f';
+            exposureSlider.onValueChangedObservable.add(evt => this.setExposure(evt.valueOf()));
+            GUI.toolWindow.addControl(contrastLabel);
+            GUI.toolWindow.addControl(contrastSlider);
+            GUI.toolWindow.addControl(exposureLabel);
+            GUI.toolWindow.addControl(exposureSlider);
+            this.GUI = {contrastLabel, contrastSlider, exposureLabel, exposureSlider};
+            this.setExposure(this.exposure);
+            this.setContrast(this.contrast);
+        }
+        cleanup() {
+            Object.entries(this.GUI).forEach(([key, value]) => value.dispose());
+        }
+        onReset() {
+            this.GUI.contrastSlider.value = 0;
+            this.GUI.exposureSlider.value = 0;
+        }
+    },
+    usesWindow: true,
+    is3D: true,
+    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0i
+    NDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMTcuNTUsMjYuNTVsOC41OS0zLjIxQTYuODYsNi44NiwwLDAsMSwyNCwyNS43NWwtMy4x
+    OSwxLjE5QTcsNywwLDAsMSwxNy41NSwyNi41NVpNMjAsMTEuNUE4LjUsOC41LDAsMSwwLDI4LjUsMjAsOC41MSw4LjUxLDAsMCwwLDIwLDExLjVNMjAsMTBBMTAsMTAs
+    MCwxLDEsMTAsMjAsMTAsMTAsMCwwLDEsMjAsMTBabS0yLjQ1LDUuMzQsNS0xLjg2QTcsNywwLDAsMCwxOS40NCwxM2wtMS44OS43MVptMCwzLjIsNy44OC0yLjk0YTYu
+    ODgsNi44OCwwLDAsMC0xLjE5LTEuMTZsLTYuNjksMi41Wm0wLDMuMiw5LjIzLTMuNDRhNy42OCw3LjY4LDAsMCwwLS41Mi0xLjQxbC04LjcxLDMuMjVabTAsMS42djEu
+    Nmw5LjI4LTMuNDZBNi42Nyw2LjY3LDAsMCwwLDI3LDE5LjgyWiIgc3R5bGU9ImZpbGw6I2ZmZiIvPjwvc3ZnPg==`
+}

+ 6 - 5
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/defaultTools.ts

@@ -1,5 +1,6 @@
-import { Paintbrush } from './Paintbrush';
-import { Eyedropper } from './Eyedropper';
-import { Floodfill } from './Floodfill';
-
-export default [Paintbrush, Eyedropper, Floodfill];
+import { Paintbrush } from './paintbrush';
+import { Eyedropper } from './eyedropper';
+import { Floodfill } from './floodfill';
+import { Contrast } from './contrast';
+
+export default [Paintbrush, Eyedropper, Floodfill, Contrast];

+ 59 - 60
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/Eyedropper.ts

@@ -1,60 +1,59 @@
-import { ToolParameters, ToolData } from '../textureEditorComponent';
-import { PointerEventTypes, PointerInfo } from 'babylonjs/Events/pointerEvents';
-
-export const Eyedropper : ToolData = {
-    name: "Eyedropper",
-    type: class {
-        getParameters: () => ToolParameters;
-        pointerObservable: any;
-        isPicking: boolean;
-
-        constructor(getParameters: () => ToolParameters) {
-            this.getParameters = getParameters;
-        }
-
-        pick(pointerInfo : PointerInfo) {
-            const p = this.getParameters();
-            const ctx = p.canvas2D.getContext('2d');
-            const x = pointerInfo.pickInfo!.getTextureCoordinates()!.x * p.size.width;
-            const y = (1 - pointerInfo.pickInfo!.getTextureCoordinates()!.y) * p.size.height;
-            const pixel = ctx!.getImageData(x, y, 1, 1).data;
-            p.setMetadata({
-                color: "#" + ("000000" + this.rgbToHex(pixel[0], pixel[1], pixel[2])).slice(-6),
-                opacity: pixel[3] / 255
-            });
-        }
-        
-        setup () {
-            this.pointerObservable = this.getParameters().scene.onPointerObservable.add((pointerInfo) => {
-                if (pointerInfo.pickInfo?.hit) {
-                    if (pointerInfo.type === PointerEventTypes.POINTERDOWN) {
-                        this.isPicking = true;
-                        this.pick(pointerInfo);
-                    }
-                    if (pointerInfo.type === PointerEventTypes.POINTERMOVE && this.isPicking) {
-                        this.pick(pointerInfo);
-                    }
-                    if (pointerInfo.type === PointerEventTypes.POINTERUP) {
-                        this.isPicking = false;
-                    }
-                }
-            });
-            this.isPicking = false;
-        }
-        cleanup () {
-            if (this.pointerObservable) {
-                this.getParameters().scene.onPointerObservable.remove(this.pointerObservable);
-            }
-        }
-        rgbToHex(r: number, g:number, b: number) {
-            return ((r << 16) | (g << 8) | b).toString(16);
-        }
-    },
-    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMjkuMzIsMTAu
-    NjhjLTEuNjYtMS42Ni00LjA2LTEtNS41Ni41YTExLjg5LDExLjg5LDAsMCwwLTEuNjYsMi4zMUwyMiwxMy40MWExLjg5LDEuODksMCwwLDAtMi42NiwwbC0uOS45YTEuODksMS44OSwwLDAsMC0uMjIsMi4zOWwtNi4wOSw2LjA5YTIuNzUsMi43NSwwLDAsMC0uNzMs
-    MS4yOGwtLjgxLDMuMjYtLjU2LjU2YTEuMTYsMS4xNiwwLDAsMCwwLDEuNjVsLjQxLjQxYTEuMTcsMS4xNywwLDAsMCwxLjY1LDBsLjU2LS41NiwzLjI2LS44MWEyLjc1LDIuNzUsMCwwLDAsMS4yOC0uNzNsNi4xNC02LjE0YTEuODcsMS44NywwLDAsMCwuODQuMjEs
-    MS44MywxLjgzLDAsMCwwLDEuMzMtLjU1bC45LS45YTEuODcsMS44NywwLDAsMCwuMDgtMi41NywxMS41NCwxMS41NCwwLDAsMCwyLjMyLTEuNjZDMzAuMzIsMTQuNzQsMzEsMTIuMzUsMjkuMzIsMTAuNjhaTTE2LjE1LDI2Ljc5YTEuMjEsMS4yMSwwLDAsMS0uNTgu
-    MzNMMTIsMjhsLjktMy41OWExLjIxLDEuMjEsMCwwLDEsLjMzLS41OGw2LjA3LTYuMDcsMi45NCwyLjk0Wm05LjIxLTcuMzgtLjkuOWMtLjE5LjItLjM0LjItLjU0LDBsLTQuNC00LjRhLjQuNCwwLDAsMSwwLS41NGwuOS0uOWEuNDMuNDMsMCwwLDEsLjI3LS4xMS4z
-    OS4zOSwwLDAsMSwuMjcuMTFsNC40LDQuNEEuMzguMzgsMCwwLDEsMjUuMzYsMTkuNDFabTMuMzgtNS45M2EzLjcsMy43LDAsMCwxLTEsMS43LDExLjY3LDExLjY3LDAsMCwxLTIuMzUsMS42MkwyMy4yLDE0LjU5YTExLjY3LDExLjY3LDAsMCwxLDEuNjItMi4zNSwz
-    LjcsMy43LDAsMCwxLDEuNy0xLDEuODMsMS44MywwLDAsMSwyLjIyLDIuMjJaIiBzdHlsZT0iZmlsbDojZmZmIi8+PC9zdmc+`
-};
+import { IToolParameters, IToolData } from '../textureEditorComponent';
+import { PointerEventTypes, PointerInfo } from 'babylonjs/Events/pointerEvents';
+
+export const Eyedropper : IToolData = {
+    name: 'Eyedropper',
+    type: class {
+        getParameters: () => IToolParameters;
+        pointerObservable: any;
+        isPicking: boolean;
+
+        constructor(getParameters: () => IToolParameters) {
+            this.getParameters = getParameters;
+        }
+
+        pick(pointerInfo : PointerInfo) {
+            const p = this.getParameters();
+            const ctx = p.canvas2D.getContext('2d');
+            const {x, y} = p.getMouseCoordinates(pointerInfo);
+            const pixel = ctx!.getImageData(x, y, 1, 1).data;
+            p.setMetadata({
+                color: '#' + ('000000' + this.rgbToHex(pixel[0], pixel[1], pixel[2])).slice(-6),
+                opacity: pixel[3] / 255
+            });
+        }
+        
+        setup () {
+            this.pointerObservable = this.getParameters().scene.onPointerObservable.add((pointerInfo) => {
+                if (pointerInfo.pickInfo?.hit) {
+                    if (pointerInfo.type === PointerEventTypes.POINTERDOWN) {
+                        this.isPicking = true;
+                        this.pick(pointerInfo);
+                    }
+                    if (pointerInfo.type === PointerEventTypes.POINTERMOVE && this.isPicking) {
+                        this.pick(pointerInfo);
+                    }
+                    if (pointerInfo.type === PointerEventTypes.POINTERUP) {
+                        this.isPicking = false;
+                    }
+                }
+            });
+            this.isPicking = false;
+        }
+        cleanup () {
+            if (this.pointerObservable) {
+                this.getParameters().scene.onPointerObservable.remove(this.pointerObservable);
+            }
+        }
+        rgbToHex(r: number, g:number, b: number) {
+            return ((r << 16) | (g << 8) | b).toString(16);
+        }
+    },
+    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMjkuMzIsMTAu
+    NjhjLTEuNjYtMS42Ni00LjA2LTEtNS41Ni41YTExLjg5LDExLjg5LDAsMCwwLTEuNjYsMi4zMUwyMiwxMy40MWExLjg5LDEuODksMCwwLDAtMi42NiwwbC0uOS45YTEuODksMS44OSwwLDAsMC0uMjIsMi4zOWwtNi4wOSw2LjA5YTIuNzUsMi43NSwwLDAsMC0uNzMs
+    MS4yOGwtLjgxLDMuMjYtLjU2LjU2YTEuMTYsMS4xNiwwLDAsMCwwLDEuNjVsLjQxLjQxYTEuMTcsMS4xNywwLDAsMCwxLjY1LDBsLjU2LS41NiwzLjI2LS44MWEyLjc1LDIuNzUsMCwwLDAsMS4yOC0uNzNsNi4xNC02LjE0YTEuODcsMS44NywwLDAsMCwuODQuMjEs
+    MS44MywxLjgzLDAsMCwwLDEuMzMtLjU1bC45LS45YTEuODcsMS44NywwLDAsMCwuMDgtMi41NywxMS41NCwxMS41NCwwLDAsMCwyLjMyLTEuNjZDMzAuMzIsMTQuNzQsMzEsMTIuMzUsMjkuMzIsMTAuNjhaTTE2LjE1LDI2Ljc5YTEuMjEsMS4yMSwwLDAsMS0uNTgu
+    MzNMMTIsMjhsLjktMy41OWExLjIxLDEuMjEsMCwwLDEsLjMzLS41OGw2LjA3LTYuMDcsMi45NCwyLjk0Wm05LjIxLTcuMzgtLjkuOWMtLjE5LjItLjM0LjItLjU0LDBsLTQuNC00LjRhLjQuNCwwLDAsMSwwLS41NGwuOS0uOWEuNDMuNDMsMCwwLDEsLjI3LS4xMS4z
+    OS4zOSwwLDAsMSwuMjcuMTFsNC40LDQuNEEuMzguMzgsMCwwLDEsMjUuMzYsMTkuNDFabTMuMzgtNS45M2EzLjcsMy43LDAsMCwxLTEsMS43LDExLjY3LDExLjY3LDAsMCwxLTIuMzUsMS42MkwyMy4yLDE0LjU5YTExLjY3LDExLjY3LDAsMCwxLDEuNjItMi4zNSwz
+    LjcsMy43LDAsMCwxLDEuNy0xLDEuODMsMS44MywwLDAsMSwyLjIyLDIuMjJaIiBzdHlsZT0iZmlsbDojZmZmIi8+PC9zdmc+`
+};

+ 44 - 44
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/Floodfill.ts

@@ -1,44 +1,44 @@
-import { ToolParameters, ToolData } from '../textureEditorComponent';
-import { PointerEventTypes } from 'babylonjs/Events/pointerEvents';
-
-export const Floodfill : ToolData = {
-    name: "Floodfill",
-    type: class {
-        getParameters: () => ToolParameters;
-        pointerObservable: any;
-
-        constructor(getParameters: () => ToolParameters) {
-            this.getParameters = getParameters;
-        }
-
-        fill() {
-            const p = this.getParameters();
-            const ctx = p.canvas2D.getContext('2d')!;
-            ctx.fillStyle = p.getMetadata().color;
-            ctx.globalAlpha = p.getMetadata().opacity;
-            ctx.globalCompositeOperation = 'source-over';
-            ctx.fillRect(0,0, p.size.width, p.size.height);
-            p.updateTexture();
-        }
-        
-        setup () {
-            this.pointerObservable = this.getParameters().scene.onPointerObservable.add((pointerInfo) => {
-                if (pointerInfo.pickInfo?.hit) {
-                    if (pointerInfo.type === PointerEventTypes.POINTERDOWN) {
-                        this.fill();
-                    }
-                }
-            });
-        }
-        cleanup () {
-            if (this.pointerObservable) {
-                this.getParameters().scene.onPointerObservable.remove(this.pointerObservable);
-            }
-        }
-    },
-    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMjAsMTAuNWEu
-    NzUuNzUsMCwwLDAtMS41LDB2MS4yNWEyLjE0LDIuMTQsMCwwLDAtLjg0LjUzbC02Ljg4LDYuODhhMi4yNSwyLjI1LDAsMCwwLDAsMy4xOGw0Ljg4LDQuODhhMi4yNSwyLjI1LDAsMCwwLDMuMTgsMGw2Ljg4LTYuODhhMi4yNSwyLjI1LDAsMCwwLDAtMy4xOGwtNC44
-    OC00Ljg4YTIuMjksMi4yOSwwLDAsMC0uODQtLjUzWm0tOC4xNiw5LjcyLDYuNjYtNi42NlYxNUEuNzUuNzUsMCwwLDAsMjAsMTVWMTMuNTZsNC42Niw0LjY2YS43NS43NSwwLDAsMSwwLDEuMDZsLTEsMUgxMS44Wm0uNDcsMS41M2g5Ljg4bC00LjQxLDQuNDFhLjc1
-    Ljc1LDAsMCwxLTEuMDYsMFoiIHN0eWxlPSJmaWxsOiNmZmYiLz48cGF0aCBkPSJNMjcuNTEsMjEuODVhLjg4Ljg4LDAsMCwwLTEuNTQsMGwtMiwzLjc3YTMuMTUsMy4xNSwwLDEsMCw1LjU2LDBabS0yLjIzLDQuNDcsMS40Ni0yLjczLDEuNDUsMi43M2ExLjY1LDEu
-    NjUsMCwxLDEtMi45MSwwWiIgc3R5bGU9ImZpbGw6I2ZmZiIvPjwvc3ZnPg==`
-};
+import { IToolParameters, IToolData } from '../textureEditorComponent';
+import { PointerEventTypes } from 'babylonjs/Events/pointerEvents';
+
+export const Floodfill : IToolData = {
+    name: 'Floodfill',
+    type: class {
+        getParameters: () => IToolParameters;
+        pointerObservable: any;
+
+        constructor(getParameters: () => IToolParameters) {
+            this.getParameters = getParameters;
+        }
+
+        fill() {
+            const p = this.getParameters();
+            const ctx = p.canvas2D.getContext('2d')!;
+            ctx.fillStyle = p.metadata.color;
+            ctx.globalAlpha = p.metadata.opacity;
+            ctx.globalCompositeOperation = 'source-over';
+            ctx.fillRect(0,0, p.size.width, p.size.height);
+            p.updateTexture();
+        }
+        
+        setup () {
+            this.pointerObservable = this.getParameters().scene.onPointerObservable.add((pointerInfo) => {
+                if (pointerInfo.pickInfo?.hit) {
+                    if (pointerInfo.type === PointerEventTypes.POINTERDOWN) {
+                        this.fill();
+                    }
+                }
+            });
+        }
+        cleanup () {
+            if (this.pointerObservable) {
+                this.getParameters().scene.onPointerObservable.remove(this.pointerObservable);
+            }
+        }
+    },
+    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMjAsMTAuNWEu
+    NzUuNzUsMCwwLDAtMS41LDB2MS4yNWEyLjE0LDIuMTQsMCwwLDAtLjg0LjUzbC02Ljg4LDYuODhhMi4yNSwyLjI1LDAsMCwwLDAsMy4xOGw0Ljg4LDQuODhhMi4yNSwyLjI1LDAsMCwwLDMuMTgsMGw2Ljg4LTYuODhhMi4yNSwyLjI1LDAsMCwwLDAtMy4xOGwtNC44
+    OC00Ljg4YTIuMjksMi4yOSwwLDAsMC0uODQtLjUzWm0tOC4xNiw5LjcyLDYuNjYtNi42NlYxNUEuNzUuNzUsMCwwLDAsMjAsMTVWMTMuNTZsNC42Niw0LjY2YS43NS43NSwwLDAsMSwwLDEuMDZsLTEsMUgxMS44Wm0uNDcsMS41M2g5Ljg4bC00LjQxLDQuNDFhLjc1
+    Ljc1LDAsMCwxLTEuMDYsMFoiIHN0eWxlPSJmaWxsOiNmZmYiLz48cGF0aCBkPSJNMjcuNTEsMjEuODVhLjg4Ljg4LDAsMCwwLTEuNTQsMGwtMiwzLjc3YTMuMTUsMy4xNSwwLDEsMCw1LjU2LDBabS0yLjIzLDQuNDcsMS40Ni0yLjczLDEuNDUsMi43M2ExLjY1LDEu
+    NjUsMCwxLDEtMi45MSwwWiIgc3R5bGU9ImZpbGw6I2ZmZiIvPjwvc3ZnPg==`
+};

+ 97 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/paintbrush.ts

@@ -0,0 +1,97 @@
+import { IToolParameters, IToolData, IToolType } from '../textureEditorComponent';
+import { PointerEventTypes, PointerInfo } from 'babylonjs/Events/pointerEvents';
+import { TextBlock } from 'babylonjs-gui/2D/controls/textBlock';
+import { Slider } from 'babylonjs-gui/2D/controls/sliders/slider';
+
+export const Paintbrush : IToolData = {
+    name: 'Paintbrush',
+    type: class implements IToolType {
+        getParameters: () => IToolParameters;
+        pointerObservable: any;
+        isPainting: boolean;
+        GUI: {
+            radiusLabel : TextBlock;
+            radiusSlider : Slider;
+        };
+        radius = 15;
+
+        constructor(getParameters: () => IToolParameters) {
+            this.getParameters = getParameters;
+        }
+
+        paint(pointerInfo : PointerInfo) {
+            const {canvas2D, getMouseCoordinates, metadata, updateTexture } = this.getParameters();
+            const ctx = canvas2D.getContext('2d')!;
+            const {x, y} = getMouseCoordinates(pointerInfo);
+            ctx.globalCompositeOperation = 'source-over';
+            ctx.strokeStyle = metadata.color;
+            ctx.globalAlpha = metadata.opacity;
+            ctx.lineWidth = this.radius;
+            ctx.lineCap = 'round';
+            ctx.lineJoin = 'round';
+            ctx.lineTo(x, y);
+            ctx.stroke();
+            updateTexture();
+        }
+        
+        setup () {
+            const {scene, getMouseCoordinates, GUI, canvas2D} = this.getParameters();
+            const radiusLabel = new TextBlock();
+            radiusLabel.text = `Brush Width: ${this.radius}`;
+            radiusLabel.color = 'white';
+            radiusLabel.height = '20px';
+            radiusLabel.style = GUI.style;
+            GUI.toolWindow.addControl(radiusLabel);
+            const radiusSlider = new Slider();
+            radiusSlider.height = '20px';
+            radiusSlider.value = this.radius;
+            radiusSlider.minimum = 1;
+            radiusSlider.maximum = 100;
+            radiusSlider.step = 1;
+            radiusSlider.isThumbCircle = true;
+            radiusSlider.background = '#a3a3a3';
+            radiusSlider.color = '#33648f';
+            radiusSlider.borderColor = '#33648f';
+            radiusSlider.onValueChangedObservable.add(value => {
+                this.radius = value;
+                this.GUI.radiusLabel.text = `Brush Width: ${this.radius}`;
+            });
+            GUI.toolWindow.addControl(radiusSlider);
+            this.GUI = {radiusLabel, radiusSlider};
+
+            this.pointerObservable = scene.onPointerObservable.add((pointerInfo) => {
+                if (pointerInfo.pickInfo?.hit) {
+                    if (pointerInfo.type === PointerEventTypes.POINTERDOWN) {
+                        if (pointerInfo.event.button == 0) {
+                            this.isPainting = true;
+                            const {x, y} = getMouseCoordinates(pointerInfo);
+                            const ctx = canvas2D.getContext('2d')!;
+                            ctx.beginPath();
+                            ctx.moveTo(x, y);
+                          }
+                    }
+                    if (pointerInfo.type === PointerEventTypes.POINTERMOVE && this.isPainting) {
+                        this.paint(pointerInfo);
+                    }
+                }
+                if (pointerInfo.type === PointerEventTypes.POINTERUP) {
+                    if (pointerInfo.event.button == 0) {
+                        this.isPainting = false;
+                      }
+                }
+            });
+            this.isPainting = false;
+        }
+        cleanup () {
+            Object.entries(this.GUI).forEach(([key, value]) => value.dispose());
+            this.isPainting = false;
+            if (this.pointerObservable) {
+                this.getParameters().scene.onPointerObservable.remove(this.pointerObservable);
+            }
+        }
+    },
+    usesWindow: true,
+    icon: `PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMjksMTFhMy41
+    NywzLjU3LDAsMCwxLDAsNS4wNkwxNywyOGEyLjM0LDIuMzQsMCwwLDEtMSwuNThMMTAuOTEsMzBhLjc1Ljc1LDAsMCwxLS45Mi0uOTJMMTEuMzgsMjRBMi4zNCwyLjM0LDAsMCwxLDEyLDIzbDEyLTEyQTMuNTcsMy41NywwLDAsMSwyOSwxMVpNMjMsMTQuMSwxMywy
+    NGEuNjkuNjksMCwwLDAtLjE5LjMzbC0xLjA1LDMuODUsMy44NS0xQS42OS42OSwwLDAsMCwxNiwyN0wyNS45LDE3Wm0yLTItMSwxTDI3LDE2bDEtMUEyLjA4LDIuMDgsMCwxLDAsMjUsMTIuMDdaIiBzdHlsZT0iZmlsbDojZmZmIi8+PC9zdmc+`
+};

File diff suppressed because it is too large
+ 10 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/defaultTools/rectangleSelect.ts


+ 143 - 143
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/propertiesBar.tsx

@@ -1,144 +1,144 @@
-import * as React from 'react';
-import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
-import { PixelData } from './textureCanvasManager';
-
-interface PropertiesBarProps {
-    texture: BaseTexture;
-    saveTexture(): void;
-    pixelData: PixelData;
-    face: number;
-    setFace(face : number): void;
-    resetTexture() : void;
-    resizeTexture(width: number, height: number) : void;
-    uploadTexture(file : File) : void;
-}
-
-interface PropertiesBarState {
-    width: number;
-    height: number;
-}
-
-interface PixelDataProps {
-    name : string;
-    data?: number;
-}
-
-const resetButton = require('./assets/reset.svg');
-const uploadButton = require('./assets/upload.svg');
-const saveButton = require('./assets/save.svg');
-const babylonLogo = require('./assets/babylonLogo.svg');
-
-const posX = require('./assets/posX.svg');
-const posY = require('./assets/posY.svg');
-const posZ = require('./assets/posZ.svg');
-const negX = require('./assets/negX.svg');
-const negY = require('./assets/negY.svg');
-const negZ = require('./assets/negZ.svg');
-
-const resizeButton = require('./assets/resizeTool.svg');
-
-const mipUp = require('./assets/mipUp.svg');
-const mipDown = require('./assets/mipDown.svg');
-
-const faces = [
-    posX,
-    negX,
-    posY,
-    negY,
-    posZ,
-    negZ
-]
-
-function PixelData(props : PixelDataProps) {
-    return <span className='pixel-data'>{props.name}: <span className='value'>{props.data || '-'}</span></span>
-}
-
-function getNewDimension(oldDim : number, newDim : any) {
-    if (!isNaN(newDim)) {
-        if (parseInt(newDim) > 0) {
-            if (Number.isInteger(parseInt(newDim)))
-                return parseInt(newDim);
-        }
-    }
-    return oldDim;
-}
-
-export class PropertiesBar extends React.Component<PropertiesBarProps,PropertiesBarState> {
-    constructor(props : PropertiesBarProps) {
-        super(props);
-
-        this.state = {
-            width: props.texture.getSize().width,
-            height: props.texture.getSize().height
-        }
-    }
-    render() {
-        return <div id='properties'>
-                <div className='tab' id='logo-tab'>
-                    <img className='icon' src={babylonLogo}/>
-                </div>
-                <div className='tab' id='dimensions-tab'>
-                    <label className='dimensions'>
-                        W: <input type='text' value={this.state.width} onChange={(evt) => this.setState({width: getNewDimension(this.state.width, evt.target.value)})}/>
-                        </label>
-                    <label className='dimensions'>
-                        H: <input type='text' value={this.state.height} onChange={(evt) => this.setState({height: getNewDimension(this.state.height, evt.target.value)})}/>
-                        </label>
-                    <img id='resize' className='icon button' title='Resize' alt='Resize' src={resizeButton} onClick={() => this.props.resizeTexture(this.state.width, this.state.height)}/>
-                </div>
-                <div className='tab' id='pixel-coords-tab'>
-                    <PixelData name='X' data={this.props.pixelData.x}/>
-                    <PixelData name='Y' data={this.props.pixelData.y}/>
-                </div>
-                <div className='tab' id='pixel-color-tab'>
-                    <PixelData name='R' data={this.props.pixelData.r}/>
-                    <PixelData name='G' data={this.props.pixelData.g}/>
-                    <PixelData name='B' data={this.props.pixelData.b}/>
-                    <PixelData name='A' data={this.props.pixelData.a}/>
-                </div>
-                {this.props.texture.isCube &&
-                <>
-                    <div className='tab' id='face-tab'>
-                        {faces.map((face, index) =>
-                        <img
-                            key={index}
-                            className={this.props.face == index ? 'icon face button active' : 'icon face button'}
-                            src={face}
-                            onClick={() => this.props.setFace(index)}
-                        />)}
-                    </div>
-                    <div className='tab' id='mip-tab'>
-                        <img title='Mip Preview Up' className='icon button' src={mipUp} />
-                        <img title='Mip Preview Down' className='icon button' src={mipDown} />
-                    </div>
-                </>}
-                <div className='tab' id='right-tab'>
-                    <div className='content'>
-                        <img title='Reset' className='icon button' src={resetButton} onClick={() => this.props.resetTexture()}/>
-                        <label>
-                            <input
-                                accept='.jpg, .png, .tga, .dds, .env'
-                                type='file'
-                                onChange={
-                                    (evt : React.ChangeEvent<HTMLInputElement>) => {
-                                        const files = evt.target.files;
-                                        if (files && files.length) {
-                                            this.props.uploadTexture(files[0]);
-                                        }
-                                
-                                        evt.target.value = "";
-                                    }
-                                }
-                            />
-                            <img
-                                title='Upload'
-                                className='icon button'
-                                src={uploadButton}
-                            />
-                        </label>
-                        <img title='Save' className='icon button' src={saveButton} onClick={() => this.props.saveTexture()}/>
-                    </div>
-                </div>
-        </div>;
-    }
+import * as React from 'react';
+import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
+import { IPixelData } from './textureCanvasManager';
+
+interface IPropertiesBarProps {
+    texture: BaseTexture;
+    saveTexture(): void;
+    pixelData: IPixelData;
+    face: number;
+    setFace(face : number): void;
+    resetTexture() : void;
+    resizeTexture(width: number, height: number) : void;
+    uploadTexture(file : File) : void;
+}
+
+interface IPropertiesBarState {
+    width: number;
+    height: number;
+}
+
+interface IPixelDataProps {
+    name : string;
+    data?: number;
+}
+
+export class PropertiesBar extends React.Component<IPropertiesBarProps,IPropertiesBarState> {
+    private _resetButton = require('./assets/reset.svg');
+    private _uploadButton = require('./assets/upload.svg');
+    private _saveButton = require('./assets/save.svg');
+    private _babylonLogo = require('./assets/babylonLogo.svg');
+    
+    private _resizeButton = require('./assets/resizeTool.svg');
+    
+    private _mipUp = require('./assets/mipUp.svg');
+    private _mipDown = require('./assets/mipDown.svg');
+    
+
+    private _faces = [
+        require('./assets/posX.svg'),
+        require('./assets/posY.svg'),
+        require('./assets/posZ.svg'),
+        require('./assets/negX.svg'),
+        require('./assets/negY.svg'),
+        require('./assets/negZ.svg')
+    ]
+
+    constructor(props : IPropertiesBarProps) {
+        super(props);
+
+        this.state = {
+            width: props.texture.getSize().width,
+            height: props.texture.getSize().height
+        }
+    }
+
+    private pixelData(props: IPixelDataProps) {
+        return <span className='pixel-data'>{props.name}: <span className='value'>{props.data || '-'}</span></span>;
+    }
+
+    private getNewDimension(oldDim : number, newDim : any) {
+        if (!isNaN(newDim)) {
+            if (parseInt(newDim) > 0) {
+                if (Number.isInteger(parseInt(newDim)))
+                    return parseInt(newDim);
+            }
+        }
+        return oldDim;
+    }
+
+    render() {
+        return <div id='properties'>
+                <div className='tab' id='logo-tab'>
+                    <img className='icon' src={this._babylonLogo}/>
+                </div>
+                <div className='tab' id='dimensions-tab'>
+                    <form onSubmit={evt => {
+                        this.props.resizeTexture(this.state.width, this.state.height);
+                        evt.preventDefault();
+                    }}>
+                        <label className='dimensions'>
+                            W: <input type='text' value={this.state.width} onChange={(evt) => this.setState({width: this.getNewDimension(this.state.width, evt.target.value)})}/>
+                            </label>
+                        <label className='dimensions'>
+                            H: <input type='text' value={this.state.height} onChange={(evt) => this.setState({height: this.getNewDimension(this.state.height, evt.target.value)})}/>
+                            </label>
+                        <img id='resize' className='icon button' title='Resize' alt='Resize' src={this._resizeButton} onClick={() => this.props.resizeTexture(this.state.width, this.state.height)}/> 
+                    </form>
+                </div>
+                <div className='tab' id='pixel-coords-tab'>
+                    <this.pixelData name='X' data={this.props.pixelData.x}/>
+                    <this.pixelData name='Y' data={this.props.pixelData.y}/>
+                </div>
+                <div className='tab' id='pixel-color-tab'>
+                    <this.pixelData name='R' data={this.props.pixelData.r}/>
+                    <this.pixelData name='G' data={this.props.pixelData.g}/>
+                    <this.pixelData name='B' data={this.props.pixelData.b}/>
+                    <this.pixelData name='A' data={this.props.pixelData.a}/>
+                </div>
+                {this.props.texture.isCube &&
+                <>
+                    <div className='tab' id='face-tab'>
+                        {this._faces.map((face, index) =>
+                        <img
+                            key={index}
+                            className={this.props.face == index ? 'icon face button active' : 'icon face button'}
+                            src={face}
+                            onClick={() => this.props.setFace(index)}
+                        />)}
+                    </div>
+                    <div className='tab' id='mip-tab'>
+                        <img title='Mip Preview Up' className='icon button' src={this._mipUp} />
+                        <img title='Mip Preview Down' className='icon button' src={this._mipDown} />
+                    </div>
+                </>}
+                <div className='tab' id='right-tab'>
+                    <div className='content'>
+                        <img title='Reset' className='icon button' src={this._resetButton} onClick={() => this.props.resetTexture()}/>
+                        <label>
+                            <input
+                                accept='.jpg, .png, .tga, .dds, .env'
+                                type='file'
+                                onChange={
+                                    (evt : React.ChangeEvent<HTMLInputElement>) => {
+                                        const files = evt.target.files;
+                                        if (files && files.length) {
+                                            this.props.uploadTexture(files[0]);
+                                        }
+                                
+                                        evt.target.value = "";
+                                    }
+                                }
+                            />
+                            <img
+                                title='Upload'
+                                className='icon button'
+                                src={this._uploadButton}
+                            />
+                        </label>
+                        <img title='Save' className='icon button' src={this._saveButton} onClick={() => this.props.saveTexture()}/>
+                    </div>
+                </div>
+        </div>;
+    }
 }

+ 5 - 5
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasComponent.tsx

@@ -1,23 +1,23 @@
 import * as React from 'react';
 import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
 
-interface TextureCanvasComponentProps {
+interface ITextureCanvasComponentProps {
     canvasUI : React.RefObject<HTMLCanvasElement>;
     canvas2D : React.RefObject<HTMLCanvasElement>;
-    canvasDisplay : React.RefObject<HTMLCanvasElement>;
+    canvas3D : React.RefObject<HTMLCanvasElement>;
     texture : BaseTexture;
 }
 
-export class TextureCanvasComponent extends React.Component<TextureCanvasComponentProps> {
-    shouldComponentUpdate(nextProps : TextureCanvasComponentProps) {
+export class TextureCanvasComponent extends React.Component<ITextureCanvasComponentProps> {
+    shouldComponentUpdate(nextProps : ITextureCanvasComponentProps) {
         return (nextProps.texture !== this.props.texture);
     }
 
     render() {
         return <div>
             <canvas id="canvas-ui" ref={this.props.canvasUI} tabIndex={1}></canvas>
-            <canvas id="canvas-display" ref={this.props.canvasDisplay} width={this.props.texture.getSize().width} height={this.props.texture.getSize().height} hidden={true}></canvas>
             <canvas id="canvas-2D" ref={this.props.canvas2D} width={this.props.texture.getSize().width} height={this.props.texture.getSize().height} hidden={true}></canvas>
+            <canvas id="canvas-3D" ref={this.props.canvas3D} width={this.props.texture.getSize().width} height={this.props.texture.getSize().height} hidden={true}></canvas>
         </div>
     }
 }

+ 293 - 107
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureCanvasManager.ts

@@ -1,7 +1,7 @@
 import { Engine } from 'babylonjs/Engines/engine';
 import { Scene } from 'babylonjs/scene';
 import { Vector3, Vector2 } from 'babylonjs/Maths/math.vector';
-import { Color4 } from 'babylonjs/Maths/math.color';
+import { Color4, Color3 } from 'babylonjs/Maths/math.color';
 import { FreeCamera } from 'babylonjs/Cameras/freeCamera';
 import { Nullable } from 'babylonjs/types'
 
@@ -13,11 +13,10 @@ import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
 import { HtmlElementTexture } from 'babylonjs/Materials/Textures/htmlElementTexture';
 import { InternalTexture } from 'babylonjs/Materials/Textures/internalTexture';
 import { Texture } from 'babylonjs/Materials/Textures/texture';
-import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
-import { PBRMaterial } from 'babylonjs/Materials/PBR/pbrMaterial';
 import { RawCubeTexture } from 'babylonjs/Materials/Textures/rawCubeTexture';
 import { CubeTexture } from 'babylonjs/Materials/Textures/cubeTexture';
-
+import { ShaderMaterial } from 'babylonjs/Materials/shaderMaterial';
+import { StandardMaterial } from 'babylonjs/Materials/standardMaterial';
 
 import { ISize } from 'babylonjs/Maths/math.size';
 import { Tools } from 'babylonjs/Misc/tools';
@@ -25,12 +24,19 @@ import { Tools } from 'babylonjs/Misc/tools';
 import { PointerEventTypes, PointerInfo } from 'babylonjs/Events/pointerEvents';
 import { KeyboardEventTypes } from 'babylonjs/Events/keyboardEvents';
 
-import { TextureHelper, TextureChannelsToDisplay } from '../../../../../../textureHelper';
+import { TextureHelper } from '../../../../../../textureHelper';
+
+import { ITool } from './toolBar';
+import { IChannel } from './channelsBar';
+import { TextBlock } from 'babylonjs-gui/2D/controls/textBlock';
+import { Rectangle } from 'babylonjs-gui/2D/controls/rectangle';
+import { StackPanel } from 'babylonjs-gui/2D/controls/stackPanel';
+import { Control } from 'babylonjs-gui/2D/controls/control';
+import { Style } from 'babylonjs-gui/2D/style';
+import { AdvancedDynamicTexture } from 'babylonjs-gui/2D/advancedDynamicTexture';
 
-import { Tool } from './toolBar';
-import { Channel } from './channelsBar';
 
-export interface PixelData {
+export interface IPixelData {
     x? : number;
     y? : number;
     r? : number;
@@ -39,6 +45,14 @@ export interface PixelData {
     a? : number;
 }
 
+export interface IToolGUI {
+    adt: AdvancedDynamicTexture;
+    toolWindow: StackPanel;
+    isDragging: boolean;
+    dragCoords: Nullable<Vector2>;
+    style: Style;
+}
+
 export class TextureCanvasManager {
     private _engine: Engine;
     private _scene: Scene;
@@ -55,12 +69,18 @@ export class TextureCanvasManager {
 
     /* The canvas we paint onto using the canvas API */
     private _2DCanvas : HTMLCanvasElement;
+    /* The canvas we apply post processes to */
+    private _3DCanvas : HTMLCanvasElement;
+    /* The canvas which handles channel filtering */
+    private _channelsTexture : HtmlElementTexture;
 
-    private _displayCanvas : HTMLCanvasElement;
-    private _channels : Channel[] = [];
+    private _3DEngine : Engine;
+    private _3DPlane : Mesh;
+    private _3DCanvasTexture : HtmlElementTexture;
+    private _3DScene : Scene;
+
+    private _channels : IChannel[] = [];
     private _face : number = 0;
-    /* The texture that we are actually displaying. It is created by sampling a combination of channels from _texture */
-    private _displayTexture : HtmlElementTexture;
 
     /* The texture from the original engine that we invoked the editor on */
     private _originalTexture: BaseTexture;
@@ -72,8 +92,7 @@ export class TextureCanvasManager {
     private _didEdit : boolean = false;
 
     private _plane : Mesh;
-    private _planeMaterial : NodeMaterial;
-    private _planeFallbackMaterial : PBRMaterial;
+    private _planeMaterial : ShaderMaterial;
 
     /* Tracks which keys are currently pressed */
     private _keyMap : any = {};
@@ -84,31 +103,36 @@ export class TextureCanvasManager {
     private static ZOOM_OUT_KEY : string = '-';
 
     private static PAN_SPEED : number = 0.002;
-    private static PAN_MOUSE_BUTTON : number = 0; // LMB
-    private static PAN_KEY : string = ' ';
+    private static PAN_MOUSE_BUTTON : number = 1; // MMB
 
     private static MIN_SCALE : number = 0.01;
     private static MAX_SCALE : number = 10;
 
-    private _tool : Nullable<Tool>;
+    private _tool : Nullable<ITool>;
+
+    private _setPixelData : (pixelData : IPixelData) => void;
+
+    private _GUI : IToolGUI;
 
-    private _setPixelData : (pixelData : PixelData) => void;
+    private _window : Window;
 
-    public metadata : any = {
-        color: '#ffffff',
-        opacity: 1.0
-    };
+    public metadata : any = {};
+
+    private _editing3D : boolean = false;
 
     public constructor(
         texture: BaseTexture,
+        window: Window,
         canvasUI: HTMLCanvasElement,
         canvas2D: HTMLCanvasElement,
-        canvasDisplay: HTMLCanvasElement,
-        setPixelData : (pixelData : PixelData) => void
+        canvas3D: HTMLCanvasElement,
+        setPixelData: (pixelData : IPixelData) => void
     ) {
+        this._window = window;
+
         this._UICanvas = canvasUI;
         this._2DCanvas = canvas2D;
-        this._displayCanvas = canvasDisplay;
+        this._3DCanvas = canvas3D;
         this._setPixelData = setPixelData;
 
         this._size = texture.getSize();
@@ -118,34 +142,178 @@ export class TextureCanvasManager {
         this._scene = new Scene(this._engine);
         this._scene.clearColor = new Color4(0.11, 0.11, 0.11, 1.0);
 
-        this._camera = new FreeCamera("Camera", new Vector3(0, 0, -1), this._scene);
+        this._camera = new FreeCamera('camera', new Vector3(0, 0, -1), this._scene);
         this._camera.mode = Camera.ORTHOGRAPHIC_CAMERA;
 
-        this._planeFallbackMaterial = new PBRMaterial('fallback_material', this._scene);
-        this._planeFallbackMaterial.albedoTexture = this._displayTexture;
-        this._planeFallbackMaterial.disableLighting = true;
-        this._planeFallbackMaterial.unlit = true;
+        this._channelsTexture = new HtmlElementTexture('ct', this._2DCanvas, {engine: this._engine, scene: null, samplingMode: Engine.TEXTURE_NEAREST_LINEAR});
+
+        this._3DEngine = new Engine(this._3DCanvas);
+        this._3DScene = new Scene(this._3DEngine);
+        this._3DScene.clearColor = new Color4(0,0,0,0);
+        this._3DCanvasTexture = new HtmlElementTexture('canvas', this._2DCanvas, {engine: this._3DEngine, scene: this._3DScene});
+        this._3DCanvasTexture.hasAlpha = true;
+        const cam = new FreeCamera('camera', new Vector3(0,0,-1), this._3DScene);
+        cam.mode = Camera.ORTHOGRAPHIC_CAMERA;
+        [cam.orthoBottom, cam.orthoLeft, cam.orthoTop, cam.orthoRight] = [-0.5, -0.5, 0.5, 0.5];
+        this._3DPlane = PlaneBuilder.CreatePlane('texture', {width: 1, height: 1}, this._3DScene);
+        const mat = new StandardMaterial('material', this._3DScene);
+        mat.diffuseTexture = this._3DCanvasTexture;
+        mat.disableLighting = true;
+        mat.emissiveColor = Color3.White();
+        this._3DPlane.material = mat;
+        
 
-        this._displayTexture = new HtmlElementTexture("display", this._displayCanvas, {engine: this._engine, scene: this._scene});
-        this._displayTexture.updateSamplingMode(Engine.TEXTURE_NEAREST_LINEAR);
         this.grabOriginalTexture();
 
-        NodeMaterial.ParseFromSnippetAsync("#TPSEV2#4", this._scene)
-            .then((material) => {
-                this._planeMaterial = material;
-                this._planeMaterial.getTextureBlocks()[0].texture = this._displayTexture;
-                this._plane.material = this._planeMaterial;
-                this._UICanvas.focus();
-            });
+
+        this._planeMaterial = new ShaderMaterial(
+            'shader',
+            this._scene,
+            {
+                vertexSource: `
+                    precision highp float;
+
+                    attribute vec3 position;
+                    attribute vec2 uv;
+
+                    uniform mat4 worldViewProjection;
+
+                    varying vec2 vUV;
+
+                    void main(void) {
+                        gl_Position = worldViewProjection * vec4(position, 1.0);
+                        vUV = uv;
+                    }
+                `,
+                fragmentSource: `
+                    precision highp float;
+            
+                    uniform sampler2D textureSampler;
+            
+                    uniform bool r;
+                    uniform bool g;
+                    uniform bool b;
+                    uniform bool a;
+            
+                    varying vec2 vUV;
+            
+                    void main(void) {
+                        float size = 20.0;
+                        vec2 pos2 = vec2(gl_FragCoord.x, gl_FragCoord.y);
+                        vec2 pos = floor(pos2 * 0.05);
+                        float pattern = mod(pos.x + pos.y, 2.0); 
+                        if (pattern == 0.0) {
+                            pattern = 0.7;
+                        }
+                        vec4 bg = vec4(pattern, pattern, pattern, 1.0);
+                        vec4 col = texture(textureSampler, vUV);
+                        if (!r && !g && !b) {
+                            if (a) {
+                                col = vec4(col.a, col.a, col.a, 1.0);
+                            } else {
+                                col = vec4(0.0,0.0,0.0,0.0);
+                            }
+                        } else {
+                            if (!r) {
+                                col.r = 0.0;
+                                if (!b) {
+                                    col.r = col.g;
+                                }
+                                else if (!g) {
+                                    col.r = col.b;
+                                }
+                            }
+                            if (!g) {
+                                col.g = 0.0;
+                                if (!b) {
+                                    col.g = col.r;
+                                }
+                                else if (!r) {
+                                    col.g = col.b;
+                                }
+                            }
+                            if (!b) {
+                                col.b = 0.0;
+                                if (!r) {
+                                    col.b = col.g;
+                                } else if (!g) {
+                                    col.b = col.r;
+                                }
+                            }
+                            if (!a) {
+                                col.a = 1.0;
+                            }
+                        }
+                        gl_FragColor = col;
+                        gl_FragColor = col * (col.a) + bg * (1.0 - col.a);
+                    }`
+            },
+        {
+            attributes: ['position', 'uv'],
+            uniforms: ['worldViewProjection', 'textureSampler', 'r', 'g', 'b', 'a']
+        });
+
+        this._planeMaterial.setTexture('textureSampler', this._channelsTexture);
+        this._planeMaterial.setFloat('r', 1.0);
+        this._planeMaterial.setFloat('g', 1.0);
+        this._planeMaterial.setFloat('b', 1.0);
+        this._planeMaterial.setFloat('a', 1.0);
+        this._plane.material = this._planeMaterial;
+        
+        const adt = AdvancedDynamicTexture.CreateFullscreenUI('gui', true, this._scene);
+        const style = adt.createStyle();
+        style.fontFamily = 'acumin-pro-condensed';
+        style.fontSize = '15px';
+
+        const toolWindow = new StackPanel();
+        toolWindow.background = '#333333';
+        toolWindow.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_RIGHT;
+        toolWindow.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM;
+        toolWindow.left = '0px';
+        toolWindow.top = '-30px';
+        toolWindow.width = '200px';
+        toolWindow.isVisible = false;
+        toolWindow.isPointerBlocker = true;
+        adt.addControl(toolWindow);
+
+        this._GUI = {adt, style, toolWindow, isDragging: false, dragCoords: null};
+
+        const topBar = new Rectangle();
+        topBar.width = '100%';
+        topBar.height = '20px';
+        topBar.background = '#666666';
+        topBar.thickness = 0;
+        topBar.hoverCursor = 'grab';
+        topBar.onPointerDownObservable.add(evt => {this._GUI.isDragging = true; topBar.hoverCursor = 'grabbing';});
+        topBar.onPointerUpObservable.add(() => {this._GUI.isDragging = false; this._GUI.dragCoords = null; topBar.hoverCursor = 'grab';});
+
+        const title = new TextBlock();
+        title.text = 'Tool Settings';
+        title.color = 'white';
+        title.height = '20px';
+        title.style = this._GUI.style;
+        topBar.addControl(title);
+        this._GUI.toolWindow.addControl(topBar);
+
+        this._window.addEventListener('pointermove',  (evt : PointerEvent) => {
+            if (!this._GUI.isDragging) return;
+            if (!this._GUI.dragCoords) {
+                this._GUI.dragCoords = new Vector2(evt.x, evt.y);
+                return;
+            }
+            let x = parseInt(this._GUI.toolWindow.left.toString().replace('px', ''));
+            let y = parseInt(this._GUI.toolWindow.top.toString().replace('px', ''));
+            x += evt.x - this._GUI.dragCoords.x;
+            y += evt.y - this._GUI.dragCoords.y;
+            this._GUI.toolWindow.left = `${x}px`;
+            this._GUI.toolWindow.top = `${y}px`;
+            this._GUI.dragCoords.x = evt.x;
+            this._GUI.dragCoords.y = evt.y;
+        });
 
         this._engine.runRenderLoop(() => {
             this._engine.resize();
             this._scene.render();
-            let cursor = 'initial';
-            if (this._keyMap[TextureCanvasManager.PAN_KEY]) {
-                cursor = 'pointer';
-            }
-            this._UICanvas.parentElement!.style.cursor = cursor;
         });
 
         this._scale = 1.5;
@@ -167,7 +335,7 @@ export class TextureCanvasManager {
                     this._scale -= (event.deltaY * TextureCanvasManager.ZOOM_MOUSE_SPEED * this._scale);
                     break;
                 case PointerEventTypes.POINTERDOWN:
-                    if (pointerInfo.event.button === TextureCanvasManager.PAN_MOUSE_BUTTON && this._keyMap[TextureCanvasManager.PAN_KEY]) {
+                    if (pointerInfo.event.button === TextureCanvasManager.PAN_MOUSE_BUTTON) {
                         this._isPanning = true;
                         this._mouseX = pointerInfo.event.x;
                         this._mouseY = pointerInfo.event.y;
@@ -211,67 +379,47 @@ export class TextureCanvasManager {
                     break;
                 case KeyboardEventTypes.KEYUP:
                     this._keyMap[kbInfo.event.key] = false;
-                    if (kbInfo.event.key == TextureCanvasManager.PAN_KEY) {
-                        this._isPanning = false;
-                    }
                 break;
             }
-        })
-
-        this._scene.debugLayer.show();
-
+        });
     }
 
     public async updateTexture() {
         this._didEdit = true;
+        const element = this._editing3D ? this._3DCanvas : this._2DCanvas;
+        if (this._editing3D) {
+            this._3DCanvasTexture.update();
+            this._3DScene.render();
+        }
         if (this._originalTexture.isCube) {
             // TODO: fix cube map editing
-            let pixels : ArrayBufferView[] = [];
-            for (let face = 0; face < 6; face++) {
-                let textureToCopy = this._originalTexture;
-                if (face === this._face) {
-                    // textureToCopy = this._texture;
-                }
-                pixels[face] = await TextureHelper.GetTextureDataAsync(textureToCopy, this._size.width, this._size.height, face, {R: true, G: true, B: true, A: true});
-            }
-            if (!this._target) {
-                this._target = new RawCubeTexture(this._originalTexture.getScene()!, pixels, this._size.width, this._originalTexture.textureFormat, Engine.TEXTURETYPE_UNSIGNED_INT, false);
-                this._target.getScene()?.removeTexture(this._target);
-            } else {
-                (this._target as RawCubeTexture).update(pixels, this._originalTexture.textureFormat, this._originalTexture.textureType, false);
-            }
         } else {
             if (!this._target) {
                 this._target = new HtmlElementTexture(
                     "editor",
-                    this._2DCanvas,
+                    element,
                     {
                         engine: this._originalTexture.getScene()?.getEngine()!,
                         scene: null,
                         samplingMode: (this._originalTexture as Texture).samplingMode
                     }
                 );
+            } else {
+                (this._target as HtmlElementTexture).element = element;
             }
             (this._target as HtmlElementTexture).update((this._originalTexture as Texture).invertY);
         }
         this._originalTexture._texture = this._target._texture;
-        this.copyTextureToDisplayTexture();
+        this._channelsTexture.element = element;
+        this.updateDisplay();
     }
 
-    private async copyTextureToDisplayTexture() {
-        let channelsToDisplay : TextureChannelsToDisplay = {
-            R: true,
-            G: true,
-            B: true,
-            A: true
-        }
-        this._channels.forEach(channel => channelsToDisplay[channel.id] = channel.visible);
-        const pixels = await TextureHelper.GetTextureDataAsync(this._originalTexture, this._size.width, this._size.height, this._face, channelsToDisplay);
-        TextureCanvasManager.paintPixelsOnCanvas(pixels, this._displayCanvas);
-        this._displayTexture.update();
+    private updateDisplay() {
+        this._3DScene.render()
+        this._channelsTexture.update();
     }
 
-    public set channels(channels: Channel[]) {
+    public set channels(channels: IChannel[]) {
         // Determine if we need to re-render the texture. This is an expensive operation, so we should only do it if channel visibility has changed.
         let needsRender = false;
         if (channels.length !== this._channels.length) {
@@ -282,13 +430,14 @@ export class TextureCanvasManager {
                 (channel,index) => {
                     if (channel.visible !== this._channels[index].visible) {
                         needsRender = true;
+                        this._planeMaterial.setFloat(channel.id.toLowerCase(), channel.visible ? 1.0 : 0.0);
                     }
                 }
             );
         }
         this._channels = channels;
         if (needsRender) {
-            this.copyTextureToDisplayTexture();
+            this.updateDisplay();
         }
     }
 
@@ -299,10 +448,9 @@ export class TextureCanvasManager {
         ctx.putImageData(imgData, 0, 0);
     }
 
-    public grabOriginalTexture() {
+    public grabOriginalTexture(adjustZoom = true) {
         // Grab image data from original texture and paint it onto the context of a DynamicTexture
-        this._size = this._originalTexture.getSize();
-        this.updateSize();
+        this.setSize(this._originalTexture.getSize(), adjustZoom);
         TextureHelper.GetTextureDataAsync(
             this._originalTexture,
             this._size.width,
@@ -311,11 +459,12 @@ export class TextureCanvasManager {
             {R:true ,G:true ,B:true ,A:true}
         ).then(data => {
             TextureCanvasManager.paintPixelsOnCanvas(data, this._2DCanvas);
-            this.copyTextureToDisplayTexture();
+            this._3DCanvasTexture.update();
+            this.updateDisplay();
         })
     }
 
-    public getMouseCoordinates(pointerInfo : PointerInfo) : Vector2 {
+    public getMouseCoordinates(pointerInfo : PointerInfo) {
         if (pointerInfo.pickInfo?.hit) {
             const x = Math.floor(pointerInfo.pickInfo.getTextureCoordinates()!.x * this._size.width);
             const y = Math.floor((1 - pointerInfo.pickInfo.getTextureCoordinates()!.y) * this._size.height);
@@ -325,39 +474,64 @@ export class TextureCanvasManager {
         }
     }
 
-    public get scene() : Scene {
+    public get scene() {
         return this._scene;
     }
 
-    public get canvas2D() : HTMLCanvasElement {
+    public get canvas2D() {
         return this._2DCanvas;
     }
 
-    public get size() : ISize {
+    public get size() {
         return this._size;
     }
 
-    public set tool(tool: Nullable<Tool>) {
+    public set tool(tool: Nullable<ITool>) {
         if (this._tool) {
             this._tool.instance.cleanup();
         }
         this._tool = tool;
         if (this._tool) {
             this._tool.instance.setup();
+            if (this._tool.usesWindow) {
+                this._GUI.toolWindow.isVisible = true;
+            } else {
+                this._GUI.toolWindow.isVisible = false;
+            }
+            if (this._editing3D && !this._tool.is3D) {
+                this._editing3D = false;
+                this._2DCanvas.getContext('2d')?.drawImage(this._3DCanvas, 0, 0);
+            }
+            else if (!this._editing3D && this._tool.is3D) {
+                this._editing3D = true;
+                this.updateTexture();
+            }
         }
     }
 
-    public get tool(): Nullable<Tool> {
+    public get tool() {
         return this._tool;
     }
 
+    // BROKEN : FIX THIS
     public set face(face: number) {
         if (this._face !== face) {
             this._face = face;
-            this.copyTextureToDisplayTexture();
+            this.grabOriginalTexture(false);
+            this.updateDisplay();
         }
     }
 
+    /** Returns the tool GUI object, allowing tools to access the GUI */
+    public get GUI() {
+        return this._GUI;
+    }
+
+    /** Returns the 3D scene used for postprocesses */
+    public get scene3D() {
+        return this._3DScene;
+    }
+
     private makePlane() {
         const textureRatio = this._size.width / this._size.height;
         if (this._plane) this._plane.dispose();
@@ -366,10 +540,13 @@ export class TextureCanvasManager {
         this._plane.edgesWidth = 4.0;
         this._plane.edgesColor = new Color4(1,1,1,1);
         this._plane.enablePointerMoveEvents = true;
-        if (this._planeMaterial) this._plane.material = this._planeMaterial; else this._plane.material = this._planeFallbackMaterial;
+        this._plane.material = this._planeMaterial;
     }
 
     public reset() : void {
+        if (this._tool && this._tool.instance.onReset) {
+            this._tool.instance.onReset();
+        }
         this._originalTexture._texture = this._originalInternalTexture;
         this.grabOriginalTexture();
         this.makePlane();
@@ -378,18 +555,23 @@ export class TextureCanvasManager {
 
     public async resize(newSize : ISize) {
         const data = await TextureHelper.GetTextureDataAsync(this._originalTexture, newSize.width, newSize.height, this._face, {R: true,G: true,B: true,A: true});
-        this._size = newSize;
-        this.updateSize();
+        this.setSize(newSize);
         TextureCanvasManager.paintPixelsOnCanvas(data, this._2DCanvas);
         this.updateTexture();
         this._didEdit = true;
     }
 
-    private updateSize() {
+    public setSize(size: ISize, adjustZoom = true) {
+        this._size = size;
         this._2DCanvas.width = this._size.width;
         this._2DCanvas.height = this._size.height;
-        this._displayCanvas.width = this._size.width;
-        this._displayCanvas.height = this._size.height;
+        this._3DCanvas.width = this._size.width;
+        this._3DCanvas.height = this._size.height;
+        if (adjustZoom) {
+            this._camera.position.x = 0;
+            this._camera.position.y = 0;
+            this._scale = 1.5 / (this._size.width/this._size.height);
+        }
         this.makePlane();
     }
 
@@ -429,14 +611,16 @@ export class TextureCanvasManager {
                         () => {
                             TextureHelper.GetTextureDataAsync(texture, texture.getSize().width, texture.getSize().height, 0, {R: true, G: true, B: true, A: true})
                                 .then((pixels) => {
-                                    this._size = texture.getSize();
-                                    this.updateSize();
+                                    if (this._tool && this._tool.instance.onReset) {
+                                        this._tool.instance.onReset();
+                                    }
+                                    this.setSize(texture.getSize());
                                     TextureCanvasManager.paintPixelsOnCanvas(pixels, this._2DCanvas);
                                     this.updateTexture();
                                     texture.dispose();
                                 });
-                        });
-                    
+                        }
+                    );
                 }
             };
 
@@ -444,17 +628,19 @@ export class TextureCanvasManager {
     }
 
     public dispose() {
-        if (this._planeMaterial) {
-            this._planeMaterial.dispose();
-        }
         if (this._didEdit) {
             this._originalInternalTexture?.dispose();
         }
         if (this._tool) {
             this._tool.instance.cleanup();
-        }
-        this._displayTexture.dispose();
+        }        
+        this._3DPlane.dispose();
+        this._3DCanvasTexture.dispose();
+        this._3DScene.dispose();
+        this._3DEngine.dispose();
         this._plane.dispose();
+        this._channelsTexture.dispose();
+        this._planeMaterial.dispose();
         this._camera.dispose();
         this._scene.dispose();
         this._engine.dispose();

+ 8 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditor.scss

@@ -3,6 +3,7 @@
     width: 100%;
     color: white;
     background-color: #1e1e1e;
+    font-family: 'acumin-pro-condensed';
 
     .icon {
         width: 40px;
@@ -13,11 +14,13 @@
                 background-color: #4a4a4a;
                 cursor: pointer;
             }
+            // When the button is selected - className='active'
             &.active {
                 background-color: #666666;
             }
+            // When the button is clicked
             &:active {
-                background-color: #888888;
+                background-color: #837c7c;
             }
         }
     }
@@ -66,6 +69,9 @@
             background-color: #333333;
         }
         #dimensions-tab {
+            form {
+                display: flex;
+            }
             label {
                 margin-left: 15px;
                 color: #afafaf;
@@ -146,7 +152,7 @@
             position: relative;
             #add-tool-popup {
                 background-color: #333333;
-                width: 340px;
+                width: 348px;
                 margin-left: 40px;
                 position: absolute;
                 top: 0px;

+ 83 - 35
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/textureEditorComponent.tsx

@@ -1,9 +1,9 @@
 import * as React from 'react';
 import { GlobalState } from '../../../../../globalState';
-import { TextureCanvasManager, PixelData } from './textureCanvasManager';
-import { Tool, ToolBar } from './toolBar';
+import { TextureCanvasManager, IPixelData, IToolGUI } from './textureCanvasManager';
+import { ITool, ToolBar } from './toolBar';
 import { PropertiesBar } from './propertiesBar';
-import { Channel, ChannelsBar } from './channelsBar';
+import { IChannel, ChannelsBar } from './channelsBar';
 import { BottomBar } from './bottomBar';
 import { TextureCanvasComponent } from './textureCanvasComponent';
 import defaultTools from './defaultTools/defaultTools';
@@ -12,52 +12,94 @@ import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
 import { Tools } from 'babylonjs/Misc/tools';
 import { Scene } from 'babylonjs/scene';
 import { ISize } from 'babylonjs/Maths/math.size';
+import { Vector2 } from 'babylonjs/Maths/math.vector';
+import { PointerInfo } from 'babylonjs/Events/pointerEvents';
+
+import { PopupComponent } from '../../../../../popupComponent';
 
 require('./textureEditor.scss');
 
-interface TextureEditorComponentProps {
+interface ITextureEditorComponentProps {
     globalState: GlobalState;
     texture: BaseTexture;
     url: string;
+    window: React.RefObject<PopupComponent>;
 }
 
-interface TextureEditorComponentState {
-    tools: Tool[];
+interface ITextureEditorComponentState {
+    tools: ITool[];
     activeToolIndex: number;
     metadata: any;
-    channels: Channel[];
-    pixelData : PixelData;
+    channels: IChannel[];
+    pixelData : IPixelData;
     face: number;
 }
 
-export interface ToolParameters {
+export interface IToolParameters {
+    /** The visible scene in the editor. Useful for adding pointer and keyboard events. */
     scene: Scene;
+    /** The 2D canvas which tools can paint on using the canvas API. */
     canvas2D: HTMLCanvasElement;
+    /** The 3D scene which tools can add post processes to. */
+    scene3D: Scene;
+    /** The size of the texture. */
     size: ISize;
+    /** Pushes the editor texture back to the original scene. This should be called every time a tool makes any modification to a texture. */
     updateTexture: () => void;
-    getMetadata: () => any;
+    /** The metadata object which is shared between all tools. Feel free to store any information here. Do not set this directly: instead call setMetadata. */
+    metadata: any;
+    /** Call this when you want to mutate the metadata. */
     setMetadata: (data : any) => void;
+    /** Returns the texture coordinates under the cursor */
+    getMouseCoordinates: (pointerInfo : PointerInfo) => Vector2;
+    /** An object which holds the GUI's ADT as well as the tool window. */
+    GUI: IToolGUI;
+    /** Provides access to the BABYLON namespace */
+    BABYLON: any;
 }
 
-export interface ToolData {
+
+/** An interface representing the definition of a tool */
+export interface IToolData {
+    /** Name to display on the toolbar */
     name: string;
-    type: any;
+    /** A class definition for the tool including setup and cleanup methods */
+    type: IToolConstructable;
+    /**  An SVG icon encoded in Base64 */
     icon: string;
+    /** Whether the tool uses the draggable GUI window */
+    usesWindow? : boolean;
+    /** Whether the tool uses postprocesses */
+    is3D? : boolean;
+}
+
+export interface IToolType {
+    /** Called when the tool is selected. */
+    setup: () => void;
+    /** Called when the tool is deselected. */
+    cleanup: () => void;
+    /** Optional. Called when the user resets the texture or uploads a new texture. Tools may want to reset their state when this happens. */
+    onReset?: () => void;
+}
+
+/** For constructable types, TS requires that you define a seperate interface which constructs your actual interface */
+interface IToolConstructable {
+    new (getParameters: () => IToolParameters) : IToolType;
 }
 
 declare global {
-    var _TOOL_DATA_ : ToolData;
+    var _TOOL_DATA_ : IToolData;
 }
 
-export class TextureEditorComponent extends React.Component<TextureEditorComponentProps, TextureEditorComponentState> {
+export class TextureEditorComponent extends React.Component<ITextureEditorComponentProps, ITextureEditorComponentState> {
     private _textureCanvasManager: TextureCanvasManager;
-    private canvasUI = React.createRef<HTMLCanvasElement>();
-    private canvas2D = React.createRef<HTMLCanvasElement>();
-    private canvasDisplay = React.createRef<HTMLCanvasElement>();
+    private _UICanvas = React.createRef<HTMLCanvasElement>();
+    private _2DCanvas = React.createRef<HTMLCanvasElement>();
+    private _3DCanvas = React.createRef<HTMLCanvasElement>();
 
-    constructor(props : TextureEditorComponentProps) {
+    constructor(props : ITextureEditorComponentProps) {
         super(props);
-        let channels : Channel[] = [
+        let channels : IChannel[] = [
             {name: 'Red', visible: true, editable: true, id: 'R', icon: require('./assets/channelR.svg')},
             {name: 'Green', visible: true, editable: true, id: 'G', icon: require('./assets/channelG.svg')},
             {name: 'Blue', visible: true, editable: true, id: 'B', icon: require('./assets/channelB.svg')},
@@ -92,16 +134,17 @@ export class TextureEditorComponent extends React.Component<TextureEditorCompone
     componentDidMount() {
         this._textureCanvasManager = new TextureCanvasManager(
             this.props.texture,
-            this.canvasUI.current!,
-            this.canvas2D.current!,
-            this.canvasDisplay.current!,
-            (data : PixelData) => {this.setState({pixelData: data})}
+            this.props.window.current!.getWindow()!,
+            this._UICanvas.current!,
+            this._2DCanvas.current!,
+            this._3DCanvas.current!,
+            (data : IPixelData) => {this.setState({pixelData: data})}
         );
         this.addTools(defaultTools);
     }
 
     componentDidUpdate() {
-        let channelsClone : Channel[] = [];
+        let channelsClone : IChannel[] = [];
         this.state.channels.forEach(channel => channelsClone.push({...channel}));
         this._textureCanvasManager.channels = channelsClone;
         this._textureCanvasManager.metadata = {...this.state.metadata};
@@ -118,12 +161,13 @@ export class TextureEditorComponent extends React.Component<TextureEditorCompone
         });
     }
     
-    addTools(tools : ToolData[]) {
-        let newTools : Tool[] = [];
+    addTools(tools : IToolData[]) {
+        let newTools : ITool[] = [];
         tools.forEach(toolData => {
-            const tool : Tool = {
+            const tool : ITool = {
                 ...toolData,
-                instance: new toolData.type(() => this.getToolParameters())};
+                instance: new toolData.type(() => this.getToolParameters())
+            };
             newTools = newTools.concat(tool);
         });
         newTools = this.state.tools.concat(newTools);
@@ -131,14 +175,18 @@ export class TextureEditorComponent extends React.Component<TextureEditorCompone
         console.log(newTools);
     }
 
-    getToolParameters() : ToolParameters {
+    getToolParameters() : IToolParameters {
         return {
             scene: this._textureCanvasManager.scene,
             canvas2D: this._textureCanvasManager.canvas2D,
+            scene3D: this._textureCanvasManager.scene3D,
             size: this._textureCanvasManager.size,
             updateTexture: () => this._textureCanvasManager.updateTexture(),
-            getMetadata: () => this.state.metadata,
-            setMetadata: (data : any) => this.setMetadata(data)
+            metadata: this.state.metadata,
+            setMetadata: (data : any) => this.setMetadata(data),
+            getMouseCoordinates: (pointerInfo : PointerInfo) => this._textureCanvasManager.getMouseCoordinates(pointerInfo),
+            GUI: this._textureCanvasManager.GUI,
+            BABYLON: BABYLON,
         };
     }
 
@@ -164,7 +212,7 @@ export class TextureEditorComponent extends React.Component<TextureEditorCompone
     }
 
     saveTexture() {
-        Tools.ToBlob(this.canvas2D.current!, (blob) => {
+        Tools.ToBlob(this._2DCanvas.current!, (blob) => {
             Tools.Download(blob!, this.props.url);
         });
     }
@@ -193,16 +241,16 @@ export class TextureEditorComponent extends React.Component<TextureEditorCompone
                 resizeTexture={this.resizeTexture}
                 uploadTexture={this.uploadTexture}
             />
-            <ToolBar
+            {!this.props.texture.isCube && <ToolBar
                 tools={this.state.tools}
                 activeToolIndex={this.state.activeToolIndex}
                 addTool={this.loadToolFromURL}
                 changeTool={this.changeTool}
                 metadata={this.state.metadata}
                 setMetadata={this.setMetadata}
-            />
+            />}
             <ChannelsBar channels={this.state.channels} setChannels={(channels) => {this.setState({channels})}}/>
-            <TextureCanvasComponent canvas2D={this.canvas2D} canvasDisplay={this.canvasDisplay} canvasUI={this.canvasUI} texture={this.props.texture}/>
+            <TextureCanvasComponent canvas2D={this._2DCanvas} canvas3D={this._3DCanvas} canvasUI={this._UICanvas} texture={this.props.texture}/>
             <BottomBar name={this.props.url}/>
         </div>
     }

+ 100 - 101
inspector/src/components/actionTabs/tabs/propertyGrids/materials/textures/toolBar.tsx

@@ -1,102 +1,101 @@
-import * as React from 'react';
-import { SketchPicker } from 'react-color';
-
-export interface Tool {
-    type: any,
-    name: string,
-    instance: any,
-    icon: string
-}
-
-interface ToolBarProps {
-    tools: Tool[];
-    addTool(url: string): void;
-    changeTool(toolIndex : number): void;
-    activeToolIndex : number;
-    metadata: any;
-    setMetadata(data : any): void;
-}
-
-interface ToolBarState {
-    toolURL : string;
-    pickerOpen : boolean;
-    addOpen : boolean;
-}
-
-const addTool = require('./assets/addTool.svg');
-
-export class ToolBar extends React.Component<ToolBarProps, ToolBarState> {
-    private pickerRef : React.RefObject<HTMLDivElement>;
-    constructor(props : ToolBarProps) {
-        super(props);
-        this.state = {
-            toolURL: "",
-            pickerOpen: false,
-            addOpen: false
-        };
-        this.pickerRef = React.createRef();
-    }
-
-    computeRGBAColor() {
-        const opacityInt = Math.floor(this.props.metadata.opacity * 255);
-        const opacityHex = opacityInt.toString(16).padStart(2, '0');
-        return `${this.props.metadata.color}${opacityHex}`;
-    }
-
-    render() {
-        return <div id='toolbar'>
-            <div id='tools'>
-                {this.props.tools.map(
-                    (item, index) => {
-                        return <img
-                            src={`data:image/svg+xml;base64,${item.icon}`}
-                            className={index === this.props.activeToolIndex ? 'icon button active' : 'icon button'}
-                            alt={item.name}
-                            title={item.name}
-                            onClick={evt => {
-                                if (evt.button === 0) {
-                                    this.props.changeTool(index)
-                                }
-                            }}
-                            key={index}
-                        />
-                    }
-                )}
-                <div id='add-tool'>
-                    <img src={addTool} className='icon button' title='Add Tool' alt='Add Tool' onClick={() => this.setState({addOpen: !this.state.addOpen})}/>
-                    { this.state.addOpen && 
-                    <div id='add-tool-popup'>
-                        <form onSubmit={event => {
-                            event.preventDefault();
-                            this.props.addTool(this.state.toolURL);
-                            this.setState({toolURL: '', addOpen: false})
-                        }}>
-                            <label>
-                                Enter tool URL: <input value={this.state.toolURL} onChange={evt => this.setState({toolURL: evt.target.value})} type='text'/>
-                            </label>
-                            <button>Add</button>
-                        </form>
-                    </div> }
-                </div>
-            </div>
-            <div id='color' onClick={() => this.setState({pickerOpen: !this.state.pickerOpen})} title='Color' className={`icon button${this.state.pickerOpen ? ` active` : ``}`}>
-                <div id='activeColor' style={{backgroundColor: this.props.metadata.color}}></div>
-            </div>
-            {
-                this.state.pickerOpen &&
-                <>
-                    <div className='color-picker-cover' onClick={evt => {
-                        if (evt.target !== this.pickerRef.current?.ownerDocument.querySelector('.color-picker-cover')) {
-                            return;
-                        }
-                        this.setState({pickerOpen: false});
-                    }}>
-                    </div>
-                    <div className='color-picker' ref={this.pickerRef}>
-                            <SketchPicker color={this.computeRGBAColor()}  onChange={color => this.props.setMetadata({color: color.hex, opacity: color.rgb.a})}/>
-                    </div>
-                </>
-            }
-        </div>;
-    }
+import * as React from 'react';
+import { SketchPicker } from 'react-color';
+import { IToolData, IToolType } from './textureEditorComponent';
+
+export interface ITool extends IToolData {
+    instance: IToolType;
+}
+
+interface IToolBarProps {
+    tools: ITool[];
+    addTool(url: string): void;
+    changeTool(toolIndex : number): void;
+    activeToolIndex : number;
+    metadata: any;
+    setMetadata(data : any): void;
+}
+
+interface IToolBarState {
+    toolURL : string;
+    pickerOpen : boolean;
+    addOpen : boolean;
+}
+
+
+export class ToolBar extends React.Component<IToolBarProps, IToolBarState> {
+    private _addTool = require('./assets/addTool.svg');
+
+    private _pickerRef : React.RefObject<HTMLDivElement>;
+    constructor(props : IToolBarProps) {
+        super(props);
+        this.state = {
+            toolURL: "",
+            pickerOpen: false,
+            addOpen: false
+        };
+        this._pickerRef = React.createRef();
+    }
+
+    computeRGBAColor() {
+        const opacityInt = Math.floor(this.props.metadata.opacity * 255);
+        const opacityHex = opacityInt.toString(16).padStart(2, '0');
+        return `${this.props.metadata.color}${opacityHex}`;
+    }
+
+    render() {
+        return <div id='toolbar'>
+            <div id='tools'>
+                {this.props.tools.map(
+                    (item, index) => {
+                        return <img
+                            src={`data:image/svg+xml;base64,${item.icon}`}
+                            className={index === this.props.activeToolIndex ? 'icon button active' : 'icon button'}
+                            alt={item.name}
+                            title={item.name}
+                            onClick={evt => {
+                                if (evt.button === 0) {
+                                    this.props.changeTool(index)
+                                }
+                            }}
+                            key={index}
+                        />
+                    }
+                )}
+                <div id='add-tool'>
+                    <img src={this._addTool} className='icon button' title='Add Tool' alt='Add Tool' onClick={() => this.setState({addOpen: !this.state.addOpen})}/>
+                    { this.state.addOpen && 
+                    <div id='add-tool-popup'>
+                        <form onSubmit={event => {
+                            event.preventDefault();
+                            this.props.addTool(this.state.toolURL);
+                            this.setState({toolURL: '', addOpen: false})
+                        }}>
+                            <label>
+                                Enter tool URL: <input value={this.state.toolURL} onChange={evt => this.setState({toolURL: evt.target.value})} type='text'/>
+                            </label>
+                            <button>Add</button>
+                        </form>
+                    </div> }
+                </div>
+            </div>
+            <div id='color' onClick={() => this.setState({pickerOpen: !this.state.pickerOpen})} title='Color' className={`icon button${this.state.pickerOpen ? ` active` : ``}`}>
+                <div id='activeColor' style={{backgroundColor: this.props.metadata.color}}></div>
+            </div>
+            {
+                this.state.pickerOpen &&
+                <>
+                    <div className='color-picker-cover' onClick={evt => {
+                        if (evt.target !== this._pickerRef.current?.ownerDocument.querySelector('.color-picker-cover')) {
+                            return;
+                        }
+                        this.setState({pickerOpen: false});
+                    }}>
+                    </div>
+                    <div className='color-picker' ref={this._pickerRef}>
+                            <SketchPicker color={this.computeRGBAColor()}  onChange={color => this.props.setMetadata({color: color.hex, opacity: color.rgb.a})}/>
+                    </div>
+                </>
+            }
+        </div>;
+    }
 }

+ 193 - 124
inspector/src/components/actionTabs/tabs/propertyGrids/meshes/skeletonPropertyGridComponent.tsx

@@ -1,125 +1,194 @@
-import * as React from "react";
-
-import { Observable } from "babylonjs/Misc/observable";
-
-import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
-import { LineContainerComponent } from "../../../lineContainerComponent";
-import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
-import { TextLineComponent } from "../../../lines/textLineComponent";
-import { LockObject } from "../lockObject";
-import { GlobalState } from '../../../../globalState';
-import { Skeleton } from 'babylonjs/Bones/skeleton';
-import { AnimationGridComponent } from '../animations/animationPropertyGridComponent';
-import { SkeletonViewer } from 'babylonjs/Debug/skeletonViewer';
-import { CustomPropertyGridComponent } from '../customPropertyGridComponent';
-
-interface ISkeletonPropertyGridComponentProps {
-    globalState: GlobalState;
-    skeleton: Skeleton,
-    lockObject: LockObject,
-    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
-}
-
-export class SkeletonPropertyGridComponent extends React.Component<ISkeletonPropertyGridComponentProps> {
-    private _skeletonViewersEnabled = false;
-    private _skeletonViewers = new Array<SkeletonViewer>();
-
-    constructor(props: ISkeletonPropertyGridComponentProps) {
-        super(props);
-        
-        this.checkSkeletonViewerState(this.props);
-    }
-
-    switchSkeletonViewers() {
-        this._skeletonViewersEnabled = !this._skeletonViewersEnabled;
-        const scene = this.props.skeleton.getScene();
-
-        if (this._skeletonViewersEnabled) {
-            for (var mesh of scene.meshes) {
-                if (mesh.skeleton === this.props.skeleton) {
-                    var found = false;
-                    for (var sIndex = 0; sIndex < this._skeletonViewers.length; sIndex++) {
-                        if (this._skeletonViewers[sIndex].skeleton === mesh.skeleton) {
-                            found = true;
-                            break;
-                        }
-                    }
-                    if (found) {
-                        continue;
-                    }
-                    var viewer = new SkeletonViewer(mesh.skeleton, mesh, scene, false, 3, { displayMode:SkeletonViewer.DISPLAY_SPHERE_AND_SPURS });
-                    viewer.isEnabled = true;
-                    this._skeletonViewers.push(viewer);
-                    if (!mesh.reservedDataStore) {
-                        mesh.reservedDataStore = {};
-                    }
-                    mesh.reservedDataStore.skeletonViewer = viewer;
-                }
-            }
-        } else {
-            for (var index = 0; index < this._skeletonViewers.length; index++) {
-                this._skeletonViewers[index].mesh.reservedDataStore.skeletonViewer = null;
-                this._skeletonViewers[index].dispose();
-            }
-            this._skeletonViewers = [];
-
-        }
-    }
-
-    checkSkeletonViewerState(props: ISkeletonPropertyGridComponentProps) {
-        const scene = props.skeleton.getScene();
-        this._skeletonViewers = [];
-
-        if (!scene) {
-            return;
-        }
-
-        for (var mesh of scene.meshes) {
-            if (mesh.skeleton === props.skeleton && mesh.reservedDataStore && mesh.reservedDataStore.skeletonViewer) {
-                this._skeletonViewers.push(mesh.reservedDataStore.skeletonViewer);
-            }
-        }
-
-        this._skeletonViewersEnabled = (this._skeletonViewers.length > 0);
-    }
-
-    shouldComponentUpdate(nextProps: ISkeletonPropertyGridComponentProps) {
-        if (nextProps.skeleton !== this.props.skeleton) {
-            this.checkSkeletonViewerState(nextProps);
-        }
-
-        return true;
-    }
-
-    onOverrideMeshLink() {
-        if (!this.props.globalState.onSelectionChangedObservable) {
-            return;
-        }
-
-        const skeleton = this.props.skeleton;
-        this.props.globalState.onSelectionChangedObservable.notifyObservers(skeleton.overrideMesh);
-    }        
-
-    render() {
-        const skeleton = this.props.skeleton;
-
-        return (
-            <div className="pane">
-                <CustomPropertyGridComponent globalState={this.props.globalState} target={skeleton}
-                    lockObject={this.props.lockObject}
-                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />                    
-                <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
-                    <TextLineComponent label="ID" value={skeleton.id} />
-                    <TextLineComponent label="Bone count" value={skeleton.bones.length.toString()} />
-                    {
-                        skeleton.overrideMesh &&
-                        <TextLineComponent label="Override mesh" value={skeleton.overrideMesh.name} onLink={() => this.onOverrideMeshLink()}/>
-                    }                        
-                    <CheckBoxLineComponent label="Use texture to store matrices" target={skeleton} propertyName="useTextureToStoreBoneMatrices" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <CheckBoxLineComponent label="Debug mode" isSelected={() => this._skeletonViewersEnabled} onSelect={() => this.switchSkeletonViewers()} />
-                </LineContainerComponent>
-                <AnimationGridComponent globalState={this.props.globalState} animatable={skeleton} scene={skeleton.getScene()} lockObject={this.props.lockObject} />
-            </div>
-        );
-    }
+import * as React from "react";
+
+import { Observable } from "babylonjs/Misc/observable";
+
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { TextLineComponent } from "../../../lines/textLineComponent";
+import { LockObject } from "../lockObject";
+import { GlobalState } from '../../../../globalState';
+import { Skeleton } from 'babylonjs/Bones/skeleton';
+import { AnimationGridComponent } from '../animations/animationPropertyGridComponent';
+import { SkeletonViewer } from 'babylonjs/Debug/skeletonViewer';
+import { CustomPropertyGridComponent } from '../customPropertyGridComponent';
+import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+
+
+interface ISkeletonPropertyGridComponentProps {
+    globalState: GlobalState;
+    skeleton: Skeleton,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class SkeletonPropertyGridComponent extends React.Component<ISkeletonPropertyGridComponentProps> {
+    private _skeletonViewersEnabled = false;
+
+    private _skeletonViewerDisplayOptions = { 
+        displayMode : SkeletonViewer.DISPLAY_LINES,
+        sphereBaseSize : 0.15,
+        sphereScaleUnit : 2,
+        sphereFactor : 0.865,
+        midStep : 0.235,
+        midStepFactor : 0.155
+
+    }
+
+    private _skeletonViewers = new Array<SkeletonViewer>();
+
+    constructor(props: ISkeletonPropertyGridComponentProps) {
+        super(props);
+        
+        this.checkSkeletonViewerState(this.props);
+    }
+
+    switchSkeletonViewers() {
+        this._skeletonViewersEnabled = !this._skeletonViewersEnabled;
+        const scene = this.props.skeleton.getScene();
+
+        if (this._skeletonViewersEnabled) {
+            for (var mesh of scene.meshes) {
+                if (mesh.skeleton === this.props.skeleton) {
+                    var found = false;
+                    for (var sIndex = 0; sIndex < this._skeletonViewers.length; sIndex++) {
+                        if (this._skeletonViewers[sIndex].skeleton === mesh.skeleton) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (found) {
+                        continue;
+                    }
+
+                    var viewer = new SkeletonViewer(mesh.skeleton, mesh, scene, false, 3, { 
+                        displayMode: this._skeletonViewerDisplayOptions.displayMode,
+                        displayOptions : {
+                            sphereBaseSize : this._skeletonViewerDisplayOptions.sphereBaseSize,
+                            sphereScaleUnit : this._skeletonViewerDisplayOptions.sphereScaleUnit,
+                            sphereFactor : this._skeletonViewerDisplayOptions.sphereFactor,
+                            midStep : this._skeletonViewerDisplayOptions.midStep,
+                            midStepFactor : this._skeletonViewerDisplayOptions.midStepFactor
+                        }
+                    });
+
+                    viewer.isEnabled = true;
+                    this._skeletonViewers.push(viewer);
+                    if (!mesh.reservedDataStore) {
+                        mesh.reservedDataStore = {};
+                    }
+                    mesh.reservedDataStore.skeletonViewer = viewer;                   
+                }
+            }
+        } else {
+            for (var index = 0; index < this._skeletonViewers.length; index++) {
+                this._skeletonViewers[index].mesh.reservedDataStore.skeletonViewer = null;
+                this._skeletonViewers[index].dispose();
+            }
+            this._skeletonViewers = [];
+
+        }
+    }
+
+    checkSkeletonViewerState(props: ISkeletonPropertyGridComponentProps) {
+        const scene = props.skeleton.getScene();
+        this._skeletonViewers = [];
+
+        if (!scene) {
+            return;
+        }
+
+        for (var mesh of scene.meshes) {
+            if (mesh.skeleton === props.skeleton && mesh.reservedDataStore && mesh.reservedDataStore.skeletonViewer) {
+                this._skeletonViewers.push(mesh.reservedDataStore.skeletonViewer);
+            }
+        }
+
+        this._skeletonViewersEnabled = (this._skeletonViewers.length > 0);
+    }
+
+    changeDisplayMode(){
+        if (this._skeletonViewersEnabled){              
+            for (var index = 0; index < this._skeletonViewers.length; index++) {
+                this._skeletonViewers[index].changeDisplayMode( this._skeletonViewerDisplayOptions.displayMode || 0 );
+            }                   
+        }
+    }
+
+    changeDisplayOptions(option: string, value: number){
+        if (this._skeletonViewersEnabled){              
+            for (var index = 0; index < this._skeletonViewers.length; index++) {
+                this._skeletonViewers[index].changeDisplayOptions( option, value );
+            } 
+            if((this._skeletonViewerDisplayOptions as any)[option] !== undefined ){
+                (this._skeletonViewerDisplayOptions as any)[option] = value;
+            }            
+        }
+    }
+
+    shouldComponentUpdate(nextProps: ISkeletonPropertyGridComponentProps) {
+        if (nextProps.skeleton !== this.props.skeleton) {
+            this.checkSkeletonViewerState(nextProps);
+        }
+
+        return true;
+    }
+
+    onOverrideMeshLink() {
+        if (!this.props.globalState.onSelectionChangedObservable) {
+            return;
+        }
+
+        const skeleton = this.props.skeleton;
+        this.props.globalState.onSelectionChangedObservable.notifyObservers(skeleton.overrideMesh);
+    }        
+
+    render() {
+        const skeleton = this.props.skeleton;
+
+        const debugModeOptions = [
+            { label: "Lines", value: SkeletonViewer.DISPLAY_LINES },
+            { label: "Spheres", value: SkeletonViewer.DISPLAY_SPHERES },
+            { label: "Sphere and Spurs", value: SkeletonViewer.DISPLAY_SPHERE_AND_SPURS }
+        ];
+
+        let displayOptions;
+        if(this._skeletonViewerDisplayOptions.displayMode > SkeletonViewer.DISPLAY_LINES){
+            displayOptions = 
+            (<LineContainerComponent globalState={this.props.globalState} title="DISPLAY OPTIONS">
+                <FloatLineComponent label="sphereBaseSize" target={this._skeletonViewerDisplayOptions} propertyName='sphereBaseSize' onPropertyChangedObservable={this.props.onPropertyChangedObservable} onChange={(value)=>{this.changeDisplayOptions('sphereBaseSize', value)}}/>
+                <FloatLineComponent label="sphereScaleUnit" target={this._skeletonViewerDisplayOptions} propertyName='sphereScaleUnit' onPropertyChangedObservable={this.props.onPropertyChangedObservable} onChange={(value)=>{this.changeDisplayOptions('sphereScaleUnit', value)}}/>
+                <FloatLineComponent label="sphereFactor" target={this._skeletonViewerDisplayOptions} propertyName='sphereFactor' onPropertyChangedObservable={this.props.onPropertyChangedObservable} onChange={(value)=>{this.changeDisplayOptions('sphereFactor', value)}}/>
+                <FloatLineComponent label="midStep" target={this._skeletonViewerDisplayOptions} propertyName='midStep' onPropertyChangedObservable={this.props.onPropertyChangedObservable} onChange={(value)=>{this.changeDisplayOptions('midStep', value)}}/>
+                <FloatLineComponent label="midStepFactor" target={this._skeletonViewerDisplayOptions} propertyName='midStepFactor' onPropertyChangedObservable={this.props.onPropertyChangedObservable} onChange={(value)=>{this.changeDisplayOptions('midStepFactor', value)}}/>
+            </LineContainerComponent>)
+        }else{
+             displayOptions = (null)
+        }
+
+        return (
+            <div className="pane">
+                <CustomPropertyGridComponent globalState={this.props.globalState} target={skeleton}
+                    lockObject={this.props.lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />                    
+                <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
+                    <TextLineComponent label="ID" value={skeleton.id} />
+                    <TextLineComponent label="Bone count" value={skeleton.bones.length.toString()} />
+                    {
+                        skeleton.overrideMesh &&
+                        <TextLineComponent label="Override mesh" value={skeleton.overrideMesh.name} onLink={() => this.onOverrideMeshLink()}/>
+                    }                        
+                    <CheckBoxLineComponent label="Use texture to store matrices" target={skeleton} propertyName="useTextureToStoreBoneMatrices" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    
+                    <LineContainerComponent globalState={this.props.globalState} title="DEBUG">                        
+                        <CheckBoxLineComponent label="Enabled" isSelected={() => this._skeletonViewersEnabled} onSelect={() => this.switchSkeletonViewers()} />
+                        <OptionsLineComponent label="displayMode" options={debugModeOptions} target={this._skeletonViewerDisplayOptions} propertyName="displayMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={() => this.changeDisplayMode()} />
+                        {displayOptions}                   
+                    </LineContainerComponent>                    
+                </LineContainerComponent>
+                <AnimationGridComponent globalState={this.props.globalState} animatable={skeleton} scene={skeleton.getScene()} lockObject={this.props.lockObject} />
+            </div>
+        );
+    }
 }

+ 9 - 5
inspector/src/components/actionTabs/tabs/propertyGrids/particleSystems/particleSystemPropertyGridComponent.tsx

@@ -83,7 +83,7 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
             case "MeshParticleEmitter":
                 return (
                     <MeshEmitterGridComponent  
-                    lockObject={this.props.lockObject} scene={system.getScene()} globalState={this.props.globalState} emitter={system.particleEmitterType as MeshParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
+                    lockObject={this.props.lockObject} scene={system.getScene()!} globalState={this.props.globalState} emitter={system.particleEmitterType as MeshParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
                 );                 
             case "PointParticleEmitter":
                 return (
@@ -165,6 +165,10 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
         const system = this.props.system;
         const scene = system.getScene();
 
+        if (!scene) {
+            return;
+        }
+
         Tools.ReadFile(file, (data) => {
             let decoder = new TextDecoder("utf-8");
             let jsonObject = JSON.parse(decoder.decode(data));
@@ -173,19 +177,19 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
             system.dispose();            
             this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
 
-            let newSystem = isGpu ? GPUParticleSystem.Parse(jsonObject, scene, "") : ParticleSystem.Parse(jsonObject, scene, "");
+            let newSystem = isGpu ? GPUParticleSystem.Parse(jsonObject, scene!, "") : ParticleSystem.Parse(jsonObject, scene!, "");
             this.props.globalState.onSelectionChangedObservable.notifyObservers(newSystem);
         }, undefined, true);
     }
 
     loadFromSnippet() {
         const system = this.props.system;
-        const scene = system.getScene();
+        const scene = system.getScene()!;
         let isGpu = system instanceof GPUParticleSystem;
 
         let snippedID = window.prompt("Please enter the snippet ID to use");
 
-        if (!snippedID) {
+        if (!snippedID || !scene) {
             return;
         }
         
@@ -272,7 +276,7 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
         ];
 
 
-        var meshEmitters = this.props.system.getScene().meshes.filter(m => !!m.name);
+        var meshEmitters = this.props.system.getScene()!.meshes.filter(m => !!m.name);
 
         var emitterOptions = [
             { label: "None", value: -1 },

+ 21 - 0
inspector/src/components/globalState.ts

@@ -6,7 +6,9 @@ import { Observable, Observer } from "babylonjs/Misc/observable";
 import { ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs/Loading/sceneLoader";
 import { Scene } from "babylonjs/scene";
 import { Light } from "babylonjs/Lights/light";
+import { Camera } from "babylonjs/Cameras/camera";
 import { LightGizmo } from "babylonjs/Gizmos/lightGizmo";
+import { CameraGizmo } from "babylonjs/Gizmos/cameraGizmo";
 import { PropertyChangedEvent } from "./propertyChangedEvent";
 import { ReplayRecorder } from './replayRecorder';
 import { DataStorage } from 'babylonjs/Misc/dataStorage';
@@ -130,4 +132,23 @@ export class GlobalState {
             light.reservedDataStore.lightGizmo = null;
         }
     }
+    // Camera gizmos
+    public cameraGizmos: Array<CameraGizmo> = [];
+    public enableCameraGizmo(camera: Camera, enable = true) {
+        if (enable) {
+            if (!camera.reservedDataStore) {
+                camera.reservedDataStore = {}
+            }
+            if (!camera.reservedDataStore.cameraGizmo) {
+                camera.reservedDataStore.cameraGizmo = new CameraGizmo();
+                this.cameraGizmos.push(camera.reservedDataStore.cameraGizmo)
+                camera.reservedDataStore.cameraGizmo.camera = camera;
+                camera.reservedDataStore.cameraGizmo.material.reservedDataStore = {hidden: true};
+            }
+        } else if (camera.reservedDataStore && camera.reservedDataStore.cameraGizmo) {
+            this.cameraGizmos.splice(this.cameraGizmos.indexOf(camera.reservedDataStore.cameraGizmo), 1);
+            camera.reservedDataStore.cameraGizmo.dispose();
+            camera.reservedDataStore.cameraGizmo = null;
+        }
+    }
 }

+ 4 - 0
inspector/src/components/popupComponent.tsx

@@ -70,6 +70,10 @@ export class PopupComponent extends React.Component<IPopupComponentProps, { isCo
         }
     }
 
+    getWindow() {
+        return this._window;
+    }
+
     render() {
         if (!this.state.isComponentMounted || this._container === null) {
             return null;

+ 24 - 5
inspector/src/components/sceneExplorer/entities/cameraTreeItemComponent.tsx

@@ -5,18 +5,20 @@ import { Camera } from "babylonjs/Cameras/camera";
 import { Scene } from "babylonjs/scene";
 
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faVideo, faCamera } from '@fortawesome/free-solid-svg-icons';
+import { faVideo, faCamera, faEye } from '@fortawesome/free-solid-svg-icons';
 import { TreeItemLabelComponent } from "../treeItemLabelComponent";
 import { ExtensionsComponent } from "../extensionsComponent";
 import * as React from "react";
+import { GlobalState } from "../../globalState";
 
 interface ICameraTreeItemComponentProps {
     camera: Camera,
     extensibilityGroups?: IExplorerExtensibilityGroup[],
-    onClick: () => void
+    onClick: () => void,
+    globalState: GlobalState
 }
 
-export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComponentProps, { isActive: boolean }> {
+export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComponentProps, { isActive: boolean, isGizmoEnabled:boolean }> {
     private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
 
     constructor(props: ICameraTreeItemComponentProps) {
@@ -25,7 +27,7 @@ export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComp
         const camera = this.props.camera;
         const scene = camera.getScene();
 
-        this.state = { isActive: scene.activeCamera === camera };
+        this.state = { isActive: scene.activeCamera === camera, isGizmoEnabled: (camera.reservedDataStore && camera.reservedDataStore.cameraGizmo) };
     }
 
     setActive(): void {
@@ -65,10 +67,24 @@ export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComp
         }
     }
 
+    toggleGizmo(): void {
+        const camera = this.props.camera;
+        if(camera.reservedDataStore && camera.reservedDataStore.cameraGizmo){
+            if (camera.getScene().reservedDataStore && camera.getScene().reservedDataStore.gizmoManager) {
+                camera.getScene().reservedDataStore.gizmoManager.attachToMesh(null);
+            }
+            this.props.globalState.enableCameraGizmo(camera, false);
+            this.setState({ isGizmoEnabled: false });
+        }else{
+            this.props.globalState.enableCameraGizmo(camera, true);
+            this.setState({ isGizmoEnabled: true });
+        }
+    }
+
     render() {
         const isActiveElement = this.state.isActive ? <FontAwesomeIcon icon={faVideo} /> : <FontAwesomeIcon icon={faVideo} className="isNotActive" />;
         const scene = this.props.camera.getScene()!;
-
+        const isGizmoEnabled = (this.state.isGizmoEnabled || (this.props.camera && this.props.camera.reservedDataStore && this.props.camera.reservedDataStore.cameraGizmo)) ? <FontAwesomeIcon icon={faEye} /> : <FontAwesomeIcon icon={faEye} className="isNotActive" />;
         return (
             <div className="cameraTools">
                 <TreeItemLabelComponent label={this.props.camera.name} onClick={() => this.props.onClick()} icon={faCamera} color="green" />
@@ -78,6 +94,9 @@ export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComp
                         {isActiveElement}
                     </div>
                 }
+                <div className="enableGizmo icon" onClick={() => this.toggleGizmo()} title="Turn on/off the camera's gizmo">
+                    {isGizmoEnabled}
+                </div>
                 <ExtensionsComponent target={this.props.camera} extensibilityGroups={this.props.extensibilityGroups} />
             </div>
         )

+ 82 - 23
inspector/src/components/sceneExplorer/entities/sceneTreeItemComponent.tsx

@@ -14,6 +14,7 @@ import { GlobalState } from "../../globalState";
 import { UtilityLayerRenderer } from "babylonjs/Rendering/utilityLayerRenderer";
 import { PropertyChangedEvent } from '../../../components/propertyChangedEvent';
 import { LightGizmo } from 'babylonjs/Gizmos/lightGizmo';
+import { CameraGizmo } from 'babylonjs/Gizmos/cameraGizmo';
 import { TmpVectors, Vector3 } from 'babylonjs/Maths/math';
 
 interface ISceneTreeItemComponentProps {
@@ -89,9 +90,17 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                         this.props.globalState.enableLightGizmo(this._selectedEntity, true);
                         this.forceUpdate();
                     }
-                    manager.attachToMesh(this._selectedEntity.reservedDataStore.lightGizmo.attachedMesh);
+                    manager.attachToNode(this._selectedEntity.reservedDataStore.lightGizmo.attachedNode);
+                } else if (className.indexOf("Camera") !== -1) {
+                    if (!this._selectedEntity.reservedDataStore || !this._selectedEntity.reservedDataStore.cameraGizmo) {
+                        this.props.globalState.enableCameraGizmo(this._selectedEntity, true);
+                        this.forceUpdate();
+                    }
+                    manager.attachToNode(this._selectedEntity.reservedDataStore.cameraGizmo.attachedNode);
+                }else if(className.indexOf("Bone") !== -1){
+                    manager.attachToMesh((this._selectedEntity._linkedTransformNode)?this._selectedEntity._linkedTransformNode:this._selectedEntity);
                 } else {
-                    manager.attachToMesh(null);
+                    manager.attachToNode(null);
                 }
             }
         });
@@ -158,7 +167,23 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                     var gizmoScene = this.props.globalState.lightGizmos[0].gizmoLayer.utilityLayerScene;
                     let pickInfo = gizmoScene.pick(pickPosition.x, pickPosition.y, (m: any) => {
                         for (var g of (this.props.globalState.lightGizmos as any)) {
-                            if (g.attachedMesh == m) {
+                            if (g.attachedNode == m) {
+                                return true;
+                            }
+                        }
+                        return false;
+                    });
+                    if (pickInfo && pickInfo.hit && this.props.onSelectionChangedObservable) {
+                        this.props.onSelectionChangedObservable.notifyObservers(pickInfo.pickedMesh);
+                        return;
+                    }
+                }
+                // Pick camera gizmos
+                if (this.props.globalState.cameraGizmos.length > 0) {
+                    var gizmoScene = this.props.globalState.cameraGizmos[0].gizmoLayer.utilityLayerScene;
+                    let pickInfo = gizmoScene.pick(pickPosition.x, pickPosition.y, (m: any) => {
+                        for (var g of (this.props.globalState.cameraGizmos as any)) {
+                            if (g.attachedNode == m) {
                                 return true;
                             }
                         }
@@ -207,7 +232,7 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                     }
                     for (var gizmo of this.props.globalState.lightGizmos) {
                         if (gizmo._rootMesh == node) {
-                            manager.attachToMesh(gizmo.attachedMesh);
+                            manager.attachToNode(gizmo.attachedNode);
                         }
                     }
                 }
@@ -230,16 +255,28 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                     if (!this._posDragEnd) {
                         // Record movement for generating replay code
                         this._posDragEnd = manager.gizmos.positionGizmo!.onDragEndObservable.add(() => {
-                            if (manager.gizmos.positionGizmo && manager.gizmos.positionGizmo.attachedMesh) {
-                                var lightGizmo: Nullable<LightGizmo> = manager.gizmos.positionGizmo.attachedMesh.reservedDataStore ? manager.gizmos.positionGizmo.attachedMesh.reservedDataStore.lightGizmo : null;
-                                var obj: any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.positionGizmo.attachedMesh;
+                            if (manager.gizmos.positionGizmo && manager.gizmos.positionGizmo.attachedNode) {
+                                var lightGizmo: Nullable<LightGizmo> = manager.gizmos.positionGizmo.attachedNode.reservedDataStore ? manager.gizmos.positionGizmo.attachedNode.reservedDataStore.lightGizmo : null;
+                                var objLight: any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.positionGizmo.attachedNode;
 
-                                if (obj.position) {
+                                if (objLight.position) {
                                     var e = new PropertyChangedEvent();
-                                    e.object = obj
+                                    e.object = objLight
                                     e.property = "position"
-                                    e.value = obj.position;
+                                    e.value = objLight.position;
                                     this.props.globalState.onPropertyChangedObservable.notifyObservers(e)
+                                } else {
+                                    var cameraGizmo: Nullable<CameraGizmo> = manager.gizmos.positionGizmo.attachedNode.reservedDataStore ? manager.gizmos.positionGizmo.attachedNode.reservedDataStore.cameraGizmo : null;
+                                    var objCamera: any = (cameraGizmo && cameraGizmo.camera) ? cameraGizmo.camera : manager.gizmos.positionGizmo.attachedNode;
+    
+                                    if (objCamera.position) {
+                                        var e = new PropertyChangedEvent();
+                                        e.object = objCamera
+                                        e.property = "position"
+                                        e.value = objCamera.position;
+                                        this.props.globalState.onPropertyChangedObservable.notifyObservers(e)
+                                    }
+
                                 }
                             }
                         })
@@ -251,27 +288,41 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                     if (!this._rotateDragEnd) {
                         // Record movement for generating replay code
                         this._rotateDragEnd = manager.gizmos.rotationGizmo!.onDragEndObservable.add(() => {
-                            if (manager.gizmos.rotationGizmo && manager.gizmos.rotationGizmo.attachedMesh) {
-                                var lightGizmo: Nullable<LightGizmo> = manager.gizmos.rotationGizmo.attachedMesh.reservedDataStore ? manager.gizmos.rotationGizmo.attachedMesh.reservedDataStore.lightGizmo : null;
-                                var obj: any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.rotationGizmo.attachedMesh;
+                            if (manager.gizmos.rotationGizmo && manager.gizmos.rotationGizmo.attachedNode) {
+                                var lightGizmo: Nullable<LightGizmo> = manager.gizmos.rotationGizmo.attachedNode.reservedDataStore ? manager.gizmos.rotationGizmo.attachedNode.reservedDataStore.lightGizmo : null;
+                                var objLight: any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.rotationGizmo.attachedNode;
+                                var cameraGizmo: Nullable<CameraGizmo> = manager.gizmos.rotationGizmo.attachedNode.reservedDataStore ? manager.gizmos.rotationGizmo.attachedNode.reservedDataStore.cameraGizmo : null;
+                                var objCamera: any = (cameraGizmo && cameraGizmo.camera) ? cameraGizmo.camera : manager.gizmos.rotationGizmo.attachedNode;
 
-                                if (obj.rotationQuaternion) {
+                                if (objLight.rotationQuaternion) {
                                     var e = new PropertyChangedEvent();
-                                    e.object = obj;
+                                    e.object = objLight;
                                     e.property = "rotationQuaternion";
-                                    e.value = obj.rotationQuaternion;
+                                    e.value = objLight.rotationQuaternion;
                                     this.props.globalState.onPropertyChangedObservable.notifyObservers(e);
-                                } else if (obj.rotation) {
+                                } else if (objLight.rotation) {
                                     var e = new PropertyChangedEvent();
-                                    e.object = obj;
+                                    e.object = objLight;
                                     e.property = "rotation";
-                                    e.value = obj.rotation;
+                                    e.value = objLight.rotation;
                                     this.props.globalState.onPropertyChangedObservable.notifyObservers(e);
-                                } else if (obj.direction) {
+                                } else if (objLight.direction) {
                                     var e = new PropertyChangedEvent();
-                                    e.object = obj;
+                                    e.object = objLight;
                                     e.property = "direction";
-                                    e.value = obj.direction;
+                                    e.value = objLight.direction;
+                                    this.props.globalState.onPropertyChangedObservable.notifyObservers(e);
+                                } else if (objCamera.rotationQuaternion) {
+                                    var e = new PropertyChangedEvent();
+                                    e.object = objCamera;
+                                    e.property = "rotationQuaternion";
+                                    e.value = objCamera.rotationQuaternion;
+                                    this.props.globalState.onPropertyChangedObservable.notifyObservers(e);
+                                } else if (objCamera.rotation) {
+                                    var e = new PropertyChangedEvent();
+                                    e.object = objCamera;
+                                    e.property = "rotation";
+                                    e.value = objCamera.rotation;
                                     this.props.globalState.onPropertyChangedObservable.notifyObservers(e);
                                 }
                             }
@@ -318,7 +369,15 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                         this.props.globalState.enableLightGizmo(this._selectedEntity, true);
                         this.forceUpdate();
                     }
-                    manager.attachToMesh(this._selectedEntity.reservedDataStore.lightGizmo.attachedMesh);
+                    manager.attachToNode(this._selectedEntity.reservedDataStore.lightGizmo.attachedNode);
+                } else if (className.indexOf("Camera") !== -1) {
+                    if (!this._selectedEntity.reservedDataStore || !this._selectedEntity.reservedDataStore.cameraGizmo) {
+                        this.props.globalState.enableCameraGizmo(this._selectedEntity, true);
+                        this.forceUpdate();
+                    }
+                    manager.attachToNode(this._selectedEntity.reservedDataStore.cameraGizmo.attachedNode);
+                } else if(className.indexOf("Bone") !== -1){
+                    manager.attachToMesh((this._selectedEntity._linkedTransformNode)?this._selectedEntity._linkedTransformNode:this._selectedEntity);
                 }
             }
         }

+ 6 - 2
inspector/src/components/sceneExplorer/sceneExplorer.scss

@@ -402,16 +402,20 @@
         .cameraTools {
             grid-column: 2;
             display: grid;
-            grid-template-columns: 1fr 20px auto 5px;
+            grid-template-columns: 1fr 20px 20px auto 5px;
             align-items: center;
 
             .activeCamera {
                 grid-column: 2;
             }
 
+            .enableGizmo {
+                grid-column: 3;          
+            } 
+
             .extensions {
                 width: 20px;
-                grid-column: 3;
+                grid-column: 4;
             }
         }
 

+ 6 - 4
inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx

@@ -215,6 +215,8 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             }
             keyEvent.preventDefault();
             return;
+        } else if (keyEvent.keyCode === 46) { // delete
+            this.state.selectedEntity.dispose();
         }
 
         if (!search) {
@@ -272,7 +274,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                 pipelineContextMenus.push({
                     label: "Add new Default Rendering Pipeline",
                     action: () => {
-                        let newPipeline = new DefaultRenderingPipeline("Default rendering pipeline", true, scene, [scene.activeCamera!]);
+                        let newPipeline = new DefaultRenderingPipeline("Default rendering pipeline", true, scene, scene.cameras);
                         this.props.globalState.onSelectionChangedObservable.notifyObservers(newPipeline);
                     }
                 });
@@ -282,7 +284,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                 pipelineContextMenus.push({
                     label: "Add new SSAO Rendering Pipeline",
                     action: () => {
-                        let newPipeline = new SSAORenderingPipeline("SSAO rendering pipeline", scene, 1, [scene.activeCamera!]);
+                        let newPipeline = new SSAORenderingPipeline("SSAO rendering pipeline", scene, 1, scene.cameras);
                         this.props.globalState.onSelectionChangedObservable.notifyObservers(newPipeline);
                     }
                 });
@@ -292,7 +294,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                 pipelineContextMenus.push({
                     label: "Add new SSAO2 Rendering Pipeline",
                     action: () => {
-                        let newPipeline = new SSAO2RenderingPipeline("SSAO2 rendering pipeline", scene, 1, [scene.activeCamera!]);
+                        let newPipeline = new SSAO2RenderingPipeline("SSAO2 rendering pipeline", scene, 1, scene.cameras);
                         this.props.globalState.onSelectionChangedObservable.notifyObservers(newPipeline);
                     }
                 });
@@ -451,7 +453,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
     render() {
         if (this.props.popupMode) {
             return (
-                <div id="sceneExplorer">
+                <div id="sceneExplorer" tabIndex={0} onKeyDown={(keyEvent) => this.processKeys(keyEvent)}>
                     {
                         !this.props.noHeader &&
                         <HeaderComponent title="SCENE EXPLORER" noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} />

+ 1 - 1
inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx

@@ -96,7 +96,7 @@ export class TreeItemSpecializedComponent extends React.Component<ITreeItemSpeci
             }
 
             if (className.indexOf("Camera") !== -1) {
-                return (<CameraTreeItemComponent extensibilityGroups={this.props.extensibilityGroups} camera={entity as Camera} onClick={() => this.onClick()} />);
+                return (<CameraTreeItemComponent globalState={this.props.globalState} extensibilityGroups={this.props.extensibilityGroups} camera={entity as Camera} onClick={() => this.onClick()} />);
             }
 
             if (className.indexOf("Light", className.length - 5) !== -1) {

+ 5 - 0
inspector/src/inspector.ts

@@ -492,6 +492,11 @@ export class Inspector {
                 this._GlobalState.enableLightGizmo(g.light, false);
             }
         });
+        this._GlobalState.cameraGizmos.forEach((g) => {
+            if (g.camera) {
+                this._GlobalState.enableCameraGizmo(g.camera, false);
+            }
+        });
         if (this._Scene && this._Scene.reservedDataStore && this._Scene.reservedDataStore.gizmoManager) {
             this._Scene.reservedDataStore.gizmoManager.dispose();
             this._Scene.reservedDataStore.gizmoManager = null;

+ 1 - 1
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -194,7 +194,7 @@ export class MSFT_lod implements IGLTFLoaderExtension {
     }
 
     /** @hidden */
-    public _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>> {
+    public _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>> {
         // Don't load material LODs if already loading a node LOD.
         if (this._nodeIndexLOD) {
             return null;

+ 23 - 9
loaders/src/glTF/2.0/glTFLoader.ts

@@ -309,6 +309,16 @@ export class GLTFLoader implements IGLTFLoader {
                 promises.push(this.loadSceneAsync(`/scenes/${scene.index}`, scene));
             }
 
+            if (this.parent.loadAllMaterials && this._gltf.materials) {
+                for (let m = 0; m < this._gltf.materials.length; ++m) {
+                    const material = this._gltf.materials[m];
+                    const context = "/materials/" + m;
+                    const babylonDrawMode = Material.TriangleFillMode;
+
+                    promises.push(this._loadMaterialAsync(context, material, null, babylonDrawMode, (material) => { }));
+                }
+            }
+
             // Restore the blocking of material dirty.
             this._babylonScene.blockMaterialDirtyMechanism = oldBlockMaterialDirtyMechanism;
 
@@ -1125,6 +1135,8 @@ export class GLTFLoader implements IGLTFLoader {
                 baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
             }
 
+            babylonBone.setBindPose(baseMatrix);
+
             babylonBone.updateMatrix(baseMatrix, false, false);
             babylonBone._updateDifferenceMatrix(undefined, false);
         }
@@ -1692,7 +1704,7 @@ export class GLTFLoader implements IGLTFLoader {
     }
 
     /** @hidden */
-    public _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void = () => { }): Promise<Material> {
+    public _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void = () => { }): Promise<Material> {
         const extensionPromise = this._extensionsLoadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign);
         if (extensionPromise) {
             return extensionPromise;
@@ -1719,14 +1731,16 @@ export class GLTFLoader implements IGLTFLoader {
             this.logClose();
         }
 
-        babylonData.babylonMeshes.push(babylonMesh);
+        if (babylonMesh) {
+            babylonData.babylonMeshes.push(babylonMesh);
 
-        babylonMesh.onDisposeObservable.addOnce(() => {
-            const index = babylonData.babylonMeshes.indexOf(babylonMesh);
-            if (index !== -1) {
-                babylonData.babylonMeshes.splice(index, 1);
-            }
-        });
+            babylonMesh.onDisposeObservable.addOnce(() => {
+                const index = babylonData.babylonMeshes.indexOf(babylonMesh);
+                if (index !== -1) {
+                    babylonData.babylonMeshes.splice(index, 1);
+                }
+            });
+        }
 
         assign(babylonData.babylonMaterial);
 
@@ -2291,7 +2305,7 @@ export class GLTFLoader implements IGLTFLoader {
         return this._applyExtensions(primitive, "loadMeshPrimitive", (extension) => extension._loadMeshPrimitiveAsync && extension._loadMeshPrimitiveAsync(context, name, node, mesh, primitive, assign));
     }
 
-    private _extensionsLoadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>> {
+    private _extensionsLoadMaterialAsync(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>> {
         return this._applyExtensions(material, "loadMaterial", (extension) => extension._loadMaterialAsync && extension._loadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign));
     }
 

+ 1 - 1
loaders/src/glTF/2.0/glTFLoaderExtension.ts

@@ -82,7 +82,7 @@ export interface IGLTFLoaderExtension extends IGLTFBaseLoaderExtension, IDisposa
      * @param assign A function called synchronously after parsing the glTF properties
      * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
      */
-    _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+    _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Nullable<Mesh>, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
 
     /**
      * Define this method to modify the default behavior when creating materials.

+ 5 - 0
loaders/src/glTF/glTFFileLoader.ts

@@ -240,6 +240,11 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
     public alwaysComputeBoundingBox = false;
 
     /**
+     * If true, load all materials defined in the file, even if not used by any mesh. Defaults to false.
+     */
+    public loadAllMaterials = false;
+
+    /**
      * Function called before loading a url referenced by the asset.
      */
     public preprocessUrlAsync = (url: string) => Promise.resolve(url);

+ 1 - 3
nodeEditor/src/components/preview/previewMeshControlComponent.tsx

@@ -158,9 +158,7 @@ export class PreviewMeshControlComponent extends React.Component<IPreviewMeshCon
                         className={"button align"}
                         onClick={(_) => this.changeBackgroundClick()}
                         >
-                        <img src={colorPicker} alt=""/>
-                        <label htmlFor="color-picker" id="color-picker-label">
-                        </label>
+                        <img src={colorPicker} alt="" id="color-picker-image"/>
                         <input ref={this.colorInputRef} id="color-picker" type="color" onChange={(evt) => this.changeBackground(evt.target.value)} />
                     </div>
                 </> }

+ 11 - 1
nodeEditor/src/main.scss

@@ -166,9 +166,19 @@
 
         #color-picker-button {
             grid-column: 4;
+            display: grid;
+            grid-template-columns: 100%;
+            grid-template-rows: 100%;
+            
+            #color-picker-image {                
+                grid-column: 1;
+                grid-row: 1;
+            }
 
             #color-picker {
-                display: none;
+                transform: scale(0);
+                grid-column: 1;
+                grid-row: 1;
             }
 
             #color-picker-label {

+ 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.28",
+    "version": "4.2.0-alpha.31",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 15 - 1
sandbox/src/components/renderingZone.tsx

@@ -61,7 +61,9 @@ export class RenderingZone extends React.Component<IRenderingZoneProps> {
                         this._scene.debugLayer.hide();
                     }
                 }
-            }, null, null);
+            }, null, (file, scene, message) => {
+                this.props.globalState.onError.notifyObservers({ message : message});
+            });
 
         filesInput.onProcessFileCallback = (file, name, extension) => {
             if (filesInput.filesToLoad && filesInput.filesToLoad.length === 1 && extension) {
@@ -78,6 +80,18 @@ export class RenderingZone extends React.Component<IRenderingZoneProps> {
         filesInput.monitorElementForDragNDrop(this._canvas);
 
         this.props.globalState.filesInput = filesInput;
+
+        window.addEventListener("keydown", (event) => {
+            // Press R to reload
+            if (event.keyCode === 82 && event.target && (event.target as HTMLElement).nodeName !== "INPUT" && this._scene) {
+                if (this.props.assetUrl) {
+                    this.loadAssetFromUrl();
+                }
+                else {
+                    filesInput.reload();
+                }
+            }
+        });
     }
 
     prepareCamera() {

+ 18 - 0
src/Bones/bone.ts

@@ -44,6 +44,7 @@ export class Bone extends Node {
     private _skeleton: Skeleton;
     private _localMatrix: Matrix;
     private _restPose: Matrix;
+    private _bindPose: Matrix;
     private _baseMatrix: Matrix;
     private _absoluteTransform = new Matrix();
     private _invertedAbsoluteTransform = new Matrix();
@@ -95,6 +96,7 @@ export class Bone extends Node {
         this._skeleton = skeleton;
         this._localMatrix = localMatrix ? localMatrix.clone() : Matrix.Identity();
         this._restPose = restPose ? restPose : this._localMatrix.clone();
+        this._bindPose = this._localMatrix.clone();
         this._baseMatrix = baseMatrix ? baseMatrix : this._localMatrix.clone();
         this._index = index;
 
@@ -213,6 +215,22 @@ export class Bone extends Node {
     }
 
     /**
+     * Gets the bind pose matrix
+     * @returns the bind pose matrix
+     */
+    public getBindPose(): Matrix {
+        return this._bindPose;
+    }
+
+    /**
+     * Sets the bind pose matrix
+     * @param matrix the local-space bind pose to set for this bone
+     */
+    public setBindPose(matrix: Matrix): void {
+        this._bindPose.copyFrom(matrix);
+    }
+
+    /**
      * Gets a matrix used to store world matrix (ie. the matrix sent to shaders)
      */
     public getWorldMatrix(): Matrix {

+ 2 - 1
src/Cameras/Inputs/freeCameraTouchInput.ts

@@ -54,7 +54,8 @@ export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
             this._pointerInput = (p) => {
                 var evt = <PointerEvent>p.event;
 
-                if (evt.pointerType === "mouse") {
+                let isMouseEvent = evt instanceof MouseEvent;
+                if (evt.pointerType === "mouse" || isMouseEvent) {
                     return;
                 }
 

+ 1 - 0
src/Cameras/arcRotateCamera.ts

@@ -56,6 +56,7 @@ export class ArcRotateCamera extends TargetCamera {
     /**
      * Defines the target point of the camera.
      * The camera looks towards it form the radius distance.
+     * Please note that you can set the target to a mesh and thus the target will be copied from mesh.position
      */
     public get target(): Vector3 {
         return this._target;

+ 7 - 3
src/Cameras/followCamera.ts

@@ -246,6 +246,9 @@ export class ArcFollowCamera extends TargetCamera {
 
     private _cartesianCoordinates: Vector3 = Vector3.Zero();
 
+        /** Define the camera target (the mesh it should follow) */
+    private _meshTarget: Nullable<AbstractMesh>;
+
     /**
      * Instantiates a new ArcFollowCamera
      * @see https://doc.babylonjs.com/features/cameras#follow-camera
@@ -264,21 +267,22 @@ export class ArcFollowCamera extends TargetCamera {
         /** The radius of the camera from its target */
         public radius: number,
         /** Define the camera target (the mesh it should follow) */
-        public target: Nullable<AbstractMesh>,
+        target: Nullable<AbstractMesh>,
         scene: Scene) {
         super(name, Vector3.Zero(), scene);
+        this._meshTarget = target;
         this._follow();
     }
 
     private _follow(): void {
-        if (!this.target) {
+        if (!this._meshTarget) {
             return;
         }
         this._cartesianCoordinates.x = this.radius * Math.cos(this.alpha) * Math.cos(this.beta);
         this._cartesianCoordinates.y = this.radius * Math.sin(this.beta);
         this._cartesianCoordinates.z = this.radius * Math.sin(this.alpha) * Math.cos(this.beta);
 
-        var targetPosition = this.target.getAbsolutePosition();
+        var targetPosition = this._meshTarget.getAbsolutePosition();
         this.position = targetPosition.add(this._cartesianCoordinates);
         this.setTarget(targetPosition);
     }

+ 11 - 0
src/Cameras/targetCamera.ts

@@ -280,6 +280,17 @@ export class TargetCamera extends Camera {
     }
 
     /**
+     * Defines the target point of the camera.
+     * The camera looks towards it form the radius distance.
+     */
+    public get target(): Vector3 {
+        return this.getTarget();
+    }
+    public set target(value: Vector3) {
+        this.setTarget(value);
+    }
+
+    /**
      * Return the current target position of the camera. This value is expressed in local space.
      * @returns the target position
      */

+ 18 - 12
src/Debug/rayHelper.ts

@@ -8,6 +8,7 @@ import { Mesh } from "../Meshes/mesh";
 import { LinesMesh } from "../Meshes/linesMesh";
 
 import "../Meshes/Builders/linesBuilder";
+import { Observer } from '../Misc/observable';
 
 /**
  * As raycast might be hard to debug, the RayHelper can help rendering the different rays
@@ -26,7 +27,8 @@ export class RayHelper {
     private _renderFunction: Nullable<() => void>;
     private _scene: Nullable<Scene>;
 
-    private _updateToMeshFunction: Nullable<() => void>;
+    private _onAfterRenderObserver: Nullable<Observer<Scene>>;
+    private _onAfterStepObserver: Nullable<Observer<Scene>>;
     private _attachedToMesh: Nullable<AbstractMesh>;
     private _meshSpaceDirection: Vector3;
     private _meshSpaceOrigin: Vector3;
@@ -72,6 +74,7 @@ export class RayHelper {
             this._scene = scene;
             this._renderPoints = [ray.origin, ray.origin.add(ray.direction.scale(ray.length))];
             this._renderLine = Mesh.CreateLines("ray", this._renderPoints, scene, true);
+            this._renderLine.isPickable = false;
 
             if (this._renderFunction) {
                 this._scene.registerBeforeRender(this._renderFunction);
@@ -160,6 +163,10 @@ export class RayHelper {
             meshSpaceDirection = new Vector3(0, 0, -1);
         }
 
+        if (!this._scene) {
+            this._scene = mesh.getScene();
+        }
+
         if (!this._meshSpaceDirection) {
             this._meshSpaceDirection = meshSpaceDirection.clone();
             this._meshSpaceOrigin = meshSpaceOrigin.clone();
@@ -168,9 +175,9 @@ export class RayHelper {
             this._meshSpaceOrigin.copyFrom(meshSpaceOrigin);
         }
 
-        if (!this._updateToMeshFunction) {
-            this._updateToMeshFunction = (<() => void>this._updateToMesh.bind(this));
-            this._attachedToMesh.getScene().registerBeforeRender(this._updateToMeshFunction);
+        if (!this._onAfterRenderObserver) {
+            this._onAfterRenderObserver = this._scene.onBeforeRenderObservable.add(() => this._updateToMesh());
+            this._onAfterStepObserver = this._scene.onAfterStepObservable.add(() => this._updateToMesh());
         }
 
         // force world matrix computation before the first ray helper computation
@@ -183,19 +190,19 @@ export class RayHelper {
      * Detach the ray helper from the mesh it has previously been attached to.
      */
     public detachFromMesh(): void {
-
-        if (this._attachedToMesh) {
-            if (this._updateToMeshFunction) {
-                this._attachedToMesh.getScene().unregisterBeforeRender(this._updateToMeshFunction);
+        if (this._attachedToMesh && this._scene) {
+            if (this._onAfterRenderObserver) {
+                this._scene.onBeforeRenderObservable.remove(this._onAfterRenderObserver);
+                this._scene.onAfterStepObservable.remove(this._onAfterStepObserver);
             }
             this._attachedToMesh = null;
-            this._updateToMeshFunction = null;
+            this._onAfterRenderObserver = null;
+            this._onAfterStepObserver = null;
+            this._scene = null;
         }
-
     }
 
     private _updateToMesh(): void {
-
         var ray = this.ray;
 
         if (!this._attachedToMesh || !ray) {
@@ -215,7 +222,6 @@ export class RayHelper {
      * Dispose the helper and release its associated resources.
      */
     public dispose(): void {
-
         this.hide();
         this.detachFromMesh();
         this.ray = null;

+ 225 - 156
src/Debug/skeletonViewer.ts

@@ -54,6 +54,8 @@ export class SkeletonViewer {
      /** The Utility Layer to render the gizmos in. */
     private _utilityLayer: Nullable<UtilityLayerRenderer>;
 
+    private _boneIndices: Set<number>;
+
     /** Gets the Scene. */
     get scene(): Scene {
         return this._scene;
@@ -86,6 +88,17 @@ export class SkeletonViewer {
     set material(value: StandardMaterial) {
          this.material = value;
     }
+    /** Gets the material */
+    get displayMode(): number {
+        return this.options.displayMode || SkeletonViewer.DISPLAY_LINES;
+    }
+    /** Sets the material */
+    set displayMode(value: number) {
+        if (value > SkeletonViewer.DISPLAY_SPHERE_AND_SPURS) {
+            value = SkeletonViewer.DISPLAY_LINES;
+        }
+        this.options.displayMode = value;
+    }
 
     /**
      * Creates a new SkeletonViewer
@@ -126,6 +139,21 @@ export class SkeletonViewer {
         options.displayOptions.sphereFactor = options.displayOptions.sphereFactor ?? 0.865;
         options.computeBonesUsingShaders = options.computeBonesUsingShaders ?? true;
 
+        const boneIndices = mesh.getVerticesData(VertexBuffer.MatricesIndicesKind);
+        const boneWeights = mesh.getVerticesData(VertexBuffer.MatricesWeightsKind);
+
+        this._boneIndices = new Set();
+
+        if (boneIndices && boneWeights) {
+            for (let i = 0; i < boneIndices.length; ++i) {
+                const index = boneIndices[i], weight = boneWeights[i];
+
+                if (weight !== 0) {
+                    this._boneIndices.add(index);
+                }
+            }
+        }
+
         /* Create Utility Layer */
         this._utilityLayer = new UtilityLayerRenderer(this._scene, false);
         this._utilityLayer.pickUtilitySceneFirst = false;
@@ -135,7 +163,7 @@ export class SkeletonViewer {
         if (displayMode > SkeletonViewer.DISPLAY_SPHERE_AND_SPURS) {
             displayMode = SkeletonViewer.DISPLAY_LINES;
         }
-        this.options.displayMode = displayMode;
+        this.displayMode = displayMode;
         //Prep the Systems
         this.update();
         this._bindObs();
@@ -143,7 +171,7 @@ export class SkeletonViewer {
 
     /** The Dynamic bindings for the update functions */
     private _bindObs(): void {
-        switch (this.options.displayMode){
+        switch (this.displayMode){
             case SkeletonViewer.DISPLAY_LINES: {
                     this._obs = this.scene.onBeforeRenderObservable.add(() => {
                         this._displayLinesUpdate();
@@ -155,7 +183,7 @@ export class SkeletonViewer {
 
     /** Update the viewer to sync with current skeleton state, only used to manually update. */
     public update(): void {
-        switch (this.options.displayMode){
+        switch (this.displayMode){
             case SkeletonViewer.DISPLAY_LINES: {
                 this._displayLinesUpdate();
                 break;
@@ -223,17 +251,22 @@ export class SkeletonViewer {
 
         let mesh = this.mesh._effectiveMesh;
         var meshPos = mesh.position;
+        let idx = 0;
         for (var i = 0; i < len; i++) {
             var bone = bones[i];
-            var points = this._debugLines[i];
+            var points = this._debugLines[idx];
+            if (bone._index === -1 || !this._boneIndices.has(bone.getIndex())) {
+                continue;
+            }
             if (!points) {
                 points = [Vector3.Zero(), Vector3.Zero()];
-                this._debugLines[i] = points;
+                this._debugLines[idx] = points;
             }
             this._getBonePosition(points[0], bone, meshMat);
             this._getBonePosition(points[1], bone, meshMat, 0, bone.length, 0);
             points[0].subtractInPlace(meshPos);
             points[1].subtractInPlace(meshPos);
+            idx++;
         }
     }
 
@@ -246,7 +279,7 @@ export class SkeletonViewer {
         for (var i = len - 1; i >= 0; i--) {
             var childBone = bones[i];
             var parentBone = childBone.getParent();
-            if (!parentBone) {
+            if (!parentBone || !this._boneIndices.has(childBone.getIndex())) {
                 continue;
             }
             var points = this._debugLines[boneNum];
@@ -263,200 +296,197 @@ export class SkeletonViewer {
     }
 
     /** function to revert the mesh and scene back to the initial state. */
-    private _revert(): void {
+    private _revert(animationState: boolean): void {
         if (this.options.pauseAnimations) {
-            this.scene.animationsEnabled = true;
+            this.scene.animationsEnabled = animationState;
         }
     }
 
     /** function to build and bind sphere joint points and spur bone representations. */
-    private _buildSpheresAndSpurs(spheresOnly = true): Promise<void> {
-        this.dispose();
+    private _buildSpheresAndSpurs(spheresOnly = true): void {
+
+        if (this._debugMesh) {
+            this._debugMesh.dispose();
+            this._debugMesh = null;
+            this.ready = false;
+        }
 
         this._ready = false;
         let scene = this.scene;
         let bones: Bone[] = this.skeleton.bones;
-        let spheres: Mesh[] = [];
+        let spheres: Array<[Mesh, Bone]> = [];
         let spurs: Mesh[] = [];
 
-        return new Promise((resolve, reject) => {
-            try {
-                if (this.options.pauseAnimations) {
-                    scene.animationsEnabled = false;
-                }
+        const animationState = scene.animationsEnabled;
+
+        try {
+            if (this.options.pauseAnimations) {
+                scene.animationsEnabled = false;
+            }
+
+            if (this.options.returnToRest) {
+                this.skeleton.returnToRest();
+            }
+
+            if (this.autoUpdateBonesMatrices) {
+                this.skeleton.computeAbsoluteTransforms();
+            }
 
-                if (this.options.returnToRest) {
-                    this.skeleton.returnToRest();
+            let longestBoneLength = Number.NEGATIVE_INFINITY;
+            let getAbsoluteRestPose = function(bone: Nullable<Bone>, matrix: Matrix) {
+                if (bone === null || bone._index === -1) {
+                    matrix.copyFrom(Matrix.Identity());
+                    return;
                 }
+                getAbsoluteRestPose(bone.getParent(), matrix);
+                bone.getBindPose().multiplyToRef(matrix, matrix);
+                return;
+            };
+
+            let displayOptions = this.options.displayOptions || {};
 
-                if (this.autoUpdateBonesMatrices) {
-                    this.skeleton.computeAbsoluteTransforms();
+            for (let i = 0; i < bones.length; i++) {
+                let bone = bones[i];
+
+                if (bone._index === -1 || !this._boneIndices.has(bone.getIndex())) {
+                    continue;
                 }
 
-                let longestBoneLength = Number.NEGATIVE_INFINITY;
-                let getAbsoluteRestPose = function(bone: Nullable<Bone>, matrix: Matrix) {
-                    if (bone == null) {
-                        matrix.copyFrom(Matrix.Identity());
-                        return;
-                    }
-                    getAbsoluteRestPose(bone.getParent(), matrix);
-                    bone.getRestPose().multiplyToRef(matrix, matrix);
-                    return;
-                };
+                let boneAbsoluteRestTransform = new Matrix();
+                getAbsoluteRestPose(bone, boneAbsoluteRestTransform);
 
-                let displayOptions = this.options.displayOptions || {};
+                let anchorPoint = new Vector3();
+                boneAbsoluteRestTransform.decompose(undefined, undefined, anchorPoint);
 
-                for (let i = 0; i < bones.length; i++) {
-                    let bone: Bone = bones[i];
+                bone.children.forEach((bc, i) => {
+                    let childAbsoluteRestTransform : Matrix = new Matrix();
+                    bc.getRestPose().multiplyToRef(boneAbsoluteRestTransform, childAbsoluteRestTransform);
+                    let childPoint = new Vector3();
+                    childAbsoluteRestTransform.decompose(undefined, undefined, childPoint);
 
-                    if (bone._index === null) {
-                        bone._index = i;
+                    let distanceFromParent = Vector3.Distance(anchorPoint, childPoint);
+
+                    if (distanceFromParent > longestBoneLength) {
+                        longestBoneLength = distanceFromParent;
                     }
-                    if (bone._index === -1) {
-                        continue;
+                    if (spheresOnly) {
+                        return;
                     }
 
-                    let boneAbsoluteRestTransform = new Matrix();
-                    getAbsoluteRestPose(bone, boneAbsoluteRestTransform);
-
-                    let anchorPoint = new Vector3();
-                    boneAbsoluteRestTransform.decompose(undefined, undefined, anchorPoint);
-
-                    bone.children.forEach((bc, i) => {
-                        let childAbsoluteRestTransform : Matrix = new Matrix();
-                        bc.getRestPose().multiplyToRef(boneAbsoluteRestTransform, childAbsoluteRestTransform);
-                        let childPoint = new Vector3();
-                        childAbsoluteRestTransform.decompose(undefined, undefined, childPoint);
-
-                        let distanceFromParent = Vector3.Distance(anchorPoint, childPoint);
-
-                        if (distanceFromParent > longestBoneLength) {
-                            longestBoneLength = distanceFromParent;
-                        }
-                        if (spheresOnly) {
-                            return;
-                        }
-
-                        let dir = childPoint.clone().subtract(anchorPoint.clone());
-                        let h = dir.length();
-                        let up = dir.normalize().scale(h);
-
-                        let midStep = displayOptions.midStep || 0.165;
-                        let midStepFactor = displayOptions.midStepFactor || 0.215;
-
-                        let up0 = up.scale(midStep);
-
-                        let spur = ShapeBuilder.ExtrudeShapeCustom(bc.name + ':spur',
-                        {
-                            shape:  [
-                                        new Vector3(1, -1,  0),
-                                        new Vector3(1,  1,  0),
-                                        new Vector3(-1,  1,  0),
-                                        new Vector3(-1, -1,  0),
-                                        new Vector3(1, -1,  0)
-                                    ],
-                            path:   [ Vector3.Zero(), up0, up ],
-                            scaleFunction:
-                                    (i: number) => {
-                                        switch (i){
-                                            case 0:
-                                            case 2:
-                                            return 0;
-                                            case 1:
-                                            return h * midStepFactor;
-                                        }
+                    let dir = childPoint.clone().subtract(anchorPoint.clone());
+                    let h = dir.length();
+                    let up = dir.normalize().scale(h);
+
+                    let midStep = displayOptions.midStep || 0.165;
+                    let midStepFactor = displayOptions.midStepFactor || 0.215;
+
+                    let up0 = up.scale(midStep);
+
+                    let spur = ShapeBuilder.ExtrudeShapeCustom(bc.name + ':spur',
+                    {
+                        shape:  [
+                                    new Vector3(1, -1,  0),
+                                    new Vector3(1,  1,  0),
+                                    new Vector3(-1,  1,  0),
+                                    new Vector3(-1, -1,  0),
+                                    new Vector3(1, -1,  0)
+                                ],
+                        path:   [ Vector3.Zero(), up0, up ],
+                        scaleFunction:
+                                (i: number) => {
+                                    switch (i){
+                                        case 0:
+                                        case 2:
                                         return 0;
-                                    },
-                            sideOrientation: Mesh.DEFAULTSIDE,
-                            updatable: true
-                        },  scene);
+                                        case 1:
+                                        return h * midStepFactor;
+                                    }
+                                    return 0;
+                                },
+                        sideOrientation: Mesh.DEFAULTSIDE,
+                        updatable: false
+                    },  scene);
+
+                    spur.convertToFlatShadedMesh();
+
+                    let numVertices = spur.getTotalVertices();
+                    let mwk: number[] = [], mik: number[] = [];
 
-                        let ind = spur.getIndices() || [];
-                        let mwk: number[] = [], mik: number[] = [];
+                    for (let i = 0; i < numVertices; i++) {
+                        mwk.push(1, 0, 0, 0);
+                        mik.push(bone.getIndex(), 0, 0, 0);
+                    }
+                    spur.position = anchorPoint.clone();
 
-                        for (let i = 0; i < ind.length; i++) {
-                            mwk.push(1, 0, 0, 0);
-                            mik.push(bone.getIndex(), 0, 0, 0);
-                        }
-                        spur.convertToFlatShadedMesh();
-                        spur.position = anchorPoint.clone();
+                    spur.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
+                    spur.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
 
-                        spur.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
-                        spur.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
-                        spurs.push(spur);
+                    spurs.push(spur);
+                });
 
-                    });
+                let sphereBaseSize = displayOptions.sphereBaseSize || 0.2;
 
-                    let sphereBaseSize = displayOptions.sphereBaseSize || 0.2;
+                let sphere = SphereBuilder.CreateSphere(bone.name + ':sphere', {
+                    segments: 6,
+                    diameter: sphereBaseSize,
+                    updatable: false
+                }, scene);
 
-                    let sphere = SphereBuilder.CreateSphere(bone.name + ':sphere', {
-                        segments: 6,
-                        diameter: sphereBaseSize,
-                        updatable: true
-                    }, scene);
+                const numVertices = sphere.getTotalVertices();
 
-                    let ind = sphere.getIndices() || [];
-                    let mwk: number[] = [], mik: number[] = [];
-
-                    for (let i = 0; i < ind.length; i++) {
-                        mwk.push(1, 0, 0, 0);
-                        mik.push(bone.getIndex(), 0, 0, 0);
-                    }
+                let mwk: number[] = [], mik: number[] = [];
 
-                    sphere.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
-                    sphere.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
-                    sphere.position = anchorPoint.clone();
-                    spheres.push(sphere);
+                for (let i = 0; i < numVertices; i++) {
+                    mwk.push(1, 0, 0, 0);
+                    mik.push(bone.getIndex(), 0, 0, 0);
                 }
 
-                let skip = 0;
-                let sphereScaleUnit = displayOptions.sphereScaleUnit || 2;
-                let sphereFactor = displayOptions.sphereFactor || 0.85;
+                sphere.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
+                sphere.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
 
-                for (let i = 0; i < bones.length; i++) {
-                    let bone: Nullable<Bone> = bones[i];
-                    if (bone.getIndex() === -1) {
-                        skip++;
-                        continue;
-                    }
-                    let sphere = spheres[i - skip];
-                    let scale = 1 / (sphereScaleUnit / longestBoneLength);
+                sphere.position = anchorPoint.clone();
+                spheres.push([sphere, bone]);
+            }
 
-                    let _stepsOut = 0;
-                    let _b: Bone = (bone as Bone) || {};
+            let sphereScaleUnit = displayOptions.sphereScaleUnit || 2;
+            let sphereFactor = displayOptions.sphereFactor || 0.85;
 
-                    while ((_b.getParent()) && (_b.getParent() as Bone).getIndex() !== -1) {
-                        _stepsOut++;
-                        _b = (_b.getParent() as Bone);
-                    }
-                    sphere.scaling.scaleInPlace(scale * Math.pow(sphereFactor, _stepsOut));
-                }
+            const meshes = [];
+            for (let i = 0; i < spheres.length; i++) {
+                let [sphere, bone] = spheres[i];
+                let scale = 1 / (sphereScaleUnit / longestBoneLength);
 
-                this.debugMesh = Mesh.MergeMeshes(spheres.concat(spurs), true, true);
-                if (this.debugMesh) {
-                    this.debugMesh.renderingGroupId = this.renderingGroupId;
-                    this.debugMesh.skeleton = this.skeleton;
-                    this.debugMesh.parent = this.mesh;
-                    this.debugMesh.computeBonesUsingShaders = this.options.computeBonesUsingShaders ?? true;
+                let _stepsOut = 0;
+                let _b = bone;
+
+                while ((_b.getParent()) && (_b.getParent() as Bone).getIndex() !== -1) {
+                    _stepsOut++;
+                    _b = (_b.getParent() as Bone);
                 }
+                sphere.scaling.scaleInPlace(scale * Math.pow(sphereFactor, _stepsOut));
+                meshes.push(sphere);
+            }
 
-                resolve();
-            } catch (err) {
-                console.log(err);
-                this._revert();
-                this.dispose();
+            this.debugMesh = Mesh.MergeMeshes(meshes.concat(spurs), true, true);
+            if (this.debugMesh) {
+                this.debugMesh.renderingGroupId = this.renderingGroupId;
+                this.debugMesh.skeleton = this.skeleton;
+                this.debugMesh.parent = this.mesh;
+                this.debugMesh.computeBonesUsingShaders = this.options.computeBonesUsingShaders ?? true;
+                this.debugMesh.alwaysSelectAsActiveMesh = true;
             }
-        }).then(() => {
-            this._revert();
+
+            this._revert(animationState);
             this.ready = true;
-        }).catch((err) => {
-            console.log(err);
+        } catch (err) {
+            console.error(err);
+            this._revert(animationState);
             this.dispose();
-        });
+        }
     }
 
     /** Update the viewer to sync with current skeleton state, only used for the line display. */
-    private  _displayLinesUpdate() {
+    private  _displayLinesUpdate(): void {
         if (!this._utilityLayer) {
             return;
         }
@@ -486,9 +516,46 @@ export class SkeletonViewer {
             this._debugMesh.color = this.color;
         }
     }
+    /** Changes the displayMode of the skeleton viewer
+     * @param mode The displayMode numerical value
+     */
+    public changeDisplayMode(mode: number): void {
+        let wasEnabled = (this.isEnabled) ? true : false;
+        if (this.displayMode !== mode) {
+            this.isEnabled = false;
+            if (this._debugMesh) {
+                this._debugMesh.dispose();
+                this._debugMesh = null;
+                this.ready = false;
+            }
+            this.displayMode = mode;
+
+            this.update();
+            this._bindObs();
+            this.isEnabled = wasEnabled;
+        }
+    }
+
+    /** Changes the displayMode of the skeleton viewer
+     * @param option String of the option name
+     * @param value The numerical option value
+     */
+    public changeDisplayOptions(option: string, value: number): void {
+        let wasEnabled = (this.isEnabled) ? true : false;
+        (this.options.displayOptions as any)[option] = value;
+        this.isEnabled = false;
+        if (this._debugMesh) {
+            this._debugMesh.dispose();
+            this._debugMesh = null;
+            this.ready = false;
+        }
+        this.update();
+        this._bindObs();
+        this.isEnabled = wasEnabled;
+    }
 
     /** Release associated resources */
-    public dispose() {
+    public dispose(): void {
         this.isEnabled = false;
         if (this._debugMesh) {
             this._debugMesh.dispose();
@@ -499,5 +566,7 @@ export class SkeletonViewer {
             this._utilityLayer.dispose();
             this._utilityLayer = null;
         }
+
+        this.ready = false;
     }
 }

+ 2 - 2
src/Engines/engine.ts

@@ -483,14 +483,14 @@ export class Engine extends ThinEngine {
     constructor(canvasOrContext: Nullable<HTMLCanvasElement | WebGLRenderingContext>, antialias?: boolean, options?: EngineOptions, adaptToDeviceRatio: boolean = false) {
         super(canvasOrContext, antialias, options, adaptToDeviceRatio);
 
+        Engine.Instances.push(this);
+
         if (!canvasOrContext) {
             return;
         }
 
         options = this._creationOptions;
 
-        Engine.Instances.push(this);
-
         if ((<any>canvasOrContext).getContext) {
             let canvas = <HTMLCanvasElement>canvasOrContext;
 

+ 18 - 21
src/Engines/nativeEngine.ts

@@ -42,7 +42,6 @@ interface INativeEngine {
     createVertexBuffer(data: ArrayBufferView, dynamic: boolean): any;
     deleteVertexBuffer(buffer: any): void;
     recordVertexBuffer(vertexArray: any, buffer: any, location: number, byteOffset: number, byteStride: number, numElements: number, type: number, normalized: boolean): void;
-    bindBuffer(buffer: any, location: number, byteOffset: number, byteStride: number, numElements: number, type: number, normalized: boolean): void;
     updateDynamicVertexBuffer(buffer: any, data: ArrayBufferView, byteOffset: number, byteLength: number): void;
 
     createProgram(vertexShader: string, fragmentShader: string): any;
@@ -205,6 +204,7 @@ export class NativeEngine extends Engine {
     private readonly _native: INativeEngine = new _native.Engine();
     /** Defines the invalid handle returned by bgfx when resource creation goes wrong */
     private readonly INVALID_HANDLE = 65535;
+    private _boundBuffersVertexArray: any = null;
 
     public getHardwareScalingLevel(): number {
         return 1.0;
@@ -276,6 +276,9 @@ export class NativeEngine extends Engine {
 
     public dispose(): void {
         super.dispose();
+        if (this._boundBuffersVertexArray) {
+            this._native.deleteVertexArray(this._boundBuffersVertexArray);
+        }
         this._native.dispose();
     }
 
@@ -365,26 +368,7 @@ export class NativeEngine extends Engine {
         return buffer;
     }
 
-    public bindBuffers(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): void {
-        // TODO : support index buffer
-        const attributes = effect.getAttributesNames();
-        for (let index = 0; index < attributes.length; index++) {
-            const location = effect.getAttributeLocation(index);
-            if (location >= 0) {
-                const kind = attributes[index];
-                const vertexBuffer = vertexBuffers[kind];
-                if (vertexBuffer) {
-                    const buffer = vertexBuffer.getBuffer() as Nullable<NativeDataBuffer>;
-                    if (buffer) {
-                        this._native.bindBuffer(buffer.nativeVertexBuffer, location, vertexBuffer.byteOffset, vertexBuffer.byteStride, vertexBuffer.getSize(), vertexBuffer.type, vertexBuffer.normalized);
-                    }
-                }
-            }
-        }
-    }
-    public recordVertexArrayObject(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): WebGLVertexArrayObject {
-        const vertexArray = this._native.createVertexArray();
-
+    protected _recordVertexArrayObject(vertexArray: any, vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): void {
         if (indexBuffer) {
             this._native.recordIndexBuffer(vertexArray, indexBuffer.nativeIndexBuffer);
         }
@@ -411,7 +395,20 @@ export class NativeEngine extends Engine {
                 }
             }
         }
+    }
 
+    public bindBuffers(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): void {
+        if (this._boundBuffersVertexArray) {
+            this._native.deleteVertexArray(this._boundBuffersVertexArray);
+        }
+        this._boundBuffersVertexArray = this._native.createVertexArray();
+        this._recordVertexArrayObject(this._boundBuffersVertexArray, vertexBuffers, indexBuffer, effect);
+        this._native.bindVertexArray(this._boundBuffersVertexArray);
+    }
+
+    public recordVertexArrayObject(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: Nullable<NativeDataBuffer>, effect: Effect): WebGLVertexArrayObject {
+        const vertexArray = this._native.createVertexArray();
+        this._recordVertexArrayObject(vertexArray, vertexBuffers, indexBuffer, effect);
         return vertexArray;
     }
 

+ 6 - 3
src/Engines/thinEngine.ts

@@ -142,14 +142,14 @@ export class ThinEngine {
      */
     // Not mixed with Version for tooling purpose.
     public static get NpmPackage(): string {
-        return "babylonjs@4.2.0-alpha.28";
+        return "babylonjs@4.2.0-alpha.31";
     }
 
     /**
      * Returns the current version of the framework
      */
     public static get Version(): string {
-        return "4.2.0-alpha.28";
+        return "4.2.0-alpha.31";
     }
 
     /**
@@ -941,7 +941,7 @@ export class ThinEngine {
     }
 
     /**
-     * Gets a string idenfifying the name of the class
+     * Gets a string identifying the name of the class
      * @returns "Engine" string
      */
     public getClassName(): string {
@@ -1563,6 +1563,9 @@ export class ThinEngine {
 
     private _vertexAttribPointer(buffer: DataBuffer, indx: number, size: number, type: number, normalized: boolean, stride: number, offset: number): void {
         var pointer = this._currentBufferPointers[indx];
+        if (!pointer) {
+            return;
+        }
 
         var changed = false;
         if (!pointer.active) {

+ 190 - 0
src/Gizmos/cameraGizmo.ts

@@ -0,0 +1,190 @@
+import { Nullable } from "../types";
+import { Vector3 } from "../Maths/math.vector";
+import { Color3 } from '../Maths/math.color';
+import { Mesh } from "../Meshes/mesh";
+import { Gizmo } from "./gizmo";
+import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
+import { StandardMaterial } from '../Materials/standardMaterial';
+import { Scene } from '../scene';
+import { Camera } from '../Cameras/camera';
+import { BoxBuilder } from "../Meshes/Builders/boxBuilder";
+import { CylinderBuilder } from '../Meshes/Builders/cylinderBuilder';
+import { Matrix } from '../Maths/math';
+import { LinesBuilder } from "../Meshes/Builders/linesBuilder";
+
+/**
+ * Gizmo that enables viewing a camera
+ */
+export class CameraGizmo extends Gizmo {
+    private _cameraMesh: Mesh;
+    private _cameraLinesMesh: Mesh;
+    private _material: StandardMaterial;
+
+    /**
+     * Creates a CameraGizmo
+     * @param gizmoLayer The utility layer the gizmo will be added to
+     */
+    constructor(gizmoLayer?: UtilityLayerRenderer) {
+        super(gizmoLayer);
+
+        this._material = new StandardMaterial("cameraGizmoMaterial", this.gizmoLayer.utilityLayerScene);
+        this._material.diffuseColor = new Color3(0.5, 0.5, 0.5);
+        this._material.specularColor = new Color3(0.1, 0.1, 0.1);
+    }
+    private _camera: Nullable<Camera> = null;
+
+    /** Gets or sets a boolean indicating if frustum lines must be rendered (true by default)) */
+    public get displayFrustum() {
+        return this._cameraLinesMesh.isEnabled();
+    }
+    public set displayFrustum(value) {
+        this._cameraLinesMesh.setEnabled(value);
+    }
+
+    /**
+     * The camera that the gizmo is attached to
+     */
+    public set camera(camera: Nullable<Camera>) {
+        this._camera = camera;
+        this.attachedNode = camera;
+        if (camera) {
+            // Create the mesh for the given camera
+            if (this._cameraMesh) {
+                this._cameraMesh.dispose();
+            }
+            if (this._cameraLinesMesh) {
+                this._cameraLinesMesh.dispose();
+            }
+            this._cameraMesh = CameraGizmo._CreateCameraMesh(this.gizmoLayer.utilityLayerScene);
+            this._cameraLinesMesh = CameraGizmo._CreateCameraFrustum(this.gizmoLayer.utilityLayerScene);
+
+            this._cameraMesh.getChildMeshes(false).forEach((m) => {
+                m.material = this._material;
+            });
+            this._cameraMesh.parent = this._rootMesh;
+
+            this._cameraLinesMesh.parent = this._rootMesh;
+
+            if (this.gizmoLayer.utilityLayerScene.activeCamera && this.gizmoLayer.utilityLayerScene.activeCamera.maxZ < camera.maxZ * 1.5) {
+                this.gizmoLayer.utilityLayerScene.activeCamera.maxZ = camera.maxZ * 1.5;
+            }
+
+            if (!this.attachedNode!.reservedDataStore) {
+                this.attachedNode!.reservedDataStore = {};
+            }
+            this.attachedNode!.reservedDataStore.cameraGizmo = this;
+
+            // Add lighting to the camera gizmo
+            var gizmoLight = this.gizmoLayer._getSharedGizmoLight();
+            gizmoLight.includedOnlyMeshes = gizmoLight.includedOnlyMeshes.concat(this._cameraMesh.getChildMeshes(false));
+
+            this._update();
+        }
+    }
+
+    public get camera() {
+        return this._camera;
+    }
+
+    /**
+     * Gets the material used to render the camera gizmo
+     */
+    public get material() {
+        return this._material;
+    }
+    /**
+     * @hidden
+     * Updates the gizmo to match the attached mesh's position/rotation
+     */
+
+    protected _update() {
+        super._update();
+        if (!this._camera) {
+            return;
+        }
+
+        // frustum matrix
+        this._camera.getProjectionMatrix().invertToRef(this._invProjection);
+        this._cameraLinesMesh.setPivotMatrix(this._invProjection, false);
+
+        this._cameraLinesMesh.scaling.x = 1 / this._rootMesh.scaling.x;
+        this._cameraLinesMesh.scaling.y = 1 / this._rootMesh.scaling.y;
+        this._cameraLinesMesh.scaling.z = 1 / this._rootMesh.scaling.z;
+    }
+
+    // Static helper methods
+    private static _Scale = 0.05;
+    private _invProjection = new Matrix();
+
+    /**
+     * Disposes of the camera gizmo
+     */
+    public dispose() {
+        if (this._cameraMesh) {
+            this._cameraMesh.dispose();
+        }
+        if (this._cameraLinesMesh) {
+            this._cameraLinesMesh.dispose();
+        }
+        this._material.dispose();
+        super.dispose();
+    }
+
+    private static _CreateCameraMesh(scene: Scene) {
+        var root = new Mesh("rootCameraGizmo", scene);
+
+        var mesh = new Mesh(root.name, scene);
+        mesh.parent = root;
+
+        var box = BoxBuilder.CreateBox(root.name, {width: 1.0, height: 0.8, depth: 0.5 }, scene);
+        box.parent = mesh;
+
+        var cyl1 = CylinderBuilder.CreateCylinder(root.name, {height: 0.5, diameterTop: 0.8, diameterBottom: 0.8}, scene);
+        cyl1.parent = mesh;
+        cyl1.position.y = 0.3;
+        cyl1.position.x = -0.6;
+        cyl1.rotation.x = Math.PI * 0.5;
+
+        var cyl2 = CylinderBuilder.CreateCylinder(root.name, {height: 0.5, diameterTop: 0.6, diameterBottom: 0.6}, scene);
+        cyl2.parent = mesh;
+        cyl2.position.y = 0.5;
+        cyl2.position.x = 0.4;
+        cyl2.rotation.x = Math.PI * 0.5;
+
+        var cyl3 = CylinderBuilder.CreateCylinder(root.name, {height: 0.5, diameterTop: 0.5, diameterBottom: 0.5}, scene);
+        cyl3.parent = mesh;
+        cyl3.position.y = 0.0;
+        cyl3.position.x = 0.6;
+        cyl3.rotation.z = Math.PI * 0.5;
+
+        root.scaling.scaleInPlace(CameraGizmo._Scale);
+        root.rotation.y = -Math.PI * 0.5;
+        mesh.position.x = -0.9;
+
+        return root;
+    }
+
+    private static _CreateCameraFrustum(scene: Scene) {
+        var root = new Mesh("rootCameraGizmo", scene);
+        var mesh = new Mesh(root.name, scene);
+        mesh.parent = root;
+
+        for (var y = 0; y < 4; y += 2)
+        {
+            for (var x = 0; x < 4; x += 2)
+            {
+                var line = LinesBuilder.CreateLines("lines", { points: [new Vector3(-1 + x, -1 + y, -1), new Vector3(-1 + x, -1 + y, 1)] }, scene);
+                line.parent = mesh;
+                line.alwaysSelectAsActiveMesh = true;
+                var line = LinesBuilder.CreateLines("lines", { points: [new Vector3(-1, -1 + x, -1 + y), new Vector3(1, -1 + x, -1 + y)] }, scene);
+                line.parent = mesh;
+                line.alwaysSelectAsActiveMesh = true;
+                var line = LinesBuilder.CreateLines("lines", { points: [new Vector3(-1 + x, -1, -1 + y), new Vector3(-1 + x,  1, -1 + y)] }, scene);
+                line.parent = mesh;
+                line.alwaysSelectAsActiveMesh = true;
+            }
+        }
+
+        return root;
+    }
+}

+ 72 - 10
src/Gizmos/gizmoManager.ts

@@ -15,20 +15,27 @@ import { ScaleGizmo } from "./scaleGizmo";
 import { BoundingBoxGizmo } from "./boundingBoxGizmo";
 
 /**
- * Helps setup gizmo's in the scene to rotate/scale/position meshes
+ * Helps setup gizmo's in the scene to rotate/scale/position nodes
  */
 export class GizmoManager implements IDisposable {
     /**
      * Gizmo's created by the gizmo manager, gizmo will be null until gizmo has been enabled for the first time
      */
     public gizmos: { positionGizmo: Nullable<PositionGizmo>, rotationGizmo: Nullable<RotationGizmo>, scaleGizmo: Nullable<ScaleGizmo>, boundingBoxGizmo: Nullable<BoundingBoxGizmo> };
+
     /** When true, the gizmo will be detached from the current object when a pointer down occurs with an empty picked mesh */
     public clearGizmoOnEmptyPointerEvent = false;
+
     /** Fires an event when the manager is attached to a mesh */
     public onAttachedToMeshObservable = new Observable<Nullable<AbstractMesh>>();
+
+    /** Fires an event when the manager is attached to a node */
+    public onAttachedToNodeObservable = new Observable<Nullable<Node>>();
+
     private _gizmosEnabled = { positionGizmo: false, rotationGizmo: false, scaleGizmo: false, boundingBoxGizmo: false };
     private _pointerObserver: Nullable<Observer<PointerInfo>> = null;
     private _attachedMesh: Nullable<AbstractMesh> = null;
+    private _attachedNode: Nullable<Node> = null;
     private _boundingBoxColor = Color3.FromHexString("#0984e3");
     private _defaultUtilityLayer: UtilityLayerRenderer;
     private _defaultKeepDepthUtilityLayer: UtilityLayerRenderer;
@@ -42,7 +49,11 @@ export class GizmoManager implements IDisposable {
      */
     public attachableMeshes: Nullable<Array<AbstractMesh>> = null;
     /**
-     * If pointer events should perform attaching/detaching a gizmo, if false this can be done manually via attachToMesh. (Default: true)
+     * Array of nodes which will have the gizmo attached when a pointer selected them. If null, all nodes are attachable. (Default: null)
+     */
+    public attachableNodes: Nullable<Array<Node>> = null;
+    /**
+     * If pointer events should perform attaching/detaching a gizmo, if false this can be done manually via attachToMesh/attachToNode. (Default: true)
      */
     public usePointerToAttachGizmos = true;
 
@@ -124,7 +135,11 @@ export class GizmoManager implements IDisposable {
         if (this._attachedMesh) {
             this._attachedMesh.removeBehavior(this.boundingBoxDragBehavior);
         }
+        if (this._attachedNode) {
+            this._attachedNode.removeBehavior(this.boundingBoxDragBehavior);
+        }
         this._attachedMesh = mesh;
+        this._attachedNode = null;
         for (var key in this.gizmos) {
             var gizmo = <Nullable<Gizmo>>((<any>this.gizmos)[key]);
             if (gizmo && (<any>this._gizmosEnabled)[key]) {
@@ -138,6 +153,31 @@ export class GizmoManager implements IDisposable {
     }
 
     /**
+     * Attaches a set of gizmos to the specified node
+     * @param node The node the gizmo's should be attached to
+     */
+    public attachToNode(node: Nullable<Node>) {
+        if (this._attachedMesh) {
+            this._attachedMesh.removeBehavior(this.boundingBoxDragBehavior);
+        }
+        if (this._attachedNode) {
+            this._attachedNode.removeBehavior(this.boundingBoxDragBehavior);
+        }
+        this._attachedMesh = null;
+        this._attachedNode = node;
+        for (var key in this.gizmos) {
+            var gizmo = <Nullable<Gizmo>>((<any>this.gizmos)[key]);
+            if (gizmo && (<any>this._gizmosEnabled)[key]) {
+                gizmo.attachedNode = node;
+            }
+        }
+        if (this.boundingBoxGizmoEnabled && this._attachedNode) {
+            this._attachedNode.addBehavior(this.boundingBoxDragBehavior);
+        }
+        this.onAttachedToNodeObservable.notifyObservers(node);
+    }
+
+    /**
      * If the position gizmo is enabled
      */
     public set positionGizmoEnabled(value: boolean) {
@@ -145,9 +185,13 @@ export class GizmoManager implements IDisposable {
             if (!this.gizmos.positionGizmo) {
                 this.gizmos.positionGizmo = new PositionGizmo(this._defaultUtilityLayer, this._thickness);
             }
-            this.gizmos.positionGizmo.attachedMesh = this._attachedMesh;
+            if (this._attachedNode) {
+                this.gizmos.positionGizmo.attachedNode = this._attachedNode;
+            } else {
+                this.gizmos.positionGizmo.attachedMesh = this._attachedMesh;
+            }
         } else if (this.gizmos.positionGizmo) {
-            this.gizmos.positionGizmo.attachedMesh = null;
+            this.gizmos.positionGizmo.attachedNode = null;
         }
         this._gizmosEnabled.positionGizmo = value;
     }
@@ -162,9 +206,13 @@ export class GizmoManager implements IDisposable {
             if (!this.gizmos.rotationGizmo) {
                 this.gizmos.rotationGizmo = new RotationGizmo(this._defaultUtilityLayer, 32, false, this._thickness);
             }
-            this.gizmos.rotationGizmo.attachedMesh = this._attachedMesh;
+            if (this._attachedNode) {
+                this.gizmos.rotationGizmo.attachedNode = this._attachedNode;
+            } else {
+                this.gizmos.rotationGizmo.attachedMesh = this._attachedMesh;
+            }
         } else if (this.gizmos.rotationGizmo) {
-            this.gizmos.rotationGizmo.attachedMesh = null;
+            this.gizmos.rotationGizmo.attachedNode = null;
         }
         this._gizmosEnabled.rotationGizmo = value;
     }
@@ -177,9 +225,13 @@ export class GizmoManager implements IDisposable {
     public set scaleGizmoEnabled(value: boolean) {
         if (value) {
             this.gizmos.scaleGizmo = this.gizmos.scaleGizmo || new ScaleGizmo(this._defaultUtilityLayer, this._thickness);
-            this.gizmos.scaleGizmo.attachedMesh = this._attachedMesh;
+            if (this._attachedNode) {
+                this.gizmos.scaleGizmo.attachedNode = this._attachedNode;
+            } else {
+                this.gizmos.scaleGizmo.attachedMesh = this._attachedMesh;
+            }
         } else if (this.gizmos.scaleGizmo) {
-            this.gizmos.scaleGizmo.attachedMesh = null;
+            this.gizmos.scaleGizmo.attachedNode = null;
         }
         this._gizmosEnabled.scaleGizmo = value;
     }
@@ -192,16 +244,26 @@ export class GizmoManager implements IDisposable {
     public set boundingBoxGizmoEnabled(value: boolean) {
         if (value) {
             this.gizmos.boundingBoxGizmo = this.gizmos.boundingBoxGizmo || new BoundingBoxGizmo(this._boundingBoxColor, this._defaultKeepDepthUtilityLayer);
-            this.gizmos.boundingBoxGizmo.attachedMesh = this._attachedMesh;
+            if (this._attachedMesh) {
+                this.gizmos.boundingBoxGizmo.attachedMesh = this._attachedMesh;
+            } else {
+                this.gizmos.boundingBoxGizmo.attachedNode = this._attachedNode;
+            }
+
             if (this._attachedMesh) {
                 this._attachedMesh.removeBehavior(this.boundingBoxDragBehavior);
                 this._attachedMesh.addBehavior(this.boundingBoxDragBehavior);
+            } else if (this._attachedNode) {
+                this._attachedNode.removeBehavior(this.boundingBoxDragBehavior);
+                this._attachedNode.addBehavior(this.boundingBoxDragBehavior);
             }
         } else if (this.gizmos.boundingBoxGizmo) {
             if (this._attachedMesh) {
                 this._attachedMesh.removeBehavior(this.boundingBoxDragBehavior);
+            } else if (this._attachedNode) {
+                this._attachedNode.removeBehavior(this.boundingBoxDragBehavior);
             }
-            this.gizmos.boundingBoxGizmo.attachedMesh = null;
+            this.gizmos.boundingBoxGizmo.attachedNode = null;
         }
         this._gizmosEnabled.boundingBoxGizmo = value;
     }

+ 1 - 0
src/Gizmos/index.ts

@@ -8,4 +8,5 @@ export * from "./positionGizmo";
 export * from "./rotationGizmo";
 export * from "./scaleGizmo";
 export * from "./lightGizmo";
+export * from "./cameraGizmo";
 export * from "./planeDragGizmo";

+ 9 - 3
src/Inputs/scene.inputManager.ts

@@ -608,7 +608,9 @@ export class InputManager {
 
         this._onPointerMove = (evt: PointerEvent) => {
             // preserve compatibility with Safari when pointerId is not present
-            (evt as any).pointerId = evt.pointerId ?? 0;
+            if (evt.pointerId === undefined) {
+                (evt as any).pointerId = 0;
+            }
 
             this._updatePointerPosition(evt);
 
@@ -637,7 +639,9 @@ export class InputManager {
             this._meshPickProceed = false;
 
             // preserve compatibility with Safari when pointerId is not present
-            (evt as any).pointerId = evt.pointerId ?? 0;
+            if (evt.pointerId === undefined) {
+                (evt as any).pointerId = 0;
+            }
 
             this._updatePointerPosition(evt);
 
@@ -684,7 +688,9 @@ export class InputManager {
             this._meshPickProceed = false;
 
             // preserve compatibility with Safari when pointerId is not present
-            (evt as any).pointerId = evt.pointerId ?? 0;
+            if (evt.pointerId === undefined) {
+                (evt as any).pointerId = 0;
+            }
 
             this._updatePointerPosition(evt);
 

+ 14 - 0
src/Loading/Plugins/babylonFileLoader.ts

@@ -29,6 +29,7 @@ import { ReflectionProbe } from "../../Probes/reflectionProbe";
 import { _TypeStore } from '../../Misc/typeStore';
 import { Tools } from '../../Misc/tools';
 import { StringTools } from '../../Misc/stringTools';
+import { PostProcess } from '../../PostProcesses/postProcess';
 
 /** @hidden */
 export var _BabylonLoaderRegistered = true;
@@ -315,6 +316,19 @@ var loadAssetContainer = (scene: Scene, data: string, rootUrl: string, onError?:
             }
         }
 
+        // Postprocesses
+        if (parsedData.postProcesses !== undefined && parsedData.postProcesses !== null) {
+            for (index = 0, cache = parsedData.postProcesses.length; index < cache; index++) {
+                var parsedPostProcess = parsedData.postProcesses[index];
+                var postProcess = PostProcess.Parse(parsedPostProcess, scene, rootUrl);
+                if (postProcess) {
+                    container.postProcesses.push(postProcess);
+                    log += (index === 0 ? "\n\Postprocesses:" : "");
+                    log += "\n\t\t" + postProcess.toString();
+                }
+            }
+        }
+
         // Animation Groups
         if (parsedData.animationGroups !== undefined && parsedData.animationGroups !== null) {
             for (index = 0, cache = parsedData.animationGroups.length; index < cache; index++) {

+ 3 - 0
src/Materials/Textures/Procedurals/proceduralTexture.ts

@@ -18,6 +18,7 @@ import "../../../Engines/Extensions/engine.renderTarget";
 import "../../../Engines/Extensions/engine.renderTargetCube";
 import "../../../Shaders/procedural.vertex";
 import { DataBuffer } from '../../../Meshes/dataBuffer';
+import { _TypeStore } from '../../../Misc/typeStore';
 
 /**
  * Procedural texturing is a way to programmatically create a texture. There are 2 types of procedural textures: code-only, and code that references some classic 2D images, sometimes calmpler' images.
@@ -634,3 +635,5 @@ export class ProceduralTexture extends Texture {
         super.dispose();
     }
 }
+
+_TypeStore.RegisteredTypes["BABYLON.ProceduralTexture"] = ProceduralTexture;

+ 2 - 2
src/Materials/Textures/baseTexture.ts

@@ -375,8 +375,8 @@ export class BaseTexture implements IAnimatable {
      */
     public delayLoadState = Constants.DELAYLOADSTATE_NONE;
 
-    private _scene: Nullable<Scene> = null;
-    private _engine: Nullable<ThinEngine> = null;
+    protected _scene: Nullable<Scene> = null;
+    protected _engine: Nullable<ThinEngine> = null;
 
     /** @hidden */
     public _texture: Nullable<InternalTexture> = null;

+ 30 - 23
src/Materials/Textures/rawTexture.ts

@@ -1,7 +1,10 @@
-import { Scene } from "../../scene";
 import { Texture } from "./texture";
 import { Constants } from "../../Engines/constants";
 import "../../Engines/Extensions/engine.rawTexture";
+import { Nullable } from '../../types';
+import { ThinEngine } from '../../Engines/thinEngine';
+
+declare type Scene = import("../../scene").Scene;
 
 /**
  * Raw texture can help creating a texture directly from an array of data.
@@ -18,7 +21,7 @@ export class RawTexture extends Texture {
      * @param width define the width of the texture
      * @param height define the height of the texture
      * @param format define the format of the data (RGB, RGBA... Engine.TEXTUREFORMAT_xxx)
-     * @param scene  define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps define whether mip maps should be generated or not
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
@@ -29,10 +32,14 @@ export class RawTexture extends Texture {
          * Define the format of the data (RGB, RGBA... Engine.TEXTUREFORMAT_xxx)
          */
         public format: number,
-        scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT) {
-        super(null, scene, !generateMipMaps, invertY);
+        sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT) {
+        super(null, sceneOrEngine, !generateMipMaps, invertY);
+
+        if (!this._engine) {
+            return;
+        }
 
-        this._texture = scene.getEngine().createRawTexture(data, width, height, format, generateMipMaps, invertY, samplingMode, null, type);
+        this._texture = this._engine.createRawTexture(data, width, height, format, generateMipMaps, invertY, samplingMode, null, type);
 
         this.wrapU = Texture.CLAMP_ADDRESSMODE;
         this.wrapV = Texture.CLAMP_ADDRESSMODE;
@@ -51,14 +58,14 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @returns the luminance texture
      */
-    public static CreateLuminanceTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_LUMINANCE, scene, generateMipMaps, invertY, samplingMode);
+    public static CreateLuminanceTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_LUMINANCE, sceneOrEngine, generateMipMaps, invertY, samplingMode);
     }
 
     /**
@@ -66,14 +73,14 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @returns the luminance alpha texture
      */
-    public static CreateLuminanceAlphaTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_LUMINANCE_ALPHA, scene, generateMipMaps, invertY, samplingMode);
+    public static CreateLuminanceAlphaTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_LUMINANCE_ALPHA, sceneOrEngine, generateMipMaps, invertY, samplingMode);
     }
 
     /**
@@ -81,14 +88,14 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @returns the alpha texture
      */
-    public static CreateAlphaTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_ALPHA, scene, generateMipMaps, invertY, samplingMode);
+    public static CreateAlphaTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_ALPHA, sceneOrEngine, generateMipMaps, invertY, samplingMode);
     }
 
     /**
@@ -96,15 +103,15 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @param type define the format of the data (int, float... Engine.TEXTURETYPE_xxx)
      * @returns the RGB alpha texture
      */
-    public static CreateRGBTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_RGB, scene, generateMipMaps, invertY, samplingMode, type);
+    public static CreateRGBTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_RGB, sceneOrEngine, generateMipMaps, invertY, samplingMode, type);
     }
 
     /**
@@ -112,15 +119,15 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @param type define the format of the data (int, float... Engine.TEXTURETYPE_xxx)
      * @returns the RGBA texture
      */
-    public static CreateRGBATexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_RGBA, scene, generateMipMaps, invertY, samplingMode, type);
+    public static CreateRGBATexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_RGBA, sceneOrEngine, generateMipMaps, invertY, samplingMode, type);
     }
 
     /**
@@ -128,14 +135,14 @@ export class RawTexture extends Texture {
      * @param data Define the texture data
      * @param width Define the width of the texture
      * @param height Define the height of the texture
-     * @param scene Define the scene the texture belongs to
+     * @param sceneOrEngine defines the scene or engine the texture will belong to
      * @param generateMipMaps Define whether or not to create mip maps for the texture
      * @param invertY define if the data should be flipped on Y when uploaded to the GPU
      * @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
      * @param type define the format of the data (int, float... Engine.TEXTURETYPE_xxx)
      * @returns the R texture
      */
-    public static CreateRTexture(data: ArrayBufferView, width: number, height: number, scene: Scene, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_FLOAT): RawTexture {
-        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_R, scene, generateMipMaps, invertY, samplingMode, type);
+    public static CreateRTexture(data: ArrayBufferView, width: number, height: number, sceneOrEngine: Nullable<Scene | ThinEngine>, generateMipMaps: boolean = true, invertY: boolean = false, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, type: number = Constants.TEXTURETYPE_FLOAT): RawTexture {
+        return new RawTexture(data, width, height, Constants.TEXTUREFORMAT_R, sceneOrEngine, generateMipMaps, invertY, samplingMode, type);
     }
 }

+ 1 - 0
src/Materials/Textures/texture.ts

@@ -796,4 +796,5 @@ export class Texture extends BaseTexture {
 }
 
 // References the dependencies.
+_TypeStore.RegisteredTypes["BABYLON.Texture"] = Texture;
 SerializationHelper._TextureParser = Texture.Parse;

+ 1 - 0
src/Materials/index.ts

@@ -7,6 +7,7 @@ export * from "./fresnelParameters";
 export * from "./imageProcessingConfiguration";
 export * from "./material";
 export * from "./materialDefines";
+export * from "./thinMaterialHelper";
 export * from "./materialHelper";
 export * from "./multiMaterial";
 export * from "./PBR/index";

+ 0 - 0
src/Materials/material.detailMapConfiguration.ts


Some files were not shown because too many files changed in this diff