Jelajahi Sumber

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

David Catuhe 7 tahun lalu
induk
melakukan
fbfdbc0d1d

+ 1 - 0
Tools/Gulp/config.json

@@ -1078,6 +1078,7 @@
                 "../../src/Gizmos/babylon.positionGizmo.js",
                 "../../src/Gizmos/babylon.rotationGizmo.js",
                 "../../src/Gizmos/babylon.scaleGizmo.js",
+                "../../src/Gizmos/babylon.boundingBoxGizmo.js",
                 "../../src/Gizmos/babylon.gizmoManager.js"
             ],
             "dependUpon": [

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

@@ -6,7 +6,7 @@
 - Added new `MixMaterial` to the Materials Library allowing to mix up to 8 textures ([julien-moreau](https://github.com/julien-moreau))
 - Added new `PhotoDome` object to display 360 photos. [Demo](https://www.babylonjs-playground.com/#14KRGG#0) ([SzeyinLee](https://github.com/SzeyinLee))
 - New GUI 3D controls toolset. [Complete doc + demos](http://doc.babylonjs.com/how_to/gui3d) ([Deltakosh](https://github.com/deltakosh))
-- Added [Environmnent Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
+- Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
 - New GUI control: the [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
 
 ## Updates
@@ -27,7 +27,7 @@
 - UtilityLayer class to render another scene as a layer on top of an existing scene ([TrevorDev](https://github.com/TrevorDev))
 - AnimationGroup has now onAnimationGroupEnd observable ([RaananW](https://github.com/RaananW))
 - Pointer drag behavior to enable drag and drop with mouse or 6dof controller on a mesh ([TrevorDev](https://github.com/TrevorDev))
-- Gizmo and gizmoManager class used to manipulate meshes in a scene, position, rotation, scale gizmos ([TrevorDev](https://github.com/TrevorDev))
+- Gizmo and GizmoManager classes used to manipulate meshes in a scene. Position, rotation, scale, and bounding box gizmos ([TrevorDev](https://github.com/TrevorDev))
 - Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
 - AssetsManager tasks will only run when their state is INIT. It is now possible to remove a task from the assets manager ([RaananW](https://github.com/RaananW))
 - Sprite isVisible field ([TrevorDev](https://github.com/TrevorDev))
@@ -42,7 +42,7 @@
 ### Viewer
 
 - No fullscreen button on small devices ([RaananW](https://github.com/RaananW))
-- Nav-Bar is now disaplayed on fullscreen per default ([RaananW](https://github.com/RaananW))
+- Nav-Bar is now displayed on fullscreen per default ([RaananW](https://github.com/RaananW))
 - Viewer configuration supports deprecated values using the new configurationCompatibility processor  ([RaananW](https://github.com/RaananW))
 - Shadows will only render while models are entering the scene or animating ([RaananW](https://github.com/RaananW))
 - Support for model drag and drop onto the canvas ([RaananW](https://github.com/RaananW))
@@ -62,7 +62,7 @@
 
 ### Core Engine
 
-- Fixed ```shadowEnabled``` property on lights. Shadows are not visble anymore when disabled ([sebavan](http://www.github.com/sebavan))
+- Fixed ```shadowEnabled``` property on lights. Shadows are not visible anymore when disabled ([sebavan](http://www.github.com/sebavan))
 - Physics `unregisterOnPhysicsCollide` didn't remove callback correctly [#4291](https://github.com/BabylonJS/Babylon.js/issues/4291) ([RaananW](https://github.com/RaananW))
 - Added missing getter and setter for global exposure in ColorCurves ([RaananW](https://github.com/RaananW))
 - Fixed an issue with view matrix when `ArcRotateCamera` was used with collisions ([Deltakosh](https://github.com/deltakosh))

+ 28 - 0
proceduralTexturesLibrary/src/brick/babylon.brickProceduralTexture.ts

@@ -19,6 +19,7 @@ module BABYLON {
             this.setColor3("jointColor", this._jointColor);
         }
 
+        @serialize()
         public get numberOfBricksHeight(): number {
             return this._numberOfBricksHeight;
         }
@@ -28,6 +29,7 @@ module BABYLON {
             this.updateShaderUniforms();
         }
 
+        @serialize()
         public get numberOfBricksWidth(): number {
             return this._numberOfBricksWidth;
         }
@@ -37,6 +39,7 @@ module BABYLON {
             this.updateShaderUniforms();
         }
 
+        @serializeAsColor3()
         public get jointColor(): Color3 {
             return this._jointColor;
         }
@@ -46,6 +49,7 @@ module BABYLON {
             this.updateShaderUniforms();
         }
 
+        @serializeAsColor3()
         public get brickColor(): Color3 {
             return this._brickColor;
         }
@@ -54,5 +58,29 @@ module BABYLON {
             this._brickColor = value;
             this.updateShaderUniforms();
         }
+
+        /**
+         * Serializes this brick procedural texture
+         * @returns a serialized brick procedural texture object
+         */
+        public serialize(): any {
+            var serializationObject = SerializationHelper.Serialize(this, super.serialize());
+            serializationObject.customType = "BABYLON.BrickProceduralTexture";
+
+            return serializationObject;
+        }
+
+        /**
+         * Creates a Brick Procedural Texture from parsed brick procedural texture data
+         * @param parsedTexture defines parsed texture data
+         * @param scene defines the current scene
+         * @param rootUrl defines the root URL containing brick procedural texture information
+         * @returns a parsed Brick Procedural Texture
+         */
+        public static Parse(parsedTexture: any, scene: Scene, rootUrl: string): BrickProceduralTexture {
+            var texture = SerializationHelper.Parse(() => new BrickProceduralTexture(parsedTexture.name, parsedTexture._size, scene, undefined, parsedTexture._generateMipMaps), parsedTexture, scene, rootUrl);
+
+            return texture;
+        }
     }	
 }

+ 44 - 20
src/Behaviors/Mesh/babylon.pointerDragBehavior.ts

@@ -9,11 +9,18 @@ module BABYLON {
         private _pointerObserver:Nullable<Observer<PointerInfo>>;
         private static _planeScene:Scene;
         private _draggingID = -1;
+        // Debug mode will display drag planes to help visualize behavior
+        private _debugMode = false;
+        private _maxDragAngle = Math.PI/5;
         
         /**
          *  Fires each time the attached mesh is dragged with the pointer
+         *  * delta between last drag position and current drag position in world space
+         *  * dragDistance along the drag axis
+         *  * dragPlaneNormal normal of the current drag plane used during the drag
+         *  * dragPlanePoint in world space where the drag intersects the drag plane
          */
-        public onDragObservable = new Observable<{delta:Vector3, dragPlanePoint:Vector3}>()
+        public onDragObservable = new Observable<{delta:Vector3, dragPlanePoint:Vector3, dragPlaneNormal:Vector3, dragDistance:number}>()
         /**
          *  Fires each time a drag begins (eg. mouse down on mesh)
          */
@@ -38,10 +45,13 @@ module BABYLON {
         
         /**
          * Creates a pointer drag behavior that can be attached to a mesh
-         * @param options The drag axis or normal of the plane that will be dragged across.
+         * @param options The drag axis or normal of the plane that will be dragged across. If no options are specified the drag plane will always face the ray's origin (eg. camera)
          */
         constructor(private options:{dragAxis?:Vector3, dragPlaneNormal?:Vector3}){
             var optionCount = 0;
+            if(options === undefined){
+                options = {}
+            }
             if(options.dragAxis){
                 optionCount++;
             }
@@ -51,9 +61,6 @@ module BABYLON {
             if(optionCount > 1){
                 throw "Multiple drag modes specified in dragBehavior options. Only one expected";
             }
-            if(optionCount < 1){
-                throw "At least one drag mode option must be specified";
-            }
         }
 
         /**
@@ -78,15 +85,20 @@ module BABYLON {
 
             // Initialize drag plane to not interfere with existing scene
             if(!PointerDragBehavior._planeScene){
-                PointerDragBehavior._planeScene = new BABYLON.Scene(this._scene.getEngine())
-                this._scene.getEngine().scenes.pop();
+                if(this._debugMode){
+                    PointerDragBehavior._planeScene = this._scene;
+                }else{
+                    PointerDragBehavior._planeScene = new BABYLON.Scene(this._scene.getEngine());
+                    this._scene.getEngine().scenes.pop();
+                }
             }
-            this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", 1000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
+            this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", this._debugMode ? 1 : 10000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
 
             // State of the drag
             var dragging = false;
             var lastPosition = new BABYLON.Vector3(0,0,0);
             var delta = new BABYLON.Vector3(0,0,0);
+            var dragLength = 0;
 
             var pickPredicate = (m:AbstractMesh)=>{
                 return this._attachedNode == m || m.isDescendantOf(this._attachedNode)
@@ -117,21 +129,30 @@ module BABYLON {
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE){
                     if(this._draggingID == (<PointerEvent>pointerInfo.event).pointerId && dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray){
                         var pickedPoint = this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
-                        this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
+                        
+                         // Get angle between drag plane and ray. Only update the drag plane at non steep angles to avoid jumps in delta position
+                        var angle = Math.acos(Vector3.Dot(this._dragPlane.forward, pointerInfo.pickInfo.ray.direction));
+                        if(angle < this._maxDragAngle){
+                            this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
+                        }
+                        
                         if (pickedPoint) {
                             // depending on the drag mode option drag accordingly
                             if(this.options.dragAxis){
-                                //get the closest point on the dragaxis from the selected mesh to the picked point location
-                                // https://www.opengl.org/discussion_boards/showthread.php/159717-Closest-point-on-a-Vector-to-a-point
-                                this.options.dragAxis.scaleToRef(BABYLON.Vector3.Dot(pickedPoint.subtract(lastPosition), this.options.dragAxis), delta);
+                                // Convert local drag axis to world
+                                var worldDragAxis = Vector3.TransformCoordinates(this.options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix());
+
+                                // Project delta drag from the drag plane onto the drag axis
+                                dragLength = BABYLON.Vector3.Dot(pickedPoint.subtract(lastPosition), worldDragAxis)
+                                worldDragAxis.scaleToRef(dragLength, delta);
                             }else{
+                                dragLength = delta.length();
                                 pickedPoint.subtractToRef(lastPosition, delta);
                             }
                             if(this.moveAttached){
                                 (<Mesh>this._attachedNode).position.addInPlace(delta);
-                                
                             }
-                            this.onDragObservable.notifyObservers({delta: delta, dragPlanePoint: pickedPoint});
+                            this.onDragObservable.notifyObservers({dragDistance: dragLength, delta: delta, dragPlanePoint: pickedPoint, dragPlaneNormal: this._dragPlane.forward});
                             lastPosition.copyFrom(pickedPoint);
                         }
                     }
@@ -153,13 +174,13 @@ module BABYLON {
 
         // Position the drag plane based on the attached mesh position, for single axis rotate the plane along the axis to face the camera
         private _updateDragPlanePosition(ray:Ray){
-            var pointA = this._dragPlaneParent ? this._dragPlaneParent.position : (<Mesh>this._attachedNode).position // center
+            var pointA = this._dragPlaneParent ? this._dragPlaneParent.absolutePosition : (<Mesh>this._attachedNode).absolutePosition;
             if(this.options.dragAxis){
-                var camPos = ray.origin;
+                var localAxis = Vector3.TransformCoordinates(this.options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix());
 
                 // Calculate plane normal in direction of camera but perpendicular to drag axis
-                var pointB = pointA.add(this.options.dragAxis); // towards drag axis
-                var pointC = pointA.add(camPos.subtract(pointA).normalize()); // towards camera
+                var pointB = pointA.add(localAxis); // towards drag axis
+                var pointC = pointA.add(ray.origin.subtract(pointA).normalize()); // towards camera
                 // Get perpendicular line from direction to camera and drag axis
                 var lineA = pointB.subtract(pointA);
                 var lineB = pointC.subtract(pointA);
@@ -168,10 +189,13 @@ module BABYLON {
                 var norm = BABYLON.Vector3.Cross(lineA, perpLine).normalize();
 
                 this._dragPlane.position.copyFrom(pointA);
-                this._dragPlane.lookAt(pointA.add(norm));
+                this._dragPlane.lookAt(pointA.subtract(norm));
             }else if(this.options.dragPlaneNormal){
                 this._dragPlane.position.copyFrom(pointA);
-                this._dragPlane.lookAt(pointA.add(this.options.dragPlaneNormal));
+                this._dragPlane.lookAt(pointA.subtract(this.options.dragPlaneNormal));
+            }else{
+                this._dragPlane.position.copyFrom(pointA);
+                this._dragPlane.lookAt(ray.origin);
             }
             this._dragPlane.computeWorldMatrix(true);
         }

+ 1 - 1
src/Gizmos/babylon.axisDragGizmo.ts

@@ -35,7 +35,7 @@ module BABYLON {
             this._rootMesh.lookAt(this._rootMesh.position.subtract(dragAxis));
 
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
+            this._dragBehavior = new PointerDragBehavior({dragAxis: new BABYLON.Vector3(0,0,1)});
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
             this._dragBehavior.onDragObservable.add((event)=>{

+ 1 - 1
src/Gizmos/babylon.axisScaleGizmo.ts

@@ -35,7 +35,7 @@ module BABYLON {
             this._rootMesh.lookAt(this._rootMesh.position.subtract(dragAxis));
 
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
+            this._dragBehavior = new PointerDragBehavior({dragAxis: new BABYLON.Vector3(0,0,1)});
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
 

+ 224 - 0
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -0,0 +1,224 @@
+module BABYLON {
+    /**
+     * Bounding box gizmo
+     */
+    export class BoundingBoxGizmo extends Gizmo {
+        private _lineBoundingBox:AbstractMesh;
+        private _rotateSpheresParent:AbstractMesh;
+        private _scaleBoxesParent:AbstractMesh;
+        private _boundingDimensions = new BABYLON.Vector3(1,1,1);
+        private _renderObserver:Nullable<Observer<Scene>> = null;
+
+        /**
+         * Creates an BoundingBoxGizmo
+         * @param gizmoLayer The utility layer the gizmo will be added to
+         * @param color The color of the gizmo
+         */
+        constructor(gizmoLayer:UtilityLayerRenderer, color:Color3){
+            super(gizmoLayer);
+
+            // Do not update the gizmo's scale so it has a fixed size to the object its attached to
+            this._updateScale = false;
+
+            // Create Material
+            var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
+            coloredMaterial.disableLighting = true;
+            coloredMaterial.emissiveColor = color;
+
+            // Build bounding box out of lines
+            this._lineBoundingBox = new BABYLON.AbstractMesh("", gizmoLayer.utilityLayerScene);
+            this._lineBoundingBox.rotationQuaternion = new BABYLON.Quaternion();
+            var lines = []
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(0,0,0), new BABYLON.Vector3(this._boundingDimensions.x,0,0)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(0,0,0), new BABYLON.Vector3(0,this._boundingDimensions.y,0)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(0,0,0), new BABYLON.Vector3(0,0,this._boundingDimensions.z)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(this._boundingDimensions.x,0,0), new BABYLON.Vector3(this._boundingDimensions.x,this._boundingDimensions.y,0)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(this._boundingDimensions.x,0,0), new BABYLON.Vector3(this._boundingDimensions.x,0,this._boundingDimensions.z)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(0,this._boundingDimensions.y,0), new BABYLON.Vector3(this._boundingDimensions.x,this._boundingDimensions.y,0)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(0,this._boundingDimensions.y,0), new BABYLON.Vector3(0,this._boundingDimensions.y,this._boundingDimensions.z)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(0,0,this._boundingDimensions.z), new BABYLON.Vector3(this._boundingDimensions.x,0,this._boundingDimensions.z)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(0,0,this._boundingDimensions.z), new BABYLON.Vector3(0,this._boundingDimensions.y,this._boundingDimensions.z)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(this._boundingDimensions.x,this._boundingDimensions.y,this._boundingDimensions.z), new BABYLON.Vector3(0,this._boundingDimensions.y,this._boundingDimensions.z)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(this._boundingDimensions.x,this._boundingDimensions.y,this._boundingDimensions.z), new BABYLON.Vector3(this._boundingDimensions.x,0,this._boundingDimensions.z)]}, gizmoLayer.utilityLayerScene));
+            lines.push(BABYLON.MeshBuilder.CreateLines("lines", {points: [new BABYLON.Vector3(this._boundingDimensions.x,this._boundingDimensions.y,this._boundingDimensions.z), new BABYLON.Vector3(this._boundingDimensions.x,this._boundingDimensions.y,0)]}, gizmoLayer.utilityLayerScene));
+            lines.forEach((l)=>{
+                l.color = color
+                l.position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2))
+                l.isPickable=false;
+                this._lineBoundingBox.addChild(l)
+            })
+            this._rootMesh.addChild(this._lineBoundingBox);
+
+            // Create rotation spheres
+            this._rotateSpheresParent = new BABYLON.AbstractMesh("", gizmoLayer.utilityLayerScene);
+            this._rotateSpheresParent.rotationQuaternion = new Quaternion();
+            for(let i=0;i<12;i++){
+                let sphere = BABYLON.MeshBuilder.CreateSphere("", {diameter: 0.1}, gizmoLayer.utilityLayerScene);
+                sphere.rotationQuaternion = new Quaternion();
+                sphere.material = coloredMaterial;
+
+                // Drag behavior
+                var _dragBehavior = new PointerDragBehavior({});
+                _dragBehavior.moveAttached = false;
+                sphere.addBehavior(_dragBehavior);
+                _dragBehavior.onDragObservable.add((event)=>{
+                    if(this.attachedMesh){
+                        var worldDragDirection = sphere.forward;
+
+                        // Project the world right on to the drag plane
+                        var toSub = event.dragPlaneNormal.scale(Vector3.Dot(event.dragPlaneNormal, worldDragDirection));
+                        var dragAxis = worldDragDirection.subtract(toSub).normalizeToNew();
+
+                        // project drag delta on to the resulting drag axis and rotate based on that
+                        var projectDist = Vector3.Dot(dragAxis, event.delta);
+
+                        // Rotate based on axis
+                        if(i>=8){
+                            this.attachedMesh.rotation.z -= projectDist;
+                        }else if(i>=4){
+                            this.attachedMesh.rotation.y -= projectDist;
+                        }else{
+                            this.attachedMesh.rotation.x -= projectDist;
+                        }
+                    }
+                });
+
+                // Selection/deselection
+                _dragBehavior.onDragStartObservable.add(()=>{
+                    this._selectNode(sphere)
+                })
+                _dragBehavior.onDragEndObservable.add(()=>{
+                    this._selectNode(null)
+                })
+
+                this._rotateSpheresParent.addChild(sphere);
+            }
+            this._rootMesh.addChild(this._rotateSpheresParent);
+
+            // Create scale cubes
+            this._scaleBoxesParent = new BABYLON.AbstractMesh("", gizmoLayer.utilityLayerScene);
+            this._scaleBoxesParent.rotationQuaternion = new Quaternion();
+            for(var i=0;i<2;i++){
+                for(var j=0;j<2;j++){
+                    for(var k=0;k<2;k++){
+                        let box = BABYLON.MeshBuilder.CreateBox("", {size: 0.1}, gizmoLayer.utilityLayerScene);
+                        box.material = coloredMaterial;
+
+                        // Dragging logic
+                        let dragAxis = new BABYLON.Vector3(i==0?-1:1,j==0?-1:1,k==0?-1:1);
+                        var _dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
+                        _dragBehavior.moveAttached = false;
+                        box.addBehavior(_dragBehavior);
+                        _dragBehavior.onDragObservable.add((event)=>{
+                            if(this.attachedMesh){
+                                // Current boudning box dimensions
+                                var boundingInfo = this.attachedMesh.getBoundingInfo().boundingBox;
+                                var boundBoxDimensions = boundingInfo.maximum.subtract(boundingInfo.minimum).multiplyInPlace(this.attachedMesh.scaling);
+                                
+                                // Get the change in bounding box size/2 and add this to the mesh's position to offset from scaling with center pivot point
+                                var deltaScale = new Vector3(event.dragDistance,event.dragDistance,event.dragDistance);
+                                var scaleRatio = deltaScale.divide(this.attachedMesh.scaling).scaleInPlace(0.5);
+                                var moveDirection = boundBoxDimensions.multiply(scaleRatio).multiplyInPlace(dragAxis);
+                                var worldMoveDirection = Vector3.TransformCoordinates(moveDirection, this.attachedMesh.getWorldMatrix().getRotationMatrix());
+                                
+                                // Update scale and position
+                                this.attachedMesh.scaling.addInPlace(deltaScale);
+                                this.attachedMesh.position.addInPlace(worldMoveDirection);
+                            }
+                        })
+
+                        // Selection/deselection
+                        _dragBehavior.onDragStartObservable.add(()=>{
+                            this._selectNode(box)
+                        })
+                        _dragBehavior.onDragEndObservable.add(()=>{
+                            this._selectNode(null)
+                        })
+
+                        this._scaleBoxesParent.addChild(box);
+                    }
+                }
+            }
+            this._rootMesh.addChild(this._scaleBoxesParent);
+
+            // Update bounding box positions
+            this._renderObserver = this.gizmoLayer.originalScene.onBeforeRenderObservable.add(()=>{
+                this._updateBoundingBox();
+            })
+            this._updateBoundingBox();
+        }
+
+        private _selectNode(selectedMesh:Nullable<Mesh>){
+            this._rotateSpheresParent.getChildMeshes()
+            .concat(this._scaleBoxesParent.getChildMeshes()).forEach((m,i)=>{
+                m.isVisible = (!selectedMesh || m == selectedMesh);
+            })
+        }
+
+        private _updateBoundingBox(){   
+            if(this.attachedMesh){
+                // Update bounding dimensions/positions
+                var boundingInfo = this.attachedMesh.getBoundingInfo().boundingBox;
+                var boundBoxDimensions = boundingInfo.maximum.subtract(boundingInfo.minimum).multiplyInPlace(this.attachedMesh.scaling);
+                this._boundingDimensions.copyFrom(boundBoxDimensions);
+                this._lineBoundingBox.scaling.copyFrom(this._boundingDimensions);
+                if(!this.attachedMesh.rotationQuaternion){
+                    this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
+                }
+                this._lineBoundingBox.rotationQuaternion!.copyFrom(this.attachedMesh.rotationQuaternion);
+                this._rotateSpheresParent.rotationQuaternion!.copyFrom(this.attachedMesh.rotationQuaternion);
+                this._scaleBoxesParent.rotationQuaternion!.copyFrom(this.attachedMesh.rotationQuaternion);
+            }
+
+            // Update rotation sphere locations
+            var rotateSpheres = this._rotateSpheresParent.getChildMeshes();
+            for(var i=0;i<3;i++){
+                for(var j=0;j<2;j++){
+                    for(var k=0;k<2;k++){
+                        var index= ((i*4)+(j*2))+k
+                        if(i==0){
+                            rotateSpheres[index].position.set(this._boundingDimensions.x/2,this._boundingDimensions.y*j,this._boundingDimensions.z*k);
+                            rotateSpheres[index].position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2));
+                            rotateSpheres[index].lookAt(Vector3.Cross(Vector3.Right(), rotateSpheres[index].position.normalizeToNew()).normalizeToNew().add(rotateSpheres[index].position));
+                        }
+                        if(i==1){
+                            rotateSpheres[index].position.set(this._boundingDimensions.x*j,this._boundingDimensions.y/2,this._boundingDimensions.z*k);
+                            rotateSpheres[index].position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2));
+                            rotateSpheres[index].lookAt(Vector3.Cross(Vector3.Up(), rotateSpheres[index].position.normalizeToNew()).normalizeToNew().add(rotateSpheres[index].position));
+                        }
+                        if(i==2){
+                            rotateSpheres[index].position.set(this._boundingDimensions.x*j,this._boundingDimensions.y*k,this._boundingDimensions.z/2);
+                            rotateSpheres[index].position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2));
+                            rotateSpheres[index].lookAt(Vector3.Cross(Vector3.Forward(), rotateSpheres[index].position.normalizeToNew()).normalizeToNew().add(rotateSpheres[index].position));
+                        }
+                    }
+                }
+            }
+
+            // Update scale box locations
+            var scaleBoxes = this._scaleBoxesParent.getChildMeshes();
+            for(var i=0;i<2;i++){
+                for(var j=0;j<2;j++){
+                    for(var k=0;k<2;k++){
+                        var index= ((i*4)+(j*2))+k;
+                        if(scaleBoxes[index]){
+                            scaleBoxes[index].position.set(this._boundingDimensions.x*i,this._boundingDimensions.y*j,this._boundingDimensions.z*k);
+                            scaleBoxes[index].position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x/2,-this._boundingDimensions.y/2,-this._boundingDimensions.z/2));
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Disposes of the gizmo
+         */
+        public dispose(){
+            this.gizmoLayer.originalScene.onBeforeRenderObservable.remove(this._renderObserver);
+            this._lineBoundingBox.dispose();
+            this._rotateSpheresParent.dispose();
+            this._scaleBoxesParent.dispose();
+            super.dispose();
+        } 
+    }
+}

+ 5 - 1
src/Gizmos/babylon.gizmo.ts

@@ -11,6 +11,10 @@ module BABYLON {
          * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
          */
         public attachedMesh:Nullable<AbstractMesh>;
+        /**
+         * When set, the gizmo will always appear the same size no matter where the camera is (default: false)
+         */
+        protected _updateScale = true;
         protected _interactionsEnabled = true;
         protected _onInteractionsEnabledChanged(value:boolean){
         }
@@ -35,7 +39,7 @@ module BABYLON {
         constructor(/** The utility layer the gizmo will be added to */ public gizmoLayer:UtilityLayerRenderer){
             this._rootMesh = new BABYLON.Mesh("gizmoRootNode",gizmoLayer.utilityLayerScene);
             this._beforeRenderObserver = this.gizmoLayer.utilityLayerScene.onBeforeRenderObservable.add(()=>{
-                if(this.gizmoLayer.utilityLayerScene.activeCamera && this.attachedMesh){
+                if(this._updateScale && this.gizmoLayer.utilityLayerScene.activeCamera && this.attachedMesh){
                     var dist = this.attachedMesh.position.subtract(this.gizmoLayer.utilityLayerScene.activeCamera.position).length()/3;
                     this._rootMesh.scaling.set(dist, dist, dist);
                 }

+ 10 - 0
src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts

@@ -1,7 +1,12 @@
 module BABYLON {
     export class ProceduralTexture extends Texture {
+        @serialize()
         private _size: number;
+
+        @serialize()
         public _generateMipMaps: boolean;
+
+        @serialize()
         public isEnabled = true;
         private _currentRefreshId = -1;
         private _refreshRate = 1;
@@ -154,6 +159,7 @@
             this._fragment = fragment;
         }
 
+        @serialize()
         public get refreshRate(): number {
             return this._refreshRate;
         }
@@ -198,6 +204,10 @@
 
             this.releaseInternalTexture();
             this._texture = this._engine.createRenderTargetTexture(size, generateMipMaps);
+
+            // Update properties
+            this._size = size;
+            this._generateMipMaps = generateMipMaps;
         }
 
         private _checkUniform(uniformName: string): void {

+ 5 - 1
src/Rendering/babylon.utilityLayerRenderer.ts

@@ -67,6 +67,10 @@ module BABYLON {
                     if(!prePointerInfo.skipOnPointerObservable){
                         this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
                     }
+                    let pointerEvent = <PointerEvent>(prePointerInfo.event);
+                    if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
+                        this._pointerCaptures[pointerEvent.pointerId] = false;
+                    }
                     return;
                 }
 
@@ -104,7 +108,7 @@ module BABYLON {
                                 delete this._lastPointerEvents[pointerEvent.pointerId];
                             }
                         }
-
+                        
                         if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
                             this._pointerCaptures[pointerEvent.pointerId] = false;
                         }