Переглянути джерело

Merge branch 'babylon/master' into plugin-dependencies

Raanan Weber 8 роки тому
батько
коміт
4eb8b21940
61 змінених файлів з 12017 додано та 11217 видалено
  1. 2 2
      Playground/index-local.html
  2. 2 1
      Playground/scripts/scripts.txt
  3. 35 0
      Playground/scripts/webvr.js
  4. 1 0
      Tools/Gulp/config.json
  5. 1 0
      Tools/Gulp/gulpfile.js
  6. 2105 2076
      dist/preview release/babylon.d.ts
  7. 40 40
      dist/preview release/babylon.js
  8. 314 152
      dist/preview release/babylon.max.js
  9. 2105 2076
      dist/preview release/babylon.module.d.ts
  10. 44 44
      dist/preview release/babylon.worker.js
  11. 1919 1890
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  12. 49 49
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  13. 2346 2179
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  14. 1919 1890
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  15. 4 0
      dist/preview release/gui/babylon.gui.d.ts
  16. 39 2
      dist/preview release/gui/babylon.gui.js
  17. 2 2
      dist/preview release/gui/babylon.gui.min.js
  18. 4 0
      dist/preview release/gui/babylon.gui.module.d.ts
  19. 4 4
      dist/preview release/inspector/babylon.inspector.bundle.js
  20. 3 1
      dist/preview release/inspector/babylon.inspector.css
  21. 49 1
      dist/preview release/inspector/babylon.inspector.d.ts
  22. 153 174
      dist/preview release/inspector/babylon.inspector.js
  23. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  24. 11 9
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  25. 78 73
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  26. 2 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  27. 11 9
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  28. 78 73
      dist/preview release/loaders/babylon.glTFFileLoader.js
  29. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  30. 39 2
      gui/src/controls/inputText.ts
  31. 2 2
      inspector/index.html
  32. 3 1
      inspector/sass/_tabPanel.scss
  33. 2 1
      inspector/sass/main.scss
  34. 1 14
      inspector/src/adapters/CameraAdapter.ts
  35. 1 92
      inspector/src/adapters/GUIAdapter.ts
  36. 1 16
      inspector/src/adapters/LightAdapter.ts
  37. 1 14
      inspector/src/adapters/MaterialAdapter.ts
  38. 1 7
      inspector/src/adapters/MeshAdapter.ts
  39. 1 7
      inspector/src/adapters/PhysicsImpostorAdapter.ts
  40. 1 12
      inspector/src/adapters/SoundAdapter.ts
  41. 71 20
      inspector/src/details/PropertyLine.ts
  42. 0 1
      inspector/src/gui/ColorElement.ts
  43. 61 0
      inspector/src/gui/ColorPickerElement.ts
  44. 26 0
      inspector/src/helpers/Helpers.ts
  45. 94 86
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  46. 2 2
      loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts
  47. 5 1
      src/Animations/babylon.runtimeAnimation.ts
  48. 2 2
      src/Cameras/Inputs/babylon.arcRotateCameraKeyboardMoveInput.ts
  49. 78 24
      src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts
  50. 129 36
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  51. 24 26
      src/Cameras/VR/babylon.webVRCamera.ts
  52. 1 4
      src/Cameras/babylon.arcRotateCamera.ts
  53. 3 2
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  54. 5 5
      src/Materials/babylon.effect.ts
  55. 12 3
      src/Mesh/babylon.meshBuilder.ts
  56. 1 2
      src/Shaders/pbr.fragment.fx
  57. 97 66
      src/babylon.engine.ts
  58. BIN
      tests/validation/ReferenceImages/gltfnormals.png
  59. BIN
      tests/validation/ReferenceImages/normals.png
  60. BIN
      tests/validation/ReferenceImages/pbrrough.png
  61. 27 15
      tests/validation/config.json

+ 2 - 2
Playground/index-local.html

@@ -16,8 +16,8 @@
     <!--Monaco-->
     <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
     <!-- Babylon.js -->
-    <script src="https://babylonjs.azurewebsites.net/cannon.js"></script>
-    <script src="https://babylonjs.azurewebsites.net/Oimo.js"></script>
+    <script src="https://preview.babylonjs.com/cannon.js"></script>
+    <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="../tools/DevLoader/BabylonLoader.js"></script>
 
     <link href="css/index.css" rel="stylesheet" />

+ 2 - 1
Playground/scripts/scripts.txt

@@ -27,4 +27,5 @@ volumetric Light Scattering
 refraction and Reflection
 pbr
 instanced bones
-pointer events handling
+pointer events handling
+webvr

+ 35 - 0
Playground/scripts/webvr.js

@@ -0,0 +1,35 @@
+var createScene = function () {
+
+    // This creates a basic Babylon Scene object (non-mesh)
+    var scene = new BABYLON.Scene(engine);
+    scene.createDefaultVRExperience();
+
+    // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
+    var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
+
+    // Default intensity is 1. Let's dim the light a small amount
+    light.intensity = 0.7;
+
+    // Create some spheres at the default eye level (2m)
+    createSphereBox(scene, 2, 2);
+    createSphereBox(scene, 3, 2);
+        
+    // Our built-in 'ground' shape. Params: name, width, depth, subdivs, scene
+    var ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
+
+    return scene;
+};
+
+function createSphereBox(scene, distance, height) {    
+    createSphere(scene,  distance, height,  distance);
+    createSphere(scene,  distance, height, -distance);
+    createSphere(scene, -distance, height,  distance);
+    createSphere(scene, -distance, height, -distance);    
+}
+function createSphere(scene, x, y, z) {
+    // Our built-in 'sphere' shape. Params: name, subdivs, size, scene
+    var sphere = BABYLON.Mesh.CreateSphere("sphere1", 4, 0.4, scene);
+    sphere.position.x = x;
+    sphere.position.y = y;
+    sphere.position.z = z;
+}

+ 1 - 0
Tools/Gulp/config.json

@@ -1562,6 +1562,7 @@
                     "../../inspector/src/details/Property.ts",
                     "../../inspector/src/details/PropertyLine.ts",
                     "../../inspector/src/gui/ColorElement.ts",
+                    "../../inspector/src/gui/ColorPickerElement.ts",
                     "../../inspector/src/gui/CubeTextureElement.ts",
                     "../../inspector/src/gui/HDRCubeTextureElement.ts",
                     "../../inspector/src/gui/SearchBar.ts",

+ 1 - 0
Tools/Gulp/gulpfile.js

@@ -498,6 +498,7 @@ gulp.task('deployLocalDev', function () {
  */
 gulp.task('webserver', function () {
     gulp.src('../../.').pipe(webserver({
+        host: '0.0.0.0',
         port: 1338,
         livereload: false
     }));

Різницю між файлами не показано, бо вона завелика
+ 2105 - 2076
dist/preview release/babylon.d.ts


Різницю між файлами не показано, бо вона завелика
+ 40 - 40
dist/preview release/babylon.js


Різницю між файлами не показано, бо вона завелика
+ 314 - 152
dist/preview release/babylon.max.js


Різницю між файлами не показано, бо вона завелика
+ 2105 - 2076
dist/preview release/babylon.module.d.ts


Різницю між файлами не показано, бо вона завелика
+ 44 - 44
dist/preview release/babylon.worker.js


Різницю між файлами не показано, бо вона завелика
+ 1919 - 1890
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Різницю між файлами не показано, бо вона завелика
+ 49 - 49
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


Різницю між файлами не показано, бо вона завелика
+ 2346 - 2179
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


Різницю між файлами не показано, бо вона завелика
+ 1919 - 1890
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


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

@@ -602,8 +602,10 @@ declare module BABYLON.GUI {
     class InputText extends Control implements IFocusableControl {
         name: string;
         private _text;
+        private _placeholderText;
         private _background;
         private _focusedBackground;
+        private _placeholderColor;
         private _thickness;
         private _margin;
         private _autoStretchWidth;
@@ -623,6 +625,8 @@ declare module BABYLON.GUI {
         thickness: number;
         focusedBackground: string;
         background: string;
+        placeholderColor: string;
+        placeholderText: string;
         text: string;
         constructor(name?: string, text?: string);
         onBlur(): void;

+ 39 - 2
dist/preview release/gui/babylon.gui.js

@@ -3793,8 +3793,10 @@ var BABYLON;
                 var _this = _super.call(this, name) || this;
                 _this.name = name;
                 _this._text = "";
+                _this._placeholderText = "";
                 _this._background = "black";
                 _this._focusedBackground = "black";
+                _this._placeholderColor = "gray";
                 _this._thickness = 1;
                 _this._margin = new GUI.ValueAndUnit(10, GUI.ValueAndUnit.UNITMODE_PIXEL);
                 _this._autoStretchWidth = true;
@@ -3895,6 +3897,34 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
+            Object.defineProperty(InputText.prototype, "placeholderColor", {
+                get: function () {
+                    return this._placeholderColor;
+                },
+                set: function (value) {
+                    if (this._placeholderColor === value) {
+                        return;
+                    }
+                    this._placeholderColor = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(InputText.prototype, "placeholderText", {
+                get: function () {
+                    return this._placeholderText;
+                },
+                set: function (value) {
+                    if (this._placeholderText === value) {
+                        return;
+                    }
+                    this._placeholderText = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(InputText.prototype, "text", {
                 get: function () {
                     return this._text;
@@ -4031,7 +4061,14 @@ var BABYLON;
                     if (this.color) {
                         context.fillStyle = this.color;
                     }
-                    var textWidth = context.measureText(this._text).width;
+                    var text = this._text;
+                    if (!this._isFocused && !this._text && this._placeholderText) {
+                        text = this._placeholderText;
+                        if (this._placeholderColor) {
+                            context.fillStyle = this._placeholderColor;
+                        }
+                    }
+                    var textWidth = context.measureText(text).width;
                     var marginWidth = this._margin.getValueInPixel(this._host, parentMeasure.width) * 2;
                     if (this._autoStretchWidth) {
                         this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), textWidth + marginWidth) + "px";
@@ -4051,7 +4088,7 @@ var BABYLON;
                     else {
                         this._scrollLeft = clipTextLeft;
                     }
-                    context.fillText(this._text, this._scrollLeft, this._currentMeasure.top + rootY);
+                    context.fillText(text, this._scrollLeft, this._currentMeasure.top + rootY);
                     // Cursor
                     if (this._isFocused) {
                         if (!this._blinkIsEven) {

Різницю між файлами не показано, бо вона завелика
+ 2 - 2
dist/preview release/gui/babylon.gui.min.js


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

@@ -605,8 +605,10 @@ declare module BABYLON.GUI {
     class InputText extends Control implements IFocusableControl {
         name: string;
         private _text;
+        private _placeholderText;
         private _background;
         private _focusedBackground;
+        private _placeholderColor;
         private _thickness;
         private _margin;
         private _autoStretchWidth;
@@ -626,6 +628,8 @@ declare module BABYLON.GUI {
         thickness: number;
         focusedBackground: string;
         background: string;
+        placeholderColor: string;
+        placeholderText: string;
         text: string;
         constructor(name?: string, text?: string);
         onBlur(): void;

Різницю між файлами не показано, бо вона завелика
+ 4 - 4
dist/preview release/inspector/babylon.inspector.bundle.js


+ 3 - 1
dist/preview release/inspector/babylon.inspector.css

@@ -26,6 +26,7 @@
       cursor: ew-resize; }
   .insp-wrapper .insp-right-panel {
     width: 750px;
+    overflow-y: auto;
     display: flex;
     flex-direction: column;
     flex-shrink: 0; }
@@ -84,7 +85,8 @@
     .insp-wrapper .treeTool.active {
       color: #5db0d7; }
   .insp-wrapper .tab-panel {
-    height: 100%; }
+    height: 100%;
+    overflow-y: auto; }
     .insp-wrapper .tab-panel.searchable {
       height: calc(100% - 30px - 10px); }
     .insp-wrapper .tab-panel .texture-image {

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

@@ -399,7 +399,6 @@ declare module INSPECTOR {
 
 declare module INSPECTOR {
     class LightAdapter extends Adapter implements IToolVisible {
-        private static _PROPERTIES;
         constructor(obj: BABYLON.Light);
         /** Returns the name displayed in the tree */
         id(): string;
@@ -541,6 +540,16 @@ declare module INSPECTOR {
         private _validateInputHandler;
         /** Handler used to validate the input by pressing 'esc' */
         private _escapeInputHandler;
+        /** Handler used on focus out */
+        private _focusOutInputHandler;
+        /** Handler used to get mouse position */
+        private _onMouseDownHandler;
+        private _onMouseDragHandler;
+        private _onMouseUpHandler;
+        /** Save previous Y mouse position */
+        private _prevY;
+        /**Save value while slider is on */
+        private _preValue;
         constructor(prop: Property, parent?: PropertyLine, level?: number);
         /**
          * Init the input element and al its handler :
@@ -553,6 +562,7 @@ declare module INSPECTOR {
          * On escape : removes the input
          */
         private _validateInput(e);
+        validateInput(value: any): void;
         /**
          * On escape : removes the input
          */
@@ -605,6 +615,21 @@ declare module INSPECTOR {
          * Add sub properties in case of a complex type
          */
         private _addDetails();
+        /**
+         * Refresh mouse position on y axis
+         * @param e
+         */
+        private _onMouseDrag(e);
+        /**
+         * Save new value from slider
+         * @param e
+         */
+        private _onMouseUp(e);
+        /**
+         * Start record mouse position
+         * @param e
+         */
+        private _onMouseDown(e);
     }
 }
 
@@ -621,6 +646,20 @@ declare module INSPECTOR {
 
 declare module INSPECTOR {
     /**
+     * Represents a html div element.
+     * The div is built when an instance of BasicElement is created.
+     */
+    class ColorPickerElement extends BasicElement {
+        protected _input: HTMLInputElement;
+        private pline;
+        constructor(color: BABYLON.Color4 | BABYLON.Color3, propertyLine: PropertyLine);
+        update(color?: BABYLON.Color4 | BABYLON.Color3): void;
+        private _toRgba(color);
+    }
+}
+
+declare module INSPECTOR {
+    /**
     * Display a very small div. A new canvas is created, with a new Babylon.js scene, containing only the
     * cube texture in a cube
     */
@@ -733,6 +772,10 @@ declare module INSPECTOR {
          * Useful function used to create a div
          */
         static CreateDiv(className?: string, parent?: HTMLElement): HTMLElement;
+        /**
+         * Useful function used to create a input
+         */
+        static CreateInput(className?: string, parent?: HTMLElement): HTMLInputElement;
         static CreateElement(element: string, className?: string, parent?: HTMLElement): HTMLElement;
         /**
          * Removes all children of the given div.
@@ -744,6 +787,11 @@ declare module INSPECTOR {
         static Css(elem: HTMLElement, cssAttribute: string): string;
         static LoadScript(): void;
         static IsSystemName(name: string): boolean;
+        /**
+         * Return an array of PropertyLine for an obj
+         * @param obj
+         */
+        static GetAllLinesProperties(obj: any): Array<PropertyLine>;
     }
 }
 

+ 153 - 174
dist/preview release/inspector/babylon.inspector.js

@@ -964,21 +964,7 @@ var INSPECTOR;
         };
         /** Returns the list of properties to be displayed for this adapter */
         CameraAdapter.prototype.getProperties = function () {
-            var propertiesLines = [];
-            var camToDisplay = [];
-            // The if is there to work with the min version of babylon
-            if (this._obj instanceof BABYLON.ArcRotateCamera) {
-                camToDisplay = INSPECTOR.PROPERTIES['ArcRotateCamera'].properties;
-            }
-            else if (this._obj instanceof BABYLON.FreeCamera) {
-                camToDisplay = INSPECTOR.PROPERTIES['FreeCamera'].properties;
-            }
-            for (var _i = 0, camToDisplay_1 = camToDisplay; _i < camToDisplay_1.length; _i++) {
-                var dirty = camToDisplay_1[_i];
-                var infos = new INSPECTOR.Property(dirty, this._obj);
-                propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-            }
-            return propertiesLines;
+            return INSPECTOR.Helpers.GetAllLinesProperties(this._obj);
         };
         CameraAdapter.prototype.getTools = function () {
             var tools = [];
@@ -1031,13 +1017,7 @@ var INSPECTOR;
         };
         /** Returns the list of properties to be displayed for this adapter */
         PhysicsImpostorAdapter.prototype.getProperties = function () {
-            var propertiesLines = [];
-            for (var _i = 0, _a = INSPECTOR.PROPERTIES['PhysicsImpostor'].properties; _i < _a.length; _i++) {
-                var dirty = _a[_i];
-                var infos = new INSPECTOR.Property(dirty, this._obj);
-                propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-            }
-            return propertiesLines;
+            return INSPECTOR.Helpers.GetAllLinesProperties(this._obj);
         };
         PhysicsImpostorAdapter.prototype.getTools = function () {
             var tools = [];
@@ -1094,97 +1074,7 @@ var INSPECTOR;
         };
         /** Returns the list of properties to be displayed for this adapter */
         GUIAdapter.prototype.getProperties = function () {
-            var propertiesLines = [];
-            for (var _i = 0, _a = INSPECTOR.PROPERTIES['Control'].properties; _i < _a.length; _i++) {
-                var dirty = _a[_i];
-                var infos = new INSPECTOR.Property(dirty, this._obj);
-                propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-            }
-            if (this._obj instanceof BABYLON.GUI.Button) {
-                for (var _b = 0, _c = INSPECTOR.PROPERTIES['Button'].properties; _b < _c.length; _b++) {
-                    var dirty = _c[_b];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.ColorPicker) {
-                for (var _d = 0, _e = INSPECTOR.PROPERTIES['ColorPicker'].properties; _d < _e.length; _d++) {
-                    var dirty = _e[_d];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.Checkbox) {
-                for (var _f = 0, _g = INSPECTOR.PROPERTIES['Checkbox'].properties; _f < _g.length; _f++) {
-                    var dirty = _g[_f];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.Ellipse) {
-                for (var _h = 0, _j = INSPECTOR.PROPERTIES['Ellipse'].properties; _h < _j.length; _h++) {
-                    var dirty = _j[_h];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.Image) {
-                for (var _k = 0, _l = INSPECTOR.PROPERTIES['Image'].properties; _k < _l.length; _k++) {
-                    var dirty = _l[_k];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.Line) {
-                for (var _m = 0, _o = INSPECTOR.PROPERTIES['Line'].properties; _m < _o.length; _m++) {
-                    var dirty = _o[_m];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.RadioButton) {
-                for (var _p = 0, _q = INSPECTOR.PROPERTIES['RadioButton'].properties; _p < _q.length; _p++) {
-                    var dirty = _q[_p];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.Rectangle) {
-                for (var _r = 0, _s = INSPECTOR.PROPERTIES['Rectangle'].properties; _r < _s.length; _r++) {
-                    var dirty = _s[_r];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.Slider) {
-                for (var _t = 0, _u = INSPECTOR.PROPERTIES['Slider'].properties; _t < _u.length; _t++) {
-                    var dirty = _u[_t];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.StackPanel) {
-                for (var _v = 0, _w = INSPECTOR.PROPERTIES['StackPanel'].properties; _v < _w.length; _v++) {
-                    var dirty = _w[_v];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.TextBlock) {
-                for (var _x = 0, _y = INSPECTOR.PROPERTIES['TextBlock'].properties; _x < _y.length; _x++) {
-                    var dirty = _y[_x];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            if (this._obj instanceof BABYLON.GUI.Container) {
-                for (var _z = 0, _0 = INSPECTOR.PROPERTIES['Container'].properties; _z < _0.length; _z++) {
-                    var dirty = _0[_z];
-                    var infos = new INSPECTOR.Property(dirty, this._obj);
-                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-                }
-            }
-            return propertiesLines;
+            return INSPECTOR.Helpers.GetAllLinesProperties(this._obj);
         };
         GUIAdapter.prototype.getTools = function () {
             var tools = [];
@@ -1235,16 +1125,7 @@ var INSPECTOR;
         };
         /** Returns the list of properties to be displayed for this adapter */
         SoundAdapter.prototype.getProperties = function () {
-            var propertiesLines = [];
-            var camToDisplay = [];
-            // The if is there to work with the min version of babylon
-            var soundProperties = INSPECTOR.PROPERTIES['Sound'].properties;
-            for (var _i = 0, soundProperties_1 = soundProperties; _i < soundProperties_1.length; _i++) {
-                var dirty = soundProperties_1[_i];
-                var infos = new INSPECTOR.Property(dirty, this._obj);
-                propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-            }
-            return propertiesLines;
+            return INSPECTOR.Helpers.GetAllLinesProperties(this._obj);
         };
         SoundAdapter.prototype.getTools = function () {
             var tools = [];
@@ -1346,13 +1227,7 @@ var INSPECTOR;
         };
         /** Returns the list of properties to be displayed for this adapter */
         LightAdapter.prototype.getProperties = function () {
-            var propertiesLines = [];
-            for (var _i = 0, _a = LightAdapter._PROPERTIES; _i < _a.length; _i++) {
-                var dirty = _a[_i];
-                var infos = new INSPECTOR.Property(dirty, this._obj);
-                propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-            }
-            return propertiesLines;
+            return INSPECTOR.Helpers.GetAllLinesProperties(this._obj);
         };
         LightAdapter.prototype.getTools = function () {
             var tools = [];
@@ -1365,14 +1240,6 @@ var INSPECTOR;
         LightAdapter.prototype.isVisible = function () {
             return this._obj.isEnabled();
         };
-        LightAdapter._PROPERTIES = [
-            'position',
-            'diffuse',
-            'intensity',
-            'radius',
-            'range',
-            'specular'
-        ];
         return LightAdapter;
     }(INSPECTOR.Adapter));
     INSPECTOR.LightAdapter = LightAdapter;
@@ -1411,21 +1278,7 @@ var INSPECTOR;
         };
         /** Returns the list of properties to be displayed for this adapter */
         MaterialAdapter.prototype.getProperties = function () {
-            var propertiesLines = [];
-            var propToDisplay = [];
-            // The if is there to work with the min version of babylon
-            if (this._obj instanceof BABYLON.StandardMaterial) {
-                propToDisplay = INSPECTOR.PROPERTIES['StandardMaterial'].properties;
-            }
-            else if (this._obj instanceof BABYLON.PBRMaterial) {
-                propToDisplay = INSPECTOR.PROPERTIES['PBRMaterial'].properties;
-            }
-            for (var _i = 0, propToDisplay_1 = propToDisplay; _i < propToDisplay_1.length; _i++) {
-                var dirty = propToDisplay_1[_i];
-                var infos = new INSPECTOR.Property(dirty, this._obj);
-                propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-            }
-            return propertiesLines;
+            return INSPECTOR.Helpers.GetAllLinesProperties(this._obj);
         };
         /** No tools for a material adapter */
         MaterialAdapter.prototype.getTools = function () {
@@ -1472,13 +1325,7 @@ var INSPECTOR;
         };
         /** Returns the list of properties to be displayed for this adapter */
         MeshAdapter.prototype.getProperties = function () {
-            var propertiesLines = [];
-            for (var _i = 0, _a = INSPECTOR.PROPERTIES['Mesh'].properties; _i < _a.length; _i++) {
-                var dirty = _a[_i];
-                var infos = new INSPECTOR.Property(dirty, this._obj);
-                propertiesLines.push(new INSPECTOR.PropertyLine(infos));
-            }
-            return propertiesLines;
+            return INSPECTOR.Helpers.GetAllLinesProperties(this._obj);
         };
         MeshAdapter.prototype.getTools = function () {
             var tools = [];
@@ -1853,6 +1700,7 @@ var INSPECTOR;
                 this._valueDiv.addEventListener('click', this._displayInputHandler);
                 this._input.addEventListener('keypress', this._validateInputHandler);
                 this._input.addEventListener('keydown', this._escapeInputHandler);
+                this._input.addEventListener('focusout', this._focusOutInputHandler);
             }
             // Add this property to the scheduler
             INSPECTOR.Scheduler.getInstance().add(this);
@@ -1870,6 +1718,10 @@ var INSPECTOR;
             this._displayInputHandler = this._displayInput.bind(this);
             this._validateInputHandler = this._validateInput.bind(this);
             this._escapeInputHandler = this._escapeInput.bind(this);
+            this._focusOutInputHandler = this.update.bind(this);
+            this._onMouseDownHandler = this._onMouseDown.bind(this);
+            this._onMouseDragHandler = this._onMouseDrag.bind(this);
+            this._onMouseUpHandler = this._onMouseUp.bind(this);
         };
         /**
          * On enter : validates the new value and removes the input
@@ -1877,29 +1729,32 @@ var INSPECTOR;
          */
         PropertyLine.prototype._validateInput = function (e) {
             if (e.keyCode == 13) {
-                // Enter : validate the new value
-                var newValue = this._input.value;
-                this.updateObject();
-                if (typeof this._property.value === 'number') {
-                    this._property.value = parseFloat(newValue);
-                }
-                else {
-                    this._property.value = newValue;
-                }
-                // Remove input
-                this.update();
-                // resume scheduler
-                INSPECTOR.Scheduler.getInstance().pause = false;
+                this.validateInput(this._input.value);
             }
             else if (e.keyCode == 27) {
                 // Esc : remove input
                 this.update();
             }
         };
+        PropertyLine.prototype.validateInput = function (value) {
+            this.updateObject();
+            if (typeof this._property.value === 'number') {
+                this._property.value = parseFloat(value);
+            }
+            else {
+                this._property.value = value;
+            }
+            // Remove input
+            this.update();
+            // resume scheduler
+            INSPECTOR.Scheduler.getInstance().pause = false;
+        };
         /**
          * On escape : removes the input
          */
         PropertyLine.prototype._escapeInput = function (e) {
+            // Remove focus out handler
+            this._input.removeEventListener('focusout', this._focusOutInputHandler);
             if (e.keyCode == 27) {
                 // Esc : remove input
                 this.update();
@@ -1925,6 +1780,11 @@ var INSPECTOR;
             this._valueDiv.textContent = "";
             this._input.value = valueTxt;
             this._valueDiv.appendChild(this._input);
+            this._input.focus();
+            if (typeof this.value === 'number') {
+                this._input.addEventListener('mousedown', this._onMouseDownHandler);
+            }
+            this._input.addEventListener('focusout', this._focusOutInputHandler);
             // Pause the scheduler
             INSPECTOR.Scheduler.getInstance().pause = true;
         };
@@ -1970,7 +1830,8 @@ var INSPECTOR;
         PropertyLine.prototype._createElements = function () {
             // Colors
             if (this.type == 'Color3' || this.type == 'Color4') {
-                this._elements.push(new INSPECTOR.ColorElement(this.value));
+                this._elements.push(new INSPECTOR.ColorPickerElement(this.value, this));
+                //this._elements.push(new ColorElement(this.value));
             }
             // Texture
             if (this.type == 'Texture') {
@@ -2104,6 +1965,33 @@ var INSPECTOR;
                 }
             }
         };
+        /**
+         * Refresh mouse position on y axis
+         * @param e
+         */
+        PropertyLine.prototype._onMouseDrag = function (e) {
+            var diff = this._prevY - e.clientY;
+            this._input.value = (this._preValue + diff).toString();
+        };
+        /**
+         * Save new value from slider
+         * @param e
+         */
+        PropertyLine.prototype._onMouseUp = function (e) {
+            window.removeEventListener('mousemove', this._onMouseDragHandler);
+            window.removeEventListener('mouseup', this._onMouseUpHandler);
+            this._prevY = e.clientY;
+        };
+        /**
+         * Start record mouse position
+         * @param e
+         */
+        PropertyLine.prototype._onMouseDown = function (e) {
+            this._prevY = e.clientY;
+            this._preValue = this.value;
+            window.addEventListener('mousemove', this._onMouseDragHandler);
+            window.addEventListener('mouseup', this._onMouseUpHandler);
+        };
         // Array representing the simple type. All others are considered 'complex'
         PropertyLine._SIMPLE_TYPE = ['number', 'string', 'boolean'];
         // The number of pixel at each children step
@@ -2177,6 +2065,74 @@ var __extends = (this && this.__extends) || (function () {
 var INSPECTOR;
 (function (INSPECTOR) {
     /**
+     * Represents a html div element.
+     * The div is built when an instance of BasicElement is created.
+     */
+    var ColorPickerElement = (function (_super) {
+        __extends(ColorPickerElement, _super);
+        function ColorPickerElement(color, propertyLine) {
+            var _this = _super.call(this) || this;
+            var scheduler = INSPECTOR.Scheduler.getInstance();
+            _this._div.className = 'color-element';
+            _this._div.style.backgroundColor = _this._toRgba(color);
+            _this._div.style.top = "5px";
+            _this.pline = propertyLine;
+            _this._input = INSPECTOR.Helpers.CreateInput();
+            _this._input.type = 'color';
+            _this._input.style.opacity = "0";
+            _this._input.style.width = '10px';
+            _this._input.style.height = '15px';
+            _this._input.value = color.toHexString();
+            _this._input.addEventListener('input', function (e) {
+                console.log('Color', _this._input.value, _this.pline);
+                _this.pline.validateInput(BABYLON.Color3.FromHexString(_this._input.value));
+                scheduler.pause = false;
+            });
+            _this._div.appendChild(_this._input);
+            _this._input.addEventListener('click', function (e) {
+                scheduler.pause = true;
+            });
+            return _this;
+        }
+        ColorPickerElement.prototype.update = function (color) {
+            if (color) {
+                this._div.style.backgroundColor = this._toRgba(color);
+                this._input.value = color.toHexString();
+            }
+        };
+        ColorPickerElement.prototype._toRgba = function (color) {
+            if (color) {
+                var r = (color.r * 255) | 0;
+                var g = (color.g * 255) | 0;
+                var b = (color.b * 255) | 0;
+                var a = 1;
+                if (color instanceof BABYLON.Color4) {
+                    var a_1 = color.a;
+                }
+                return "rgba(" + r + ", " + g + ", " + b + ", " + a + ")";
+            }
+            else {
+                return '';
+            }
+        };
+        return ColorPickerElement;
+    }(INSPECTOR.BasicElement));
+    INSPECTOR.ColorPickerElement = ColorPickerElement;
+})(INSPECTOR || (INSPECTOR = {}));
+
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var INSPECTOR;
+(function (INSPECTOR) {
+    /**
     * Display a very small div. A new canvas is created, with a new Babylon.js scene, containing only the
     * cube texture in a cube
     */
@@ -2539,6 +2495,12 @@ var INSPECTOR;
         Helpers.CreateDiv = function (className, parent) {
             return Helpers.CreateElement('div', className, parent);
         };
+        /**
+         * Useful function used to create a input
+         */
+        Helpers.CreateInput = function (className, parent) {
+            return Helpers.CreateElement('input', className, parent);
+        };
         Helpers.CreateElement = function (element, className, parent) {
             var elem = INSPECTOR.Inspector.DOCUMENT.createElement(element);
             if (className) {
@@ -2595,6 +2557,23 @@ var INSPECTOR;
             }
             return name.indexOf("###") === 0 && name.lastIndexOf("###") === (name.length - 3);
         };
+        /**
+         * Return an array of PropertyLine for an obj
+         * @param obj
+         */
+        Helpers.GetAllLinesProperties = function (obj) {
+            var propertiesLines = [];
+            for (var prop in obj) {
+                /**
+                 * No private and no function
+                 */
+                if (prop.substring(0, 1) !== '_' && typeof obj[prop] !== 'function') {
+                    var infos = new INSPECTOR.Property(prop, obj);
+                    propertiesLines.push(new INSPECTOR.PropertyLine(infos));
+                }
+            }
+            return propertiesLines;
+        };
         return Helpers;
     }());
     INSPECTOR.Helpers = Helpers;

Різницю між файлами не показано, бо вона завелика
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


+ 11 - 9
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -213,9 +213,9 @@ declare module BABYLON.GLTF2 {
         indices?: number;
         material?: number;
         mode?: EMeshPrimitiveMode;
-        targets?: [{
+        targets?: {
             [name: string]: number;
-        }];
+        }[];
     }
     interface IGLTFMesh extends IGLTFChildRootProperty {
         primitives: IGLTFMeshPrimitive[];
@@ -234,7 +234,7 @@ declare module BABYLON.GLTF2 {
         index?: number;
         parent?: IGLTFNode;
         babylonMesh?: Mesh;
-        babylonSkinToBones?: {
+        babylonBones?: {
             [skin: number]: Bone;
         };
         babylonAnimationTargets?: Node[];
@@ -330,15 +330,17 @@ declare module BABYLON.GLTF2 {
         private _showMeshes();
         private _startAnimations();
         private _loadScene(nodeNames);
-        private _loadSkin(node);
-        private _updateBone(node, parentNode, skin, inverseBindMatrixData);
-        private _createBone(node, skin);
-        private _loadMesh(node);
-        private _loadMeshData(node, mesh, babylonMesh);
+        private _loadNode(node);
+        private _loadMesh(node, mesh);
         private _loadVertexDataAsync(primitive, onSuccess);
         private _createMorphTargets(node, mesh, primitive, babylonMesh);
         private _loadMorphTargetsData(mesh, primitive, vertexData, babylonMesh);
-        private _loadTransform(node, babylonMesh);
+        private _loadTransform(node);
+        private _loadSkin(skin);
+        private _createBone(node, skin, parent, localMatrix, baseMatrix, index);
+        private _loadBones(skin, inverseBindMatrixData);
+        private _loadBone(node, skin, inverseBindMatrixData, babylonBones);
+        private _getNodeMatrix(node);
         private _traverseNodes(indices, action, parentNode?);
         private _traverseNode(index, action, parentNode?);
         private _loadAnimations();

+ 78 - 73
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -547,85 +547,35 @@ var BABYLON;
                     });
                     nodeIndices = filteredNodeIndices;
                 }
-                this._traverseNodes(nodeIndices, function (node) { return _this._loadSkin(node); });
-                this._traverseNodes(nodeIndices, function (node) { return _this._loadMesh(node); });
+                this._traverseNodes(nodeIndices, function (node) { return _this._loadNode(node); });
             };
-            GLTFLoader.prototype._loadSkin = function (node) {
-                var _this = this;
-                if (node.skin !== undefined) {
-                    var skin = this._gltf.skins[node.skin];
-                    var skeletonId = "skeleton" + node.skin;
-                    skin.babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
-                    skin.index = node.skin;
-                    for (var i = 0; i < skin.joints.length; i++) {
-                        this._createBone(this._gltf.nodes[skin.joints[i]], skin);
-                    }
-                    if (skin.skeleton === undefined) {
-                        // TODO: handle when skeleton is not defined
-                        throw new Error("Not implemented");
-                    }
-                    if (skin.inverseBindMatrices === undefined) {
-                        // TODO: handle when inverse bind matrices are not defined
-                        throw new Error("Not implemented");
-                    }
-                    var accessor = this._gltf.accessors[skin.inverseBindMatrices];
-                    this._loadAccessorAsync(accessor, function (data) {
-                        _this._traverseNode(skin.skeleton, function (node, index, parent) { return _this._updateBone(node, parent, skin, data); });
-                    });
-                }
-                return true;
-            };
-            GLTFLoader.prototype._updateBone = function (node, parentNode, skin, inverseBindMatrixData) {
-                var jointIndex = skin.joints.indexOf(node.index);
-                if (jointIndex === -1) {
-                    this._createBone(node, skin);
-                }
-                var babylonBone = node.babylonSkinToBones[skin.index];
-                // TODO: explain the math
-                var matrix = jointIndex === -1 ? BABYLON.Matrix.Identity() : BABYLON.Matrix.FromArray(inverseBindMatrixData, jointIndex * 16);
-                matrix.invertToRef(matrix);
-                if (parentNode) {
-                    babylonBone.setParent(parentNode.babylonSkinToBones[skin.index], false);
-                    matrix.multiplyToRef(babylonBone.getParent().getInvertedAbsoluteTransform(), matrix);
-                }
-                babylonBone.updateMatrix(matrix);
-                return true;
-            };
-            GLTFLoader.prototype._createBone = function (node, skin) {
-                var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton);
-                node.babylonSkinToBones = node.babylonSkinToBones || {};
-                node.babylonSkinToBones[skin.index] = babylonBone;
-                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
-                node.babylonAnimationTargets.push(babylonBone);
-                return babylonBone;
-            };
-            GLTFLoader.prototype._loadMesh = function (node) {
-                var babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                babylonMesh.isVisible = false;
-                this._loadTransform(node, babylonMesh);
+            GLTFLoader.prototype._loadNode = function (node) {
+                node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
+                node.babylonMesh.isVisible = false;
+                this._loadTransform(node);
                 if (node.mesh !== undefined) {
                     var mesh = this._gltf.meshes[node.mesh];
-                    this._loadMeshData(node, mesh, babylonMesh);
+                    this._loadMesh(node, mesh);
                 }
-                babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
-                node.babylonMesh = babylonMesh;
+                node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
                 node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(node.babylonMesh);
                 if (node.skin !== undefined) {
                     var skin = this._gltf.skins[node.skin];
-                    babylonMesh.skeleton = skin.babylonSkeleton;
+                    skin.index = node.skin;
+                    node.babylonMesh.skeleton = this._loadSkin(skin);
                 }
                 if (node.camera !== undefined) {
                     // TODO: handle cameras
                 }
                 return true;
             };
-            GLTFLoader.prototype._loadMeshData = function (node, mesh, babylonMesh) {
+            GLTFLoader.prototype._loadMesh = function (node, mesh) {
                 var _this = this;
-                babylonMesh.name = mesh.name || babylonMesh.name;
-                var babylonMultiMaterial = new BABYLON.MultiMaterial(babylonMesh.name, this._babylonScene);
-                babylonMesh.material = babylonMultiMaterial;
-                var geometry = new BABYLON.Geometry(babylonMesh.name, this._babylonScene, null, false, babylonMesh);
+                node.babylonMesh.name = mesh.name || node.babylonMesh.name;
+                var babylonMultiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
+                node.babylonMesh.material = babylonMultiMaterial;
+                var geometry = new BABYLON.Geometry(node.babylonMesh.name, this._babylonScene, null, false, node.babylonMesh);
                 var vertexData = new BABYLON.VertexData();
                 vertexData.positions = [];
                 vertexData.indices = [];
@@ -638,9 +588,9 @@ var BABYLON;
                         // TODO: handle other primitive modes
                         throw new Error("Not implemented");
                     }
-                    this_1._createMorphTargets(node, mesh, primitive, babylonMesh);
+                    this_1._createMorphTargets(node, mesh, primitive, node.babylonMesh);
                     this_1._loadVertexDataAsync(primitive, function (subVertexData) {
-                        _this._loadMorphTargetsData(mesh, primitive, subVertexData, babylonMesh);
+                        _this._loadMorphTargetsData(mesh, primitive, subVertexData, node.babylonMesh);
                         subMeshInfos.push({
                             materialIndex: i,
                             verticesStart: vertexData.positions.length,
@@ -659,7 +609,7 @@ var BABYLON;
                                         }
                                         if (_this._parent.onBeforeMaterialReadyAsync) {
                                             _this.addLoaderPendingData(material);
-                                            _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
+                                            _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
                                                 babylonMultiMaterial.subMaterials[i] = babylonMaterial;
                                                 _this.removeLoaderPendingData(material);
                                             });
@@ -677,8 +627,8 @@ var BABYLON;
                             subMeshInfos.forEach(function (info) { return info.loadMaterial(); });
                             // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                             // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
-                            babylonMesh.subMeshes = [];
-                            subMeshInfos.forEach(function (info) { return new BABYLON.SubMesh(info.materialIndex, info.verticesStart, info.verticesCount, info.indicesStart, info.indicesCount, babylonMesh); });
+                            node.babylonMesh.subMeshes = [];
+                            subMeshInfos.forEach(function (info) { return new BABYLON.SubMesh(info.materialIndex, info.verticesStart, info.verticesCount, info.indicesStart, info.indicesCount, node.babylonMesh); });
                         }
                     });
                 };
@@ -816,7 +766,7 @@ var BABYLON;
                     _loop_3();
                 }
             };
-            GLTFLoader.prototype._loadTransform = function (node, babylonMesh) {
+            GLTFLoader.prototype._loadTransform = function (node) {
                 var position = BABYLON.Vector3.Zero();
                 var rotation = BABYLON.Quaternion.Identity();
                 var scaling = BABYLON.Vector3.One();
@@ -832,9 +782,64 @@ var BABYLON;
                     if (node.scale)
                         scaling = BABYLON.Vector3.FromArray(node.scale);
                 }
-                babylonMesh.position = position;
-                babylonMesh.rotationQuaternion = rotation;
-                babylonMesh.scaling = scaling;
+                node.babylonMesh.position = position;
+                node.babylonMesh.rotationQuaternion = rotation;
+                node.babylonMesh.scaling = scaling;
+            };
+            GLTFLoader.prototype._loadSkin = function (skin) {
+                var _this = this;
+                var skeletonId = "skeleton" + skin.index;
+                skin.babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
+                if (skin.inverseBindMatrices === undefined) {
+                    this._loadBones(skin, null);
+                }
+                else {
+                    var accessor = this._gltf.accessors[skin.inverseBindMatrices];
+                    this._loadAccessorAsync(accessor, function (data) {
+                        _this._loadBones(skin, data);
+                    });
+                }
+                return skin.babylonSkeleton;
+            };
+            GLTFLoader.prototype._createBone = function (node, skin, parent, localMatrix, baseMatrix, index) {
+                var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+                node.babylonBones = node.babylonBones || {};
+                node.babylonBones[skin.index] = babylonBone;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
+                node.babylonAnimationTargets.push(babylonBone);
+                return babylonBone;
+            };
+            GLTFLoader.prototype._loadBones = function (skin, inverseBindMatrixData) {
+                var babylonBones = {};
+                for (var i = 0; i < skin.joints.length; i++) {
+                    var node = this._gltf.nodes[skin.joints[i]];
+                    this._loadBone(node, skin, inverseBindMatrixData, babylonBones);
+                }
+            };
+            GLTFLoader.prototype._loadBone = function (node, skin, inverseBindMatrixData, babylonBones) {
+                var babylonBone = babylonBones[node.index];
+                if (babylonBone) {
+                    return babylonBone;
+                }
+                var boneIndex = skin.joints.indexOf(node.index);
+                var baseMatrix = BABYLON.Matrix.Identity();
+                if (inverseBindMatrixData && boneIndex !== -1) {
+                    baseMatrix = BABYLON.Matrix.FromArray(inverseBindMatrixData, boneIndex * 16);
+                    baseMatrix.invertToRef(baseMatrix);
+                }
+                var babylonParentBone;
+                if (node.index != skin.skeleton && node.parent) {
+                    babylonParentBone = this._loadBone(node.parent, skin, inverseBindMatrixData, babylonBones);
+                    baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
+                }
+                babylonBone = this._createBone(node, skin, babylonParentBone, this._getNodeMatrix(node), baseMatrix, boneIndex);
+                babylonBones[node.index] = babylonBone;
+                return babylonBone;
+            };
+            GLTFLoader.prototype._getNodeMatrix = function (node) {
+                return node.matrix ?
+                    BABYLON.Matrix.FromArray(node.matrix) :
+                    BABYLON.Matrix.Compose(node.scale ? BABYLON.Vector3.FromArray(node.scale) : BABYLON.Vector3.One(), node.rotation ? BABYLON.Quaternion.FromArray(node.rotation) : BABYLON.Quaternion.Identity(), node.translation ? BABYLON.Vector3.FromArray(node.translation) : BABYLON.Vector3.Zero());
             };
             GLTFLoader.prototype._traverseNodes = function (indices, action, parentNode) {
                 if (parentNode === void 0) { parentNode = null; }

Різницю між файлами не показано, бо вона завелика
+ 2 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 11 - 9
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -709,9 +709,9 @@ declare module BABYLON.GLTF2 {
         indices?: number;
         material?: number;
         mode?: EMeshPrimitiveMode;
-        targets?: [{
+        targets?: {
             [name: string]: number;
-        }];
+        }[];
     }
     interface IGLTFMesh extends IGLTFChildRootProperty {
         primitives: IGLTFMeshPrimitive[];
@@ -730,7 +730,7 @@ declare module BABYLON.GLTF2 {
         index?: number;
         parent?: IGLTFNode;
         babylonMesh?: Mesh;
-        babylonSkinToBones?: {
+        babylonBones?: {
             [skin: number]: Bone;
         };
         babylonAnimationTargets?: Node[];
@@ -826,15 +826,17 @@ declare module BABYLON.GLTF2 {
         private _showMeshes();
         private _startAnimations();
         private _loadScene(nodeNames);
-        private _loadSkin(node);
-        private _updateBone(node, parentNode, skin, inverseBindMatrixData);
-        private _createBone(node, skin);
-        private _loadMesh(node);
-        private _loadMeshData(node, mesh, babylonMesh);
+        private _loadNode(node);
+        private _loadMesh(node, mesh);
         private _loadVertexDataAsync(primitive, onSuccess);
         private _createMorphTargets(node, mesh, primitive, babylonMesh);
         private _loadMorphTargetsData(mesh, primitive, vertexData, babylonMesh);
-        private _loadTransform(node, babylonMesh);
+        private _loadTransform(node);
+        private _loadSkin(skin);
+        private _createBone(node, skin, parent, localMatrix, baseMatrix, index);
+        private _loadBones(skin, inverseBindMatrixData);
+        private _loadBone(node, skin, inverseBindMatrixData, babylonBones);
+        private _getNodeMatrix(node);
         private _traverseNodes(indices, action, parentNode?);
         private _traverseNode(index, action, parentNode?);
         private _loadAnimations();

+ 78 - 73
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -2705,85 +2705,35 @@ var BABYLON;
                     });
                     nodeIndices = filteredNodeIndices;
                 }
-                this._traverseNodes(nodeIndices, function (node) { return _this._loadSkin(node); });
-                this._traverseNodes(nodeIndices, function (node) { return _this._loadMesh(node); });
+                this._traverseNodes(nodeIndices, function (node) { return _this._loadNode(node); });
             };
-            GLTFLoader.prototype._loadSkin = function (node) {
-                var _this = this;
-                if (node.skin !== undefined) {
-                    var skin = this._gltf.skins[node.skin];
-                    var skeletonId = "skeleton" + node.skin;
-                    skin.babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
-                    skin.index = node.skin;
-                    for (var i = 0; i < skin.joints.length; i++) {
-                        this._createBone(this._gltf.nodes[skin.joints[i]], skin);
-                    }
-                    if (skin.skeleton === undefined) {
-                        // TODO: handle when skeleton is not defined
-                        throw new Error("Not implemented");
-                    }
-                    if (skin.inverseBindMatrices === undefined) {
-                        // TODO: handle when inverse bind matrices are not defined
-                        throw new Error("Not implemented");
-                    }
-                    var accessor = this._gltf.accessors[skin.inverseBindMatrices];
-                    this._loadAccessorAsync(accessor, function (data) {
-                        _this._traverseNode(skin.skeleton, function (node, index, parent) { return _this._updateBone(node, parent, skin, data); });
-                    });
-                }
-                return true;
-            };
-            GLTFLoader.prototype._updateBone = function (node, parentNode, skin, inverseBindMatrixData) {
-                var jointIndex = skin.joints.indexOf(node.index);
-                if (jointIndex === -1) {
-                    this._createBone(node, skin);
-                }
-                var babylonBone = node.babylonSkinToBones[skin.index];
-                // TODO: explain the math
-                var matrix = jointIndex === -1 ? BABYLON.Matrix.Identity() : BABYLON.Matrix.FromArray(inverseBindMatrixData, jointIndex * 16);
-                matrix.invertToRef(matrix);
-                if (parentNode) {
-                    babylonBone.setParent(parentNode.babylonSkinToBones[skin.index], false);
-                    matrix.multiplyToRef(babylonBone.getParent().getInvertedAbsoluteTransform(), matrix);
-                }
-                babylonBone.updateMatrix(matrix);
-                return true;
-            };
-            GLTFLoader.prototype._createBone = function (node, skin) {
-                var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton);
-                node.babylonSkinToBones = node.babylonSkinToBones || {};
-                node.babylonSkinToBones[skin.index] = babylonBone;
-                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
-                node.babylonAnimationTargets.push(babylonBone);
-                return babylonBone;
-            };
-            GLTFLoader.prototype._loadMesh = function (node) {
-                var babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                babylonMesh.isVisible = false;
-                this._loadTransform(node, babylonMesh);
+            GLTFLoader.prototype._loadNode = function (node) {
+                node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
+                node.babylonMesh.isVisible = false;
+                this._loadTransform(node);
                 if (node.mesh !== undefined) {
                     var mesh = this._gltf.meshes[node.mesh];
-                    this._loadMeshData(node, mesh, babylonMesh);
+                    this._loadMesh(node, mesh);
                 }
-                babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
-                node.babylonMesh = babylonMesh;
+                node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
                 node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(node.babylonMesh);
                 if (node.skin !== undefined) {
                     var skin = this._gltf.skins[node.skin];
-                    babylonMesh.skeleton = skin.babylonSkeleton;
+                    skin.index = node.skin;
+                    node.babylonMesh.skeleton = this._loadSkin(skin);
                 }
                 if (node.camera !== undefined) {
                     // TODO: handle cameras
                 }
                 return true;
             };
-            GLTFLoader.prototype._loadMeshData = function (node, mesh, babylonMesh) {
+            GLTFLoader.prototype._loadMesh = function (node, mesh) {
                 var _this = this;
-                babylonMesh.name = mesh.name || babylonMesh.name;
-                var babylonMultiMaterial = new BABYLON.MultiMaterial(babylonMesh.name, this._babylonScene);
-                babylonMesh.material = babylonMultiMaterial;
-                var geometry = new BABYLON.Geometry(babylonMesh.name, this._babylonScene, null, false, babylonMesh);
+                node.babylonMesh.name = mesh.name || node.babylonMesh.name;
+                var babylonMultiMaterial = new BABYLON.MultiMaterial(node.babylonMesh.name, this._babylonScene);
+                node.babylonMesh.material = babylonMultiMaterial;
+                var geometry = new BABYLON.Geometry(node.babylonMesh.name, this._babylonScene, null, false, node.babylonMesh);
                 var vertexData = new BABYLON.VertexData();
                 vertexData.positions = [];
                 vertexData.indices = [];
@@ -2796,9 +2746,9 @@ var BABYLON;
                         // TODO: handle other primitive modes
                         throw new Error("Not implemented");
                     }
-                    this_1._createMorphTargets(node, mesh, primitive, babylonMesh);
+                    this_1._createMorphTargets(node, mesh, primitive, node.babylonMesh);
                     this_1._loadVertexDataAsync(primitive, function (subVertexData) {
-                        _this._loadMorphTargetsData(mesh, primitive, subVertexData, babylonMesh);
+                        _this._loadMorphTargetsData(mesh, primitive, subVertexData, node.babylonMesh);
                         subMeshInfos.push({
                             materialIndex: i,
                             verticesStart: vertexData.positions.length,
@@ -2817,7 +2767,7 @@ var BABYLON;
                                         }
                                         if (_this._parent.onBeforeMaterialReadyAsync) {
                                             _this.addLoaderPendingData(material);
-                                            _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
+                                            _this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, function () {
                                                 babylonMultiMaterial.subMaterials[i] = babylonMaterial;
                                                 _this.removeLoaderPendingData(material);
                                             });
@@ -2835,8 +2785,8 @@ var BABYLON;
                             subMeshInfos.forEach(function (info) { return info.loadMaterial(); });
                             // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                             // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
-                            babylonMesh.subMeshes = [];
-                            subMeshInfos.forEach(function (info) { return new BABYLON.SubMesh(info.materialIndex, info.verticesStart, info.verticesCount, info.indicesStart, info.indicesCount, babylonMesh); });
+                            node.babylonMesh.subMeshes = [];
+                            subMeshInfos.forEach(function (info) { return new BABYLON.SubMesh(info.materialIndex, info.verticesStart, info.verticesCount, info.indicesStart, info.indicesCount, node.babylonMesh); });
                         }
                     });
                 };
@@ -2974,7 +2924,7 @@ var BABYLON;
                     _loop_3();
                 }
             };
-            GLTFLoader.prototype._loadTransform = function (node, babylonMesh) {
+            GLTFLoader.prototype._loadTransform = function (node) {
                 var position = BABYLON.Vector3.Zero();
                 var rotation = BABYLON.Quaternion.Identity();
                 var scaling = BABYLON.Vector3.One();
@@ -2990,9 +2940,64 @@ var BABYLON;
                     if (node.scale)
                         scaling = BABYLON.Vector3.FromArray(node.scale);
                 }
-                babylonMesh.position = position;
-                babylonMesh.rotationQuaternion = rotation;
-                babylonMesh.scaling = scaling;
+                node.babylonMesh.position = position;
+                node.babylonMesh.rotationQuaternion = rotation;
+                node.babylonMesh.scaling = scaling;
+            };
+            GLTFLoader.prototype._loadSkin = function (skin) {
+                var _this = this;
+                var skeletonId = "skeleton" + skin.index;
+                skin.babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
+                if (skin.inverseBindMatrices === undefined) {
+                    this._loadBones(skin, null);
+                }
+                else {
+                    var accessor = this._gltf.accessors[skin.inverseBindMatrices];
+                    this._loadAccessorAsync(accessor, function (data) {
+                        _this._loadBones(skin, data);
+                    });
+                }
+                return skin.babylonSkeleton;
+            };
+            GLTFLoader.prototype._createBone = function (node, skin, parent, localMatrix, baseMatrix, index) {
+                var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+                node.babylonBones = node.babylonBones || {};
+                node.babylonBones[skin.index] = babylonBone;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
+                node.babylonAnimationTargets.push(babylonBone);
+                return babylonBone;
+            };
+            GLTFLoader.prototype._loadBones = function (skin, inverseBindMatrixData) {
+                var babylonBones = {};
+                for (var i = 0; i < skin.joints.length; i++) {
+                    var node = this._gltf.nodes[skin.joints[i]];
+                    this._loadBone(node, skin, inverseBindMatrixData, babylonBones);
+                }
+            };
+            GLTFLoader.prototype._loadBone = function (node, skin, inverseBindMatrixData, babylonBones) {
+                var babylonBone = babylonBones[node.index];
+                if (babylonBone) {
+                    return babylonBone;
+                }
+                var boneIndex = skin.joints.indexOf(node.index);
+                var baseMatrix = BABYLON.Matrix.Identity();
+                if (inverseBindMatrixData && boneIndex !== -1) {
+                    baseMatrix = BABYLON.Matrix.FromArray(inverseBindMatrixData, boneIndex * 16);
+                    baseMatrix.invertToRef(baseMatrix);
+                }
+                var babylonParentBone;
+                if (node.index != skin.skeleton && node.parent) {
+                    babylonParentBone = this._loadBone(node.parent, skin, inverseBindMatrixData, babylonBones);
+                    baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
+                }
+                babylonBone = this._createBone(node, skin, babylonParentBone, this._getNodeMatrix(node), baseMatrix, boneIndex);
+                babylonBones[node.index] = babylonBone;
+                return babylonBone;
+            };
+            GLTFLoader.prototype._getNodeMatrix = function (node) {
+                return node.matrix ?
+                    BABYLON.Matrix.FromArray(node.matrix) :
+                    BABYLON.Matrix.Compose(node.scale ? BABYLON.Vector3.FromArray(node.scale) : BABYLON.Vector3.One(), node.rotation ? BABYLON.Quaternion.FromArray(node.rotation) : BABYLON.Quaternion.Identity(), node.translation ? BABYLON.Vector3.FromArray(node.translation) : BABYLON.Vector3.Zero());
             };
             GLTFLoader.prototype._traverseNodes = function (indices, action, parentNode) {
                 if (parentNode === void 0) { parentNode = null; }

Різницю між файлами не показано, бо вона завелика
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 39 - 2
gui/src/controls/inputText.ts

@@ -3,8 +3,10 @@
 module BABYLON.GUI {
     export class InputText extends Control implements IFocusableControl {
         private _text = "";
+        private _placeholderText = "";
         private _background = "black";   
         private _focusedBackground = "black";   
+        private _placeholderColor = "gray";   
         private _thickness = 1;
         private _margin = new ValueAndUnit(10, ValueAndUnit.UNITMODE_PIXEL);
         private _autoStretchWidth = true;        
@@ -99,7 +101,32 @@ module BABYLON.GUI {
 
             this._background = value;
             this._markAsDirty();
+        }  
+
+        public get placeholderColor(): string {
+            return this._placeholderColor;
+        }
+
+        public set placeholderColor(value: string) {
+            if (this._placeholderColor === value) {
+                return;
+            }
+
+            this._placeholderColor = value;
+            this._markAsDirty();
         }          
+        
+        public get placeholderText(): string {
+            return this._placeholderText;
+        }
+
+        public set placeholderText(value: string) {
+            if (this._placeholderText === value) {
+                return;
+            }
+            this._placeholderText = value;
+            this._markAsDirty();
+        }        
 
         public get text(): string {
             return this._text;
@@ -256,7 +283,17 @@ module BABYLON.GUI {
                     context.fillStyle = this.color;
                 }
 
-                let textWidth = context.measureText(this._text).width;   
+                let text = this._text;
+
+                if (!this._isFocused && !this._text && this._placeholderText) {  
+                    text = this._placeholderText;
+
+                    if (this._placeholderColor) {
+                        context.fillStyle = this._placeholderColor;
+                    }
+                }
+
+                let textWidth = context.measureText(text).width;   
                 let marginWidth = this._margin.getValueInPixel(this._host, parentMeasure.width) * 2;
                 if (this._autoStretchWidth) {
                     this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), textWidth + marginWidth) + "px";
@@ -278,7 +315,7 @@ module BABYLON.GUI {
                     this._scrollLeft = clipTextLeft;
                 }
 
-                context.fillText(this._text, this._scrollLeft, this._currentMeasure.top + rootY);
+                context.fillText(text, this._scrollLeft, this._currentMeasure.top + rootY);
 
                 // Cursor
                 if (this._isFocused) {         

+ 2 - 2
inspector/index.html

@@ -26,7 +26,7 @@
         }
         
         canvas {
-            width: 75%;
+            width: 100%;
         }
         
         #inspector {
@@ -40,7 +40,7 @@
 
     <div id="wrapper">
         <canvas id='game-canvas'></canvas>
-        <div id="inspector"></div>
+        <!-- <div id="inspector"></div> -->
     </div>
 
 

+ 3 - 1
inspector/sass/_tabPanel.scss

@@ -1,5 +1,7 @@
 .tab-panel {
-    height:100%;     
+    height:100%;   
+    overflow-y: auto;
+
     &.searchable {
         height:calc(100% - #{$searchbar-height} - 10px);   
     }  

+ 2 - 1
inspector/sass/main.scss

@@ -23,6 +23,7 @@
   // The panel containing the two subpanel : tree and details
   .insp-right-panel {
     width: 750px;
+    overflow-y: auto;
 
     &.popupmode {
       width:100% !important;
@@ -97,4 +98,4 @@
   @import "tabbar";
   @import "toolbar";
   @import "searchbar";
-}
+}

+ 1 - 14
inspector/src/adapters/CameraAdapter.ts

@@ -24,20 +24,7 @@ module INSPECTOR {
         
         /** Returns the list of properties to be displayed for this adapter */
         public getProperties() : Array<PropertyLine> {
-           let propertiesLines : Array<PropertyLine> = [];
-           let camToDisplay = [];
-           // The if is there to work with the min version of babylon
-            if (this._obj instanceof BABYLON.ArcRotateCamera) {
-                camToDisplay =  PROPERTIES['ArcRotateCamera'].properties;
-            } else if (this._obj instanceof BABYLON.FreeCamera) {
-                camToDisplay =  PROPERTIES['FreeCamera'].properties; 
-            }
-                
-            for (let dirty of camToDisplay) {
-                let infos = new Property(dirty, this._obj);
-                propertiesLines.push(new PropertyLine(infos));
-            }
-            return propertiesLines; 
+            return Helpers.GetAllLinesProperties(this._obj);
         }
         
         public getTools() : Array<AbstractTreeTool> {

+ 1 - 92
inspector/src/adapters/GUIAdapter.ts

@@ -24,98 +24,7 @@ module INSPECTOR {
 
         /** Returns the list of properties to be displayed for this adapter */
         public getProperties(): Array<PropertyLine> {
-            let propertiesLines: Array<PropertyLine> = [];
-
-            for (let dirty of PROPERTIES['Control'].properties) {
-                let infos = new Property(dirty, this._obj);
-                propertiesLines.push(new PropertyLine(infos));
-            }
-
-            if(this._obj instanceof BABYLON.GUI.Button){
-                for (let dirty of PROPERTIES['Button'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.ColorPicker){
-                for (let dirty of PROPERTIES['ColorPicker'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.Checkbox){
-                for (let dirty of PROPERTIES['Checkbox'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.Ellipse){
-                for (let dirty of PROPERTIES['Ellipse'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.Image){
-                for (let dirty of PROPERTIES['Image'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.Line){
-                for (let dirty of PROPERTIES['Line'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.RadioButton){
-                for (let dirty of PROPERTIES['RadioButton'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.Rectangle){
-                for (let dirty of PROPERTIES['Rectangle'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.Slider){
-                for (let dirty of PROPERTIES['Slider'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.StackPanel){
-                for (let dirty of PROPERTIES['StackPanel'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.TextBlock){
-                for (let dirty of PROPERTIES['TextBlock'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            if(this._obj instanceof BABYLON.GUI.Container){
-                for (let dirty of PROPERTIES['Container'].properties) {
-                    let infos = new Property(dirty, this._obj);
-                    propertiesLines.push(new PropertyLine(infos));
-                }
-            }
-
-            return propertiesLines;
+            return Helpers.GetAllLinesProperties(this._obj);
         }
 
         public getTools(): Array<AbstractTreeTool> {

+ 1 - 16
inspector/src/adapters/LightAdapter.ts

@@ -3,15 +3,6 @@ module INSPECTOR {
     export class LightAdapter 
         extends Adapter 
         implements IToolVisible{
-
-        private static _PROPERTIES = [
-            'position',
-            'diffuse', 
-            'intensity', 
-            'radius', 
-            'range', 
-            'specular'
-        ];
         
         constructor(obj:BABYLON.Light) {
             super(obj);
@@ -33,13 +24,7 @@ module INSPECTOR {
         
         /** Returns the list of properties to be displayed for this adapter */
         public getProperties() : Array<PropertyLine> {
-           let propertiesLines : Array<PropertyLine> = [];
-                
-            for (let dirty of LightAdapter._PROPERTIES) {
-                let infos = new Property(dirty, this._obj);
-                propertiesLines.push(new PropertyLine(infos));
-            }
-            return propertiesLines; 
+            return Helpers.GetAllLinesProperties(this._obj);
         }
         
         public getTools() : Array<AbstractTreeTool> {

+ 1 - 14
inspector/src/adapters/MaterialAdapter.ts

@@ -23,20 +23,7 @@ module INSPECTOR {
         
         /** Returns the list of properties to be displayed for this adapter */
         public getProperties() : Array<PropertyLine> {
-            let propertiesLines : Array<PropertyLine> = [];
-            let propToDisplay = [];
-            // The if is there to work with the min version of babylon
-            if (this._obj instanceof BABYLON.StandardMaterial) {
-                propToDisplay =  PROPERTIES['StandardMaterial'].properties;
-            } else if (this._obj instanceof BABYLON.PBRMaterial) {
-                propToDisplay =  PROPERTIES['PBRMaterial'].properties;
-            }
-
-            for (let dirty of propToDisplay) {
-                let infos = new Property(dirty, this._obj);
-                propertiesLines.push(new PropertyLine(infos));
-            }
-            return propertiesLines; 
+            return Helpers.GetAllLinesProperties(this._obj);
         }
         
         /** No tools for a material adapter */

+ 1 - 7
inspector/src/adapters/MeshAdapter.ts

@@ -27,13 +27,7 @@ module INSPECTOR {
 
         /** Returns the list of properties to be displayed for this adapter */
         public getProperties(): Array<PropertyLine> {
-            let propertiesLines: Array<PropertyLine> = [];
-
-            for (let dirty of PROPERTIES['Mesh'].properties) {
-                let infos = new Property(dirty, this._obj);
-                propertiesLines.push(new PropertyLine(infos));
-            }
-            return propertiesLines;
+            return Helpers.GetAllLinesProperties(this._obj);
         }
 
         public getTools(): Array<AbstractTreeTool> {

+ 1 - 7
inspector/src/adapters/PhysicsImpostorAdapter.ts

@@ -29,13 +29,7 @@ module INSPECTOR {
 
         /** Returns the list of properties to be displayed for this adapter */
         public getProperties(): Array<PropertyLine> {
-            let propertiesLines: Array<PropertyLine> = [];
-
-            for (let dirty of PROPERTIES['PhysicsImpostor'].properties) {
-                let infos = new Property(dirty, this._obj);
-                propertiesLines.push(new PropertyLine(infos));
-            }
-            return propertiesLines;
+            return Helpers.GetAllLinesProperties(this._obj);
         }
 
         public getTools(): Array<AbstractTreeTool> {

+ 1 - 12
inspector/src/adapters/SoundAdapter.ts

@@ -24,18 +24,7 @@ module INSPECTOR {
 
         /** Returns the list of properties to be displayed for this adapter */
         public getProperties(): Array<PropertyLine> {
-            let propertiesLines: Array<PropertyLine> = [];
-            let camToDisplay = [];
-            // The if is there to work with the min version of babylon
-
-            let soundProperties = PROPERTIES['Sound'].properties;
-
-
-            for (let dirty of soundProperties) {
-                let infos = new Property(dirty, this._obj);
-                propertiesLines.push(new PropertyLine(infos));
-            }
-            return propertiesLines;
+            return Helpers.GetAllLinesProperties(this._obj);
         }
 
         public getTools(): Array<AbstractTreeTool> {

+ 71 - 20
inspector/src/details/PropertyLine.ts

@@ -81,6 +81,16 @@ module INSPECTOR {
         private _validateInputHandler: EventListener;
         /** Handler used to validate the input by pressing 'esc' */
         private _escapeInputHandler: EventListener;
+        /** Handler used on focus out */
+        private _focusOutInputHandler: EventListener;
+        /** Handler used to get mouse position */
+        private _onMouseDownHandler: EventListener;
+        private _onMouseDragHandler: EventListener;
+        private _onMouseUpHandler: EventListener;
+        /** Save previous Y mouse position */
+        private _prevY: number;
+        /**Save value while slider is on */
+        private _preValue: number;
 
         constructor(prop: Property, parent?: PropertyLine, level: number = 0) {
             this._property = prop;
@@ -115,6 +125,7 @@ module INSPECTOR {
                 this._valueDiv.addEventListener('click', this._displayInputHandler);
                 this._input.addEventListener('keypress', this._validateInputHandler);
                 this._input.addEventListener('keydown', this._escapeInputHandler);
+                this._input.addEventListener('focusout', this._focusOutInputHandler);
             }
 
             // Add this property to the scheduler
@@ -127,7 +138,6 @@ module INSPECTOR {
          * - enters updates the property
          */
         private _initInput() {
-
             // Create the input element
             this._input = document.createElement('input') as HTMLInputElement;
             this._input.setAttribute('type', 'text');
@@ -136,6 +146,11 @@ module INSPECTOR {
             this._displayInputHandler = this._displayInput.bind(this);
             this._validateInputHandler = this._validateInput.bind(this);
             this._escapeInputHandler = this._escapeInput.bind(this);
+            this._focusOutInputHandler = this.update.bind(this);
+
+            this._onMouseDownHandler = this._onMouseDown.bind(this);
+            this._onMouseDragHandler = this._onMouseDrag.bind(this);
+            this._onMouseUpHandler = this._onMouseUp.bind(this);
         }
 
         /** 
@@ -144,31 +159,33 @@ module INSPECTOR {
          */
         private _validateInput(e: KeyboardEvent) {
             if (e.keyCode == 13) {
-                // Enter : validate the new value
-                let newValue:any = this._input.value;
-
-                this.updateObject();
-
-                if(typeof this._property.value === 'number'){
-                    this._property.value = parseFloat(newValue);
-                }else{
-                    this._property.value = newValue;
-                }
-                // Remove input
-                this.update();
-                // resume scheduler
-                Scheduler.getInstance().pause = false;
-
+                this.validateInput(this._input.value);
             } else if (e.keyCode == 27) {
                 // Esc : remove input
                 this.update();
             }
         }
 
+        public validateInput(value: any): void {
+            this.updateObject();
+
+            if(typeof this._property.value === 'number'){
+                this._property.value = parseFloat(value);
+            }else{
+                this._property.value = value;
+            }
+            // Remove input
+            this.update();
+            // resume scheduler
+            Scheduler.getInstance().pause = false;
+        }
+
         /** 
          * On escape : removes the input
          */
         private _escapeInput(e: KeyboardEvent) {
+            // Remove focus out handler
+            this._input.removeEventListener('focusout', this._focusOutInputHandler);
             if (e.keyCode == 27) {
                 // Esc : remove input
                 this.update();
@@ -196,7 +213,12 @@ module INSPECTOR {
             this._valueDiv.textContent = "";
             this._input.value = valueTxt;
             this._valueDiv.appendChild(this._input);
+            this._input.focus();
 
+            if(typeof this.value === 'number') {
+                this._input.addEventListener('mousedown', this._onMouseDownHandler);
+            }
+            this._input.addEventListener('focusout', this._focusOutInputHandler);
             // Pause the scheduler
             Scheduler.getInstance().pause = true;
         }
@@ -233,10 +255,10 @@ module INSPECTOR {
          * type of the property.
          */
         private _createElements() {
-
             // Colors
             if (this.type == 'Color3' || this.type == 'Color4') {
-                this._elements.push(new ColorElement(this.value));
+                this._elements.push(new ColorPickerElement(this.value, this));
+                //this._elements.push(new ColorElement(this.value));
             }
             // Texture
             if (this.type == 'Texture') {
@@ -256,7 +278,6 @@ module INSPECTOR {
         // - If the type is complex, but instance of Vector2, Size, display the type and its tostring
         // - If the type is another one, display the Type
         private _displayValueContent() {
-
             let value = this.value;
             // If the value is a number, truncate it if needed
             if (typeof value === 'number') {
@@ -372,6 +393,36 @@ module INSPECTOR {
                 }
             }
         }
-    }
 
+
+        /**
+         * Refresh mouse position on y axis
+         * @param e 
+         */
+        private _onMouseDrag(e: MouseEvent): void {      
+            const diff = this._prevY - e.clientY;
+            this._input.value = (this._preValue + diff).toString();
+        }
+
+        /**
+         * Save new value from slider
+         * @param e 
+         */
+        private _onMouseUp(e: MouseEvent): void {
+            window.removeEventListener('mousemove', this._onMouseDragHandler);
+            window.removeEventListener('mouseup', this._onMouseUpHandler);
+            this._prevY = e.clientY;
+        }
+      
+        /**
+         * Start record mouse position
+         * @param e 
+         */
+        private _onMouseDown(e: MouseEvent): void {
+            this._prevY = e.clientY;
+            this._preValue = this.value;
+            window.addEventListener('mousemove', this._onMouseDragHandler);
+            window.addEventListener('mouseup', this._onMouseUpHandler);
+        }
+    }
 }

+ 0 - 1
inspector/src/gui/ColorElement.ts

@@ -17,7 +17,6 @@
             }
         }
         
-        
         private _toRgba(color:BABYLON.Color4|BABYLON.Color3) : string {
             if (color) {
                 let r = (color.r * 255) | 0;

+ 61 - 0
inspector/src/gui/ColorPickerElement.ts

@@ -0,0 +1,61 @@
+module INSPECTOR {
+    /**
+     * Represents a html div element. 
+     * The div is built when an instance of BasicElement is created.
+     */
+    export class ColorPickerElement extends BasicElement {
+        
+        protected _input : HTMLInputElement;
+        private pline : PropertyLine;
+        
+        constructor(color:BABYLON.Color4|BABYLON.Color3, propertyLine: PropertyLine) {
+            super();
+            let scheduler = Scheduler.getInstance();
+            this._div.className = 'color-element';
+            this._div.style.backgroundColor = this._toRgba(color);
+            this._div.style.top = "5px";
+            this.pline = propertyLine;
+
+            this._input = Helpers.CreateInput();  
+            this._input.type = 'color';
+            this._input.style.opacity = "0";
+            this._input.style.width = '10px';
+            this._input.style.height = '15px';
+            this._input.value = color.toHexString();
+            
+            this._input.addEventListener('input', (e) => {
+                console.log('Color', this._input.value, this.pline)
+                this.pline.validateInput(BABYLON.Color3.FromHexString(this._input.value));
+                scheduler.pause = false;
+            });
+            
+            this._div.appendChild(this._input);
+
+            this._input.addEventListener('click', (e) => {
+                scheduler.pause = true;
+            });
+        }
+
+        public update(color?:BABYLON.Color4|BABYLON.Color3) {
+            if (color) {
+                this._div.style.backgroundColor = this._toRgba(color);
+                this._input.value = color.toHexString();
+            }
+        }
+
+        private _toRgba(color:BABYLON.Color4|BABYLON.Color3) : string {
+            if (color) {
+                let r = (color.r * 255) | 0;
+                let g = (color.g * 255) | 0;
+                let b = (color.b * 255) | 0;
+                let a = 1;
+                if (color instanceof BABYLON.Color4) {
+                    let a = (color as BABYLON.Color4).a;
+                }
+                return `rgba(${r}, ${g}, ${b}, ${a})`;
+            } else {
+                return '';
+            }
+        }
+    }
+}

+ 26 - 0
inspector/src/helpers/Helpers.ts

@@ -98,6 +98,13 @@ module INSPECTOR {
             return Helpers.CreateElement('div', className, parent);
         }
 
+        /**
+         * Useful function used to create a input
+         */
+        public static CreateInput(className?: string, parent?: HTMLElement): HTMLInputElement {
+            return <HTMLInputElement>Helpers.CreateElement('input', className, parent);
+        }
+
         public static CreateElement(element: string, className?: string, parent?: HTMLElement): HTMLElement {
             let elem = Inspector.DOCUMENT.createElement(element);
 
@@ -163,5 +170,24 @@ module INSPECTOR {
             }
             return name.indexOf("###") === 0 && name.lastIndexOf("###") === (name.length - 3);
         }
+
+        /**
+         * Return an array of PropertyLine for an obj
+         * @param obj 
+         */
+        public static GetAllLinesProperties(obj: any): Array<PropertyLine> {
+            let propertiesLines: Array<PropertyLine> = [];
+            
+            for (let prop in obj) {
+                /**
+                 * No private and no function
+                 */
+                if(prop.substring(0, 1) !== '_' && typeof obj[prop] !== 'function') {
+                    let infos = new Property(prop, obj);
+                    propertiesLines.push(new PropertyLine(infos));
+                }
+            }
+            return propertiesLines;
+        }
     }
 }

+ 94 - 86
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -286,92 +286,29 @@ module BABYLON.GLTF2 {
                 nodeIndices = filteredNodeIndices;
             }
 
-            this._traverseNodes(nodeIndices, node => this._loadSkin(node));
-            this._traverseNodes(nodeIndices, node => this._loadMesh(node));
+            this._traverseNodes(nodeIndices, node => this._loadNode(node));
         }
 
-        private _loadSkin(node: IGLTFNode): boolean {
-            if (node.skin !== undefined) {
-                var skin = this._gltf.skins[node.skin];
-                var skeletonId = "skeleton" + node.skin;
-                skin.babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
-                skin.index = node.skin;
-
-                for (var i = 0; i < skin.joints.length; i++) {
-                    this._createBone(this._gltf.nodes[skin.joints[i]], skin);
-                }
-
-                if (skin.skeleton === undefined) {
-                    // TODO: handle when skeleton is not defined
-                    throw new Error("Not implemented");
-                }
-
-                if (skin.inverseBindMatrices === undefined) {
-                    // TODO: handle when inverse bind matrices are not defined
-                    throw new Error("Not implemented");
-                }
-
-                var accessor = this._gltf.accessors[skin.inverseBindMatrices];
-                this._loadAccessorAsync(accessor, data => {
-                    this._traverseNode(skin.skeleton, (node, index, parent) => this._updateBone(node, parent, skin, <Float32Array>data));
-                });
-            }
-
-            return true;
-        }
-
-        private _updateBone(node: IGLTFNode, parentNode: IGLTFNode, skin: IGLTFSkin, inverseBindMatrixData: Float32Array): boolean {
-            var jointIndex = skin.joints.indexOf(node.index);
-            if (jointIndex === -1) {
-                this._createBone(node, skin);
-            }
-
-            var babylonBone = node.babylonSkinToBones[skin.index];
-
-            // TODO: explain the math
-            var matrix = jointIndex === -1 ? Matrix.Identity() : Matrix.FromArray(inverseBindMatrixData, jointIndex * 16);
-            matrix.invertToRef(matrix);
-            if (parentNode) {
-                babylonBone.setParent(parentNode.babylonSkinToBones[skin.index], false);
-                matrix.multiplyToRef(babylonBone.getParent().getInvertedAbsoluteTransform(), matrix);
-            }
-
-            babylonBone.updateMatrix(matrix);
-            return true;
-        }
-
-        private _createBone(node: IGLTFNode, skin: IGLTFSkin): Bone {
-            var babylonBone = new Bone(node.name || "bone" + node.index, skin.babylonSkeleton);
-
-            node.babylonSkinToBones = node.babylonSkinToBones || {};
-            node.babylonSkinToBones[skin.index] = babylonBone;
-
-            node.babylonAnimationTargets = node.babylonAnimationTargets || [];
-            node.babylonAnimationTargets.push(babylonBone);
-
-            return babylonBone;
-        }
+        private _loadNode(node: IGLTFNode): boolean {
+            node.babylonMesh = new Mesh(node.name || "mesh" + node.index, this._babylonScene);
+            node.babylonMesh.isVisible = false;
 
-        private _loadMesh(node: IGLTFNode): boolean {
-            var babylonMesh = new Mesh(node.name || "mesh" + node.index, this._babylonScene);
-            babylonMesh.isVisible = false;
-
-            this._loadTransform(node, babylonMesh);
+            this._loadTransform(node);
 
             if (node.mesh !== undefined) {
                 var mesh = this._gltf.meshes[node.mesh];
-                this._loadMeshData(node, mesh, babylonMesh);
+                this._loadMesh(node, mesh);
             }
 
-            babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
-            node.babylonMesh = babylonMesh;
+            node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
 
             node.babylonAnimationTargets = node.babylonAnimationTargets || [];
             node.babylonAnimationTargets.push(node.babylonMesh);
 
             if (node.skin !== undefined) {
                 var skin = this._gltf.skins[node.skin];
-                babylonMesh.skeleton = skin.babylonSkeleton;
+                skin.index = node.skin;
+                node.babylonMesh.skeleton = this._loadSkin(skin);
             }
 
             if (node.camera !== undefined) {
@@ -381,13 +318,13 @@ module BABYLON.GLTF2 {
             return true;
         }
 
-        private _loadMeshData(node: IGLTFNode, mesh: IGLTFMesh, babylonMesh: Mesh): void {
-            babylonMesh.name = mesh.name || babylonMesh.name;
+        private _loadMesh(node: IGLTFNode, mesh: IGLTFMesh): void {
+            node.babylonMesh.name = mesh.name || node.babylonMesh.name;
 
-            var babylonMultiMaterial = new MultiMaterial(babylonMesh.name, this._babylonScene);
-            babylonMesh.material = babylonMultiMaterial;
+            var babylonMultiMaterial = new MultiMaterial(node.babylonMesh.name, this._babylonScene);
+            node.babylonMesh.material = babylonMultiMaterial;
 
-            var geometry = new Geometry(babylonMesh.name, this._babylonScene, null, false, babylonMesh);
+            var geometry = new Geometry(node.babylonMesh.name, this._babylonScene, null, false, node.babylonMesh);
             var vertexData = new VertexData();
             vertexData.positions = [];
             vertexData.indices = [];
@@ -403,10 +340,10 @@ module BABYLON.GLTF2 {
                     throw new Error("Not implemented");
                 }
 
-                this._createMorphTargets(node, mesh, primitive, babylonMesh);
+                this._createMorphTargets(node, mesh, primitive, node.babylonMesh);
 
                 this._loadVertexDataAsync(primitive, subVertexData => {
-                    this._loadMorphTargetsData(mesh, primitive, subVertexData, babylonMesh);
+                    this._loadMorphTargetsData(mesh, primitive, subVertexData, node.babylonMesh);
 
                     subMeshInfos.push({
                         materialIndex: i,
@@ -427,7 +364,7 @@ module BABYLON.GLTF2 {
                                     
                                     if (this._parent.onBeforeMaterialReadyAsync) {
                                         this.addLoaderPendingData(material);
-                                        this._parent.onBeforeMaterialReadyAsync(babylonMaterial, babylonMesh, babylonMultiMaterial.subMaterials[i] != null, () => {
+                                        this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, babylonMultiMaterial.subMaterials[i] != null, () => {
                                             babylonMultiMaterial.subMaterials[i] = babylonMaterial;
                                             this.removeLoaderPendingData(material);
                                         });
@@ -448,8 +385,8 @@ module BABYLON.GLTF2 {
 
                         // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                         // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
-                        babylonMesh.subMeshes = [];
-                        subMeshInfos.forEach(info => new SubMesh(info.materialIndex, info.verticesStart, info.verticesCount, info.indicesStart, info.indicesCount, babylonMesh));
+                        node.babylonMesh.subMeshes = [];
+                        subMeshInfos.forEach(info => new SubMesh(info.materialIndex, info.verticesStart, info.verticesCount, info.indicesStart, info.indicesCount, node.babylonMesh));
                     }
                 });
             }
@@ -582,7 +519,7 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private _loadTransform(node: IGLTFNode, babylonMesh: Mesh): void {
+        private _loadTransform(node: IGLTFNode): void {
             var position: Vector3 = Vector3.Zero();
             var rotation: Quaternion = Quaternion.Identity();
             var scaling: Vector3 = Vector3.One();
@@ -597,9 +534,80 @@ module BABYLON.GLTF2 {
                 if (node.scale) scaling = Vector3.FromArray(node.scale);
             }
 
-            babylonMesh.position = position;
-            babylonMesh.rotationQuaternion = rotation;
-            babylonMesh.scaling = scaling;
+            node.babylonMesh.position = position;
+            node.babylonMesh.rotationQuaternion = rotation;
+            node.babylonMesh.scaling = scaling;
+        }
+
+        private _loadSkin(skin: IGLTFSkin): Skeleton {
+            var skeletonId = "skeleton" + skin.index;
+            skin.babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
+
+            if (skin.inverseBindMatrices === undefined) {
+                this._loadBones(skin, null);
+            }
+            else {
+                var accessor = this._gltf.accessors[skin.inverseBindMatrices];
+                this._loadAccessorAsync(accessor, data => {
+                    this._loadBones(skin, <Float32Array>data);
+                });
+            }
+
+            return skin.babylonSkeleton;
+        }
+
+        private _createBone(node: IGLTFNode, skin: IGLTFSkin, parent: Bone, localMatrix: Matrix, baseMatrix: Matrix, index: number): Bone {
+            var babylonBone = new Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+
+            node.babylonBones = node.babylonBones || {};
+            node.babylonBones[skin.index] = babylonBone;
+
+            node.babylonAnimationTargets = node.babylonAnimationTargets || [];
+            node.babylonAnimationTargets.push(babylonBone);
+
+            return babylonBone;
+        }
+
+        private _loadBones(skin: IGLTFSkin, inverseBindMatrixData: Float32Array): void {
+            var babylonBones: { [index: number]: Bone } = {};
+            for (var i = 0; i < skin.joints.length; i++) {
+                var node = this._gltf.nodes[skin.joints[i]];
+                this._loadBone(node, skin, inverseBindMatrixData, babylonBones);
+            }
+        }
+
+        private _loadBone(node: IGLTFNode, skin: IGLTFSkin, inverseBindMatrixData: Float32Array, babylonBones: { [index: number]: Bone }): Bone {
+            var babylonBone = babylonBones[node.index];
+            if (babylonBone) {
+                return babylonBone;
+            }
+
+            var boneIndex = skin.joints.indexOf(node.index);
+
+            var baseMatrix = Matrix.Identity();
+            if (inverseBindMatrixData && boneIndex !== -1) {
+                baseMatrix = Matrix.FromArray(inverseBindMatrixData, boneIndex * 16);
+                baseMatrix.invertToRef(baseMatrix);
+            }
+
+            var babylonParentBone: Bone;
+            if (node.index != skin.skeleton && node.parent) {
+                babylonParentBone = this._loadBone(node.parent, skin, inverseBindMatrixData, babylonBones);
+                baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
+            }
+
+            babylonBone = this._createBone(node, skin, babylonParentBone, this._getNodeMatrix(node), baseMatrix, boneIndex);
+            babylonBones[node.index] = babylonBone;
+            return babylonBone;
+        }
+
+        private _getNodeMatrix(node: IGLTFNode): Matrix {
+            return node.matrix ?
+                Matrix.FromArray(node.matrix) :
+                Matrix.Compose(
+                    node.scale ? Vector3.FromArray(node.scale) : Vector3.One(),
+                    node.rotation ? Quaternion.FromArray(node.rotation) : Quaternion.Identity(),
+                    node.translation ? Vector3.FromArray(node.translation) : Vector3.Zero());
         }
 
         private _traverseNodes(indices: number[], action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode = null): void {

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

@@ -193,7 +193,7 @@ module BABYLON.GLTF2 {
         indices?: number;
         material?: number;
         mode?: EMeshPrimitiveMode;
-        targets?: [ { [name: string]: number } ];
+        targets?: { [name: string]: number }[];
     }
 
     export interface IGLTFMesh extends IGLTFChildRootProperty {
@@ -216,7 +216,7 @@ module BABYLON.GLTF2 {
         index?: number;
         parent?: IGLTFNode;
         babylonMesh?: Mesh;
-        babylonSkinToBones?: { [skin: number]: Bone };
+        babylonBones?: { [skin: number]: Bone };
         babylonAnimationTargets?: Node[];
     }
 

+ 5 - 1
src/Animations/babylon.runtimeAnimation.ts

@@ -274,7 +274,11 @@
 
             //to and from cannot be the same key
             if(from === to) {
-                from++;
+                if (from > keys[0].frame) {
+                    from--;
+                } else if (to < keys[keys.length - 1].frame) {
+                    to++;
+                }
             }
             
             // Compute ratio

+ 2 - 2
src/Cameras/Inputs/babylon.arcRotateCameraKeyboardMoveInput.ts

@@ -19,10 +19,10 @@ module BABYLON {
         public keysReset = [220];
 
         @serialize()
-        public panningSensibility: number = 300.0;
+        public panningSensibility: number = 50.0;
 
         @serialize()
-        public zoomingSensibility: number = 50.0;
+        public zoomingSensibility: number = 25.0;
 
         @serialize()
         public useAltToZoom: boolean = true;

+ 78 - 24
src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts

@@ -14,14 +14,17 @@ module BABYLON {
         public angularSensibilityY = 1000.0;
 
         @serialize()
-        public pinchPrecision = 6.0;
+        public pinchPrecision = 12.0;
 
         @serialize()
-        public panningSensibility: number = 3000.0;
+        public panningSensibility: number = 1000.0;
 
         @serialize()
         public multiTouchPanning: boolean = true;
 
+        @serialize()
+        public multiTouchPanAndZoom: boolean = true;
+
         private _isPanClick: boolean = false;
         public pinchInwards = true;
 
@@ -39,8 +42,9 @@ module BABYLON {
             var cacheSoloPointer: { x: number, y: number, pointerId: number, type: any }; // cache pointer object for better perf on camera rotation
             var pointA: { x: number, y: number, pointerId: number, type: any }, pointB: { x: number, y: number, pointerId: number, type: any };
             var previousPinchSquaredDistance = 0;
-            var previousPinchDistance = 0;
-            var previousMultiTouchPanPosition: { x: number, y: number, isPaning: boolean } = { x: 0, y:0, isPaning: false };
+            var initialDistance = 0;
+            var twoFingerActivityCount = 0;
+            var previousMultiTouchPanPosition: { x: number, y: number, isPaning: boolean, isPinching: boolean } = { x: 0, y:0, isPaning: false, isPinching: false };
 
             this._pointerInput = (p, s) => {
                 var evt = <PointerEvent>p.event;
@@ -88,14 +92,34 @@ module BABYLON {
 
                     cacheSoloPointer = null;
                     previousPinchSquaredDistance = 0;
-                    previousPinchDistance = 0;
                     previousMultiTouchPanPosition.isPaning = false;
+                    previousMultiTouchPanPosition.isPinching = false;
+                    twoFingerActivityCount = 0;
+                    initialDistance = 0;
 
                     //would be better to use pointers.remove(evt.pointerId) for multitouch gestures, 
                     //but emptying completly pointers collection is required to fix a bug on iPhone : 
                     //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
                     //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
-                    pointA = pointB = undefined;
+                    if (engine.badOS) {
+                        pointA = pointB = undefined;
+                    }
+                    else {
+                        //only remove the impacted pointer in case of multitouch allowing on most 
+                        //platforms switching from rotate to zoom and pan seamlessly.
+                        if (pointB && pointA && pointA.pointerId == evt.pointerId) {
+                            pointA = pointB;
+                            pointB = undefined;
+                            cacheSoloPointer = { x: pointA.x, y: pointA.y, pointerId: pointA.pointerId, type: evt.pointerType };
+                        }
+                        else if (pointA && pointB && pointB.pointerId == evt.pointerId) {
+                            pointB = undefined;
+                            cacheSoloPointer = { x: pointA.x, y: pointA.y, pointerId: pointA.pointerId, type: evt.pointerType };
+                        }
+                        else {
+                            pointA = pointB = undefined;
+                        }
+                    }
 
                     if (!noPreventDefault) {
                         evt.preventDefault();
@@ -133,41 +157,69 @@ module BABYLON {
                         var distY = pointA.y - pointB.y;
                         var pinchSquaredDistance = (distX * distX) + (distY * distY);
                         var pinchDistance = Math.sqrt(pinchSquaredDistance);
+
                         if (previousPinchSquaredDistance === 0) {
+                            initialDistance = pinchDistance;
                             previousPinchSquaredDistance = pinchSquaredDistance;
-                            previousPinchDistance = pinchDistance;
+                            previousMultiTouchPanPosition.x = (pointA.x + pointB.x) / 2;
+                            previousMultiTouchPanPosition.y = (pointA.y + pointB.y) / 2;
                             return;
                         }
 
-                        if (pinchDistance > this.camera.panMaxFingersDistance || Math.abs(pinchDistance - previousPinchDistance) > this.camera.pinchToPanMaxDistance) {
+                        if (this.multiTouchPanAndZoom) {
                             this.camera
                                 .inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
                                 (this.pinchPrecision *
                                     ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
                                     direction);
-                            previousMultiTouchPanPosition.isPaning = false;
+                            
+                            if (this.panningSensibility !== 0) {
+                                var pointersCenterX = (pointA.x + pointB.x) / 2;
+                                var pointersCenterY = (pointA.y + pointB.y) / 2;
+                                var pointersCenterDistX = pointersCenterX - previousMultiTouchPanPosition.x;
+                                var pointersCenterDistY = pointersCenterY - previousMultiTouchPanPosition.y;
+
+                                previousMultiTouchPanPosition.x = pointersCenterX;
+                                previousMultiTouchPanPosition.y = pointersCenterY;
+
+                                this.camera.inertialPanningX += -(pointersCenterDistX) / (this.panningSensibility);
+                                this.camera.inertialPanningY += (pointersCenterDistY) / (this.panningSensibility);
+                            }
                         }
                         else {
-                            if (cacheSoloPointer.pointerId === ed.pointerId && this.panningSensibility !== 0 && this.multiTouchPanning) {
-                                if (!previousMultiTouchPanPosition.isPaning) {
-                                    previousMultiTouchPanPosition.isPaning = true;
-                                    previousMultiTouchPanPosition.x = ed.x;
-                                    previousMultiTouchPanPosition.y = ed.y;
-                                    return;
-                                }
+                            twoFingerActivityCount++;
 
-                                this.camera.inertialPanningX += -(ed.x - previousMultiTouchPanPosition.x) / this.panningSensibility;
-                                this.camera.inertialPanningY += (ed.y - previousMultiTouchPanPosition.y) / this.panningSensibility;
+                            if (previousMultiTouchPanPosition.isPinching || (twoFingerActivityCount < 20 && Math.abs(pinchDistance - initialDistance) > this.camera.pinchToPanMaxDistance)) {                   
+                                this.camera
+                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
+                                (this.pinchPrecision *
+                                    ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
+                                    direction);
+                                previousMultiTouchPanPosition.isPaning = false;
+                                previousMultiTouchPanPosition.isPinching = true;
+                            }
+                            else {
+                                if (cacheSoloPointer.pointerId === ed.pointerId && this.panningSensibility !== 0 && this.multiTouchPanning) {
+                                    if (!previousMultiTouchPanPosition.isPaning) {
+                                        previousMultiTouchPanPosition.isPaning = true;
+                                        previousMultiTouchPanPosition.isPinching = false;
+                                        previousMultiTouchPanPosition.x = ed.x;
+                                        previousMultiTouchPanPosition.y = ed.y;
+                                        return;
+                                    }
+
+                                    this.camera.inertialPanningX += -(ed.x - previousMultiTouchPanPosition.x) / (this.panningSensibility);
+                                    this.camera.inertialPanningY += (ed.y - previousMultiTouchPanPosition.y) / (this.panningSensibility);
+                                }
                             }
-                        }
 
-                        if (cacheSoloPointer.pointerId === evt.pointerId) {
-                            previousMultiTouchPanPosition.x = ed.x;
-                            previousMultiTouchPanPosition.y = ed.y;
+                            if (cacheSoloPointer.pointerId === evt.pointerId) {
+                                previousMultiTouchPanPosition.x = ed.x;
+                                previousMultiTouchPanPosition.y = ed.y;
+                            }
                         }
 
                         previousPinchSquaredDistance = pinchSquaredDistance;
-                        previousPinchDistance = pinchDistance;
                     }
                 }
             }
@@ -186,9 +238,11 @@ module BABYLON {
                 //this._keys = [];
                 pointA = pointB = undefined;
                 previousPinchSquaredDistance = 0;
-                previousPinchDistance = 0;
                 previousMultiTouchPanPosition.isPaning = false;
+                previousMultiTouchPanPosition.isPinching = false;
+                twoFingerActivityCount = 0;
                 cacheSoloPointer = null;
+                initialDistance = 0;
             };
 
             this._onMouseMove = evt => {

Різницю між файлами не показано, бо вона завелика
+ 129 - 36
src/Cameras/VR/babylon.vrExperienceHelper.ts


+ 24 - 26
src/Cameras/VR/babylon.webVRCamera.ts

@@ -40,7 +40,7 @@ module BABYLON {
     export class WebVRFreeCamera extends FreeCamera implements PoseControlled {
         public _vrDevice = null;
         public rawPose: DevicePose = null;
-        private _vrEnabled = false;
+        private _onVREnabled: (success: boolean) => void;
         private _specsVersion: number = 1.1;
         private _attached: boolean = false;
 
@@ -95,7 +95,20 @@ module BABYLON {
             }
 
             //enable VR
-            this.getEngine().initWebVR();
+            var engine = this.getEngine();
+            this._onVREnabled = (success:boolean) => { if (success) { this.initControllers(); } };
+            engine.onVRRequestPresentComplete.add(this._onVREnabled);
+            engine.initWebVR().add((event:IDisplayChangedEventArgs) => {
+                
+                this._vrDevice = event.vrDisplay;
+
+                //reset the rig parameters.
+                this.setCameraRigMode(Camera.RIG_MODE_WEBVR, { parentCamera: this, vrDisplay: this._vrDevice, frameData: this._frameData, specs: this._specsVersion });
+
+                if (this._attached && this._vrDevice) {
+                    this.getEngine().enableVR();
+                }
+            });
 
             //check specs version
             if (!window.VRFrameData) {
@@ -106,22 +119,6 @@ module BABYLON {
                 this._frameData = new VRFrameData();
             }
 
-            this.getEngine().getVRDevice(this.webVROptions.displayName, device => {
-                if (!device) {
-                    return;
-                }
-
-                this._vrEnabled = true;               
-                this._vrDevice = device;
-
-                //reset the rig parameters.
-                this.setCameraRigMode(Camera.RIG_MODE_WEBVR, { parentCamera: this, vrDisplay: this._vrDevice, frameData: this._frameData, specs: this._specsVersion });
-
-                if (this._attached) {
-                    this.getEngine().enableVR(this._vrDevice)
-                }
-            });                
-
             /**
              * The idea behind the following lines:
              * objects that have the camera as parent should actually have the rig cameras as a parent.
@@ -155,6 +152,11 @@ module BABYLON {
                 }
             });
         }
+        
+        public dispose(): void {
+            this.getEngine().onVRRequestPresentComplete.removeCallback(this._onVREnabled);
+            super.dispose();
+        }
 
         public getControllerByName(name: string): WebVRController {
             for (var gp of this.controllers) {
@@ -194,11 +196,11 @@ module BABYLON {
         } 
 
         public _checkInputs(): void {
-            if (this._vrEnabled) {
+            if (this._vrDevice && this._vrDevice.isPresenting) {
                 if (this._specsVersion === 1.1) {
                     this._vrDevice.getFrameData(this._frameData);
                 } else {
-                    //backwards comp
+                    // TODO: Does backwards comp need to be here any more? The Engine class doesn't support it any more.
                     let pose = this._vrDevice.getPose();
                     this._frameData.pose = pose;
                 }
@@ -244,12 +246,9 @@ module BABYLON {
 
             noPreventDefault = Camera.ForceAttachControlToAlwaysPreventDefault ? false : noPreventDefault;
 
-            if (this._vrEnabled) {
-                this.getEngine().enableVR(this._vrDevice);
+            if (this._vrDevice) {
+                this.getEngine().enableVR();
             }
-
-            // try to attach the controllers, if found.
-            this.initControllers();
         }
 
         public detachControl(element: HTMLElement): void {
@@ -257,7 +256,6 @@ module BABYLON {
             this.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
             
             super.detachControl(element);
-            this._vrEnabled = false;
             this._attached = false;
             this.getEngine().disableVR();
         }

+ 1 - 4
src/Cameras/babylon.arcRotateCamera.ts

@@ -57,10 +57,7 @@ module BABYLON {
         public inertialPanningY: number = 0;
 
         @serialize()
-        public pinchToPanMaxDistance: number = 3;
-
-        @serialize()
-        public panMaxFingersDistance: number = 100;
+        public pinchToPanMaxDistance: number = 20;
 
         @serialize()
         public panningDistanceLimit: number = null;

+ 3 - 2
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -1264,11 +1264,12 @@
                 scene.ambientColor.multiplyToRef(this._ambientColor, this._globalAmbientColor);
 
                 var eyePosition = scene._mirroredCameraPosition ? scene._mirroredCameraPosition : scene.activeCamera.globalPosition;
-                effect.setFloat4("vEyePosition", 
+                var invertNormal = (scene.useRightHandedSystem === (scene._mirroredCameraPosition != null));
+                effect.setFloat4("vEyePosition",
                     eyePosition.x,
                     eyePosition.y,
                     eyePosition.z,
-                    scene._mirroredCameraPosition ? -1 : 1);
+                    invertNormal ? -1 : 1);
                 effect.setColor3("vAmbientColor", this._globalAmbientColor);
             }
 

+ 5 - 5
src/Materials/babylon.effect.ts

@@ -398,14 +398,14 @@
             result = result.replace(/[ \t]attribute/g, " in");
             
             if (isFragment) {
-                result = result.replace(/texture2DLodEXT\(/g, "textureLod(");
-                result = result.replace(/textureCubeLodEXT\(/g, "textureLod(");
-                result = result.replace(/texture2D\(/g, "texture(");
-                result = result.replace(/textureCube\(/g, "texture(");
+                result = result.replace(/texture2DLodEXT\s*\(/g, "textureLod(");
+                result = result.replace(/textureCubeLodEXT\s*\(/g, "textureLod(");
+                result = result.replace(/texture2D\s*\(/g, "texture(");
+                result = result.replace(/textureCube\s*\(/g, "texture(");
                 result = result.replace(/gl_FragDepthEXT/g, "gl_FragDepth");
                 result = result.replace(/gl_FragColor/g, "glFragColor");
                 result = result.replace(/gl_FragData/g, "glFragData");
-                result = result.replace(/void\s+?main\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
+                result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
             }
             
             callback(result);

+ 12 - 3
src/Mesh/babylon.meshBuilder.ts

@@ -859,14 +859,20 @@
          */
         public static CreateTube(name: string, options: { path: Vector3[], radius?: number, tessellation?: number, radiusFunction?: { (i: number, distance: number): number; }, cap?: number, arc?: number, updatable?: boolean, sideOrientation?: number, frontUVs?: Vector4, backUVs?: Vector4, instance?: Mesh, invertUV?: boolean }, scene: Scene): Mesh {
             var path = options.path;
-            var radius = options.radius || 1.0;
+            var instance = options.instance;
+            var radius = 1.0;
+            if (instance) {
+                radius = (<any>instance).radius;
+            }
+            if (options.radius !== undefined) {
+                radius = options.radius;
+            };
             var tessellation = options.tessellation || 64|0;
             var radiusFunction = options.radiusFunction;
             var cap = options.cap || Mesh.NO_CAP;
             var invertUV = options.invertUV || false;
             var updatable = options.updatable;
             var sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
-            var instance = options.instance;
             options.arc = (options.arc <= 0.0 || options.arc > 1.0) ? 1.0 : options.arc || 1.0;
 
             // tube geometry
@@ -929,6 +935,7 @@
                 }
                 return circlePaths;
             };
+
             var path3D;
             var pathArray;
             if (instance) { // tube update
@@ -939,10 +946,11 @@
                 (<any>instance).path3D = path3D;
                 (<any>instance).pathArray = pathArray;
                 (<any>instance).arc = arc;
+                (<any>instance).radius = radius;
 
                 return instance;
-
             }
+
             // tube creation
             path3D = <any>new Path3D(path);
             var newPathArray = new Array<Array<Vector3>>();
@@ -954,6 +962,7 @@
             (<any>tube).tessellation = tessellation;
             (<any>tube).cap = cap;
             (<any>tube).arc = options.arc;
+            (<any>tube).radius = radius;
 
             return tube;
         }

+ 1 - 2
src/Shaders/pbr.fragment.fx

@@ -234,8 +234,7 @@ void main(void) {
 		faceNormal = gl_FrontFacing ? faceNormal : -faceNormal;
 	#endif
 
-	float comp = sign(dot(normalW, faceNormal));
-    normalW *= -comp;
+	normalW *= sign(dot(normalW, faceNormal));
 #endif
 
 #if defined(TWOSIDEDLIGHTING) && defined(NORMAL)

+ 97 - 66
src/babylon.engine.ts

@@ -254,6 +254,11 @@
         doNotHandleContextLost?: boolean;
     }
 
+    export interface IDisplayChangedEventArgs {
+        vrDisplay: any;
+        vrSupported: boolean;
+    }
+
     /**
      * The engine class is responsible for interfacing with all lower-level APIs such as WebGL and Audio.
      */
@@ -556,12 +561,8 @@
 
         //WebVR
 
-        //The new WebVR uses promises.
-        //this promise resolves with the current devices available.
-        public vrDisplaysPromise;
-
-        private _vrDisplays;
-        private _vrDisplayEnabled;
+        private _vrDisplay: any = undefined;
+        private _vrSupported: boolean = false;
         private _oldSize: BABYLON.Size;
         private _oldHardwareScaleFactor: number;
         private _vrExclusivePointerMode = false;
@@ -613,6 +614,14 @@
         private _onVRDisplayPointerRestricted: () => void;
         private _onVRDisplayPointerUnrestricted: () => void;
 
+        // VRDisplay connection
+        private _onVrDisplayConnect: (display: any) => void;
+        private _onVrDisplayDisconnect: () => void;
+        private _onVrDisplayPresentChange: () => void;
+        public onVRDisplayChangedObservable = new Observable<IDisplayChangedEventArgs>();
+        public onVRRequestPresentComplete = new Observable<boolean>();
+        public onVRRequestPresentStart = new Observable<Engine>();
+
         private _hardwareScalingLevel: number;
         private _caps: EngineCapabilities;
         private _pointerLockRequested: boolean;
@@ -946,12 +955,10 @@
                 document.addEventListener("webkitpointerlockchange", this._onPointerLockChange, false);
 
                 this._onVRDisplayPointerRestricted = () => {
-                    this._vrExclusivePointerMode = true;
                     canvas.requestPointerLock();
                 }
 
                 this._onVRDisplayPointerUnrestricted = () => {
-                    this._vrExclusivePointerMode = false;
                     document.exitPointerLock();
                 }
 
@@ -1401,7 +1408,10 @@
 
             if (this._activeRenderLoops.length > 0) {
                 // Register new frame
-                this._frameHandler = Tools.QueueNewFrame(this._bindedRenderFunction, this._vrDisplayEnabled);
+                var requester = window;
+                if (this._vrDisplay && this._vrDisplay.isPresenting)
+                    requester = this._vrDisplay;
+                this._frameHandler = Tools.QueueNewFrame(this._bindedRenderFunction, requester);
             } else {
                 this._renderingQueueLaunched = false;
             }
@@ -1528,8 +1538,9 @@
             }
 
             //submit frame to the vr device, if enabled
-            if (this._vrDisplayEnabled && this._vrDisplayEnabled.isPresenting) {
-                this._vrDisplayEnabled.submitFrame()
+            if (this._vrDisplay && this._vrDisplay.isPresenting) {
+                // TODO: We should only submit the frame if we read frameData successfully.
+                this._vrDisplay.submitFrame();
             }
         }
 
@@ -1542,7 +1553,7 @@
          */
         public resize(): void {
             // We're not resizing the size of the canvas while in VR mode & presenting
-            if (!(this._vrDisplayEnabled && this._vrDisplayEnabled.isPresenting)) {
+            if (!(this._vrDisplay && this._vrDisplay.isPresenting)) {
                 var width = navigator.isCocoonJS ? window.innerWidth : this._renderingCanvas.clientWidth;
                 var height = navigator.isCocoonJS ? window.innerHeight : this._renderingCanvas.clientHeight;
 
@@ -1578,94 +1589,106 @@
             }
         }
 
+        // WebVR functions
+        public isVRDevicePresent() : boolean {
+            return !!this._vrDisplay;
+        }
 
-        //WebVR functions
-        public isVRDevicePresent(callback: (result: boolean) => void) {
-            this.getVRDevice(null, (device) => {
-                callback(device !== null);
-            });
+        public getVRDevice() : any {
+            return this._vrDisplay;
         }
 
-        public getVRDevice(name: string, callback: (device: any) => void) {
-            if (!this.vrDisplaysPromise) {
-                callback(null);
-                return;
+        public initWebVR(): Observable<any> {
+            var notifyObservers = () => {
+                var eventArgs = {
+                    vrDisplay: this._vrDisplay,
+                    vrSupported: this._vrSupported
+                };
+                this.onVRDisplayChangedObservable.notifyObservers(eventArgs);
             }
 
-            this.vrDisplaysPromise.then((devices) => {
-                if (devices.length > 0) {
-                    if (name) {
-                        var found = devices.some(device => {
-                            if (device.displayName === name) {
-                                callback(device);
-                                return true;
-                            } else {
-                                return false;
-                            }
-                        });
-                        if (!found) {
-                            Tools.Warn("Display " + name + " was not found. Using " + devices[0].displayName);
-                            callback(devices[0]);
-                        }
-                    } else {
-                        //choose the first one
-                        callback(devices[0]);
-                    }
-                } else {
-                    Tools.Error("No WebVR devices found!");
-                    callback(null);
+            if (!this._onVrDisplayConnect) {
+                this._onVrDisplayConnect = (event) => {
+                    this._vrDisplay = event.display;
+                    notifyObservers();
+                };
+                this._onVrDisplayDisconnect = () => {
+                    this._vrDisplay.cancelAnimationFrame(this._frameHandler);
+                    this._vrDisplay = undefined;
+                    this._frameHandler = Tools.QueueNewFrame(this._bindedRenderFunction);
+                    notifyObservers();
+                };
+                this._onVrDisplayPresentChange = () => {                    
+                    this._vrExclusivePointerMode = this._vrDisplay && this._vrDisplay.isPresenting;
                 }
-            });
-        }
-
-        public initWebVR(): void {
-            if (!this.vrDisplaysPromise) {
-                this._getVRDisplays();
+                window.addEventListener('vrdisplayconnect', this._onVrDisplayConnect);
+                window.addEventListener('vrdisplaydisconnect', this._onVrDisplayDisconnect);
+                window.addEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
             }
+            
+            this._getVRDisplays(notifyObservers);
+
+            return this.onVRDisplayChangedObservable;
         }
 
-        public enableVR(vrDevice) {
-            this._vrDisplayEnabled = vrDevice;
-            this._vrDisplayEnabled.requestPresent([{ source: this.getRenderingCanvas() }]).then(this._onVRFullScreenTriggered);
+        public enableVR() {
+            if (this._vrDisplay && !this._vrDisplay.isPresenting) {
+                var onResolved = () => {
+                    this.onVRRequestPresentComplete.notifyObservers(true);
+                    this._onVRFullScreenTriggered();
+                };
+                var onRejected = () => {
+                    this.onVRRequestPresentComplete.notifyObservers(false);
+                };
+                
+                this.onVRRequestPresentStart.notifyObservers(this);
+                this._vrDisplay.requestPresent([{ source: this.getRenderingCanvas() }]).then(onResolved).catch(onRejected);
+            }
         }
 
         public disableVR() {
-            if (this._vrDisplayEnabled) {
-                this._vrDisplayEnabled.exitPresent().then(this._onVRFullScreenTriggered);
+            if (this._vrDisplay && this._vrDisplay.isPresenting) {
+                this._vrDisplay.exitPresent().then(this._onVRFullScreenTriggered).catch(this._onVRFullScreenTriggered);
             }
         }
 
         private _onVRFullScreenTriggered = () => {
-            if (this._vrDisplayEnabled && this._vrDisplayEnabled.isPresenting) {
+            if (this._vrDisplay && this._vrDisplay.isPresenting) {
                 //get the old size before we change
                 this._oldSize = new BABYLON.Size(this.getRenderWidth(), this.getRenderHeight());
                 this._oldHardwareScaleFactor = this.getHardwareScalingLevel();
 
                 //get the width and height, change the render size
-                var leftEye = this._vrDisplayEnabled.getEyeParameters('left');
+                var leftEye = this._vrDisplay.getEyeParameters('left');
                 var width, height;
                 this.setHardwareScalingLevel(1);
                 this.setSize(leftEye.renderWidth * 2, leftEye.renderHeight);
             } else {
-                //When the specs are implemented, need to uncomment this.
                 this.setHardwareScalingLevel(this._oldHardwareScaleFactor);
                 this.setSize(this._oldSize.width, this._oldSize.height);
-                this._vrDisplayEnabled = undefined;
             }
         }
 
-        private _getVRDisplays() {
+        private _getVRDisplays(callback) {
             var getWebVRDevices = (devices: Array<any>) => {
-
-                this._vrDisplays = devices.filter(function (device) {
-                    return device instanceof VRDisplay;
-                });
-
-                return this._vrDisplays;
+                this._vrSupported = true;
+                // note that devices may actually be an empty array. This is fine;
+                // we expect this._vrDisplay to be undefined in this case.
+                return this._vrDisplay = devices[0];
             }
 
             if (navigator.getVRDisplays) {
-                this.vrDisplaysPromise = navigator.getVRDisplays().then(getWebVRDevices);
+                // TODO: Backwards compatible for 1.0?
+                navigator.getVRDisplays().then(getWebVRDevices).then(callback).catch((error) => {
+                    // TODO: System CANNOT support WebVR, despite API presence.
+                    this._vrSupported = false;
+                    callback();
+                });
+            } else {
+                // TODO: Browser does not support WebVR
+                this._vrDisplay = undefined;
+                this._vrSupported = false;
+                callback();
             }
         }
 
@@ -4416,6 +4439,14 @@
             document.removeEventListener("mspointerlockchange", this._onPointerLockChange);
             document.removeEventListener("mozpointerlockchange", this._onPointerLockChange);
             document.removeEventListener("webkitpointerlockchange", this._onPointerLockChange);
+            
+            if (this._onVrDisplayConnect) {
+                window.removeEventListener('vrdisplayconnect', this._onVrDisplayConnect);
+                window.removeEventListener('vrdisplaydisconnect', this._onVrDisplayDisconnect);
+                window.removeEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
+                this._onVrDisplayConnect = undefined;
+                this._onVrDisplayDisconnect = undefined;                
+            }
 
             // Remove from Instances
             var index = Engine.Instances.indexOf(this);

BIN
tests/validation/ReferenceImages/gltfnormals.png


BIN
tests/validation/ReferenceImages/normals.png


BIN
tests/validation/ReferenceImages/pbrrough.png


+ 27 - 15
tests/validation/config.json

@@ -1,12 +1,12 @@
 {
   "root": "https://rawgit.com/BabylonJS/Website/master",
-  "tests": [   
+  "tests": [
     {
       "title": "Sponza",
       "sceneFolder": "/Scenes/Sponza/",
       "sceneFilename": "Sponza.babylon",
       "referenceImage": "Sponza.png"
-    },    
+    },
     {
       "title": "Windows cafe",
       "sceneFolder": "/Scenes/WCafe/",
@@ -14,7 +14,7 @@
       "referenceImage": "WCafe.png"
     },
     {
-      "title": "Espilit",      
+      "title": "Espilit",
       "renderCount": 10,
       "sceneFolder": "/Scenes/Espilit/",
       "sceneFilename": "Espilit.binary.babylon",
@@ -91,7 +91,7 @@
       "scriptToRun": "/Demos/Polygon/polygon.js",
       "functionToCall": "CreatePolygonScene",
       "referenceImage": "polygon.png"
-    },        
+    },
     {
       "title": "Soft Shadows",
       "renderCount": 5,
@@ -104,7 +104,7 @@
       "scriptToRun": "/Demos/Fresnel/fresnel.js",
       "functionToCall": "CreateFresnelTestScene",
       "referenceImage": "fresnel.png"
-    }, 
+    },
     {
       "title": "Highlights",
       "renderCount": 10,
@@ -173,7 +173,20 @@
       "referenceImage": "displacementMap.png",
       "replace": "/Scenes/Customs/skybox/, https://cdn.rawgit.com/BabylonJS/Website/06ecbea7/Assets/skybox/"
     },
-     {
+    {
+      "title": "Normals",
+      "scriptToRun": "/Demos/Normals/index.js",
+      "functionToCall": "createScene",
+      "referenceImage": "normals.png"
+    },
+    {
+      "title": "GLTF Normals",
+      "renderCount": 10,
+      "scriptToRun": "/Demos/GLTFNormals/index.js",
+      "functionToCall": "createScene",
+      "referenceImage": "gltfnormals.png"
+    },
+    {
       "title": "PBR glossy",
       "renderCount": 10,
       "scriptToRun": "/Demos/PBRGlossy/index.js",
@@ -193,41 +206,41 @@
       "scriptToRun": "/Demos/RefProbe/reflectionProbe.js",
       "functionToCall": "CreateReflectionProbeTestScene ",
       "referenceImage": "refprobe.png"
-    },   
+    },
     {
       "title": "PBRMetallicRoughnessMaterial",
       "playgroundId": "#2FDQT5#13",
       "referenceImage": "PBRMetallicRoughnessMaterial.png"
-    },   
+    },
     {
       "title": "PBRSpecularGlossinessMaterial",
       "playgroundId": "#Z1VL3V#4",
       "referenceImage": "PBRSpecularGlossinessMaterial.png"
-    },   
+    },
     {
       "title": "PBR",
       "playgroundId": "#LCA0Q4",
       "referenceImage": "pbr.png"
-    },   
+    },
     {
       "title": "MultiSample render targets",
       "renderCount": 10,
       "playgroundId": "#12MKMN#0",
       "referenceImage": "MultiSample render targets.png"
-    },      
+    },
     {
       "title": "Default rendering pipeline",
       "renderCount": 20,
       "playgroundId": "#5XB8YT#2",
       "referenceImage": "DefaultRenderingPipeline.png"
-    },             
+    },
     {
       "title": "Water material (only visual check)",
       "renderCount": 10,
       "scriptToRun": "/Demos/WaterMaterial/water.js",
       "functionToCall": "CreateWaterTestScene",
       "referenceImage": "waterMaterial.png",
-      "onlyVisual": true 
+      "onlyVisual": true
     },
     {
       "title": "Instances (only visual check)",
@@ -257,5 +270,4 @@
       "onlyVisual": true
     }
   ]
-}
-
+}