David Catuhe преди 10 години
родител
ревизия
fe25ec3eac
променени са 49 файла, в които са добавени 2134 реда и са изтрити 954 реда
  1. 7 9
      Babylon/Audio/babylon.sound.js
  2. 7 9
      Babylon/Audio/babylon.sound.ts
  3. 6 3
      Babylon/Cameras/babylon.arcRotateCamera.js
  4. 39 35
      Babylon/Cameras/babylon.arcRotateCamera.ts
  5. 10 0
      Babylon/Cameras/babylon.camera.js
  6. 11 0
      Babylon/Cameras/babylon.camera.ts
  7. 3 3
      Babylon/Cameras/babylon.followCamera.ts
  8. 1 1
      Babylon/Cameras/babylon.targetCamera.js
  9. 27 27
      Babylon/Cameras/babylon.targetCamera.ts
  10. 7 2
      Babylon/Collisions/babylon.pickingInfo.js
  11. 6 2
      Babylon/Collisions/babylon.pickingInfo.ts
  12. 1 1
      Babylon/Debug/babylon.debugLayer.js
  13. 1 1
      Babylon/Debug/babylon.debugLayer.js.map
  14. 2 1
      Babylon/Debug/babylon.debugLayer.ts
  15. 4 0
      Babylon/Lights/babylon.light.js
  16. 6 1
      Babylon/Lights/babylon.light.ts
  17. 3 1
      Babylon/Loading/Plugins/babylon.babylonFileLoader.js
  18. 3 1
      Babylon/Loading/Plugins/babylon.babylonFileLoader.ts
  19. 0 4
      Babylon/Materials/Textures/Procedurals/babylon.standardProceduralTexture.js
  20. 1 1
      Babylon/Materials/Textures/Procedurals/babylon.standardProceduralTexture.ts
  21. 2 1
      Babylon/Materials/babylon.material.js
  22. 3 1
      Babylon/Materials/babylon.material.ts
  23. 44 0
      Babylon/Math/babylon.math.js
  24. 40 0
      Babylon/Math/babylon.math.ts
  25. 1 1
      Babylon/Mesh/babylon.abstractMesh.js
  26. 1 1
      Babylon/Mesh/babylon.abstractMesh.ts
  27. 162 0
      Babylon/Mesh/babylon.mesh.js
  28. 211 8
      Babylon/Mesh/babylon.mesh.ts
  29. 107 130
      Babylon/Mesh/babylon.meshSimplification.js
  30. 115 132
      Babylon/Mesh/babylon.meshSimplification.ts
  31. 71 39
      Babylon/PostProcess/babylon.lensRenderingPipeline.js
  32. 124 82
      Babylon/PostProcess/babylon.lensRenderingPipeline.ts
  33. 10 3
      Babylon/PostProcess/babylon.postProcessManager.js
  34. 10 2
      Babylon/PostProcess/babylon.postProcessManager.ts
  35. 32 4
      Babylon/PostProcess/babylon.ssaoRenderingPipeline.js
  36. 37 4
      Babylon/PostProcess/babylon.ssaoRenderingPipeline.ts
  37. 15 3
      Babylon/Rendering/babylon.boundingBoxRenderer.js
  38. 19 3
      Babylon/Rendering/babylon.boundingBoxRenderer.ts
  39. 10 12
      Babylon/Shaders/chromaticAberration.fragment.fx
  40. 95 165
      Babylon/Shaders/depthOfField.fragment.fx
  41. 140 0
      Babylon/Shaders/lensHighlights.fragment.fx
  42. 8 7
      Babylon/Shaders/ssao.fragment.fx
  43. 42 5
      Babylon/babylon.engine.js
  44. 45 7
      Babylon/babylon.engine.ts
  45. 3 2
      Babylon/babylon.scene.js
  46. 3 2
      Babylon/babylon.scene.ts
  47. 86 5
      Exporters/Blender/io_export_babylon.py
  48. 532 212
      babylon.2.1-alpha.debug.js
  49. 21 21
      babylon.2.1-alpha.js

+ 7 - 9
Babylon/Audio/babylon.sound.js

@@ -231,13 +231,13 @@ var BABYLON;
         };
         Sound.prototype.setPosition = function (newPosition) {
             this._position = newPosition;
-            if (this.spatialSound) {
+            if (BABYLON.Engine.audioEngine.canUseWebAudio && this.spatialSound) {
                 this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
             }
         };
         Sound.prototype.setLocalDirectionToMesh = function (newLocalDirection) {
             this._localDirection = newLocalDirection;
-            if (this._connectedMesh && this.isPlaying) {
+            if (BABYLON.Engine.audioEngine.canUseWebAudio && this._connectedMesh && this.isPlaying) {
                 this._updateDirection();
             }
         };
@@ -248,7 +248,7 @@ var BABYLON;
             this._soundPanner.setOrientation(direction.x, direction.y, direction.z);
         };
         Sound.prototype.updateDistanceFromListener = function () {
-            if (this._connectedMesh && this.useCustomAttenuation) {
+            if (BABYLON.Engine.audioEngine.canUseWebAudio && this._connectedMesh && this.useCustomAttenuation) {
                 var distance = this._connectedMesh.getDistanceToCamera(this._scene.activeCamera);
                 this._soundGain.gain.value = this._customAttenuationFunction(this._volume, distance, this.maxDistance, this.refDistance, this.rolloffFactor);
             }
@@ -300,11 +300,9 @@ var BABYLON;
             }
         };
         Sound.prototype._onended = function () {
-            if (!this.loop) {
-                this.isPlaying = false;
-                if (this.onended) {
-                    this.onended();
-                }
+            this.isPlaying = false;
+            if (this.onended) {
+                this.onended();
             }
         };
         /**
@@ -363,7 +361,7 @@ var BABYLON;
         };
         Sound.prototype._onRegisterAfterWorldMatrixUpdate = function (connectedMesh) {
             this.setPosition(connectedMesh.getBoundingInfo().boundingSphere.centerWorld);
-            if (this._isDirectional && this.isPlaying) {
+            if (BABYLON.Engine.audioEngine.canUseWebAudio && this._isDirectional && this.isPlaying) {
                 this._updateDirection();
             }
         };

+ 7 - 9
Babylon/Audio/babylon.sound.ts

@@ -251,7 +251,7 @@
         public setPosition(newPosition: Vector3) {
             this._position = newPosition;
 
-            if (this.spatialSound) {
+            if (Engine.audioEngine.canUseWebAudio && this.spatialSound) {
                 this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
             }
         }
@@ -259,7 +259,7 @@
         public setLocalDirectionToMesh(newLocalDirection: Vector3) {
             this._localDirection = newLocalDirection;
 
-            if (this._connectedMesh && this.isPlaying) {
+            if (Engine.audioEngine.canUseWebAudio && this._connectedMesh && this.isPlaying) {
                 this._updateDirection();
             }
         }
@@ -272,7 +272,7 @@
         }
 
         public updateDistanceFromListener() {
-            if (this._connectedMesh && this.useCustomAttenuation) {
+            if (Engine.audioEngine.canUseWebAudio && this._connectedMesh && this.useCustomAttenuation) {
                 var distance = this._connectedMesh.getDistanceToCamera(this._scene.activeCamera);
                 this._soundGain.gain.value = this._customAttenuationFunction(this._volume, distance, this.maxDistance, this.refDistance, this.rolloffFactor);
             }
@@ -324,11 +324,9 @@
         }
 
         private _onended() {
-            if (!this.loop) {
-                this.isPlaying = false;
-                if (this.onended) {
-                    this.onended();
-                }
+            this.isPlaying = false;
+            if (this.onended) {
+                this.onended();
             }
         }
 
@@ -393,7 +391,7 @@
 
         private _onRegisterAfterWorldMatrixUpdate(connectedMesh: AbstractMesh) {
             this.setPosition(connectedMesh.getBoundingInfo().boundingSphere.centerWorld);
-            if (this._isDirectional && this.isPlaying) {
+            if (Engine.audioEngine.canUseWebAudio && this._isDirectional && this.isPlaying) {
                 this._updateDirection();
             }
         }

+ 6 - 3
Babylon/Cameras/babylon.arcRotateCamera.js

@@ -41,6 +41,9 @@ var BABYLON;
             this._previousPosition = BABYLON.Vector3.Zero();
             this._collisionVelocity = BABYLON.Vector3.Zero();
             this._newPosition = BABYLON.Vector3.Zero();
+            if (!this.target) {
+                this.target = BABYLON.Vector3.Zero();
+            }
             this.getViewMatrix();
         }
         ArcRotateCamera.prototype._getTargetPosition = function () {
@@ -127,7 +130,7 @@ var BABYLON;
                                 previousPinchDistance = pinchSquaredDistance;
                                 return;
                             }
-                            if (pinchSquaredDistance != previousPinchDistance) {
+                            if (pinchSquaredDistance !== previousPinchDistance) {
                                 if (pinchSquaredDistance > previousPinchDistance) {
                                     direction = -1;
                                 }
@@ -247,7 +250,7 @@ var BABYLON;
             ]);
         };
         ArcRotateCamera.prototype.detachControl = function (element) {
-            if (this._attachedElement != element) {
+            if (this._attachedElement !== element) {
                 return;
             }
             element.removeEventListener(eventPrefix + "down", this._onPointerDown);
@@ -287,7 +290,7 @@ var BABYLON;
                 }
             }
             // Inertia
-            if (this.inertialAlphaOffset != 0 || this.inertialBetaOffset != 0 || this.inertialRadiusOffset != 0) {
+            if (this.inertialAlphaOffset !== 0 || this.inertialBetaOffset !== 0 || this.inertialRadiusOffset != 0) {
                 this.alpha += this.inertialAlphaOffset;
                 this.beta += this.inertialBetaOffset;
                 this.radius -= this.inertialRadiusOffset;

+ 39 - 35
Babylon/Cameras/babylon.arcRotateCamera.ts

@@ -19,11 +19,11 @@
         public keysLeft = [37];
         public keysRight = [39];
         public zoomOnFactor = 1;
-		public targetScreenOffset = Vector2.Zero();
-		
-        
+        public targetScreenOffset = Vector2.Zero();
+
+
         private _keys = [];
-        private _viewMatrix = new BABYLON.Matrix();
+        private _viewMatrix = new Matrix();
         private _attachedElement: HTMLElement;
 
         private _onPointerDown: (e: PointerEvent) => void;
@@ -52,7 +52,11 @@
         private _previousRadius: number;
 
         constructor(name: string, public alpha: number, public beta: number, public radius: number, public target: any, scene: Scene) {
-            super(name, BABYLON.Vector3.Zero(), scene);
+            super(name, Vector3.Zero(), scene);
+
+            if (!this.target) {
+                this.target = Vector3.Zero();
+            }
 
             this.getViewMatrix();
         }
@@ -64,11 +68,11 @@
         // Cache
         public _initCache(): void {
             super._initCache();
-            this._cache.target = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+            this._cache.target = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             this._cache.alpha = undefined;
             this._cache.beta = undefined;
             this._cache.radius = undefined;
-			this._cache.targetScreenOffset = undefined;
+            this._cache.targetScreenOffset = undefined;
         }
 
         public _updateCache(ignoreParentClass?: boolean): void {
@@ -80,7 +84,7 @@
             this._cache.alpha = this.alpha;
             this._cache.beta = this.beta;
             this._cache.radius = this.radius;
-			this._cache.targetScreenOffset = this.targetScreenOffset.clone();
+            this._cache.targetScreenOffset = this.targetScreenOffset.clone();
         }
 
         // Synchronized
@@ -92,15 +96,15 @@
                 && this._cache.alpha === this.alpha
                 && this._cache.beta === this.beta
                 && this._cache.radius === this.radius
-				&& this._cache.targetScreenOffset.equals(this.targetScreenOffset);
+                && this._cache.targetScreenOffset.equals(this.targetScreenOffset);
         }
 
         // Methods
         public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
             var cacheSoloPointer; // cache pointer object for better perf on camera rotation
             var previousPinchDistance = 0;
-            var pointers = new BABYLON.SmartCollection();
-            
+            var pointers = new SmartCollection();
+
             if (this._attachedElement) {
                 return;
             }
@@ -132,25 +136,25 @@
                     }
 
                     switch (pointers.count) {
-                        
+
                         case 1: //normal camera rotation
                             //var offsetX = evt.clientX - pointers.item(evt.pointerId).x;
                             //var offsetY = evt.clientY - pointers.item(evt.pointerId).y;
                             var offsetX = evt.clientX - cacheSoloPointer.x;
-                            var offsetY = evt.clientY - cacheSoloPointer.y;                            
+                            var offsetY = evt.clientY - cacheSoloPointer.y;
                             this.inertialAlphaOffset -= offsetX / this.angularSensibility;
-                            this.inertialBetaOffset -= offsetY / this.angularSensibility;                            
+                            this.inertialBetaOffset -= offsetY / this.angularSensibility;
                             //pointers.item(evt.pointerId).x = evt.clientX;
                             //pointers.item(evt.pointerId).y = evt.clientY;
                             cacheSoloPointer.x = evt.clientX;
                             cacheSoloPointer.y = evt.clientY;
                             break;
-                            
+
                         case 2: //pinch
                             //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
                             pointers.item(evt.pointerId).x = evt.clientX;
                             pointers.item(evt.pointerId).y = evt.clientY;
-                            var direction = 1;             
+                            var direction = 1;
                             var distX = pointers.getItemByIndex(0).x - pointers.getItemByIndex(1).x;
                             var distY = pointers.getItemByIndex(0).y - pointers.getItemByIndex(1).y;
                             var pinchSquaredDistance = (distX * distX) + (distY * distY);
@@ -159,20 +163,20 @@
                                 return;
                             }
 
-                            if (pinchSquaredDistance != previousPinchDistance) {
+                            if (pinchSquaredDistance !== previousPinchDistance) {
                                 if (pinchSquaredDistance > previousPinchDistance) {
                                     direction = -1;
-                                }    
+                                }
                                 this.inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) / (this.pinchPrecision * this.wheelPrecision * this.angularSensibility);
                                 previousPinchDistance = pinchSquaredDistance;
                             }
                             break;
-                            
-                        default: 
+
+                        default:
                             if (pointers.item(evt.pointerId)) {
                                 pointers.item(evt.pointerId).x = evt.clientX;
                                 pointers.item(evt.pointerId).y = evt.clientY;
-                            }                            
+                            }
                     }
                 };
 
@@ -290,7 +294,7 @@
                     cacheSoloPointer = null;
                 };
 
-           
+
             }
 
             element.addEventListener(eventPrefix + "down", this._onPointerDown, false);
@@ -311,7 +315,7 @@
         }
 
         public detachControl(element: HTMLElement): void {
-            if (this._attachedElement != element) {
+            if (this._attachedElement !== element) {
                 return;
             }
 
@@ -356,7 +360,7 @@
             }
 
             // Inertia
-            if (this.inertialAlphaOffset != 0 || this.inertialBetaOffset != 0 || this.inertialRadiusOffset != 0) {
+            if (this.inertialAlphaOffset !== 0 || this.inertialBetaOffset !== 0 || this.inertialRadiusOffset != 0) {
 
                 this.alpha += this.inertialAlphaOffset;
                 this.beta += this.inertialBetaOffset;
@@ -366,13 +370,13 @@
                 this.inertialBetaOffset *= this.inertia;
                 this.inertialRadiusOffset *= this.inertia;
 
-                if (Math.abs(this.inertialAlphaOffset) < BABYLON.Engine.Epsilon)
+                if (Math.abs(this.inertialAlphaOffset) < Engine.Epsilon)
                     this.inertialAlphaOffset = 0;
 
-                if (Math.abs(this.inertialBetaOffset) < BABYLON.Engine.Epsilon)
+                if (Math.abs(this.inertialBetaOffset) < Engine.Epsilon)
                     this.inertialBetaOffset = 0;
 
-                if (Math.abs(this.inertialRadiusOffset) < BABYLON.Engine.Epsilon)
+                if (Math.abs(this.inertialRadiusOffset) < Engine.Epsilon)
                     this.inertialRadiusOffset = 0;
             }
 
@@ -421,7 +425,7 @@
 
             var target = this._getTargetPosition();
 
-            target.addToRef(new BABYLON.Vector3(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb), this.position);
+            target.addToRef(new Vector3(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb), this.position);
 
             if (this.checkCollisions) {
                 this._collider.radius = this.collisionRadius;
@@ -449,17 +453,17 @@
             this._previousRadius = this.radius;
             this._previousPosition.copyFrom(this.position);
 
-			this._viewMatrix.m[12] += this.targetScreenOffset.x;
-			this._viewMatrix.m[13] += this.targetScreenOffset.y;
-						
+            this._viewMatrix.m[12] += this.targetScreenOffset.x;
+            this._viewMatrix.m[13] += this.targetScreenOffset.y;
+
             return this._viewMatrix;
         }
 
         public zoomOn(meshes?: AbstractMesh[]): void {
             meshes = meshes || this.getScene().meshes;
 
-            var minMaxVector = BABYLON.Mesh.MinMax(meshes);
-            var distance = BABYLON.Vector3.Distance(minMaxVector.min, minMaxVector.max);
+            var minMaxVector = Mesh.MinMax(meshes);
+            var distance = Vector3.Distance(minMaxVector.min, minMaxVector.max);
 
             this.radius = distance * this.zoomOnFactor;
 
@@ -472,8 +476,8 @@
 
             if (meshesOrMinMaxVectorAndDistance.min === undefined) { // meshes
                 meshesOrMinMaxVector = meshesOrMinMaxVectorAndDistance || this.getScene().meshes;
-                meshesOrMinMaxVector = BABYLON.Mesh.MinMax(meshesOrMinMaxVector);
-                distance = BABYLON.Vector3.Distance(meshesOrMinMaxVector.min, meshesOrMinMaxVector.max);
+                meshesOrMinMaxVector = Mesh.MinMax(meshesOrMinMaxVector);
+                distance = Vector3.Distance(meshesOrMinMaxVector.min, meshesOrMinMaxVector.max);
             }
             else { //minMaxVector and distance
                 meshesOrMinMaxVector = meshesOrMinMaxVectorAndDistance;

+ 10 - 0
Babylon/Cameras/babylon.camera.js

@@ -32,6 +32,7 @@ var BABYLON;
             this._postProcesses = new Array();
             this._postProcessesTakenIndices = [];
             this._activeMeshes = new BABYLON.SmartArray(256);
+            this._globalPosition = BABYLON.Vector3.Zero();
             scene.addCamera(this);
             if (!scene.activeCamera) {
                 scene.activeCamera = this;
@@ -65,6 +66,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Camera.prototype, "globalPosition", {
+            get: function () {
+                return this._globalPosition;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Camera.prototype.getActiveMeshes = function () {
             return this._activeMeshes;
         };
@@ -221,6 +229,7 @@ var BABYLON;
         Camera.prototype.getViewMatrix = function () {
             this._computedViewMatrix = this._computeViewMatrix();
             if (!this.parent || !this.parent.getWorldMatrix || this.isSynchronized()) {
+                this._globalPosition.copyFrom(this.position);
                 return this._computedViewMatrix;
             }
             if (!this._worldMatrix) {
@@ -230,6 +239,7 @@ var BABYLON;
             this._worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._computedViewMatrix);
             this._computedViewMatrix.invert();
             this._currentRenderId = this.getScene().getRenderId();
+            this._globalPosition.copyFromFloats(this._computedViewMatrix.m[12], this._computedViewMatrix.m[13], this._computedViewMatrix.m[14]);
             return this._computedViewMatrix;
         };
         Camera.prototype._computeViewMatrix = function (force) {

+ 11 - 0
Babylon/Cameras/babylon.camera.ts

@@ -48,6 +48,8 @@
 
         public _activeMeshes = new SmartArray<Mesh>(256);
 
+        private _globalPosition = Vector3.Zero();
+
         constructor(name: string, public position: Vector3, scene: Scene) {
             super(name, scene);
 
@@ -58,6 +60,10 @@
             }
         }
 
+        public get globalPosition(): Vector3 {
+            return this._globalPosition;
+        }
+
         public getActiveMeshes(): SmartArray<Mesh> {
             return this._activeMeshes;
         }
@@ -281,6 +287,9 @@
             if (!this.parent
                 || !this.parent.getWorldMatrix
                 || this.isSynchronized()) {
+
+                this._globalPosition.copyFrom(this.position);
+
                 return this._computedViewMatrix;
             }
 
@@ -295,6 +304,8 @@
             this._computedViewMatrix.invert();
 
             this._currentRenderId = this.getScene().getRenderId();
+            this._globalPosition.copyFromFloats(this._computedViewMatrix.m[12], this._computedViewMatrix.m[13], this._computedViewMatrix.m[14]);
+
             return this._computedViewMatrix;
         }
 

+ 3 - 3
Babylon/Cameras/babylon.followCamera.ts

@@ -6,7 +6,7 @@
         public heightOffset:number = 4;
         public cameraAcceleration:number = 0.05;
         public maxCameraSpeed:number = 20;
-        public target:BABYLON.AbstractMesh;
+        public target:AbstractMesh;
 
         constructor(name:string, position:Vector3, scene:Scene) {
             super(name, position, scene);
@@ -16,7 +16,7 @@
             return degrees * Math.PI / 180;
         }
 
-        private follow(cameraTarget:BABYLON.AbstractMesh) {
+        private follow(cameraTarget:AbstractMesh) {
             if (!cameraTarget)
                 return;
 
@@ -43,7 +43,7 @@
                 vz = vz < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
             }
 
-            this.position = new BABYLON.Vector3(this.position.x + vx, this.position.y + vy, this.position.z + vz);
+            this.position = new Vector3(this.position.x + vx, this.position.y + vy, this.position.z + vz);
             this.setTarget(cameraTarget.position);
         }
 

+ 1 - 1
Babylon/Cameras/babylon.targetCamera.js

@@ -147,7 +147,7 @@ var BABYLON;
         TargetCamera.prototype._getViewMatrix = function () {
             if (!this.lockedTarget) {
                 // Compute
-                if (this.upVector.x != 0 || this.upVector.y != 1.0 || this.upVector.z != 0) {
+                if (this.upVector.x !== 0 || this.upVector.y !== 1.0 || this.upVector.z !== 0) {
                     BABYLON.Matrix.LookAtLHToRef(BABYLON.Vector3.Zero(), this._referencePoint, this.upVector, this._lookAtTemp);
                     BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
                     this._lookAtTemp.multiplyToRef(this._cameraRotationMatrix, this._tempMatrix);

+ 27 - 27
Babylon/Cameras/babylon.targetCamera.ts

@@ -1,23 +1,23 @@
 module BABYLON {
     export class TargetCamera extends Camera {
 
-        public cameraDirection = new BABYLON.Vector3(0, 0, 0);
-        public cameraRotation = new BABYLON.Vector2(0, 0);
-        public rotation = new BABYLON.Vector3(0, 0, 0);
+        public cameraDirection = new Vector3(0, 0, 0);
+        public cameraRotation = new Vector2(0, 0);
+        public rotation = new Vector3(0, 0, 0);
 
         public speed = 2.0;
         public noRotationConstraint = false;
         public lockedTarget = null;
 
-        public _currentTarget = BABYLON.Vector3.Zero();
-        public _viewMatrix = BABYLON.Matrix.Zero();
-        public _camMatrix = BABYLON.Matrix.Zero();
-        public _cameraTransformMatrix = BABYLON.Matrix.Zero();
-        public _cameraRotationMatrix = BABYLON.Matrix.Zero();
-        public _referencePoint = new BABYLON.Vector3(0, 0, 1);
-        public _transformedReferencePoint = BABYLON.Vector3.Zero();
-        public _lookAtTemp = BABYLON.Matrix.Zero();
-        public _tempMatrix = BABYLON.Matrix.Zero();
+        public _currentTarget = Vector3.Zero();
+        public _viewMatrix = Matrix.Zero();
+        public _camMatrix = Matrix.Zero();
+        public _cameraTransformMatrix = Matrix.Zero();
+        public _cameraRotationMatrix = Matrix.Zero();
+        public _referencePoint = new Vector3(0, 0, 1);
+        public _transformedReferencePoint = Vector3.Zero();
+        public _lookAtTemp = Matrix.Zero();
+        public _tempMatrix = Matrix.Zero();
 
         public _reset:() => void;
 
@@ -38,8 +38,8 @@
         // Cache
         public _initCache() {
             super._initCache();
-            this._cache.lockedTarget = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
-            this._cache.rotation = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+            this._cache.lockedTarget = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+            this._cache.rotation = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
         }
 
         public _updateCache(ignoreParentClass?:boolean):void {
@@ -85,7 +85,7 @@
         public setTarget(target:Vector3):void {
             this.upVector.normalize();
 
-            BABYLON.Matrix.LookAtLHToRef(this.position, target, this.upVector, this._camMatrix);
+            Matrix.LookAtLHToRef(this.position, target, this.upVector, this._camMatrix);
             this._camMatrix.invert();
 
             this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
@@ -98,7 +98,7 @@
                 this.rotation.y = (-Math.atan(vDir.z / vDir.x) - Math.PI / 2.0);
             }
 
-            this.rotation.z = -Math.acos(BABYLON.Vector3.Dot(new BABYLON.Vector3(0, 1.0, 0), this.upVector));
+            this.rotation.z = -Math.acos(Vector3.Dot(new Vector3(0, 1.0, 0), this.upVector));
 
             if (isNaN(this.rotation.x)) {
                 this.rotation.x = 0;
@@ -153,26 +153,26 @@
 
             // Inertia
             if (needToMove) {
-                if (Math.abs(this.cameraDirection.x) < BABYLON.Engine.Epsilon) {
+                if (Math.abs(this.cameraDirection.x) < Engine.Epsilon) {
                     this.cameraDirection.x = 0;
                 }
 
-                if (Math.abs(this.cameraDirection.y) < BABYLON.Engine.Epsilon) {
+                if (Math.abs(this.cameraDirection.y) < Engine.Epsilon) {
                     this.cameraDirection.y = 0;
                 }
 
-                if (Math.abs(this.cameraDirection.z) < BABYLON.Engine.Epsilon) {
+                if (Math.abs(this.cameraDirection.z) < Engine.Epsilon) {
                     this.cameraDirection.z = 0;
                 }
 
                 this.cameraDirection.scaleInPlace(this.inertia);
             }
             if (needToRotate) {
-                if (Math.abs(this.cameraRotation.x) < BABYLON.Engine.Epsilon) {
+                if (Math.abs(this.cameraRotation.x) < Engine.Epsilon) {
                     this.cameraRotation.x = 0;
                 }
 
-                if (Math.abs(this.cameraRotation.y) < BABYLON.Engine.Epsilon) {
+                if (Math.abs(this.cameraRotation.y) < Engine.Epsilon) {
                     this.cameraRotation.y = 0;
                 }
                 this.cameraRotation.scaleInPlace(this.inertia);
@@ -183,19 +183,19 @@
         public _getViewMatrix():Matrix {
             if (!this.lockedTarget) {
                 // Compute
-                if (this.upVector.x != 0 || this.upVector.y != 1.0 || this.upVector.z != 0) {
-                    BABYLON.Matrix.LookAtLHToRef(BABYLON.Vector3.Zero(), this._referencePoint, this.upVector, this._lookAtTemp);
-                    BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
+                if (this.upVector.x !== 0 || this.upVector.y !== 1.0 || this.upVector.z !== 0) {
+                    Matrix.LookAtLHToRef(Vector3.Zero(), this._referencePoint, this.upVector, this._lookAtTemp);
+                    Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
 
 
                     this._lookAtTemp.multiplyToRef(this._cameraRotationMatrix, this._tempMatrix);
                     this._lookAtTemp.invert();
                     this._tempMatrix.multiplyToRef(this._lookAtTemp, this._cameraRotationMatrix);
                 } else {
-                    BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
+                    Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
                 }
 
-                BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
+                Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
 
                 // Computing target and final matrix
                 this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
@@ -203,7 +203,7 @@
                 this._currentTarget.copyFrom(this._getLockedTargetPosition());
             }
 
-            BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
+            Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
             return this._viewMatrix;
         }
     }

+ 7 - 2
Babylon/Collisions/babylon.pickingInfo.js

@@ -23,7 +23,8 @@ var BABYLON;
             this.subMeshId = 0;
         }
         // Methods
-        PickingInfo.prototype.getNormal = function () {
+        PickingInfo.prototype.getNormal = function (useWorldCoordinates) {
+            if (useWorldCoordinates === void 0) { useWorldCoordinates = false; }
             if (!this.pickedMesh || !this.pickedMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
                 return null;
             }
@@ -35,7 +36,11 @@ var BABYLON;
             normal0 = normal0.scale(this.bu);
             normal1 = normal1.scale(this.bv);
             normal2 = normal2.scale(1.0 - this.bu - this.bv);
-            return new BABYLON.Vector3(normal0.x + normal1.x + normal2.x, normal0.y + normal1.y + normal2.y, normal0.z + normal1.z + normal2.z);
+            var result = new BABYLON.Vector3(normal0.x + normal1.x + normal2.x, normal0.y + normal1.y + normal2.y, normal0.z + normal1.z + normal2.z);
+            if (useWorldCoordinates) {
+                result = BABYLON.Vector3.TransformNormal(result, this.pickedMesh.getWorldMatrix());
+            }
+            return result;
         };
         PickingInfo.prototype.getTextureCoordinates = function () {
             if (!this.pickedMesh || !this.pickedMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {

+ 6 - 2
Babylon/Collisions/babylon.pickingInfo.ts

@@ -18,7 +18,7 @@
         public subMeshId = 0;
 
         // Methods
-        public getNormal(): Vector3 {
+        public getNormal(useWorldCoordinates = false): Vector3 {
             if (!this.pickedMesh || !this.pickedMesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
                 return null;
             }
@@ -34,7 +34,11 @@
             normal1 = normal1.scale(this.bv);
             normal2 = normal2.scale(1.0 - this.bu - this.bv);
 
-            return new Vector3(normal0.x + normal1.x + normal2.x, normal0.y + normal1.y + normal2.y, normal0.z + normal1.z + normal2.z);
+            var result = new Vector3(normal0.x + normal1.x + normal2.x, normal0.y + normal1.y + normal2.y, normal0.z + normal1.z + normal2.z);
+            if (useWorldCoordinates) {
+                result = Vector3.TransformNormal(result, this.pickedMesh.getWorldMatrix());
+            }
+            return result;
         }
 
         public getTextureCoordinates(): Vector2 {

+ 1 - 1
Babylon/Debug/babylon.debugLayer.js

@@ -269,13 +269,13 @@ var BABYLON;
             if (this._enabled) {
                 return;
             }
+            this._enabled = true;
             if (camera) {
                 this._camera = camera;
             }
             else {
                 this._camera = this._scene.activeCamera;
             }
-            this._enabled = true;
             this._showUI = showUI;
             var engine = this._scene.getEngine();
             this._globalDiv = document.createElement("div");

Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
Babylon/Debug/babylon.debugLayer.js.map


+ 2 - 1
Babylon/Debug/babylon.debugLayer.ts

@@ -373,13 +373,14 @@
                 return;
             }
 
+            this._enabled = true;
+
             if (camera) {
                 this._camera = camera;
             } else {
                 this._camera = this._scene.activeCamera;
             }
 
-            this._enabled = true;
             this._showUI = showUI;
 
             var engine = this._scene.getEngine();

+ 4 - 0
Babylon/Lights/babylon.light.js

@@ -14,6 +14,7 @@ var BABYLON;
             this.specular = new BABYLON.Color3(1.0, 1.0, 1.0);
             this.intensity = 1.0;
             this.range = Number.MAX_VALUE;
+            this.includeOnlyWithLayerMask = 0;
             this.includedOnlyMeshes = new Array();
             this.excludedMeshes = new Array();
             this._excludedMeshesIds = new Array();
@@ -41,6 +42,9 @@ var BABYLON;
             if (this.excludedMeshes.length > 0 && this.excludedMeshes.indexOf(mesh) !== -1) {
                 return false;
             }
+            if (this.includeOnlyWithLayerMask !== 0 && this.includeOnlyWithLayerMask !== mesh.layerMask) {
+                return false;
+            }
             return true;
         };
         Light.prototype.getWorldMatrix = function () {

+ 6 - 1
Babylon/Lights/babylon.light.ts

@@ -22,6 +22,7 @@
         public specular = new Color3(1.0, 1.0, 1.0);
         public intensity = 1.0;
         public range = Number.MAX_VALUE;
+        public includeOnlyWithLayerMask = 0;
         public includedOnlyMeshes = new Array<AbstractMesh>();
         public excludedMeshes = new Array<AbstractMesh>();
 
@@ -43,7 +44,7 @@
         public getAbsolutePosition(): Vector3 {
             return Vector3.Zero();
         }
-       
+
         public transferToEffect(effect: Effect, uniformName0?: string, uniformName1?: string): void {
         }
 
@@ -64,6 +65,10 @@
                 return false;
             }
 
+            if (this.includeOnlyWithLayerMask !== 0 && this.includeOnlyWithLayerMask !== mesh.layerMask) {
+                return false;
+            }
+
             return true;
         }
 

+ 3 - 1
Babylon/Loading/Plugins/babylon.babylonFileLoader.js

@@ -780,7 +780,9 @@ var BABYLON;
                 var triggerParams;
                 var trigger = parsedActions.children[i];
                 if (trigger.properties.length > 0) {
-                    triggerParams = { trigger: BABYLON.ActionManager[trigger.name], parameter: scene.getMeshByName(trigger.properties[0].value) };
+                    var param = trigger.properties[0].value;
+                    var value = trigger.properties[0].targetType == null ? param : scene.getMeshByName(param);
+                    triggerParams = { trigger: BABYLON.ActionManager[trigger.name], parameter: value };
                 }
                 else
                     triggerParams = BABYLON.ActionManager[trigger.name];

+ 3 - 1
Babylon/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -971,7 +971,9 @@
             var trigger = parsedActions.children[i];
 
             if (trigger.properties.length > 0) {
-                triggerParams = { trigger: BABYLON.ActionManager[trigger.name], parameter: scene.getMeshByName(trigger.properties[0].value) };
+                var param = trigger.properties[0].value;
+                var value = trigger.properties[0].targetType == null ? param : scene.getMeshByName(param);
+                triggerParams = { trigger: BABYLON.ActionManager[trigger.name], parameter: value };
             }
             else
                 triggerParams = BABYLON.ActionManager[trigger.name];

+ 0 - 4
Babylon/Materials/Textures/Procedurals/babylon.standardProceduralTexture.js

@@ -321,10 +321,6 @@ var BABYLON;
             get: function () {
                 return this._numberOfBricksHeight;
             },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(BrickProceduralTexture.prototype, "cloudColor", {
             set: function (value) {
                 this._numberOfBricksHeight = value;
                 this.updateShaderUniforms();

+ 1 - 1
Babylon/Materials/Textures/Procedurals/babylon.standardProceduralTexture.ts

@@ -285,7 +285,7 @@
             return this._numberOfBricksHeight;
         }
 
-        public set cloudColor(value: number) {
+        public set numberOfBricksHeight(value: number) {
             this._numberOfBricksHeight = value;
             this.updateShaderUniforms();
         }

+ 2 - 1
Babylon/Materials/babylon.material.js

@@ -11,6 +11,7 @@ var BABYLON;
             this._wasPreviouslyReady = false;
             this._fillMode = Material.TriangleFillMode;
             this.pointSize = 1.0;
+            this.zOffset = 0;
             this.id = name;
             this._scene = scene;
             if (!doNotAdd) {
@@ -91,7 +92,7 @@ var BABYLON;
         Material.prototype._preBind = function () {
             var engine = this._scene.getEngine();
             engine.enableEffect(this._effect);
-            engine.setState(this.backFaceCulling);
+            engine.setState(this.backFaceCulling, this.zOffset);
         };
         Material.prototype.bind = function (world, mesh) {
             this._scene._cachedMaterial = this;

+ 3 - 1
Babylon/Materials/babylon.material.ts

@@ -35,6 +35,8 @@
 
         public pointSize = 1.0;
 
+        public zOffset = 0;
+
         public get wireframe(): boolean {
             return this._fillMode === Material.WireFrameFillMode;
         }
@@ -100,7 +102,7 @@
             var engine = this._scene.getEngine();
 
             engine.enableEffect(this._effect);
-            engine.setState(this.backFaceCulling);
+            engine.setState(this.backFaceCulling, this.zOffset);
         }
 
         public bind(world: Matrix, mesh: Mesh): void {

+ 44 - 0
Babylon/Math/babylon.math.js

@@ -580,6 +580,12 @@ var BABYLON;
             return this;
         };
         // Statics
+        Vector3.GetClipFactor = function (vector0, vector1, axis, size) {
+            var d0 = Vector3.Dot(vector0, axis) - size;
+            var d1 = Vector3.Dot(vector1, axis) - size;
+            var s = d0 / (d0 - d1);
+            return s;
+        };
         Vector3.FromArray = function (array, offset) {
             if (!offset) {
                 offset = 0;
@@ -2730,9 +2736,47 @@ var BABYLON;
         Curve3.prototype.getPoints = function () {
             return this._points;
         };
+        Curve3.prototype.continue = function (curve) {
+            var lastPoint = this._points[this._points.length - 1];
+            var continuedPoints = this._points.slice();
+            var curvePoints = curve.getPoints();
+            for (var i = 1; i < curvePoints.length; i++) {
+                continuedPoints.push(curvePoints[i].add(lastPoint));
+            }
+            return new Curve3(continuedPoints);
+        };
         return Curve3;
     })();
     BABYLON.Curve3 = Curve3;
+    // Vertex formats
+    var PositionNormalVertex = (function () {
+        function PositionNormalVertex(position, normal) {
+            if (position === void 0) { position = Vector3.Zero(); }
+            if (normal === void 0) { normal = Vector3.Up(); }
+            this.position = position;
+            this.normal = normal;
+        }
+        PositionNormalVertex.prototype.clone = function () {
+            return new PositionNormalVertex(this.position.clone(), this.normal.clone());
+        };
+        return PositionNormalVertex;
+    })();
+    BABYLON.PositionNormalVertex = PositionNormalVertex;
+    var PositionNormalTextureVertex = (function () {
+        function PositionNormalTextureVertex(position, normal, uv) {
+            if (position === void 0) { position = Vector3.Zero(); }
+            if (normal === void 0) { normal = Vector3.Up(); }
+            if (uv === void 0) { uv = Vector2.Zero(); }
+            this.position = position;
+            this.normal = normal;
+            this.uv = uv;
+        }
+        PositionNormalTextureVertex.prototype.clone = function () {
+            return new PositionNormalTextureVertex(this.position.clone(), this.normal.clone(), this.uv.clone());
+        };
+        return PositionNormalTextureVertex;
+    })();
+    BABYLON.PositionNormalTextureVertex = PositionNormalTextureVertex;
     // SIMD
     if (window.SIMD !== undefined) {
         // Replace functions

+ 40 - 0
Babylon/Math/babylon.math.ts

@@ -716,6 +716,15 @@
         }
 
         // Statics
+        public static GetClipFactor(vector0: Vector3, vector1: Vector3, axis: Vector3, size) {
+            var d0 = Vector3.Dot(vector0, axis) - size;
+            var d1 = Vector3.Dot(vector1, axis) - size;
+
+            var s = d0 / (d0 - d1);
+
+            return s;
+        }
+
         public static FromArray(array: number[], offset?: number): Vector3 {
             if (!offset) {
                 offset = 0;
@@ -3412,6 +3421,37 @@
         public getPoints() {
             return this._points;
         }
+
+        public continue(curve: Curve3): Curve3 {
+            var lastPoint = this._points[this._points.length - 1];
+            var continuedPoints = this._points.slice();
+            var curvePoints = curve.getPoints();
+            for (var i = 1; i < curvePoints.length; i++) {
+                continuedPoints.push(curvePoints[i].add(lastPoint));
+            }
+            return new Curve3(continuedPoints);
+        }
+    }
+
+    // Vertex formats
+    export class PositionNormalVertex {
+        constructor(public position: Vector3 = Vector3.Zero(), public normal: Vector3 = Vector3.Up()) {
+            
+        }
+
+        public clone(): PositionNormalVertex {
+            return new PositionNormalVertex(this.position.clone(), this.normal.clone());
+        }
+    }
+
+    export class PositionNormalTextureVertex {
+        constructor(public position: Vector3 = Vector3.Zero(), public normal: Vector3 = Vector3.Up(), public uv: Vector2 = Vector2.Zero()) {
+
+        }
+
+        public clone(): PositionNormalTextureVertex {
+            return new PositionNormalTextureVertex(this.position.clone(), this.normal.clone(), this.uv.clone());
+        }
     }
 
     // SIMD

+ 1 - 1
Babylon/Mesh/babylon.abstractMesh.js

@@ -40,7 +40,7 @@ var BABYLON;
             this.useOctreeForRenderingSelection = true;
             this.useOctreeForPicking = true;
             this.useOctreeForCollisions = true;
-            this.layerMask = 0xFFFFFFFF;
+            this.layerMask = 0x0FFFFFFF;
             // Physics
             this._physicImpostor = BABYLON.PhysicsEngine.NoImpostor;
             // Collisions

+ 1 - 1
Babylon/Mesh/babylon.abstractMesh.ts

@@ -63,7 +63,7 @@
         public useOctreeForPicking = true;
         public useOctreeForCollisions = true;
 
-        public layerMask: number = 0xFFFFFFFF;
+        public layerMask: number = 0x0FFFFFFF;
 
         // Physics
         public _physicImpostor = PhysicsEngine.NoImpostor;

+ 162 - 0
Babylon/Mesh/babylon.mesh.js

@@ -1115,6 +1115,168 @@ var BABYLON;
             var tube = Mesh.CreateRibbon(name, circlePaths, false, true, 0, scene, updatable, sideOrientation);
             return tube;
         };
+        // Decals
+        // Inspired by https://github.com/mrdoob/three.js/blob/eee231960882f6f3b6113405f524956145148146/examples/js/geometries/DecalGeometry.js
+        Mesh.CreateDecal = function (name, sourceMesh, position, normal, size, angle) {
+            if (angle === void 0) { angle = 0; }
+            var indices = sourceMesh.getIndices();
+            var positions = sourceMesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+            var normals = sourceMesh.getVerticesData(BABYLON.VertexBuffer.NormalKind);
+            // Getting correct rotation
+            if (!normal) {
+                var target = new BABYLON.Vector3(0, 0, 1);
+                var camera = sourceMesh.getScene().activeCamera;
+                var cameraWorldTarget = BABYLON.Vector3.TransformCoordinates(target, camera.getWorldMatrix());
+                normal = camera.globalPosition.subtract(cameraWorldTarget);
+            }
+            var yaw = -Math.atan2(normal.z, normal.x) - Math.PI / 2;
+            var len = Math.sqrt(normal.x * normal.x + normal.z * normal.z);
+            var pitch = Math.atan2(normal.y, len);
+            // Matrix
+            var decalWorldMatrix = BABYLON.Matrix.RotationYawPitchRoll(yaw, pitch, angle).multiply(BABYLON.Matrix.Translation(position.x, position.y, position.z));
+            var inverseDecalWorldMatrix = BABYLON.Matrix.Invert(decalWorldMatrix);
+            var meshWorldMatrix = sourceMesh.getWorldMatrix();
+            var transformMatrix = meshWorldMatrix.multiply(inverseDecalWorldMatrix);
+            var vertexData = new BABYLON.VertexData();
+            vertexData.indices = [];
+            vertexData.positions = [];
+            vertexData.normals = [];
+            vertexData.uvs = [];
+            var currentVertexDataIndex = 0;
+            var extractDecalVector3 = function (indexId) {
+                var vertexId = indices[indexId];
+                var result = new BABYLON.PositionNormalVertex();
+                result.position = new BABYLON.Vector3(positions[vertexId * 3], positions[vertexId * 3 + 1], positions[vertexId * 3 + 2]);
+                // Send vector to decal local world
+                result.position = BABYLON.Vector3.TransformCoordinates(result.position, transformMatrix);
+                // Get normal
+                result.normal = new BABYLON.Vector3(normals[vertexId * 3], normals[vertexId * 3 + 1], normals[vertexId * 3 + 2]);
+                return result;
+            };
+            var clip = function (vertices, axis) {
+                if (vertices.length === 0) {
+                    return vertices;
+                }
+                var clipSize = 0.5 * Math.abs(BABYLON.Vector3.Dot(size, axis));
+                var clipVertices = function (v0, v1) {
+                    var clipFactor = BABYLON.Vector3.GetClipFactor(v0.position, v1.position, axis, clipSize);
+                    return new BABYLON.PositionNormalVertex(BABYLON.Vector3.Lerp(v0.position, v1.position, clipFactor), BABYLON.Vector3.Lerp(v0.normal, v1.normal, clipFactor));
+                };
+                var result = new Array();
+                for (var index = 0; index < vertices.length; index += 3) {
+                    var v1Out;
+                    var v2Out;
+                    var v3Out;
+                    var total = 0;
+                    var nV1, nV2, nV3, nV4;
+                    var d1 = BABYLON.Vector3.Dot(vertices[index].position, axis) - clipSize;
+                    var d2 = BABYLON.Vector3.Dot(vertices[index + 1].position, axis) - clipSize;
+                    var d3 = BABYLON.Vector3.Dot(vertices[index + 2].position, axis) - clipSize;
+                    v1Out = d1 > 0;
+                    v2Out = d2 > 0;
+                    v3Out = d3 > 0;
+                    total = (v1Out ? 1 : 0) + (v2Out ? 1 : 0) + (v3Out ? 1 : 0);
+                    switch (total) {
+                        case 0:
+                            result.push(vertices[index]);
+                            result.push(vertices[index + 1]);
+                            result.push(vertices[index + 2]);
+                            break;
+                        case 1:
+                            if (v1Out) {
+                                nV1 = vertices[index + 1];
+                                nV2 = vertices[index + 2];
+                                nV3 = clipVertices(vertices[index], nV1);
+                                nV4 = clipVertices(vertices[index], nV2);
+                            }
+                            if (v2Out) {
+                                nV1 = vertices[index];
+                                nV2 = vertices[index + 2];
+                                nV3 = clipVertices(vertices[index + 1], nV1);
+                                nV4 = clipVertices(vertices[index + 1], nV2);
+                                result.push(nV3);
+                                result.push(nV2.clone());
+                                result.push(nV1.clone());
+                                result.push(nV2.clone());
+                                result.push(nV3.clone());
+                                result.push(nV4);
+                                break;
+                            }
+                            if (v3Out) {
+                                nV1 = vertices[index];
+                                nV2 = vertices[index + 1];
+                                nV3 = clipVertices(vertices[index + 2], nV1);
+                                nV4 = clipVertices(vertices[index + 2], nV2);
+                            }
+                            result.push(nV1.clone());
+                            result.push(nV2.clone());
+                            result.push(nV3);
+                            result.push(nV4);
+                            result.push(nV3.clone());
+                            result.push(nV2.clone());
+                            break;
+                        case 2:
+                            if (!v1Out) {
+                                nV1 = vertices[index].clone();
+                                nV2 = clipVertices(nV1, vertices[index + 1]);
+                                nV3 = clipVertices(nV1, vertices[index + 2]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            if (!v2Out) {
+                                nV1 = vertices[index + 1].clone();
+                                nV2 = clipVertices(nV1, vertices[index + 2]);
+                                nV3 = clipVertices(nV1, vertices[index]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            if (!v3Out) {
+                                nV1 = vertices[index + 2].clone();
+                                nV2 = clipVertices(nV1, vertices[index]);
+                                nV3 = clipVertices(nV1, vertices[index + 1]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            break;
+                        case 3:
+                            break;
+                    }
+                }
+                return result;
+            };
+            for (var index = 0; index < indices.length; index += 3) {
+                var faceVertices = new Array();
+                faceVertices.push(extractDecalVector3(index));
+                faceVertices.push(extractDecalVector3(index + 1));
+                faceVertices.push(extractDecalVector3(index + 2));
+                // Clip
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(1, 0, 0));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(-1, 0, 0));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(0, 1, 0));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(0, -1, 0));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(0, 0, 1));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(0, 0, -1));
+                if (faceVertices.length === 0) {
+                    continue;
+                }
+                for (var vIndex = 0; vIndex < faceVertices.length; vIndex++) {
+                    var vertex = faceVertices[vIndex];
+                    vertexData.indices.push(currentVertexDataIndex);
+                    BABYLON.Vector3.TransformCoordinates(vertex.position, decalWorldMatrix).toArray(vertexData.positions, currentVertexDataIndex * 3);
+                    vertex.normal.toArray(vertexData.normals, currentVertexDataIndex * 3);
+                    vertexData.uvs.push(0.5 + vertex.position.x / size.x);
+                    vertexData.uvs.push(0.5 + vertex.position.y / size.y);
+                    currentVertexDataIndex++;
+                }
+            }
+            // Return mesh
+            var decal = new Mesh(name, sourceMesh.getScene());
+            vertexData.applyToMesh(decal);
+            return decal;
+        };
         // Tools
         Mesh.MinMax = function (meshes) {
             var minVector = null;

+ 211 - 8
Babylon/Mesh/babylon.mesh.ts

@@ -766,7 +766,7 @@
 
                     this.delayLoadState = Engine.DELAYLOADSTATE_LOADED;
                     scene._removePendingData(this);
-                },() => { }, scene.database, getBinaryData);
+                }, () => { }, scene.database, getBinaryData);
             }
         }
 
@@ -923,7 +923,7 @@
                 }
             };
 
-            Tools.LoadImage(url, onload,() => { }, scene.database);
+            Tools.LoadImage(url, onload, () => { }, scene.database);
         }
 
         public applyDisplacementMapFromBuffer(buffer: Uint8Array, heightMapWidth: number, heightMapHeight: number, minHeight: number, maxHeight: number): void {
@@ -944,7 +944,7 @@
             for (var index = 0; index < positions.length; index += 3) {
                 Vector3.FromArrayToRef(positions, index, position);
                 Vector3.FromArrayToRef(normals, index, normal);
-                Vector2.FromArrayToRef(uvs,(index / 3) * 2, uv);
+                Vector2.FromArrayToRef(uvs, (index / 3) * 2, uv);
 
                 // Compute height
                 var u = ((Math.abs(uv.x) * heightMapWidth) % heightMapWidth) | 0;
@@ -1025,8 +1025,8 @@
                 indices[index + 2] = index + 2;
 
                 var p1 = Vector3.FromArray(positions, index * 3);
-                var p2 = Vector3.FromArray(positions,(index + 1) * 3);
-                var p3 = Vector3.FromArray(positions,(index + 2) * 3);
+                var p2 = Vector3.FromArray(positions, (index + 1) * 3);
+                var p3 = Vector3.FromArray(positions, (index + 2) * 3);
 
                 var p1p2 = p1.subtract(p2);
                 var p3p2 = p3.subtract(p2);
@@ -1105,7 +1105,7 @@
             }
             var dupes = [];
 
-            AsyncLoop.SyncAsyncForLoop(vectorPositions.length, 40,(iteration) => {
+            AsyncLoop.SyncAsyncForLoop(vectorPositions.length, 40, (iteration) => {
                 var realPos = vectorPositions.length - 1 - iteration;
                 var testedPosition = vectorPositions[realPos];
                 for (var j = 0; j < realPos; ++j) {
@@ -1115,7 +1115,7 @@
                         break;
                     }
                 }
-            },() => {
+            }, () => {
                     for (var i = 0; i < indices.length; ++i) {
                         indices[i] = dupes[indices[i]] || indices[i];
                     }
@@ -1317,7 +1317,7 @@
                 }
             };
 
-            Tools.LoadImage(url, onload,() => { }, scene.database);
+            Tools.LoadImage(url, onload, () => { }, scene.database);
 
             return ground;
         }
@@ -1353,6 +1353,209 @@
             return tube;
         }
 
+        // Decals
+        public static CreateDecal(name: string, sourceMesh: AbstractMesh, position: Vector3, normal: Vector3, size: Vector3, angle: number = 0) {
+            var indices = sourceMesh.getIndices();
+            var positions = sourceMesh.getVerticesData(VertexBuffer.PositionKind);
+            var normals = sourceMesh.getVerticesData(VertexBuffer.NormalKind);
+
+            // Getting correct rotation
+            if (!normal) {
+                var target = new Vector3(0, 0, 1);
+                var camera = sourceMesh.getScene().activeCamera;
+                var cameraWorldTarget = Vector3.TransformCoordinates(target, camera.getWorldMatrix());
+
+                normal = camera.globalPosition.subtract(cameraWorldTarget);
+            }
+
+            var yaw = -Math.atan2(normal.z, normal.x) - Math.PI / 2;
+            var len = Math.sqrt(normal.x * normal.x + normal.z * normal.z);
+            var pitch = Math.atan2(normal.y, len);
+
+            // Matrix
+            var decalWorldMatrix = Matrix.RotationYawPitchRoll(yaw, pitch, angle).multiply(Matrix.Translation(position.x, position.y, position.z));
+            var inverseDecalWorldMatrix = Matrix.Invert(decalWorldMatrix);
+            var meshWorldMatrix = sourceMesh.getWorldMatrix();
+            var transformMatrix = meshWorldMatrix.multiply(inverseDecalWorldMatrix);
+
+            var vertexData = new VertexData();
+            vertexData.indices = [];
+            vertexData.positions = [];
+            vertexData.normals = [];
+            vertexData.uvs = [];
+
+            var currentVertexDataIndex = 0;
+
+            var extractDecalVector3 = (indexId: number): PositionNormalVertex => {
+                var vertexId = indices[indexId];
+                var result = new PositionNormalVertex();
+                result.position = new Vector3(positions[vertexId * 3], positions[vertexId * 3 + 1], positions[vertexId * 3 + 2]);
+
+                // Send vector to decal local world
+                result.position = Vector3.TransformCoordinates(result.position, transformMatrix);
+
+                // Get normal
+                result.normal = new Vector3(normals[vertexId * 3], normals[vertexId * 3 + 1], normals[vertexId * 3 + 2]);
+
+                return result;
+            }
+
+            
+            // Inspired by https://github.com/mrdoob/three.js/blob/eee231960882f6f3b6113405f524956145148146/examples/js/geometries/DecalGeometry.js
+            var clip = (vertices: PositionNormalVertex[], axis: Vector3): PositionNormalVertex[]=> {
+                if (vertices.length === 0) {
+                    return vertices;
+                }
+
+                var clipSize = 0.5 * Math.abs(Vector3.Dot(size, axis));
+
+                var clipVertices = (v0: PositionNormalVertex, v1: PositionNormalVertex): PositionNormalVertex => {
+                    var clipFactor = Vector3.GetClipFactor(v0.position, v1.position, axis, clipSize);
+
+                    return new PositionNormalVertex(
+                        Vector3.Lerp(v0.position, v1.position, clipFactor),
+                        Vector3.Lerp(v0.normal, v1.normal, clipFactor)
+                    );
+                }
+
+                var result = new Array<PositionNormalVertex>();
+
+                for (var index = 0; index < vertices.length; index += 3) {
+                    var v1Out: boolean;
+                    var v2Out: boolean;
+                    var v3Out: boolean;
+                    var total = 0;
+                    var nV1: PositionNormalVertex, nV2: PositionNormalVertex, nV3: PositionNormalVertex, nV4: PositionNormalVertex;
+
+                    var d1 = Vector3.Dot(vertices[index].position, axis) - clipSize;
+                    var d2 = Vector3.Dot(vertices[index + 1].position, axis) - clipSize;
+                    var d3 = Vector3.Dot(vertices[index + 2].position, axis) - clipSize;
+
+                    v1Out = d1 > 0;
+                    v2Out = d2 > 0;
+                    v3Out = d3 > 0;
+
+                    total = (v1Out ? 1 : 0) + (v2Out ? 1 : 0) + (v3Out ? 1 : 0);
+
+                    switch (total) {
+                        case 0:
+                            result.push(vertices[index]);
+                            result.push(vertices[index + 1]);
+                            result.push(vertices[index + 2]);
+                            break;
+                        case 1:
+
+                            if (v1Out) {
+                                nV1 = vertices[index + 1];
+                                nV2 = vertices[index + 2];
+                                nV3 = clipVertices(vertices[index], nV1);
+                                nV4 = clipVertices(vertices[index], nV2);
+                            }
+
+                            if (v2Out) {
+                                nV1 = vertices[index];
+                                nV2 = vertices[index + 2];
+                                nV3 = clipVertices(vertices[index + 1], nV1);
+                                nV4 = clipVertices(vertices[index + 1], nV2);
+
+                                result.push(nV3);
+                                result.push(nV2.clone());
+                                result.push(nV1.clone());
+
+                                result.push(nV2.clone());
+                                result.push(nV3.clone());
+                                result.push(nV4);
+                                break;
+                            }
+                            if (v3Out) {
+                                nV1 = vertices[index];
+                                nV2 = vertices[index + 1];
+                                nV3 = clipVertices(vertices[index + 2], nV1);
+                                nV4 = clipVertices(vertices[index + 2], nV2);
+                            }
+
+                            result.push(nV1.clone());
+                            result.push(nV2.clone());
+                            result.push(nV3);
+
+                            result.push(nV4);
+                            result.push(nV3.clone());
+                            result.push(nV2.clone());
+                            break;
+                        case 2:
+                            if (!v1Out) {
+                                nV1 = vertices[index].clone();
+                                nV2 = clipVertices(nV1, vertices[index + 1]);
+                                nV3 = clipVertices(nV1, vertices[index + 2]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            if (!v2Out) {
+                                nV1 = vertices[index + 1].clone();
+                                nV2 = clipVertices(nV1, vertices[index + 2]);
+                                nV3 = clipVertices(nV1, vertices[index]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            if (!v3Out) {
+                                nV1 = vertices[index + 2].clone();
+                                nV2 = clipVertices(nV1, vertices[index]);
+                                nV3 = clipVertices(nV1, vertices[index + 1]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            break;
+                        case 3:
+                            break;
+                    }
+                }
+
+                return result;
+            }
+
+            for (var index = 0; index < indices.length; index += 3) {
+                var faceVertices = new Array<PositionNormalVertex>();
+
+                faceVertices.push(extractDecalVector3(index));
+                faceVertices.push(extractDecalVector3(index + 1));
+                faceVertices.push(extractDecalVector3(index + 2));
+
+                // Clip
+                faceVertices = clip(faceVertices, new Vector3(1, 0, 0));
+                faceVertices = clip(faceVertices, new Vector3(-1, 0, 0));
+                faceVertices = clip(faceVertices, new Vector3(0, 1, 0));
+                faceVertices = clip(faceVertices, new Vector3(0, -1, 0));
+                faceVertices = clip(faceVertices, new Vector3(0, 0, 1));
+                faceVertices = clip(faceVertices, new Vector3(0, 0, -1));
+
+                if (faceVertices.length === 0) {
+                    continue;
+                }
+                
+                // Add UVs and get back to world
+                for (var vIndex = 0; vIndex < faceVertices.length; vIndex++) {
+                    var vertex = faceVertices[vIndex];
+
+                    vertexData.indices.push(currentVertexDataIndex);
+                    Vector3.TransformCoordinates(vertex.position, decalWorldMatrix).toArray(vertexData.positions, currentVertexDataIndex * 3);
+                    vertex.normal.toArray(vertexData.normals, currentVertexDataIndex * 3);
+                    vertexData.uvs.push(0.5 + vertex.position.x / size.x);
+                    vertexData.uvs.push(0.5 + vertex.position.y / size.y);
+
+                    currentVertexDataIndex++;
+                }
+            }
+
+            // Return mesh
+            var decal = new Mesh(name, sourceMesh.getScene());
+            vertexData.applyToMesh(decal);
+
+            return decal;
+        }
+
         // Tools
         public static MinMax(meshes: AbstractMesh[]): { min: Vector3; max: Vector3 } {
             var minVector: Vector3 = null;

+ 107 - 130
Babylon/Mesh/babylon.meshSimplification.js

@@ -1,9 +1,10 @@
 var BABYLON;
 (function (BABYLON) {
     var SimplificationSettings = (function () {
-        function SimplificationSettings(quality, distance) {
+        function SimplificationSettings(quality, distance, optimizeMesh) {
             this.quality = quality;
             this.distance = distance;
+            this.optimizeMesh = optimizeMesh;
         }
         return SimplificationSettings;
     })();
@@ -99,16 +100,18 @@ var BABYLON;
     })();
     BABYLON.DecimationTriangle = DecimationTriangle;
     var DecimationVertex = (function () {
-        function DecimationVertex(position, normal, uv, id) {
+        function DecimationVertex(position, id) {
             this.position = position;
-            this.normal = normal;
-            this.uv = uv;
             this.id = id;
             this.isBorder = true;
             this.q = new QuadraticMatrix();
             this.triangleCount = 0;
             this.triangleStart = 0;
+            this.originalOffsets = [];
         }
+        DecimationVertex.prototype.updatePosition = function (newPosition) {
+            this.position.copyFrom(newPosition);
+        };
         return DecimationVertex;
     })();
     BABYLON.DecimationVertex = DecimationVertex;
@@ -172,7 +175,7 @@ var BABYLON;
     var QuadraticErrorSimplification = (function () {
         function QuadraticErrorSimplification(_mesh) {
             this._mesh = _mesh;
-            this.initialised = false;
+            this.initialized = false;
             this.syncIterations = 5000;
             this.aggressiveness = 7;
             this.decimationIterations = 100;
@@ -183,11 +186,11 @@ var BABYLON;
             this.initDecimatedMesh();
             //iterating through the submeshes array, one after the other.
             BABYLON.AsyncLoop.Run(this._mesh.subMeshes.length, function (loop) {
-                _this.initWithMesh(_this._mesh, loop.index, function () {
+                _this.initWithMesh(loop.index, function () {
                     _this.runDecimation(settings, loop.index, function () {
                         loop.executeNext();
                     });
-                });
+                }, settings.optimizeMesh);
             }, function () {
                 setTimeout(function () {
                     successCallback(_this._reconstructedMesh);
@@ -197,9 +200,9 @@ var BABYLON;
         QuadraticErrorSimplification.prototype.isTriangleOnBoundingBox = function (triangle) {
             var _this = this;
             var gCount = 0;
-            triangle.vertices.forEach(function (vId) {
+            triangle.vertices.forEach(function (vertex) {
                 var count = 0;
-                var vPos = _this.vertices[vId].position;
+                var vPos = vertex.position;
                 var bbox = _this._mesh.getBoundingInfo().boundingBox;
                 if (bbox.maximum.x - vPos.x < _this.boundingBoxEpsilon || vPos.x - bbox.minimum.x > _this.boundingBoxEpsilon)
                     ++count;
@@ -243,10 +246,8 @@ var BABYLON;
                             if (t.error[j] < threshold) {
                                 var deleted0 = [];
                                 var deleted1 = [];
-                                var i0 = t.vertices[j];
-                                var i1 = t.vertices[(j + 1) % 3];
-                                var v0 = _this.vertices[i0];
-                                var v1 = _this.vertices[i1];
+                                var v0 = t.vertices[j];
+                                var v1 = t.vertices[(j + 1) % 3];
                                 if (v0.isBorder !== v1.isBorder)
                                     continue;
                                 var p = BABYLON.Vector3.Zero();
@@ -255,9 +256,9 @@ var BABYLON;
                                 var color = new BABYLON.Color4(0, 0, 0, 1);
                                 _this.calculateError(v0, v1, p, n, uv, color);
                                 var delTr = [];
-                                if (_this.isFlipped(v0, i1, p, deleted0, t.borderFactor, delTr))
+                                if (_this.isFlipped(v0, v1, p, deleted0, t.borderFactor, delTr))
                                     continue;
-                                if (_this.isFlipped(v1, i0, p, deleted1, t.borderFactor, delTr))
+                                if (_this.isFlipped(v1, v0, p, deleted1, t.borderFactor, delTr))
                                     continue;
                                 if (deleted0.indexOf(true) < 0 || deleted1.indexOf(true) < 0)
                                     continue;
@@ -271,16 +272,11 @@ var BABYLON;
                                 if (uniqueArray.length % 2 != 0) {
                                     continue;
                                 }
-                                v0.normal = n;
-                                if (v0.uv)
-                                    v0.uv = uv;
-                                else if (v0.color)
-                                    v0.color = color;
                                 v0.q = v1.q.add(v0.q);
-                                v0.position = p;
+                                v0.updatePosition(p);
                                 var tStart = _this.references.length;
-                                deletedTriangles = _this.updateTriangles(v0.id, v0, deleted0, deletedTriangles);
-                                deletedTriangles = _this.updateTriangles(v0.id, v1, deleted1, deletedTriangles);
+                                deletedTriangles = _this.updateTriangles(v0, v0, deleted0, deletedTriangles);
+                                deletedTriangles = _this.updateTriangles(v0, v1, deleted1, deletedTriangles);
                                 var tCount = _this.references.length - tStart;
                                 if (tCount <= v0.triangleCount) {
                                     if (tCount) {
@@ -318,41 +314,48 @@ var BABYLON;
                 }, 0);
             });
         };
-        QuadraticErrorSimplification.prototype.initWithMesh = function (mesh, submeshIndex, callback) {
+        QuadraticErrorSimplification.prototype.initWithMesh = function (submeshIndex, callback, optimizeMesh) {
             var _this = this;
-            if (!mesh)
-                return;
             this.vertices = [];
             this.triangles = [];
-            this._mesh = mesh;
-            //It is assumed that a mesh has positions, normals and either uvs or colors.
             var positionData = this._mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
-            var normalData = this._mesh.getVerticesData(BABYLON.VertexBuffer.NormalKind);
-            var uvs = this._mesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
-            var colorsData = this._mesh.getVerticesData(BABYLON.VertexBuffer.ColorKind);
-            var indices = mesh.getIndices();
-            var submesh = mesh.subMeshes[submeshIndex];
+            var indices = this._mesh.getIndices();
+            var submesh = this._mesh.subMeshes[submeshIndex];
+            var findInVertices = function (positionToSearch) {
+                if (optimizeMesh) {
+                    for (var ii = 0; ii < _this.vertices.length; ++ii) {
+                        if (_this.vertices[ii].position.equals(positionToSearch)) {
+                            return _this.vertices[ii];
+                        }
+                    }
+                }
+                return null;
+            };
+            var vertexReferences = [];
             var vertexInit = function (i) {
                 var offset = i + submesh.verticesStart;
-                var vertex = new DecimationVertex(BABYLON.Vector3.FromArray(positionData, offset * 3), BABYLON.Vector3.FromArray(normalData, offset * 3), null, i);
-                if (_this._mesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                    vertex.uv = BABYLON.Vector2.FromArray(uvs, offset * 2);
+                var position = BABYLON.Vector3.FromArray(positionData, offset * 3);
+                var vertex = findInVertices(position) || new DecimationVertex(position, _this.vertices.length);
+                vertex.originalOffsets.push(offset);
+                if (vertex.id == _this.vertices.length) {
+                    _this.vertices.push(vertex);
                 }
-                else if (_this._mesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
-                    vertex.color = BABYLON.Color4.FromArray(colorsData, offset * 4);
-                }
-                _this.vertices.push(vertex);
+                vertexReferences.push(vertex.id);
             };
             //var totalVertices = mesh.getTotalVertices();
             var totalVertices = submesh.verticesCount;
-            BABYLON.AsyncLoop.SyncAsyncForLoop(totalVertices, this.syncIterations, vertexInit, function () {
+            BABYLON.AsyncLoop.SyncAsyncForLoop(totalVertices, (this.syncIterations / 4) >> 0, vertexInit, function () {
                 var indicesInit = function (i) {
                     var offset = (submesh.indexStart / 3) + i;
                     var pos = (offset * 3);
-                    var i0 = indices[pos + 0] - submesh.verticesStart;
-                    var i1 = indices[pos + 1] - submesh.verticesStart;
-                    var i2 = indices[pos + 2] - submesh.verticesStart;
-                    var triangle = new DecimationTriangle([_this.vertices[i0].id, _this.vertices[i1].id, _this.vertices[i2].id]);
+                    var i0 = indices[pos + 0];
+                    var i1 = indices[pos + 1];
+                    var i2 = indices[pos + 2];
+                    var v0 = _this.vertices[vertexReferences[i0 - submesh.verticesStart]];
+                    var v1 = _this.vertices[vertexReferences[i1 - submesh.verticesStart]];
+                    var v2 = _this.vertices[vertexReferences[i2 - submesh.verticesStart]];
+                    var triangle = new DecimationTriangle([v0, v1, v2]);
+                    triangle.originalOffset = pos;
                     _this.triangles.push(triangle);
                 };
                 BABYLON.AsyncLoop.SyncAsyncForLoop(submesh.indexCount / 3, _this.syncIterations, indicesInit, function () {
@@ -364,21 +367,21 @@ var BABYLON;
             var _this = this;
             var triangleInit1 = function (i) {
                 var t = _this.triangles[i];
-                t.normal = BABYLON.Vector3.Cross(_this.vertices[t.vertices[1]].position.subtract(_this.vertices[t.vertices[0]].position), _this.vertices[t.vertices[2]].position.subtract(_this.vertices[t.vertices[0]].position)).normalize();
+                t.normal = BABYLON.Vector3.Cross(t.vertices[1].position.subtract(t.vertices[0].position), t.vertices[2].position.subtract(t.vertices[0].position)).normalize();
                 for (var j = 0; j < 3; j++) {
-                    _this.vertices[t.vertices[j]].q.addArrayInPlace(QuadraticMatrix.DataFromNumbers(t.normal.x, t.normal.y, t.normal.z, -(BABYLON.Vector3.Dot(t.normal, _this.vertices[t.vertices[0]].position))));
+                    t.vertices[j].q.addArrayInPlace(QuadraticMatrix.DataFromNumbers(t.normal.x, t.normal.y, t.normal.z, -(BABYLON.Vector3.Dot(t.normal, t.vertices[0].position))));
                 }
             };
             BABYLON.AsyncLoop.SyncAsyncForLoop(this.triangles.length, this.syncIterations, triangleInit1, function () {
                 var triangleInit2 = function (i) {
                     var t = _this.triangles[i];
                     for (var j = 0; j < 3; ++j) {
-                        t.error[j] = _this.calculateError(_this.vertices[t.vertices[j]], _this.vertices[t.vertices[(j + 1) % 3]]);
+                        t.error[j] = _this.calculateError(t.vertices[j], t.vertices[(j + 1) % 3]);
                     }
                     t.error[3] = Math.min(t.error[0], t.error[1], t.error[2]);
                 };
                 BABYLON.AsyncLoop.SyncAsyncForLoop(_this.triangles.length, _this.syncIterations, triangleInit2, function () {
-                    _this.initialised = true;
+                    _this.initialized = true;
                     callback();
                 });
             });
@@ -395,52 +398,42 @@ var BABYLON;
                 if (!this.triangles[i].deleted) {
                     t = this.triangles[i];
                     for (j = 0; j < 3; ++j) {
-                        this.vertices[t.vertices[j]].triangleCount = 1;
+                        t.vertices[j].triangleCount = 1;
                     }
                     newTriangles.push(t);
                 }
             }
-            var newVerticesOrder = [];
-            //compact vertices, get the IDs of the vertices used.
-            var dst = 0;
-            for (i = 0; i < this.vertices.length; ++i) {
-                if (this.vertices[i].triangleCount) {
-                    this.vertices[i].triangleStart = dst;
-                    this.vertices[dst].position = this.vertices[i].position;
-                    this.vertices[dst].normal = this.vertices[i].normal;
-                    this.vertices[dst].uv = this.vertices[i].uv;
-                    this.vertices[dst].color = this.vertices[i].color;
-                    newVerticesOrder.push(dst);
-                    dst++;
-                }
-            }
-            for (i = 0; i < newTriangles.length; ++i) {
-                t = newTriangles[i];
-                for (j = 0; j < 3; ++j) {
-                    t.vertices[j] = this.vertices[t.vertices[j]].triangleStart;
-                }
-            }
-            this.vertices = this.vertices.slice(0, dst);
             var newPositionData = this._reconstructedMesh.getVerticesData(BABYLON.VertexBuffer.PositionKind) || [];
             var newNormalData = this._reconstructedMesh.getVerticesData(BABYLON.VertexBuffer.NormalKind) || [];
             var newUVsData = this._reconstructedMesh.getVerticesData(BABYLON.VertexBuffer.UVKind) || [];
             var newColorsData = this._reconstructedMesh.getVerticesData(BABYLON.VertexBuffer.ColorKind) || [];
-            for (i = 0; i < newVerticesOrder.length; ++i) {
-                newPositionData.push(this.vertices[i].position.x);
-                newPositionData.push(this.vertices[i].position.y);
-                newPositionData.push(this.vertices[i].position.z);
-                newNormalData.push(this.vertices[i].normal.x);
-                newNormalData.push(this.vertices[i].normal.y);
-                newNormalData.push(this.vertices[i].normal.z);
-                if (this.vertices[i].uv) {
-                    newUVsData.push(this.vertices[i].uv.x);
-                    newUVsData.push(this.vertices[i].uv.y);
-                }
-                else if (this.vertices[i].color) {
-                    newColorsData.push(this.vertices[i].color.r);
-                    newColorsData.push(this.vertices[i].color.g);
-                    newColorsData.push(this.vertices[i].color.b);
-                    newColorsData.push(this.vertices[i].color.a);
+            var normalData = this._mesh.getVerticesData(BABYLON.VertexBuffer.NormalKind);
+            var uvs = this._mesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
+            var colorsData = this._mesh.getVerticesData(BABYLON.VertexBuffer.ColorKind);
+            var vertexCount = 0;
+            for (i = 0; i < this.vertices.length; ++i) {
+                var vertex = this.vertices[i];
+                vertex.id = vertexCount;
+                if (vertex.triangleCount) {
+                    vertex.originalOffsets.forEach(function (originalOffset) {
+                        newPositionData.push(vertex.position.x);
+                        newPositionData.push(vertex.position.y);
+                        newPositionData.push(vertex.position.z);
+                        newNormalData.push(normalData[originalOffset * 3]);
+                        newNormalData.push(normalData[(originalOffset * 3) + 1]);
+                        newNormalData.push(normalData[(originalOffset * 3) + 2]);
+                        if (uvs && uvs.length) {
+                            newUVsData.push(uvs[(originalOffset * 2)]);
+                            newUVsData.push(uvs[(originalOffset * 2) + 1]);
+                        }
+                        else if (colorsData && colorsData.length) {
+                            newColorsData.push(colorsData[(originalOffset * 4)]);
+                            newColorsData.push(colorsData[(originalOffset * 4) + 1]);
+                            newColorsData.push(colorsData[(originalOffset * 4) + 2]);
+                            newColorsData.push(colorsData[(originalOffset * 4) + 3]);
+                        }
+                        ++vertexCount;
+                    });
                 }
             }
             var startingIndex = this._reconstructedMesh.getTotalIndices();
@@ -448,10 +441,17 @@ var BABYLON;
             var submeshesArray = this._reconstructedMesh.subMeshes;
             this._reconstructedMesh.subMeshes = [];
             var newIndicesArray = this._reconstructedMesh.getIndices(); //[];
+            var originalIndices = this._mesh.getIndices();
             for (i = 0; i < newTriangles.length; ++i) {
-                newIndicesArray.push(newTriangles[i].vertices[0] + startingVertex);
-                newIndicesArray.push(newTriangles[i].vertices[1] + startingVertex);
-                newIndicesArray.push(newTriangles[i].vertices[2] + startingVertex);
+                var t = newTriangles[i];
+                //now get the new referencing point for each vertex
+                [0, 1, 2].forEach(function (idx) {
+                    var id = originalIndices[t.originalOffset + idx];
+                    var offset = t.vertices[idx].originalOffsets.indexOf(id);
+                    if (offset < 0)
+                        offset = 0;
+                    newIndicesArray.push(t.vertices[idx].id + offset + startingVertex);
+                });
             }
             //overwriting the old vertex buffers and indices.
             this._reconstructedMesh.setIndices(newIndicesArray);
@@ -468,7 +468,7 @@ var BABYLON;
                 submeshesArray.forEach(function (submesh) {
                     new BABYLON.SubMesh(submesh.materialIndex, submesh.verticesStart, submesh.verticesCount, submesh.indexStart, submesh.indexCount, submesh.getMesh());
                 });
-                var newSubmesh = new BABYLON.SubMesh(originalSubmesh.materialIndex, startingVertex, newVerticesOrder.length, startingIndex, newTriangles.length * 3, this._reconstructedMesh);
+                var newSubmesh = new BABYLON.SubMesh(originalSubmesh.materialIndex, startingVertex, vertexCount, startingIndex, newTriangles.length * 3, this._reconstructedMesh);
             }
         };
         QuadraticErrorSimplification.prototype.initDecimatedMesh = function () {
@@ -477,22 +477,22 @@ var BABYLON;
             this._reconstructedMesh.parent = this._mesh.parent;
             this._reconstructedMesh.isVisible = false;
         };
-        QuadraticErrorSimplification.prototype.isFlipped = function (vertex1, index2, point, deletedArray, borderFactor, delTr) {
+        QuadraticErrorSimplification.prototype.isFlipped = function (vertex1, vertex2, point, deletedArray, borderFactor, delTr) {
             for (var i = 0; i < vertex1.triangleCount; ++i) {
                 var t = this.triangles[this.references[vertex1.triangleStart + i].triangleId];
                 if (t.deleted)
                     continue;
                 var s = this.references[vertex1.triangleStart + i].vertexId;
-                var id1 = t.vertices[(s + 1) % 3];
-                var id2 = t.vertices[(s + 2) % 3];
-                if ((id1 === index2 || id2 === index2)) {
+                var v1 = t.vertices[(s + 1) % 3];
+                var v2 = t.vertices[(s + 2) % 3];
+                if ((v1 === vertex2 || v2 === vertex2)) {
                     deletedArray[i] = true;
                     delTr.push(t);
                     continue;
                 }
-                var d1 = this.vertices[id1].position.subtract(point);
+                var d1 = v1.position.subtract(point);
                 d1 = d1.normalize();
-                var d2 = this.vertices[id2].position.subtract(point);
+                var d2 = v2.position.subtract(point);
                 d2 = d2.normalize();
                 if (Math.abs(BABYLON.Vector3.Dot(d1, d2)) > 0.999)
                     return true;
@@ -503,7 +503,7 @@ var BABYLON;
             }
             return false;
         };
-        QuadraticErrorSimplification.prototype.updateTriangles = function (vertexId, vertex, deletedArray, deletedTriangles) {
+        QuadraticErrorSimplification.prototype.updateTriangles = function (origVertex, vertex, deletedArray, deletedTriangles) {
             var newDeleted = deletedTriangles;
             for (var i = 0; i < vertex.triangleCount; ++i) {
                 var ref = this.references[vertex.triangleStart + i];
@@ -515,11 +515,11 @@ var BABYLON;
                     newDeleted++;
                     continue;
                 }
-                t.vertices[ref.vertexId] = vertexId;
+                t.vertices[ref.vertexId] = origVertex;
                 t.isDirty = true;
-                t.error[0] = this.calculateError(this.vertices[t.vertices[0]], this.vertices[t.vertices[1]]) + (t.borderFactor / 2);
-                t.error[1] = this.calculateError(this.vertices[t.vertices[1]], this.vertices[t.vertices[2]]) + (t.borderFactor / 2);
-                t.error[2] = this.calculateError(this.vertices[t.vertices[2]], this.vertices[t.vertices[0]]) + (t.borderFactor / 2);
+                t.error[0] = this.calculateError(t.vertices[0], t.vertices[1]) + (t.borderFactor / 2);
+                t.error[1] = this.calculateError(t.vertices[1], t.vertices[2]) + (t.borderFactor / 2);
+                t.error[2] = this.calculateError(t.vertices[2], t.vertices[0]) + (t.borderFactor / 2);
                 t.error[3] = Math.min(t.error[0], t.error[1], t.error[2]);
                 this.references.push(ref);
             }
@@ -535,15 +535,15 @@ var BABYLON;
                     var triangle = this.triangles[this.references[v.triangleStart + j].triangleId];
                     for (var ii = 0; ii < 3; ii++) {
                         var ofs = 0;
-                        var id = triangle.vertices[ii];
+                        var vv = triangle.vertices[ii];
                         while (ofs < vCount.length) {
-                            if (vId[ofs] === id)
+                            if (vId[ofs] === vv.id)
                                 break;
                             ++ofs;
                         }
                         if (ofs === vCount.length) {
                             vCount.push(1);
-                            vId.push(id);
+                            vId.push(vv.id);
                         }
                         else {
                             vCount[ofs]++;
@@ -582,7 +582,7 @@ var BABYLON;
             for (i = 0; i < this.triangles.length; ++i) {
                 t = this.triangles[i];
                 for (j = 0; j < 3; ++j) {
-                    v = this.vertices[t.vertices[j]];
+                    v = t.vertices[j];
                     v.triangleCount++;
                 }
             }
@@ -596,7 +596,7 @@ var BABYLON;
             for (i = 0; i < this.triangles.length; ++i) {
                 t = this.triangles[i];
                 for (j = 0; j < 3; ++j) {
-                    v = this.vertices[t.vertices[j]];
+                    v = t.vertices[j];
                     newReferences[v.triangleStart + v.triangleCount] = new Reference(j, i);
                     v.triangleCount++;
                 }
@@ -625,14 +625,6 @@ var BABYLON;
                 pointResult.y = 1 / qDet * (q.det(0, 2, 3, 1, 5, 6, 2, 7, 8));
                 pointResult.z = -1 / qDet * (q.det(0, 1, 3, 1, 4, 6, 2, 5, 8));
                 error = this.vertexError(q, pointResult);
-                //TODO this should be correctly calculated
-                if (normalResult) {
-                    normalResult.copyFrom(vertex1.normal);
-                    if (vertex1.uv)
-                        uvResult.copyFrom(vertex1.uv);
-                    else if (vertex1.color)
-                        colorResult.copyFrom(vertex1.color);
-                }
             }
             else {
                 var p3 = (vertex1.position.add(vertex2.position)).divide(new BABYLON.Vector3(2, 2, 2));
@@ -644,31 +636,16 @@ var BABYLON;
                 if (error === error1) {
                     if (pointResult) {
                         pointResult.copyFrom(vertex1.position);
-                        normalResult.copyFrom(vertex1.normal);
-                        if (vertex1.uv)
-                            uvResult.copyFrom(vertex1.uv);
-                        else if (vertex1.color)
-                            colorResult.copyFrom(vertex1.color);
                     }
                 }
                 else if (error === error2) {
                     if (pointResult) {
                         pointResult.copyFrom(vertex2.position);
-                        normalResult.copyFrom(vertex2.normal);
-                        if (vertex2.uv)
-                            uvResult.copyFrom(vertex2.uv);
-                        else if (vertex2.color)
-                            colorResult.copyFrom(vertex2.color);
                     }
                 }
                 else {
                     if (pointResult) {
                         pointResult.copyFrom(p3);
-                        normalResult.copyFrom(vertex1.normal);
-                        if (vertex1.uv)
-                            uvResult.copyFrom(vertex1.uv);
-                        else if (vertex1.color)
-                            colorResult.copyFrom(vertex1.color);
                     }
                 }
             }

+ 115 - 132
Babylon/Mesh/babylon.meshSimplification.ts

@@ -22,10 +22,11 @@
     export interface ISimplificationSettings {
         quality: number;
         distance: number;
+        optimizeMesh?: boolean;
     }
 
     export class SimplificationSettings implements ISimplificationSettings {
-        constructor(public quality: number, public distance: number) {
+        constructor(public quality: number, public distance: number, public optimizeMesh?: boolean) {
         }
     }
 
@@ -128,7 +129,9 @@
         public borderFactor: number;
         public deletePending: boolean;
 
-        constructor(public vertices: Array<number>) {
+        public originalOffset: number;
+
+        constructor(public vertices: Array<DecimationVertex>) {
             this.error = new Array<number>(4);
             this.deleted = false;
             this.isDirty = false;
@@ -144,14 +147,18 @@
         public triangleStart: number;
         public triangleCount: number;
 
-        //if color is present instead of uvs.
-        public color: Color4;
+        public originalOffsets: Array<number>;
 
-        constructor(public position: Vector3, public normal: Vector3, public uv: Vector2, public id) {
+        constructor(public position: Vector3, public id) {
             this.isBorder = true;
             this.q = new QuadraticMatrix();
             this.triangleCount = 0;
             this.triangleStart = 0;
+            this.originalOffsets = [];
+        }
+
+        public updatePosition(newPosition: Vector3) {
+            this.position.copyFrom(newPosition);
         }
     }
 
@@ -222,7 +229,7 @@
         private vertices: Array<DecimationVertex>;
         private references: Array<Reference>;
 
-        private initialised: boolean = false;
+        private initialized: boolean = false;
 
         private _reconstructedMesh: Mesh;
 
@@ -237,18 +244,17 @@
             this.aggressiveness = 7;
             this.decimationIterations = 100;
             this.boundingBoxEpsilon = Engine.Epsilon;
-
         }
 
         public simplify(settings: ISimplificationSettings, successCallback: (simplifiedMesh: Mesh) => void) {
             this.initDecimatedMesh();
             //iterating through the submeshes array, one after the other.
             AsyncLoop.Run(this._mesh.subMeshes.length,(loop: AsyncLoop) => {
-                this.initWithMesh(this._mesh, loop.index,() => {
+                this.initWithMesh(loop.index,() => {
                     this.runDecimation(settings, loop.index,() => {
                         loop.executeNext();
                     });
-                });
+                }, settings.optimizeMesh);
             },() => {
                     setTimeout(() => {
                         successCallback(this._reconstructedMesh);
@@ -258,9 +264,9 @@
 
         private isTriangleOnBoundingBox(triangle: DecimationTriangle): boolean {
             var gCount = 0;
-            triangle.vertices.forEach((vId) => {
+            triangle.vertices.forEach((vertex) => {
                 var count = 0;
-                var vPos = this.vertices[vId].position;
+                var vPos = vertex.position;
                 var bbox = this._mesh.getBoundingInfo().boundingBox;
 
                 if (bbox.maximum.x - vPos.x < this.boundingBoxEpsilon || vPos.x - bbox.minimum.x > this.boundingBoxEpsilon)
@@ -311,10 +317,8 @@
                                 var deleted0: Array<boolean> = [];
                                 var deleted1: Array<boolean> = [];
 
-                                var i0 = t.vertices[j];
-                                var i1 = t.vertices[(j + 1) % 3];
-                                var v0 = this.vertices[i0];
-                                var v1 = this.vertices[i1];
+                                var v0 = t.vertices[j];
+                                var v1 = t.vertices[(j + 1) % 3];
 
                                 if (v0.isBorder !== v1.isBorder) continue;
 
@@ -327,8 +331,8 @@
 
                                 var delTr = [];
 
-                                if (this.isFlipped(v0, i1, p, deleted0, t.borderFactor, delTr)) continue;
-                                if (this.isFlipped(v1, i0, p, deleted1, t.borderFactor, delTr)) continue;
+                                if (this.isFlipped(v0, v1, p, deleted0, t.borderFactor, delTr)) continue;
+                                if (this.isFlipped(v1, v0, p, deleted1, t.borderFactor, delTr)) continue;
 
                                 if (deleted0.indexOf(true) < 0 || deleted1.indexOf(true) < 0)
                                     continue;
@@ -345,19 +349,14 @@
                                     continue;
                                 }
 
-                                v0.normal = n;
-                                if (v0.uv)
-                                    v0.uv = uv;
-                                else if (v0.color)
-                                    v0.color = color;
                                 v0.q = v1.q.add(v0.q);
 
-                                v0.position = p;
+                                v0.updatePosition(p);
 
                                 var tStart = this.references.length;
 
-                                deletedTriangles = this.updateTriangles(v0.id, v0, deleted0, deletedTriangles);
-                                deletedTriangles = this.updateTriangles(v0.id, v1, deleted1, deletedTriangles);
+                                deletedTriangles = this.updateTriangles(v0, v0, deleted0, deletedTriangles);
+                                deletedTriangles = this.updateTriangles(v0, v1, deleted1, deletedTriangles);
 
                                 var tCount = this.references.length - tStart;
 
@@ -396,42 +395,55 @@
                 });
         }
 
-        private initWithMesh(mesh: Mesh, submeshIndex: number, callback: Function) {
-            if (!mesh) return;
+        private initWithMesh(submeshIndex: number, callback: Function, optimizeMesh?: boolean) {
 
             this.vertices = [];
             this.triangles = [];
 
-            this._mesh = mesh;
-            //It is assumed that a mesh has positions, normals and either uvs or colors.
             var positionData = this._mesh.getVerticesData(VertexBuffer.PositionKind);
-            var normalData = this._mesh.getVerticesData(VertexBuffer.NormalKind);
-            var uvs = this._mesh.getVerticesData(VertexBuffer.UVKind);
-            var colorsData = this._mesh.getVerticesData(VertexBuffer.ColorKind);
-            var indices = mesh.getIndices();
-            var submesh = mesh.subMeshes[submeshIndex];
+
+            var indices = this._mesh.getIndices();
+            var submesh = this._mesh.subMeshes[submeshIndex];
+
+            var findInVertices = (positionToSearch: Vector3) => {
+                if (optimizeMesh) {
+                    for (var ii = 0; ii < this.vertices.length; ++ii) {
+                        if (this.vertices[ii].position.equals(positionToSearch)) {
+                            return this.vertices[ii];
+                        }
+                    }
+                }
+                return null;
+            }
+
+            var vertexReferences: Array<number> = [];
 
             var vertexInit = (i) => {
                 var offset = i + submesh.verticesStart;
-                var vertex = new DecimationVertex(Vector3.FromArray(positionData, offset * 3), Vector3.FromArray(normalData, offset * 3), null, i);
-                if (this._mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                    vertex.uv = Vector2.FromArray(uvs, offset * 2);
-                } else if (this._mesh.isVerticesDataPresent(VertexBuffer.ColorKind)) {
-                    vertex.color = Color4.FromArray(colorsData, offset * 4);
+                var position = Vector3.FromArray(positionData, offset * 3);
+
+                var vertex = findInVertices(position) || new DecimationVertex(position, this.vertices.length);
+                vertex.originalOffsets.push(offset);
+                if (vertex.id == this.vertices.length) {
+                    this.vertices.push(vertex);
                 }
-                this.vertices.push(vertex);
+                vertexReferences.push(vertex.id);
             };
             //var totalVertices = mesh.getTotalVertices();
             var totalVertices = submesh.verticesCount;
-            AsyncLoop.SyncAsyncForLoop(totalVertices, this.syncIterations, vertexInit,() => {
+            AsyncLoop.SyncAsyncForLoop(totalVertices,(this.syncIterations / 4) >> 0, vertexInit,() => {
 
                 var indicesInit = (i) => {
                     var offset = (submesh.indexStart / 3) + i;
                     var pos = (offset * 3);
-                    var i0 = indices[pos + 0] - submesh.verticesStart;
-                    var i1 = indices[pos + 1] - submesh.verticesStart;
-                    var i2 = indices[pos + 2] - submesh.verticesStart;
-                    var triangle = new DecimationTriangle([this.vertices[i0].id, this.vertices[i1].id, this.vertices[i2].id]);
+                    var i0 = indices[pos + 0];
+                    var i1 = indices[pos + 1];
+                    var i2 = indices[pos + 2];
+                    var v0: DecimationVertex = this.vertices[vertexReferences[i0 - submesh.verticesStart]];
+                    var v1: DecimationVertex = this.vertices[vertexReferences[i1 - submesh.verticesStart]];
+                    var v2: DecimationVertex = this.vertices[vertexReferences[i2 - submesh.verticesStart]];
+                    var triangle = new DecimationTriangle([v0, v1, v2]);
+                    triangle.originalOffset = pos;
                     this.triangles.push(triangle);
                 };
                 AsyncLoop.SyncAsyncForLoop(submesh.indexCount / 3, this.syncIterations, indicesInit,() => {
@@ -443,9 +455,9 @@
         private init(callback: Function) {
             var triangleInit1 = (i) => {
                 var t = this.triangles[i];
-                t.normal = Vector3.Cross(this.vertices[t.vertices[1]].position.subtract(this.vertices[t.vertices[0]].position), this.vertices[t.vertices[2]].position.subtract(this.vertices[t.vertices[0]].position)).normalize();
+                t.normal = Vector3.Cross(t.vertices[1].position.subtract(t.vertices[0].position), t.vertices[2].position.subtract(t.vertices[0].position)).normalize();
                 for (var j = 0; j < 3; j++) {
-                    this.vertices[t.vertices[j]].q.addArrayInPlace(QuadraticMatrix.DataFromNumbers(t.normal.x, t.normal.y, t.normal.z, -(Vector3.Dot(t.normal, this.vertices[t.vertices[0]].position))));
+                    t.vertices[j].q.addArrayInPlace(QuadraticMatrix.DataFromNumbers(t.normal.x, t.normal.y, t.normal.z, -(Vector3.Dot(t.normal, t.vertices[0].position))));
                 }
             };
             AsyncLoop.SyncAsyncForLoop(this.triangles.length, this.syncIterations, triangleInit1,() => {
@@ -453,12 +465,12 @@
                 var triangleInit2 = (i) => {
                     var t = this.triangles[i];
                     for (var j = 0; j < 3; ++j) {
-                        t.error[j] = this.calculateError(this.vertices[t.vertices[j]], this.vertices[t.vertices[(j + 1) % 3]]);
+                        t.error[j] = this.calculateError(t.vertices[j], t.vertices[(j + 1) % 3]);
                     }
                     t.error[3] = Math.min(t.error[0], t.error[1], t.error[2]);
                 };
                 AsyncLoop.SyncAsyncForLoop(this.triangles.length, this.syncIterations, triangleInit2,() => {
-                    this.initialised = true;
+                    this.initialized = true;
                     callback();
                 });
             });
@@ -477,56 +489,44 @@
                 if (!this.triangles[i].deleted) {
                     t = this.triangles[i];
                     for (j = 0; j < 3; ++j) {
-                        this.vertices[t.vertices[j]].triangleCount = 1;
+                        t.vertices[j].triangleCount = 1;
                     }
                     newTriangles.push(t);
                 }
             }
 
-            var newVerticesOrder = [];
-
-            //compact vertices, get the IDs of the vertices used.
-            var dst = 0;
-            for (i = 0; i < this.vertices.length; ++i) {
-                if (this.vertices[i].triangleCount) {
-                    this.vertices[i].triangleStart = dst;
-                    this.vertices[dst].position = this.vertices[i].position;
-                    this.vertices[dst].normal = this.vertices[i].normal;
-                    this.vertices[dst].uv = this.vertices[i].uv;
-                    this.vertices[dst].color = this.vertices[i].color;
-                    newVerticesOrder.push(dst);
-                    dst++;
-                }
-            }
-
-            for (i = 0; i < newTriangles.length; ++i) {
-                t = newTriangles[i];
-                for (j = 0; j < 3; ++j) {
-                    t.vertices[j] = this.vertices[t.vertices[j]].triangleStart;
-                }
-            }
-            this.vertices = this.vertices.slice(0, dst);
-
             var newPositionData = this._reconstructedMesh.getVerticesData(VertexBuffer.PositionKind) || [];
             var newNormalData = this._reconstructedMesh.getVerticesData(VertexBuffer.NormalKind) || [];
             var newUVsData = this._reconstructedMesh.getVerticesData(VertexBuffer.UVKind) || [];
             var newColorsData = this._reconstructedMesh.getVerticesData(VertexBuffer.ColorKind) || [];
 
-            for (i = 0; i < newVerticesOrder.length; ++i) {
-                newPositionData.push(this.vertices[i].position.x);
-                newPositionData.push(this.vertices[i].position.y);
-                newPositionData.push(this.vertices[i].position.z);
-                newNormalData.push(this.vertices[i].normal.x);
-                newNormalData.push(this.vertices[i].normal.y);
-                newNormalData.push(this.vertices[i].normal.z);
-                if (this.vertices[i].uv) {
-                    newUVsData.push(this.vertices[i].uv.x);
-                    newUVsData.push(this.vertices[i].uv.y);
-                } else if (this.vertices[i].color) {
-                    newColorsData.push(this.vertices[i].color.r);
-                    newColorsData.push(this.vertices[i].color.g);
-                    newColorsData.push(this.vertices[i].color.b);
-                    newColorsData.push(this.vertices[i].color.a);
+            var normalData = this._mesh.getVerticesData(VertexBuffer.NormalKind);
+            var uvs = this._mesh.getVerticesData(VertexBuffer.UVKind);
+            var colorsData = this._mesh.getVerticesData(VertexBuffer.ColorKind);
+
+            var vertexCount = 0;
+            for (i = 0; i < this.vertices.length; ++i) {
+                var vertex = this.vertices[i];
+                vertex.id = vertexCount;
+                if (vertex.triangleCount) {
+                    vertex.originalOffsets.forEach(function (originalOffset) {
+                        newPositionData.push(vertex.position.x);
+                        newPositionData.push(vertex.position.y);
+                        newPositionData.push(vertex.position.z);
+                        newNormalData.push(normalData[originalOffset * 3]);
+                        newNormalData.push(normalData[(originalOffset * 3) + 1]);
+                        newNormalData.push(normalData[(originalOffset * 3) + 2]);
+                        if (uvs && uvs.length) {
+                            newUVsData.push(uvs[(originalOffset * 2)]);
+                            newUVsData.push(uvs[(originalOffset * 2) + 1]);
+                        } else if (colorsData && colorsData.length) {
+                            newColorsData.push(colorsData[(originalOffset * 4)]);
+                            newColorsData.push(colorsData[(originalOffset * 4) + 1]);
+                            newColorsData.push(colorsData[(originalOffset * 4) + 2]);
+                            newColorsData.push(colorsData[(originalOffset * 4) + 3]);
+                        }
+                        ++vertexCount;
+                    });
                 }
             }
 
@@ -537,10 +537,16 @@
             this._reconstructedMesh.subMeshes = [];
 
             var newIndicesArray: Array<number> = this._reconstructedMesh.getIndices(); //[];
+            var originalIndices = this._mesh.getIndices();
             for (i = 0; i < newTriangles.length; ++i) {
-                newIndicesArray.push(newTriangles[i].vertices[0] + startingVertex);
-                newIndicesArray.push(newTriangles[i].vertices[1] + startingVertex);
-                newIndicesArray.push(newTriangles[i].vertices[2] + startingVertex);
+                var t = newTriangles[i];
+                //now get the new referencing point for each vertex
+                [0, 1, 2].forEach(function (idx) {
+                    var id = originalIndices[t.originalOffset + idx]
+                    var offset = t.vertices[idx].originalOffsets.indexOf(id);
+                    if (offset < 0) offset = 0;
+                    newIndicesArray.push(t.vertices[idx].id + offset + startingVertex);
+                });
             }
 
             //overwriting the old vertex buffers and indices.
@@ -560,7 +566,7 @@
                 submeshesArray.forEach(function (submesh) {
                     new SubMesh(submesh.materialIndex, submesh.verticesStart, submesh.verticesCount,/* 0, newPositionData.length/3, */submesh.indexStart, submesh.indexCount, submesh.getMesh());
                 });
-                var newSubmesh = new SubMesh(originalSubmesh.materialIndex, startingVertex, newVerticesOrder.length,/* 0, newPositionData.length / 3, */startingIndex, newTriangles.length * 3, this._reconstructedMesh);
+                var newSubmesh = new SubMesh(originalSubmesh.materialIndex, startingVertex, vertexCount,/* 0, newPositionData.length / 3, */startingIndex, newTriangles.length * 3, this._reconstructedMesh);
             }
         }
 
@@ -571,7 +577,7 @@
             this._reconstructedMesh.isVisible = false;
         }
 
-        private isFlipped(vertex1: DecimationVertex, index2: number, point: Vector3, deletedArray: Array<boolean>, borderFactor: number, delTr: Array<DecimationTriangle>): boolean {
+        private isFlipped(vertex1: DecimationVertex, vertex2: DecimationVertex, point: Vector3, deletedArray: Array<boolean>, borderFactor: number, delTr: Array<DecimationTriangle>): boolean {
 
             for (var i = 0; i < vertex1.triangleCount; ++i) {
                 var t = this.triangles[this.references[vertex1.triangleStart + i].triangleId];
@@ -579,18 +585,18 @@
 
                 var s = this.references[vertex1.triangleStart + i].vertexId;
 
-                var id1 = t.vertices[(s + 1) % 3];
-                var id2 = t.vertices[(s + 2) % 3];
+                var v1 = t.vertices[(s + 1) % 3];
+                var v2 = t.vertices[(s + 2) % 3];
 
-                if ((id1 === index2 || id2 === index2)/* && !this.isTriangleOnBoundingBox(t)*/) {
+                if ((v1 === vertex2 || v2 === vertex2)/* && !this.isTriangleOnBoundingBox(t)*/) {
                     deletedArray[i] = true;
                     delTr.push(t);
                     continue;
                 }
 
-                var d1 = this.vertices[id1].position.subtract(point);
+                var d1 = v1.position.subtract(point);
                 d1 = d1.normalize();
-                var d2 = this.vertices[id2].position.subtract(point);
+                var d2 = v2.position.subtract(point);
                 d2 = d2.normalize();
                 if (Math.abs(Vector3.Dot(d1, d2)) > 0.999) return true;
                 var normal = Vector3.Cross(d1, d2).normalize();
@@ -601,7 +607,7 @@
             return false;
         }
 
-        private updateTriangles(vertexId: number, vertex: DecimationVertex, deletedArray: Array<boolean>, deletedTriangles: number): number {
+        private updateTriangles(origVertex: DecimationVertex, vertex: DecimationVertex, deletedArray: Array<boolean>, deletedTriangles: number): number {
             var newDeleted = deletedTriangles;
             for (var i = 0; i < vertex.triangleCount; ++i) {
                 var ref = this.references[vertex.triangleStart + i];
@@ -612,11 +618,11 @@
                     newDeleted++;
                     continue;
                 }
-                t.vertices[ref.vertexId] = vertexId;
+                t.vertices[ref.vertexId] = origVertex;
                 t.isDirty = true;
-                t.error[0] = this.calculateError(this.vertices[t.vertices[0]], this.vertices[t.vertices[1]]) + (t.borderFactor / 2);
-                t.error[1] = this.calculateError(this.vertices[t.vertices[1]], this.vertices[t.vertices[2]]) + (t.borderFactor / 2);
-                t.error[2] = this.calculateError(this.vertices[t.vertices[2]], this.vertices[t.vertices[0]]) + (t.borderFactor / 2);
+                t.error[0] = this.calculateError(t.vertices[0], t.vertices[1]) + (t.borderFactor / 2);
+                t.error[1] = this.calculateError(t.vertices[1], t.vertices[2]) + (t.borderFactor / 2);
+                t.error[2] = this.calculateError(t.vertices[2], t.vertices[0]) + (t.borderFactor / 2);
                 t.error[3] = Math.min(t.error[0], t.error[1], t.error[2]);
                 this.references.push(ref);
             }
@@ -634,14 +640,14 @@
                     var triangle = this.triangles[this.references[v.triangleStart + j].triangleId];
                     for (var ii = 0; ii < 3; ii++) {
                         var ofs = 0;
-                        var id = triangle.vertices[ii];
+                        var vv = triangle.vertices[ii];
                         while (ofs < vCount.length) {
-                            if (vId[ofs] === id) break;
+                            if (vId[ofs] === vv.id) break;
                             ++ofs;
                         }
                         if (ofs === vCount.length) {
                             vCount.push(1);
-                            vId.push(id);
+                            vId.push(vv.id);
                         } else {
                             vCount[ofs]++;
                         }
@@ -681,7 +687,7 @@
             for (i = 0; i < this.triangles.length; ++i) {
                 t = this.triangles[i];
                 for (j = 0; j < 3; ++j) {
-                    v = this.vertices[t.vertices[j]];
+                    v = t.vertices[j];
                     v.triangleCount++;
                 }
             }
@@ -698,7 +704,7 @@
             for (i = 0; i < this.triangles.length; ++i) {
                 t = this.triangles[i];
                 for (j = 0; j < 3; ++j) {
-                    v = this.vertices[t.vertices[j]];
+                    v = t.vertices[j];
                     newReferences[v.triangleStart + v.triangleCount] = new Reference(j, i);
                     v.triangleCount++;
                 }
@@ -733,14 +739,6 @@
                 pointResult.y = 1 / qDet * (q.det(0, 2, 3, 1, 5, 6, 2, 7, 8));
                 pointResult.z = -1 / qDet * (q.det(0, 1, 3, 1, 4, 6, 2, 5, 8));
                 error = this.vertexError(q, pointResult);
-                //TODO this should be correctly calculated
-                if (normalResult) {
-                    normalResult.copyFrom(vertex1.normal);
-                    if (vertex1.uv)
-                        uvResult.copyFrom(vertex1.uv);
-                    else if (vertex1.color)
-                        colorResult.copyFrom(vertex1.color);
-                }
             } else {
                 var p3 = (vertex1.position.add(vertex2.position)).divide(new Vector3(2, 2, 2));
                 //var norm3 = (vertex1.normal.add(vertex2.normal)).divide(new Vector3(2, 2, 2)).normalize();
@@ -751,29 +749,14 @@
                 if (error === error1) {
                     if (pointResult) {
                         pointResult.copyFrom(vertex1.position);
-                        normalResult.copyFrom(vertex1.normal);
-                        if (vertex1.uv)
-                            uvResult.copyFrom(vertex1.uv);
-                        else if (vertex1.color)
-                            colorResult.copyFrom(vertex1.color);
                     }
                 } else if (error === error2) {
                     if (pointResult) {
                         pointResult.copyFrom(vertex2.position);
-                        normalResult.copyFrom(vertex2.normal);
-                        if (vertex2.uv)
-                            uvResult.copyFrom(vertex2.uv);
-                        else if (vertex2.color)
-                            colorResult.copyFrom(vertex2.color);
                     }
                 } else {
                     if (pointResult) {
                         pointResult.copyFrom(p3);
-                        normalResult.copyFrom(vertex1.normal);
-                        if (vertex1.uv)
-                            uvResult.copyFrom(vertex1.uv);
-                        else if (vertex1.color)
-                            colorResult.copyFrom(vertex1.color);
                     }
                 }
             }

+ 71 - 39
Babylon/PostProcess/babylon.lensRenderingPipeline.js

@@ -10,28 +10,28 @@ var BABYLON;
         __extends(LensRenderingPipeline, _super);
         /**
          * @constructor
+         *
+         * Effect parameters are as follow:
+         * {
+         *      chromatic_aberration: number;       // from 0 to x (1 for realism)
+         *      edge_blur: number;                  // from 0 to x (1 for realism)
+         *      distortion: number;                 // from 0 to x (1 for realism)
+         *      grain_amount: number;               // from 0 to 1
+         *      grain_texture: BABYLON.Texture;     // texture to use for grain effect; if unset, use random B&W noise
+         *      dof_focus_depth: number;            // depth-of-field: focus depth; unset to disable (disabled by default)
+         *      dof_aperture: number;               // depth-of-field: focus blur bias (default: 1)
+         *      dof_pentagon: boolean;              // depth-of-field: makes a pentagon-like "bokeh" effect
+         *      dof_gain: number;                   // depth-of-field: depthOfField gain; unset to disable (disabled by default)
+         *      dof_threshold: number;              // depth-of-field: depthOfField threshold (default: 1)
+         *      blur_noise: boolean;                // add a little bit of noise to the blur (default: true)
+         * }
+         * Note: if an effect parameter is unset, effect is disabled
+         *
          * @param {string} name - The rendering pipeline name
-         * @param {object} parameters - An object containing all parameters (see below)
+         * @param {object} parameters - An object containing all parameters (see above)
          * @param {BABYLON.Scene} scene - The scene linked to this pipeline
          * @param {number} ratio - The size of the postprocesses (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
          * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
-
-            Effect parameters are as follow:
-            {
-                chromatic_aberration: number;		// from 0 to x (1 for realism)
-                edge_blur: number;					// from 0 to x (1 for realism)
-                distortion: number;					// from 0 to x (1 for realism)
-                grain_amount: number;				// from 0 to 1
-                grain_texture: BABYLON.Texture;		// texture to use for grain effect; if unset, use random B&W noise
-                dof_focus_depth: number;			// depth-of-field: focus depth; unset to disable
-                dof_aperture: number;				// depth-of-field: focus blur bias (default: 1)
-                dof_pentagon: boolean;				// depth-of-field: makes a pentagon-like "bokeh" effect
-                dof_gain: boolean;					// depth-of-field: depthOfField gain (default: 1)
-                dof_threshold: boolean;				// depth-of-field: depthOfField threshold (default: 1)
-                blur_noise: boolean;				// add a little bit of noise to the blur (default: true)
-            }
-
-            Note: if an effect parameter is unset, effect is disabled
          */
         function LensRenderingPipeline(name, parameters, scene, ratio, cameras) {
             var _this = this;
@@ -41,8 +41,9 @@ var BABYLON;
             // - chromatic aberration (slight shift of RGB colors)
             // - blur on the edge of the lens
             // - lens distortion
-            // - depth-of-field 'bokeh' effect (shapes appearing in blured areas, stronger highlights)
-            // - grain/dust-on-lens effect
+            // - depth-of-field blur & highlights enhancing
+            // - depth-of-field 'bokeh' effect (shapes appearing in blurred areas)
+            // - grain effect (noise or custom texture)
             // Two additional texture samplers are needed:
             // - depth map (for depth-of-field)
             // - grain texture
@@ -52,6 +53,11 @@ var BABYLON;
             */
             this.LensChromaticAberrationEffect = "LensChromaticAberrationEffect";
             /**
+            * The highlights enhancing PostProcess id in the pipeline
+            * @type {string}
+            */
+            this.HighlightsEnhancingEffect = "HighlightsEnhancingEffect";
+            /**
             * The depth-of-field PostProcess id in the pipeline
             * @type {string}
             */
@@ -70,7 +76,7 @@ var BABYLON;
             this._grainAmount = parameters.grain_amount ? parameters.grain_amount : 0;
             this._chromaticAberration = parameters.chromatic_aberration ? parameters.chromatic_aberration : 0;
             this._distortion = parameters.distortion ? parameters.distortion : 0;
-            this._highlightsGain = parameters.dof_gain ? parameters.dof_gain : 1;
+            this._highlightsGain = parameters.dof_gain !== undefined ? parameters.dof_gain : -1;
             this._highlightsThreshold = parameters.dof_threshold ? parameters.dof_threshold : 1;
             this._dofDepth = parameters.dof_focus_depth !== undefined ? parameters.dof_focus_depth : -1;
             this._dofAperture = parameters.dof_aperture ? parameters.dof_aperture : 1;
@@ -78,21 +84,28 @@ var BABYLON;
             this._blurNoise = parameters.blur_noise !== undefined ? parameters.blur_noise : true;
             // Create effects
             this._createChromaticAberrationPostProcess(ratio);
+            this._createHighlightsPostProcess(ratio);
             this._createDepthOfFieldPostProcess(ratio);
             // Set up pipeline
             this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), this.LensChromaticAberrationEffect, function () {
                 return _this._chromaticAberrationPostProcess;
             }, true));
+            this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), this.HighlightsEnhancingEffect, function () {
+                return _this._highlightsPostProcess;
+            }, true));
             this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), this.LensDepthOfFieldEffect, function () {
                 return _this._depthOfFieldPostProcess;
             }, true));
+            if (this._highlightsGain == -1) {
+                this._disableEffect(this.HighlightsEnhancingEffect, null);
+            }
             // Finish
             scene.postProcessRenderPipelineManager.addPipeline(this);
             if (cameras) {
                 scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(name, cameras);
             }
         }
-        // public methods
+        // public methods (self explanatory)
         LensRenderingPipeline.prototype.setEdgeBlur = function (amount) {
             this._edgeBlur = amount;
         };
@@ -117,12 +130,6 @@ var BABYLON;
         LensRenderingPipeline.prototype.disableEdgeDistortion = function () {
             this._distortion = 0;
         };
-        LensRenderingPipeline.prototype.setHighlightsGain = function (amount) {
-            this._highlightsGain = amount;
-        };
-        LensRenderingPipeline.prototype.setHighlightsThreshold = function (amount) {
-            this._highlightsThreshold = amount;
-        };
         LensRenderingPipeline.prototype.setFocusDepth = function (amount) {
             this._dofDepth = amount;
         };
@@ -144,6 +151,18 @@ var BABYLON;
         LensRenderingPipeline.prototype.disableNoiseBlur = function () {
             this._blurNoise = false;
         };
+        LensRenderingPipeline.prototype.setHighlightsGain = function (amount) {
+            this._highlightsGain = amount;
+        };
+        LensRenderingPipeline.prototype.setHighlightsThreshold = function (amount) {
+            if (this._highlightsGain == -1) {
+                this._highlightsGain = 1.0;
+            }
+            this._highlightsThreshold = amount;
+        };
+        LensRenderingPipeline.prototype.disableHighlights = function () {
+            this._highlightsGain = -1;
+        };
         /**
          * Removes the internal pipeline assets and detaches the pipeline from the scene cameras
          */
@@ -151,6 +170,7 @@ var BABYLON;
             if (disableDepthRender === void 0) { disableDepthRender = false; }
             this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
             this._chromaticAberrationPostProcess = undefined;
+            this._highlightsPostProcess = undefined;
             this._depthOfFieldPostProcess = undefined;
             this._grainTexture.dispose();
             if (disableDepthRender)
@@ -162,16 +182,27 @@ var BABYLON;
             this._chromaticAberrationPostProcess = new BABYLON.PostProcess("LensChromaticAberration", "chromaticAberration", ["chromatic_aberration", "screen_width", "screen_height"], [], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
             this._chromaticAberrationPostProcess.onApply = function (effect) {
                 effect.setFloat('chromatic_aberration', _this._chromaticAberration);
-                effect.setFloat('screen_width', _this._scene.getEngine().getRenderWidth());
-                effect.setFloat('screen_height', _this._scene.getEngine().getRenderHeight());
+                effect.setFloat('screen_width', _this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', _this._scene.getEngine().getRenderingCanvas().height);
+            };
+        };
+        // highlights enhancing
+        LensRenderingPipeline.prototype._createHighlightsPostProcess = function (ratio) {
+            var _this = this;
+            this._highlightsPostProcess = new BABYLON.PostProcess("LensHighlights", "lensHighlights", ["pentagon", "gain", "threshold", "screen_width", "screen_height"], [], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
+            this._highlightsPostProcess.onApply = function (effect) {
+                effect.setFloat('gain', _this._highlightsGain);
+                effect.setFloat('threshold', _this._highlightsThreshold);
+                effect.setBool('pentagon', _this._dofPentagon);
+                effect.setTextureFromPostProcess("textureSampler", _this._chromaticAberrationPostProcess);
+                effect.setFloat('screen_width', _this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', _this._scene.getEngine().getRenderingCanvas().height);
             };
         };
         // colors shifting and distortion
         LensRenderingPipeline.prototype._createDepthOfFieldPostProcess = function (ratio) {
             var _this = this;
             this._depthOfFieldPostProcess = new BABYLON.PostProcess("LensDepthOfField", "depthOfField", [
-                "gain",
-                "threshold",
                 "focus_depth",
                 "aperture",
                 "pentagon",
@@ -182,23 +213,24 @@ var BABYLON;
                 "blur_noise",
                 "grain_amount",
                 "screen_width",
-                "screen_height"
-            ], ["depthSampler", "grainSampler"], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
+                "screen_height",
+                "highlights"
+            ], ["depthSampler", "grainSampler", "highlightsSampler"], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
             this._depthOfFieldPostProcess.onApply = function (effect) {
-                effect.setBool('pentagon', _this._dofPentagon);
                 effect.setBool('blur_noise', _this._blurNoise);
                 effect.setFloat('maxZ', _this._scene.activeCamera.maxZ);
                 effect.setFloat('grain_amount', _this._grainAmount);
                 effect.setTexture("depthSampler", _this._depthTexture);
                 effect.setTexture("grainSampler", _this._grainTexture);
-                effect.setFloat('screen_width', _this._scene.getEngine().getRenderWidth());
-                effect.setFloat('screen_height', _this._scene.getEngine().getRenderHeight());
+                effect.setTextureFromPostProcess("textureSampler", _this._highlightsPostProcess);
+                effect.setTextureFromPostProcess("highlightsSampler", _this._depthOfFieldPostProcess);
+                effect.setFloat('screen_width', _this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', _this._scene.getEngine().getRenderingCanvas().height);
                 effect.setFloat('distortion', _this._distortion);
                 effect.setFloat('focus_depth', _this._dofDepth);
                 effect.setFloat('aperture', _this._dofAperture);
-                effect.setFloat('gain', _this._highlightsGain);
-                effect.setFloat('threshold', _this._highlightsThreshold);
                 effect.setFloat('edge_blur', _this._edgeBlur);
+                effect.setBool('highlights', (_this._highlightsGain != -1));
             };
         };
         // creates a black and white random noise texture, 512x512

+ 124 - 82
Babylon/PostProcess/babylon.lensRenderingPipeline.ts

@@ -1,16 +1,17 @@
 module BABYLON {
     export class LensRenderingPipeline extends PostProcessRenderPipeline {
 
-    	// Lens effects can be of the following:
-    	// - chromatic aberration (slight shift of RGB colors)
-    	// - blur on the edge of the lens
-    	// - lens distortion
-    	// - depth-of-field 'bokeh' effect (shapes appearing in blured areas, stronger highlights)
-    	// - grain/dust-on-lens effect
-
-    	// Two additional texture samplers are needed:
-    	// - depth map (for depth-of-field)
-    	// - grain texture
+        // Lens effects can be of the following:
+        // - chromatic aberration (slight shift of RGB colors)
+        // - blur on the edge of the lens
+        // - lens distortion
+        // - depth-of-field blur & highlights enhancing
+        // - depth-of-field 'bokeh' effect (shapes appearing in blurred areas)
+        // - grain effect (noise or custom texture)
+
+        // Two additional texture samplers are needed:
+        // - depth map (for depth-of-field)
+        // - grain texture
 
         /**
         * The chromatic aberration PostProcess id in the pipeline
@@ -18,6 +19,11 @@ module BABYLON {
         */
         public LensChromaticAberrationEffect: string = "LensChromaticAberrationEffect";
         /**
+        * The highlights enhancing PostProcess id in the pipeline
+        * @type {string}
+        */
+        public HighlightsEnhancingEffect: string = "HighlightsEnhancingEffect";
+        /**
         * The depth-of-field PostProcess id in the pipeline
         * @type {string}
         */
@@ -28,6 +34,7 @@ module BABYLON {
         private _grainTexture: Texture;
 
         private _chromaticAberrationPostProcess: PostProcess;
+        private _highlightsPostProcess: PostProcess;
         private _depthOfFieldPostProcess: PostProcess;
 
         private _edgeBlur: number;
@@ -41,72 +48,77 @@ module BABYLON {
         private _dofPentagon: boolean;
         private _blurNoise: boolean;
 
+
         /**
          * @constructor
+         *
+         * Effect parameters are as follow:
+         * {
+         *      chromatic_aberration: number;       // from 0 to x (1 for realism)
+         *      edge_blur: number;                  // from 0 to x (1 for realism)
+         *      distortion: number;                 // from 0 to x (1 for realism)
+         *      grain_amount: number;               // from 0 to 1
+         *      grain_texture: BABYLON.Texture;     // texture to use for grain effect; if unset, use random B&W noise
+         *      dof_focus_depth: number;            // depth-of-field: focus depth; unset to disable (disabled by default)
+         *      dof_aperture: number;               // depth-of-field: focus blur bias (default: 1)
+         *      dof_pentagon: boolean;              // depth-of-field: makes a pentagon-like "bokeh" effect
+         *      dof_gain: number;                   // depth-of-field: depthOfField gain; unset to disable (disabled by default)
+         *      dof_threshold: number;              // depth-of-field: depthOfField threshold (default: 1)
+         *      blur_noise: boolean;                // add a little bit of noise to the blur (default: true)
+         * }
+         * Note: if an effect parameter is unset, effect is disabled
+         *
          * @param {string} name - The rendering pipeline name
-         * @param {object} parameters - An object containing all parameters (see below)
+         * @param {object} parameters - An object containing all parameters (see above)
          * @param {BABYLON.Scene} scene - The scene linked to this pipeline
          * @param {number} ratio - The size of the postprocesses (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
          * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
-
-         	Effect parameters are as follow:
-		 	{
-				chromatic_aberration: number;		// from 0 to x (1 for realism)
-				edge_blur: number;					// from 0 to x (1 for realism)
-				distortion: number;					// from 0 to x (1 for realism)
-				grain_amount: number;				// from 0 to 1
-				grain_texture: BABYLON.Texture;		// texture to use for grain effect; if unset, use random B&W noise
-				dof_focus_depth: number;			// depth-of-field: focus depth; unset to disable
-				dof_aperture: number;				// depth-of-field: focus blur bias (default: 1)
-				dof_pentagon: boolean;				// depth-of-field: makes a pentagon-like "bokeh" effect
-				dof_gain: boolean;					// depth-of-field: depthOfField gain (default: 1)
-				dof_threshold: boolean;				// depth-of-field: depthOfField threshold (default: 1)
-				blur_noise: boolean;				// add a little bit of noise to the blur (default: true)
-			}
-
-			Note: if an effect parameter is unset, effect is disabled
          */
         constructor(name: string, parameters: any, scene: Scene, ratio: number = 1.0, cameras?: Camera[]) {
-			super(scene.getEngine(), name);
-
-			this._scene = scene;
-
-			// Fetch texture samplers
-			this._depthTexture = scene.enableDepthRenderer().getDepthMap(); // Force depth renderer "on"
-			if (parameters.grain_texture) {
-			    this._grainTexture = parameters.grain_texture;
-			} else {
-			     this._createGrainTexture();
-			}
-
-			// save parameters
-			this._edgeBlur = parameters.edge_blur ? parameters.edge_blur : 0;
-			this._grainAmount = parameters.grain_amount ? parameters.grain_amount : 0;
-			this._chromaticAberration = parameters.chromatic_aberration ? parameters.chromatic_aberration : 0;
-			this._distortion = parameters.distortion ? parameters.distortion : 0;
-			this._highlightsGain = parameters.dof_gain ? parameters.dof_gain : 1;
-			this._highlightsThreshold = parameters.dof_threshold ? parameters.dof_threshold : 1;
-			this._dofDepth = parameters.dof_focus_depth !== undefined ? parameters.dof_focus_depth : -1;
-			this._dofAperture = parameters.dof_aperture ? parameters.dof_aperture : 1;
-			this._dofPentagon = parameters.dof_pentagon !== undefined ? parameters.dof_pentagon : true;
-			this._blurNoise = parameters.blur_noise !== undefined ? parameters.blur_noise : true;
-
-			// Create effects
+            super(scene.getEngine(), name);
+
+            this._scene = scene;
+
+            // Fetch texture samplers
+            this._depthTexture = scene.enableDepthRenderer().getDepthMap(); // Force depth renderer "on"
+            if (parameters.grain_texture) { this._grainTexture = parameters.grain_texture; }
+            else { this._createGrainTexture(); }
+
+            // save parameters
+            this._edgeBlur = parameters.edge_blur ? parameters.edge_blur : 0;
+            this._grainAmount = parameters.grain_amount ? parameters.grain_amount : 0;
+            this._chromaticAberration = parameters.chromatic_aberration ? parameters.chromatic_aberration : 0;
+            this._distortion = parameters.distortion ? parameters.distortion : 0;
+            this._highlightsGain = parameters.dof_gain !== undefined ? parameters.dof_gain : -1;
+            this._highlightsThreshold = parameters.dof_threshold ? parameters.dof_threshold : 1;
+            this._dofDepth = parameters.dof_focus_depth !== undefined ? parameters.dof_focus_depth : -1;
+            this._dofAperture = parameters.dof_aperture ? parameters.dof_aperture : 1;
+            this._dofPentagon = parameters.dof_pentagon !== undefined ? parameters.dof_pentagon : true;
+            this._blurNoise = parameters.blur_noise !== undefined ? parameters.blur_noise : true;
+
+            // Create effects
             this._createChromaticAberrationPostProcess(ratio);
+            this._createHighlightsPostProcess(ratio);
             this._createDepthOfFieldPostProcess(ratio);
 
             // Set up pipeline
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.LensChromaticAberrationEffect, () => { return this._chromaticAberrationPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.HighlightsEnhancingEffect, () => { return this._highlightsPostProcess; }, true));
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.LensDepthOfFieldEffect, () => { return this._depthOfFieldPostProcess; }, true));
 
+            if (this._highlightsGain == -1) {
+                this._disableEffect(this.HighlightsEnhancingEffect, null);
+            }
+
             // Finish
             scene.postProcessRenderPipelineManager.addPipeline(this);
-            if(cameras) {
+            if (cameras) {
                 scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(name, cameras);
             }
         }
 
-        // public methods
+        // public methods (self explanatory)
+
         public setEdgeBlur(amount: number) { this._edgeBlur = amount; }
         public disableEdgeBlur() { this._edgeBlur = 0; }
         public setGrainAmount(amount: number) { this._grainAmount = amount; }
@@ -115,8 +127,6 @@ module BABYLON {
         public disableChromaticAberration() { this._chromaticAberration = 0; }
         public setEdgeDistortion(amount: number) { this._distortion = amount; }
         public disableEdgeDistortion() { this._distortion = 0; }
-        public setHighlightsGain(amount: number) { this._highlightsGain = amount; }
-        public setHighlightsThreshold(amount: number) { this._highlightsThreshold = amount; }
         public setFocusDepth(amount: number) { this._dofDepth = amount; }
         public disableDepthOfField() { this._dofDepth = -1; }
         public setAperture(amount: number) { this._dofAperture = amount; }
@@ -124,6 +134,18 @@ module BABYLON {
         public disablePentagonBokeh() { this._dofPentagon = false; }
         public enableNoiseBlur() { this._blurNoise = true; }
         public disableNoiseBlur() { this._blurNoise = false; }
+        public setHighlightsGain(amount: number) {
+            this._highlightsGain = amount;
+        }
+        public setHighlightsThreshold(amount: number) {
+            if (this._highlightsGain == -1) {
+                this._highlightsGain = 1.0;
+            }
+            this._highlightsThreshold = amount;
+        }
+        public disableHighlights() {
+            this._highlightsGain = -1;
+        }
 
         /**
          * Removes the internal pipeline assets and detaches the pipeline from the scene cameras
@@ -132,6 +154,7 @@ module BABYLON {
             this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
 
             this._chromaticAberrationPostProcess = undefined;
+            this._highlightsPostProcess = undefined;
             this._depthOfFieldPostProcess = undefined;
 
             this._grainTexture.dispose();
@@ -143,49 +166,68 @@ module BABYLON {
         // colors shifting and distortion
         private _createChromaticAberrationPostProcess(ratio: number): void {
             this._chromaticAberrationPostProcess = new PostProcess("LensChromaticAberration", "chromaticAberration",
-            	["chromatic_aberration", "screen_width", "screen_height"],		// uniforms
-            	[],											// samplers
+                ["chromatic_aberration", "screen_width", "screen_height"],		// uniforms
+                [],											// samplers
                 ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
                 this._scene.getEngine(), false);
 
             this._chromaticAberrationPostProcess.onApply = (effect: Effect) => {
-            	effect.setFloat('chromatic_aberration', this._chromaticAberration);
-                effect.setFloat('screen_width', this._scene.getEngine().getRenderWidth());
-                effect.setFloat('screen_height', this._scene.getEngine().getRenderHeight());
+                effect.setFloat('chromatic_aberration', this._chromaticAberration);
+                effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
+            };
+        }
+
+        // highlights enhancing
+        private _createHighlightsPostProcess(ratio: number): void {
+            this._highlightsPostProcess = new PostProcess("LensHighlights", "lensHighlights",
+                ["pentagon", "gain", "threshold", "screen_width", "screen_height"],      // uniforms
+                [],     // samplers
+                ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
+                this._scene.getEngine(), false);
+
+            this._highlightsPostProcess.onApply = (effect: Effect) => {
+                effect.setFloat('gain', this._highlightsGain);
+                effect.setFloat('threshold', this._highlightsThreshold);
+                effect.setBool('pentagon', this._dofPentagon);
+                effect.setTextureFromPostProcess("textureSampler", this._chromaticAberrationPostProcess);
+                effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
             };
         }
 
         // colors shifting and distortion
         private _createDepthOfFieldPostProcess(ratio: number): void {
             this._depthOfFieldPostProcess = new PostProcess("LensDepthOfField", "depthOfField",
-            	[
-            		"gain", "threshold", "focus_depth", "aperture", "pentagon", "maxZ", "edge_blur",
-            		"chromatic_aberration", "distortion", "blur_noise", "grain_amount", "screen_width", "screen_height"
-            	],
-            	["depthSampler", "grainSampler"],
+                [
+                    "focus_depth", "aperture", "pentagon", "maxZ", "edge_blur", "chromatic_aberration",
+                    "distortion", "blur_noise", "grain_amount", "screen_width", "screen_height", "highlights"
+                ],
+                ["depthSampler", "grainSampler", "highlightsSampler"],
                 ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
                 this._scene.getEngine(), false);
 
             this._depthOfFieldPostProcess.onApply = (effect: Effect) => {
-	            effect.setBool('pentagon', this._dofPentagon);
-            	effect.setBool('blur_noise', this._blurNoise);
-            	effect.setFloat('maxZ', this._scene.activeCamera.maxZ);
-            	effect.setFloat('grain_amount', this._grainAmount);
+                effect.setBool('blur_noise', this._blurNoise);
+                effect.setFloat('maxZ', this._scene.activeCamera.maxZ);
+                effect.setFloat('grain_amount', this._grainAmount);
+
+                effect.setTexture("depthSampler", this._depthTexture);
+                effect.setTexture("grainSampler", this._grainTexture);
+                effect.setTextureFromPostProcess("textureSampler", this._highlightsPostProcess);
+                effect.setTextureFromPostProcess("highlightsSampler", this._depthOfFieldPostProcess);
 
-            	effect.setTexture("depthSampler", this._depthTexture);
-            	effect.setTexture("grainSampler", this._grainTexture);
+                effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
 
-            	effect.setFloat('screen_width', this._scene.getEngine().getRenderWidth());
-            	effect.setFloat('screen_height', this._scene.getEngine().getRenderHeight());
+                effect.setFloat('distortion', this._distortion);
 
-            	effect.setFloat('distortion', this._distortion);
+                effect.setFloat('focus_depth', this._dofDepth);
+                effect.setFloat('aperture', this._dofAperture);
 
-	            effect.setFloat('focus_depth', this._dofDepth);
-	            effect.setFloat('aperture', this._dofAperture);
-            	effect.setFloat('gain', this._highlightsGain);
-            	effect.setFloat('threshold', this._highlightsThreshold);
+                effect.setFloat('edge_blur', this._edgeBlur);
 
-            	effect.setFloat('edge_blur', this._edgeBlur);
+                effect.setBool('highlights', (this._highlightsGain != -1));
             };
         }
 
@@ -206,7 +248,7 @@ module BABYLON {
             var value;
             for (var x = 0; x < size; x++) {
                 for (var y = 0; y < size; y++) {
-                    value = Math.floor(rand(0.42,0.58)*255);
+                    value = Math.floor(rand(0.42, 0.58) * 255);
                     context.fillStyle = 'rgb(' + value + ', ' + value + ', ' + value + ')';
                     context.fillRect(x, y, 1, 1);
                 }

+ 10 - 3
Babylon/PostProcess/babylon.postProcessManager.js

@@ -5,13 +5,18 @@ var BABYLON;
             this._vertexDeclaration = [2];
             this._vertexStrideSize = 2 * 4;
             this._scene = scene;
+        }
+        PostProcessManager.prototype._prepareBuffers = function () {
+            if (this._vertexBuffer) {
+                return;
+            }
             // VBO
             var vertices = [];
             vertices.push(1, 1);
             vertices.push(-1, 1);
             vertices.push(-1, -1);
             vertices.push(1, -1);
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            this._vertexBuffer = this._scene.getEngine().createVertexBuffer(vertices);
             // Indices
             var indices = [];
             indices.push(0);
@@ -20,8 +25,8 @@ var BABYLON;
             indices.push(0);
             indices.push(2);
             indices.push(3);
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
-        }
+            this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
+        };
         // Methods
         PostProcessManager.prototype._prepareFrame = function (sourceTexture) {
             var postProcesses = this._scene.activeCamera._postProcesses;
@@ -53,6 +58,7 @@ var BABYLON;
                         pp.onBeforeRender(effect);
                     }
                     // VBOs
+                    this._prepareBuffers();
                     engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
                     // Draw order
                     engine.draw(true, 0, 6);
@@ -91,6 +97,7 @@ var BABYLON;
                         pp.onBeforeRender(effect);
                     }
                     // VBOs
+                    this._prepareBuffers();
                     engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
                     // Draw order
                     engine.draw(true, 0, 6);

+ 10 - 2
Babylon/PostProcess/babylon.postProcessManager.ts

@@ -8,6 +8,12 @@
 
         constructor(scene: Scene) {
             this._scene = scene;
+        }
+
+        private _prepareBuffers(): void {
+            if (this._vertexBuffer) {
+                return;
+            }
 
             // VBO
             var vertices = [];
@@ -15,7 +21,7 @@
             vertices.push(-1, 1);
             vertices.push(-1, -1);
             vertices.push(1, -1);
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            this._vertexBuffer = this._scene.getEngine().createVertexBuffer(vertices);
 
             // Indices
             var indices = [];
@@ -27,7 +33,7 @@
             indices.push(2);
             indices.push(3);
 
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+            this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
         }
 
         // Methods
@@ -67,6 +73,7 @@
                     }
 
                     // VBOs
+                    this._prepareBuffers();
                     engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
 
                     // Draw order
@@ -111,6 +118,7 @@
                     }
 
                     // VBOs
+                    this._prepareBuffers();
                     engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
 
                     // Draw order

+ 32 - 4
Babylon/PostProcess/babylon.ssaoRenderingPipeline.js

@@ -12,7 +12,7 @@ var BABYLON;
          * @constructor
          * @param {string} name - The rendering pipeline name
          * @param {BABYLON.Scene} scene - The scene linked to this pipeline
-         * @param {any} ratio - The size of the postprocesses (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
+         * @param {any} ratio - The size of the postprocesses. Can be a number shared between passes or an object for more precision: { ssaoRatio: 0.5, combineRatio: 1.0 }
          * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
          */
         function SSAORenderingPipeline(name, scene, ratio, cameras) {
@@ -44,6 +44,30 @@ var BABYLON;
             * @type {string}
             */
             this.SSAOCombineRenderEffect = "SSAOCombineRenderEffect";
+            /**
+            * The output strength of the SSAO post-process. Default value is 1.0.
+            * @type {number}
+            */
+            this.totalStrength = 1.0;
+            /**
+            * The radius around the analyzed pixel used by the SSAO post-process. Default value is 0.0002
+            * @type {number}
+            */
+            this.radius = 0.0002;
+            /**
+            * Related to fallOff, used to interpolate SSAO samples (first interpolate function input) based on the occlusion difference of each pixel
+            * Must not be equal to fallOff and superior to fallOff.
+            * Default value is 0.0075
+            * @type {number}
+            */
+            this.area = 0.0075;
+            /**
+            * Related to area, used to interpolate SSAO samples (second interpolate function input) based on the occlusion difference of each pixel
+            * Must not be equal to area and inferior to area.
+            * Default value is 0.0002
+            * @type {number}
+            */
+            this.fallOff = 0.0002;
             this._firstUpdate = true;
             this._scene = scene;
             // Set up assets
@@ -53,8 +77,8 @@ var BABYLON;
             var combineRatio = ratio.combineRatio || ratio;
             this._originalColorPostProcess = new BABYLON.PassPostProcess("SSAOOriginalSceneColor", combineRatio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
             this._createSSAOPostProcess(ssaoRatio);
-            this._blurHPostProcess = new BABYLON.BlurPostProcess("SSAOBlurH", new BABYLON.Vector2(2.0, 0.0), 1.3, ssaoRatio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
-            this._blurVPostProcess = new BABYLON.BlurPostProcess("SSAOBlurV", new BABYLON.Vector2(0.0, 2.0), 1.3, ssaoRatio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this._blurHPostProcess = new BABYLON.BlurPostProcess("SSAOBlurH", new BABYLON.Vector2(2.0, 0.0), 2.0, ssaoRatio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this._blurVPostProcess = new BABYLON.BlurPostProcess("SSAOBlurV", new BABYLON.Vector2(0.0, 2.0), 2.0, ssaoRatio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
             this._createSSAOCombinePostProcess(combineRatio);
             // Set up pipeline
             this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), this.SSAOOriginalSceneColorEffect, function () {
@@ -161,7 +185,7 @@ var BABYLON;
                 -0.0271
             ];
             var samplesFactor = 1.0 / 16.0;
-            this._ssaoPostProcess = new BABYLON.PostProcess("ssao", "ssao", ["sampleSphere", "samplesFactor", "randTextureTiles"], ["randomSampler"], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
+            this._ssaoPostProcess = new BABYLON.PostProcess("ssao", "ssao", ["sampleSphere", "samplesFactor", "randTextureTiles", "totalStrength", "radius", "area", "fallOff"], ["randomSampler"], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
             this._ssaoPostProcess.onApply = function (effect) {
                 if (_this._firstUpdate) {
                     effect.setArray3("sampleSphere", sampleSphere);
@@ -169,6 +193,10 @@ var BABYLON;
                     effect.setFloat("randTextureTiles", 4.0 / ratio);
                     _this._firstUpdate = false;
                 }
+                effect.setFloat("totalStrength", _this.totalStrength);
+                effect.setFloat("radius", _this.radius);
+                effect.setFloat("area", _this.area);
+                effect.setFloat("fallOff", _this.fallOff);
                 effect.setTexture("textureSampler", _this._depthTexture);
                 effect.setTexture("randomSampler", _this._randomTexture);
             };

+ 37 - 4
Babylon/PostProcess/babylon.ssaoRenderingPipeline.ts

@@ -28,6 +28,34 @@
         */
         public SSAOCombineRenderEffect: string = "SSAOCombineRenderEffect";
 
+        /**
+        * The output strength of the SSAO post-process. Default value is 1.0.
+        * @type {number}
+        */
+        public totalStrength: number = 1.0;
+
+        /**
+        * The radius around the analyzed pixel used by the SSAO post-process. Default value is 0.0002
+        * @type {number}
+        */
+        public radius: number = 0.0002;
+
+        /**
+        * Related to fallOff, used to interpolate SSAO samples (first interpolate function input) based on the occlusion difference of each pixel
+        * Must not be equal to fallOff and superior to fallOff.
+        * Default value is 0.0075
+        * @type {number}
+        */
+        public area: number = 0.0075;
+
+        /**
+        * Related to area, used to interpolate SSAO samples (second interpolate function input) based on the occlusion difference of each pixel
+        * Must not be equal to area and inferior to area.
+        * Default value is 0.0002
+        * @type {number}
+        */
+        public fallOff: number = 0.0002;
+
         private _scene: Scene;
         private _depthTexture: RenderTargetTexture;
         private _randomTexture: DynamicTexture;
@@ -44,7 +72,7 @@
          * @constructor
          * @param {string} name - The rendering pipeline name
          * @param {BABYLON.Scene} scene - The scene linked to this pipeline
-         * @param {any} ratio - The size of the postprocesses (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
+         * @param {any} ratio - The size of the postprocesses. Can be a number shared between passes or an object for more precision: { ssaoRatio: 0.5, combineRatio: 1.0 }
          * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
          */
         constructor(name: string, scene: Scene, ratio: any, cameras?: Camera[]) {
@@ -61,8 +89,8 @@
 
             this._originalColorPostProcess = new PassPostProcess("SSAOOriginalSceneColor", combineRatio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
             this._createSSAOPostProcess(ssaoRatio);
-            this._blurHPostProcess = new BlurPostProcess("SSAOBlurH", new Vector2(2.0, 0.0), 1.3, ssaoRatio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
-            this._blurVPostProcess = new BlurPostProcess("SSAOBlurV", new Vector2(0.0, 2.0), 1.3, ssaoRatio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this._blurHPostProcess = new BlurPostProcess("SSAOBlurH", new Vector2(2.0, 0.0), 2.0, ssaoRatio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this._blurVPostProcess = new BlurPostProcess("SSAOBlurV", new Vector2(0.0, 2.0), 2.0, ssaoRatio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
             this._createSSAOCombinePostProcess(combineRatio);
 
             // Set up pipeline
@@ -135,7 +163,7 @@
             ];
             var samplesFactor = 1.0 / 16.0;
 
-            this._ssaoPostProcess = new PostProcess("ssao", "ssao", ["sampleSphere", "samplesFactor", "randTextureTiles"],
+            this._ssaoPostProcess = new PostProcess("ssao", "ssao", ["sampleSphere", "samplesFactor", "randTextureTiles", "totalStrength", "radius", "area", "fallOff"],
                                                     ["randomSampler"],
                                                     ratio, null, Texture.BILINEAR_SAMPLINGMODE,
                                                     this._scene.getEngine(), false);
@@ -148,6 +176,11 @@
                     this._firstUpdate = false;
                 }
 
+                effect.setFloat("totalStrength", this.totalStrength);
+                effect.setFloat("radius", this.radius);
+                effect.setFloat("area", this.area);
+                effect.setFloat("fallOff", this.fallOff);
+
                 effect.setTexture("textureSampler", this._depthTexture);
                 effect.setTexture("randomSampler", this._randomTexture);
             };

+ 15 - 3
Babylon/Rendering/babylon.boundingBoxRenderer.js

@@ -7,7 +7,12 @@ var BABYLON;
             this.showBackLines = true;
             this.renderList = new BABYLON.SmartArray(32);
             this._scene = scene;
-            this._colorShader = new BABYLON.ShaderMaterial("colorShader", scene, "color", {
+        }
+        BoundingBoxRenderer.prototype._prepareRessources = function () {
+            if (this._colorShader) {
+                return;
+            }
+            this._colorShader = new BABYLON.ShaderMaterial("colorShader", this._scene, "color", {
                 attributes: ["position"],
                 uniforms: ["worldViewProjection", "color"]
             });
@@ -15,12 +20,16 @@ var BABYLON;
             var boxdata = BABYLON.VertexData.CreateBox(1.0);
             this._vb = new BABYLON.VertexBuffer(engine, boxdata.positions, BABYLON.VertexBuffer.PositionKind, false);
             this._ib = engine.createIndexBuffer([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 7, 1, 6, 2, 5, 3, 4]);
-        }
+        };
         BoundingBoxRenderer.prototype.reset = function () {
             this.renderList.reset();
         };
         BoundingBoxRenderer.prototype.render = function () {
-            if (this.renderList.length === 0 || !this._colorShader.isReady()) {
+            if (this.renderList.length === 0) {
+                return;
+            }
+            this._prepareRessources();
+            if (!this._colorShader.isReady()) {
                 return;
             }
             var engine = this._scene.getEngine();
@@ -57,6 +66,9 @@ var BABYLON;
             engine.setDepthWrite(true);
         };
         BoundingBoxRenderer.prototype.dispose = function () {
+            if (!this._colorShader) {
+                return;
+            }
             this._colorShader.dispose();
             this._vb.dispose();
             this._scene.getEngine()._releaseBuffer(this._ib);

+ 19 - 3
Babylon/Rendering/babylon.boundingBoxRenderer.ts

@@ -11,9 +11,15 @@
         private _ib: WebGLBuffer;
 
         constructor(scene: Scene) {
-
             this._scene = scene;
-            this._colorShader = new ShaderMaterial("colorShader", scene, "color",
+        }
+
+        private _prepareRessources(): void {
+            if (this._colorShader) {
+                return;
+            }
+
+            this._colorShader = new ShaderMaterial("colorShader", this._scene, "color",
                 {
                     attributes: ["position"],
                     uniforms: ["worldViewProjection", "color"]
@@ -31,7 +37,13 @@
         }
 
         public render(): void {
-            if (this.renderList.length === 0 || !this._colorShader.isReady()) {
+            if (this.renderList.length === 0) {
+                return;
+            }
+
+            this._prepareRessources();
+
+            if (!this._colorShader.isReady()) {
                 return;
             }
 
@@ -78,6 +90,10 @@
         }
 
         public dispose(): void {
+            if (!this._colorShader) {
+                return;
+            }
+
             this._colorShader.dispose();
             this._vb.dispose();
             this._scene.getEngine()._releaseBuffer(this._ib);

+ 10 - 12
Babylon/Shaders/chromaticAberration.fragment.fx

@@ -1,9 +1,7 @@
-/*
-	BABYLON.JS Chromatic Aberration GLSL Shader
-	Author: Olivier Guyot
-	Separates very slightly R, G and B colors on the edges of the screen
-	Inspired by Francois Tarlier & Martins Upitis	
-*/
+// BABYLON.JS Chromatic Aberration GLSL Shader
+// Author: Olivier Guyot
+// Separates very slightly R, G and B colors on the edges of the screen
+// Inspired by Francois Tarlier & Martins Upitis
 
 #ifdef GL_ES
 precision highp float;
@@ -22,18 +20,18 @@ varying vec2 vUV;
 
 void main(void)
 {
-	vec2 centered_screen_pos = vec2(vUV.x-0.5, vUV.y-0.5);
+	vec2 centered_screen_pos = vec2(vUV.x - 0.5, vUV.y - 0.5);
 	float radius2 = centered_screen_pos.x*centered_screen_pos.x
-					+ centered_screen_pos.y*centered_screen_pos.y;
+		+ centered_screen_pos.y*centered_screen_pos.y;
 	float radius = sqrt(radius2);
 
 	vec4 original = texture2D(textureSampler, vUV);
 
-	if(chromatic_aberration > 0.0) {
+	if (chromatic_aberration > 0.0) {
 		//index of refraction of each color channel, causing chromatic dispersion
-		vec3 ref_indices = vec3(0.6, 0.3, 0.0);
-		float ref_shiftX = chromatic_aberration * radius * 12.0 / screen_width;
-		float ref_shiftY = chromatic_aberration * radius * 12.0 / screen_height;
+		vec3 ref_indices = vec3(-0.3, 0.0, 0.3);
+		float ref_shiftX = chromatic_aberration * radius * 17.0 / screen_width;
+		float ref_shiftY = chromatic_aberration * radius * 17.0 / screen_height;
 
 		// shifts for red, green & blue
 		vec2 ref_coords_r = vec2(vUV.x + ref_indices.r*ref_shiftX, vUV.y + ref_indices.r*ref_shiftY*0.5);

+ 95 - 165
Babylon/Shaders/depthOfField.fragment.fx

@@ -1,9 +1,7 @@
-/*
-	BABYLON.JS Depth-of-field GLSL Shader
-	Author: Olivier Guyot
-	Does depth-of-field blur, edge blur, highlights enhancing
-	Inspired by Francois Tarlier & Martins Upitis
-*/
+// BABYLON.JS Depth-of-field GLSL Shader
+// Author: Olivier Guyot
+// Does depth-of-field blur, edge blur
+// Inspired by Francois Tarlier & Martins Upitis
 
 #ifdef GL_ES
 precision highp float;
@@ -12,12 +10,12 @@ precision highp float;
 
 // samplers
 uniform sampler2D textureSampler;
+uniform sampler2D highlightsSampler;
 uniform sampler2D depthSampler;
 uniform sampler2D grainSampler;
 
 // uniforms
 uniform float grain_amount;
-uniform bool pentagon;
 uniform float maxZ;
 uniform bool blur_noise;
 uniform float screen_width;
@@ -25,24 +23,14 @@ uniform float screen_height;
 uniform float distortion;
 uniform float focus_depth;
 uniform float aperture;
-uniform float gain;
-uniform float threshold;
 uniform float edge_blur;
+uniform bool highlights;
 
 // varyings
 varying vec2 vUV;
 
 // constants
 #define PI 3.14159265
-const int RING_1_SAMPLES = 4;
-const int RING_2_SAMPLES = 6;
-const int RING_3_SAMPLES = 9;
-const int RING_4_SAMPLES = 12;
-const int RING_5_SAMPLES = 16;
-//const int RING_6_SAMPLES = 15;
-const float RING_STEP_DIST = 0.4;			// a new blur ring is added each time this distance is passed
-const float PENTAGON_ANGLE_SUB = 1.2566;		// 2PI / 5
-const float PENTAGON_ANGLE_SUB_HALF = 0.6283;	// 2PI / 10
 
 // common calculations
 vec2 centered_screen_pos;
@@ -53,7 +41,7 @@ float radius;
 // applies edge distortion on texture coords
 vec2 getDistortedCoords(vec2 coords) {
 
-	if(distortion == 0.0) { return coords; }
+	if (distortion == 0.0) { return coords; }
 
 	vec2 direction = 1.0 * normalize(centered_screen_pos);
 	vec2 dist_coords = vec2(0.5, 0.5);
@@ -66,153 +54,83 @@ vec2 getDistortedCoords(vec2 coords) {
 	return dist_coords;
 }
 
-// picks either original screen color or highlights only
-vec4 getColor(vec2 coords, bool highlight) {
-
-	vec4 color = texture2D(textureSampler, coords);
-
-	if(highlight) {
-		float luminance = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));
-		float lum_threshold;
-		if(threshold > 1.0) { lum_threshold = 0.94 + 0.01 * threshold; }
-		else { lum_threshold = 0.5 + 0.44 * threshold; }
-		if(luminance < lum_threshold) {
-			color.rgb = vec3(0.0, 0.0, 0.0);
-			color.a = 1.0;
-		}
+// returns original screen color after blur
+vec4 getBlurColor(vec2 coords, float size) {
+
+	vec4 col = texture2D(textureSampler, coords);
+	if (size == 0.0) { return col; }
+
+	// there are max. 30 samples; the number of samples chosen is dependant on the blur size
+	// there can be 10, 20 or 30 samples chosen; levels of blur are then 1, 2 or 3
+	float blur_level = min(3.0, ceil(size / 1.0));
+
+	float w = (size / screen_width);
+	float h = (size / screen_height);
+	float total_weight = 1.0;
+
+	col += texture2D(textureSampler, coords + vec2(-0.53*w, 0.15*h))*0.93;
+	col += texture2D(textureSampler, coords + vec2(0.42*w, -0.69*h))*0.90;
+	col += texture2D(textureSampler, coords + vec2(0.20*w, 1.00*h))*0.87;
+	col += texture2D(textureSampler, coords + vec2(-0.97*w, -0.72*h))*0.85;
+	col += texture2D(textureSampler, coords + vec2(1.37*w, -0.14*h))*0.83;
+	col += texture2D(textureSampler, coords + vec2(-1.02*w, 1.16*h))*0.80;
+	col += texture2D(textureSampler, coords + vec2(-0.03*w, -1.69*h))*0.78;
+	col += texture2D(textureSampler, coords + vec2(1.27*w, 1.34*h))*0.76;
+	col += texture2D(textureSampler, coords + vec2(-1.98*w, -0.14*h))*0.74;
+	col += texture2D(textureSampler, coords + vec2(1.66*w, -1.32*h))*0.72;
+	total_weight += 8.18;
+
+	if (blur_level > 1.0) {
+		col += texture2D(textureSampler, coords + vec2(-0.35*w, 2.22*h))*0.70;
+		col += texture2D(textureSampler, coords + vec2(-1.31*w, -1.98*h))*0.67;
+		col += texture2D(textureSampler, coords + vec2(2.42*w, 0.61*h))*0.65;
+		col += texture2D(textureSampler, coords + vec2(-2.31*w, 1.25*h))*0.63;
+		col += texture2D(textureSampler, coords + vec2(0.90*w, -2.59*h))*0.61;
+		col += texture2D(textureSampler, coords + vec2(1.14*w, 2.62*h))*0.59;
+		col += texture2D(textureSampler, coords + vec2(-2.72*w, -1.21*h))*0.56;
+		col += texture2D(textureSampler, coords + vec2(2.93*w, -0.98*h))*0.54;
+		col += texture2D(textureSampler, coords + vec2(-1.56*w, 2.80*h))*0.52;
+		col += texture2D(textureSampler, coords + vec2(-0.77*w, -3.22*h))*0.49;
+		total_weight += 5.96;
 	}
 
-	return color;
-}
-
-// returns a modifier to be applied on the radius, in order to simulate a pentagon
-float pentagonShape(float angle) {
-    float a1 = mod(angle, PENTAGON_ANGLE_SUB) / PENTAGON_ANGLE_SUB - 0.5;
-    float a2 = 0.5 - a1 * a1;
-    return 1.35 - 0.94 * a2;
-}
-
-// returns original screen color after blur
-vec4 getBlurColor(vec2 coords, float size, bool highlight) {
-
-	float w = (size/screen_width);
-	float h = (size/screen_height);
-
-	vec4 col = getColor(coords, highlight);
-	if(size == 0.0) { return col; }
-
-	float s = 1.0;
-	float pw;			// sample x relative coord
-	float ph;			// sample y relative coord
-	float bias = 0.65;	// inner/outer ring bias
-	if(highlight) { bias = 0.95; }
-	float sample_angle;
-	float ratio_rings;
-	float ring_radius;
-	float penta;		// pentagon shape modifier
-
-	int ring_count;
-	if(size >= 6.0 * RING_STEP_DIST) { ring_count = 6; }
-	else if(size >= 5.0 * RING_STEP_DIST) { ring_count = 5; }
-	else if(size >= 4.0 * RING_STEP_DIST) { ring_count = 4; }
-	else if(size >= 3.0 * RING_STEP_DIST) { ring_count = 3; }
-	else if(size >= 2.0 * RING_STEP_DIST) { ring_count = 2; }
-	else { ring_count = 1; }
-	
-	// RING 1
-	if(size > RING_STEP_DIST) {
-		ring_radius = size / float(ring_count);
-		ratio_rings = 1.0 / float(ring_count);
-		for(int i = 0; i < RING_1_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_1_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);
-		}
-	}	
-
-	// RING 2
-	if(size > RING_STEP_DIST * 2.0) {
-		ring_radius = 2.0 * size / float(ring_count);
-		ratio_rings = 2.0 / float(ring_count);
-		for(int i = 0; i < RING_2_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_2_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);  
-		}
-	}	
-
-	// RING 3
-	if(size > RING_STEP_DIST * 3.0) {
-		ring_radius = 3.0 * size / float(ring_count);
-		ratio_rings = 3.0 / float(ring_count);
-		for(int i = 0; i < RING_3_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_3_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);  
-		}
-	}	
-
-	// RING 4
-	if(size > RING_STEP_DIST * 4.0) {
-		ring_radius = 4.0 * size / float(ring_count);
-		ratio_rings = 4.0 / float(ring_count);
-		for(int i = 0; i < RING_4_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_4_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);  
-		}
-	}	
-
-	// RING 5
-	if(size > RING_STEP_DIST * 5.0) {
-		ring_radius = 5.0 * size / float(ring_count);
-		ratio_rings = 5.0 / float(ring_count);
-		for(int i = 0; i < RING_5_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_5_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);  
-		}
-	}	
+	if (blur_level > 2.0) {
+		col += texture2D(textureSampler, coords + vec2(2.83*w, 1.92*h))*0.46;
+		col += texture2D(textureSampler, coords + vec2(-3.49*w, 0.51*h))*0.44;
+		col += texture2D(textureSampler, coords + vec2(2.30*w, -2.82*h))*0.41;
+		col += texture2D(textureSampler, coords + vec2(0.22*w, 3.74*h))*0.38;
+		col += texture2D(textureSampler, coords + vec2(-2.76*w, -2.68*h))*0.34;
+		col += texture2D(textureSampler, coords + vec2(3.95*w, 0.11*h))*0.31;
+		col += texture2D(textureSampler, coords + vec2(-3.07*w, 2.65*h))*0.26;
+		col += texture2D(textureSampler, coords + vec2(0.48*w, -4.13*h))*0.22;
+		col += texture2D(textureSampler, coords + vec2(2.49*w, 3.46*h))*0.15;
+		total_weight += 2.97;
+	}
 
-	col /= s;		// scales color according to samples taken
+	col /= total_weight;		// scales color according to weights
 	col.a = 1.0;
 
+	// blur levels debug
+	// if(blur_level == 1.0) { col.b = 0.0; }
+	// if(blur_level == 2.0) { col.r = 0.0; }
+	// if(blur_level == 3.0) { col.g = 0.0; }
+
 	return col;
 }
 
 // on-the-fly constant noise
 vec2 rand(vec2 co)
 {
-	float noise1 = (fract(sin(dot(co ,vec2(12.9898,78.233))) * 43758.5453));
-	float noise2 = (fract(sin(dot(co ,vec2(12.9898,78.233)*2.0)) * 43758.5453));
-	return clamp(vec2(noise1,noise2),0.0,1.0);
+	float noise1 = (fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453));
+	float noise2 = (fract(sin(dot(co, vec2(12.9898, 78.233)*2.0)) * 43758.5453));
+	return clamp(vec2(noise1, noise2), 0.0, 1.0);
 }
 
 void main(void)
 {
 
 	// Common calc
-	centered_screen_pos = vec2(vUV.x-0.5, vUV.y-0.5);
+	centered_screen_pos = vec2(vUV.x - 0.5, vUV.y - 0.5);
 	radius2 = centered_screen_pos.x*centered_screen_pos.x + centered_screen_pos.y*centered_screen_pos.y;
 	radius = sqrt(radius2);
 
@@ -222,41 +140,53 @@ void main(void)
 
 	// blur from depth of field effect
 	float dof_blur_amount = 0.0;
-	if(focus_depth != -1.0) {
+	float depth_bias = 0.0;		// positive if the pixel is further than focus depth; negative if closer
+	if (focus_depth != -1.0) {
 		vec4 depth_sample = texture2D(depthSampler, distorted_coords);
 		float depth = depth_sample.r;
-		dof_blur_amount = abs(depth - focus_depth) * aperture * 3.5;
-		if(dof_blur_amount < 0.05) { dof_blur_amount = 0.0; }				// no blur at all
-		else if( depth - focus_depth < 0.0 ) { dof_blur_amount *= 2.0; }	// blur more when close to camera
-		dof_blur_amount = clamp(dof_blur_amount, 0.0, 1.0);
+		depth_bias = depth - focus_depth;
+
+		// compute blur amount with distance
+		if (depth_bias > 0.0) { dof_blur_amount = depth_bias * aperture * 2.2; }
+		else { dof_blur_amount = depth_bias * depth_bias * aperture * 30.0; }
+
+		if (dof_blur_amount < 0.05) { dof_blur_amount = 0.0; }	// no blur at all
 	}
 
 	// blur from edge blur effect
 	float edge_blur_amount = 0.0;
-	if(edge_blur > 0.0) {
-		edge_blur_amount = clamp( ( radius*2.0 - 1.0 + 0.15*edge_blur ) * 1.5 , 0.0 , 1.0 ) * 1.3;
+	if (edge_blur > 0.0) {
+		edge_blur_amount = clamp((radius*2.0 - 1.0 + 0.15*edge_blur) * 1.5, 0.0, 1.0) * 1.3;
 	}
 
 	// total blur amount
 	float blur_amount = max(edge_blur_amount, dof_blur_amount);
 
 	// apply blur if necessary
-	if(blur_amount == 0.0) {
-		gl_FragColor = getColor(distorted_coords, false);
-	} else {
-		gl_FragColor = getBlurColor(distorted_coords, blur_amount * 1.7, false)
-					   + gain * blur_amount*getBlurColor(distorted_coords, blur_amount * 2.75, true);
+	if (blur_amount == 0.0) {
+		gl_FragColor = texture2D(textureSampler, distorted_coords);
+	}
+	else {
+
+		// add blurred color
+		gl_FragColor = getBlurColor(distorted_coords, blur_amount * 1.7);
+
+		// if further than focus depth & we have computed highlights: enhance highlights
+		if (depth_bias > 0.0 && highlights) {
+			gl_FragColor += clamp(dof_blur_amount, 0.0, 1.0)*texture2D(highlightsSampler, distorted_coords);
+		}
 
-		if(blur_noise) {
+		if (blur_noise) {
 			// we put a slight amount of noise in the blurred color
 			vec2 noise = rand(distorted_coords) * 0.01 * blur_amount;
 			vec2 blurred_coord = vec2(distorted_coords.x + noise.x, distorted_coords.y + noise.y);
-			gl_FragColor = 0.04 * getColor(blurred_coord, false) + 0.96 * gl_FragColor;
+			gl_FragColor = 0.04 * texture2D(textureSampler, blurred_coord) + 0.96 * gl_FragColor;
 		}
 	}
 
-	if(grain_amount > 0.0) {
+	// apply grain
+	if (grain_amount > 0.0) {
 		vec4 grain_color = texture2D(grainSampler, texels_coords*0.003);
-		gl_FragColor.rgb += ( -0.5 + grain_color.rgb ) * 0.20;
+		gl_FragColor.rgb += (-0.5 + grain_color.rgb) * 0.20;
 	}
 }

+ 140 - 0
Babylon/Shaders/lensHighlights.fragment.fx

@@ -0,0 +1,140 @@
+#ifdef GL_ES
+precision highp float;
+#endif
+
+// samplers
+uniform sampler2D textureSampler;	// original color
+
+// uniforms
+uniform float gain;
+uniform float threshold;
+uniform bool pentagon;
+uniform float screen_width;
+uniform float screen_height;
+
+// varyings
+varying vec2 vUV;
+
+// apply luminance filter
+vec4 highlightColor(vec4 color) {
+	vec4 highlight = color;
+	float luminance = dot(highlight.rgb, vec3(0.2125, 0.7154, 0.0721));
+	float lum_threshold;
+	if (threshold > 1.0) { lum_threshold = 0.94 + 0.01 * threshold; }
+	else { lum_threshold = 0.5 + 0.44 * threshold; }
+
+	luminance = clamp((luminance - lum_threshold) * (1.0 / (1.0 - lum_threshold)), 0.0, 1.0);
+
+	highlight *= luminance * gain;
+	highlight.a = 1.0;
+
+	return highlight;
+}
+
+void main(void)
+{
+	vec4 original = texture2D(textureSampler, vUV);
+
+	// quick exit if no highlight computing
+	if (gain == -1.0) {
+		gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
+		return;
+	}
+
+	float w = 2.0 / screen_width;
+	float h = 2.0 / screen_height;
+
+	float weight = 1.0;
+
+	// compute blurred color
+	vec4 blurred = vec4(0.0, 0.0, 0.0, 0.0);
+
+	if (pentagon) {
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.84*w, 0.43*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.48*w, -1.29*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.61*w, 1.51*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.55*w, -0.74*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.71*w, -0.52*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.94*w, 1.59*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.40*w, -1.87*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.62*w, 1.16*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.09*w, 0.25*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.46*w, -1.71*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.08*w, 2.42*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.85*w, -1.89*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.89*w, 0.16*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.29*w, 1.88*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.40*w, -2.81*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.54*w, 2.26*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.60*w, -0.61*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.31*w, -1.30*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.83*w, 2.53*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.12*w, -2.48*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.60*w, 1.11*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.82*w, 0.99*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.50*w, -2.81*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.85*w, 3.33*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.94*w, -1.92*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.27*w, -0.53*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.95*w, 2.48*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.23*w, -3.04*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.17*w, 2.05*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.97*w, -0.04*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.25*w, -2.00*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.31*w, 3.08*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.94*w, -2.59*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.37*w, 0.64*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-3.13*w, 1.93*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.03*w, -3.65*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.60*w, 3.17*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-3.14*w, -1.19*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.00*w, -1.19*h)));
+	}
+	else {
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.85*w, 0.36*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.52*w, -1.14*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.46*w, 1.42*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.46*w, -0.83*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.79*w, -0.42*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.11*w, 1.62*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.29*w, -2.07*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.69*w, 1.39*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.28*w, 0.12*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.65*w, -1.69*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.08*w, 2.44*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.63*w, -1.90*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.55*w, 0.31*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.13*w, 1.52*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.56*w, -2.61*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.38*w, 2.34*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.64*w, -0.81*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.53*w, -1.21*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.06*w, 2.63*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.00*w, -2.69*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.59*w, 1.32*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.82*w, 0.78*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.57*w, -2.50*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.54*w, 2.93*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.39*w, -1.81*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.01*w, -0.28*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.04*w, 2.25*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.02*w, -3.05*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.09*w, 2.25*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-3.07*w, -0.25*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.44*w, -1.90*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.52*w, 3.05*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.68*w, -2.61*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.01*w, 0.79*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.76*w, 1.46*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.05*w, -2.94*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.21*w, 2.88*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.84*w, -1.30*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.98*w, -0.96*h)));
+	}
+
+	blurred /= 39.0;
+
+	gl_FragColor = blurred;
+
+	//if(vUV.x > 0.5) { gl_FragColor.rgb *= 0.0; }
+}

+ 8 - 7
Babylon/Shaders/ssao.fragment.fx

@@ -11,10 +11,15 @@ uniform float randTextureTiles;
 uniform float samplesFactor;
 uniform vec3 sampleSphere[16];
 
+uniform float totalStrength;
+uniform float radius;
+uniform float area;
+uniform float fallOff;
+
 varying vec2 vUV;
 
-const vec2 offset1 = vec2(0.0, 0.01);
-const vec2 offset2 = vec2(0.01, 0.0);
+const vec2 offset1 = vec2(0.0, 0.001);
+const vec2 offset2 = vec2(0.001, 0.0);
 
 vec3 normalFromDepth(const float depth, const vec2 coords) {
 	float depth1 = texture2D(textureSampler, coords + offset1).r;
@@ -31,11 +36,7 @@ vec3 normalFromDepth(const float depth, const vec2 coords) {
 
 void main(void)
 {
-	const float totalStrength = 1.0;
 	const float base = 0.2;
-	const float area = 0.0075;
-	const float fallOff = 0.000001;
-	const float radius = 0.0005;
 
 	vec3 random = texture2D(randomSampler, vUV * randTextureTiles).rgb;
 	float depth = texture2D(textureSampler, vUV).r;
@@ -52,7 +53,7 @@ void main(void)
 	for (int i = 0; i < SAMPLES; i++)
 	{
 		ray = radiusDepth * reflect(sampleSphere[i], random);
-		hemiRay = position + dot(ray, normal) * ray;
+		hemiRay = position + sign(dot(ray, normal)) * ray;
 
 		occlusionDepth = texture2D(textureSampler, clamp(hemiRay.xy, 0.0, 1.0)).r;
 		difference = depth - occlusionDepth;

+ 42 - 5
Babylon/babylon.engine.js

@@ -7,10 +7,25 @@ var BABYLON;
             this._isDepthFuncDirty = false;
             this._isCullFaceDirty = false;
             this._isCullDirty = false;
+            this._isZOffsetDirty = false;
         }
         Object.defineProperty(_DepthCullingState.prototype, "isDirty", {
             get: function () {
-                return this._isDepthFuncDirty || this._isDepthTestDirty || this._isDepthMaskDirty || this._isCullFaceDirty || this._isCullDirty;
+                return this._isDepthFuncDirty || this._isDepthTestDirty || this._isDepthMaskDirty || this._isCullFaceDirty || this._isCullDirty || this._isZOffsetDirty;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(_DepthCullingState.prototype, "zOffset", {
+            get: function () {
+                return this._zOffset;
+            },
+            set: function (value) {
+                if (this._zOffset === value) {
+                    return;
+                }
+                this._zOffset = value;
+                this._isZOffsetDirty = true;
             },
             enumerable: true,
             configurable: true
@@ -91,11 +106,13 @@ var BABYLON;
             this._depthFunc = null;
             this._cull = null;
             this._cullFace = null;
+            this._zOffset = 0;
             this._isDepthTestDirty = true;
             this._isDepthMaskDirty = true;
             this._isDepthFuncDirty = false;
             this._isCullFaceDirty = false;
             this._isCullDirty = false;
+            this._isZOffsetDirty = false;
         };
         _DepthCullingState.prototype.apply = function (gl) {
             if (!this.isDirty) {
@@ -136,6 +153,17 @@ var BABYLON;
                 gl.depthFunc(this.depthFunc);
                 this._isDepthFuncDirty = false;
             }
+            // zOffset
+            if (this._isZOffsetDirty) {
+                if (this.zOffset) {
+                    gl.enable(gl.POLYGON_OFFSET_FILL);
+                    gl.polygonOffset(this.zOffset, 0);
+                }
+                else {
+                    gl.disable(gl.POLYGON_OFFSET_FILL);
+                }
+                this._isZOffsetDirty = false;
+            }
         };
         return _DepthCullingState;
     })();
@@ -370,9 +398,6 @@ var BABYLON;
             };
             window.addEventListener("blur", this._onBlur);
             window.addEventListener("focus", this._onFocus);
-            // Textures
-            this._workingCanvas = document.createElement("canvas");
-            this._workingContext = this._workingCanvas.getContext("2d");
             // Viewport
             this._hardwareScalingLevel = 1.0 / (window.devicePixelRatio || 1.0);
             this.resize();
@@ -551,6 +576,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Engine.prototype._prepareWorkingCanvas = function () {
+            if (this._workingCanvas) {
+                return;
+            }
+            this._workingCanvas = document.createElement("canvas");
+            this._workingContext = this._workingCanvas.getContext("2d");
+        };
         Engine.prototype.getGlInfo = function () {
             return {
                 vendor: this._glVendor,
@@ -1105,7 +1137,8 @@ var BABYLON;
             this._gl.uniform4f(uniform, color3.r, color3.g, color3.b, alpha);
         };
         // States
-        Engine.prototype.setState = function (culling, force) {
+        Engine.prototype.setState = function (culling, zOffset, force) {
+            if (zOffset === void 0) { zOffset = 0; }
             // Culling        
             if (this._depthCullingState.cull !== culling || force) {
                 if (culling) {
@@ -1116,6 +1149,8 @@ var BABYLON;
                     this._depthCullingState.cull = false;
                 }
             }
+            // Z offset
+            this._depthCullingState.zOffset = zOffset;
         };
         Engine.prototype.setDepthBuffer = function (enable) {
             this._depthCullingState.depthTest = enable;
@@ -1259,6 +1294,7 @@ var BABYLON;
                     prepareWebGLTexture(texture, _this._gl, scene, img.width, img.height, invertY, noMipmap, false, function (potWidth, potHeight) {
                         var isPot = (img.width === potWidth && img.height === potHeight);
                         if (!isPot) {
+                            _this._prepareWorkingCanvas();
                             _this._workingCanvas.width = potWidth;
                             _this._workingCanvas.height = potHeight;
                             if (samplingMode === BABYLON.Texture.NEAREST_SAMPLINGMODE) {
@@ -1494,6 +1530,7 @@ var BABYLON;
                 cascadeLoad(rootUrl, scene, function (imgs) {
                     var width = BABYLON.Tools.GetExponantOfTwo(imgs[0].width, _this._caps.maxCubemapTextureSize);
                     var height = width;
+                    _this._prepareWorkingCanvas();
                     _this._workingCanvas.width = width;
                     _this._workingCanvas.height = height;
                     var faces = [

+ 45 - 7
Babylon/babylon.engine.ts

@@ -5,15 +5,30 @@
         private _isDepthFuncDirty = false;
         private _isCullFaceDirty = false;
         private _isCullDirty = false;
+        private _isZOffsetDirty = false;
 
         private _depthTest: boolean;
         private _depthMask: boolean;
         private _depthFunc: number;
         private _cull: boolean;
         private _cullFace: number;
+        private _zOffset: number;
 
         public get isDirty(): boolean {
-            return this._isDepthFuncDirty || this._isDepthTestDirty || this._isDepthMaskDirty || this._isCullFaceDirty || this._isCullDirty;
+            return this._isDepthFuncDirty || this._isDepthTestDirty || this._isDepthMaskDirty || this._isCullFaceDirty || this._isCullDirty || this._isZOffsetDirty;
+        }
+
+        public get zOffset(): number {
+            return this._zOffset;
+        }
+
+        public set zOffset(value: number) {
+            if (this._zOffset === value) {
+                return;
+            }
+
+            this._zOffset = value;
+            this._isZOffsetDirty = true;
         }
 
         public get cullFace(): number {
@@ -87,12 +102,14 @@
             this._depthFunc = null;
             this._cull = null;
             this._cullFace = null;
+            this._zOffset = 0;
 
             this._isDepthTestDirty = true;
             this._isDepthMaskDirty = true;
             this._isDepthFuncDirty = false;
             this._isCullFaceDirty = false;
             this._isCullDirty = false;
+            this._isZOffsetDirty = false;
         }
 
         public apply(gl: WebGLRenderingContext) {
@@ -139,6 +156,18 @@
                 gl.depthFunc(this.depthFunc);
                 this._isDepthFuncDirty = false;
             }
+
+            // zOffset
+            if (this._isZOffsetDirty) {
+                if (this.zOffset) {
+                    gl.enable(gl.POLYGON_OFFSET_FILL);
+                    gl.polygonOffset(this.zOffset, 0);
+                } else {
+                    gl.disable(gl.POLYGON_OFFSET_FILL);
+                }
+
+                this._isZOffsetDirty = false;
+            }
         }
     }
 
@@ -540,11 +569,6 @@
             window.addEventListener("blur", this._onBlur);
             window.addEventListener("focus", this._onFocus);
 
-
-            // Textures
-            this._workingCanvas = document.createElement("canvas");
-            this._workingContext = this._workingCanvas.getContext("2d");
-
             // Viewport
             this._hardwareScalingLevel = 1.0 / (window.devicePixelRatio || 1.0);
             this.resize();
@@ -638,6 +662,15 @@
             Tools.Log("Babylon.js engine (v" + Engine.Version + ") launched");
         }
 
+        private _prepareWorkingCanvas(): void {
+            if (this._workingCanvas) {
+                return;
+            }
+
+            this._workingCanvas = document.createElement("canvas");
+            this._workingContext = this._workingCanvas.getContext("2d");
+        }
+
         public getGlInfo() {
             return {
                 vendor: this._glVendor,
@@ -1335,7 +1368,7 @@
         }
 
         // States
-        public setState(culling: boolean, force?: boolean): void {
+        public setState(culling: boolean, zOffset: number = 0, force?: boolean): void {
             // Culling        
             if (this._depthCullingState.cull !== culling || force) {
                 if (culling) {
@@ -1345,6 +1378,9 @@
                     this._depthCullingState.cull = false;
                 }
             }
+
+            // Z offset
+            this._depthCullingState.zOffset = zOffset;
         }
 
         public setDepthBuffer(enable: boolean): void {
@@ -1517,6 +1553,7 @@
                     prepareWebGLTexture(texture, this._gl, scene, img.width, img.height, invertY, noMipmap, false, (potWidth, potHeight) => {
                         var isPot = (img.width === potWidth && img.height === potHeight);
                         if (!isPot) {
+                            this._prepareWorkingCanvas();
                             this._workingCanvas.width = potWidth;
                             this._workingCanvas.height = potHeight;
 
@@ -1808,6 +1845,7 @@
                     var width = Tools.GetExponantOfTwo(imgs[0].width, this._caps.maxCubemapTextureSize);
                     var height = width;
 
+                    this._prepareWorkingCanvas();
                     this._workingCanvas.width = width;
                     this._workingCanvas.height = height;
 

+ 3 - 2
Babylon/babylon.scene.js

@@ -970,7 +970,8 @@ var BABYLON;
                     var renderTarget = this._renderTargets.data[renderIndex];
                     if (renderTarget._shouldRender()) {
                         this._renderId++;
-                        renderTarget.render(false, this.dumpNextRenderTargets);
+                        var hasSpecialRenderTargetCamera = renderTarget.activeCamera && renderTarget.activeCamera !== this.activeCamera;
+                        renderTarget.render(hasSpecialRenderTargetCamera, this.dumpNextRenderTargets);
                     }
                 }
                 BABYLON.Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
@@ -1133,7 +1134,7 @@ var BABYLON;
                         engine.setViewport(this.activeCamera.viewport);
                         // Camera
                         this.updateTransformMatrix();
-                        renderTarget.render(false, this.dumpNextRenderTargets);
+                        renderTarget.render(currentActiveCamera !== this.activeCamera, this.dumpNextRenderTargets);
                     }
                 }
                 BABYLON.Tools.EndPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);

+ 3 - 2
Babylon/babylon.scene.ts

@@ -1255,7 +1255,8 @@
                     var renderTarget = this._renderTargets.data[renderIndex];
                     if (renderTarget._shouldRender()) {
                         this._renderId++;
-                        renderTarget.render(false, this.dumpNextRenderTargets);
+                        var hasSpecialRenderTargetCamera = renderTarget.activeCamera && renderTarget.activeCamera !== this.activeCamera;
+                        renderTarget.render(hasSpecialRenderTargetCamera, this.dumpNextRenderTargets);
                     }
                 }
                 Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
@@ -1457,7 +1458,7 @@
                         // Camera
                         this.updateTransformMatrix();
 
-                        renderTarget.render(false, this.dumpNextRenderTargets);
+                        renderTarget.render(currentActiveCamera !== this.activeCamera, this.dumpNextRenderTargets);
                     }
                 }
                 Tools.EndPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);

+ 86 - 5
Exporters/Blender/io_export_babylon.py

@@ -139,11 +139,33 @@ class BabylonExporter(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
         default = False,
         )
     
+    attachedSound = bpy.props.StringProperty(
+        name='Music', 
+        description='',
+        default = ''
+    )
+    loopSound = bpy.props.BoolProperty(
+        name='Loop sound', 
+        description='',
+        default = True
+    )
+    autoPlaySound = bpy.props.BoolProperty(
+        name='Auto play sound', 
+        description='',
+        default = True
+    )
+
     def draw(self, context):
         layout = self.layout
 
         layout.prop(self, 'export_onlyCurrentLayer') 
 
+        layout.separator()
+
+        layout.prop(self, 'attachedSound') 
+        layout.prop(self, 'autoPlaySound') 
+        layout.prop(self, 'loopSound') 
+
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     nWarnings = 0      
     @staticmethod
@@ -217,6 +239,16 @@ class BabylonExporter(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
             skeletonId = 0
             self.meshesAndNodes = []
             self.multiMaterials = []
+            self.meshesWithSound = []
+
+            # Music
+            if self.attachedSound != '':
+                music = type('', (), {})()  #Fake mesh object 
+                music.data = type('', (), {})() 
+                music.data.attachedSound = self.attachedSound
+                music.data.loopSound = self.loopSound
+                music.data.autoPlaySound = self.autoPlaySound
+                self.meshesWithSound.append(music)
             
             # exclude lamps in this pass, so ShadowGenerator constructor can be passed meshesAnNodes
             for object in [object for object in scene.objects]:
@@ -241,6 +273,10 @@ class BabylonExporter(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
                     while True and self.isInSelectedLayer(object, scene):
                         mesh = Mesh(object, scene, self.multiMaterials, nextStartFace, forcedParent, nameID)
                         self.meshesAndNodes.append(mesh)
+
+                        if object.data.attachedSound != '':
+                            self.meshesWithSound.append(object)
+
                         nextStartFace = mesh.offsetFace
                         if nextStartFace == 0:
                             break
@@ -251,6 +287,7 @@ class BabylonExporter(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
                             BabylonExporter.warn('WARNING: The following mesh has exceeded the maximum # of vertex elements & will be broken into multiple Babylon meshes: ' + object.name)
 
                         nameID = nameID + 1
+
                 elif object.type == 'EMPTY':
                     self.meshesAndNodes.append(Node(object))
                     
@@ -393,6 +430,26 @@ class BabylonExporter(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
             first = False
             shadowGen.to_scene_file(file_handler)
         file_handler.write(']')
+
+        # Sounds
+        if len(self.meshesWithSound) > 0:
+            file_handler.write('\n,"sounds":[')
+            first = True
+            for mesh in self.meshesWithSound:
+                if first == False:
+                    file_handler.write(',')
+                file_handler.write('{')        
+                write_string(file_handler, 'name', mesh.data.attachedSound, True)        
+                write_bool(file_handler, 'autoplay', mesh.data.autoPlaySound)
+                write_bool(file_handler, 'loop', mesh.data.loopSound) 
+
+                if hasattr(mesh, 'name'):
+                    write_string(file_handler, 'connectedMeshId', mesh.name) 
+                    write_float(file_handler, 'maxDistance', mesh.data.maxSoundDistance) 
+                
+                file_handler.write('}')   
+
+            file_handler.write(']')
         
         # Closing
         file_handler.write('}')
@@ -434,6 +491,7 @@ class World:
             write_float(file_handler, 'fogStart', self.fogStart)
             write_float(file_handler, 'fogEnd', self.fogEnd)
             write_float(file_handler, 'fogDensity', self.fogDensity)
+
 #===============================================================================
 class FCurveAnimatable:
     def __init__(self, object, supportsRotation, supportsPosition, supportsScaling, xOffsetForRotation = 0):  
@@ -564,7 +622,7 @@ class Mesh(FCurveAnimatable):
         
         world = object.matrix_world
         if object.parent and not hasSkeleton:
-            world = object.matrix_local
+            world *= object.parent.matrix_world.inverted()
             
         # use defaults when not None
         if forcedParent is None:
@@ -1370,10 +1428,6 @@ class Material:
                         # Specular
                         BabylonExporter.log('Specular texture found');
                         self.textures.append(Texture('specularTexture', mtex.specular_color_factor, mtex, filepath))
-                    else:
-                        BabylonExporter.warn('WARNING image texture type not recognized:  ' + str(mtex) + ', ignored.')
-
-                        
             else:
                  BabylonExporter.warn('WARNING texture type not currently supported:  ' + mtex.texture.type + ', ignored.')
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -                
@@ -1641,6 +1695,26 @@ bpy.types.Mesh.receiveShadows = bpy.props.BoolProperty(
     description='',
     default = False
 )
+bpy.types.Mesh.attachedSound = bpy.props.StringProperty(
+    name='Sound', 
+    description='',
+    default = ''
+)
+bpy.types.Mesh.loopSound = bpy.props.BoolProperty(
+    name='Loop sound', 
+    description='',
+    default = True
+)
+bpy.types.Mesh.autoPlaySound = bpy.props.BoolProperty(
+    name='Auto play sound', 
+    description='',
+    default = True
+)
+bpy.types.Mesh.maxSoundDistance = bpy.props.FloatProperty(
+    name='Max sound distance', 
+    description='',
+    default = 100
+)
 #===============================================================================
 bpy.types.Camera.autoAnimate = bpy.props.BoolProperty(
     name='Automatically launch animations', 
@@ -1730,6 +1804,13 @@ class ObjectPanel(bpy.types.Panel):
             layout.separator()
 
             layout.prop(ob.data, 'autoAnimate')   
+
+            layout.separator()
+
+            layout.prop(ob.data, 'attachedSound') 
+            layout.prop(ob.data, 'autoPlaySound') 
+            layout.prop(ob.data, 'loopSound') 
+            layout.prop(ob.data, 'maxSoundDistance') 
             
         elif isCamera:
             layout.prop(ob.data, 'CameraType')

Файловите разлики са ограничени, защото са твърде много
+ 532 - 212
babylon.2.1-alpha.debug.js


Файловите разлики са ограничени, защото са твърде много
+ 21 - 21
babylon.2.1-alpha.js