Pārlūkot izejas kodu

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

David Catuhe 7 gadi atpakaļ
vecāks
revīzija
fbfdbc0d1d

+ 1 - 0
Tools/Gulp/config.json

@@ -1078,6 +1078,7 @@
                 "../../src/Gizmos/babylon.positionGizmo.js",
                 "../../src/Gizmos/babylon.positionGizmo.js",
                 "../../src/Gizmos/babylon.rotationGizmo.js",
                 "../../src/Gizmos/babylon.rotationGizmo.js",
                 "../../src/Gizmos/babylon.scaleGizmo.js",
                 "../../src/Gizmos/babylon.scaleGizmo.js",
+                "../../src/Gizmos/babylon.boundingBoxGizmo.js",
                 "../../src/Gizmos/babylon.gizmoManager.js"
                 "../../src/Gizmos/babylon.gizmoManager.js"
             ],
             ],
             "dependUpon": [
             "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 `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))
 - 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))
 - 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))
 - New GUI control: the [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
 
 
 ## Updates
 ## 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))
 - 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))
 - 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))
 - 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))
 - 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))
 - 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))
 - Sprite isVisible field ([TrevorDev](https://github.com/TrevorDev))
@@ -42,7 +42,7 @@
 ### Viewer
 ### Viewer
 
 
 - No fullscreen button on small devices ([RaananW](https://github.com/RaananW))
 - 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))
 - 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))
 - 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))
 - Support for model drag and drop onto the canvas ([RaananW](https://github.com/RaananW))
@@ -62,7 +62,7 @@
 
 
 ### Core Engine
 ### 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))
 - 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))
 - 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))
 - 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);
             this.setColor3("jointColor", this._jointColor);
         }
         }
 
 
+        @serialize()
         public get numberOfBricksHeight(): number {
         public get numberOfBricksHeight(): number {
             return this._numberOfBricksHeight;
             return this._numberOfBricksHeight;
         }
         }
@@ -28,6 +29,7 @@ module BABYLON {
             this.updateShaderUniforms();
             this.updateShaderUniforms();
         }
         }
 
 
+        @serialize()
         public get numberOfBricksWidth(): number {
         public get numberOfBricksWidth(): number {
             return this._numberOfBricksWidth;
             return this._numberOfBricksWidth;
         }
         }
@@ -37,6 +39,7 @@ module BABYLON {
             this.updateShaderUniforms();
             this.updateShaderUniforms();
         }
         }
 
 
+        @serializeAsColor3()
         public get jointColor(): Color3 {
         public get jointColor(): Color3 {
             return this._jointColor;
             return this._jointColor;
         }
         }
@@ -46,6 +49,7 @@ module BABYLON {
             this.updateShaderUniforms();
             this.updateShaderUniforms();
         }
         }
 
 
+        @serializeAsColor3()
         public get brickColor(): Color3 {
         public get brickColor(): Color3 {
             return this._brickColor;
             return this._brickColor;
         }
         }
@@ -54,5 +58,29 @@ module BABYLON {
             this._brickColor = value;
             this._brickColor = value;
             this.updateShaderUniforms();
             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 _pointerObserver:Nullable<Observer<PointerInfo>>;
         private static _planeScene:Scene;
         private static _planeScene:Scene;
         private _draggingID = -1;
         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
          *  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)
          *  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
          * 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}){
         constructor(private options:{dragAxis?:Vector3, dragPlaneNormal?:Vector3}){
             var optionCount = 0;
             var optionCount = 0;
+            if(options === undefined){
+                options = {}
+            }
             if(options.dragAxis){
             if(options.dragAxis){
                 optionCount++;
                 optionCount++;
             }
             }
@@ -51,9 +61,6 @@ module BABYLON {
             if(optionCount > 1){
             if(optionCount > 1){
                 throw "Multiple drag modes specified in dragBehavior options. Only one expected";
                 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
             // Initialize drag plane to not interfere with existing scene
             if(!PointerDragBehavior._planeScene){
             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
             // State of the drag
             var dragging = false;
             var dragging = false;
             var lastPosition = new BABYLON.Vector3(0,0,0);
             var lastPosition = new BABYLON.Vector3(0,0,0);
             var delta = new BABYLON.Vector3(0,0,0);
             var delta = new BABYLON.Vector3(0,0,0);
+            var dragLength = 0;
 
 
             var pickPredicate = (m:AbstractMesh)=>{
             var pickPredicate = (m:AbstractMesh)=>{
                 return this._attachedNode == m || m.isDescendantOf(this._attachedNode)
                 return this._attachedNode == m || m.isDescendantOf(this._attachedNode)
@@ -117,21 +129,30 @@ module BABYLON {
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE){
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE){
                     if(this._draggingID == (<PointerEvent>pointerInfo.event).pointerId && dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray){
                     if(this._draggingID == (<PointerEvent>pointerInfo.event).pointerId && dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray){
                         var pickedPoint = this._pickWithRayOnDragPlane(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) {
                         if (pickedPoint) {
                             // depending on the drag mode option drag accordingly
                             // depending on the drag mode option drag accordingly
                             if(this.options.dragAxis){
                             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{
                             }else{
+                                dragLength = delta.length();
                                 pickedPoint.subtractToRef(lastPosition, delta);
                                 pickedPoint.subtractToRef(lastPosition, delta);
                             }
                             }
                             if(this.moveAttached){
                             if(this.moveAttached){
                                 (<Mesh>this._attachedNode).position.addInPlace(delta);
                                 (<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);
                             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
         // 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){
         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){
             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
                 // 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
                 // Get perpendicular line from direction to camera and drag axis
                 var lineA = pointB.subtract(pointA);
                 var lineA = pointB.subtract(pointA);
                 var lineB = pointC.subtract(pointA);
                 var lineB = pointC.subtract(pointA);
@@ -168,10 +189,13 @@ module BABYLON {
                 var norm = BABYLON.Vector3.Cross(lineA, perpLine).normalize();
                 var norm = BABYLON.Vector3.Cross(lineA, perpLine).normalize();
 
 
                 this._dragPlane.position.copyFrom(pointA);
                 this._dragPlane.position.copyFrom(pointA);
-                this._dragPlane.lookAt(pointA.add(norm));
+                this._dragPlane.lookAt(pointA.subtract(norm));
             }else if(this.options.dragPlaneNormal){
             }else if(this.options.dragPlaneNormal){
                 this._dragPlane.position.copyFrom(pointA);
                 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);
             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));
             this._rootMesh.lookAt(this._rootMesh.position.subtract(dragAxis));
 
 
             // Add drag behavior to handle events when the gizmo is dragged
             // 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._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
             this._rootMesh.addBehavior(this._dragBehavior);
             this._dragBehavior.onDragObservable.add((event)=>{
             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));
             this._rootMesh.lookAt(this._rootMesh.position.subtract(dragAxis));
 
 
             // Add drag behavior to handle events when the gizmo is dragged
             // 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._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
             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)
          * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
          */
          */
         public attachedMesh:Nullable<AbstractMesh>;
         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 _interactionsEnabled = true;
         protected _onInteractionsEnabledChanged(value:boolean){
         protected _onInteractionsEnabledChanged(value:boolean){
         }
         }
@@ -35,7 +39,7 @@ module BABYLON {
         constructor(/** The utility layer the gizmo will be added to */ public gizmoLayer:UtilityLayerRenderer){
         constructor(/** The utility layer the gizmo will be added to */ public gizmoLayer:UtilityLayerRenderer){
             this._rootMesh = new BABYLON.Mesh("gizmoRootNode",gizmoLayer.utilityLayerScene);
             this._rootMesh = new BABYLON.Mesh("gizmoRootNode",gizmoLayer.utilityLayerScene);
             this._beforeRenderObserver = this.gizmoLayer.utilityLayerScene.onBeforeRenderObservable.add(()=>{
             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;
                     var dist = this.attachedMesh.position.subtract(this.gizmoLayer.utilityLayerScene.activeCamera.position).length()/3;
                     this._rootMesh.scaling.set(dist, dist, dist);
                     this._rootMesh.scaling.set(dist, dist, dist);
                 }
                 }

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

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

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

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