فهرست منبع

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

David Rousset 7 سال پیش
والد
کامیت
51e4adb7df

+ 4 - 0
dist/preview release/inspector/babylon.inspector.css

@@ -273,6 +273,10 @@
         display: inline-block;
         font-family: 'FontAwesome', sans-serif;
         content: "\f054"; }
+      .insp-wrapper .insp-tree .line.unfolded.transformNode > span:first-of-type {
+        color: #f29766; }
+      .insp-wrapper .insp-tree .line.folded.transformNode > span:first-of-type {
+        color: #f29766; }
       .insp-wrapper .insp-tree .line .line-content {
         padding-left: 15px; }
         .insp-wrapper .insp-tree .line .line-content:hover {

+ 8 - 1
inspector/sass/_tree.scss

@@ -35,7 +35,7 @@
             line-height: 1em;
             display    : inline-block;
             font-family: 'FontAwesome', sans-serif;
-            content    : "\f078";
+            content    : "\f078";            
         }
         &.folded:before{            
             width      : 1em;
@@ -45,6 +45,13 @@
             font-family: 'FontAwesome', sans-serif;
             content    : "\f054";
         }
+        
+        &.unfolded.transformNode > span:first-of-type{
+            color:$color-top;
+        }
+        &.folded.transformNode > span:first-of-type{ 
+            color:$color-top;
+        }
 
         // Sub lines
         .line-content {

+ 0 - 5
inspector/src/adapters/Adapter.ts

@@ -20,11 +20,6 @@ module INSPECTOR {
         /** Returns the list of properties to be displayed for this adapter */
         public abstract getProperties(): Array<PropertyLine>;
 
-        /** Returns the actual object behind this adapter */
-        public get actualObject(): any {
-            return this._obj;
-        }
-
         /** Returns true if the given object correspond to this  */
         public correspondsTo(obj: any) {
             return obj === this._obj;

+ 16 - 8
inspector/src/adapters/MeshAdapter.ts

@@ -8,7 +8,7 @@ module INSPECTOR {
         private _axesViewer: BABYLON.Nullable<BABYLON.Debug.AxesViewer>;
         private onBeforeRenderObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Scene>>;
 
-        constructor(mesh: BABYLON.AbstractMesh) {
+        constructor(mesh: BABYLON.Node) {
             super(mesh);
         }
 
@@ -35,10 +35,13 @@ module INSPECTOR {
             let tools = [];
             tools.push(new Checkbox(this));
             tools.push(new DebugArea(this));
-            if ((this._obj as BABYLON.AbstractMesh).getTotalVertices() > 0) {
-                tools.push(new BoundingBox(this));
+            if (this._obj instanceof BABYLON.AbstractMesh) {
+                if ((this._obj as BABYLON.AbstractMesh).getTotalVertices() > 0) {
+                    tools.push(new BoundingBox(this));
+                }
             }
 
+
             tools.push(new Info(this));
             return tools;
         }
@@ -60,7 +63,7 @@ module INSPECTOR {
         public debug(enable: boolean) {
             // Draw axis the first time
             if (!this._axesViewer) {
-                 this._drawAxis();
+                this._drawAxis();
             }
             // Display or hide axis
             if (!enable && this._axesViewer) {
@@ -73,7 +76,10 @@ module INSPECTOR {
 
         /** Returns some information about this mesh */
         public getInfo(): string {
-            return `${(this._obj as BABYLON.AbstractMesh).getTotalVertices()} vertices`;
+            if (this._obj instanceof BABYLON.AbstractMesh) {
+                return `${(this._obj as BABYLON.AbstractMesh).getTotalVertices()} vertices`;
+            }
+            return '0 vertices';
         }
 
         /** Draw X, Y and Z axis for the actual object if this adapter.
@@ -82,8 +88,6 @@ module INSPECTOR {
         private _drawAxis() {
             this._obj.computeWorldMatrix();
 
-            let mesh = this._obj as BABYLON.Mesh;
-
             // Axis
             var x = new BABYLON.Vector3(1, 0, 0);
             var y = new BABYLON.Vector3(0, 1, 0);
@@ -91,9 +95,13 @@ module INSPECTOR {
 
             this._axesViewer = new BABYLON.Debug.AxesViewer(this._obj.getScene());
 
+            let mesh = this._obj as BABYLON.TransformNode;
             this.onBeforeRenderObserver = mesh.getScene().onBeforeRenderObservable.add(() => {
                 let matrix = mesh.getWorldMatrix();
-                let extend = mesh.getBoundingInfo()!.boundingBox.extendSizeWorld;
+                let extend = new BABYLON.Vector3(1, 1, 1);
+                if (mesh instanceof BABYLON.AbstractMesh) {
+                    extend = mesh.getBoundingInfo()!.boundingBox.extendSizeWorld;
+                }
                 this._axesViewer!.scaleLines = Math.max(extend.x, extend.y, extend.z) * 2;
                 this._axesViewer!.update(this._obj.position, BABYLON.Vector3.TransformNormal(x, matrix), BABYLON.Vector3.TransformNormal(y, matrix), BABYLON.Vector3.TransformNormal(z, matrix));
             });

+ 15 - 13
inspector/src/details/PropertyLine.ts

@@ -150,7 +150,7 @@ module INSPECTOR {
             }
         }
 
-        public validateInput(value: any, forceupdate:boolean = true): void {
+        public validateInput(value: any, forceupdate: boolean = true): void {
             this.updateObject();
             if (typeof this._property.value === 'number') {
                 this._property.value = parseFloat(value);
@@ -182,7 +182,7 @@ module INSPECTOR {
             Helpers.CleanDiv(this._valueDiv);
             if (typeof this.value !== 'boolean' && !this._isSliderType()) {
                 this._valueDiv.textContent = "-";
-            } 
+            }
             // restore elements
             for (let elem of this._elements) {
                 this._valueDiv.appendChild(elem.toHtml());
@@ -313,7 +313,7 @@ module INSPECTOR {
             // Then update its value
             // this._valueDiv.textContent = " "; // TOFIX this removes the elements after
             if (typeof this.value === 'boolean') {
-                 this._checkboxInput();
+                this._checkboxInput();
             } else if (this._isSliderType()) { // Add slider when parent have slider property
                 this._rangeInput();
             } else {
@@ -386,7 +386,9 @@ module INSPECTOR {
                 this._div.classList.toggle('unfolded');
                 if (this._children.length == 0) {
                     let objToDetail = this.value;
-                    let propToDisplay = (<any>PROPERTIES)[Helpers.GET_TYPE(objToDetail)].properties.slice().reverse();
+                    // Display all properties that are not functions
+                    let propToDisplay = Helpers.GetAllLinesPropertiesAsString(objToDetail);
+                    propToDisplay.sort().reverse();
 
                     for (let prop of propToDisplay) {
                         let infos = new Property(prop, this._property.value);
@@ -395,7 +397,7 @@ module INSPECTOR {
                     }
                 }
                 // otherwise display it    
-                if (this._div.parentNode) {                
+                if (this._div.parentNode) {
                     for (let child of this._children) {
                         this._div.parentNode.insertBefore(child.toHtml(), this._div.nextSibling);
                     }
@@ -437,7 +439,7 @@ module INSPECTOR {
          * Create input entry
          */
         private _checkboxInput() {
-            if(this._valueDiv.childElementCount < 1) { // Prevent display two checkbox
+            if (this._valueDiv.childElementCount < 1) { // Prevent display two checkbox
                 this._input = Helpers.CreateInput('checkbox-element', this._valueDiv);
                 this._input.type = 'checkbox'
                 this._input.checked = this.value;
@@ -445,11 +447,11 @@ module INSPECTOR {
                     Scheduler.getInstance().pause = true;
                     this.validateInput(!this.value)
                 })
-            }            
+            }
         }
 
         private _rangeInput() {
-            if(this._valueDiv.childElementCount < 1) { // Prevent display two input range
+            if (this._valueDiv.childElementCount < 1) { // Prevent display two input range
                 this._input = Helpers.CreateInput('slider-element', this._valueDiv);
                 this._input.type = 'range';
                 this._input.style.display = 'inline-block';
@@ -457,7 +459,7 @@ module INSPECTOR {
                 this._input.max = this._getSliderProperty().max;
                 this._input.step = this._getSliderProperty().step;
                 this._input.value = this.value;
-                
+
                 this._validateInputHandler = this._rangeHandler.bind(this)
                 this._input.addEventListener('input', this._validateInputHandler)
                 this._input.addEventListener('change', () => {
@@ -479,10 +481,10 @@ module INSPECTOR {
         }
 
         private _isSliderType() { //Check if property have slider definition
-            return this._property  && 
-            PROPERTIES.hasOwnProperty(this._property.obj.constructor.name) &&
-            (<any>PROPERTIES)[this._property.obj.constructor.name].hasOwnProperty('slider') && 
-            (<any>PROPERTIES)[this._property.obj.constructor.name].slider.hasOwnProperty(this.name);
+            return this._property &&
+                PROPERTIES.hasOwnProperty(this._property.obj.constructor.name) &&
+                (<any>PROPERTIES)[this._property.obj.constructor.name].hasOwnProperty('slider') &&
+                (<any>PROPERTIES)[this._property.obj.constructor.name].slider.hasOwnProperty(this.name);
         }
 
         private _getSliderProperty() {

+ 29 - 13
inspector/src/helpers/Helpers.ts

@@ -7,7 +7,7 @@ module INSPECTOR {
          * uses getClassName. If nothing is returned, used the type of the constructor
          */
         public static GET_TYPE(obj: any): string {
-            if(typeof obj === 'boolean') {
+            if (typeof obj === 'boolean') {
                 return 'boolean';
             }
 
@@ -24,10 +24,10 @@ module INSPECTOR {
                 if (!this._CheckIfTypeExists(classname)) {
                     return this._GetTypeFor(obj);
                 }
-                
+
                 return classname;
             } else {
-                
+
                 return 'type_not_defined';
             }
         }
@@ -91,6 +91,9 @@ module INSPECTOR {
 
         /** Returns the given number with 2 decimal number max if a decimal part exists */
         public static Trunc(nb: number): number {
+            if (typeof nb !== 'number') {
+                return 0;
+            }
             if (Math.round(nb) !== nb) {
                 return (<any>nb.toFixed(2));
             }
@@ -141,7 +144,7 @@ module INSPECTOR {
             div.style.display = 'none';
             div.appendChild(clone);
             let value = (<any>Inspector.WINDOW.getComputedStyle(clone))[cssAttribute];
-            if (div.parentNode)  {
+            if (div.parentNode) {
                 div.parentNode.removeChild(div);
             }
             return value;
@@ -185,20 +188,33 @@ module INSPECTOR {
          */
         public static GetAllLinesProperties(obj: any): Array<PropertyLine> {
             let propertiesLines: Array<PropertyLine> = [];
-            
+            let props = Helpers.GetAllLinesPropertiesAsString(obj);
+
+            for (let prop of props) {
+                let infos = new Property(prop, obj);
+                propertiesLines.push(new PropertyLine(infos));
+            }
+            return propertiesLines;
+        }
+
+
+        /**
+         * Returns an array of string corresponding to tjhe list of properties of the object to be displayed
+         * @param obj 
+         */
+        public static GetAllLinesPropertiesAsString(obj: any): Array<string> {
+            let props: Array<string> = [];
+
             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));
+                //No private and no function
+                if (prop.substring(0, 1) !== '_' && typeof obj[prop] !== 'function') {
+                    props.push(prop);
                 }
             }
-            return propertiesLines;
+            return props;
         }
 
-        public static Capitalize (str : string) : string {
+        public static Capitalize(str: string): string {
             return str.charAt(0).toUpperCase() + str.slice(1);
         }
     }

+ 17 - 237
inspector/src/properties.ts

@@ -20,59 +20,39 @@ module INSPECTOR {
         },
         'Vector2': {
             type: BABYLON.Vector2,
-            properties: ['x', 'y'],
             format: (vec: BABYLON.Vector2) => { return `x:${Helpers.Trunc(vec.x)}, y:${Helpers.Trunc(vec.y)}`; }
         },
         'Vector3': {
             type: BABYLON.Vector3,
-            properties: ['x', 'y', 'z'],
             format: (vec: BABYLON.Vector3) => { return `x:${Helpers.Trunc(vec.x)}, y:${Helpers.Trunc(vec.y)}, z:${Helpers.Trunc(vec.z)}` }
         },
         'Color3': {
             type: BABYLON.Color3,
-            properties: ['r', 'g', 'b'],
             format: (color: BABYLON.Color3) => { return `R:${color.r}, G:${color.g}, B:${color.b}` },
             slider: {
-                r: {min: 0, max: 1, step: 0.01},
-                g: {min: 0, max: 1, step: 0.01},
-                b: {min: 0, max: 1, step: 0.01}
+                r: { min: 0, max: 1, step: 0.01 },
+                g: { min: 0, max: 1, step: 0.01 },
+                b: { min: 0, max: 1, step: 0.01 }
             }
         },
         'Color4': {
             type: BABYLON.Color4,
-            properties: ['r', 'g', 'b'],
             format: (color: BABYLON.Color4) => { return `R:${color.r}, G:${color.g}, B:${color.b}` },
             slider: {
-                r: {min: 0, max: 1, step: 0.01},
-                g: {min: 0, max: 1, step: 0.01},
-                b: {min: 0, max: 1, step: 0.01}
+                r: { min: 0, max: 1, step: 0.01 },
+                g: { min: 0, max: 1, step: 0.01 },
+                b: { min: 0, max: 1, step: 0.01 }
             }
         },
         'Quaternion': {
-            type: BABYLON.Quaternion,
-            properties: ['x', 'y', 'z', 'w']
+            type: BABYLON.Quaternion
         },
         'Size': {
             type: BABYLON.Size,
-            properties: ['width', 'height'],
             format: (size: BABYLON.Size) => { return `Size - w:${Helpers.Trunc(size.width)}, h:${Helpers.Trunc(size.height)}` }
         },
         'Texture': {
             type: BABYLON.Texture,
-            properties: [
-                'hasAlpha',
-                'level',
-                'name',
-                'wrapU',
-                'wrapV',
-                'uScale',
-                'vScale',
-                'uAng',
-                'vAng',
-                'wAng',
-                'uOffset',
-                'vOffset'
-            ],
             format: (tex: BABYLON.Texture) => { return tex.name }
         },
         'RenderTargetTexture': {
@@ -91,247 +71,47 @@ module INSPECTOR {
             type: BABYLON.HDRCubeTexture
         },
         'Sound': {
-            type: BABYLON.Sound,
-            properties: [
-                'name',
-                'autoplay',
-                'loop',
-                'useCustomAttenuation',
-                'soundTrackId',
-                'spatialSound',
-                'refDistance',
-                'rolloffFactor',
-                'maxDistance',
-                'distanceModel',
-                'isPlaying',
-                'isPaused'
-            ]
+            type: BABYLON.Sound
         },
         'ArcRotateCamera': {
             type: BABYLON.ArcRotateCamera,
-            properties: [
-                'position',
-                'alpha',
-                'beta',
-                'radius',
-                'angularSensibilityX',
-                'angularSensibilityY',
-                'angularTouchSensibilityX',
-                'angularTouchSensibilityY',
-                'target',
-                'lowerAlphaLimit',
-                'lowerBetaLimit',
-                'upperAlphaLimit',
-                'upperBetaLimit',
-                'lowerRadiusLimit',
-                'upperRadiusLimit',
-
-                'pinchPrecision',
-                'wheelPrecision',
-                'allowUpsideDown',
-                'checkCollisions'
-            ],
             slider: {
-                alpha: {min: 0, max: 2*Math.PI, step: 0.01},
-                beta: {min: -Math.PI, max: Math.PI, step: 0.01},
-                fov: {min: 0, max: 180, step: 1}
+                alpha: { min: 0, max: 2 * Math.PI, step: 0.01 },
+                beta: { min: -Math.PI, max: Math.PI, step: 0.01 },
+                fov: { min: 0, max: 180, step: 1 }
             }
         },
         'FreeCamera': {
             type: BABYLON.FreeCamera,
-            properties: [
-                'position',
-                'rotation',
-                'rotationQuaternion',
-                'cameraDirection',
-                'cameraRotation',
-                'ellipsoid',
-                'applyGravity',
-                'angularSensibility',
-                'keysUp',
-                'keysDown',
-                'keysLeft',
-                'keysRight',
-                'checkCollisions',
-                'speed',
-                'lockedTarget',
-                'noRotationConstraint',
-                'fov',
-                'inertia',
-                'minZ', 'maxZ',
-                'layerMask',
-                'mode',
-                'orthoBottom',
-                'orthoTop',
-                'orthoLeft',
-                'orthoRight'
-            ],
             slider: {
-                fov: {min: 0, max: 180, step: 1}
+                fov: { min: 0, max: 180, step: 1 }
             }
         },
         'Scene': {
             type: BABYLON.Scene,
-            properties: [
-                'actionManager',
-                'activeCamera',
-                'ambientColor',
-                'clearColor',
-                'forceWireframe',
-                'forcePointsCloud',
-                'forceShowBoundingBoxes',
-                'useRightHandedSystem',
-                'hoverCursor',
-                'cameraToUseForPointers',
-                'fogEnabled',
-                'fogColor',
-                'fogDensity',
-                'fogStart',
-                'fogEnd',
-                'shadowsEnabled',
-                'lightsEnabled',
-                'collisionsEnabled',
-                'gravity',
-                'meshUnderPointer',
-                'pointerX',
-                'pointerY',
-                'uid'
-            ]
         },
         'Mesh': {
             type: BABYLON.Mesh,
-            properties: [
-                'name',
-                'position',
-                'rotation',
-                'rotationQuaternion',
-                'absolutePosition',
-                'material',
-                'actionManager',
-                'visibility',
-                'isVisible',
-                'isPickable',
-                'renderingGroupId',
-                'receiveShadows',
-                'renderOutline',
-                'outlineColor',
-                'outlineWidth',
-                'renderOverlay',
-                'overlayColor',
-                'overlayAlpha',
-                'hasVertexAlpha',
-                'useVertexColors',
-                'layerMask',
-                'alwaysSelectAsActiveMesh',
-                'ellipsoid',
-                'ellipsoidOffset',
-                'edgesWidth',
-                'edgesColor',
-                'checkCollisions',
-                'hasLODLevels'
-            ],
             format: (m: BABYLON.Mesh): string => { return m.name; },
             slider: {
-                visibility: {min: 0, max: 1, step: 0.1}
+                visibility: { min: 0, max: 1, step: 0.1 }
             }
         },
         'StandardMaterial': {
             type: BABYLON.StandardMaterial,
-            properties: [
-                'name',
-                'alpha',
-                'alphaMode',
-                'wireframe',
-                'isFrozen',
-                'zOffset',
-
-                'ambientColor',
-                'emissiveColor',
-                'diffuseColor',
-                'specularColor',
-
-                'specularPower',
-                'useAlphaFromDiffuseTexture',
-                'linkEmissiveWithDiffuse',
-                'useSpecularOverAlpha',
-
-                'diffuseFresnelParameters',
-                'opacityFresnelParameters',
-                'reflectionFresnelParameters',
-                'refractionFresnelParameters',
-                'emissiveFresnelParameters',
-
-                'diffuseTexture',
-                'emissiveTexture',
-                'specularTexture',
-                'ambientTexture',
-                'bumpTexture',
-                'lightMapTexture',
-                'opacityTexture',
-                'reflectionTexture',
-                'refractionTexture'
-            ],
             format: (mat: BABYLON.StandardMaterial): string => { return mat.name; },
             slider: {
-                alpha: {min: 0, max: 1, step: 0.01}
+                alpha: { min: 0, max: 1, step: 0.01 }
             }
         },
         'PBRMaterial': {
             type: BABYLON.PBRMaterial,
-            properties: [
-                'name',
-                'albedoColor',
-                'albedoTexture',
-
-                'opacityTexture',
-                'reflectionTexture',
-                'emissiveTexture',
-                'bumpTexture',
-                'lightmapTexture',
-
-                'opacityFresnelParameters',
-                'emissiveFresnelParameters',
-
-                'linkEmissiveWithAlbedo',
-                'useLightmapAsShadowmap',
-
-                'useAlphaFromAlbedoTexture',
-                'useSpecularOverAlpha',
-                'useAutoMicroSurfaceFromReflectivityMap',
-                'useLogarithmicDepth',
-
-                'reflectivityColor',
-                'reflectivityTexture',
-                'reflectionTexture',
-                'reflectionColor',
-
-                'alpha',
-                'linkRefractionWithTransparency',
-                'indexOfRefraction',
-
-                'microSurface',
-                'useMicroSurfaceFromReflectivityMapAlpha',
-
-                'directIntensity',
-                'emissiveIntensity',
-                'specularIntensity',
-                'environmentIntensity',
-                'cameraExposure',
-                'cameraContrast',
-                'cameraColorGradingTexture',
-                'cameraColorCurves'
-            ],
             slider: {
-                alpha: {min: 0, max: 1, step: 0.01}
+                alpha: { min: 0, max: 1, step: 0.01 }
             }
-        },  
+        },
         'PhysicsImpostor': {
-            type: BABYLON.PhysicsImpostor,
-            properties: [
-                'friction',
-                'mass',
-                'restitution',
-            ]
+            type: BABYLON.PhysicsImpostor
         },
     }
 

+ 39 - 22
inspector/src/tabs/MeshTab.ts

@@ -1,66 +1,83 @@
 /// <reference path="../../../dist/preview release/babylon.d.ts"/>
 
-module INSPECTOR{
-    
+module INSPECTOR {
+
     export class MeshTab extends PropertyTab {
-                
-        constructor(tabbar:TabBar, inspector:Inspector) {
-            super(tabbar, 'Mesh', inspector); 
+
+        constructor(tabbar: TabBar, inspector: Inspector) {
+            super(tabbar, 'Mesh', inspector);
         }
 
         /* Overrides super */
-        protected _getTree() : Array<TreeItem> {
+        protected _getTree(): Array<TreeItem> {
             let arr = new Array<TreeItem>();
             // Tab containing mesh already in results
-            let alreadyIn = new Array<BABYLON.AbstractMesh>();
+            let alreadyIn = new Array<BABYLON.Node>();
 
             // Recursive method building the tree panel
-            let createNode = (obj : BABYLON.AbstractMesh) => {
+            let createNode = (obj: BABYLON.Node) => {
                 let descendants = obj.getDescendants(true);
 
                 let node = new TreeItem(this, new MeshAdapter(obj));
 
                 if (descendants.length > 0) {
-                    for (let child of descendants) {     
-                        if (child instanceof BABYLON.AbstractMesh) {
-                            if (!Helpers.IsSystemName(child.name)) {  
+                    for (let child of descendants) {
+                        if (child instanceof BABYLON.TransformNode) {
+                            if (!Helpers.IsSystemName(child.name)) {
                                 let n = createNode(child);
-                                node.add(n); 
+                                node.add(n);
                             }
                         }
                     }
                     node.update();
-                } 
+                }
 
                 // Retrieve the root node if the mesh is actually child of another mesh
                 // This can hapen if the child mesh has been created before the parent mesh
-                if(obj.parent != null && alreadyIn.indexOf(obj) != -1){
+                if (obj.parent != null && alreadyIn.indexOf(obj) != -1) {
                     let i: number = 0;
                     let notFound: boolean = true;
                     // Find and delete the root node standing for this mesh
-                    while(i < arr.length && notFound){
-                        if(obj.name === arr[i].id){
-                             arr.splice(i, 1);
-                             notFound = false;
+                    while (i < arr.length && notFound) {
+                        if (obj.name === arr[i].id) {
+                            arr.splice(i, 1);
+                            notFound = false;
                         }
                         i++;
                     }
                 }
 
-                alreadyIn.push(obj);                
+                alreadyIn.push(obj);
                 return node;
             };
-            
+
             // get all meshes from the first scene
             let instances = this._inspector.scene;
+
+            // Find top of hierarchy for meshes...
+            let meshWithoutAnyParent: Array<BABYLON.Node> = [];
             for (let mesh of instances.meshes) {
+                // Not already in the array, not system name and no parent
+                if (meshWithoutAnyParent.indexOf(mesh) == -1 && !Helpers.IsSystemName(mesh.name) && !mesh.parent) {
+                    meshWithoutAnyParent.push(mesh);
+                }
+            }
+            // ... and for transforms
+            for (let tn of instances.transformNodes) {
+                // Not already in the array, not system name and no parent
+                if (meshWithoutAnyParent.indexOf(tn) == -1 && !Helpers.IsSystemName(tn.name) && !tn.parent) {
+                    meshWithoutAnyParent.push(tn);
+                }
+            }
+
+            for (let mesh of meshWithoutAnyParent) {
                 if (alreadyIn.indexOf(mesh) == -1 && !Helpers.IsSystemName(mesh.name)) {
                     let node = createNode(mesh);
                     arr.push(node);
                 }
             }
             return arr;
-        }  
+        }
     }
-    
+
 }

+ 78 - 77
inspector/src/tabs/SceneTab.ts

@@ -1,55 +1,56 @@
 declare function Split(elements: HTMLElement[], options: any): void;
 module INSPECTOR {
-    
+
     export class SceneTab extends Tab {
-        
-        private _inspector : Inspector;
+
+        private _inspector: Inspector;
         /** The list of  channels/options that can be activated/deactivated */
-        private _actions   : HTMLDivElement;
+        private _actions: HTMLDivElement;
 
         /** The list of skeleton viewer */
-        private _skeletonViewers : Array<BABYLON.Debug.SkeletonViewer> = [];
+        private _skeletonViewers: Array<BABYLON.Debug.SkeletonViewer> = [];
 
         /** The detail of the scene */
-        private _detailsPanel : DetailPanel;        
-                
-        constructor(tabbar:TabBar, insp:Inspector) {
-            super(tabbar, 'Scene');            
-            this._inspector = insp;  
-            
+        private _detailsPanel: DetailPanel;
+
+        constructor(tabbar: TabBar, insp: Inspector) {
+            super(tabbar, 'Scene');
+            this._inspector = insp;
+
             // Build the properties panel : a div that will contains the tree and the detail panel
-            this._panel   = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
-            
+            this._panel = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
+
             this._actions = Helpers.CreateDiv('scene-actions', this._panel) as HTMLDivElement;
-           
+
             this._detailsPanel = new DetailPanel();
             this._panel.appendChild(this._detailsPanel.toHtml());
-            
+
             // build propertiesline
             let details = [];
-            for (let prop of PROPERTIES['Scene'].properties) {
-                details.push(new PropertyLine(new Property(prop, this._inspector.scene)));
+            let props = Helpers.GetAllLinesProperties(this._inspector.scene);
+            for (let prop of props) {
+                details.push(prop);
             }
             this._detailsPanel.details = details;
-            
-            Split([this._actions, this._detailsPanel.toHtml()], {  
-                blockDrag : this._inspector.popupMode,
-                sizes:[50, 50],
-                direction:'vertical'
-            });  
-            
+
+            Split([this._actions, this._detailsPanel.toHtml()], {
+                blockDrag: this._inspector.popupMode,
+                sizes: [50, 50],
+                direction: 'vertical'
+            });
+
             // Build actions
             {
-                
+
                 // Rendering mode
                 let title = Helpers.CreateDiv('actions-title', this._actions);
                 title.textContent = 'Rendering mode';
-                let point             = Helpers.CreateDiv('action-radio', this._actions);
-                let wireframe         = Helpers.CreateDiv('action-radio', this._actions);
-                let solid             = Helpers.CreateDiv('action-radio', this._actions);
-                point.textContent     = 'Point';
+                let point = Helpers.CreateDiv('action-radio', this._actions);
+                let wireframe = Helpers.CreateDiv('action-radio', this._actions);
+                let solid = Helpers.CreateDiv('action-radio', this._actions);
+                point.textContent = 'Point';
                 wireframe.textContent = 'Wireframe';
-                solid.textContent     = 'Solid';
+                solid.textContent = 'Solid';
                 if (this._inspector.scene.forcePointsCloud) {
                     point.classList.add('active');
                 } else if (this._inspector.scene.forceWireframe) {
@@ -58,64 +59,64 @@ module INSPECTOR {
                     solid.classList.add('active');
                 }
                 this._generateRadioAction([point, wireframe, solid]);
-                point.addEventListener('click', () => {this._inspector.scene.forcePointsCloud = true; this._inspector.scene.forceWireframe = false;});
-                wireframe.addEventListener('click', () => {this._inspector.scene.forcePointsCloud = false; this._inspector.scene.forceWireframe = true; });
-                solid.addEventListener('click',  () => {this._inspector.scene.forcePointsCloud = false; this._inspector.scene.forceWireframe = false; });
+                point.addEventListener('click', () => { this._inspector.scene.forcePointsCloud = true; this._inspector.scene.forceWireframe = false; });
+                wireframe.addEventListener('click', () => { this._inspector.scene.forcePointsCloud = false; this._inspector.scene.forceWireframe = true; });
+                solid.addEventListener('click', () => { this._inspector.scene.forcePointsCloud = false; this._inspector.scene.forceWireframe = false; });
 
                 // Textures
                 title = Helpers.CreateDiv('actions-title', this._actions);
                 title.textContent = 'Textures channels';
-                this._generateActionLine('Diffuse Texture', BABYLON.StandardMaterial.DiffuseTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.DiffuseTextureEnabled = b });
-                this._generateActionLine('Ambient Texture', BABYLON.StandardMaterial.AmbientTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.AmbientTextureEnabled = b });
-                this._generateActionLine('Specular Texture', BABYLON.StandardMaterial.SpecularTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.SpecularTextureEnabled = b });
-                this._generateActionLine('Emissive Texture', BABYLON.StandardMaterial.EmissiveTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.EmissiveTextureEnabled = b });
-                this._generateActionLine('Bump Texture', BABYLON.StandardMaterial.BumpTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.BumpTextureEnabled = b });
-                this._generateActionLine('Opacity Texture', BABYLON.StandardMaterial.OpacityTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.OpacityTextureEnabled = b });
-                this._generateActionLine('Reflection Texture', BABYLON.StandardMaterial.ReflectionTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.ReflectionTextureEnabled = b });
-                this._generateActionLine('Refraction Texture', BABYLON.StandardMaterial.RefractionTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.RefractionTextureEnabled = b });
-                this._generateActionLine('ColorGrading', BABYLON.StandardMaterial.ColorGradingTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.ColorGradingTextureEnabled = b });
-                this._generateActionLine('Lightmap Texture', BABYLON.StandardMaterial.LightmapTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.LightmapTextureEnabled = b });
-                this._generateActionLine('Fresnel', BABYLON.StandardMaterial.FresnelEnabled, (b:boolean) => {BABYLON.StandardMaterial.FresnelEnabled = b });
+                this._generateActionLine('Diffuse Texture', BABYLON.StandardMaterial.DiffuseTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.DiffuseTextureEnabled = b });
+                this._generateActionLine('Ambient Texture', BABYLON.StandardMaterial.AmbientTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.AmbientTextureEnabled = b });
+                this._generateActionLine('Specular Texture', BABYLON.StandardMaterial.SpecularTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.SpecularTextureEnabled = b });
+                this._generateActionLine('Emissive Texture', BABYLON.StandardMaterial.EmissiveTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.EmissiveTextureEnabled = b });
+                this._generateActionLine('Bump Texture', BABYLON.StandardMaterial.BumpTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.BumpTextureEnabled = b });
+                this._generateActionLine('Opacity Texture', BABYLON.StandardMaterial.OpacityTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.OpacityTextureEnabled = b });
+                this._generateActionLine('Reflection Texture', BABYLON.StandardMaterial.ReflectionTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.ReflectionTextureEnabled = b });
+                this._generateActionLine('Refraction Texture', BABYLON.StandardMaterial.RefractionTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.RefractionTextureEnabled = b });
+                this._generateActionLine('ColorGrading', BABYLON.StandardMaterial.ColorGradingTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.ColorGradingTextureEnabled = b });
+                this._generateActionLine('Lightmap Texture', BABYLON.StandardMaterial.LightmapTextureEnabled, (b: boolean) => { BABYLON.StandardMaterial.LightmapTextureEnabled = b });
+                this._generateActionLine('Fresnel', BABYLON.StandardMaterial.FresnelEnabled, (b: boolean) => { BABYLON.StandardMaterial.FresnelEnabled = b });
 
                 // Options
                 title = Helpers.CreateDiv('actions-title', this._actions);
-                title.textContent = 'Options'; 
-                this._generateActionLine('Animations', this._inspector.scene.animationsEnabled, (b:boolean) => {this._inspector.scene.animationsEnabled = b });
-                this._generateActionLine('Collisions', this._inspector.scene.collisionsEnabled, (b:boolean) => {this._inspector.scene.collisionsEnabled = b });
-                this._generateActionLine('Fog', this._inspector.scene.fogEnabled, (b:boolean) => {this._inspector.scene.fogEnabled = b });
-                this._generateActionLine('Lens flares', this._inspector.scene.lensFlaresEnabled, (b:boolean) => {this._inspector.scene.lensFlaresEnabled = b });
-                this._generateActionLine('Lights', this._inspector.scene.lightsEnabled, (b:boolean) => {this._inspector.scene.lightsEnabled = b });
-                this._generateActionLine('Particles', this._inspector.scene.particlesEnabled, (b:boolean) => {this._inspector.scene.particlesEnabled = b });
-                this._generateActionLine('Post-processes', this._inspector.scene.postProcessesEnabled, (b:boolean) => {this._inspector.scene.postProcessesEnabled = b });
-                this._generateActionLine('Probes', this._inspector.scene.probesEnabled, (b:boolean) => {this._inspector.scene.probesEnabled = b });
-                this._generateActionLine('Procedural textures', this._inspector.scene.proceduralTexturesEnabled, (b:boolean) => {this._inspector.scene.proceduralTexturesEnabled = b });
-                this._generateActionLine('Render targets', this._inspector.scene.renderTargetsEnabled, (b:boolean) => {this._inspector.scene.renderTargetsEnabled = b });
-                this._generateActionLine('Shadows', this._inspector.scene.shadowsEnabled, (b:boolean) => {this._inspector.scene.shadowsEnabled = b });
-                this._generateActionLine('Skeletons', this._inspector.scene.skeletonsEnabled, (b:boolean) => {this._inspector.scene.skeletonsEnabled = b });
-                this._generateActionLine('Sprites', this._inspector.scene.spritesEnabled, (b:boolean) => {this._inspector.scene.spritesEnabled = b });
-                this._generateActionLine('Textures', this._inspector.scene.texturesEnabled, (b:boolean) => {this._inspector.scene.texturesEnabled = b });
-                
+                title.textContent = 'Options';
+                this._generateActionLine('Animations', this._inspector.scene.animationsEnabled, (b: boolean) => { this._inspector.scene.animationsEnabled = b });
+                this._generateActionLine('Collisions', this._inspector.scene.collisionsEnabled, (b: boolean) => { this._inspector.scene.collisionsEnabled = b });
+                this._generateActionLine('Fog', this._inspector.scene.fogEnabled, (b: boolean) => { this._inspector.scene.fogEnabled = b });
+                this._generateActionLine('Lens flares', this._inspector.scene.lensFlaresEnabled, (b: boolean) => { this._inspector.scene.lensFlaresEnabled = b });
+                this._generateActionLine('Lights', this._inspector.scene.lightsEnabled, (b: boolean) => { this._inspector.scene.lightsEnabled = b });
+                this._generateActionLine('Particles', this._inspector.scene.particlesEnabled, (b: boolean) => { this._inspector.scene.particlesEnabled = b });
+                this._generateActionLine('Post-processes', this._inspector.scene.postProcessesEnabled, (b: boolean) => { this._inspector.scene.postProcessesEnabled = b });
+                this._generateActionLine('Probes', this._inspector.scene.probesEnabled, (b: boolean) => { this._inspector.scene.probesEnabled = b });
+                this._generateActionLine('Procedural textures', this._inspector.scene.proceduralTexturesEnabled, (b: boolean) => { this._inspector.scene.proceduralTexturesEnabled = b });
+                this._generateActionLine('Render targets', this._inspector.scene.renderTargetsEnabled, (b: boolean) => { this._inspector.scene.renderTargetsEnabled = b });
+                this._generateActionLine('Shadows', this._inspector.scene.shadowsEnabled, (b: boolean) => { this._inspector.scene.shadowsEnabled = b });
+                this._generateActionLine('Skeletons', this._inspector.scene.skeletonsEnabled, (b: boolean) => { this._inspector.scene.skeletonsEnabled = b });
+                this._generateActionLine('Sprites', this._inspector.scene.spritesEnabled, (b: boolean) => { this._inspector.scene.spritesEnabled = b });
+                this._generateActionLine('Textures', this._inspector.scene.texturesEnabled, (b: boolean) => { this._inspector.scene.texturesEnabled = b });
+
                 // Audio
                 title = Helpers.CreateDiv('actions-title', this._actions);
-                title.textContent         = 'Audio'; 
-                let headphones            = Helpers.CreateDiv('action-radio', this._actions);
-                let normalSpeaker         = Helpers.CreateDiv('action-radio', this._actions);
-                this._generateActionLine('Disable audio', !this._inspector.scene.audioEnabled, (b:boolean) => {this._inspector.scene.audioEnabled = !b });
-                headphones.textContent    = 'Headphones';
-                normalSpeaker.textContent = 'Normal speakers';                
+                title.textContent = 'Audio';
+                let headphones = Helpers.CreateDiv('action-radio', this._actions);
+                let normalSpeaker = Helpers.CreateDiv('action-radio', this._actions);
+                this._generateActionLine('Disable audio', !this._inspector.scene.audioEnabled, (b: boolean) => { this._inspector.scene.audioEnabled = !b });
+                headphones.textContent = 'Headphones';
+                normalSpeaker.textContent = 'Normal speakers';
                 this._generateRadioAction([headphones, normalSpeaker]);
                 if (this._inspector.scene.headphone) {
                     headphones.classList.add('active');
                 } else {
                     normalSpeaker.classList.add('active');
                 }
-                headphones.addEventListener('click', () => {this._inspector.scene.headphone = true;});
-                normalSpeaker.addEventListener('click', () => {this._inspector.scene.headphone = false;});
-                
+                headphones.addEventListener('click', () => { this._inspector.scene.headphone = true; });
+                normalSpeaker.addEventListener('click', () => { this._inspector.scene.headphone = false; });
+
                 // Viewers
                 title = Helpers.CreateDiv('actions-title', this._actions);
                 title.textContent = 'Viewer';
-                this._generateActionLine('Skeletons', false, (b:boolean) => {
+                this._generateActionLine('Skeletons', false, (b: boolean) => {
                     if (b) {
                         for (var index = 0; index < this._inspector.scene.meshes.length; index++) {
                             var mesh = this._inspector.scene.meshes[index];
@@ -141,17 +142,17 @@ module INSPECTOR {
                         }
                         this._skeletonViewers = [];
                     }
-                });                
-            }            
+                });
+            }
         }
 
         /** Overrides super.dispose */
         public dispose() {
             this._detailsPanel.dispose();
         }
-        
+
         /** generates a div which correspond to an option that can be activated/deactivated */
-        private _generateActionLine(name:string, initValue:boolean, action:(b:boolean) => void) {
+        private _generateActionLine(name: string, initValue: boolean, action: (b: boolean) => void) {
             let div = Helpers.CreateDiv('scene-actions', this._actions);
             div.textContent = name;
             div.classList.add('action');
@@ -159,9 +160,9 @@ module INSPECTOR {
                 div.classList.add('active');
             }
             div.addEventListener('click', (e) => {
-                div.classList.toggle('active');      
+                div.classList.toggle('active');
                 let isActivated = div.classList.contains('active');
-                action(isActivated);          
+                action(isActivated);
             })
         }
 
@@ -169,7 +170,7 @@ module INSPECTOR {
          * Add a click action for all given elements : 
          * the clicked element is set as active, all others elements are deactivated
          */
-        private _generateRadioAction(arr:Array<HTMLElement>) {
+        private _generateRadioAction(arr: Array<HTMLElement>) {
             let active = (elem: HTMLElement, evt: any) => {
                 for (let e of arr) {
                     e.classList.remove('active');

+ 8 - 0
inspector/src/tree/TreeItem.ts

@@ -82,6 +82,14 @@ module INSPECTOR {
         protected _build() {
             this._div.className = 'line';
 
+            // special class for transform node ONLY
+            if (this.adapter instanceof MeshAdapter) {
+                let obj = this.adapter.object;
+                if (obj instanceof BABYLON.TransformNode && !(obj instanceof BABYLON.AbstractMesh)) {
+                    this._div.className += ' transformNode';
+                }
+            }
+
 
             for (let tool of this._tools) {
                 this._div.appendChild(tool.toHtml());

+ 24 - 284
inspector/test/index.js

@@ -35,301 +35,41 @@ var Test = (function () {
     Test.prototype._initScene = function () {
         var scene = new BABYLON.Scene(this.engine);
         var canvas = scene.getEngine().getRenderingCanvas();
-        var light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 1, 0), scene);
 
-        var camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 2, 1.0, 110, new BABYLON.Vector3(0, -20, 0), scene);
-        camera.attachControl(canvas, true);
 
-        var camera1 = new BABYLON.ArcRotateCamera("Camera1", 1.58, 1.2, 110, BABYLON.Vector3.Zero(), scene);
-        var camera2 = new BABYLON.ArcRotateCamera("Camera2", Math.PI / 3, .8, 40, BABYLON.Vector3.Zero(), scene);
-        var camera3 = new BABYLON.ArcRotateCamera("Camera3", -Math.PI / 5, 1.0, 70, BABYLON.Vector3.Zero(), scene);
-        var camera4 = new BABYLON.ArcRotateCamera("Camera4", -Math.PI / 6, 1.3, 110, BABYLON.Vector3.Zero(), scene);
+        var sceneRoot = new BABYLON.TransformNode("abstractmesh");
 
-        camera1.layerMask = 2;
-        camera2.layerMask = 2;
-        camera3.layerMask = 2;
-        camera4.layerMask = 2;
-        /*
-            camera1.parent = camera;
-            camera2.parent = camera;
-            camera3.parent = camera;
-            camera4.parent = camera;
-        */
+        var tn = new BABYLON.TransformNode("transform node");
 
-        //Sounds
-        var jump = new BABYLON.Sound("Jump", "test/jump.wav", scene);
-        var explosion = new BABYLON.Sound("Explosion", "test/explosion.wav", scene);
-        jump.setVolume(0.1);
-        window.addEventListener("keydown", function (evt) {
-            if (evt.keyCode === 32) {
-                jump.play();
-            }
-        });
-
-        // Skybox
-        var skybox = BABYLON.Mesh.CreateBox("skyBox", 500.0, scene);
-        var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
-        skyboxMaterial.backFaceCulling = false;
-        skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("/playground/textures/TropicalSunnyDay", scene);
-        skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
-        skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
-        skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
-        skyboxMaterial.disableLighting = true;
-        skybox.material = skyboxMaterial;
-
-        var sphere1 = BABYLON.Mesh.CreateSphere("Sphere1", 10.0, 9.0, scene);
-        var sphere2 = BABYLON.Mesh.CreateSphere("Sphere2", 2.0, 9.0, scene);//Only two segments
-        var sphere3 = BABYLON.Mesh.CreateSphere("Sphere3", 10.0, 9.0, scene);
-        var sphere4 = BABYLON.Mesh.CreateSphere("Sphere4", 10.0, 9.0, scene);
-        var sphere5 = BABYLON.Mesh.CreateSphere("Sphere5", 10.0, 9.0, scene);
-        var sphere6 = BABYLON.Mesh.CreateSphere("Sphere6", 10.0, 9.0, scene);
-
-        sphere1.position.x = 40;
-        sphere2.position.x = 25;
-        sphere3.position.x = 10;
-        sphere4.position.x = -5;
-        sphere5.position.x = -20;
-        sphere6.position.x = -35;
-
-        var rt1 = new BABYLON.RenderTargetTexture("depth", 1024, scene, true, true);
-        scene.customRenderTargets.push(rt1);
-        rt1.activeCamera = camera1;
-        rt1.renderList = scene.meshes;
-
-        var rt2 = new BABYLON.RenderTargetTexture("depth", 1024, scene, true, true);
-        scene.customRenderTargets.push(rt2);
-        rt2.activeCamera = camera2;
-        rt2.renderList = scene.meshes;
-
-        var rt3 = new BABYLON.RenderTargetTexture("depth", 1024, scene, true, true);
-        scene.customRenderTargets.push(rt3);
-        rt3.activeCamera = camera3;
-        rt3.renderList = scene.meshes;
-
-        var rt4 = new BABYLON.RenderTargetTexture("depth", 1024, scene, true, true);
-        scene.customRenderTargets.push(rt4);
-        rt4.activeCamera = camera4;
-        rt4.renderList = scene.meshes;
-
-        var mon1 = BABYLON.Mesh.CreatePlane("plane", 5, scene);
-        mon1.position = new BABYLON.Vector3(-8.8, -7.8, 25)
-        // mon1.showBoundingBox = true;
-        var mon1mat = new BABYLON.StandardMaterial("texturePlane", scene);
-        mon1mat.diffuseTexture = rt1;
-        mon1.material = mon1mat;
-        mon1.parent = camera;
-        mon1.layerMask = 1;
-
-        var mon2 = BABYLON.Mesh.CreatePlane("plane", 5, scene);
-        mon2.position = new BABYLON.Vector3(-2.9, -7.8, 25)
-        // mon2.showBoundingBox = true;
-        var mon2mat = new BABYLON.StandardMaterial("texturePlane", scene);
-        mon2mat.diffuseTexture = rt2;
-        mon2.material = mon2mat;
-        mon2.parent = camera;
-        mon2.layerMask = 1;
-
-        var mon3 = BABYLON.Mesh.CreatePlane("plane", 5, scene);
-        mon3.position = new BABYLON.Vector3(2.9, -7.8, 25)
-        // mon3.showBoundingBox = true;
-        var mon3mat = new BABYLON.StandardMaterial("texturePlane", scene);
-        mon3mat.diffuseTexture = rt3;
-        mon3.material = mon3mat;
-        mon3.parent = camera;
-        mon3.layerMask = 1;
-
-
-        var mon4 = BABYLON.Mesh.CreatePlane("plane", 5, scene);
-        mon4.position = new BABYLON.Vector3(8.8, -7.8, 25)
-        // mon4.showBoundingBox = true;
-        var mon4mat = new BABYLON.StandardMaterial("texturePlane", scene);
-        mon4mat.diffuseTexture = rt4;
-        mon4.material = mon4mat;
-        mon4.parent = camera;
-        mon4.layerMask = 1;
-
-
-        var butback = BABYLON.MeshBuilder.CreatePlane("plane", { width: 25, height: 6 }, scene);
-        butback.position = new BABYLON.Vector3(0, -8.2, 26)
-        // butback.showBoundingBox = true;
-        var butbackmat = new BABYLON.StandardMaterial("texturePlane", scene);
-        butbackmat.diffuseColor = BABYLON.Color3.Black();
-        butback.material = butbackmat;
-        butback.parent = camera;
-        butback.layerMask = 1;
-
-        var plane = BABYLON.Mesh.CreatePlane("plane", 120, scene);
-        plane.position.y = -5;
-        plane.rotation.x = Math.PI / 2;
-
-        var materialSphere1 = new BABYLON.StandardMaterial("texture1", scene);
-        materialSphere1.wireframe = true;
-
-        //Creation of a red material with alpha
-        var materialSphere2 = new BABYLON.StandardMaterial("texture2", scene);
-        materialSphere2.diffuseColor = new BABYLON.Color3(1, 0, 0); //Red
-        materialSphere2.alpha = 0.3;
-
-        //Creation of a material with an image texture
-        var materialSphere3 = new BABYLON.StandardMaterial("texture3", scene);
-        materialSphere3.diffuseTexture = new BABYLON.Texture("/playground/textures/amiga.jpg", scene);
-
-        //Creation of a material with translated texture
-        var materialSphere4 = new BABYLON.StandardMaterial("texture4", scene);
-        materialSphere4.diffuseTexture = new BABYLON.Texture("/playground/textures/floor.png", scene);
-        materialSphere4.diffuseTexture.vOffset = 0.1;//Vertical offset of 10%
-        materialSphere4.diffuseTexture.uOffset = 0.4;//Horizontal offset of 40%
-
-        //Creation of a material with an alpha texture
-        var materialSphere5 = new BABYLON.StandardMaterial("texture5", scene);
-        materialSphere5.diffuseTexture = new BABYLON.Texture("/playground/textures/rock.png", scene);
-        materialSphere5.diffuseTexture.hasAlpha = true;//Has an alpha
-
-        //Creation of a material and show all the faces
-        var materialSphere6 = new BABYLON.StandardMaterial("texture6", scene);
-        materialSphere6.diffuseTexture = new BABYLON.Texture("/playground/textures/grass.png", scene);
-        materialSphere6.diffuseTexture.hasAlpha = true;//Have an alpha
-        materialSphere6.backFaceCulling = false;//Show all the faces of the element
-
-        //Creation of a repeated textured material
-        var materialPlane = new BABYLON.StandardMaterial("texturePlane", scene);
-        materialPlane.diffuseTexture = new BABYLON.Texture("/playground/textures/mixMap.png", scene);
-        materialPlane.diffuseTexture.uScale = 5.0;
-        materialPlane.diffuseTexture.vScale = 5.0;
-        materialPlane.backFaceCulling = false;
-
-        //Apply the materials to meshes
-        sphere1.material = materialSphere1;
-        sphere2.material = materialSphere2;
+        // Our built-in 'ground' shape. Params: name, width, depth, subdivs, scene
+        var ground = BABYLON.Mesh.CreateGround("node_damagedHelmet_-6514", 6, 6, 2, scene);
+        ground.parent = tn;
 
-        sphere3.material = materialSphere3;
-        sphere4.material = materialSphere4;
+        let num = 5;
+        let angStep = 6.283185307 / num;
+        let rad = 2;
+        let p = sceneRoot;
+        for (let i = 0; i < num; i++) {
+            // Our built-in 'sphere' shape. Params: name, subdivs, size, scene
+            let sphere = BABYLON.Mesh.CreateSphere(`sphere${i}`, 16, 2, scene);
 
-        sphere5.material = materialSphere5;
-        sphere6.material = materialSphere6;
-
-        plane.material = materialPlane;
-
-        new BABYLON.Mesh('mesh_without_geometry', scene);
-
-
-
-        var d = 50;
-        var cubes = new Array();
-        for (var i = 0; i < 360; i += 20) {
-            var r = BABYLON.Tools.ToRadians(i);
-            var b = BABYLON.Mesh.CreateBox("Box #" + i / 20, 5, scene, false);
-            b.position.x = Math.cos(r) * d;
-            b.position.z = Math.sin(r) * d;
-            cubes.push(b);
+            // Move the sphere upward 1/2 its height        
+            sphere.position.y = 0.2;
+            sphere.position.x = Math.sin(i * angStep) * rad;
+            sphere.position.z = Math.cos(i * angStep) * rad;
+            sphere.parent = p;
+            p = sphere;
         }
 
-        scene.getMeshByName("Box #6").scaling.x = 2;
-
-        //Other meshes, to contrôl mesh hierarchy
-        var box1 = BABYLON.MeshBuilder.CreateBox("box1", {size: 1}, scene);
-
-        var box2 = BABYLON.MeshBuilder.CreateBox("box2", {size: 1}, scene);
-        box2.position.x = 1.5;
-        box2.parent = box1;
-
-        var box3 = BABYLON.MeshBuilder.CreateBox("box3", {size: 1}, scene);
-        box3.position.x = 1.5;
-        box3.parent = box2;
-
-        var box4 = BABYLON.MeshBuilder.CreateBox("box4", {size: 1}, scene);
-        box4.position.x = 1.5;
-        box4.parent = box3;
-
-        window.addEventListener("keydown", function (evt) {
-            sphere1.dispose();
-            var sphere7 = BABYLON.Mesh.CreateSphere("Sphere7", 10.0, 9.0, scene);
-            sphere7.position.x = 40;
+        let t = 0;
+        scene.registerBeforeRender(() => {
+            ground.rotation.y += 0.01;
+            ground.position.y = Math.cos(t += 0.01);
         });
 
-        var sphere_1 = BABYLON.Mesh.CreateSphere("_sphere_1", 16, 2, scene);
-
-        var assets_mesh = new BABYLON.AbstractMesh("assets_mesh", scene);
-        var sphere_2 = BABYLON.Mesh.CreateSphere("_sphere_2", 3, 2, scene);
-        var sphere_3 = BABYLON.Mesh.CreateSphere("_sphere_3", 2, 2, scene);
-        var scene_mesh = new BABYLON.AbstractMesh;
-        scene_mesh.name="scene_mesh";
-    
-        sphere_1.parent = assets_mesh;
-        sphere_2.parent = assets_mesh;
-        sphere_3.parent = assets_mesh;
-
-        for (var i=0; i<10 ; i++){
-            var inst = sphere_1.createInstance("C_" + i + "clone");
-            inst.isVisible = true;
-            inst.setEnabled = true;
-            inst.parent = scene_mesh;
-            inst.position.x = i*2;
-            inst.refreshBoundingInfo();
-            inst.computeWorldMatrix();
-        }
-
-        // to test reflection prob texture handling
-        var probe = new BABYLON.ReflectionProbe("probe", 512, scene);
-        probe.renderList.push(sphere1);
-
-        // gui
-        var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
-
-        var picker = new BABYLON.GUI.ColorPicker();
-        picker.height = "150px";
-        picker.width = "150px";
-        picker.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
-
-        var checkbox = new BABYLON.GUI.Checkbox();
-        checkbox.width = "20px";
-        checkbox.height = "20px";
-
-        var slider = new BABYLON.GUI.Slider();
-        slider.minimum = 0;
-        slider.maximum = 2 * Math.PI;
-        slider.value = 0;
-        slider.height = "20px";
-        slider.width = "200px";
-
-        var line = new BABYLON.GUI.Line();
-        line.x1 = 10;
-        line.y1 = 10;
-        line.x2 = 1000;
-        line.y2 = 500;
-
-        var panel = new BABYLON.GUI.StackPanel();    
-
-        var button = BABYLON.GUI.Button.CreateSimpleButton("but", "Click Me");
-        button.width = 0.2;
-        button.height = "40px";
-        button.color = "white";
-        button.background = "green";
-        panel.addControl(button);     
-
-        var button2 = BABYLON.GUI.Button.CreateSimpleButton("but2", "Click Me also!");
-        button2.width = 0.2;
-        button2.height = "40px";
-        button2.color = "white";
-        button2.background = "green";
-        panel.addControl(button2);     
-
-        var ellipse1 = new BABYLON.GUI.Ellipse();
-        ellipse1.width = "100px"
-        ellipse1.height = "100px";
-        ellipse1.color = "Orange";
-        ellipse1.thickness = 4;
-        ellipse1.background = "green";
-
+        scene.createDefaultCameraOrLight(true);
+        scene.activeCamera.attachControl(canvas);
 
-        advancedTexture.addControl(ellipse1);    
-        advancedTexture.addControl(panel);   
-        advancedTexture.addControl(picker);    
-        advancedTexture.addControl(checkbox);    
-        advancedTexture.addControl(slider);    
-        advancedTexture.addControl(line);    
-        advancedTexture.addControl(checkbox);  
-        
         this.scene = scene;
     };
     return Test;

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

@@ -20,6 +20,7 @@ module BABYLON.GLTF2.Extensions {
                 loader._loadMaterialBaseProperties(context, material);
                 this._loadSpecularGlossinessProperties(loader, context, material, extension);
                 assign(material.babylonMaterial, true);
+                onComplete();
             });
         }
 

+ 22 - 16
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -10,7 +10,7 @@ module BABYLON.GLTF2.Extensions {
         /**
          * Specify the minimal delay between LODs in ms (default = 250)
          */
-        public static MinimalLODDelay = 250;
+        public Delay = 250;
 
         public get name() {
             return "MSFT_lod";
@@ -70,7 +70,7 @@ module BABYLON.GLTF2.Extensions {
                     loader._tryCatchOnError(() => {
                         this._loadNodeLOD(loader, context, nodes, index - 1, onComplete);
                     });
-                }, MSFTLOD.MinimalLODDelay);
+                }, this.Delay);
             });
         }
 
@@ -96,24 +96,30 @@ module BABYLON.GLTF2.Extensions {
 
         private _loadMaterialLOD(loader: GLTFLoader, context: string, materials: IGLTFMaterial[], index: number, assign: (babylonMaterial: Material, isNew: boolean) => void, onComplete: () => void): void {
             loader._loadMaterial(context, materials[index], (babylonMaterial, isNew) => {
-                assign(babylonMaterial, isNew);
+                if (index === materials.length - 1) {
+                    assign(babylonMaterial, isNew);
 
-                if (index === 0) {
-                    onComplete();
-                    return;
+                    // Load the next LOD when the loader is ready to render.
+                    loader._executeWhenRenderReady(() => {
+                        this._loadMaterialLOD(loader, context, materials, index - 1, assign, onComplete);
+                    });
                 }
-
-                // Load the next LOD when the loader is ready to render and
-                // all active material textures of the current LOD are loaded.
-                loader._executeWhenRenderReady(() => {
+                else {
                     BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), () => {
-                        setTimeout(() => {
-                            loader._tryCatchOnError(() => {
-                                this._loadMaterialLOD(loader, context, materials, index - 1, assign, onComplete);
-                            });
-                        }, MSFTLOD.MinimalLODDelay);
+                        assign(babylonMaterial, isNew);
+
+                        if (index === 0) {
+                            onComplete();
+                        }
+                        else {
+                            setTimeout(() => {
+                                loader._tryCatchOnError(() => {
+                                    this._loadMaterialLOD(loader, context, materials, index - 1, assign, onComplete);
+                                });
+                            }, this.Delay);
+                        }
                     });
-                });
+                }
             });
         }
     }

+ 162 - 47
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -33,6 +33,11 @@ module BABYLON.GLTF2 {
         readonly BYTES_PER_ELEMENT: number;
     }
 
+    interface GLTFLoaderRequest extends XMLHttpRequest {
+        _loaded: Nullable<number>;
+        _total: Nullable<number>;
+    }
+
     export class GLTFLoader implements IGLTFLoader {
         public _gltf: IGLTF;
         public _babylonScene: Scene;
@@ -46,7 +51,7 @@ module BABYLON.GLTF2 {
         private _progressCallback: (event: ProgressEvent) => void;
         private _errorCallback: (message: string) => void;
         private _renderReady = false;
-        private _requests = new Array<XMLHttpRequest>();
+        private _requests = new Array<GLTFLoaderRequest>();
 
         private _renderReadyObservable = new Observable<GLTFLoader>();
 
@@ -127,10 +132,27 @@ module BABYLON.GLTF2 {
             });
         }
 
-        private _onProgress(event: ProgressEvent): void {
-            if (this._progressCallback) {
-                this._progressCallback(event);
+        private _onProgress(): void {
+            if (!this._progressCallback) {
+                return;
             }
+
+            let loaded = 0;
+            let total = 0;
+            for (let request of this._requests) {
+                if (!request._loaded || !request._total) {
+                    return;
+                }
+
+                loaded += request._loaded;
+                total += request._total;
+            }
+
+            this._progressCallback(new ProgressEvent("GLTFLoaderProgress", {
+                lengthComputable: true,
+                loaded: loaded,
+                total: total
+            }));
         }
 
         public _executeWhenRenderReady(func: () => void): void {
@@ -276,6 +298,10 @@ module BABYLON.GLTF2 {
                 }
             }
 
+            if (this._parent.onMeshLoaded) {
+                this._parent.onMeshLoaded(this._rootNode.babylonMesh);
+            }
+
             let nodeIndices = scene.nodes;
 
             this._traverseNodes(context, nodeIndices, (node, parentNode) => {
@@ -361,6 +387,10 @@ module BABYLON.GLTF2 {
                     this._loadNode("#/nodes/" + index, childNode);
                 }
             }
+
+            if (this._parent.onMeshLoaded) {
+                this._parent.onMeshLoaded(node.babylonMesh);
+            }
         }
 
         private _loadMesh(context: string, node: IGLTFNode, mesh: IGLTFMesh): void {
@@ -417,17 +447,7 @@ module BABYLON.GLTF2 {
                             this._parent.onMaterialLoaded(babylonMaterial);
                         }
 
-                        if (this._parent.onBeforeMaterialReadyAsync) {
-                            this._addLoaderPendingData(material);
-                            this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, subMaterials[index] != null, () => {
-                                this._tryCatchOnError(() => {
-                                    subMaterials[index] = babylonMaterial;
-                                    this._removeLoaderPendingData(material);
-                                });
-                            });
-                        } else {
-                            subMaterials[index] = babylonMaterial;
-                        }
+                        subMaterials[index] = babylonMaterial;
                     });
                 }
             };
@@ -1078,26 +1098,17 @@ module BABYLON.GLTF2 {
                     throw new Error(context + ": Uri is missing");
                 }
 
-                if (GLTFUtils.IsBase64(buffer.uri)) {
-                    const data = GLTFUtils.DecodeBase64(buffer.uri);
-                    buffer.loadedData = new Uint8Array(data);
-                    onSuccess(buffer.loadedData);
+                buffer.loadedObservable = new Observable<IGLTFBuffer>();
+                buffer.loadedObservable.add(buffer => {
+                    onSuccess(buffer.loadedData!);
                     this._removePendingData(buffer);
-                }
-                else {
-
-                    buffer.loadedObservable = new Observable<IGLTFBuffer>();
-                    buffer.loadedObservable.add(buffer => {
-                        onSuccess(buffer.loadedData!);
-                        this._removePendingData(buffer);
-                    });
+                });
 
-                    this._loadUri(context, buffer.uri, data => {
-                        buffer.loadedData = data;
-                        buffer.loadedObservable!.notifyObservers(buffer);
-                        buffer.loadedObservable = undefined;
-                    });
-                }
+                this._loadUriAsync(context, buffer.uri, data => {
+                    buffer.loadedData = data;
+                    buffer.loadedObservable!.notifyObservers(buffer);
+                    buffer.loadedObservable = undefined;
+                });
             }
         }
 
@@ -1217,8 +1228,14 @@ module BABYLON.GLTF2 {
         public _removePendingData(data: any): void {
             if (!this._renderReady) {
                 if (--this._renderPendingCount === 0) {
-                    this._renderReady = true;
-                    this._onRenderReady();
+                    this._addLoaderPendingData(this);
+                    this._compileMaterialsAsync(() => {
+                        this._compileShadowGeneratorsAsync(() => {
+                            this._removeLoaderPendingData(this);
+                            this._renderReady = true;
+                            this._onRenderReady();
+                        });
+                    });
                 }
             }
 
@@ -1252,7 +1269,7 @@ module BABYLON.GLTF2 {
             this._loaderTrackers.push(tracker);
 
             this._addLoaderPendingData(tracker);
-
+            
             action();
 
             this._removeLoaderPendingData(tracker);
@@ -1469,7 +1486,7 @@ module BABYLON.GLTF2 {
                     throw new Error(context + ": Failed to find source " + texture.source);
                 }
 
-                this._loadImage("#/images/" + image.index, image, data => {
+                this._loadImageAsync("#/images/" + image.index, image, data => {
                     texture.url = URL.createObjectURL(new Blob([data], { type: image.mimeType }));
                     texture.dataReadyObservable!.notifyObservers(texture);
                     texture.dataReadyObservable = undefined;
@@ -1488,14 +1505,9 @@ module BABYLON.GLTF2 {
             return babylonTexture;
         }
 
-        private _loadImage(context: string, image: IGLTFImage, onSuccess: (data: ArrayBufferView) => void): void {
+        private _loadImageAsync(context: string, image: IGLTFImage, onSuccess: (data: ArrayBufferView) => void): void {
             if (image.uri) {
-                if (GLTFUtils.IsBase64(image.uri)) {
-                    onSuccess(new Uint8Array(GLTFUtils.DecodeBase64(image.uri)));
-                }
-                else {
-                    this._loadUri(context, image.uri, onSuccess);
-                }
+                this._loadUriAsync(context, image.uri, onSuccess);
             }
             else {
                 const bufferView = GLTFLoader._GetProperty(this._gltf.bufferViews, image.bufferView);
@@ -1507,7 +1519,12 @@ module BABYLON.GLTF2 {
             }
         }
 
-        public _loadUri(context: string, uri: string, onSuccess: (data: ArrayBufferView) => void): void {
+        public _loadUriAsync(context: string, uri: string, onSuccess: (data: ArrayBufferView) => void): void {
+            if (GLTFUtils.IsBase64(uri)) {
+                onSuccess(new Uint8Array(GLTFUtils.DecodeBase64(uri)));
+                return;
+            }
+
             if (!GLTFUtils.ValidateUri(uri)) {
                 throw new Error(context + ": Uri '" + uri + "' is invalid");
             }
@@ -1518,15 +1535,21 @@ module BABYLON.GLTF2 {
                 });
             }, event => {
                 this._tryCatchOnError(() => {
-                    this._onProgress(event);
+                    if (request && !this._renderReady) {
+                        request._loaded = event.loaded;
+                        request._total = event.total;
+                        this._onProgress();
+                    }
                 });
             }, this._babylonScene.database, true, request => {
                 this._tryCatchOnError(() => {
                     throw new Error(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""));
                 });
-            });
+            }) as GLTFLoaderRequest;
 
             if (request) {
+                request._loaded = null;
+                request._total = null;
                 this._requests.push(request);
             }
         }
@@ -1630,6 +1653,98 @@ module BABYLON.GLTF2 {
 
             return 0;
         }
+
+        private _compileMaterialAsync(babylonMaterial: Material, babylonMesh: AbstractMesh, onSuccess: () => void): void {
+            if (!this._parent.compileMaterials) {
+                onSuccess();
+                return;
+            }
+
+            if (this._parent.useClipPlane) {
+                babylonMaterial.forceCompilation(babylonMesh, () => {
+                    babylonMaterial.forceCompilation(babylonMesh, () => {
+                        this._tryCatchOnError(onSuccess);
+                    }, { clipPlane: true });
+                });
+            }
+            else {
+                babylonMaterial.forceCompilation(babylonMesh, () => {
+                    this._tryCatchOnError(onSuccess);
+                });
+            }
+        }
+
+        private _compileMaterialsAsync(onSuccess: () => void): void {
+            if (!this._parent.compileMaterials || !this._gltf.materials) {
+                onSuccess();
+                return;
+            }
+
+            let meshes = this._getMeshes();
+
+            let remaining = 0;
+            for (let mesh of meshes) {
+                if (mesh.material instanceof MultiMaterial) {
+                    for (let subMaterial of mesh.material.subMaterials) {
+                        if (subMaterial) {
+                            remaining++;
+                        }
+                    }
+                }
+            }
+
+            if (remaining === 0) {
+                onSuccess();
+                return;
+            }
+
+            for (let mesh of meshes) {
+                if (mesh.material instanceof MultiMaterial) {
+                    for (let subMaterial of mesh.material.subMaterials) {
+                        if (subMaterial) {
+                            this._compileMaterialAsync(subMaterial, mesh, () => {
+                                if (--remaining === 0) {
+                                    onSuccess();
+                                }
+                            });
+                        }
+                    }
+                }
+            }
+        }
+
+        private _compileShadowGeneratorsAsync(onSuccess: () => void): void {
+            if (!this._parent.compileShadowGenerators) {
+                onSuccess();
+                return;
+            }
+
+            let lights = this._babylonScene.lights;
+
+            let remaining = 0;
+            for (let light of lights) {
+                let generator = light.getShadowGenerator();
+                if (generator) {
+                    remaining++;
+                }
+            }
+
+            if (remaining === 0) {
+                onSuccess();
+                return;
+            }
+
+            for (let light of lights) {
+                let generator = light.getShadowGenerator();
+                if (generator) {
+                    generator.forceCompilation(() => {
+                        if (--remaining === 0) {
+                            this._tryCatchOnError(onSuccess);
+                        }
+                    });
+                }
+            }
+        }
     }
 
     BABYLON.GLTFFileLoader.CreateGLTFLoaderV2 = parent => new GLTFLoader(parent);

+ 7 - 8
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -31,20 +31,19 @@ module BABYLON {
         public onParsed: (data: IGLTFLoaderData) => void;
 
         // V1 options
-        public static HomogeneousCoordinates: boolean = false;
-        public static IncrementalLoading: boolean = true;
+        public static HomogeneousCoordinates = false;
+        public static IncrementalLoading = true;
 
         // V2 options
-        public coordinateSystemMode: GLTFLoaderCoordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+        public coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+        public compileMaterials = false;
+        public compileShadowGenerators = false;
+        public useClipPlane = false;
+        public onMeshLoaded: (mesh: AbstractMesh) => void;
         public onTextureLoaded: (texture: BaseTexture) => void;
         public onMaterialLoaded: (material: Material) => void;
 
         /**
-         * Let the user decides if he needs to process the material (like precompilation) before affecting it to meshes
-         */
-        public onBeforeMaterialReadyAsync: (material: Material, targetMesh: AbstractMesh, isLOD: boolean, callback: () => void) => void;
-
-        /**
          * Raised when the asset is completely loaded, just before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete just after onSuccess.

+ 1 - 1
sandbox/index.css

@@ -56,7 +56,7 @@ a {
     width: 100%;
     height: 100%;
     top: 0;
-    margin-bottom: 70px;
+    margin-bottom: 65px;
     touch-action: none;
     -ms-touch-action: none;
 }

+ 2 - 7
sandbox/index.js

@@ -38,13 +38,8 @@ if (BABYLON.Engine.isSupported()) {
         if (plugin.name !== "gltf") {
             return;
         }
-        plugin.onBeforeMaterialReadyAsync = function(material, mesh, isLOD, callback) {
-            if (!isLOD) {
-                callback();
-                return;
-            }
-            material.forceCompilation(mesh, callback);
-        }
+
+        plugin.compileMaterials = true;
     });
 
     // Resize

+ 24 - 10
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -14,7 +14,7 @@
 
         recreateShadowMap(): void;
 
-        forceCompilation(onCompiled: (generator: ShadowGenerator) => void, options?: { useInstances: boolean }): void;
+        forceCompilation(onCompiled?: (generator: ShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void;
 
         serialize(): any;
         dispose(): void;
@@ -602,31 +602,47 @@
         /**
          * Force shader compilation including textures ready check
          */
-        public forceCompilation(onCompiled: (generator: ShadowGenerator) => void, options?: { useInstances: boolean }): void {
+        public forceCompilation(onCompiled?: (generator: ShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void {
+            let localOptions = {
+                useInstances: false,
+                ...options
+            };
+
             let shadowMap = this.getShadowMap();
             if (!shadowMap) {
+                if (onCompiled) {
+                    onCompiled(this);
+                }
                 return;
             }
 
-            var subMeshes = new Array<SubMesh>();
-            var currentIndex = 0;
-
             let renderList = shadowMap.renderList;
-
             if (!renderList) {
+                if (onCompiled) {
+                    onCompiled(this);
+                }
                 return;
             }
 
+            var subMeshes = new Array<SubMesh>();
             for (var mesh of renderList) {
                 subMeshes.push(...mesh.subMeshes);
             }
+            if (subMeshes.length === 0) {
+                if (onCompiled) {
+                    onCompiled(this);
+                }
+                return;
+            }
+
+            var currentIndex = 0;
 
             var checkReady = () => {
                 if (!this._scene || !this._scene.getEngine()) {
                     return;
                 }
 
-                while (this.isReady(subMeshes[currentIndex], options ? options.useInstances : false)) {
+                while (this.isReady(subMeshes[currentIndex], localOptions.useInstances)) {
                     currentIndex++;
                     if (currentIndex >= subMeshes.length) {
                         if (onCompiled) {
@@ -638,9 +654,7 @@
                 setTimeout(checkReady, 16);
             };
 
-            if (subMeshes.length > 0) {
-                checkReady();
-            }
+            checkReady();
         }
 
         /**

+ 9 - 3
src/Materials/babylon.material.ts

@@ -565,7 +565,13 @@
         /**
          * Force shader compilation including textures ready check
          */
-        public forceCompilation(mesh: AbstractMesh, onCompiled: (material: Material) => void, options?: { alphaTest: boolean, clipPlane: boolean }): void {
+        public forceCompilation(mesh: AbstractMesh, onCompiled?: (material: Material) => void, options?: Partial<{ alphaTest: Nullable<boolean>, clipPlane: boolean }>): void {
+            let localOptions = {
+                alphaTest: null,
+                clipPlane: false,
+                ...options
+            };
+
             var subMesh = new BaseSubMesh();
             var scene = this.getScene();
             var engine = scene.getEngine();
@@ -582,9 +588,9 @@
                 var alphaTestState = engine.getAlphaTesting();
                 var clipPlaneState = scene.clipPlane;
 
-                engine.setAlphaTesting(options ? options.alphaTest : this.needAlphaTesting());
+                engine.setAlphaTesting(localOptions.alphaTest || (!this.needAlphaBlendingForMesh(mesh) && this.needAlphaTesting()));
 
-                if (options && options.clipPlane) {
+                if (localOptions.clipPlane) {
                     scene.clipPlane = new Plane(0, 0, 0, 1);
                 }
 

+ 46 - 20
src/Mesh/babylon.abstractMesh.ts

@@ -42,10 +42,12 @@
             Y: 1,
             Z: 1
         };
-        private _facetDepthSort: boolean = false;                           // is the facet depth sort enabled
-        private _originalIndices: IndicesArray;                             // copy of the original indices array
-        private _depthSortedFacets: { ind: number, sqDistance: number }[];    // array of depth sorted facets
-        private _facetDepthSortFunction: (f1: { ind: number, sqDistance: number }, f2: { ind: number, sqDistance: number }) => number;  // facet depth sort function
+      
+        private _facetDepthSort: boolean = false;                           // is the facet depth sort to be computed
+        private _facetDepthSortEnabled: boolean = false;                    // is the facet depth sort initialized
+        private _depthSortedIndices: IndicesArray;                          // copy of the indices array to store them once sorted
+        private _depthSortedFacets: {ind: number, sqDistance: number}[];    // array of depth sorted facets
+        private _facetDepthSortFunction: (f1: {ind: number, sqDistance: number}, f2: {ind: number, sqDistance: number}) => number;  // facet depth sort function
         private _facetDepthSortFrom: Vector3;                               // location where to depth sort from
         private _facetDepthSortOrigin: Vector3;                             // same as facetDepthSortFrom but expressed in the mesh local space
         private _invertedMatrix: Matrix;                                    // Mesh inverted World Matrix
@@ -78,6 +80,7 @@
         /**
          * Boolean : must the facet be depth sorted on next call to `updateFacetData()` ?  
          * Works only for updatable meshes.  
+         * Doesn't work with multi-materials.  
          */
         public get mustDepthSortFacets(): boolean {
             return this._facetDepthSort;
@@ -1495,7 +1498,6 @@
             }
             var positions = this.getVerticesData(VertexBuffer.PositionKind);
             var indices = this.getIndices();
-            var indicesForComputeNormals = indices;
             var normals = this.getVerticesData(VertexBuffer.NormalKind);
             var bInfo = this.getBoundingInfo();
 
@@ -1503,10 +1505,31 @@
                 return this;
             }
 
-            if (this._facetDepthSort && !this._originalIndices) {
+            if (this._facetDepthSort && !this._facetDepthSortEnabled) {
                 // init arrays, matrix and sort function on first call
-                this._originalIndices = new Uint32Array(indices!);
-                this._facetDepthSortFunction = function (f1, f2) {
+                this._facetDepthSortEnabled = true;
+                if (indices instanceof Uint16Array) {
+                    this._depthSortedIndices = new Uint16Array(indices!);
+                }
+                else if (indices instanceof Uint32Array) {
+                    this._depthSortedIndices = new Uint32Array(indices!);
+                } 
+                else {
+                    var needs32bits = false;
+                    for (var i = 0; i < indices!.length; i++) {
+                        if (indices![i] > 65535) {
+                            needs32bits = true;
+                            break;
+                        }
+                    }
+                    if (needs32bits) {
+                        this._depthSortedIndices = new Uint32Array(indices!);
+                    } 
+                    else {
+                        this._depthSortedIndices = new Uint16Array(indices!);
+                    }
+                }               
+                this._facetDepthSortFunction = function(f1, f2) {
                     return (f2.sqDistance - f1.sqDistance);
                 };
                 if (!this._facetDepthSortFrom) {
@@ -1543,25 +1566,25 @@
             this._facetParameters.subDiv = this._subDiv;
             this._facetParameters.ratio = this.partitioningBBoxRatio;
             this._facetParameters.depthSort = this._facetDepthSort;
-            if (this._facetDepthSort) {
+            if (this._facetDepthSort && this._facetDepthSortEnabled) {
                 this.computeWorldMatrix(true);
                 this._worldMatrix.invertToRef(this._invertedMatrix);
                 Vector3.TransformCoordinatesToRef(this._facetDepthSortFrom, this._invertedMatrix, this._facetDepthSortOrigin);
                 this._facetParameters.distanceTo = this._facetDepthSortOrigin;
-                indicesForComputeNormals = this._originalIndices;
             }
             this._facetParameters.depthSortedFacets = this._depthSortedFacets;
-            VertexData.ComputeNormals(positions, indicesForComputeNormals, normals, this._facetParameters);
+            VertexData.ComputeNormals(positions, indices, normals, this._facetParameters);
 
-            if (this._facetDepthSort) {
+            if (this._facetDepthSort && this._facetDepthSortEnabled) {
                 this._depthSortedFacets.sort(this._facetDepthSortFunction);
-                for (var sorted = 0; sorted < this._facetNb; sorted++) {
-                    var sind = this._depthSortedFacets[sorted].ind;
-                    indices![sorted * 3] = this._originalIndices[sind];
-                    indices![sorted * 3 + 1] = this._originalIndices[sind + 1];
-                    indices![sorted * 3 + 2] = this._originalIndices[sind + 2];
+                var l = (this._depthSortedIndices.length / 3)|0;
+                for (var f = 0; f < l; f++) {
+                    var sind = this._depthSortedFacets[f].ind;
+                    this._depthSortedIndices[f * 3] = indices![sind];
+                    this._depthSortedIndices[f * 3 + 1] = indices![sind + 1];
+                    this._depthSortedIndices[f * 3 + 2] = indices![sind + 2];
                 }
-                this.updateIndices(indices!);
+                this.updateIndices(this._depthSortedIndices);
             }
 
             return this;
@@ -1750,7 +1773,7 @@
                 this._facetNormals = new Array<Vector3>();
                 this._facetPartitioning = new Array<number[]>();
                 this._facetParameters = null;
-                this._originalIndices = new Uint32Array(0);
+                this._depthSortedIndices = new Uint32Array(0);
             }
             return this;
         }
@@ -1761,7 +1784,10 @@
         public updateIndices(indices: IndicesArray): AbstractMesh {
             return this;
         }
-
+        /**
+         * The mesh Geometry. Actually used by the Mesh object.
+         * Returns a blank geometry object.
+         */
         /**
          * Creates new normals data for the mesh.
          * @param updatable.

+ 1 - 1
src/Mesh/babylon.mesh.vertexData.ts

@@ -2307,7 +2307,7 @@
             }
 
             // Loop : 1 indice triplet = 1 facet
-            var nbFaces = indices.length / 3;
+            var nbFaces = (indices.length / 3)|0;
             for (index = 0; index < nbFaces; index++) {
 
                 // get the indexes of the coordinates of each vertex of the facet