sebastien пре 7 година
родитељ
комит
0e80303485

+ 2 - 1
Tools/Gulp/config.json

@@ -1204,7 +1204,8 @@
         "octrees": {
             "files": [
                 "../../src/Culling/Octrees/babylon.octree.js",
-                "../../src/Culling/Octrees/babylon.octreeBlock.js"
+                "../../src/Culling/Octrees/babylon.octreeBlock.js",
+                "../../src/Culling/Octrees/babylon.octreeSceneComponent.js"
             ],
             "dependUpon": [
                 "core"

+ 2 - 1
src/Collisions/babylon.collisionWorker.ts

@@ -113,7 +113,8 @@ module BABYLON {
             //return colTransMat;
         }
 
-        private processCollisionsForSubMeshes(transformMatrix: Matrix, mesh: SerializedMesh): void { // No Octrees for now
+        private processCollisionsForSubMeshes(transformMatrix: Matrix, mesh: SerializedMesh): void { 
+            // No Octrees for now
             //if (this._submeshesOctree && this.useOctreeForCollisions) {
             //    var radius = collider.velocityWorldLength + Math.max(collider.radius.x, collider.radius.y, collider.radius.z);
             //    var intersections = this._submeshesOctree.intersects(collider.basePointWorld, radius);

+ 227 - 0
src/Culling/Octrees/babylon.octreeSceneComponent.ts

@@ -0,0 +1,227 @@
+module BABYLON {
+    export interface Scene {
+        /**
+         * @hidden
+         * Backing Filed
+         */
+        _selectionOctree: Octree<AbstractMesh>;
+
+        /**
+         * Gets the octree used to boost mesh selection (picking)
+         * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
+         */
+        selectionOctree: Octree<AbstractMesh>;
+
+        /**
+         * Creates or updates the octree used to boost selection (picking)
+         * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
+         * @param maxCapacity defines the maximum capacity per leaf
+         * @param maxDepth defines the maximum depth of the octree
+         * @returns an octree of AbstractMesh
+         */
+        createOrUpdateSelectionOctree(maxCapacity?: number, maxDepth?: number): Octree<AbstractMesh>;
+    }
+
+    Scene.prototype.createOrUpdateSelectionOctree = function(maxCapacity = 64, maxDepth = 2): Octree<AbstractMesh> {
+        let component = this._getComponent(SceneComponentConstants.NAME_OCTREE);
+        if (!component) {
+            component = new OctreeSceneComponent(this);
+            this._addComponent(component);
+        }
+        
+        if (!this._selectionOctree) {
+            this._selectionOctree = new Octree<AbstractMesh>(Octree.CreationFuncForMeshes, maxCapacity, maxDepth);
+        }
+
+        var worldExtends = this.getWorldExtends();
+
+        // Update octree
+        this._selectionOctree.update(worldExtends.min, worldExtends.max, this.meshes);
+
+        return this._selectionOctree;
+   }
+
+    Object.defineProperty(Scene.prototype, "selectionOctree", {
+        get: function (this:Scene) {
+            return this._selectionOctree;
+        },
+        enumerable: true,
+        configurable: true
+    });
+
+    export interface AbstractMesh {
+        /** 
+         * @hidden 
+         * Backing Field
+         */
+        _submeshesOctree: Octree<SubMesh>;
+
+        /**
+         * This function will create an octree to help to select the right submeshes for rendering, picking and collision computations.  
+         * Please note that you must have a decent number of submeshes to get performance improvements when using an octree
+         * @param maxCapacity defines the maximum size of each block (64 by default)
+         * @param maxDepth defines the maximum depth to use (no more than 2 levels by default)
+         * @returns the new octree
+         * @see https://www.babylonjs-playground.com/#NA4OQ#12
+         * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
+         */
+        createOrUpdateSubmeshesOctree(maxCapacity?: number, maxDepth?: number): Octree<SubMesh>;
+    }
+
+    /**
+     * This function will create an octree to help to select the right submeshes for rendering, picking and collision computations.  
+     * Please note that you must have a decent number of submeshes to get performance improvements when using an octree
+     * @param maxCapacity defines the maximum size of each block (64 by default)
+     * @param maxDepth defines the maximum depth to use (no more than 2 levels by default)
+     * @returns the new octree
+     * @see https://www.babylonjs-playground.com/#NA4OQ#12
+     * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
+     */
+    AbstractMesh.prototype.createOrUpdateSubmeshesOctree = function(maxCapacity = 64, maxDepth = 2): Octree<SubMesh> {
+        const scene = this.getScene();
+        let component = scene._getComponent(SceneComponentConstants.NAME_OCTREE);
+        if (!component) {
+            component = new OctreeSceneComponent(scene);
+            scene._addComponent(component);
+        }
+
+        if (!this._submeshesOctree) {
+            this._submeshesOctree = new Octree<SubMesh>(Octree.CreationFuncForSubMeshes, maxCapacity, maxDepth);
+        }
+
+        this.computeWorldMatrix(true);
+
+        let boundingInfo = this.getBoundingInfo();
+
+        // Update octree
+        var bbox = boundingInfo.boundingBox;
+        this._submeshesOctree.update(bbox.minimumWorld, bbox.maximumWorld, this.subMeshes);
+
+        return this._submeshesOctree;
+    }
+
+    /**
+     * Defines the octree scene component responsible to manage any octrees
+     * in a given scene.
+     */
+    export class OctreeSceneComponent implements ISceneComponent, IActiveMeshCandidateProvider {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_OCTREE;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        /** 
+         * Indicates if the meshes have been checked to make sure they are isEnabled()
+         */
+        public readonly checksIsEnabled = true;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+
+            this.scene.setActiveMeshCandidateProvider(this);
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene.onMeshRemovedObservable.add((mesh: AbstractMesh) => {
+                const sceneOctree = this.scene.selectionOctree;
+                if (sceneOctree !== undefined && sceneOctree !== null) {
+                    var index = sceneOctree.dynamicContent.indexOf(mesh);
+
+                    if (index !== -1) {
+                        sceneOctree.dynamicContent.splice(index, 1);
+                    }
+                }
+            });
+
+            this.scene.onMeshImportedObservable.add((mesh: AbstractMesh) => {
+                const sceneOctree = this.scene.selectionOctree;
+                if (sceneOctree !== undefined && sceneOctree !== null) {
+                    sceneOctree.addMesh(mesh);
+                }
+            })
+        }
+
+        /**
+         * Return the list of active meshes
+         * @param scene defines the current scene
+         * @returns the list of active meshes
+         */
+        public getMeshes(scene: Scene): ISmartArrayLike<AbstractMesh> {
+            var selection = scene._selectionOctree.select(scene.frustumPlanes);
+            return selection;
+        }
+
+        /**
+         * Return the list of active sub meshes
+         * @param scene defines the current scene
+         * @returns the list of active sub meshes
+         */
+        public getSubMeshes(mesh: AbstractMesh): ISmartArrayLike<SubMesh> {
+            if (mesh._submeshesOctree && mesh.useOctreeForRenderingSelection) {
+                var intersections = mesh._submeshesOctree.select(this.scene.frustumPlanes);
+                return intersections;
+            }
+            return this.scene._getDefaultSubMeshCandidates(mesh);
+        }
+
+        private _tempRay = new BABYLON.Ray(Vector3.Zero(), new Vector3(1, 1, 1));
+        /**
+         * Return the list of sub meshes intersecting with a given local ray
+         * @param mesh defines the mesh to find the submesh for
+         * @param localRay defines the ray in local space
+         * @returns the list of intersecting sub meshes
+         */
+        public getActiveCanditates(mesh: AbstractMesh, localRay: Ray): ISmartArrayLike<SubMesh> {
+            if (mesh._submeshesOctree && mesh.useOctreeForPicking) {
+                Ray.TransformToRef(localRay, mesh.getWorldMatrix(), this._tempRay);
+                var intersections = mesh._submeshesOctree.intersectsRay(this._tempRay);
+
+                return intersections;
+            }
+            return this.scene._getDefaultSubMeshCandidates(mesh);
+        }
+
+        /**
+         * Return the list of sub meshes colliding with a collider
+         * @param mesh defines the mesh to find the submesh for
+         * @param collider defines the collider to evaluate the collision against
+         * @returns the list of colliding sub meshes
+         */
+        public getCollidingCandidates(mesh: AbstractMesh, collider: Collider): ISmartArrayLike<SubMesh> {
+            if (mesh._submeshesOctree && mesh.useOctreeForCollisions) {
+                var radius = collider._velocityWorldLength + Math.max(collider._radius.x, collider._radius.y, collider._radius.z);
+                var intersections = mesh._submeshesOctree.intersects(collider._basePointWorld, radius);
+
+                return intersections;
+            }
+            return this.scene._getDefaultSubMeshCandidates(mesh);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            // Nothing to do here.
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            // Nothing to do here.
+        }
+    }
+} 

+ 7 - 70
src/Mesh/babylon.abstractMesh.ts

@@ -541,8 +541,7 @@
          * @see http://doc.babylonjs.com/how_to/multi_materials
          */
         public subMeshes: SubMesh[];
-        /** @hidden */
-        public _submeshesOctree: Octree<SubMesh>;
+
         /** @hidden */
         public _intersectionsInProgress = new Array<AbstractMesh>();
 
@@ -1307,33 +1306,6 @@
             this.onCollisionPositionChangeObservable.notifyObservers(this.position);
         }
 
-        // Submeshes octree
-
-        /**
-        * This function will create an octree to help to select the right submeshes for rendering, picking and collision computations.  
-        * Please note that you must have a decent number of submeshes to get performance improvements when using an octree
-        * @param maxCapacity defines the maximum size of each block (64 by default)
-        * @param maxDepth defines the maximum depth to use (no more than 2 levels by default)
-        * @returns the new octree
-        * @see https://www.babylonjs-playground.com/#NA4OQ#12
-        * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
-        */
-        public createOrUpdateSubmeshesOctree(maxCapacity = 64, maxDepth = 2): Octree<SubMesh> {
-            if (!this._submeshesOctree) {
-                this._submeshesOctree = new Octree<SubMesh>(Octree.CreationFuncForSubMeshes, maxCapacity, maxDepth);
-            }
-
-            this.computeWorldMatrix(true);
-
-            let boundingInfo = this.getBoundingInfo();
-
-            // Update octree
-            var bbox = boundingInfo.boundingBox;
-            this._submeshesOctree.update(bbox.minimumWorld, bbox.maximumWorld, this.subMeshes);
-
-            return this._submeshesOctree;
-        }
-
         // Collisions
         /** @hidden */
         public _collideForSubMesh(subMesh: SubMesh, transformMatrix: Matrix, collider: Collider): AbstractMesh {
@@ -1364,23 +1336,11 @@
 
         /** @hidden */
         public _processCollisionsForSubMeshes(collider: Collider, transformMatrix: Matrix): AbstractMesh {
-            var subMeshes: SubMesh[];
-            var len: number;
-
-            // Octrees
-            if (this._submeshesOctree && this.useOctreeForCollisions) {
-                var radius = collider._velocityWorldLength + Math.max(collider._radius.x, collider._radius.y, collider._radius.z);
-                var intersections = this._submeshesOctree.intersects(collider._basePointWorld, radius);
-
-                len = intersections.length;
-                subMeshes = intersections.data;
-            } else {
-                subMeshes = this.subMeshes;
-                len = subMeshes.length;
-            }
+            const subMeshes = this._scene.getSubMeshCandidateProvider().getCollidingCandidates(this, collider);
+            const len = subMeshes.length;
 
             for (var index = 0; index < len; index++) {
-                var subMesh = subMeshes[index];
+                var subMesh = subMeshes.data[index];
 
                 // Bounding test
                 if (len > 1 && !subMesh._checkCollision(collider))
@@ -1430,23 +1390,10 @@
 
             var intersectInfo: Nullable<IntersectionInfo> = null;
 
-            // Octrees
-            var subMeshes: SubMesh[];
-            var len: number;
-
-            if (this._submeshesOctree && this.useOctreeForPicking) {
-                var worldRay = Ray.Transform(ray, this.getWorldMatrix());
-                var intersections = this._submeshesOctree.intersectsRay(worldRay);
-
-                len = intersections.length;
-                subMeshes = intersections.data;
-            } else {
-                subMeshes = this.subMeshes;
-                len = subMeshes.length;
-            }
-
+            var subMeshes = this._scene.getSubMeshCandidateProvider().getIntersectingCandidates(this, ray);
+            var len: number = subMeshes.length;
             for (var index = 0; index < len; index++) {
-                var subMesh = subMeshes[index];
+                var subMesh = subMeshes.data[index];
 
                 // Bounding test
                 if (len > 1 && !subMesh.canIntersects(ray))
@@ -1589,16 +1536,6 @@
                 this.releaseSubMeshes();
             }
 
-            // Octree
-            const sceneOctree = this.getScene().selectionOctree;
-            if (sceneOctree !== undefined && sceneOctree !== null) {
-                var index = sceneOctree.dynamicContent.indexOf(this);
-
-                if (index !== -1) {
-                    sceneOctree.dynamicContent.splice(index, 1);
-                }
-            }
-
             // Query
             let engine = this.getScene().getEngine();
             if (this._occlusionQuery) {

+ 1 - 5
src/Mesh/babylon.geometry.ts

@@ -1297,11 +1297,7 @@
             // Update
             mesh.computeWorldMatrix(true);
 
-            // Octree
-            const sceneOctree = scene.selectionOctree;
-            if (sceneOctree !== undefined && sceneOctree !== null) {
-                sceneOctree.addMesh(<AbstractMesh>mesh);
-            }
+            scene.onMeshImportedObservable.notifyObservers(<AbstractMesh>mesh);
         }
 
         private static _CleanMatricesWeights(parsedGeometry: any, mesh: Mesh): void {

+ 6 - 1
src/Mesh/babylon.groundMesh.ts

@@ -45,7 +45,12 @@
             this._subdivisionsX = chunksCount;
             this._subdivisionsY = chunksCount;
             this.subdivide(chunksCount);
-            this.createOrUpdateSubmeshesOctree(octreeBlocksSize);
+
+            // Call the octree system optimization if it is defined.
+            const thisAsAny = this as any;
+            if (thisAsAny.createOrUpdateSubmeshesOctree) {
+                thisAsAny.createOrUpdateSubmeshesOctree(octreeBlocksSize);
+            }
         }
 
         /**

+ 10 - 6
src/Tools/babylon.sceneOptimizer.ts

@@ -436,13 +436,17 @@
                 Mesh.MergeMeshes(currentPool, undefined, true);
             }
 
-            if (updateSelectionTree != undefined) {
-                if (updateSelectionTree) {
-                    scene.createOrUpdateSelectionOctree();
+            // Call the octree system optimization if it is defined.
+            const sceneAsAny = scene as any;
+            if (sceneAsAny.createOrUpdateSelectionOctree) {
+                if (updateSelectionTree != undefined) {
+                    if (updateSelectionTree) {
+                        sceneAsAny.createOrUpdateSelectionOctree();
+                    }
+                }
+                else if (MergeMeshesOptimization.UpdateSelectionTree) {
+                    sceneAsAny.createOrUpdateSelectionOctree();
                 }
-            }
-            else if (MergeMeshesOptimization.UpdateSelectionTree) {
-                scene.createOrUpdateSelectionOctree();
             }
 
             return true;

+ 86 - 7
src/Tools/babylon.smartArray.ts

@@ -1,17 +1,48 @@
 module BABYLON {
-    export class SmartArray<T> {
+    /**
+     * Defines an array and its length.
+     * It can be helpfull to group result from both Arrays and smart arrays in one structure.
+     */
+    export interface ISmartArrayLike<T> {
+        /**
+         * The data of the array.
+         */
+        data: Array<T>;
+        /**
+         * The active length of the array.
+         */
+        length: number;
+    }
+
+    /**
+     * Defines an GC Friendly array where the backfield array do not shrink to prevent over allocations.
+     */
+    export class SmartArray<T> implements ISmartArrayLike<T> {
+        /**
+         * The full set of data from the array.
+         */
         public data: Array<T>;
+
+        /**
+         * The active length of the array.
+         */
         public length: number = 0;
 
         protected _id: number;
 
-        [index: number]: T;
-
+        /**
+         * Instantiates a Smart Array.
+         * @param capacity defines the default capacity of the array.
+         */
         constructor(capacity: number) {
             this.data = new Array(capacity);
             this._id = SmartArray._GlobalId++;
         }
 
+        /**
+         * Pushes a value at the end of the active data.
+         * @param value defines the object to push in the array.
+         */
         public push(value: T): void {
             this.data[this.length++] = value;
 
@@ -20,20 +51,34 @@
             }
         }
 
+        /**
+         * Iterates over the active data and apply the lambda to them.
+         * @param func defines the action to apply on each value.
+         */
         public forEach(func: (content: T) => void): void {
             for (var index = 0; index < this.length; index++) {
                 func(this.data[index]);
             }
         }
-    
+
+        /**
+         * Sorts the full sets of data.
+         * @param compareFn defines the comparison function to apply.
+         */
         public sort(compareFn: (a: T, b: T) => number): void {
             this.data.sort(compareFn);
         }
 
+        /**
+         * Resets the active data to an empty array.
+         */
         public reset(): void {
             this.length = 0;
         }
 
+        /**
+         * Releases all the data from the array as well as the array.
+         */
         public dispose(): void {
             this.reset();
 
@@ -43,6 +88,10 @@
             }
         }
 
+        /**
+         * Concats the active data with a given array.
+         * @param array defines the data to concatenate with.
+         */
         public concat(array: any): void {
             if (array.length === 0) {
                 return;
@@ -56,6 +105,11 @@
             }
         }
 
+        /**
+         * Returns the position of a value in the active data.
+         * @param value defines the value to find the index for
+         * @returns the index if found in the active data otherwise -1
+         */
         public indexOf(value: T): number {
             var position = this.data.indexOf(value);
 
@@ -66,6 +120,11 @@
             return position;
         }
 
+        /**
+         * Returns whether an element is part of the active data.
+         * @param value defines the value to look for
+         * @returns true if found in the active data otherwise false
+         */
         public contains(value: T): boolean {
             return this.data.indexOf(value) !== -1;
         }
@@ -74,11 +133,18 @@
         private static _GlobalId = 0;
     }
 
+    /**
+     * Defines an GC Friendly array where the backfield array do not shrink to prevent over allocations.
+     * The data in this array can only be present once
+     */
     export class SmartArrayNoDuplicate<T> extends SmartArray<T> {
         private _duplicateId = 0;
 
-        [index: number]: T;
-
+        /**
+         * Pushes a value at the end of the active data.
+         * THIS DOES NOT PREVENT DUPPLICATE DATA
+         * @param value defines the object to push in the array.
+         */
         public push(value: T): void {
             super.push(value);
 
@@ -89,7 +155,12 @@
             (<any>value).__smartArrayFlags[this._id] = this._duplicateId;
         }
 
-
+        /**
+         * Pushes a value at the end of the active data.
+         * If the data is already present, it won t be added again
+         * @param value defines the object to push in the array.
+         * @returns true if added false if it was already present
+         */
         public pushNoDuplicate(value: T): boolean {
             if ((<any>value).__smartArrayFlags && (<any>value).__smartArrayFlags[this._id] === this._duplicateId) {
                 return false;
@@ -98,11 +169,19 @@
             return true;
         }
 
+        /**
+         * Resets the active data to an empty array.
+         */
         public reset(): void {
             super.reset();
             this._duplicateId++;
         }
 
+        /**
+         * Concats the active data with a given array.
+         * This ensures no dupplicate will be present in the result.
+         * @param array defines the data to concatenate with.
+         */
         public concatWithNoDuplicate(array: any): void {
             if (array.length === 0) {
                 return;

+ 119 - 88
src/babylon.scene.ts

@@ -18,13 +18,40 @@
          * @param scene defines the current scene
          * @returns the list of active meshes
          */
-        getMeshes(scene: Scene): AbstractMesh[];
+        getMeshes(scene: Scene): ISmartArrayLike<AbstractMesh>;
+
         /** 
          * Indicates if the meshes have been checked to make sure they are isEnabled()
          */
         readonly checksIsEnabled: boolean;
     }
 
+    /**
+     * Interface used to let developers provide their own sub mesh selection mechanism
+     */
+    export interface ISubMeshCandidateProvider {
+        /**
+         * Return the list of active sub meshes
+         * @param mesh defines the mesh to find the submesh for
+         * @returns the list of active sub meshes
+         */
+        getActiveCanditates(mesh: AbstractMesh): ISmartArrayLike<SubMesh>;
+        /**
+         * Return the list of sub meshes intersecting with a given local ray
+         * @param mesh defines the mesh to find the submesh for
+         * @param localRay defines the ray in local space
+         * @returns the list of intersecting sub meshes
+         */
+        getIntersectingCandidates(mesh: AbstractMesh, localRay: Ray): ISmartArrayLike<SubMesh>;
+        /**
+         * Return the list of sub meshes colliding with a collider
+         * @param mesh defines the mesh to find the submesh for
+         * @param collider defines the collider to evaluate the collision against
+         * @returns the list of colliding sub meshes
+         */
+        getCollidingCandidates(mesh: AbstractMesh, collider: Collider): ISmartArrayLike<SubMesh>;
+    }
+
     /** @hidden */
     class ClickInfo {
         private _singleClick = false;
@@ -502,6 +529,11 @@
          */
         public onAfterRenderingGroupObservable = new Observable<RenderingGroupInfo>();
 
+        /**
+         * This Observable will when a mesh has been imported into the scene.
+         */
+        public onMeshImportedObservable = new Observable<AbstractMesh>();
+
         // Animations
         private _registeredForLateAnimationBindings = new SmartArrayNoDuplicate<any>(256);
 
@@ -1025,8 +1057,6 @@
          */
         public requireLightSorting = false;
 
-        private _selectionOctree: Octree<AbstractMesh>;
-
         private _pointerOverMesh: Nullable<AbstractMesh>;
 
         private _debugLayer: DebugLayer;
@@ -1220,6 +1250,52 @@
             if (ImageProcessingConfiguration) {
                 this._imageProcessingConfiguration = new ImageProcessingConfiguration();
             }
+
+            this._setDefaultCandidateProviders();
+        }
+
+        private _defaultMeshCandidates: ISmartArrayLike<AbstractMesh> = {
+            data: [],
+            length: 0
+        }
+
+        /**
+         * @hidden
+         */
+        public _getDefaultMeshCandidates(): ISmartArrayLike<AbstractMesh> {
+            this._defaultMeshCandidates.data = this.meshes;
+            this._defaultMeshCandidates.length = this.meshes.length;
+            return this._defaultMeshCandidates;
+        }
+
+        private _defaultSubMeshCandidates: ISmartArrayLike<SubMesh> = {
+            data: [],
+            length: 0
+        }
+
+        /**
+         * @hidden
+         */
+        public _getDefaultSubMeshCandidates(mesh: AbstractMesh): ISmartArrayLike<SubMesh> {
+            this._defaultSubMeshCandidates.data = mesh.subMeshes;
+            this._defaultSubMeshCandidates.length = mesh.subMeshes.length;
+            return this._defaultSubMeshCandidates;
+        }
+
+        /**
+         * Sets the default candidate provider for the scene.
+         */
+        private _setDefaultCandidateProviders() {
+            this.setActiveMeshCandidateProvider({
+                getMeshes: this._getDefaultMeshCandidates.bind(this),
+                checksIsEnabled: true
+            });
+
+            this.setSubMeshCandidateProvider({
+                getActiveCanditates: this._getDefaultSubMeshCandidates.bind(this),
+                getIntersectingCandidates: this._getDefaultSubMeshCandidates.bind(this),
+                getCollidingCandidates: this._getDefaultSubMeshCandidates.bind(this),
+            });
         }
 
         /**
@@ -1259,14 +1335,6 @@
         }
 
         /**
-         * Gets the octree used to boost mesh selection (picking)
-         * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
-         */
-        public get selectionOctree(): Octree<AbstractMesh> {
-            return this._selectionOctree;
-        }
-
-        /**
          * Gets the mesh that is currently under the pointer
          */
         public get meshUnderPointer(): Nullable<AbstractMesh> {
@@ -2927,11 +2995,11 @@
         }
 
         /**
-           * Remove a mesh for the list of scene's meshes
-           * @param toRemove defines the mesh to remove
-           * @param recursive if all child meshes should also be removed from the scene
-           * @returns the index where the mesh was in the mesh list
-           */
+         * Remove a mesh for the list of scene's meshes
+         * @param toRemove defines the mesh to remove
+         * @param recursive if all child meshes should also be removed from the scene
+         * @returns the index where the mesh was in the mesh list
+         */
         public removeMesh(toRemove: AbstractMesh, recursive = false): number {
             var index = this.meshes.indexOf(toRemove);
             if (index !== -1) {
@@ -4002,21 +4070,44 @@
         }
 
         private _activeMeshCandidateProvider: IActiveMeshCandidateProvider;
+        private _activeMeshCandidateCheckIsReady: boolean = true;
+
         /**
          * Defines the current active mesh candidate provider
+         * Please note that in case of octree, the activeMeshCandidateProvider will be the octree one
          * @param provider defines the provider to use
          */
         public setActiveMeshCandidateProvider(provider: IActiveMeshCandidateProvider): void {
             this._activeMeshCandidateProvider = provider;
+            this._activeMeshCandidateCheckIsReady = provider.checksIsEnabled;
         }
         /**
          * Gets the current active mesh candidate provider
+         * Please note that in case of octree, the activeMeshCandidateProvider will be the octree one
          * @returns the current active mesh candidate provider
          */
         public getActiveMeshCandidateProvider(): IActiveMeshCandidateProvider {
             return this._activeMeshCandidateProvider;
         }
 
+        private _subMeshCandidateProvider: ISubMeshCandidateProvider;
+        /**
+         * Defines the current sub mesh candidate provider
+         * Please note that in case of octree, the subMeshCandidateProvider will be the octree one
+         * @param provider defines the provider to use
+         */
+        public setSubMeshCandidateProvider(provider: ISubMeshCandidateProvider): void {
+            this._subMeshCandidateProvider = provider;
+        }
+        /**
+         * Gets the current sub mesh candidate provider
+         * Please note that in case of octree, the subMeshCandidateProvider will be the octree one
+         * @returns the current provider
+         */
+        public getSubMeshCandidateProvider(): ISubMeshCandidateProvider {
+            return this._subMeshCandidateProvider;
+        }
+
         private _activeMeshesFrozen = false;
 
         /**
@@ -4068,43 +4159,20 @@
                 step.action();
             }
 
-            // Meshes
-            var meshes: AbstractMesh[];
-            var len: number;
-            var checkIsEnabled = true;
-
             // Determine mesh candidates
-            if (this._activeMeshCandidateProvider !== undefined) {
-                // Use _activeMeshCandidateProvider
-                meshes = this._activeMeshCandidateProvider.getMeshes(this);
-                checkIsEnabled = this._activeMeshCandidateProvider.checksIsEnabled === false;
-                if (meshes !== undefined) {
-                    len = meshes.length;
-                } else {
-                    len = 0;
-                }
-            } else if (this._selectionOctree !== undefined) {
-                // Octree
-                var selection = this._selectionOctree.select(this._frustumPlanes);
-                meshes = selection.data;
-                len = selection.length;
-            } else {
-                // Full scene traversal
-                len = this.meshes.length;
-                meshes = this.meshes;
-            }
-
+            const meshes = this._activeMeshCandidateProvider.getMeshes(this);
+            
             // Check each mesh
-            for (var meshIndex = 0, mesh, meshLOD; meshIndex < len; meshIndex++) {
-                mesh = meshes[meshIndex];
-
+            const len = meshes.length;
+            for (let i = 0; i < len; i++) {
+                const mesh = meshes.data[i];
                 if (mesh.isBlocked) {
                     continue;
                 }
 
                 this._totalVertices.addCount(mesh.getTotalVertices(), false);
 
-                if (!mesh.isReady() || (checkIsEnabled && !mesh.isEnabled())) {
+                if (!mesh.isReady() || (this._activeMeshCandidateCheckIsReady && !mesh.isEnabled())) {
                     continue;
                 }
 
@@ -4116,8 +4184,7 @@
                 }
 
                 // Switch to current LOD
-                meshLOD = mesh.getLOD(this.activeCamera);
-
+                const meshLOD = mesh.getLOD(this.activeCamera);
                 if (meshLOD === undefined || meshLOD === null) {
                     continue;
                 }
@@ -4179,23 +4246,10 @@
                 mesh !== undefined && mesh !== null
                 && mesh.subMeshes !== undefined && mesh.subMeshes !== null && mesh.subMeshes.length > 0
             ) {
-                // Submeshes Octrees
-                var len: number;
-                var subMeshes: SubMesh[];
-
-                if (mesh.useOctreeForRenderingSelection && mesh._submeshesOctree !== undefined && mesh._submeshesOctree !== null) {
-                    var intersections = mesh._submeshesOctree.select(this._frustumPlanes);
-
-                    len = intersections.length;
-                    subMeshes = intersections.data;
-                } else {
-                    subMeshes = mesh.subMeshes;
-                    len = subMeshes.length;
-                }
-
-                for (var subIndex = 0, subMesh; subIndex < len; subIndex++) {
-                    subMesh = subMeshes[subIndex];
-
+                const subMeshes = this._subMeshCandidateProvider.getActiveCanditates(mesh);
+                const len = subMeshes.length;
+                for (let i = 0; i < len; i++) {
+                    const subMesh = subMeshes.data[i];
                     this._evaluateSubMesh(subMesh, mesh);
                 }
             }
@@ -4599,7 +4653,6 @@
                 if (data) {
                     data.dispose();
                 }
-                this._toBeDisposed[index] = null;
             }
 
             this._toBeDisposed.reset();
@@ -4851,6 +4904,7 @@
             this.onDataLoadedObservable.clear();
             this.onBeforeRenderingGroupObservable.clear();
             this.onAfterRenderingGroupObservable.clear();
+            this.onMeshImportedObservable.clear();
 
             this.detachControl();
 
@@ -5007,9 +5061,6 @@
             }
         }
 
-
-        // Octrees
-
         /**
          * Get the world extend vectors with an optional filter
          * 
@@ -5042,26 +5093,6 @@
             };
         }
 
-        /**
-         * Creates or updates the octree used to boost selection (picking)
-         * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
-         * @param maxCapacity defines the maximum capacity per leaf
-         * @param maxDepth defines the maximum depth of the octree
-         * @returns an octree of AbstractMesh
-         */
-        public createOrUpdateSelectionOctree(maxCapacity = 64, maxDepth = 2): Octree<AbstractMesh> {
-            if (!this._selectionOctree) {
-                this._selectionOctree = new Octree<AbstractMesh>(Octree.CreationFuncForMeshes, maxCapacity, maxDepth);
-            }
-
-            var worldExtends = this.getWorldExtends();
-
-            // Update octree
-            this._selectionOctree.update(worldExtends.min, worldExtends.max, this.meshes);
-
-            return this._selectionOctree;
-        }
-
         // Picking
 
         /**

+ 1 - 0
src/babylon.sceneComponent.ts

@@ -18,6 +18,7 @@
         public static readonly NAME_OUTLINERENDERER = "Outline";
         public static readonly NAME_PROCEDURALTEXTURE = "ProceduralTexture";
         public static readonly NAME_SHADOWGENERATOR = "ShadowGenerator";
+        public static readonly NAME_OCTREE = "Octree";
 
         public static readonly STEP_ISREADYFORMESH_EFFECTLAYER = 0;