ソースを参照

Merge branch 'master' into LineMeshBoundingBias

Julien Barrois 6 年 前
コミット
9fd00a8df4
34 ファイル変更22006 行追加21376 行削除
  1. 8893 8844
      Playground/babylon.d.txt
  2. 12284 12241
      dist/preview release/babylon.d.ts
  3. 1 1
      dist/preview release/babylon.js
  4. 181 60
      dist/preview release/babylon.max.js
  5. 181 60
      dist/preview release/babylon.no-module.max.js
  6. 1 1
      dist/preview release/babylon.worker.js
  7. 181 60
      dist/preview release/es6.js
  8. 1 4
      dist/preview release/glTF2Interface/package.json
  9. 8 2
      dist/preview release/gui/babylon.gui.d.ts
  10. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  11. 16 4
      dist/preview release/gui/babylon.gui.module.d.ts
  12. 2 2
      dist/preview release/gui/package.json
  13. 5 5
      dist/preview release/inspector/package.json
  14. 3 5
      dist/preview release/loaders/package.json
  15. 2 2
      dist/preview release/materialsLibrary/package.json
  16. 2 2
      dist/preview release/postProcessesLibrary/package.json
  17. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  18. 3 3
      dist/preview release/serializers/package.json
  19. 1 1
      dist/preview release/viewer/babylon.viewer.js
  20. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  21. 10 4
      dist/preview release/what's new.md
  22. 8 2
      gui/src/2D/controls/stackPanel.ts
  23. 1 1
      package.json
  24. 1 1
      src/Engine/babylon.engine.ts
  25. 66 40
      src/Materials/babylon.material.ts
  26. 3 2
      src/Materials/babylon.shaderMaterial.ts
  27. 4 2
      src/Materials/babylon.uniformBuffer.ts
  28. 9 0
      src/Mesh/babylon.abstractMesh.ts
  29. 5 5
      src/Mesh/babylon.instancedMesh.ts
  30. 1 2
      src/Mesh/babylon.linesMesh.ts
  31. 56 6
      src/Mesh/babylon.mesh.ts
  32. 1 0
      src/Mesh/babylon.transformNode.ts
  33. 4 1
      src/babylon.abstractScene.ts
  34. 68 9
      src/babylon.scene.ts

ファイルの差分が大きいため隠しています
+ 8893 - 8844
Playground/babylon.d.txt


ファイルの差分が大きいため隠しています
+ 12284 - 12241
dist/preview release/babylon.d.ts


ファイルの差分が大きいため隠しています
+ 1 - 1
dist/preview release/babylon.js


+ 181 - 60
dist/preview release/babylon.max.js

@@ -12694,7 +12694,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.0";
+                return "4.0.0-alpha.1";
             },
             enumerable: true,
             configurable: true
@@ -21200,7 +21200,14 @@ var BABYLON;
                 if (this._material === value) {
                     return;
                 }
+                // remove from material mesh map id needed
+                if (this._material && this._material.meshMap) {
+                    this._material.meshMap[this.uniqueId] = undefined;
+                }
                 this._material = value;
+                if (value && value.meshMap) {
+                    value.meshMap[this.uniqueId] = this;
+                }
                 if (this.onMaterialChangedObservable.hasObservers) {
                     this.onMaterialChangedObservable.notifyObservers(this);
                 }
@@ -25194,6 +25201,9 @@ var BABYLON;
             this.multiMaterials = new Array();
             /**
             * All of the materials added to this scene
+            * In the context of a Scene, it is not supposed to be modified manually.
+            * Any addition or removal should be done using the addMaterial and removeMAterial Scene methods.
+            * Note also that the order of the Material wihin the array is not significant and might change.
             * @see http://doc.babylonjs.com/babylon101/materials
             */
             this.materials = new Array();
@@ -25208,7 +25218,7 @@ var BABYLON;
             this.geometries = new Array();
             /**
             * All of the tranform nodes added to this scene
-            * In the context a the Scene, it is not supposed to be modified manually.
+            * In the context of a Scene, it is not supposed to be modified manually.
             * Any addition or removal should be done using the addTransformNode and removeTransformNode Scene methods.
             * Note also that the order of the TransformNode wihin the array is not significant and might change.
             * @see http://doc.babylonjs.com/how_to/transformnode
@@ -25893,6 +25903,7 @@ var BABYLON;
                 data: [],
                 length: 0
             };
+            _this._preventFreeActiveMeshesAndRenderingGroups = false;
             _this._activeMeshesFrozen = false;
             /** @hidden */
             _this._allowPostProcessClearColor = true;
@@ -25926,6 +25937,8 @@ var BABYLON;
             if (options && options.useGeometryIdsMap === true) {
                 _this.geometriesById = {};
             }
+            _this.useMaterialMeshMap = options && options.useGeometryIdsMap || false;
+            _this.useClonedMeshhMap = options && options.useClonedMeshhMap || false;
             return _this;
         }
         Object.defineProperty(Scene.prototype, "environmentTexture", {
@@ -26866,7 +26879,7 @@ var BABYLON;
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
             }
-            if (this.onPointerUp) {
+            if (this.onPointerUp && !clickInfo.ignore) {
                 this.onPointerUp(evt, pickResult, type);
             }
             return this;
@@ -26951,7 +26964,6 @@ var BABYLON;
                             if (Date.now() - _this._previousStartingPointerTime > Scene.DoubleClickDelay ||
                                 btn !== _this._previousButtonPressed) {
                                 clickInfo.singleClick = true;
-                                eventRaised = true;
                                 cb(clickInfo, _this._currentPickResult);
                             }
                         }
@@ -27017,10 +27029,8 @@ var BABYLON;
                         }
                     }
                 }
-                if (!eventRaised) {
-                    clickInfo.ignore = true;
-                    cb(clickInfo, _this._currentPickResult);
-                }
+                clickInfo.ignore = true;
+                cb(clickInfo, _this._currentPickResult);
             };
             this._onPointerMove = function (evt) {
                 _this._updatePointerPosition(evt);
@@ -27913,7 +27923,8 @@ var BABYLON;
             var index = this.meshes.indexOf(toRemove);
             if (index !== -1) {
                 // Remove from the scene if mesh found
-                this.meshes.splice(index, 1);
+                this.meshes[index] = this.meshes[this.meshes.length - 1];
+                this.meshes.pop();
             }
             this.onMeshRemovedObservable.notifyObservers(toRemove);
             if (recursive) {
@@ -28080,9 +28091,15 @@ var BABYLON;
          * @returns The index of the removed material
          */
         Scene.prototype.removeMaterial = function (toRemove) {
-            var index = this.materials.indexOf(toRemove);
+            var index = toRemove._indexInSceneMaterialArray;
             if (index !== -1) {
-                this.materials.splice(index, 1);
+                if (index !== this.materials.length - 1) {
+                    var lastMaterial = this.materials[this.materials.length - 1];
+                    this.materials[index] = lastMaterial;
+                    lastMaterial._indexInSceneMaterialArray = index;
+                }
+                toRemove._indexInSceneMaterialArray = -1;
+                this.materials.pop();
             }
             this.onMaterialRemovedObservable.notifyObservers(toRemove);
             return index;
@@ -28185,6 +28202,7 @@ var BABYLON;
          * @param newMaterial The material to add
          */
         Scene.prototype.addMaterial = function (newMaterial) {
+            newMaterial._indexInSceneMaterialArray = this.materials.length;
             this.materials.push(newMaterial);
             this.onNewMaterialAddedObservable.notifyObservers(newMaterial);
         };
@@ -28822,10 +28840,35 @@ var BABYLON;
         Scene.prototype.freeProcessedMaterials = function () {
             this._processedMaterials.dispose();
         };
+        Object.defineProperty(Scene.prototype, "blockfreeActiveMeshesAndRenderingGroups", {
+            /** Gets or sets a boolean blocking all the calls to freeActiveMeshes and freeRenderingGroups
+             * It can be used in order to prevent going through methods freeRenderingGroups and freeActiveMeshes several times to improve performance
+             * when disposing several meshes in a row or a hierarchy of meshes.
+             * When used, it is the responsability of the user to blockfreeActiveMeshesAndRenderingGroups back to false.
+             */
+            get: function () {
+                return this._preventFreeActiveMeshesAndRenderingGroups;
+            },
+            set: function (value) {
+                if (this._preventFreeActiveMeshesAndRenderingGroups === value) {
+                    return;
+                }
+                if (value) {
+                    this.freeActiveMeshes();
+                    this.freeRenderingGroups();
+                }
+                this._preventFreeActiveMeshesAndRenderingGroups = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Clear the active meshes smart array preventing retention point in mesh dispose.
          */
         Scene.prototype.freeActiveMeshes = function () {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
             this._activeMeshes.dispose();
             if (this.activeCamera && this.activeCamera._activeMeshes) {
                 this.activeCamera._activeMeshes.dispose();
@@ -28843,6 +28886,9 @@ var BABYLON;
          * Clear the info related to rendering groups preventing retention points during dispose.
          */
         Scene.prototype.freeRenderingGroups = function () {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
             if (this._renderingManager) {
                 this._renderingManager.freeRenderingGroups();
             }
@@ -32479,6 +32525,8 @@ var BABYLON;
             _this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NONE;
             /**
              * Gets the list of instances created from this mesh
+             * it is not supposed to be modified manually.
+             * Note also that the order of the InstancedMesh wihin the array is not significant and might change.
              * @see http://doc.babylonjs.com/how_to/how_to_use_instances
              */
             _this.instances = new Array();
@@ -32508,6 +32556,12 @@ var BABYLON;
                 ], ["_poseMatrix"]);
                 // Source mesh
                 _this._source = source;
+                if (scene.useClonedMeshhMap) {
+                    if (!source.meshMap) {
+                        source.meshMap = {};
+                    }
+                    source.meshMap[_this.uniqueId] = _this;
+                }
                 // Construction Params
                 // Clone parameters allowing mesh to be updated in case of parametric shapes.
                 _this._originalBuilderSideOrientation = source._originalBuilderSideOrientation;
@@ -34148,7 +34202,6 @@ var BABYLON;
          * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
          */
         Mesh.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
-            var _this = this;
             if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
             this.morphTargetManager = null;
             if (this._geometry) {
@@ -34164,13 +34217,30 @@ var BABYLON;
                 this._onAfterRenderObservable.clear();
             }
             // Sources
-            var meshes = this.getScene().meshes;
-            meshes.forEach(function (abstractMesh) {
-                var mesh = abstractMesh;
-                if (mesh._source && mesh._source === _this) {
-                    mesh._source = null;
+            if (this._scene.useClonedMeshhMap) {
+                if (this.meshMap) {
+                    for (var uniqueId in this.meshMap) {
+                        var mesh = this.meshMap[uniqueId];
+                        if (mesh) {
+                            mesh._source = null;
+                            this.meshMap[uniqueId] = undefined;
+                        }
+                    }
                 }
-            });
+                if (this._source && this._source.meshMap) {
+                    this._source.meshMap[this.uniqueId] = undefined;
+                }
+            }
+            else {
+                var meshes = this.getScene().meshes;
+                for (var _i = 0, meshes_1 = meshes; _i < meshes_1.length; _i++) {
+                    var abstractMesh = meshes_1[_i];
+                    var mesh = abstractMesh;
+                    if (mesh._source && mesh._source === this) {
+                        mesh._source = null;
+                    }
+                }
+            }
             this._source = null;
             // Instances
             if (this._instanceDataStorage.instancesBuffer) {
@@ -35705,6 +35775,25 @@ var BABYLON;
             }
             return meshSubclass;
         };
+        /** @hidden */
+        Mesh.prototype.addInstance = function (instance) {
+            instance._indexInSourceMeshInstanceArray = this.instances.length;
+            this.instances.push(instance);
+        };
+        /** @hidden */
+        Mesh.prototype.removeInstance = function (instance) {
+            // Remove from mesh
+            var index = instance._indexInSourceMeshInstanceArray;
+            if (index != -1) {
+                if (index !== this.instances.length - 1) {
+                    var last = this.instances[this.instances.length - 1];
+                    this.instances[index] = last;
+                    last._indexInSourceMeshInstanceArray = index;
+                }
+                instance._indexInSourceMeshInstanceArray = -1;
+                this.instances.pop();
+            }
+        };
         // Consts
         /**
          * Mesh side orientation : usually the external or front surface
@@ -36463,6 +36552,8 @@ var BABYLON;
              * Stores the fill mode state
              */
             this._fillMode = Material.TriangleFillMode;
+            /** @hidden */
+            this._indexInSceneMaterialArray = -1;
             this.name = name;
             this.id = name || BABYLON.Tools.RandomId();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
@@ -36476,8 +36567,10 @@ var BABYLON;
             this._uniformBuffer = new BABYLON.UniformBuffer(this._scene.getEngine());
             this._useUBO = this.getScene().getEngine().supportsUniformBuffers;
             if (!doNotAdd) {
-                this._scene.materials.push(this);
-                this._scene.onNewMaterialAddedObservable.notifyObservers(this);
+                this._scene.addMaterial(this);
+            }
+            if (scene.useMaterialMeshMap) {
+                this.meshMap = {};
             }
         }
         Object.defineProperty(Material, "TriangleFillMode", {
@@ -37072,14 +37165,21 @@ var BABYLON;
          * @returns an array of meshes bound to the material
          */
         Material.prototype.getBindedMeshes = function () {
-            var result = new Array();
-            for (var index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-                if (mesh.material === this) {
-                    result.push(mesh);
+            var _this = this;
+            if (this.meshMap) {
+                var result = new Array();
+                for (var meshId in this.meshMap) {
+                    var mesh = this.meshMap[meshId];
+                    if (mesh) {
+                        result.push(mesh);
+                    }
                 }
+                return result;
+            }
+            else {
+                var meshes = this._scene.meshes;
+                return meshes.filter(function (mesh) { return mesh.material === _this; });
             }
-            return result;
         };
         /**
          * Force shader compilation
@@ -37245,35 +37345,33 @@ var BABYLON;
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
-        Material.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures) {
+        Material.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures, notBoundToMesh) {
+            var scene = this.getScene();
             // Animations
-            this.getScene().stopAnimation(this);
-            this.getScene().freeProcessedMaterials();
+            scene.stopAnimation(this);
+            scene.freeProcessedMaterials();
             // Remove from scene
-            var index = this._scene.materials.indexOf(this);
-            if (index >= 0) {
-                this._scene.materials.splice(index, 1);
-            }
-            this._scene.onMaterialRemovedObservable.notifyObservers(this);
-            // Remove from meshes
-            for (index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-                if (mesh.material === this) {
-                    mesh.material = null;
-                    if (mesh.geometry) {
-                        var geometry = (mesh.geometry);
-                        if (this._storeEffectOnSubMeshes) {
-                            for (var _i = 0, _a = mesh.subMeshes; _i < _a.length; _i++) {
-                                var subMesh = _a[_i];
-                                geometry._releaseVertexArrayObject(subMesh._materialEffect);
-                                if (forceDisposeEffect && subMesh._materialEffect) {
-                                    this._scene.getEngine()._releaseEffect(subMesh._materialEffect);
-                                }
-                            }
+            scene.removeMaterial(this);
+            if (notBoundToMesh !== true) {
+                // Remove from meshes
+                if (this.meshMap) {
+                    for (var meshId in this.meshMap) {
+                        var mesh = this.meshMap[meshId];
+                        if (mesh) {
+                            mesh.material = null; // will set the entry in the map to undefined
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
                         }
-                        else {
-                            geometry._releaseVertexArrayObject(this._effect);
+                    }
+                }
+                else {
+                    var meshes = scene.meshes;
+                    for (var _i = 0, meshes_1 = meshes; _i < meshes_1.length; _i++) {
+                        var mesh = meshes_1[_i];
+                        if (mesh.material === this) {
+                            mesh.material = null;
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
                         }
                     }
                 }
@@ -37282,7 +37380,7 @@ var BABYLON;
             // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
             if (forceDisposeEffect && this._effect) {
                 if (!this._storeEffectOnSubMeshes) {
-                    this._scene.getEngine()._releaseEffect(this._effect);
+                    scene.getEngine()._releaseEffect(this._effect);
                 }
                 this._effect = null;
             }
@@ -37296,6 +37394,25 @@ var BABYLON;
                 this._onUnBindObservable.clear();
             }
         };
+        /** @hidden */
+        Material.prototype.releaseVertexArrayObject = function (mesh, forceDisposeEffect) {
+            if (mesh.geometry) {
+                var geometry = (mesh.geometry);
+                var scene = this.getScene();
+                if (this._storeEffectOnSubMeshes) {
+                    for (var _i = 0, _a = mesh.subMeshes; _i < _a.length; _i++) {
+                        var subMesh = _a[_i];
+                        geometry._releaseVertexArrayObject(subMesh._materialEffect);
+                        if (forceDisposeEffect && subMesh._materialEffect) {
+                            scene.getEngine()._releaseEffect(subMesh._materialEffect);
+                        }
+                    }
+                }
+                else {
+                    geometry._releaseVertexArrayObject(this._effect);
+                }
+            }
+        };
         /**
          * Serializes this material
          * @returns the serialized material object
@@ -37928,9 +38045,11 @@ var BABYLON;
             if (this._noUBO) {
                 return;
             }
-            var index = this._engine._uniformBuffers.indexOf(this);
+            var uniformBuffers = this._engine._uniformBuffers;
+            var index = uniformBuffers.indexOf(this);
             if (index !== -1) {
-                this._engine._uniformBuffers.splice(index, 1);
+                uniformBuffers[index] = uniformBuffers[uniformBuffers.length - 1];
+                uniformBuffers.pop();
             }
             if (!this._buffer) {
                 return;
@@ -64455,8 +64574,9 @@ var BABYLON;
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
-        ShaderMaterial.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures) {
+        ShaderMaterial.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures, notBoundToMesh) {
             if (forceDisposeTextures) {
                 var name;
                 for (name in this._textures) {
@@ -64470,7 +64590,7 @@ var BABYLON;
                 }
             }
             this._textures = {};
-            _super.prototype.dispose.call(this, forceDisposeEffect, forceDisposeTextures);
+            _super.prototype.dispose.call(this, forceDisposeEffect, forceDisposeTextures, notBoundToMesh);
         };
         /**
          * Serializes this material in a JSON representation
@@ -64958,7 +65078,9 @@ var BABYLON;
         __extends(InstancedMesh, _super);
         function InstancedMesh(name, source) {
             var _this = _super.call(this, name, source.getScene()) || this;
-            source.instances.push(_this);
+            /** @hidden */
+            _this._indexInSourceMeshInstanceArray = -1;
+            source.addInstance(_this);
             _this._sourceMesh = source;
             _this.position.copyFrom(source.position);
             _this.rotation.copyFrom(source.rotation);
@@ -65253,8 +65375,7 @@ var BABYLON;
         InstancedMesh.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
             if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
             // Remove from mesh
-            var index = this._sourceMesh.instances.indexOf(this);
-            this._sourceMesh.instances.splice(index, 1);
+            this._sourceMesh.removeInstance(this);
             _super.prototype.dispose.call(this, doNotRecurse, disposeMaterialAndTextures);
         };
         return InstancedMesh;
@@ -65420,7 +65541,7 @@ var BABYLON;
          * @param doNotRecurse If children should be disposed
          */
         LinesMesh.prototype.dispose = function (doNotRecurse) {
-            this._colorShader.dispose();
+            this._colorShader.dispose(false, false, true);
             _super.prototype.dispose.call(this, doNotRecurse);
         };
         /**

+ 181 - 60
dist/preview release/babylon.no-module.max.js

@@ -12661,7 +12661,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.0";
+                return "4.0.0-alpha.1";
             },
             enumerable: true,
             configurable: true
@@ -21167,7 +21167,14 @@ var BABYLON;
                 if (this._material === value) {
                     return;
                 }
+                // remove from material mesh map id needed
+                if (this._material && this._material.meshMap) {
+                    this._material.meshMap[this.uniqueId] = undefined;
+                }
                 this._material = value;
+                if (value && value.meshMap) {
+                    value.meshMap[this.uniqueId] = this;
+                }
                 if (this.onMaterialChangedObservable.hasObservers) {
                     this.onMaterialChangedObservable.notifyObservers(this);
                 }
@@ -25161,6 +25168,9 @@ var BABYLON;
             this.multiMaterials = new Array();
             /**
             * All of the materials added to this scene
+            * In the context of a Scene, it is not supposed to be modified manually.
+            * Any addition or removal should be done using the addMaterial and removeMAterial Scene methods.
+            * Note also that the order of the Material wihin the array is not significant and might change.
             * @see http://doc.babylonjs.com/babylon101/materials
             */
             this.materials = new Array();
@@ -25175,7 +25185,7 @@ var BABYLON;
             this.geometries = new Array();
             /**
             * All of the tranform nodes added to this scene
-            * In the context a the Scene, it is not supposed to be modified manually.
+            * In the context of a Scene, it is not supposed to be modified manually.
             * Any addition or removal should be done using the addTransformNode and removeTransformNode Scene methods.
             * Note also that the order of the TransformNode wihin the array is not significant and might change.
             * @see http://doc.babylonjs.com/how_to/transformnode
@@ -25860,6 +25870,7 @@ var BABYLON;
                 data: [],
                 length: 0
             };
+            _this._preventFreeActiveMeshesAndRenderingGroups = false;
             _this._activeMeshesFrozen = false;
             /** @hidden */
             _this._allowPostProcessClearColor = true;
@@ -25893,6 +25904,8 @@ var BABYLON;
             if (options && options.useGeometryIdsMap === true) {
                 _this.geometriesById = {};
             }
+            _this.useMaterialMeshMap = options && options.useGeometryIdsMap || false;
+            _this.useClonedMeshhMap = options && options.useClonedMeshhMap || false;
             return _this;
         }
         Object.defineProperty(Scene.prototype, "environmentTexture", {
@@ -26833,7 +26846,7 @@ var BABYLON;
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
             }
-            if (this.onPointerUp) {
+            if (this.onPointerUp && !clickInfo.ignore) {
                 this.onPointerUp(evt, pickResult, type);
             }
             return this;
@@ -26918,7 +26931,6 @@ var BABYLON;
                             if (Date.now() - _this._previousStartingPointerTime > Scene.DoubleClickDelay ||
                                 btn !== _this._previousButtonPressed) {
                                 clickInfo.singleClick = true;
-                                eventRaised = true;
                                 cb(clickInfo, _this._currentPickResult);
                             }
                         }
@@ -26984,10 +26996,8 @@ var BABYLON;
                         }
                     }
                 }
-                if (!eventRaised) {
-                    clickInfo.ignore = true;
-                    cb(clickInfo, _this._currentPickResult);
-                }
+                clickInfo.ignore = true;
+                cb(clickInfo, _this._currentPickResult);
             };
             this._onPointerMove = function (evt) {
                 _this._updatePointerPosition(evt);
@@ -27880,7 +27890,8 @@ var BABYLON;
             var index = this.meshes.indexOf(toRemove);
             if (index !== -1) {
                 // Remove from the scene if mesh found
-                this.meshes.splice(index, 1);
+                this.meshes[index] = this.meshes[this.meshes.length - 1];
+                this.meshes.pop();
             }
             this.onMeshRemovedObservable.notifyObservers(toRemove);
             if (recursive) {
@@ -28047,9 +28058,15 @@ var BABYLON;
          * @returns The index of the removed material
          */
         Scene.prototype.removeMaterial = function (toRemove) {
-            var index = this.materials.indexOf(toRemove);
+            var index = toRemove._indexInSceneMaterialArray;
             if (index !== -1) {
-                this.materials.splice(index, 1);
+                if (index !== this.materials.length - 1) {
+                    var lastMaterial = this.materials[this.materials.length - 1];
+                    this.materials[index] = lastMaterial;
+                    lastMaterial._indexInSceneMaterialArray = index;
+                }
+                toRemove._indexInSceneMaterialArray = -1;
+                this.materials.pop();
             }
             this.onMaterialRemovedObservable.notifyObservers(toRemove);
             return index;
@@ -28152,6 +28169,7 @@ var BABYLON;
          * @param newMaterial The material to add
          */
         Scene.prototype.addMaterial = function (newMaterial) {
+            newMaterial._indexInSceneMaterialArray = this.materials.length;
             this.materials.push(newMaterial);
             this.onNewMaterialAddedObservable.notifyObservers(newMaterial);
         };
@@ -28789,10 +28807,35 @@ var BABYLON;
         Scene.prototype.freeProcessedMaterials = function () {
             this._processedMaterials.dispose();
         };
+        Object.defineProperty(Scene.prototype, "blockfreeActiveMeshesAndRenderingGroups", {
+            /** Gets or sets a boolean blocking all the calls to freeActiveMeshes and freeRenderingGroups
+             * It can be used in order to prevent going through methods freeRenderingGroups and freeActiveMeshes several times to improve performance
+             * when disposing several meshes in a row or a hierarchy of meshes.
+             * When used, it is the responsability of the user to blockfreeActiveMeshesAndRenderingGroups back to false.
+             */
+            get: function () {
+                return this._preventFreeActiveMeshesAndRenderingGroups;
+            },
+            set: function (value) {
+                if (this._preventFreeActiveMeshesAndRenderingGroups === value) {
+                    return;
+                }
+                if (value) {
+                    this.freeActiveMeshes();
+                    this.freeRenderingGroups();
+                }
+                this._preventFreeActiveMeshesAndRenderingGroups = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Clear the active meshes smart array preventing retention point in mesh dispose.
          */
         Scene.prototype.freeActiveMeshes = function () {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
             this._activeMeshes.dispose();
             if (this.activeCamera && this.activeCamera._activeMeshes) {
                 this.activeCamera._activeMeshes.dispose();
@@ -28810,6 +28853,9 @@ var BABYLON;
          * Clear the info related to rendering groups preventing retention points during dispose.
          */
         Scene.prototype.freeRenderingGroups = function () {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
             if (this._renderingManager) {
                 this._renderingManager.freeRenderingGroups();
             }
@@ -32446,6 +32492,8 @@ var BABYLON;
             _this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NONE;
             /**
              * Gets the list of instances created from this mesh
+             * it is not supposed to be modified manually.
+             * Note also that the order of the InstancedMesh wihin the array is not significant and might change.
              * @see http://doc.babylonjs.com/how_to/how_to_use_instances
              */
             _this.instances = new Array();
@@ -32475,6 +32523,12 @@ var BABYLON;
                 ], ["_poseMatrix"]);
                 // Source mesh
                 _this._source = source;
+                if (scene.useClonedMeshhMap) {
+                    if (!source.meshMap) {
+                        source.meshMap = {};
+                    }
+                    source.meshMap[_this.uniqueId] = _this;
+                }
                 // Construction Params
                 // Clone parameters allowing mesh to be updated in case of parametric shapes.
                 _this._originalBuilderSideOrientation = source._originalBuilderSideOrientation;
@@ -34115,7 +34169,6 @@ var BABYLON;
          * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
          */
         Mesh.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
-            var _this = this;
             if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
             this.morphTargetManager = null;
             if (this._geometry) {
@@ -34131,13 +34184,30 @@ var BABYLON;
                 this._onAfterRenderObservable.clear();
             }
             // Sources
-            var meshes = this.getScene().meshes;
-            meshes.forEach(function (abstractMesh) {
-                var mesh = abstractMesh;
-                if (mesh._source && mesh._source === _this) {
-                    mesh._source = null;
+            if (this._scene.useClonedMeshhMap) {
+                if (this.meshMap) {
+                    for (var uniqueId in this.meshMap) {
+                        var mesh = this.meshMap[uniqueId];
+                        if (mesh) {
+                            mesh._source = null;
+                            this.meshMap[uniqueId] = undefined;
+                        }
+                    }
                 }
-            });
+                if (this._source && this._source.meshMap) {
+                    this._source.meshMap[this.uniqueId] = undefined;
+                }
+            }
+            else {
+                var meshes = this.getScene().meshes;
+                for (var _i = 0, meshes_1 = meshes; _i < meshes_1.length; _i++) {
+                    var abstractMesh = meshes_1[_i];
+                    var mesh = abstractMesh;
+                    if (mesh._source && mesh._source === this) {
+                        mesh._source = null;
+                    }
+                }
+            }
             this._source = null;
             // Instances
             if (this._instanceDataStorage.instancesBuffer) {
@@ -35672,6 +35742,25 @@ var BABYLON;
             }
             return meshSubclass;
         };
+        /** @hidden */
+        Mesh.prototype.addInstance = function (instance) {
+            instance._indexInSourceMeshInstanceArray = this.instances.length;
+            this.instances.push(instance);
+        };
+        /** @hidden */
+        Mesh.prototype.removeInstance = function (instance) {
+            // Remove from mesh
+            var index = instance._indexInSourceMeshInstanceArray;
+            if (index != -1) {
+                if (index !== this.instances.length - 1) {
+                    var last = this.instances[this.instances.length - 1];
+                    this.instances[index] = last;
+                    last._indexInSourceMeshInstanceArray = index;
+                }
+                instance._indexInSourceMeshInstanceArray = -1;
+                this.instances.pop();
+            }
+        };
         // Consts
         /**
          * Mesh side orientation : usually the external or front surface
@@ -36430,6 +36519,8 @@ var BABYLON;
              * Stores the fill mode state
              */
             this._fillMode = Material.TriangleFillMode;
+            /** @hidden */
+            this._indexInSceneMaterialArray = -1;
             this.name = name;
             this.id = name || BABYLON.Tools.RandomId();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
@@ -36443,8 +36534,10 @@ var BABYLON;
             this._uniformBuffer = new BABYLON.UniformBuffer(this._scene.getEngine());
             this._useUBO = this.getScene().getEngine().supportsUniformBuffers;
             if (!doNotAdd) {
-                this._scene.materials.push(this);
-                this._scene.onNewMaterialAddedObservable.notifyObservers(this);
+                this._scene.addMaterial(this);
+            }
+            if (scene.useMaterialMeshMap) {
+                this.meshMap = {};
             }
         }
         Object.defineProperty(Material, "TriangleFillMode", {
@@ -37039,14 +37132,21 @@ var BABYLON;
          * @returns an array of meshes bound to the material
          */
         Material.prototype.getBindedMeshes = function () {
-            var result = new Array();
-            for (var index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-                if (mesh.material === this) {
-                    result.push(mesh);
+            var _this = this;
+            if (this.meshMap) {
+                var result = new Array();
+                for (var meshId in this.meshMap) {
+                    var mesh = this.meshMap[meshId];
+                    if (mesh) {
+                        result.push(mesh);
+                    }
                 }
+                return result;
+            }
+            else {
+                var meshes = this._scene.meshes;
+                return meshes.filter(function (mesh) { return mesh.material === _this; });
             }
-            return result;
         };
         /**
          * Force shader compilation
@@ -37212,35 +37312,33 @@ var BABYLON;
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
-        Material.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures) {
+        Material.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures, notBoundToMesh) {
+            var scene = this.getScene();
             // Animations
-            this.getScene().stopAnimation(this);
-            this.getScene().freeProcessedMaterials();
+            scene.stopAnimation(this);
+            scene.freeProcessedMaterials();
             // Remove from scene
-            var index = this._scene.materials.indexOf(this);
-            if (index >= 0) {
-                this._scene.materials.splice(index, 1);
-            }
-            this._scene.onMaterialRemovedObservable.notifyObservers(this);
-            // Remove from meshes
-            for (index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-                if (mesh.material === this) {
-                    mesh.material = null;
-                    if (mesh.geometry) {
-                        var geometry = (mesh.geometry);
-                        if (this._storeEffectOnSubMeshes) {
-                            for (var _i = 0, _a = mesh.subMeshes; _i < _a.length; _i++) {
-                                var subMesh = _a[_i];
-                                geometry._releaseVertexArrayObject(subMesh._materialEffect);
-                                if (forceDisposeEffect && subMesh._materialEffect) {
-                                    this._scene.getEngine()._releaseEffect(subMesh._materialEffect);
-                                }
-                            }
+            scene.removeMaterial(this);
+            if (notBoundToMesh !== true) {
+                // Remove from meshes
+                if (this.meshMap) {
+                    for (var meshId in this.meshMap) {
+                        var mesh = this.meshMap[meshId];
+                        if (mesh) {
+                            mesh.material = null; // will set the entry in the map to undefined
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
                         }
-                        else {
-                            geometry._releaseVertexArrayObject(this._effect);
+                    }
+                }
+                else {
+                    var meshes = scene.meshes;
+                    for (var _i = 0, meshes_1 = meshes; _i < meshes_1.length; _i++) {
+                        var mesh = meshes_1[_i];
+                        if (mesh.material === this) {
+                            mesh.material = null;
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
                         }
                     }
                 }
@@ -37249,7 +37347,7 @@ var BABYLON;
             // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
             if (forceDisposeEffect && this._effect) {
                 if (!this._storeEffectOnSubMeshes) {
-                    this._scene.getEngine()._releaseEffect(this._effect);
+                    scene.getEngine()._releaseEffect(this._effect);
                 }
                 this._effect = null;
             }
@@ -37263,6 +37361,25 @@ var BABYLON;
                 this._onUnBindObservable.clear();
             }
         };
+        /** @hidden */
+        Material.prototype.releaseVertexArrayObject = function (mesh, forceDisposeEffect) {
+            if (mesh.geometry) {
+                var geometry = (mesh.geometry);
+                var scene = this.getScene();
+                if (this._storeEffectOnSubMeshes) {
+                    for (var _i = 0, _a = mesh.subMeshes; _i < _a.length; _i++) {
+                        var subMesh = _a[_i];
+                        geometry._releaseVertexArrayObject(subMesh._materialEffect);
+                        if (forceDisposeEffect && subMesh._materialEffect) {
+                            scene.getEngine()._releaseEffect(subMesh._materialEffect);
+                        }
+                    }
+                }
+                else {
+                    geometry._releaseVertexArrayObject(this._effect);
+                }
+            }
+        };
         /**
          * Serializes this material
          * @returns the serialized material object
@@ -37895,9 +38012,11 @@ var BABYLON;
             if (this._noUBO) {
                 return;
             }
-            var index = this._engine._uniformBuffers.indexOf(this);
+            var uniformBuffers = this._engine._uniformBuffers;
+            var index = uniformBuffers.indexOf(this);
             if (index !== -1) {
-                this._engine._uniformBuffers.splice(index, 1);
+                uniformBuffers[index] = uniformBuffers[uniformBuffers.length - 1];
+                uniformBuffers.pop();
             }
             if (!this._buffer) {
                 return;
@@ -64422,8 +64541,9 @@ var BABYLON;
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
-        ShaderMaterial.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures) {
+        ShaderMaterial.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures, notBoundToMesh) {
             if (forceDisposeTextures) {
                 var name;
                 for (name in this._textures) {
@@ -64437,7 +64557,7 @@ var BABYLON;
                 }
             }
             this._textures = {};
-            _super.prototype.dispose.call(this, forceDisposeEffect, forceDisposeTextures);
+            _super.prototype.dispose.call(this, forceDisposeEffect, forceDisposeTextures, notBoundToMesh);
         };
         /**
          * Serializes this material in a JSON representation
@@ -64925,7 +65045,9 @@ var BABYLON;
         __extends(InstancedMesh, _super);
         function InstancedMesh(name, source) {
             var _this = _super.call(this, name, source.getScene()) || this;
-            source.instances.push(_this);
+            /** @hidden */
+            _this._indexInSourceMeshInstanceArray = -1;
+            source.addInstance(_this);
             _this._sourceMesh = source;
             _this.position.copyFrom(source.position);
             _this.rotation.copyFrom(source.rotation);
@@ -65220,8 +65342,7 @@ var BABYLON;
         InstancedMesh.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
             if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
             // Remove from mesh
-            var index = this._sourceMesh.instances.indexOf(this);
-            this._sourceMesh.instances.splice(index, 1);
+            this._sourceMesh.removeInstance(this);
             _super.prototype.dispose.call(this, doNotRecurse, disposeMaterialAndTextures);
         };
         return InstancedMesh;
@@ -65387,7 +65508,7 @@ var BABYLON;
          * @param doNotRecurse If children should be disposed
          */
         LinesMesh.prototype.dispose = function (doNotRecurse) {
-            this._colorShader.dispose();
+            this._colorShader.dispose(false, false, true);
             _super.prototype.dispose.call(this, doNotRecurse);
         };
         /**

ファイルの差分が大きいため隠しています
+ 1 - 1
dist/preview release/babylon.worker.js


+ 181 - 60
dist/preview release/es6.js

@@ -12661,7 +12661,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.0";
+                return "4.0.0-alpha.1";
             },
             enumerable: true,
             configurable: true
@@ -21167,7 +21167,14 @@ var BABYLON;
                 if (this._material === value) {
                     return;
                 }
+                // remove from material mesh map id needed
+                if (this._material && this._material.meshMap) {
+                    this._material.meshMap[this.uniqueId] = undefined;
+                }
                 this._material = value;
+                if (value && value.meshMap) {
+                    value.meshMap[this.uniqueId] = this;
+                }
                 if (this.onMaterialChangedObservable.hasObservers) {
                     this.onMaterialChangedObservable.notifyObservers(this);
                 }
@@ -25161,6 +25168,9 @@ var BABYLON;
             this.multiMaterials = new Array();
             /**
             * All of the materials added to this scene
+            * In the context of a Scene, it is not supposed to be modified manually.
+            * Any addition or removal should be done using the addMaterial and removeMAterial Scene methods.
+            * Note also that the order of the Material wihin the array is not significant and might change.
             * @see http://doc.babylonjs.com/babylon101/materials
             */
             this.materials = new Array();
@@ -25175,7 +25185,7 @@ var BABYLON;
             this.geometries = new Array();
             /**
             * All of the tranform nodes added to this scene
-            * In the context a the Scene, it is not supposed to be modified manually.
+            * In the context of a Scene, it is not supposed to be modified manually.
             * Any addition or removal should be done using the addTransformNode and removeTransformNode Scene methods.
             * Note also that the order of the TransformNode wihin the array is not significant and might change.
             * @see http://doc.babylonjs.com/how_to/transformnode
@@ -25860,6 +25870,7 @@ var BABYLON;
                 data: [],
                 length: 0
             };
+            _this._preventFreeActiveMeshesAndRenderingGroups = false;
             _this._activeMeshesFrozen = false;
             /** @hidden */
             _this._allowPostProcessClearColor = true;
@@ -25893,6 +25904,8 @@ var BABYLON;
             if (options && options.useGeometryIdsMap === true) {
                 _this.geometriesById = {};
             }
+            _this.useMaterialMeshMap = options && options.useGeometryIdsMap || false;
+            _this.useClonedMeshhMap = options && options.useClonedMeshhMap || false;
             return _this;
         }
         Object.defineProperty(Scene.prototype, "environmentTexture", {
@@ -26833,7 +26846,7 @@ var BABYLON;
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
             }
-            if (this.onPointerUp) {
+            if (this.onPointerUp && !clickInfo.ignore) {
                 this.onPointerUp(evt, pickResult, type);
             }
             return this;
@@ -26918,7 +26931,6 @@ var BABYLON;
                             if (Date.now() - _this._previousStartingPointerTime > Scene.DoubleClickDelay ||
                                 btn !== _this._previousButtonPressed) {
                                 clickInfo.singleClick = true;
-                                eventRaised = true;
                                 cb(clickInfo, _this._currentPickResult);
                             }
                         }
@@ -26984,10 +26996,8 @@ var BABYLON;
                         }
                     }
                 }
-                if (!eventRaised) {
-                    clickInfo.ignore = true;
-                    cb(clickInfo, _this._currentPickResult);
-                }
+                clickInfo.ignore = true;
+                cb(clickInfo, _this._currentPickResult);
             };
             this._onPointerMove = function (evt) {
                 _this._updatePointerPosition(evt);
@@ -27880,7 +27890,8 @@ var BABYLON;
             var index = this.meshes.indexOf(toRemove);
             if (index !== -1) {
                 // Remove from the scene if mesh found
-                this.meshes.splice(index, 1);
+                this.meshes[index] = this.meshes[this.meshes.length - 1];
+                this.meshes.pop();
             }
             this.onMeshRemovedObservable.notifyObservers(toRemove);
             if (recursive) {
@@ -28047,9 +28058,15 @@ var BABYLON;
          * @returns The index of the removed material
          */
         Scene.prototype.removeMaterial = function (toRemove) {
-            var index = this.materials.indexOf(toRemove);
+            var index = toRemove._indexInSceneMaterialArray;
             if (index !== -1) {
-                this.materials.splice(index, 1);
+                if (index !== this.materials.length - 1) {
+                    var lastMaterial = this.materials[this.materials.length - 1];
+                    this.materials[index] = lastMaterial;
+                    lastMaterial._indexInSceneMaterialArray = index;
+                }
+                toRemove._indexInSceneMaterialArray = -1;
+                this.materials.pop();
             }
             this.onMaterialRemovedObservable.notifyObservers(toRemove);
             return index;
@@ -28152,6 +28169,7 @@ var BABYLON;
          * @param newMaterial The material to add
          */
         Scene.prototype.addMaterial = function (newMaterial) {
+            newMaterial._indexInSceneMaterialArray = this.materials.length;
             this.materials.push(newMaterial);
             this.onNewMaterialAddedObservable.notifyObservers(newMaterial);
         };
@@ -28789,10 +28807,35 @@ var BABYLON;
         Scene.prototype.freeProcessedMaterials = function () {
             this._processedMaterials.dispose();
         };
+        Object.defineProperty(Scene.prototype, "blockfreeActiveMeshesAndRenderingGroups", {
+            /** Gets or sets a boolean blocking all the calls to freeActiveMeshes and freeRenderingGroups
+             * It can be used in order to prevent going through methods freeRenderingGroups and freeActiveMeshes several times to improve performance
+             * when disposing several meshes in a row or a hierarchy of meshes.
+             * When used, it is the responsability of the user to blockfreeActiveMeshesAndRenderingGroups back to false.
+             */
+            get: function () {
+                return this._preventFreeActiveMeshesAndRenderingGroups;
+            },
+            set: function (value) {
+                if (this._preventFreeActiveMeshesAndRenderingGroups === value) {
+                    return;
+                }
+                if (value) {
+                    this.freeActiveMeshes();
+                    this.freeRenderingGroups();
+                }
+                this._preventFreeActiveMeshesAndRenderingGroups = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Clear the active meshes smart array preventing retention point in mesh dispose.
          */
         Scene.prototype.freeActiveMeshes = function () {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
             this._activeMeshes.dispose();
             if (this.activeCamera && this.activeCamera._activeMeshes) {
                 this.activeCamera._activeMeshes.dispose();
@@ -28810,6 +28853,9 @@ var BABYLON;
          * Clear the info related to rendering groups preventing retention points during dispose.
          */
         Scene.prototype.freeRenderingGroups = function () {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
             if (this._renderingManager) {
                 this._renderingManager.freeRenderingGroups();
             }
@@ -32446,6 +32492,8 @@ var BABYLON;
             _this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NONE;
             /**
              * Gets the list of instances created from this mesh
+             * it is not supposed to be modified manually.
+             * Note also that the order of the InstancedMesh wihin the array is not significant and might change.
              * @see http://doc.babylonjs.com/how_to/how_to_use_instances
              */
             _this.instances = new Array();
@@ -32475,6 +32523,12 @@ var BABYLON;
                 ], ["_poseMatrix"]);
                 // Source mesh
                 _this._source = source;
+                if (scene.useClonedMeshhMap) {
+                    if (!source.meshMap) {
+                        source.meshMap = {};
+                    }
+                    source.meshMap[_this.uniqueId] = _this;
+                }
                 // Construction Params
                 // Clone parameters allowing mesh to be updated in case of parametric shapes.
                 _this._originalBuilderSideOrientation = source._originalBuilderSideOrientation;
@@ -34115,7 +34169,6 @@ var BABYLON;
          * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
          */
         Mesh.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
-            var _this = this;
             if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
             this.morphTargetManager = null;
             if (this._geometry) {
@@ -34131,13 +34184,30 @@ var BABYLON;
                 this._onAfterRenderObservable.clear();
             }
             // Sources
-            var meshes = this.getScene().meshes;
-            meshes.forEach(function (abstractMesh) {
-                var mesh = abstractMesh;
-                if (mesh._source && mesh._source === _this) {
-                    mesh._source = null;
+            if (this._scene.useClonedMeshhMap) {
+                if (this.meshMap) {
+                    for (var uniqueId in this.meshMap) {
+                        var mesh = this.meshMap[uniqueId];
+                        if (mesh) {
+                            mesh._source = null;
+                            this.meshMap[uniqueId] = undefined;
+                        }
+                    }
                 }
-            });
+                if (this._source && this._source.meshMap) {
+                    this._source.meshMap[this.uniqueId] = undefined;
+                }
+            }
+            else {
+                var meshes = this.getScene().meshes;
+                for (var _i = 0, meshes_1 = meshes; _i < meshes_1.length; _i++) {
+                    var abstractMesh = meshes_1[_i];
+                    var mesh = abstractMesh;
+                    if (mesh._source && mesh._source === this) {
+                        mesh._source = null;
+                    }
+                }
+            }
             this._source = null;
             // Instances
             if (this._instanceDataStorage.instancesBuffer) {
@@ -35672,6 +35742,25 @@ var BABYLON;
             }
             return meshSubclass;
         };
+        /** @hidden */
+        Mesh.prototype.addInstance = function (instance) {
+            instance._indexInSourceMeshInstanceArray = this.instances.length;
+            this.instances.push(instance);
+        };
+        /** @hidden */
+        Mesh.prototype.removeInstance = function (instance) {
+            // Remove from mesh
+            var index = instance._indexInSourceMeshInstanceArray;
+            if (index != -1) {
+                if (index !== this.instances.length - 1) {
+                    var last = this.instances[this.instances.length - 1];
+                    this.instances[index] = last;
+                    last._indexInSourceMeshInstanceArray = index;
+                }
+                instance._indexInSourceMeshInstanceArray = -1;
+                this.instances.pop();
+            }
+        };
         // Consts
         /**
          * Mesh side orientation : usually the external or front surface
@@ -36430,6 +36519,8 @@ var BABYLON;
              * Stores the fill mode state
              */
             this._fillMode = Material.TriangleFillMode;
+            /** @hidden */
+            this._indexInSceneMaterialArray = -1;
             this.name = name;
             this.id = name || BABYLON.Tools.RandomId();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
@@ -36443,8 +36534,10 @@ var BABYLON;
             this._uniformBuffer = new BABYLON.UniformBuffer(this._scene.getEngine());
             this._useUBO = this.getScene().getEngine().supportsUniformBuffers;
             if (!doNotAdd) {
-                this._scene.materials.push(this);
-                this._scene.onNewMaterialAddedObservable.notifyObservers(this);
+                this._scene.addMaterial(this);
+            }
+            if (scene.useMaterialMeshMap) {
+                this.meshMap = {};
             }
         }
         Object.defineProperty(Material, "TriangleFillMode", {
@@ -37039,14 +37132,21 @@ var BABYLON;
          * @returns an array of meshes bound to the material
          */
         Material.prototype.getBindedMeshes = function () {
-            var result = new Array();
-            for (var index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-                if (mesh.material === this) {
-                    result.push(mesh);
+            var _this = this;
+            if (this.meshMap) {
+                var result = new Array();
+                for (var meshId in this.meshMap) {
+                    var mesh = this.meshMap[meshId];
+                    if (mesh) {
+                        result.push(mesh);
+                    }
                 }
+                return result;
+            }
+            else {
+                var meshes = this._scene.meshes;
+                return meshes.filter(function (mesh) { return mesh.material === _this; });
             }
-            return result;
         };
         /**
          * Force shader compilation
@@ -37212,35 +37312,33 @@ var BABYLON;
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
-        Material.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures) {
+        Material.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures, notBoundToMesh) {
+            var scene = this.getScene();
             // Animations
-            this.getScene().stopAnimation(this);
-            this.getScene().freeProcessedMaterials();
+            scene.stopAnimation(this);
+            scene.freeProcessedMaterials();
             // Remove from scene
-            var index = this._scene.materials.indexOf(this);
-            if (index >= 0) {
-                this._scene.materials.splice(index, 1);
-            }
-            this._scene.onMaterialRemovedObservable.notifyObservers(this);
-            // Remove from meshes
-            for (index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-                if (mesh.material === this) {
-                    mesh.material = null;
-                    if (mesh.geometry) {
-                        var geometry = (mesh.geometry);
-                        if (this._storeEffectOnSubMeshes) {
-                            for (var _i = 0, _a = mesh.subMeshes; _i < _a.length; _i++) {
-                                var subMesh = _a[_i];
-                                geometry._releaseVertexArrayObject(subMesh._materialEffect);
-                                if (forceDisposeEffect && subMesh._materialEffect) {
-                                    this._scene.getEngine()._releaseEffect(subMesh._materialEffect);
-                                }
-                            }
+            scene.removeMaterial(this);
+            if (notBoundToMesh !== true) {
+                // Remove from meshes
+                if (this.meshMap) {
+                    for (var meshId in this.meshMap) {
+                        var mesh = this.meshMap[meshId];
+                        if (mesh) {
+                            mesh.material = null; // will set the entry in the map to undefined
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
                         }
-                        else {
-                            geometry._releaseVertexArrayObject(this._effect);
+                    }
+                }
+                else {
+                    var meshes = scene.meshes;
+                    for (var _i = 0, meshes_1 = meshes; _i < meshes_1.length; _i++) {
+                        var mesh = meshes_1[_i];
+                        if (mesh.material === this) {
+                            mesh.material = null;
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
                         }
                     }
                 }
@@ -37249,7 +37347,7 @@ var BABYLON;
             // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
             if (forceDisposeEffect && this._effect) {
                 if (!this._storeEffectOnSubMeshes) {
-                    this._scene.getEngine()._releaseEffect(this._effect);
+                    scene.getEngine()._releaseEffect(this._effect);
                 }
                 this._effect = null;
             }
@@ -37263,6 +37361,25 @@ var BABYLON;
                 this._onUnBindObservable.clear();
             }
         };
+        /** @hidden */
+        Material.prototype.releaseVertexArrayObject = function (mesh, forceDisposeEffect) {
+            if (mesh.geometry) {
+                var geometry = (mesh.geometry);
+                var scene = this.getScene();
+                if (this._storeEffectOnSubMeshes) {
+                    for (var _i = 0, _a = mesh.subMeshes; _i < _a.length; _i++) {
+                        var subMesh = _a[_i];
+                        geometry._releaseVertexArrayObject(subMesh._materialEffect);
+                        if (forceDisposeEffect && subMesh._materialEffect) {
+                            scene.getEngine()._releaseEffect(subMesh._materialEffect);
+                        }
+                    }
+                }
+                else {
+                    geometry._releaseVertexArrayObject(this._effect);
+                }
+            }
+        };
         /**
          * Serializes this material
          * @returns the serialized material object
@@ -37895,9 +38012,11 @@ var BABYLON;
             if (this._noUBO) {
                 return;
             }
-            var index = this._engine._uniformBuffers.indexOf(this);
+            var uniformBuffers = this._engine._uniformBuffers;
+            var index = uniformBuffers.indexOf(this);
             if (index !== -1) {
-                this._engine._uniformBuffers.splice(index, 1);
+                uniformBuffers[index] = uniformBuffers[uniformBuffers.length - 1];
+                uniformBuffers.pop();
             }
             if (!this._buffer) {
                 return;
@@ -64422,8 +64541,9 @@ var BABYLON;
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
-        ShaderMaterial.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures) {
+        ShaderMaterial.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures, notBoundToMesh) {
             if (forceDisposeTextures) {
                 var name;
                 for (name in this._textures) {
@@ -64437,7 +64557,7 @@ var BABYLON;
                 }
             }
             this._textures = {};
-            _super.prototype.dispose.call(this, forceDisposeEffect, forceDisposeTextures);
+            _super.prototype.dispose.call(this, forceDisposeEffect, forceDisposeTextures, notBoundToMesh);
         };
         /**
          * Serializes this material in a JSON representation
@@ -64925,7 +65045,9 @@ var BABYLON;
         __extends(InstancedMesh, _super);
         function InstancedMesh(name, source) {
             var _this = _super.call(this, name, source.getScene()) || this;
-            source.instances.push(_this);
+            /** @hidden */
+            _this._indexInSourceMeshInstanceArray = -1;
+            source.addInstance(_this);
             _this._sourceMesh = source;
             _this.position.copyFrom(source.position);
             _this.rotation.copyFrom(source.rotation);
@@ -65220,8 +65342,7 @@ var BABYLON;
         InstancedMesh.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
             if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
             // Remove from mesh
-            var index = this._sourceMesh.instances.indexOf(this);
-            this._sourceMesh.instances.splice(index, 1);
+            this._sourceMesh.removeInstance(this);
             _super.prototype.dispose.call(this, doNotRecurse, disposeMaterialAndTextures);
         };
         return InstancedMesh;
@@ -65387,7 +65508,7 @@ var BABYLON;
          * @param doNotRecurse If children should be disposed
          */
         LinesMesh.prototype.dispose = function (doNotRecurse) {
-            this._colorShader.dispose();
+            this._colorShader.dispose(false, false, true);
             _super.prototype.dispose.call(this, doNotRecurse);
         };
         /**

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

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "4.0.0-alpha.0",
+    "version": "4.0.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -19,9 +19,6 @@
         "gltf2"
     ],
     "license": "Apache-2.0",
-    "peerDependencies": {
-        "babylonjs": ">=3.3.0-rc.4"
-    },
     "engines": {
         "node": "*"
     }

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

@@ -1617,9 +1617,15 @@ declare module BABYLON.GUI {
             name?: string | undefined;
             /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
             isVertical: boolean;
-            /** Gets or sets panel width */
+            /**
+                * Gets or sets panel width.
+                * This value should not be set when in horizontal mode as it will be computed automatically
+                */
             width: string | number;
-            /** Gets or sets panel height */
+            /**
+                * Gets or sets panel height.
+                * This value should not be set when in vertical mode as it will be computed automatically
+                */
             height: string | number;
             /**
                 * Creates a new StackPanel

ファイルの差分が大きいため隠しています
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js.map


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

@@ -1756,9 +1756,15 @@ declare module 'babylonjs-gui/2D/controls/stackPanel' {
             name?: string | undefined;
             /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
             isVertical: boolean;
-            /** Gets or sets panel width */
+            /**
+                * Gets or sets panel width.
+                * This value should not be set when in horizontal mode as it will be computed automatically
+                */
             width: string | number;
-            /** Gets or sets panel height */
+            /**
+                * Gets or sets panel height.
+                * This value should not be set when in vertical mode as it will be computed automatically
+                */
             height: string | number;
             /**
                 * Creates a new StackPanel
@@ -4446,9 +4452,15 @@ declare module BABYLON.GUI {
             name?: string | undefined;
             /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
             isVertical: boolean;
-            /** Gets or sets panel width */
+            /**
+                * Gets or sets panel width.
+                * This value should not be set when in horizontal mode as it will be computed automatically
+                */
             width: string | number;
-            /** Gets or sets panel height */
+            /**
+                * Gets or sets panel height.
+                * This value should not be set when in vertical mode as it will be computed automatically
+                */
             height: string | number;
             /**
                 * Creates a new StackPanel

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

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

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.0.0-alpha.0",
+    "version": "4.0.0-alpha.1",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,10 +28,10 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.0",
-        "babylonjs-gui": "4.0.0-alpha.0",
-        "babylonjs-loaders": "4.0.0-alpha.0",
-        "babylonjs-serializers": "4.0.0-alpha.0"
+        "babylonjs": "4.0.0-alpha.1",
+        "babylonjs-gui": "4.0.0-alpha.1",
+        "babylonjs-loaders": "4.0.0-alpha.1",
+        "babylonjs-serializers": "4.0.0-alpha.1"
     },
     "engines": {
         "node": "*"

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

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

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

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

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

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

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

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

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

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

ファイルの差分が大きいため隠しています
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


ファイルの差分が大きいため隠しています
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -20,9 +20,15 @@
 - Added utility function `Tools.BuildArray` for array initialisation ([barroij](https://github.com/barroij))
 - Introduced a new `IOfflineSupport` interface to hide IndexedDB ([Deltakosh](https://github.com/deltakosh))
 - `PBRMaterial` and `StandardMaterial` now use hot swapping feature for shaders. This means they can keep using a previous shader while a new one is being compiled ([Deltakosh](https://github.com/deltakosh))
-- Performance oriented updates ([barroij](https://github.com/barroij))
-  - Prevent avoidable matrix inversion or square root computation
-  - Enable a removal in O(1) from the `transformNodes`array of the Scene
+- Performance oriented changes ([barroij](https://github.com/barroij))
+  - Prevented avoidable matrix inversion or square root computation.
+  - Enabled a removal in O(1) from the `transformNodes` array and `materials` array of the `Scene`. As a consequence, the order of the element within these arrays might change during a removal.
+  - Enabled a removal in O(1) from the `instances` array of a `Mesh`. As a consequence, the order of the element within this array might change during a removal.
+  - Stopped calling `Array.splice` on the `scene.meshes` array and on the `engine._uniformBuffer` when removing an element. As a consequence, the order of the element within these arrays might change during a removal.
+  - Added an option `useMaterialMeshMap` in the `Scene` constructor options. When set to true, each `Material` isntance will have and will keep up-to-date a map of its bound meshes. This is to avoid browsing all the meshes of the scene to retrieve the ones bound to the current material when disposing the Material. Disabled by default.
+  - Added an option `useClonedMeshhMap` in the `Scene` constructor options. When set to true, each `Mesh` will have and will keep up-to-date a map of cloned meshes. This is to avoid browsing all the meshes of the scene to retrieve the ones that have the current mesh as source mesh. Disabled by default.
+  - Added `blockfreeActiveMeshesAndRenderingGroups` property in the `Scene`, following the same model as `blockMaterialDirtyMechanism`. This is to avoid calling `Scene.freeActiveMeshes` and `Scene.freeRenderingGroups` for each disposed mesh when we dispose several meshes in a row. One have to set `blockfreeActiveMeshesAndRenderingGroups` to `true` just before disposing the meshes, and set it back to `false` just after.
+  - Prevented code from doing useless and possible time consuming computation when disposing the `ShaderMaterial` of a `LinesMesh`.
 
 ### glTF Loader
 
@@ -37,7 +43,7 @@
 ### Core Engine
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))
 - Fixed a bug with pointer up being fire twice ([Deltakosh](https://github.com/deltakosh))
-- Fixed a bug with particle systems being update once per camera instead of once per frame ([Deltakosh](https://github.com/deltakosh)) 
+- Fixed a bug with particle systems being update once per camera instead of once per frame ([Deltakosh](https://github.com/deltakosh))
 
 
 ### Viewer

+ 8 - 2
gui/src/2D/controls/stackPanel.ts

@@ -26,7 +26,10 @@ export class StackPanel extends Container {
         this._markAsDirty();
     }
 
-    /** Gets or sets panel width */
+    /**
+     * Gets or sets panel width.
+     * This value should not be set when in horizontal mode as it will be computed automatically
+     */
     public set width(value: string | number) {
         if (!this._doNotTrackManualChanges) {
             this._manualWidth = true;
@@ -45,7 +48,10 @@ export class StackPanel extends Container {
         return this._width.toString(this._host);
     }
 
-    /** Gets or sets panel height */
+    /**
+     * Gets or sets panel height.
+     * This value should not be set when in vertical mode as it will be computed automatically
+     */
     public set height(value: string | number) {
         if (!this._doNotTrackManualChanges) {
             this._manualHeight = true;

+ 1 - 1
package.json

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

+ 1 - 1
src/Engine/babylon.engine.ts

@@ -481,7 +481,7 @@ module BABYLON {
          * Returns the current version of the framework
          */
         public static get Version(): string {
-            return "4.0.0-alpha.0";
+            return "4.0.0-alpha.1";
         }
 
         // Updatable statics so stick with vars here

+ 66 - 40
src/Materials/babylon.material.ts

@@ -765,6 +765,12 @@ module BABYLON {
          */
         protected _uniformBuffer: UniformBuffer;
 
+        /** @hidden */
+        public _indexInSceneMaterialArray = -1;
+
+        /** @hidden */
+        public meshMap: Nullable<{[id: string]: AbstractMesh | undefined}>;
+
         /**
          * Creates a material instance
          * @param name defines the name of the material
@@ -788,8 +794,11 @@ module BABYLON {
             this._useUBO = this.getScene().getEngine().supportsUniformBuffers;
 
             if (!doNotAdd) {
-                this._scene.materials.push(this);
-                this._scene.onNewMaterialAddedObservable.notifyObservers(this);
+                this._scene.addMaterial(this);
+            }
+
+            if (this._scene.useMaterialMeshMap) {
+                this.meshMap = {};
             }
         }
 
@@ -1057,17 +1066,20 @@ module BABYLON {
          * @returns an array of meshes bound to the material
          */
         public getBindedMeshes(): AbstractMesh[] {
-            var result = new Array<AbstractMesh>();
-
-            for (var index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-
-                if (mesh.material === this) {
-                    result.push(mesh);
+            if (this.meshMap) {
+                var result = new Array<AbstractMesh>();
+                for (let meshId in this.meshMap) {
+                    const mesh = this.meshMap[meshId];
+                    if (mesh) {
+                        result.push(mesh);
+                    }
                 }
+                return result;
+            }
+            else {
+                const meshes = this._scene.meshes;
+                return meshes.filter((mesh) => mesh.material === this);
             }
-
-            return result;
         }
 
         /**
@@ -1259,38 +1271,34 @@ module BABYLON {
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
-        public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
+        public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean, notBoundToMesh?: boolean): void {
+            const scene = this.getScene();
             // Animations
-            this.getScene().stopAnimation(this);
-            this.getScene().freeProcessedMaterials();
+            scene.stopAnimation(this);
+            scene.freeProcessedMaterials();
 
             // Remove from scene
-            var index = this._scene.materials.indexOf(this);
-            if (index >= 0) {
-                this._scene.materials.splice(index, 1);
-            }
-            this._scene.onMaterialRemovedObservable.notifyObservers(this);
-
-            // Remove from meshes
-            for (index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-
-                if (mesh.material === this) {
-                    mesh.material = null;
-
-                    if ((<Mesh>mesh).geometry) {
-                        var geometry = <Geometry>((<Mesh>mesh).geometry);
-
-                        if (this._storeEffectOnSubMeshes) {
-                            for (var subMesh of mesh.subMeshes) {
-                                geometry._releaseVertexArrayObject(subMesh._materialEffect);
-                                if (forceDisposeEffect && subMesh._materialEffect) {
-                                    this._scene.getEngine()._releaseEffect(subMesh._materialEffect);
-                                }
-                            }
-                        } else {
-                            geometry._releaseVertexArrayObject(this._effect);
+            scene.removeMaterial(this);
+
+            if (notBoundToMesh !== true) {
+                // Remove from meshes
+                if (this.meshMap) {
+                    for (let meshId in this.meshMap) {
+                        const mesh = this.meshMap[meshId];
+                        if (mesh) {
+                            mesh.material = null; // will set the entry in the map to undefined
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
+                        }
+                    }
+                }
+                else {
+                    const meshes = scene.meshes;
+                    for (let mesh of meshes) {
+                        if (mesh.material === this) {
+                            mesh.material = null;
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
                         }
                     }
                 }
@@ -1301,7 +1309,7 @@ module BABYLON {
             // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
             if (forceDisposeEffect && this._effect) {
                 if (!this._storeEffectOnSubMeshes) {
-                    this._scene.getEngine()._releaseEffect(this._effect);
+                    scene.getEngine()._releaseEffect(this._effect);
                 }
 
                 this._effect = null;
@@ -1320,6 +1328,24 @@ module BABYLON {
             }
         }
 
+        /** @hidden */
+        private  releaseVertexArrayObject(mesh: AbstractMesh, forceDisposeEffect?: boolean) {
+            if ((<Mesh>mesh).geometry) {
+                var geometry = <Geometry>((<Mesh>mesh).geometry);
+                const scene = this.getScene();
+                if (this._storeEffectOnSubMeshes) {
+                    for (var subMesh of mesh.subMeshes) {
+                        geometry._releaseVertexArrayObject(subMesh._materialEffect);
+                        if (forceDisposeEffect && subMesh._materialEffect) {
+                            scene.getEngine()._releaseEffect(subMesh._materialEffect);
+                        }
+                    }
+                } else {
+                    geometry._releaseVertexArrayObject(this._effect);
+                }
+            }
+        }
+
         /**
          * Serializes this material
          * @returns the serialized material object

+ 3 - 2
src/Materials/babylon.shaderMaterial.ts

@@ -656,8 +656,9 @@ module BABYLON {
          * Disposes the material
          * @param forceDisposeEffect specifies if effects should be forcefully disposed
          * @param forceDisposeTextures specifies if textures should be forcefully disposed
+         * @param notBoundToMesh specifies if the material that is being disposed is known to be not bound to any mesh
          */
-        public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
+        public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean, notBoundToMesh?: boolean): void {
 
             if (forceDisposeTextures) {
                 var name: string;
@@ -675,7 +676,7 @@ module BABYLON {
 
             this._textures = {};
 
-            super.dispose(forceDisposeEffect, forceDisposeTextures);
+            super.dispose(forceDisposeEffect, forceDisposeTextures, notBoundToMesh);
         }
 
         /**

+ 4 - 2
src/Materials/babylon.uniformBuffer.ts

@@ -616,10 +616,12 @@ module BABYLON {
                 return;
             }
 
-            let index = this._engine._uniformBuffers.indexOf(this);
+            const uniformBuffers = this._engine._uniformBuffers;
+            let index = uniformBuffers.indexOf(this);
 
             if (index !== -1) {
-                this._engine._uniformBuffers.splice(index, 1);
+                uniformBuffers[index] = uniformBuffers[uniformBuffers.length - 1];
+                uniformBuffers.pop();
             }
 
             if (!this._buffer) {

+ 9 - 0
src/Mesh/babylon.abstractMesh.ts

@@ -264,8 +264,17 @@ module BABYLON {
                 return;
             }
 
+            // remove from material mesh map id needed
+            if (this._material && this._material.meshMap) {
+                this._material.meshMap[this.uniqueId] = undefined;
+            }
+
             this._material = value;
 
+            if (value && value.meshMap) {
+                value.meshMap[this.uniqueId] = this;
+            }
+
             if (this.onMaterialChangedObservable.hasObservers) {
                 this.onMaterialChangedObservable.notifyObservers(this);
             }

+ 5 - 5
src/Mesh/babylon.instancedMesh.ts

@@ -7,10 +7,13 @@ module BABYLON {
         private _sourceMesh: Mesh;
         private _currentLOD: Mesh;
 
+        /** @hidden */
+        public _indexInSourceMeshInstanceArray = -1;
+
         constructor(name: string, source: Mesh) {
             super(name, source.getScene());
 
-            source.instances.push(this);
+            source.addInstance(this);
 
             this._sourceMesh = source;
 
@@ -322,11 +325,8 @@ module BABYLON {
          * Returns nothing.
          */
         public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
-
             // Remove from mesh
-            var index = this._sourceMesh.instances.indexOf(this);
-            this._sourceMesh.instances.splice(index, 1);
-
+            this._sourceMesh.removeInstance(this);
             super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
     }

+ 1 - 2
src/Mesh/babylon.linesMesh.ts

@@ -163,8 +163,7 @@ module BABYLON {
          * @param doNotRecurse If children should be disposed
          */
         public dispose(doNotRecurse?: boolean): void {
-            this._colorShader.dispose();
-
+            this._colorShader.dispose(false, false, true);
             super.dispose(doNotRecurse);
         }
 

+ 56 - 6
src/Mesh/babylon.mesh.ts

@@ -139,6 +139,8 @@ module BABYLON {
 
         /**
          * Gets the list of instances created from this mesh
+         * it is not supposed to be modified manually.
+         * Note also that the order of the InstancedMesh wihin the array is not significant and might change.
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
          */
         public instances = new Array<InstancedMesh>();
@@ -213,6 +215,8 @@ module BABYLON {
 
         // Will be used to save a source mesh reference, If any
         private _source: Nullable<Mesh> = null;
+        // Will be used to for fast cloned mesh lookup
+        private meshMap: Nullable<{[id: string]: Mesh | undefined}>;
 
         /**
          * Gets the source mesh (the one used to clone this one from)
@@ -266,6 +270,12 @@ module BABYLON {
 
                 // Source mesh
                 this._source = source;
+                if (scene.useClonedMeshhMap) {
+                    if (!source.meshMap) {
+                        source.meshMap = {};
+                    }
+                    source.meshMap[this.uniqueId] = this;
+                }
 
                 // Construction Params
                 // Clone parameters allowing mesh to be updated in case of parametric shapes.
@@ -2020,13 +2030,31 @@ module BABYLON {
             }
 
             // Sources
-            var meshes = this.getScene().meshes;
-            meshes.forEach((abstractMesh: AbstractMesh) => {
-                let mesh = abstractMesh as Mesh;
-                if (mesh._source && mesh._source === this) {
-                    mesh._source = null;
+            if (this._scene.useClonedMeshhMap) {
+                if (this.meshMap) {
+                    for (const uniqueId in this.meshMap) {
+                        const mesh = this.meshMap[uniqueId];
+                        if (mesh) {
+                            mesh._source = null;
+                            this.meshMap[uniqueId] = undefined;
+                        }
+                    }
                 }
-            });
+
+                if (this._source && this._source.meshMap) {
+                    this._source.meshMap[this.uniqueId] = undefined;
+                }
+            }
+            else {
+                var meshes = this.getScene().meshes;
+                for (const abstractMesh of meshes) {
+                    let mesh = abstractMesh as Mesh;
+                    if (mesh._source && mesh._source === this) {
+                        mesh._source = null;
+                    }
+                }
+            }
+
             this._source = null;
 
             // Instances
@@ -3786,5 +3814,27 @@ module BABYLON {
 
             return meshSubclass;
         }
+
+        /** @hidden */
+        public addInstance(instance: InstancedMesh) {
+            instance._indexInSourceMeshInstanceArray = this.instances.length;
+            this.instances.push(instance);
+        }
+
+        /** @hidden */
+        public removeInstance(instance: InstancedMesh) {
+            // Remove from mesh
+            const index = instance._indexInSourceMeshInstanceArray;
+            if (index != -1) {
+                if (index !== this.instances.length - 1) {
+                    const last = this.instances[this.instances.length - 1];
+                    this.instances[index] = last;
+                    last._indexInSourceMeshInstanceArray = index;
+                }
+
+                instance._indexInSourceMeshInstanceArray = -1;
+                this.instances.pop();
+            }
+        }
     }
 }

+ 1 - 0
src/Mesh/babylon.transformNode.ts

@@ -95,6 +95,7 @@ module BABYLON {
 
         /** @hidden */
         public _indexInSceneTransformNodesArray = -1;
+
         /**
         * An event triggered after the world matrix is updated
         */

+ 4 - 1
src/babylon.abstractScene.ts

@@ -138,6 +138,9 @@ module BABYLON {
 
         /**
         * All of the materials added to this scene
+        * In the context of a Scene, it is not supposed to be modified manually.
+        * Any addition or removal should be done using the addMaterial and removeMAterial Scene methods.
+        * Note also that the order of the Material wihin the array is not significant and might change.
         * @see http://doc.babylonjs.com/babylon101/materials
         */
         public materials = new Array<Material>();
@@ -155,7 +158,7 @@ module BABYLON {
 
         /**
         * All of the tranform nodes added to this scene
-        * In the context a the Scene, it is not supposed to be modified manually.
+        * In the context of a Scene, it is not supposed to be modified manually.
         * Any addition or removal should be done using the addTransformNode and removeTransformNode Scene methods.
         * Note also that the order of the TransformNode wihin the array is not significant and might change.
         * @see http://doc.babylonjs.com/how_to/transformnode

+ 68 - 9
src/babylon.scene.ts

@@ -70,6 +70,18 @@ module BABYLON {
          * It will improve performance when the number of geometries becomes important.
          */
         useGeometryIdsMap?: boolean;
+
+        /**
+         * Defines that each material of the scene should keep up-to-date a map of referencing meshes for fast diposing
+         * It will improve performance when the number of mesh becomes important, but might consume a bit more memory
+         */
+        useMaterialMeshMap?: boolean;
+
+        /**
+         * Defines that each mesh of the scene should keep up-to-date a map of referencing cloned meshes for fast diposing
+         * It will improve performance when the number of mesh becomes important, but might consume a bit more memory
+         */
+        useClonedMeshhMap?: boolean;
     }
 
     /**
@@ -1015,6 +1027,11 @@ module BABYLON {
          */
         public requireLightSorting = false;
 
+        /** @hidden */
+        public readonly useMaterialMeshMap: boolean;
+        /** @hidden */
+        public readonly useClonedMeshhMap: boolean;
+
         private _pointerOverMesh: Nullable<AbstractMesh>;
 
         private _pickedDownMesh: Nullable<AbstractMesh>;
@@ -1222,6 +1239,9 @@ module BABYLON {
             if (options && options.useGeometryIdsMap === true) {
                 this.geometriesById = {};
             }
+
+            this.useMaterialMeshMap = options && options.useGeometryIdsMap || false;
+            this.useClonedMeshhMap = options && options.useClonedMeshhMap || false;
         }
 
         private _defaultMeshCandidates: ISmartArrayLike<AbstractMesh> = {
@@ -1808,7 +1828,7 @@ module BABYLON {
                 }
             }
 
-            if (this.onPointerUp) {
+            if (this.onPointerUp && !clickInfo.ignore) {
                 this.onPointerUp(evt, pickResult, type);
             }
 
@@ -1899,7 +1919,6 @@ module BABYLON {
                             if (Date.now() - this._previousStartingPointerTime > Scene.DoubleClickDelay ||
                                 btn !== this._previousButtonPressed) {
                                 clickInfo.singleClick = true;
-                                eventRaised = true;
                                 cb(clickInfo, this._currentPickResult);
                             }
                         }
@@ -1969,10 +1988,8 @@ module BABYLON {
                     }
                 }
 
-                if (!eventRaised) {
-                    clickInfo.ignore = true;
-                    cb(clickInfo, this._currentPickResult);
-                }
+                clickInfo.ignore = true;
+                cb(clickInfo, this._currentPickResult);
             };
 
             this._onPointerMove = (evt: PointerEvent) => {
@@ -2999,7 +3016,8 @@ module BABYLON {
             var index = this.meshes.indexOf(toRemove);
             if (index !== -1) {
                 // Remove from the scene if mesh found
-                this.meshes.splice(index, 1);
+                this.meshes[index] = this.meshes[this.meshes.length - 1];
+                this.meshes.pop();
             }
 
             this.onMeshRemovedObservable.notifyObservers(toRemove);
@@ -3183,10 +3201,18 @@ module BABYLON {
          * @returns The index of the removed material
          */
         public removeMaterial(toRemove: Material): number {
-            var index = this.materials.indexOf(toRemove);
+            var index = toRemove._indexInSceneMaterialArray;
             if (index !== -1) {
-                this.materials.splice(index, 1);
+                if (index !== this.materials.length - 1) {
+                    const lastMaterial = this.materials[this.materials.length - 1];
+                    this.materials[index] = lastMaterial;
+                    lastMaterial._indexInSceneMaterialArray = index;
+                }
+
+                toRemove._indexInSceneMaterialArray = -1;
+                this.materials.pop();
             }
+
             this.onMaterialRemovedObservable.notifyObservers(toRemove);
 
             return index;
@@ -3302,6 +3328,7 @@ module BABYLON {
          * @param newMaterial The material to add
          */
         public addMaterial(newMaterial: Material): void {
+            newMaterial._indexInSceneMaterialArray = this.materials.length;
             this.materials.push(newMaterial);
             this.onNewMaterialAddedObservable.notifyObservers(newMaterial);
         }
@@ -4039,10 +4066,38 @@ module BABYLON {
             this._processedMaterials.dispose();
         }
 
+        private _preventFreeActiveMeshesAndRenderingGroups = false;
+
+        /** Gets or sets a boolean blocking all the calls to freeActiveMeshes and freeRenderingGroups
+         * It can be used in order to prevent going through methods freeRenderingGroups and freeActiveMeshes several times to improve performance
+         * when disposing several meshes in a row or a hierarchy of meshes.
+         * When used, it is the responsability of the user to blockfreeActiveMeshesAndRenderingGroups back to false.
+         */
+        public get blockfreeActiveMeshesAndRenderingGroups(): boolean {
+            return this._preventFreeActiveMeshesAndRenderingGroups;
+        }
+
+        public set blockfreeActiveMeshesAndRenderingGroups(value: boolean) {
+            if (this._preventFreeActiveMeshesAndRenderingGroups === value) {
+                return;
+            }
+
+            if (value) {
+                this.freeActiveMeshes();
+                this.freeRenderingGroups();
+            }
+
+            this._preventFreeActiveMeshesAndRenderingGroups = value;
+        }
+
         /**
          * Clear the active meshes smart array preventing retention point in mesh dispose.
          */
         public freeActiveMeshes(): void {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
+
             this._activeMeshes.dispose();
             if (this.activeCamera && this.activeCamera._activeMeshes) {
                 this.activeCamera._activeMeshes.dispose();
@@ -4061,6 +4116,10 @@ module BABYLON {
          * Clear the info related to rendering groups preventing retention points during dispose.
          */
         public freeRenderingGroups(): void {
+            if (this.blockfreeActiveMeshesAndRenderingGroups) {
+                return;
+            }
+
             if (this._renderingManager) {
                 this._renderingManager.freeRenderingGroups();
             }