瀏覽代碼

Added new GUI.Control3D
Added behavior support

David Catuhe 7 年之前
父節點
當前提交
92fa953102

+ 4 - 1
Tools/Gulp/config.json

@@ -1659,7 +1659,10 @@
                     "../../gui/src/2D/controls/inputText.ts",
                     "../../gui/src/2D/controls/virtualKeyboard.ts",
                     "../../gui/src/2D/controls/multiLine.ts",
-                    "../../gui/src/3D/controls/control3D.ts"
+                    "../../gui/src/3D/gui3DManager.ts",
+                    "../../gui/src/3D/controls/control3D.ts",
+                    "../../gui/src/3D/controls/container3D.ts",
+                    "../../gui/src/3D/controls/button3D.ts"
                 ],
                 "output": "babylon.gui.js",
                 "buildAsModule": true,

File diff suppressed because it is too large
+ 15189 - 15184
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 43 - 43
dist/preview release/babylon.js


+ 1 - 5
dist/preview release/babylon.max.js

@@ -17657,12 +17657,8 @@ var BABYLON;
             behavior.init();
             if (this._scene.isLoading) {
                 // We defer the attach when the scene will be loaded
-                var observer = this._scene.onDataLoadedObservable.add(function () {
+                this._scene.onDataLoadedObservable.addOnce(function () {
                     behavior.attach(_this);
-                    setTimeout(function () {
-                        // Need to use a timeout to avoid removing an observer while iterating the list of observers
-                        _this._scene.onDataLoadedObservable.remove(observer);
-                    }, 0);
                 });
             }
             else {

+ 1 - 5
dist/preview release/babylon.no-module.max.js

@@ -17624,12 +17624,8 @@ var BABYLON;
             behavior.init();
             if (this._scene.isLoading) {
                 // We defer the attach when the scene will be loaded
-                var observer = this._scene.onDataLoadedObservable.add(function () {
+                this._scene.onDataLoadedObservable.addOnce(function () {
                     behavior.attach(_this);
-                    setTimeout(function () {
-                        // Need to use a timeout to avoid removing an observer while iterating the list of observers
-                        _this._scene.onDataLoadedObservable.remove(observer);
-                    }, 0);
                 });
             }
             else {

File diff suppressed because it is too large
+ 43 - 43
dist/preview release/babylon.worker.js


+ 1 - 5
dist/preview release/es6.js

@@ -17624,12 +17624,8 @@ var BABYLON;
             behavior.init();
             if (this._scene.isLoading) {
                 // We defer the attach when the scene will be loaded
-                var observer = this._scene.onDataLoadedObservable.add(function () {
+                this._scene.onDataLoadedObservable.addOnce(function () {
                     behavior.attach(_this);
-                    setTimeout(function () {
-                        // Need to use a timeout to avoid removing an observer while iterating the list of observers
-                        _this._scene.onDataLoadedObservable.remove(observer);
-                    }, 0);
                 });
             }
             else {

+ 98 - 1
dist/preview release/gui/babylon.gui.d.ts

@@ -965,8 +965,105 @@ declare module BABYLON.GUI {
 
 
 declare module BABYLON.GUI {
-    class Control3D {
+    /**
+     * Class used to manage 3D user interface
+     */
+    class GUI3DManager implements BABYLON.IDisposable {
+        private _scene;
+        private _sceneDisposeObserver;
+        private _utilityLayer;
+        private _rootContainer;
+        /** Gets the hosting scene */
+        readonly scene: Scene;
+        /**
+         * Creates a new GUI3DManager
+         * @param scene
+         */
+        constructor(scene?: Scene);
+        /**
+         * Gets the root container
+         */
+        readonly rootContainer: Container3D;
+        /**
+         * Releases all associated resources
+         */
+        dispose(): void;
+    }
+}
+
+
+declare module BABYLON.GUI {
+    /**
+     * Class used as base class for controls
+     */
+    class Control3D implements IDisposable, IBehaviorAware<Control3D> {
+        /** Defines the control name */
+        name: string | undefined;
+        /** @hidden */
+        _host: GUI3DManager;
+        private _behaviors;
+        /**
+         * Gets the list of attached behaviors
+         * @see http://doc.babylonjs.com/features/behaviour
+         */
+        readonly behaviors: Behavior<Control3D>[];
+        /**
+         * Attach a behavior to the control
+         * @see http://doc.babylonjs.com/features/behaviour
+         * @param behavior defines the behavior to attach
+         * @returns the current control
+         */
+        addBehavior(behavior: Behavior<Control3D>): Control3D;
+        /**
+         * Remove an attached behavior
+         * @see http://doc.babylonjs.com/features/behaviour
+         * @param behavior defines the behavior to attach
+         * @returns the current control
+         */
+        removeBehavior(behavior: Behavior<Control3D>): Control3D;
+        /**
+         * Gets an attached behavior by name
+         * @param name defines the name of the behavior to look for
+         * @see http://doc.babylonjs.com/features/behaviour
+         * @returns null if behavior was not found else the requested behavior
+         */
+        getBehaviorByName(name: string): Nullable<Behavior<Control3D>>;
+        /**
+         * Creates a new control
+         * @param name defines the control name
+         */
+        constructor(
+            /** Defines the control name */
+            name?: string | undefined);
+        /**
+         * Gets a string representing the class name
+         */
+        readonly typeName: string;
+        protected _getTypeName(): string;
+        /**
+         * Releases all associated resources
+         */
+        dispose(): void;
+    }
+}
+
+
+declare module BABYLON.GUI {
+    /**
+     * Class used to create containers for controls
+     */
+    class Container3D extends Control3D {
+        private _children;
+        /**
+         * Creates a new container
+         * @param name defines the container name
+         */
+        constructor(name?: string);
         readonly typeName: string;
         protected _getTypeName(): string;
+        /**
+         * Releases all associated resources
+         */
+        dispose(): void;
     }
 }

+ 202 - 1
dist/preview release/gui/babylon.gui.js

@@ -5592,15 +5592,159 @@ var BABYLON;
     })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));
 })(BABYLON || (BABYLON = {}));
 
+/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+var BABYLON;
+(function (BABYLON) {
+    var GUI;
+    (function (GUI) {
+        /**
+         * Class used to manage 3D user interface
+         */
+        var GUI3DManager = /** @class */ (function () {
+            /**
+             * Creates a new GUI3DManager
+             * @param scene
+             */
+            function GUI3DManager(scene) {
+                var _this = this;
+                this._scene = scene || BABYLON.Engine.LastCreatedScene;
+                this._sceneDisposeObserver = this._scene.onDisposeObservable.add(function () {
+                    _this._sceneDisposeObserver = null;
+                    _this._utilityLayer = null;
+                    _this.dispose();
+                });
+                this._utilityLayer = new BABYLON.UtilityLayerRenderer(this._scene);
+                this._rootContainer = new GUI.Container3D("RootContainer");
+                this._rootContainer._host = this;
+            }
+            Object.defineProperty(GUI3DManager.prototype, "scene", {
+                /** Gets the hosting scene */
+                get: function () {
+                    return this._scene;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(GUI3DManager.prototype, "rootContainer", {
+                /**
+                 * Gets the root container
+                 */
+                get: function () {
+                    return this._rootContainer;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            /**
+             * Releases all associated resources
+             */
+            GUI3DManager.prototype.dispose = function () {
+                this._rootContainer.dispose();
+                if (this._scene && this._sceneDisposeObserver) {
+                    this._scene.onDisposeObservable.remove(this._sceneDisposeObserver);
+                    this._sceneDisposeObserver = null;
+                }
+                if (this._utilityLayer) {
+                    this._utilityLayer.dispose();
+                }
+            };
+            return GUI3DManager;
+        }());
+        GUI.GUI3DManager = GUI3DManager;
+    })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));
+})(BABYLON || (BABYLON = {}));
+
 /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 var BABYLON;
 (function (BABYLON) {
     var GUI;
     (function (GUI) {
+        /**
+         * Class used as base class for controls
+         */
         var Control3D = /** @class */ (function () {
-            function Control3D() {
+            /**
+             * Creates a new control
+             * @param name defines the control name
+             */
+            function Control3D(
+            /** Defines the control name */
+            name) {
+                this.name = name;
+                // Behaviors
+                this._behaviors = new Array();
             }
+            Object.defineProperty(Control3D.prototype, "behaviors", {
+                /**
+                 * Gets the list of attached behaviors
+                 * @see http://doc.babylonjs.com/features/behaviour
+                 */
+                get: function () {
+                    return this._behaviors;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            /**
+             * Attach a behavior to the control
+             * @see http://doc.babylonjs.com/features/behaviour
+             * @param behavior defines the behavior to attach
+             * @returns the current control
+             */
+            Control3D.prototype.addBehavior = function (behavior) {
+                var _this = this;
+                var index = this._behaviors.indexOf(behavior);
+                if (index !== -1) {
+                    return this;
+                }
+                behavior.init();
+                var scene = this._host.scene;
+                if (scene.isLoading) {
+                    // We defer the attach when the scene will be loaded
+                    scene.onDataLoadedObservable.addOnce(function () {
+                        behavior.attach(_this);
+                    });
+                }
+                else {
+                    behavior.attach(this);
+                }
+                this._behaviors.push(behavior);
+                return this;
+            };
+            /**
+             * Remove an attached behavior
+             * @see http://doc.babylonjs.com/features/behaviour
+             * @param behavior defines the behavior to attach
+             * @returns the current control
+             */
+            Control3D.prototype.removeBehavior = function (behavior) {
+                var index = this._behaviors.indexOf(behavior);
+                if (index === -1) {
+                    return this;
+                }
+                this._behaviors[index].detach();
+                this._behaviors.splice(index, 1);
+                return this;
+            };
+            /**
+             * Gets an attached behavior by name
+             * @param name defines the name of the behavior to look for
+             * @see http://doc.babylonjs.com/features/behaviour
+             * @returns null if behavior was not found else the requested behavior
+             */
+            Control3D.prototype.getBehaviorByName = function (name) {
+                for (var _i = 0, _a = this._behaviors; _i < _a.length; _i++) {
+                    var behavior = _a[_i];
+                    if (behavior.name === name) {
+                        return behavior;
+                    }
+                }
+                return null;
+            };
             Object.defineProperty(Control3D.prototype, "typeName", {
+                /**
+                 * Gets a string representing the class name
+                 */
                 get: function () {
                     return this._getTypeName();
                 },
@@ -5610,12 +5754,69 @@ var BABYLON;
             Control3D.prototype._getTypeName = function () {
                 return "Control3D";
             };
+            /**
+             * Releases all associated resources
+             */
+            Control3D.prototype.dispose = function () {
+                // Behaviors
+                for (var _i = 0, _a = this._behaviors; _i < _a.length; _i++) {
+                    var behavior = _a[_i];
+                    behavior.detach();
+                }
+            };
             return Control3D;
         }());
         GUI.Control3D = Control3D;
     })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));
 })(BABYLON || (BABYLON = {}));
 
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+var BABYLON;
+(function (BABYLON) {
+    var GUI;
+    (function (GUI) {
+        /**
+         * Class used to create containers for controls
+         */
+        var Container3D = /** @class */ (function (_super) {
+            __extends(Container3D, _super);
+            /**
+             * Creates a new container
+             * @param name defines the container name
+             */
+            function Container3D(name) {
+                var _this = _super.call(this, name) || this;
+                _this._children = new Array();
+                return _this;
+            }
+            Object.defineProperty(Container3D.prototype, "typeName", {
+                get: function () {
+                    return this._getTypeName();
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Container3D.prototype._getTypeName = function () {
+                return "Container3D";
+            };
+            /**
+             * Releases all associated resources
+             */
+            Container3D.prototype.dispose = function () {
+                for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
+                    var control = _a[_i];
+                    control.dispose();
+                }
+                this._children = [];
+                _super.prototype.dispose.call(this);
+            };
+            return Container3D;
+        }(GUI.Control3D));
+        GUI.Container3D = Container3D;
+    })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));
+})(BABYLON || (BABYLON = {}));
+
     
 
     return BABYLON.GUI;

File diff suppressed because it is too large
+ 2 - 2
dist/preview release/gui/babylon.gui.min.js


+ 98 - 1
dist/preview release/gui/babylon.gui.module.d.ts

@@ -970,8 +970,105 @@ declare module BABYLON.GUI {
 
 
 declare module BABYLON.GUI {
-    class Control3D {
+    /**
+     * Class used to manage 3D user interface
+     */
+    class GUI3DManager implements BABYLON.IDisposable {
+        private _scene;
+        private _sceneDisposeObserver;
+        private _utilityLayer;
+        private _rootContainer;
+        /** Gets the hosting scene */
+        readonly scene: Scene;
+        /**
+         * Creates a new GUI3DManager
+         * @param scene
+         */
+        constructor(scene?: Scene);
+        /**
+         * Gets the root container
+         */
+        readonly rootContainer: Container3D;
+        /**
+         * Releases all associated resources
+         */
+        dispose(): void;
+    }
+}
+
+
+declare module BABYLON.GUI {
+    /**
+     * Class used as base class for controls
+     */
+    class Control3D implements IDisposable, IBehaviorAware<Control3D> {
+        /** Defines the control name */
+        name: string | undefined;
+        /** @hidden */
+        _host: GUI3DManager;
+        private _behaviors;
+        /**
+         * Gets the list of attached behaviors
+         * @see http://doc.babylonjs.com/features/behaviour
+         */
+        readonly behaviors: Behavior<Control3D>[];
+        /**
+         * Attach a behavior to the control
+         * @see http://doc.babylonjs.com/features/behaviour
+         * @param behavior defines the behavior to attach
+         * @returns the current control
+         */
+        addBehavior(behavior: Behavior<Control3D>): Control3D;
+        /**
+         * Remove an attached behavior
+         * @see http://doc.babylonjs.com/features/behaviour
+         * @param behavior defines the behavior to attach
+         * @returns the current control
+         */
+        removeBehavior(behavior: Behavior<Control3D>): Control3D;
+        /**
+         * Gets an attached behavior by name
+         * @param name defines the name of the behavior to look for
+         * @see http://doc.babylonjs.com/features/behaviour
+         * @returns null if behavior was not found else the requested behavior
+         */
+        getBehaviorByName(name: string): Nullable<Behavior<Control3D>>;
+        /**
+         * Creates a new control
+         * @param name defines the control name
+         */
+        constructor(
+            /** Defines the control name */
+            name?: string | undefined);
+        /**
+         * Gets a string representing the class name
+         */
+        readonly typeName: string;
+        protected _getTypeName(): string;
+        /**
+         * Releases all associated resources
+         */
+        dispose(): void;
+    }
+}
+
+
+declare module BABYLON.GUI {
+    /**
+     * Class used to create containers for controls
+     */
+    class Container3D extends Control3D {
+        private _children;
+        /**
+         * Creates a new container
+         * @param name defines the container name
+         */
+        constructor(name?: string);
         readonly typeName: string;
         protected _getTypeName(): string;
+        /**
+         * Releases all associated resources
+         */
+        dispose(): void;
     }
 }

File diff suppressed because it is too large
+ 13 - 13
dist/preview release/viewer/babylon.viewer.js


+ 1 - 5
dist/preview release/viewer/babylon.viewer.max.js

@@ -17745,12 +17745,8 @@ var BABYLON;
             behavior.init();
             if (this._scene.isLoading) {
                 // We defer the attach when the scene will be loaded
-                var observer = this._scene.onDataLoadedObservable.add(function () {
+                this._scene.onDataLoadedObservable.addOnce(function () {
                     behavior.attach(_this);
-                    setTimeout(function () {
-                        // Need to use a timeout to avoid removing an observer while iterating the list of observers
-                        _this._scene.onDataLoadedObservable.remove(observer);
-                    }, 0);
                 });
             }
             else {

+ 30 - 0
gui/src/3D/controls/button3D.ts

@@ -0,0 +1,30 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GUI {
+    /**
+     * Class used to create a button in 3D
+     */
+    export class Button3D extends Control3D {
+        
+        /**
+         * Creates a new button
+         * @param name defines the control name
+         */
+        constructor(name?: string) {
+            super(name);
+        }
+
+        protected _getTypeName(): string {
+            return "Button3D";
+        }        
+
+        // Mesh association
+        protected _createMesh(scene: Scene): Mesh {
+            return MeshBuilder.CreateBox(this.name + "Mesh", {
+                width: 1.0, 
+                height: 1.0,
+                depth: 0.1
+            }, scene);            
+        }
+    }
+}

+ 82 - 0
gui/src/3D/controls/container3D.ts

@@ -0,0 +1,82 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GUI {
+    /**
+     * Class used to create containers for controls
+     */
+    export class Container3D extends Control3D {
+        private _children = new Array<Control3D>();
+
+        /**
+         * Creates a new container
+         * @param name defines the container name
+         */
+        constructor(name?: string) {
+            super(name);
+        }
+
+        /**
+         * Gets a boolean indicating if the given control is in the children of this control
+         * @param control defines the control to check
+         * @returns true if the control is in the child list
+         */
+        public containsControl(control: Control3D): boolean {
+            return this._children.indexOf(control) !== -1;
+        }
+
+        /**
+         * Adds a control to the children of this control
+         * @param control defines the control to add
+         * @returns the current container
+         */
+        public addControl(control: Control3D): Container3D {
+           var index = this._children.indexOf(control);
+
+            if (index !== -1) {
+                return this;
+            }
+            control.parent = this;
+            control._host = this._host;
+
+            if (this._host.utilityLayer) {
+                control.getAttachedMesh(this._host.utilityLayer.utilityLayerScene);
+            }
+
+            return this;
+        }
+
+        /**
+         * Removes the control from the children of this control
+         * @param control defines the control to remove
+         * @returns the current container
+         */
+        public removeControl(control: Control3D): Container3D {
+            var index = this._children.indexOf(control);
+
+            if (index !== -1) {
+                this._children.splice(index, 1);
+
+                control.parent = null;
+            }
+
+            return this;
+        }
+
+        protected _getTypeName(): string {
+            return "Container3D";
+        }
+        
+        /**
+         * Releases all associated resources
+         */
+        public dispose() {
+            for (var control of this._children) {
+                control.dispose();
+            }
+
+            this._children = [];
+
+            super.dispose();
+        }
+    }
+}

+ 133 - 1
gui/src/3D/controls/control3D.ts

@@ -1,7 +1,105 @@
 /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
 module BABYLON.GUI {
-    export class Control3D {
+    /**
+     * Class used as base class for controls
+     */
+    export class Control3D implements IDisposable, IBehaviorAware<Control3D> {
+        /** @hidden */
+        public _host: GUI3DManager;
+        private _mesh: Nullable<Mesh>;
+
+        /**
+         * Gets or sets the parent container
+         */
+        public parent: Nullable<Container3D>;
+
+        // Behaviors
+        private _behaviors = new Array<Behavior<Control3D>>();
+
+        /**
+         * Gets the list of attached behaviors
+         * @see http://doc.babylonjs.com/features/behaviour
+         */
+        public get behaviors(): Behavior<Control3D>[] {
+            return this._behaviors;
+        }        
+
+        /**
+         * Attach a behavior to the control
+         * @see http://doc.babylonjs.com/features/behaviour
+         * @param behavior defines the behavior to attach
+         * @returns the current control
+         */
+        public addBehavior(behavior: Behavior<Control3D>): Control3D {
+            var index = this._behaviors.indexOf(behavior);
+
+            if (index !== -1) {
+                return this;
+            }
+
+            behavior.init();
+            let scene = this._host.scene;
+            if (scene.isLoading) {
+                // We defer the attach when the scene will be loaded
+                scene.onDataLoadedObservable.addOnce(() => {
+                    behavior.attach(this);
+                });
+            } else {
+                behavior.attach(this);
+            }
+            this._behaviors.push(behavior);
+
+            return this;
+        }
+
+        /**
+         * Remove an attached behavior
+         * @see http://doc.babylonjs.com/features/behaviour
+         * @param behavior defines the behavior to attach
+         * @returns the current control
+         */
+        public removeBehavior(behavior: Behavior<Control3D>): Control3D {
+            var index = this._behaviors.indexOf(behavior);
+
+            if (index === -1) {
+                return this;
+            }
+
+            this._behaviors[index].detach();
+            this._behaviors.splice(index, 1);
+
+            return this;
+        }        
+
+        /**
+         * Gets an attached behavior by name
+         * @param name defines the name of the behavior to look for
+         * @see http://doc.babylonjs.com/features/behaviour
+         * @returns null if behavior was not found else the requested behavior
+         */
+        public getBehaviorByName(name: string): Nullable<Behavior<Control3D>> {
+            for (var behavior of this._behaviors) {
+                if (behavior.name === name) {
+                    return behavior;
+                }
+            }
+
+            return null;
+        }        
+
+        /**
+         * Creates a new control
+         * @param name defines the control name
+         */
+        constructor(
+            /** Defines the control name */
+            public name?: string) {
+        }
+
+        /**
+         * Gets a string representing the class name
+         */
         public get typeName(): string {
             return this._getTypeName();
         }
@@ -9,5 +107,39 @@ module BABYLON.GUI {
         protected _getTypeName(): string {
             return "Control3D";
         }
+
+        /**
+         * Get the attached mesh used to render the control
+         * @param scene defines the scene where the mesh must be attached
+         * @returns the attached mesh or null if none
+         */        
+        public getAttachedMesh(scene: Scene): Nullable<Mesh> {
+            if (!this._mesh) {
+                this._mesh = this._createMesh(scene);
+            }
+
+            return this._mesh;
+        }
+
+        /**
+         * Mesh creation.
+         * Can be overriden by children
+         * @param scene defines the scene where the mesh must be attached
+         * @returns the attached mesh or null if none
+         */
+        protected _createMesh(scene: Scene): Nullable<Mesh> {
+            // Do nothing by default
+            return null;
+        }
+
+        /**
+         * Releases all associated resources
+         */
+        public dispose() {
+            // Behaviors
+            for (var behavior of this._behaviors) {
+                behavior.detach();
+            }
+        }
     }
 }

+ 92 - 0
gui/src/3D/gui3DManager.ts

@@ -0,0 +1,92 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GUI {
+    /**
+     * Class used to manage 3D user interface
+     */
+    export class GUI3DManager implements BABYLON.IDisposable {
+        private _scene: Scene;
+        private _sceneDisposeObserver: Nullable<Observer<Scene>>;
+        private _utilityLayer: Nullable<UtilityLayerRenderer>;
+        private _rootContainer: Container3D;
+
+        /** Gets the hosting scene */
+        public get scene(): Scene {
+            return this._scene;
+        }
+
+        public get utilityLayer(): Nullable<UtilityLayerRenderer> {
+            return this._utilityLayer;
+        }
+
+        /**
+         * Creates a new GUI3DManager
+         * @param scene 
+         */
+        public constructor(scene?: Scene) {
+            this._scene = scene || Engine.LastCreatedScene!;
+            this._sceneDisposeObserver = this._scene.onDisposeObservable.add(() => {
+                this._sceneDisposeObserver = null;
+                this._utilityLayer = null;
+                this.dispose();
+            })
+
+            this._utilityLayer = new UtilityLayerRenderer(this._scene);
+
+            this._rootContainer = new Container3D("RootContainer");
+            this._rootContainer._host = this;
+        }
+
+        /**
+         * Gets the root container
+         */
+        public get rootContainer(): Container3D {
+            return this._rootContainer;
+        }
+
+        /**
+         * Gets a boolean indicating if the given control is in the root child list
+         * @param control defines the control to check
+         * @returns true if the control is in the root child list
+         */
+        public containsControl(control: Control3D): boolean {
+            return this._rootContainer.containsControl(control);
+        }
+
+        /**
+         * Adds a control to the root child list
+         * @param control defines the control to add
+         * @returns the current manager
+         */
+        public addControl(control: Control3D): GUI3DManager {
+           this._rootContainer.addControl(control);
+           return this;
+        }
+
+        /**
+         * Removes the control from the root child list
+         * @param control defines the control to remove
+         * @returns the current container
+         */
+        public removeControl(control: Control3D): GUI3DManager {
+            this._rootContainer.removeControl(control);
+            return this;
+        }        
+
+        /**
+         * Releases all associated resources
+         */
+        public dispose() {
+            this._rootContainer.dispose();
+
+            if (this._scene && this._sceneDisposeObserver) {
+                this._scene.onDisposeObservable.remove(this._sceneDisposeObserver);
+                this._sceneDisposeObserver = null;
+            }
+
+            if (this._utilityLayer) {
+                this._utilityLayer.dispose();
+            }
+        }
+    }
+}

+ 8 - 2
src/Behaviors/babylon.behavior.ts

@@ -1,9 +1,15 @@
 module BABYLON {
-    export interface Behavior<T extends Node> {
+    export interface Behavior<T> {
         name: string;
 
         init(): void
-        attach(node: T): void;
+        attach(target: T): void;
         detach(): void;
     }
+
+    export interface IBehaviorAware<T> {
+        addBehavior(behavior: Behavior<T>): T
+        removeBehavior(behavior: Behavior<T>): T;
+        getBehaviorByName(name: string): Nullable<Behavior<T>>;
+    }
 }

+ 2 - 6
src/babylon.node.ts

@@ -3,7 +3,7 @@
     /**
      * Node is the basic class for all scene objects (Mesh, Light Camera).
      */
-    export class Node {
+    export class Node implements IBehaviorAware<Node> {
         /**
          * Gets or sets the name of the node
          */
@@ -198,12 +198,8 @@
             behavior.init();
             if (this._scene.isLoading) {
                 // We defer the attach when the scene will be loaded
-                var observer = this._scene.onDataLoadedObservable.add(() => {
+                this._scene.onDataLoadedObservable.addOnce(() => {
                     behavior.attach(this);
-                    setTimeout(() => {
-                        // Need to use a timeout to avoid removing an observer while iterating the list of observers
-                        this._scene.onDataLoadedObservable.remove(observer);
-                    }, 0);
                 });
             } else {
                 behavior.attach(this);