Prechádzať zdrojové kódy

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe 7 rokov pred
rodič
commit
1bf24e5be2

+ 2 - 0
Playground/js/index.js

@@ -458,6 +458,8 @@
 
             } catch (e) {
                 showError(e.message, e);
+                // Also log error in console to help debug playgrounds
+                console.error(e);
             }
         };
         window.addEventListener("resize",

+ 5 - 3
Tools/Gulp/config.json

@@ -1024,7 +1024,11 @@
                 "../../src/Rendering/babylon.utilityLayerRenderer.js",
                 "../../src/Gizmos/babylon.gizmo.js",
                 "../../src/Gizmos/babylon.axisDragGizmo.js",
-                "../../src/Gizmos/babylon.positionGizmo.js"
+                "../../src/Gizmos/babylon.axisScaleGizmo.js",
+                "../../src/Gizmos/babylon.planeRotationGizmo.js",
+                "../../src/Gizmos/babylon.positionGizmo.js",
+                "../../src/Gizmos/babylon.rotationGizmo.js",
+                "../../src/Gizmos/babylon.scaleGizmo.js"
             ],
             "dependUpon": [
                 "shaderMaterial",
@@ -1560,7 +1564,6 @@
             {
                 "files": [
                     "../../loaders/src/glTF/babylon.glTFFileLoader.ts",
-                    "../../loaders/src/glTF/2.0/babylon.glTFLoaderUtilities.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoader.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts",
@@ -1585,7 +1588,6 @@
                     "../../loaders/src/glTF/1.0/babylon.glTFLoaderExtension.ts",
                     "../../loaders/src/glTF/1.0/babylon.glTFBinaryExtension.ts",
                     "../../loaders/src/glTF/1.0/babylon.glTFMaterialsCommonExtension.ts",
-                    "../../loaders/src/glTF/2.0/babylon.glTFLoaderUtilities.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoader.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts",

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

@@ -22,7 +22,7 @@
 - UtilityLayer class to render another scene as a layer on top of an existing scene ([TrevorDev](https://github.com/TrevorDev))
 - AnimationGroup has now onAnimationGroupEnd observable ([RaananW](https://github.com/RaananW))
 - Pointer drag behavior to enable drag and drop with mouse or 6dof controller on a mesh ([TrevorDev](https://github.com/TrevorDev))
-- Gizmo class used to manipulate meshes in a scene, position gizmo ([TrevorDev](https://github.com/TrevorDev))
+- Gizmo class used to manipulate meshes in a scene, position, rotation, scale gizmos ([TrevorDev](https://github.com/TrevorDev))
 - Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
 
 ### glTF Loader

+ 11 - 2
inspector/src/tabs/GLTFTab.ts

@@ -19,6 +19,10 @@ module INSPECTOR {
         private _detailsPanel: DetailPanel | null = null;
         private _split: any;
 
+        public static get IsSupported(): boolean {
+            return !!(BABYLON.SceneLoader && BABYLON.GLTFFileLoader && BABYLON.GLTF2.GLTFLoader) || !!BABYLON.GLTF2Export;
+        }
+
         /** @hidden */
         public static _Initialize(): void {
             // Must register with OnPluginActivatedObservable as early as possible to
@@ -45,8 +49,13 @@ module INSPECTOR {
                 this._closeDetailsPanel();
             });
 
-            this._addImport();
-            this._addExport();
+            if (BABYLON.SceneLoader && BABYLON.GLTFFileLoader && BABYLON.GLTF2.GLTFLoader) {
+                this._addImport();
+            }
+
+            if (BABYLON.GLTF2Export) {
+                this._addExport();
+            }
         }
 
         public dispose() {

+ 1 - 1
inspector/src/tabs/TabBar.ts

@@ -32,7 +32,7 @@ module INSPECTOR {
             this._tabs.push(this._meshTab);
             this._tabs.push(new LightTab(this, this._inspector));
             this._tabs.push(new MaterialTab(this, this._inspector));
-            if (BABYLON.GLTF2Export) {
+            if (GLTFTab.IsSupported) {
                 this._tabs.push(new GLTFTab(this, this._inspector));
             }
             if (BABYLON.GUI) {

+ 1 - 1
loaders/src/glTF/1.0/babylon.glTFLoader.ts

@@ -1820,5 +1820,5 @@ module BABYLON.GLTF1 {
         }
     };
 
-    GLTFFileLoader.CreateGLTFLoaderV1 = () => new GLTFLoader();
+    GLTFFileLoader._CreateGLTFLoaderV1 = () => new GLTFLoader();
 }

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

@@ -18,7 +18,7 @@ module BABYLON.GLTF2.Extensions {
         public readonly name = NAME;
 
         /** @hidden */
-        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             return this._loadExtensionAsync<IKHRMaterialsPbrSpecularGlossiness>(context, material, (extensionContext, extension) => {
                 material._babylonData = material._babylonData || {};
                 let babylonData = material._babylonData[babylonDrawMode];

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

@@ -9,7 +9,7 @@ module BABYLON.GLTF2.Extensions {
     export class KHR_materials_unlit extends GLTFLoaderExtension {
         public readonly name = NAME;
 
-        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             return this._loadExtensionAsync<{}>(context, material, () => {
                 material._babylonData = material._babylonData || {};
                 let babylonData = material._babylonData[babylonDrawMode];

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

@@ -121,7 +121,7 @@ module BABYLON.GLTF2.Extensions {
             });
         }
 
-        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             // Don't load material LODs if already loading a node LOD.
             if (this._loadingNodeLOD) {
                 return null;
@@ -142,7 +142,7 @@ module BABYLON.GLTF2.Extensions {
                         }
                     }
 
-                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD._index}`, materialLOD, babylonMesh, babylonDrawMode, indexLOD === 0 ? assign : () => {}).then(() => {
+                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD._index}`, materialLOD, mesh, babylonMesh, babylonDrawMode, indexLOD === 0 ? assign : () => {}).then(() => {
                         if (indexLOD !== 0) {
                             const babylonDataLOD = materialLOD._babylonData!;
                             assign(babylonDataLOD[babylonDrawMode].material);

+ 16 - 20
loaders/src/glTF/2.0/Extensions/MSFT_minecraftMesh.ts

@@ -7,29 +7,25 @@ module BABYLON.GLTF2.Extensions {
     export class MSFT_minecraftMesh extends GLTFLoaderExtension {
         public readonly name = NAME;
 
-        constructor(loader: GLTFLoader) {
-            super(loader);
+        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+            return this._loadExtrasValueAsync<boolean>(context, mesh, (extensionContext, value) => {
+                if (value) {
+                    return this._loader._loadMaterialAsync(context, material, mesh, babylonMesh, babylonDrawMode, (babylonMaterial: PBRMaterial) => {
+                        if (babylonMaterial.needAlphaBlending()) {
+                            babylonMaterial.forceDepthWrite = true;
+                            babylonMaterial.separateCullingPass = true;
+                        }
 
-            const meshes = loader._gltf.meshes;
-            if (meshes && meshes.length) {
-                for (const mesh of meshes) {
-                    if (mesh && mesh.extras && mesh.extras.MSFT_minecraftMesh) {
-                        this._loader.onMaterialLoadedObservable.add(this._onMaterialLoaded);
-                        break;
-                    }
-                }
-            }
-        }
+                        babylonMaterial.backFaceCulling = babylonMaterial.forceDepthWrite;
+                        babylonMaterial.twoSidedLighting = true;
 
-        private _onMaterialLoaded = (material: PBRMaterial): void => {
-            if (material.needAlphaBlending()) {
-                material.forceDepthWrite = true;
-                material.separateCullingPass = true;
-            }
+                        assign(babylonMaterial);
+                    });
+                }
 
-            material.backFaceCulling = material.forceDepthWrite;
-            material.twoSidedLighting = true;
-        };
+                return null;
+            });
+        }
     }
 
     GLTFLoader._Register(NAME, loader => new MSFT_minecraftMesh(loader));

+ 16 - 20
loaders/src/glTF/2.0/Extensions/MSFT_sRGBFactors.ts

@@ -7,29 +7,25 @@ module BABYLON.GLTF2.Extensions {
     export class MSFT_sRGBFactors extends GLTFLoaderExtension {
         public readonly name = NAME;
 
-        constructor(loader: GLTFLoader) {
-            super(loader);
+        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+            return this._loadExtrasValueAsync<boolean>(context, material, (extensionContext, value) => {
+                if (value) {
+                    return this._loader._loadMaterialAsync(context, material, mesh, babylonMesh, babylonDrawMode, (babylonMaterial: PBRMaterial) => {
+                        if (!babylonMaterial.albedoTexture) {
+                            babylonMaterial.albedoColor.toLinearSpaceToRef(babylonMaterial.albedoColor);
+                        }
 
-            const materials = loader._gltf.materials;
-            if (materials && materials.length) {
-                for (const material of materials) {
-                    if (material && material.extras && material.extras.MSFT_sRGBFactors) {
-                        this._loader.onMaterialLoadedObservable.add(this._onMaterialLoaded);
-                        break;
-                    }
-                }
-            }
-        }
+                        if (!babylonMaterial.reflectivityTexture) {
+                            babylonMaterial.reflectivityColor.toLinearSpaceToRef(babylonMaterial.reflectivityColor);
+                        }
 
-        private _onMaterialLoaded = (material: PBRMaterial): void => {
-            if (!material.albedoTexture) {
-                material.albedoColor.toLinearSpaceToRef(material.albedoColor);
-            }
+                        assign(babylonMaterial);
+                    });
+                }
 
-            if (!material.reflectivityTexture) {
-                material.reflectivityColor.toLinearSpaceToRef(material.reflectivityColor);
-            }
-        };
+                return null;
+            });
+        }
     }
 
     GLTFLoader._Register(NAME, loader => new MSFT_sRGBFactors(loader));

+ 17 - 7
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -10,6 +10,16 @@ module BABYLON.GLTF2 {
         _total?: number;
     }
 
+    class _ArrayItem {
+        public static Assign(values?: _IArrayItem[]): void {
+            if (values) {
+                for (let index = 0; index < values.length; index++) {
+                    values[index]._index = index;
+                }
+            }
+        }
+    }
+
     /**
      * Loader for loading a glTF 2.0 asset
      */
@@ -520,7 +530,7 @@ module BABYLON.GLTF2 {
 
             const promises = new Array<Promise<void>>();
 
-            const babylonMesh = new Mesh(node.name || `node${node._index}`, this._babylonScene, node._parent._babylonMesh);
+            const babylonMesh = new Mesh(node.name || `node${node._index}`, this._babylonScene, node._parent ? node._parent._babylonMesh : null);
             node._babylonMesh = babylonMesh;
 
             GLTFLoader._LoadTransform(node, babylonMesh);
@@ -598,7 +608,7 @@ module BABYLON.GLTF2 {
             }
             else {
                 const material = GLTFLoader._GetProperty(`${context}/material}`, this._gltf.materials, primitive.material);
-                promises.push(this._loadMaterialAsync(`#/materials/${material._index}`, material, babylonMesh, babylonDrawMode, babylonMaterial => {
+                promises.push(this._loadMaterialAsync(`#/materials/${material._index}`, material, mesh, babylonMesh, babylonDrawMode, babylonMaterial => {
                     babylonMesh.material = babylonMaterial;
                 }));
             }
@@ -822,7 +832,7 @@ module BABYLON.GLTF2 {
             }
 
             let babylonParentBone: Nullable<Bone> = null;
-            if (node._parent._babylonMesh !== this._rootBabylonMesh) {
+            if (node._parent && node._parent._babylonMesh !== this._rootBabylonMesh) {
                 babylonParentBone = this._loadBone(node._parent, skin, babylonBones);
             }
 
@@ -1336,8 +1346,8 @@ module BABYLON.GLTF2 {
         }
 
         /** @hidden */
-        public _loadMaterialAsync(context: string, material: _ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, babylonDrawMode, assign);
+        public _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Promise<void> {
+            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
             if (promise) {
                 return promise;
             }
@@ -1347,7 +1357,7 @@ module BABYLON.GLTF2 {
             if (!babylonData) {
                 const promises = new Array<Promise<void>>();
 
-                const name = material.name || `materialSG_${material._index}`;
+                const name = material.name || `material_${material._index}`;
                 const babylonMaterial = this._createMaterial(name, babylonDrawMode);
 
                 promises.push(this._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
@@ -1799,5 +1809,5 @@ module BABYLON.GLTF2 {
         }
     }
 
-    GLTFFileLoader.CreateGLTFLoaderV2 = () => new GLTFLoader();
+    GLTFFileLoader._CreateGLTFLoaderV2 = () => new GLTFLoader();
 }

+ 52 - 13
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -5,9 +5,14 @@ module BABYLON.GLTF2 {
      * Abstract class that can be implemented to extend existing glTF loader behavior.
      */
     export abstract class GLTFLoaderExtension implements IGLTFLoaderExtension, IDisposable {
-        /** Gets or sets a boolean indicating if the extension is enabled */
+        /**
+         * Gets or sets a boolean indicating if the extension is enabled
+         */
         public enabled = true;
-        /** Gets or sets extension name */
+
+        /**
+         * Gets or sets extension name
+         */
         public abstract readonly name: string;
 
         protected _loader: GLTFLoader;
@@ -20,15 +25,17 @@ module BABYLON.GLTF2 {
             this._loader = loader;
         }
 
-        /** Release all resources */
+        /**
+         * Release all resources
+         */
         public dispose(): void {
             delete this._loader;
         }
 
         // #region Overridable Methods
 
-        /** 
-         * Override this method to modify the default behavior for loading scenes. 
+        /**
+         * Override this method to modify the default behavior for loading scenes.
          * @hidden
          */
         protected _loadSceneAsync(context: string, node: _ILoaderScene): Nullable<Promise<void>> { return null; }
@@ -39,18 +46,22 @@ module BABYLON.GLTF2 {
          */
         protected _loadNodeAsync(context: string, node: _ILoaderNode): Nullable<Promise<void>> { return null; }
 
-        /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
+        /**
+         * Override this method to modify the default behavior for loading mesh primitive vertex data.
+         * @hidden
+         */
         protected _loadVertexDataAsync(context: string, primitive: _ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<Geometry>> { return null; }
 
-        /** Override this method to modify the default behavior for loading materials. 
+        /**
+         * Override this method to modify the default behavior for loading materials.
          * @hidden
          */
-        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> { return null; }
+        protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> { return null; }
 
         /** 
          * Override this method to modify the default behavior for loading textures. 
          * @hidden
-         */ 
+         */
         protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>> { return null; }
 
         /** 
@@ -65,7 +76,7 @@ module BABYLON.GLTF2 {
          * Helper method called by a loader extension to load an glTF extension. 
          * @hidden
          */
-        protected _loadExtensionAsync<TProperty, TResult = void>(context: string, property: IProperty, actionAsync: (extensionContext: string, extension: TProperty) => Promise<TResult>): Nullable<Promise<TResult>> {
+        protected _loadExtensionAsync<TProperty, TResult = void>(context: string, property: IProperty, actionAsync: (extensionContext: string, extension: TProperty) => Nullable<Promise<TResult>>): Nullable<Promise<TResult>> {
             if (!property.extensions) {
                 return null;
             }
@@ -77,7 +88,7 @@ module BABYLON.GLTF2 {
                 return null;
             }
 
-            // Clear out the extension before executing the action to avoid recursing into the same property.
+            // Clear out the extension before executing the action to avoid infinite recursion.
             delete extensions[this.name];
 
             try {
@@ -89,6 +100,34 @@ module BABYLON.GLTF2 {
             }
         }
 
+        /**
+         * Helper method called by the loader to allow extensions to override loading scenes.
+         * @hidden
+         */
+        protected _loadExtrasValueAsync<TProperty, TResult = void>(context: string, property: IProperty, actionAsync: (extensionContext: string, value: TProperty) => Nullable<Promise<TResult>>): Nullable<Promise<TResult>> {
+            if (!property.extras) {
+                return null;
+            }
+
+            const extras = property.extras;
+
+            const value = extras[this.name] as TProperty;
+            if (value === undefined) {
+                return null;
+            }
+
+            // Clear out the extras value before executing the action to avoid infinite recursion.
+            delete extras[this.name];
+
+            try {
+                return actionAsync(`${context}/extras/${this.name}`, value);
+            }
+            finally {
+                // Restore the extras value after executing the action.
+                extras[this.name] = value;
+            }
+        }
+
         /** 
          * Helper method called by the loader to allow extensions to override loading scenes. 
          * @hidden
@@ -117,8 +156,8 @@ module BABYLON.GLTF2 {
          * Helper method called by the loader to allow extensions to override loading materials. 
          * @hidden
          */
-        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
-            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign));
+        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, mesh, babylonMesh, babylonDrawMode, assign));
         }
 
         /** 

+ 7 - 2
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -3,6 +3,11 @@
 
 module BABYLON.GLTF2 {
     /** @hidden */
+    export interface _IArrayItem {
+        _index: number;
+    }
+
+    /** @hidden */
     export interface _ILoaderAccessor extends IAccessor, _IArrayItem {
         _data?: Promise<ArrayBufferView>;
         _babylonVertexBuffer?: Promise<VertexBuffer>;
@@ -21,7 +26,7 @@ module BABYLON.GLTF2 {
 
     /** @hidden */
     export interface _ILoaderAnimationSampler extends IAnimationSampler, _IArrayItem {
-        _data: Promise<_ILoaderAnimationSamplerData>;
+        _data?: Promise<_ILoaderAnimationSamplerData>;
     }
 
     /** @hidden */
@@ -74,7 +79,7 @@ module BABYLON.GLTF2 {
 
     /** @hidden */
     export interface _ILoaderNode extends INode, _IArrayItem {
-        _parent: _ILoaderNode;
+        _parent?: _ILoaderNode;
         _babylonMesh?: Mesh;
         _primitiveBabylonMeshes?: Mesh[];
         _babylonBones?: Bone[];

+ 0 - 20
loaders/src/glTF/2.0/babylon.glTFLoaderUtilities.ts

@@ -1,20 +0,0 @@
-/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
-
-module BABYLON.GLTF2 {
-    /** @hidden */
-    export interface _IArrayItem {
-        _index: number;
-    }
-
-    /** @hidden */
-    export class _ArrayItem {
-        /** @hidden */
-        public static Assign(values?: _IArrayItem[]): void {
-            if (values) {
-                for (let index = 0; index < values.length; index++) {
-                    values[index]._index = index;
-                }
-            }
-        }
-    }
-}

+ 6 - 4
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -190,13 +190,15 @@ module BABYLON {
     export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         /**
          * Factory function that creates a glTF 1.0 loader
+         * @hidden
          */
-        public static CreateGLTFLoaderV1: () => IGLTFLoader;
+        public static _CreateGLTFLoaderV1: () => IGLTFLoader;
 
         /**
          * Factory function that creates a glTF 2.0 loader
+         * @hidden
          */
-        public static CreateGLTFLoaderV2: () => IGLTFLoader;
+        public static _CreateGLTFLoaderV2: () => IGLTFLoader;
 
         // #region Common options
 
@@ -577,8 +579,8 @@ module BABYLON {
             }
 
             const createLoaders: { [key: number]: () => IGLTFLoader } = {
-                1: GLTFFileLoader.CreateGLTFLoaderV1,
-                2: GLTFFileLoader.CreateGLTFLoaderV2
+                1: GLTFFileLoader._CreateGLTFLoaderV1,
+                2: GLTFFileLoader._CreateGLTFLoaderV2
             };
 
             const createLoader = createLoaders[version.major];

+ 1 - 0
sandbox/index.html

@@ -35,6 +35,7 @@
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
 
     <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
+    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
     <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
 </head>
 <body>

+ 23 - 10
sandbox/index.js

@@ -209,12 +209,16 @@ if (BABYLON.Engine.isSupported()) {
         });
     };
 
-    if (assetUrl) {
+    var loadFromAssetUrl = function () {
         var rootUrl = BABYLON.Tools.GetFolderPath(assetUrl);
         var fileName = BABYLON.Tools.GetFilename(assetUrl);
         BABYLON.SceneLoader.LoadAsync(rootUrl, fileName, engine).then(function (scene) {
+            if (currentScene) {
+                currentScene.dispose();
+            }
+
             sceneLoaded({ name: fileName }, scene);
-            currentScene = scene;
+
             scene.whenReadyAsync().then(function () {
                 engine.runRenderLoop(function () {
                     scene.render();
@@ -223,6 +227,10 @@ if (BABYLON.Engine.isSupported()) {
         }).catch(function (reason) {
             sceneError({ name: fileName }, null, reason.message || reason);
         });
+    };
+
+    if (assetUrl) {
+        loadFromAssetUrl();
     }
     else {
         filesInput = new BABYLON.FilesInput(engine, null, sceneLoaded, null, null, null, function () { BABYLON.Tools.ClearLogCache() }, null, sceneError);
@@ -236,14 +244,6 @@ if (BABYLON.Engine.isSupported()) {
         }).bind(this);
         filesInput.monitorElementForDragNDrop(canvas);
 
-        window.addEventListener("keydown", function (event) {
-            // Press R to reload
-            if (event.keyCode === 82 && event.target.nodeName !== "INPUT") {
-                debugLayerLastActiveTab = currentScene.debugLayer.getActiveTab();
-                filesInput.reload();
-            }
-        });
-
         htmlInput.addEventListener('change', function (event) {
             var filestoLoad;
             // Handling data transfer via drag'n'drop
@@ -258,6 +258,19 @@ if (BABYLON.Engine.isSupported()) {
         }, false);
     }
 
+    window.addEventListener("keydown", function (event) {
+        // Press R to reload
+        if (event.keyCode === 82 && event.target.nodeName !== "INPUT") {
+            debugLayerLastActiveTab = currentScene.debugLayer.getActiveTab();
+            if (assetUrl) {
+                loadFromAssetUrl();
+            }
+            else {
+                filesInput.reload();
+            }
+        }
+    });
+
     if (kiosk) {
         footer.style.display = "none";
     }

+ 10 - 2
src/Behaviors/Mesh/babylon.pointerDragBehavior.ts

@@ -30,6 +30,11 @@ module BABYLON {
          *  Mesh with the position where the drag plane should be placed
          */
         public _dragPlaneParent:Nullable<Mesh>=null;
+
+        /**
+         *  If the drag behavior will react to drag events
+         */
+        public enabled = true;
         
         /**
          * Creates a pointer drag behavior that can be attached to a mesh
@@ -91,6 +96,9 @@ module BABYLON {
             }
 
             this._pointerObserver = this.options.pointerObservableScene!.onPrePointerObservable.add((pointerInfoPre, eventState)=>{
+                if(!this.enabled){
+                    return;
+                }
                 // Check if attached mesh is picked
                 var pickInfo = pointerInfoPre.ray ? this._scene.pickWithRay(pointerInfoPre.ray, pickPredicate) : this._scene.pick(this._scene.pointerX, this._scene.pointerY, pickPredicate);
                 if(pickInfo){
@@ -98,8 +106,8 @@ module BABYLON {
                     if(!pickInfo.ray){
                         pickInfo.ray = this.options.pointerObservableScene!.createPickingRay(this._scene.pointerX, this._scene.pointerY, Matrix.Identity(), this._scene.activeCamera);
                     }
-                    if(pickInfo.hit){
-                        eventState.skipNextObservers = true;
+                    if(pickInfo.hit && pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERDOWN){
+                        pointerInfoPre.skipOnPointerObservable = true;
                     }
                 }
                 

+ 6 - 0
src/Gizmos/babylon.axisDragGizmo.ts

@@ -39,11 +39,17 @@ module BABYLON {
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
             this._dragBehavior.onDragObservable.add((event)=>{
+                if(!this.interactionsEnabled){
+                    return;
+                }
                 if(this.attachedMesh){
                     this.attachedMesh.position.addInPlace(event.delta);
                 }
             })
         }
+        protected _onInteractionsEnabledChanged(value:boolean){
+            this._dragBehavior.enabled = value;
+        }
         /**
          * Disposes of the gizmo
          */

+ 64 - 0
src/Gizmos/babylon.axisScaleGizmo.ts

@@ -0,0 +1,64 @@
+module BABYLON {
+    /**
+     * Single axis scale gizmo
+     */
+    export class AxisScaleGizmo extends Gizmo {
+        private _dragBehavior:PointerDragBehavior;
+        /**
+         * Creates an AxisScaleGizmo
+         * @param gizmoLayer The utility layer the gizmo will be added to
+         * @param dragAxis The axis which the gizmo will be able to scale on
+         * @param color The color of the gizmo
+         */
+        constructor(gizmoLayer:UtilityLayerRenderer, dragAxis:Vector3, color:Color3){
+            super(gizmoLayer);
+
+            // Create Material
+            var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
+            coloredMaterial.disableLighting = true;
+            coloredMaterial.emissiveColor = color;
+
+            // Build mesh on root node
+            var arrowMesh = BABYLON.MeshBuilder.CreateBox("yPosMesh", {size: 1}, gizmoLayer.utilityLayerScene);
+            var arrowTail = BABYLON.MeshBuilder.CreateCylinder("yPosMesh", {diameter:0.03, height: 0.2, tessellation: 96}, gizmoLayer.utilityLayerScene);
+            this._rootMesh.addChild(arrowMesh);
+            this._rootMesh.addChild(arrowTail);
+
+            // Position arrow pointing in its drag axis
+            arrowMesh.scaling.scaleInPlace(0.1);
+            arrowMesh.material = coloredMaterial;
+            arrowMesh.rotation.x = Math.PI/2;
+            arrowMesh.position.z+=0.3;
+            arrowTail.rotation.x = Math.PI/2;
+            arrowTail.material = coloredMaterial;
+            arrowTail.position.z+=0.2;
+            this._rootMesh.lookAt(this._rootMesh.position.subtract(dragAxis));
+
+            // Add drag behavior to handle events when the gizmo is dragged
+            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis, pointerObservableScene: gizmoLayer.originalScene});
+            this._dragBehavior.moveAttached = false;
+            this._rootMesh.addBehavior(this._dragBehavior);
+
+            this._dragBehavior.onDragObservable.add((event)=>{
+                if(!this.interactionsEnabled){
+                    return;
+                }
+                if(this.attachedMesh){
+                    this.attachedMesh.scaling.addInPlace(event.delta);
+                }
+            })
+        }
+        
+        protected _onInteractionsEnabledChanged(value:boolean){
+            this._dragBehavior.enabled = value;
+        }
+        
+        /**
+         * Disposes of the gizmo
+         */
+        public dispose(){
+            this._dragBehavior.detach();
+            super.dispose();
+        } 
+    }
+}

+ 18 - 2
src/Gizmos/babylon.gizmo.ts

@@ -10,8 +10,24 @@ module BABYLON {
         /**
          * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
          */
-        public attachedMesh:Nullable<Mesh>;
+        public attachedMesh:Nullable<AbstractMesh>;
+        protected _interactionsEnabled = true;
+        protected _onInteractionsEnabledChanged(value:boolean){
+        }
+
+        /**
+         * If interactions are enabled with this gizmo. (eg. dragging/rotation)
+         */
+        public set interactionsEnabled(value:boolean){
+            this._interactionsEnabled = value;
+            this._onInteractionsEnabledChanged(value);
+        }
+        public get interactionsEnabled(){
+            return this._interactionsEnabled;
+        }
+
         private _beforeRenderObserver:Nullable<Observer<Scene>>;
+        
         /**
          * Creates a gizmo
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -20,7 +36,7 @@ module BABYLON {
             this._rootMesh = new BABYLON.Mesh("gizmoRootNode",gizmoLayer.utilityLayerScene);
             this._beforeRenderObserver = this.gizmoLayer.utilityLayerScene.onBeforeRenderObservable.add(()=>{
                 if(this.gizmoLayer.utilityLayerScene.activeCamera && this.attachedMesh){
-                    var dist = this.attachedMesh.position.subtract(this.gizmoLayer.utilityLayerScene.activeCamera.position).length()/5;
+                    var dist = this.attachedMesh.position.subtract(this.gizmoLayer.utilityLayerScene.activeCamera.position).length()/3;
                     this._rootMesh.scaling.set(dist, dist, dist);
                 }
                 if(this.attachedMesh){

+ 95 - 0
src/Gizmos/babylon.planeRotationGizmo.ts

@@ -0,0 +1,95 @@
+module BABYLON {
+    /**
+     * Single plane rotation gizmo
+     */
+    export class PlaneRotationGizmo extends Gizmo {
+        private _dragBehavior:PointerDragBehavior;
+        /**
+         * Creates a PlaneRotationGizmo
+         * @param gizmoLayer The utility layer the gizmo will be added to
+         * @param planeNormal The normal of the plane which the gizmo will be able to rotate on
+         * @param color The color of the gizmo
+         */
+        constructor(gizmoLayer:UtilityLayerRenderer, planeNormal:Vector3, color:Color3){
+            super(gizmoLayer);
+
+            // Create Material
+            var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
+            coloredMaterial.disableLighting = true;
+            coloredMaterial.emissiveColor = color;
+
+            // Build mesh on root node
+            var rotationMesh = BABYLON.Mesh.CreateTorus("torus", 3, 0.3, 20, gizmoLayer.utilityLayerScene, false);
+            this._rootMesh.addChild(rotationMesh);
+
+            // Position arrow pointing in its drag axis
+            rotationMesh.scaling.scaleInPlace(0.1);
+            rotationMesh.material = coloredMaterial;
+            rotationMesh.rotation.x = Math.PI/2;
+            this._rootMesh.lookAt(this._rootMesh.position.subtract(planeNormal));
+
+            // Add drag behavior to handle events when the gizmo is dragged
+            this._dragBehavior = new PointerDragBehavior({dragPlaneNormal: planeNormal, pointerObservableScene: gizmoLayer.originalScene});
+            this._dragBehavior.moveAttached = false;
+            this._rootMesh.addBehavior(this._dragBehavior);
+
+            var lastDragPosition:Nullable<Vector3> = null;
+
+            this._dragBehavior.onDragStartObservable.add((e)=>{
+                if(!this.interactionsEnabled){
+                    return;
+                }
+                lastDragPosition = e.dragPlanePoint;
+            })
+
+            this._dragBehavior.onDragObservable.add((event)=>{
+                if(!this.interactionsEnabled){
+                    return;
+                }
+                if(this.attachedMesh && lastDragPosition){
+                    if(!this.attachedMesh.rotationQuaternion){
+                        this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
+                    }
+                    // Calc angle over full 360 degree (https://stackoverflow.com/questions/43493711/the-angle-between-two-3d-vectors-with-a-result-range-0-360)
+                    var newVector = event.dragPlanePoint.subtract(this.attachedMesh.position).normalize();
+                    var originalVector = lastDragPosition.subtract(this.attachedMesh.position).normalize();
+                    var cross = Vector3.Cross(newVector,originalVector);
+                    var dot = Vector3.Dot(newVector,originalVector);
+                    var angle = Math.atan2(cross.length(), dot);
+                    var up = planeNormal.clone();
+                    // Flip up vector depending on which side the camera is on
+                    if(gizmoLayer.utilityLayerScene.activeCamera){
+                        var camVec = gizmoLayer.utilityLayerScene.activeCamera.position.subtract(this.attachedMesh.position);
+                        if(Vector3.Dot(camVec, up) > 0){
+                            up.scaleInPlace(-1);
+                        }
+                    }
+                    var halfCircleSide = Vector3.Dot(up, cross) > 0.0;
+                    if (halfCircleSide) angle = -angle;
+                    
+
+                    // Convert angle and axis to quaternion (http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm)
+                    var quaternionCoefficient = Math.sin(angle/2)
+                    var amountToRotate = new BABYLON.Quaternion(up.x*quaternionCoefficient,up.y*quaternionCoefficient,up.z*quaternionCoefficient,Math.cos(angle/2));
+
+                    // Rotate selected mesh quaternion over fixed axis
+                    amountToRotate.multiplyToRef(this.attachedMesh.rotationQuaternion,this.attachedMesh.rotationQuaternion);
+
+                    lastDragPosition = event.dragPlanePoint;
+                }
+            })
+        }
+
+        protected _onInteractionsEnabledChanged(value:boolean){
+            this._dragBehavior.enabled = value;
+        }
+
+        /**
+         * Disposes of the gizmo
+         */
+        public dispose(){
+            this._dragBehavior.detach();
+            super.dispose();
+        } 
+    }
+}

+ 7 - 1
src/Gizmos/babylon.positionGizmo.ts

@@ -7,7 +7,7 @@ module BABYLON {
         private _yDrag:AxisDragGizmo;
         private _zDrag:AxisDragGizmo;
 
-        public set attachedMesh(mesh:Nullable<Mesh>){
+        public set attachedMesh(mesh:Nullable<AbstractMesh>){
             this._xDrag.attachedMesh = mesh;
             this._yDrag.attachedMesh = mesh;
             this._zDrag.attachedMesh = mesh;
@@ -23,6 +23,12 @@ module BABYLON {
             this._zDrag = new AxisDragGizmo(gizmoLayer, new Vector3(0,0,1), BABYLON.Color3.FromHexString("#0984e3"));
         }
 
+        protected _onInteractionsEnabledChanged(value:boolean){
+            this._xDrag.interactionsEnabled = value
+            this._yDrag.interactionsEnabled = value
+            this._zDrag.interactionsEnabled = value
+        }
+        
         /**
          * Disposes of the gizmo
          */

+ 41 - 0
src/Gizmos/babylon.rotationGizmo.ts

@@ -0,0 +1,41 @@
+module BABYLON {
+    /**
+     * Gizmo that enables rotating a mesh along 3 axis
+     */
+    export class RotationGizmo extends Gizmo {
+        private _xDrag:PlaneRotationGizmo;
+        private _yDrag:PlaneRotationGizmo;
+        private _zDrag:PlaneRotationGizmo;
+
+        public set attachedMesh(mesh:Nullable<AbstractMesh>){
+            this._xDrag.attachedMesh = mesh;
+            this._yDrag.attachedMesh = mesh;
+            this._zDrag.attachedMesh = mesh;
+        }
+        /**
+         * Creates a RotationGizmo
+         * @param gizmoLayer The utility layer the gizmo will be added to
+         */
+        constructor(gizmoLayer:UtilityLayerRenderer){
+            super(gizmoLayer);
+            this._xDrag = new PlaneRotationGizmo(gizmoLayer, new Vector3(1,0,0), BABYLON.Color3.FromHexString("#00b894"));
+            this._yDrag = new PlaneRotationGizmo(gizmoLayer, new Vector3(0,1,0), BABYLON.Color3.FromHexString("#d63031"));
+            this._zDrag = new PlaneRotationGizmo(gizmoLayer, new Vector3(0,0,1), BABYLON.Color3.FromHexString("#0984e3"));
+        }
+
+        protected _onInteractionsEnabledChanged(value:boolean){
+            this._xDrag.interactionsEnabled = value
+            this._yDrag.interactionsEnabled = value
+            this._zDrag.interactionsEnabled = value
+        }
+
+        /**
+         * Disposes of the gizmo
+         */
+        public dispose(){
+            this._xDrag.dispose();
+            this._yDrag.dispose();
+            this._zDrag.dispose();
+        }
+    }
+}

+ 41 - 0
src/Gizmos/babylon.scaleGizmo.ts

@@ -0,0 +1,41 @@
+module BABYLON {
+    /**
+     * Gizmo that enables scaling a mesh along 3 axis
+     */
+    export class ScaleGizmo extends Gizmo {
+        private _xDrag:AxisScaleGizmo;
+        private _yDrag:AxisScaleGizmo;
+        private _zDrag:AxisScaleGizmo;
+
+        public set attachedMesh(mesh:Nullable<AbstractMesh>){
+            this._xDrag.attachedMesh = mesh;
+            this._yDrag.attachedMesh = mesh;
+            this._zDrag.attachedMesh = mesh;
+        }
+        /**
+         * Creates a ScaleGizmo
+         * @param gizmoLayer The utility layer the gizmo will be added to
+         */
+        constructor(gizmoLayer:UtilityLayerRenderer){
+            super(gizmoLayer);
+            this._xDrag = new AxisScaleGizmo(gizmoLayer, new Vector3(1,0,0), BABYLON.Color3.FromHexString("#00b894"));
+            this._yDrag = new AxisScaleGizmo(gizmoLayer, new Vector3(0,1,0), BABYLON.Color3.FromHexString("#d63031"));
+            this._zDrag = new AxisScaleGizmo(gizmoLayer, new Vector3(0,0,1), BABYLON.Color3.FromHexString("#0984e3"));
+        }
+
+        protected _onInteractionsEnabledChanged(value:boolean){
+            this._xDrag.interactionsEnabled = value
+            this._yDrag.interactionsEnabled = value
+            this._zDrag.interactionsEnabled = value
+        }
+
+        /**
+         * Disposes of the gizmo
+         */
+        public dispose(){
+            this._xDrag.dispose();
+            this._yDrag.dispose();
+            this._zDrag.dispose();
+        }
+    }
+}

+ 2 - 0
src/Rendering/babylon.utilityLayerRenderer.ts

@@ -36,6 +36,8 @@ module BABYLON {
             this._sceneDisposeObserver = this.originalScene.onDisposeObservable.add(()=>{
                 this.dispose();
             })
+            
+            this._updateCamera();
         }
 
         /**