浏览代码

updatem from upstream

nockawa 9 年之前
父节点
当前提交
641bb2105a
共有 30 个文件被更改,包括 4517 次插入3791 次删除
  1. 12 12
      Tools/Gulp/config.json
  2. 21 21
      dist/preview release/babylon.core.js
  3. 1951 1856
      dist/preview release/babylon.d.ts
  4. 35 35
      dist/preview release/babylon.js
  5. 442 169
      dist/preview release/babylon.max.js
  6. 34 34
      dist/preview release/babylon.noworker.js
  7. 3 1
      dist/preview release/what's new.md
  8. 3 0
      src/Actions/babylon.actionManager.js
  9. 6 0
      src/Actions/babylon.directActions.js
  10. 30 0
      src/Animations/babylon.animation.js
  11. 101 43
      src/Canvas2d/babylon.canvas2d.js
  12. 28 6
      src/Canvas2d/babylon.prim2dBase.js
  13. 81 18
      src/Canvas2d/babylon.smartPropertyPrim.js
  14. 2 2
      src/Canvas2d/babylon.smartPropertyPrim.ts
  15. 11 0
      src/Canvas2d/babylon.sprite2d.js
  16. 6 6
      src/Canvas2d/babylon.text2d.js
  17. 2 2
      src/Materials/Textures/babylon.fontTexture.js
  18. 14 0
      src/Materials/Textures/babylon.renderTargetTexture.js
  19. 23 0
      src/Materials/Textures/babylon.renderTargetTexture.ts
  20. 17 5
      src/Materials/babylon.pbrMaterial.js
  21. 1425 1413
      src/Materials/babylon.pbrMaterial.ts
  22. 17 6
      src/Materials/babylon.standardMaterial.js
  23. 18 7
      src/Materials/babylon.standardMaterial.ts
  24. 13 0
      src/Math/babylon.math.js
  25. 1 0
      src/Particles/babylon.solidParticle.js
  26. 1 0
      src/Particles/babylon.solidParticle.ts
  27. 102 73
      src/Particles/babylon.solidParticleSystem.js
  28. 112 79
      src/Particles/babylon.solidParticleSystem.ts
  29. 4 1
      src/Shaders/ShadersInclude/bumpFragmentFunctions.fx
  30. 2 2
      src/Shaders/rect2d.vertex.fx

+ 12 - 12
Tools/Gulp/config.json

@@ -43,18 +43,18 @@
       "../../src/Collisions/babylon.collider.js",
       "../../src/Collisions/babylon.collisionCoordinator.js",
       "../../src/Cameras/babylon.camera.js",
-      "../../src/cameras/babylon.camerainputsmanager.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.mouse.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.keyboard.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.touch.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.deviceorientation.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.vrdeviceorientation.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.gamepad.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.keyboard.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.mousewheel.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.pointers.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.gamepad.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js",
+      "../../src/Cameras/babylon.camerainputsmanager.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.mouse.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.keyboard.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.touch.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.deviceorientation.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.vrdeviceorientation.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.gamepad.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.keyboard.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.mousewheel.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.pointers.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.gamepad.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js",
       "../../src/Cameras/babylon.targetCamera.js",
       "../../src/Cameras/babylon.freeCamera.js",
       "../../src/Cameras/babylon.freeCameraInputsManager.js",

文件差异内容过多而无法显示
+ 21 - 21
dist/preview release/babylon.core.js


文件差异内容过多而无法显示
+ 1951 - 1856
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 35 - 35
dist/preview release/babylon.js


文件差异内容过多而无法显示
+ 442 - 169
dist/preview release/babylon.max.js


文件差异内容过多而无法显示
+ 34 - 34
dist/preview release/babylon.noworker.js


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

@@ -20,7 +20,8 @@
 	  - Added a dynamic [2D Bin Packing Algorithm](http://stackoverflow.com/questions/8762569/how-is-2d-bin-packing-achieved-programmatically), ([more info here](http://www.html5gamedevs.com/topic/22565-two-new-texture-types-fonttexture-and-maptexture/)) ([nockawa](https://github.com/nockawa))
 	  - Introduced Canvas2D feature: a 2D engine to render primitives, sprites in 2D, text. Canvas2D can be displayed in Screen Space (above the 3D scene) or in World Space to be a part of the Scene. [overview](http://doc.babylonjs.com/overviews/Using_The_Canvas2D), [tutorial](http://doc.babylonjs.com/tutorials/Using_the_Canvas2D) ([nockawa](https://github.com/nockawa))	
   - **Updates**
-    - Added support for OpenGL normal maps ([deltakosh](https://github.com/deltakosh))
+    - Renderlists can now also be defined using predicates ([deltakosh](https://github.com/deltakosh))
+    - Added support for various normal maps conventions ([deltakosh](https://github.com/deltakosh))
     - Added postprocess.enablePixelPerfectMode to avoid texture scaling/stretching when dealing with non-power of 2 resolutions. cannot be used on post-processes chain ([deltakosh](https://github.com/deltakosh))
     - Enabled other post processes to be used when also using a 3D Rig ([jcpalmer](https://github.com/Palmer-JC))
     - Added skeleton.getBoneIndexByName(boneName: string) ([dad72](https://github.com/dad72))
@@ -31,6 +32,7 @@
     - New OnPickTrigger support for spritesManager ([deltakosh](https://github.com/deltakosh))
     - New SPS method `digest()` ([jerome](https://github.com/jbousquie))    
     - New SPS property `computeBoundingBox` ([jerome](https://github.com/jbousquie))  
+    - New SPS particle property `isVisible` ([jerome](https://github.com/jbousquie)) 
     - Added a new OnPickOut trigger fired when you release the pointer button outside of a mesh or sprite. ([deltakosh](https://github.com/deltakosh))
     - Added support for OnPointerOver and OnPointerOut for sprites. ([deltakosh](https://github.com/deltakosh))
     - Added an optional predicate on Node.getDescendants, Node.getChildren to filter out Nodes based on a callback execution. ([nockawa](https://github.com/nockawa))

+ 3 - 0
src/Actions/babylon.actionManager.js

@@ -46,6 +46,9 @@ var BABYLON;
         ActionEvent.CreateNewFromScene = function (scene, evt) {
             return new ActionEvent(null, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt);
         };
+        ActionEvent.CreateNewFromPrimitive = function (prim, pointerPos, evt, additionalData) {
+            return new ActionEvent(prim, pointerPos.x, pointerPos.y, null, evt, additionalData);
+        };
         return ActionEvent;
     })();
     BABYLON.ActionEvent = ActionEvent;

+ 6 - 0
src/Actions/babylon.directActions.js

@@ -67,6 +67,9 @@ var BABYLON;
         };
         SetValueAction.prototype.execute = function () {
             this._effectiveTarget[this._property] = this.value;
+            if (this._target.markAsDirty) {
+                this._target.markAsDirty(this._property);
+            }
         };
         SetValueAction.prototype.serialize = function (parent) {
             return _super.prototype._serialize.call(this, {
@@ -98,6 +101,9 @@ var BABYLON;
         };
         IncrementValueAction.prototype.execute = function () {
             this._effectiveTarget[this._property] += this.value;
+            if (this._target.markAsDirty) {
+                this._target.markAsDirty(this._property);
+            }
         };
         IncrementValueAction.prototype.serialize = function (parent) {
             return _super.prototype._serialize.call(this, {

+ 30 - 0
src/Animations/babylon.animation.js

@@ -120,6 +120,9 @@ var BABYLON;
             else if (from instanceof BABYLON.Color3) {
                 dataType = Animation.ANIMATIONTYPE_COLOR3;
             }
+            else if (from instanceof BABYLON.Size) {
+                dataType = Animation.ANIMATIONTYPE_SIZE;
+            }
             if (dataType == undefined) {
                 return null;
             }
@@ -248,6 +251,9 @@ var BABYLON;
         Animation.prototype.vector2InterpolateFunction = function (startValue, endValue, gradient) {
             return BABYLON.Vector2.Lerp(startValue, endValue, gradient);
         };
+        Animation.prototype.sizeInterpolateFunction = function (startValue, endValue, gradient) {
+            return BABYLON.Size.Lerp(startValue, endValue, gradient);
+        };
         Animation.prototype.color3InterpolateFunction = function (startValue, endValue, gradient) {
             return BABYLON.Color3.Lerp(startValue, endValue, gradient);
         };
@@ -342,6 +348,15 @@ var BABYLON;
                                 case Animation.ANIMATIONLOOPMODE_RELATIVE:
                                     return this.vector2InterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
                             }
+                        // Size
+                        case Animation.ANIMATIONTYPE_SIZE:
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    return this.sizeInterpolateFunction(startValue, endValue, gradient);
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return this.sizeInterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
+                            }
                         // Color3
                         case Animation.ANIMATIONTYPE_COLOR3:
                             switch (loopMode) {
@@ -481,6 +496,9 @@ var BABYLON;
                             // Vector2
                             case Animation.ANIMATIONTYPE_VECTOR2:
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
+                            // Size
+                            case Animation.ANIMATIONTYPE_SIZE:
+                                this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
                             // Color3
                             case Animation.ANIMATIONTYPE_COLOR3:
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
@@ -511,6 +529,10 @@ var BABYLON;
                     case Animation.ANIMATIONTYPE_VECTOR2:
                         offsetValue = BABYLON.Vector2.Zero();
                         break;
+                    // Size
+                    case Animation.ANIMATIONTYPE_SIZE:
+                        offsetValue = BABYLON.Size.Zero();
+                        break;
                     // Color3
                     case Animation.ANIMATIONTYPE_COLOR3:
                         offsetValue = BABYLON.Color3.Black();
@@ -604,6 +626,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Animation, "ANIMATIONTYPE_SIZE", {
+            get: function () {
+                return Animation._ANIMATIONTYPE_SIZE;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animation, "ANIMATIONTYPE_QUATERNION", {
             get: function () {
                 return Animation._ANIMATIONTYPE_QUATERNION;
@@ -702,6 +731,7 @@ var BABYLON;
         Animation._ANIMATIONTYPE_MATRIX = 3;
         Animation._ANIMATIONTYPE_COLOR3 = 4;
         Animation._ANIMATIONTYPE_VECTOR2 = 5;
+        Animation._ANIMATIONTYPE_SIZE = 6;
         Animation._ANIMATIONLOOPMODE_RELATIVE = 0;
         Animation._ANIMATIONLOOPMODE_CYCLE = 1;
         Animation._ANIMATIONLOOPMODE_CONSTANT = 2;

+ 101 - 43
src/Canvas2d/babylon.canvas2d.js

@@ -107,14 +107,13 @@ var BABYLON;
             }
             this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", function (k) { return new Canvas2DEngineBoundData(); });
             this._cachingStrategy = cachingstrategy;
-            this._depthLevel = 0;
-            this._hierarchyMaxDepth = 100;
-            this._hierarchyLevelZFactor = 1 / this._hierarchyMaxDepth;
-            this._hierarchyLevelMaxSiblingCount = 1000;
-            this._hierarchySiblingZDelta = this._hierarchyLevelZFactor / this._hierarchyLevelMaxSiblingCount;
             this._primPointerInfo = new BABYLON.PrimitivePointerInfo();
             this._capturedPointers = new BABYLON.StringDictionary();
+            this._pickStartingPosition = BABYLON.Vector2.Zero();
             this.setupGroup2D(this, null, name, BABYLON.Vector2.Zero(), size, this._cachingStrategy === Canvas2D.CACHESTRATEGY_ALLGROUPS ? BABYLON.Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : BABYLON.Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
+            this._hierarchyLevelMaxSiblingCount = 100;
+            this._hierarchyDepthOffset = 0;
+            this._siblingDepthOffset = 1 / this._hierarchyLevelMaxSiblingCount;
             this._scene = scene;
             this._engine = engine;
             this._renderingSize = new BABYLON.Size(0, 0);
@@ -144,6 +143,13 @@ var BABYLON;
             //            this._supprtInstancedArray = false; // TODO REMOVE!!!
             this._setupInteraction(enableInteraction);
         };
+        Object.defineProperty(Canvas2D.prototype, "hierarchyLevelMaxSiblingCount", {
+            get: function () {
+                return this._hierarchyLevelMaxSiblingCount;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Canvas2D.prototype._setupInteraction = function (enable) {
             var _this = this;
             // No change detection
@@ -178,7 +184,7 @@ var BABYLON;
             catch (e) {
             }
             this._primPointerInfo.updateRelatedTarget(primitive, BABYLON.Vector2.Zero());
-            this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerGotCapture);
+            this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerGotCapture, null);
             this._capturedPointers.add(pointerId.toString(), primitive);
             return true;
         };
@@ -196,7 +202,7 @@ var BABYLON;
             catch (e) {
             }
             this._primPointerInfo.updateRelatedTarget(primitive, BABYLON.Vector2.Zero());
-            this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerLostCapture);
+            this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerLostCapture, null);
             this._capturedPointers.remove(pointerId.toString());
             return true;
         };
@@ -234,16 +240,16 @@ var BABYLON;
             this._primPointerInfo.updateRelatedTarget(targetPrim, targetPointerPos);
             // Analyze the pointer event type and fire proper events on the primitive
             if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
-                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMouseWheel);
+                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMouseWheel, eventData.event);
             }
             else if (eventData.type === BABYLON.PointerEventTypes.POINTERMOVE) {
-                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMove);
+                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMove, eventData.event);
             }
             else if (eventData.type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerDown);
+                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerDown, eventData.event);
             }
             else if (eventData.type === BABYLON.PointerEventTypes.POINTERUP) {
-                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerUp);
+                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerUp, eventData.event);
             }
         };
         Canvas2D.prototype._updatePointerInfo = function (eventData) {
@@ -318,12 +324,12 @@ var BABYLON;
                 // Notify the previous "over" prim that the pointer is no longer over it
                 if ((capturedPrim && capturedPrim === prevPrim) || (!capturedPrim && prevPrim)) {
                     this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
-                    this._bubbleNotifyPrimPointerObserver(prevPrim, BABYLON.PrimitivePointerInfo.PointerOut);
+                    this._bubbleNotifyPrimPointerObserver(prevPrim, BABYLON.PrimitivePointerInfo.PointerOut, null);
                 }
                 // Notify the new "over" prim that the pointer is over it
                 if ((capturedPrim && capturedPrim === actualPrim) || (!capturedPrim && actualPrim)) {
                     this._primPointerInfo.updateRelatedTarget(actualPrim, this._actualOverPrimitive.intersectionLocation);
-                    this._bubbleNotifyPrimPointerObserver(actualPrim, BABYLON.PrimitivePointerInfo.PointerOver);
+                    this._bubbleNotifyPrimPointerObserver(actualPrim, BABYLON.PrimitivePointerInfo.PointerOver, null);
                 }
             }
             this._hoverStatusRenderId = this.scene.getRenderId();
@@ -354,8 +360,8 @@ var BABYLON;
             debug += "[RID:" + this.scene.getRenderId() + "] [" + prim.hierarchyDepth + "] event:" + BABYLON.PrimitivePointerInfo.getEventTypeName(mask) + ", id: " + prim.id + " (" + BABYLON.Tools.getClassName(prim) + "), primPos: " + pii.primitivePointerPos.toString() + ", canvasPos: " + pii.canvasPointerPos.toString();
             console.log(debug);
         };
-        Canvas2D.prototype._bubbleNotifyPrimPointerObserver = function (prim, mask) {
-            var pii = this._primPointerInfo;
+        Canvas2D.prototype._bubbleNotifyPrimPointerObserver = function (prim, mask, eventData) {
+            var ppi = this._primPointerInfo;
             // In case of PointerOver/Out we will first notify the children (but the deepest to the closest) with PointerEnter/Leave
             if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) !== 0) {
                 this._notifChildren(prim, mask);
@@ -368,10 +374,11 @@ var BABYLON;
                     this._updatePrimPointerPos(cur);
                     // Exec the observers
                     this._debugExecObserver(cur, mask);
-                    cur._pointerEventObservable.notifyObservers(pii, mask);
+                    cur._pointerEventObservable.notifyObservers(ppi, mask);
+                    this._triggerActionManager(cur, ppi, mask, eventData);
                     // Bubble canceled? If we're not executing PointerOver or PointerOut, quit immediately
                     // If it's PointerOver/Out we have to trigger PointerEnter/Leave no matter what
-                    if (pii.cancelBubble) {
+                    if (ppi.cancelBubble) {
                         if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) === 0) {
                             return;
                         }
@@ -386,16 +393,91 @@ var BABYLON;
                 // Trigger a PointerEnter corresponding to the PointerOver
                 if (mask === BABYLON.PrimitivePointerInfo.PointerOver) {
                     this._debugExecObserver(cur, BABYLON.PrimitivePointerInfo.PointerEnter);
-                    cur._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerEnter);
+                    cur._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerEnter);
                 }
                 else if (mask === BABYLON.PrimitivePointerInfo.PointerOut) {
                     this._debugExecObserver(cur, BABYLON.PrimitivePointerInfo.PointerLeave);
-                    cur._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerLeave);
+                    cur._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerLeave);
                 }
                 // Loop to the parent
                 cur = cur.parent;
             }
         };
+        Canvas2D.prototype._triggerActionManager = function (prim, ppi, mask, eventData) {
+            var _this = this;
+            // Process Trigger related to PointerDown
+            if ((mask & BABYLON.PrimitivePointerInfo.PointerDown) !== 0) {
+                // On pointer down, record the current position and time to be able to trick PickTrigger and LongPressTrigger
+                this._pickStartingPosition = ppi.primitivePointerPos.clone();
+                this._pickStartingTime = new Date().getTime();
+                this._pickedDownPrim = null;
+                if (prim.actionManager) {
+                    this._pickedDownPrim = prim;
+                    if (prim.actionManager.hasPickTriggers) {
+                        var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                        switch (eventData.button) {
+                            case 0:
+                                prim.actionManager.processTrigger(BABYLON.ActionManager.OnLeftPickTrigger, actionEvent);
+                                break;
+                            case 1:
+                                prim.actionManager.processTrigger(BABYLON.ActionManager.OnCenterPickTrigger, actionEvent);
+                                break;
+                            case 2:
+                                prim.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, actionEvent);
+                                break;
+                        }
+                        prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickDownTrigger, actionEvent);
+                    }
+                    if (prim.actionManager.hasSpecificTrigger(BABYLON.ActionManager.OnLongPressTrigger)) {
+                        window.setTimeout(function () {
+                            var ppi = _this._primPointerInfo;
+                            var capturedPrim = _this.getCapturedPrimitive(ppi.pointerId);
+                            _this._updateIntersectionList(ppi.canvasPointerPos, capturedPrim !== null);
+                            var ii = new BABYLON.IntersectInfo2D();
+                            ii.pickPosition = ppi.canvasPointerPos.clone();
+                            ii.findFirstOnly = false;
+                            _this.intersect(ii);
+                            if (ii.isPrimIntersected(prim) !== null) {
+                                if (prim.actionManager) {
+                                    if (_this._pickStartingTime !== 0 && ((new Date().getTime() - _this._pickStartingTime) > BABYLON.ActionManager.LongPressDelay) && (Math.abs(_this._pickStartingPosition.x - ii.pickPosition.x) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(_this._pickStartingPosition.y - ii.pickPosition.y) < BABYLON.ActionManager.DragMovementThreshold)) {
+                                        _this._pickStartingTime = 0;
+                                        prim.actionManager.processTrigger(BABYLON.ActionManager.OnLongPressTrigger, BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData));
+                                    }
+                                }
+                            }
+                        }, BABYLON.ActionManager.LongPressDelay);
+                    }
+                }
+            }
+            else if ((mask & BABYLON.PrimitivePointerInfo.PointerUp) !== 0) {
+                this._pickStartingTime = 0;
+                var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                if (prim.actionManager) {
+                    // OnPickUpTrigger
+                    prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickUpTrigger, actionEvent);
+                    // OnPickTrigger
+                    if (Math.abs(this._pickStartingPosition.x - ppi.canvasPointerPos.x) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ppi.canvasPointerPos.y) < BABYLON.ActionManager.DragMovementThreshold) {
+                        prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, actionEvent);
+                    }
+                }
+                // OnPickOutTrigger
+                if (this._pickedDownPrim && this._pickedDownPrim.actionManager && (this._pickedDownPrim !== prim)) {
+                    this._pickedDownPrim.actionManager.processTrigger(BABYLON.ActionManager.OnPickOutTrigger, actionEvent);
+                }
+            }
+            else if ((mask & BABYLON.PrimitivePointerInfo.PointerOver) !== 0) {
+                if (prim.actionManager) {
+                    var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                    prim.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOverTrigger, actionEvent);
+                }
+            }
+            else if ((mask & BABYLON.PrimitivePointerInfo.PointerOut) !== 0) {
+                if (prim.actionManager) {
+                    var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                    prim.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOutTrigger, actionEvent);
+                }
+            }
+        };
         Canvas2D.prototype._notifChildren = function (prim, mask) {
             var _this = this;
             var pii = this._primPointerInfo;
@@ -586,30 +668,6 @@ var BABYLON;
                 throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
             }
         };
-        Object.defineProperty(Canvas2D.prototype, "hierarchySiblingZDelta", {
-            /**
-             * Read-only property that return the Z delta to apply for each sibling primitives inside of a given one.
-             * Sibling Primitives are defined in a specific order, the first ones will be draw below the next ones.
-             * This property define the Z value to apply between each sibling Primitive. Current implementation allows 1000 Siblings Primitives per level.
-             * @returns The Z Delta
-             */
-            get: function () {
-                return this._hierarchySiblingZDelta;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(Canvas2D.prototype, "hierarchyLevelZFactor", {
-            /**
-             * Return the Z Factor that will be applied for each new hierarchy level.
-             * @returns The Z Factor
-             */
-            get: function () {
-                return this._hierarchyLevelZFactor;
-            },
-            enumerable: true,
-            configurable: true
-        });
         Canvas2D.prototype._updateCanvasState = function () {
             // Check if the update has already been made for this render Frame
             if (this.scene.getRenderId() === this._updateRenderId) {

+ 28 - 6
src/Canvas2d/babylon.prim2dBase.js

@@ -207,6 +207,15 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        IntersectInfo2D.prototype.isPrimIntersected = function (prim) {
+            for (var _i = 0, _a = this.intersectedPrimitives; _i < _a.length; _i++) {
+                var cur = _a[_i];
+                if (cur.prim === prim) {
+                    return cur.intersectionLocation;
+                }
+            }
+            return null;
+        };
         // Internals, don't use
         IntersectInfo2D.prototype._exit = function (firstLevel) {
             if (firstLevel) {
@@ -258,6 +267,16 @@ var BABYLON;
             this.levelVisible = isVisible;
             this.origin = new BABYLON.Vector2(0.5, 0.5);
         };
+        Object.defineProperty(Prim2DBase.prototype, "actionManager", {
+            get: function () {
+                if (!this._actionManager) {
+                    this._actionManager = new BABYLON.ActionManager(this.owner.scene);
+                }
+                return this._actionManager;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * From 'this' primitive, traverse up (from parent to parent) until the given predicate is true
          * @param predicate the predicate to test on each parent
@@ -601,15 +620,18 @@ var BABYLON;
             this._children.splice(prevIndex + 1, 0, this._children.splice(childIndex, 1)[0]);
         };
         Prim2DBase.prototype.addChild = function (child) {
-            child._siblingDepthOffset = (this._children.length + 1) * this.owner.hierarchySiblingZDelta;
-            child._depthLevel = this._depthLevel + 1;
-            child._hierarchyDepthOffset = child._depthLevel * this.owner.hierarchyLevelZFactor;
+            child._hierarchyDepthOffset = this._hierarchyDepthOffset + ((this._children.length + 1) * this._siblingDepthOffset);
+            child._siblingDepthOffset = this._siblingDepthOffset / this.owner.hierarchyLevelMaxSiblingCount;
             this._children.push(child);
         };
         Prim2DBase.prototype.dispose = function () {
             if (!_super.prototype.dispose.call(this)) {
                 return false;
             }
+            if (this._actionManager) {
+                this._actionManager.dispose();
+                this._actionManager = null;
+            }
             // If there's a parent, remove this object from its parent list
             if (this._parent) {
                 var i = this._parent._children.indexOf(this);
@@ -627,7 +649,7 @@ var BABYLON;
             return true;
         };
         Prim2DBase.prototype.getActualZOffset = function () {
-            return this._zOrder || 1 - (this._siblingDepthOffset + this._hierarchyDepthOffset);
+            return this._zOrder || (1 - this._hierarchyDepthOffset);
         };
         Prim2DBase.prototype.onPrimBecomesDirty = function () {
             if (this._renderGroup) {
@@ -635,7 +657,7 @@ var BABYLON;
             }
         };
         Prim2DBase.prototype._needPrepare = function () {
-            return (this.isVisible || this._visibilityChanged) && (this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep));
+            return this._visibilityChanged || this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
         };
         Prim2DBase.prototype._prepareRender = function (context) {
             this._prepareRenderPre(context);
@@ -710,7 +732,7 @@ var BABYLON;
                 var curVisibleState = this.isVisible;
                 this.isVisible = (!this._parent || this._parent.isVisible) && this.levelVisible;
                 // Detect a change of visibility
-                this._visibilityChanged = (curVisibleState !== undefined) && curVisibleState !== this.isVisible;
+                this._visibilityChanged = curVisibleState !== this.isVisible;
                 // Get/compute the localTransform
                 var localDirty = this._updateLocalTransform();
                 // Check if we have to update the globalTransform

+ 81 - 18
src/Canvas2d/babylon.smartPropertyPrim.js

@@ -140,22 +140,45 @@ var BABYLON;
             this._instanceDirtyFlags = 0;
             this._isDisposed = false;
             this._levelBoundingInfo = new BABYLON.BoundingInfo2D();
+            this.animations = new Array();
         };
         Object.defineProperty(SmartPropertyPrim.prototype, "isDisposed", {
+            /**
+             * Check if the object is disposed or not.
+             * @returns true if the object is dispose, false otherwise.
+             */
             get: function () {
                 return this._isDisposed;
             },
             enumerable: true,
             configurable: true
         });
+        /**
+         * Disposable pattern, this method must be overloaded by derived types in order to clean up hardware related resources.
+         * @returns false if the object is already dispose, true otherwise. Your implementation must call super.dispose() and check for a false return and return immediately if it's the case.
+         */
         SmartPropertyPrim.prototype.dispose = function () {
             if (this.isDisposed) {
                 return false;
             }
+            // Don't set to null, it may upset somebody...
+            this.animations.splice(0);
             this._isDisposed = true;
             return true;
         };
+        /**
+         * Returns as a new array populated with the Animatable used by the primitive. Must be overloaded by derived primitives.
+         * Look at Sprite2D for more information
+         */
+        SmartPropertyPrim.prototype.getAnimatables = function () {
+            return new Array();
+        };
         Object.defineProperty(SmartPropertyPrim.prototype, "modelKey", {
+            /**
+             * Property giving the Model Key associated to the property.
+             * This value is constructed from the type of the primitive and all the name/value of its properties declared with the modelLevelProperty decorator
+             * @returns the model key string.
+             */
             get: function () {
                 var _this = this;
                 // No need to compute it?
@@ -178,6 +201,10 @@ var BABYLON;
             configurable: true
         });
         Object.defineProperty(SmartPropertyPrim.prototype, "isDirty", {
+            /**
+             * States if the Primitive is dirty and should be rendered again next time.
+             * @returns true is dirty, false otherwise
+             */
             get: function () {
                 return (this._instanceDirtyFlags !== 0) || this._modelDirty;
             },
@@ -185,6 +212,10 @@ var BABYLON;
             configurable: true
         });
         Object.defineProperty(SmartPropertyPrim.prototype, "propDic", {
+            /**
+             * Access the dictionary of properties metadata. Only properties decorated with XXXXLevelProperty are concerned
+             * @returns the dictionary, the key is the property name as declared in Javascript, the value is the metadata object
+             */
             get: function () {
                 if (!this._propInfo) {
                     var cti = ClassTreeInfo.get(Object.getPrototypeOf(this));
@@ -213,7 +244,7 @@ var BABYLON;
             propInfo.name = propName;
             propInfo.dirtyBoundingInfo = dirtyBoundingInfo;
             propInfo.typeLevelCompare = typeLevelCompare;
-            node.levelContent.add(propId.toString(), propInfo);
+            node.levelContent.add(propName, propInfo);
             return propInfo;
         };
         SmartPropertyPrim._checkUnchanged = function (curValue, newValue) {
@@ -236,7 +267,36 @@ var BABYLON;
             }
             return false;
         };
+        SmartPropertyPrim.prototype.markAsDirty = function (propertyName) {
+            var i = propertyName.indexOf(".");
+            if (i !== -1) {
+                propertyName = propertyName.substr(0, i);
+            }
+            var propInfo = this.propDic.get(propertyName);
+            if (!propInfo) {
+                return;
+            }
+            var newValue = this[propertyName];
+            this._handlePropChanged(undefined, newValue, propertyName, propInfo, propInfo.typeLevelCompare);
+        };
         SmartPropertyPrim.prototype._handlePropChanged = function (curValue, newValue, propName, propInfo, typeLevelCompare) {
+            // If the property change also dirty the boundingInfo, update the boundingInfo dirty flags
+            if (propInfo.dirtyBoundingInfo) {
+                this._levelBoundingInfoDirty = true;
+                // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
+                if (this instanceof BABYLON.Prim2DBase) {
+                    var curprim = this.parent;
+                    while (curprim) {
+                        curprim._boundingInfoDirty = true;
+                        if (curprim instanceof BABYLON.Group2D) {
+                            if (curprim.isRenderableGroup) {
+                                break;
+                            }
+                        }
+                        curprim = curprim.parent;
+                    }
+                }
+            }
             // Trigger property changed
             var info = SmartPropertyPrim.propChangedInfo;
             info.oldValue = curValue;
@@ -273,14 +333,28 @@ var BABYLON;
         };
         SmartPropertyPrim.prototype.handleGroupChanged = function (prop) {
         };
+        /**
+         * Check if a given set of properties are dirty or not.
+         * @param flags a ORed combination of Prim2DPropInfo.flagId values
+         * @return true if at least one property is dirty, false if none of them are.
+         */
         SmartPropertyPrim.prototype.checkPropertiesDirty = function (flags) {
             return (this._instanceDirtyFlags & flags) !== 0;
         };
+        /**
+         * Clear a given set of properties.
+         * @param flags a ORed combination of Prim2DPropInfo.flagId values
+         * @return the new set of property still marked as dirty
+         */
         SmartPropertyPrim.prototype.clearPropertiesDirty = function (flags) {
             this._instanceDirtyFlags &= ~flags;
             return this._instanceDirtyFlags;
         };
         Object.defineProperty(SmartPropertyPrim.prototype, "levelBoundingInfo", {
+            /**
+             * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children
+             * @returns {}
+             */
             get: function () {
                 if (this._levelBoundingInfoDirty) {
                     this.updateLevelBoundingInfo();
@@ -291,8 +365,14 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * This method must be overridden by a given Primitive implementation to compute its boundingInfo
+         */
         SmartPropertyPrim.prototype.updateLevelBoundingInfo = function () {
         };
+        /**
+         * Property method called when the Primitive becomes dirty
+         */
         SmartPropertyPrim.prototype.onPrimBecomesDirty = function () {
         };
         SmartPropertyPrim._hookProperty = function (propId, piStore, typeLevelCompare, dirtyBoundingInfo, kind) {
@@ -316,23 +396,6 @@ var BABYLON;
                     var prim = this;
                     // Change the value
                     setter.call(this, val);
-                    // If the property change also dirty the boundingInfo, update the boundingInfo dirty flags
-                    if (propInfo.dirtyBoundingInfo) {
-                        prim._levelBoundingInfoDirty = true;
-                        // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
-                        if (prim instanceof BABYLON.Prim2DBase) {
-                            var curprim = prim.parent;
-                            while (curprim) {
-                                curprim._boundingInfoDirty = true;
-                                if (curprim instanceof BABYLON.Group2D) {
-                                    if (curprim.isRenderableGroup) {
-                                        break;
-                                    }
-                                }
-                                curprim = curprim.parent;
-                            }
-                        }
-                    }
                     // Notify change, dirty flags update
                     prim._handlePropChanged(curVal, val, propName, propInfo, typeLevelCompare);
                 };

+ 2 - 2
src/Canvas2d/babylon.smartPropertyPrim.ts

@@ -91,7 +91,7 @@
                     }
                 }
                 let node = new ClassTreeInfo<TClass, TProp>(this, type, this._classContentFactory);
-                let info = { type: type, node: node};
+                let info = { type: type, node: node };
                 this._subClasses.push(info);
                 return info.node;
             }
@@ -126,7 +126,7 @@
         private _type: Object;
         private _classContent: TClass;
         private _baseClass: ClassTreeInfo<TClass, TProp>;
-        private _subClasses: Array<{type: Object, node: ClassTreeInfo<TClass, TProp>}>;
+        private _subClasses: Array<{ type: Object, node: ClassTreeInfo<TClass, TProp> }>;
         private _levelContent: StringDictionary<TProp>;
         private _fullContent: StringDictionary<TProp>;
         private _classContentFactory: (base: TClass) => TClass;

+ 11 - 0
src/Canvas2d/babylon.sprite2d.js

@@ -194,6 +194,17 @@ var BABYLON;
         Sprite2D.prototype.updateLevelBoundingInfo = function () {
             BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.spriteSize, this._levelBoundingInfo, this.origin);
         };
+        Sprite2D.prototype.getAnimatables = function () {
+            var res = new Array();
+            if (this.texture && this.texture.animations && this.texture.animations.length > 0) {
+                res.push(this.texture);
+            }
+            return res;
+        };
+        Sprite2D.prototype.levelIntersect = function (intersectInfo) {
+            // If we've made it so far it means the boundingInfo intersection test succeed, the Sprite2D is shaped the same, so we always return true
+            return true;
+        };
         Sprite2D.prototype.setupSprite2D = function (owner, parent, id, position, texture, spriteSize, spriteLocation, invertY) {
             this.setupRenderablePrim2D(owner, parent, id, position, true);
             this.texture = texture;

+ 6 - 6
src/Canvas2d/babylon.text2d.js

@@ -31,7 +31,7 @@ var BABYLON;
             this.effect.setTexture("diffuseSampler", this.fontTexture);
             engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
             var cur = engine.getAlphaMode();
-            engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+            engine.setAlphaMode(BABYLON.Engine.ALPHA_ADD);
             var count = instanceInfo._instancesPartsData[0].usedElementCount;
             if (instanceInfo._owner.owner.supportInstancedArray) {
                 engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], null, this.instancingAttributes);
@@ -238,7 +238,6 @@ var BABYLON;
             this.hAlign = hAlign;
             this._tabulationSize = tabulationSize;
             this._isTransparent = true;
-            this.origin = BABYLON.Vector2.Zero();
         };
         Text2D.Create = function (parent, id, x, y, fontName, text, defaultFontColor, areaSize, vAlign, hAlign, tabulationSize) {
             if (vAlign === void 0) { vAlign = Text2D.TEXT2D_VALIGN_TOP; }
@@ -249,6 +248,10 @@ var BABYLON;
             text2d.setupText2D(parent.owner, parent, id, new BABYLON.Vector2(x, y), fontName, text, areaSize, defaultFontColor || new BABYLON.Color4(0, 0, 0, 1), vAlign, hAlign, tabulationSize);
             return text2d;
         };
+        Text2D.prototype.levelIntersect = function (intersectInfo) {
+            // For now I can't do something better that boundingInfo is a hit, detecting an intersection on a particular letter would be possible, but do we really need it? Not for now...
+            return true;
+        };
         Text2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
             var renderCache = new Text2DRenderCache(this.owner.engine, modelKey, isTransparent);
             return renderCache;
@@ -272,9 +275,7 @@ var BABYLON;
             renderCache.ib = engine.createIndexBuffer(ib);
             // Effects
             var ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"]);
-            renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null, function (e) {
-                //                renderCache.setupUniformsLocation(e, ei.uniforms, Text2D.TEXT2D_MAINPARTID);
-            });
+            renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null);
             return renderCache;
         };
         Text2D.prototype.createInstanceDataParts = function () {
@@ -309,7 +310,6 @@ var BABYLON;
                 var charxpos = 0;
                 d.dataElementCount = this._charCount;
                 d.curElement = 0;
-                var customOrigin = BABYLON.Vector2.Zero();
                 for (var _i = 0, _a = this.text; _i < _a.length; _i++) {
                     var char = _a[_i];
                     // Line feed

+ 2 - 2
src/Materials/Textures/babylon.fontTexture.js

@@ -95,7 +95,7 @@ var BABYLON;
                 ++ft._usedCounter;
                 return ft;
             }
-            ft = new FontTexture(null, lfn, scene);
+            ft = new FontTexture(null, lfn, scene, 200, BABYLON.Texture.NEAREST_SAMPLINGMODE);
             dic.add(lfn, ft);
             return ft;
         };
@@ -144,7 +144,7 @@ var BABYLON;
             this._context.fillText(char, this._currentFreePosition.x - 0.5, this._currentFreePosition.y - this._offset - 0.5);
             // Fill the CharInfo object
             info.topLeftUV = new BABYLON.Vector2(this._currentFreePosition.x / textureSize.width, this._currentFreePosition.y / textureSize.height);
-            info.bottomRightUV = new BABYLON.Vector2(info.topLeftUV.x + (width / textureSize.width), info.topLeftUV.y + ((this._lineHeight + 1) / textureSize.height));
+            info.bottomRightUV = new BABYLON.Vector2(info.topLeftUV.x + (width / textureSize.width), info.topLeftUV.y + ((this._lineHeight + 2) / textureSize.height));
             info.charWidth = width;
             // Add the info structure
             this._charInfos[char] = info;

+ 14 - 0
src/Materials/Textures/babylon.renderTargetTexture.js

@@ -13,6 +13,9 @@ var BABYLON;
             if (isCube === void 0) { isCube = false; }
             _super.call(this, null, scene, !generateMipMaps);
             this.isCube = isCube;
+            /**
+            * Use this list to define the list of mesh you want to render.
+            */
             this.renderList = new Array();
             this.renderParticles = true;
             this.renderSprites = false;
@@ -192,6 +195,17 @@ var BABYLON;
                 }
                 delete this._waitingRenderList;
             }
+            // Is predicate defined?
+            if (this.renderListPredicate) {
+                this.renderList.splice(0); // Clear previous renderList
+                var sceneMeshes = this.getScene().meshes;
+                for (var index = 0; index < sceneMeshes.length; index++) {
+                    var mesh = sceneMeshes[index];
+                    if (this.renderListPredicate(mesh)) {
+                        this.renderList.push(mesh);
+                    }
+                }
+            }
             if (this.renderList && this.renderList.length === 0) {
                 return;
             }

+ 23 - 0
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -16,6 +16,15 @@
             return RenderTargetTexture._REFRESHRATE_RENDER_ONEVERYTWOFRAMES;
         }
 
+        /**
+        * Use this predicate to dynamically define the list of mesh you want to render.
+        * If set, the renderList property will be overwritten.
+        */
+        public renderListPredicate: (AbstractMesh) => boolean;
+
+        /**
+        * Use this list to define the list of mesh you want to render.
+        */
         public renderList = new Array<AbstractMesh>();
         public renderParticles = true;
         public renderSprites = false;
@@ -195,6 +204,20 @@
                 delete this._waitingRenderList;
             }
 
+            // Is predicate defined?
+            if (this.renderListPredicate) {
+                this.renderList.splice(0); // Clear previous renderList
+
+                var sceneMeshes = this.getScene().meshes;
+
+                for (var index = 0; index < sceneMeshes.length; index++) {
+                    var mesh = sceneMeshes[index];
+                    if (this.renderListPredicate(mesh)) {
+                        this.renderList.push(mesh);
+                    }
+                }
+            }
+
             if (this.renderList && this.renderList.length === 0) {
                 return;
             }

+ 17 - 5
src/Materials/babylon.pbrMaterial.js

@@ -75,6 +75,8 @@ var BABYLON;
             this.USEPMREMREFLECTION = false;
             this.USEPMREMREFRACTION = false;
             this.OPENGLNORMALMAP = false;
+            this.INVERTNORMALMAPX = false;
+            this.INVERTNORMALMAPY = false;
             this.rebuild();
         }
         return PBRMaterialDefines;
@@ -303,9 +305,13 @@ var BABYLON;
              */
             this.maxSimultaneousLights = 4;
             /**
-             * If sets to true, normal map will be considered following OpenGL convention.
+             * If sets to true, x component of normal map value will invert (x = 1.0 - x).
              */
-            this.useOpenGLNormalMap = false;
+            this.invertNormalMapX = false;
+            /**
+             * If sets to true, y component of normal map value will invert (y = 1.0 - y).
+             */
+            this.invertNormalMapY = false;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
             this._globalAmbientColor = new BABYLON.Color3(0, 0, 0);
@@ -551,8 +557,11 @@ var BABYLON;
                                 this._defines.PARALLAXOCCLUSION = true;
                             }
                         }
-                        if (this.useOpenGLNormalMap) {
-                            this._defines.OPENGLNORMALMAP = true;
+                        if (this.invertNormalMapX) {
+                            this._defines.INVERTNORMALMAPX = true;
+                        }
+                        if (this.invertNormalMapY) {
+                            this._defines.INVERTNORMALMAPY = true;
                         }
                     }
                 }
@@ -1242,7 +1251,10 @@ var BABYLON;
         ], PBRMaterial.prototype, "maxSimultaneousLights", void 0);
         __decorate([
             BABYLON.serialize()
-        ], PBRMaterial.prototype, "useOpenGLNormalMap", void 0);
+        ], PBRMaterial.prototype, "invertNormalMapX", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], PBRMaterial.prototype, "invertNormalMapY", void 0);
         __decorate([
             BABYLON.serialize()
         ], PBRMaterial.prototype, "useLogarithmicDepth", null);

文件差异内容过多而无法显示
+ 1425 - 1413
src/Materials/babylon.pbrMaterial.ts


+ 17 - 6
src/Materials/babylon.standardMaterial.js

@@ -67,7 +67,8 @@ var BABYLON;
             this.REFRACTION = false;
             this.REFRACTIONMAP_3D = false;
             this.REFLECTIONOVERALPHA = false;
-            this.OPENGLNORMALMAP = false;
+            this.INVERTNORMALMAPX = false;
+            this.INVERTNORMALMAPY = false;
             this.rebuild();
         }
         return StandardMaterialDefines;
@@ -99,9 +100,13 @@ var BABYLON;
             this.useGlossinessFromSpecularMapAlpha = false;
             this.maxSimultaneousLights = 4;
             /**
-             * If sets to true, normal map will be considered following OpenGL convention.
+             * If sets to true, x component of normal map value will invert (x = 1.0 - x).
              */
-            this.useOpenGLNormalMap = false;
+            this.invertNormalMapX = false;
+            /**
+             * If sets to true, y component of normal map value will invert (y = 1.0 - y).
+             */
+            this.invertNormalMapY = false;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
             this._globalAmbientColor = new BABYLON.Color3(0, 0, 0);
@@ -292,8 +297,11 @@ var BABYLON;
                                 this._defines.PARALLAXOCCLUSION = true;
                             }
                         }
-                        if (this.useOpenGLNormalMap) {
-                            this._defines.OPENGLNORMALMAP = true;
+                        if (this.invertNormalMapX) {
+                            this._defines.INVERTNORMALMAPX = true;
+                        }
+                        if (this.invertNormalMapY) {
+                            this._defines.INVERTNORMALMAPY = true;
                         }
                     }
                 }
@@ -827,7 +835,10 @@ var BABYLON;
         ], StandardMaterial.prototype, "maxSimultaneousLights", void 0);
         __decorate([
             BABYLON.serialize()
-        ], StandardMaterial.prototype, "useOpenGLNormalMap", void 0);
+        ], StandardMaterial.prototype, "invertNormalMapX", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], StandardMaterial.prototype, "invertNormalMapY", void 0);
         __decorate([
             BABYLON.serialize()
         ], StandardMaterial.prototype, "useLogarithmicDepth", null);

+ 18 - 7
src/Materials/babylon.standardMaterial.ts

@@ -52,7 +52,8 @@
         public REFRACTION = false;
         public REFRACTIONMAP_3D = false;
         public REFLECTIONOVERALPHA = false;
-        public OPENGLNORMALMAP = false;
+        public INVERTNORMALMAPX = false;
+        public INVERTNORMALMAPY = false;
 
         constructor() {
             super();
@@ -166,11 +167,17 @@
         @serialize()
         public maxSimultaneousLights = 4;
 
-        /**
-         * If sets to true, normal map will be considered following OpenGL convention.
-         */
+        /**
+         * If sets to true, x component of normal map value will invert (x = 1.0 - x).
+         */
         @serialize()
-        public useOpenGLNormalMap = false;
+        public invertNormalMapX = false;
+
+        /**
+         * If sets to true, y component of normal map value will invert (y = 1.0 - y).
+         */
+        @serialize()
+        public invertNormalMapY = false;
 
         private _renderTargets = new SmartArray<RenderTargetTexture>(16);
         private _worldViewProjectionMatrix = Matrix.Zero();
@@ -394,8 +401,12 @@
                             }
                         }
 
-                        if (this.useOpenGLNormalMap) {
-                            this._defines.OPENGLNORMALMAP = true;
+                        if (this.invertNormalMapX) {
+                            this._defines.INVERTNORMALMAPX = true;
+                        }
+
+                        if (this.invertNormalMapY) {
+                            this._defines.INVERTNORMALMAPY = true;
                         }
                     }
                 }

+ 13 - 0
src/Math/babylon.math.js

@@ -1287,6 +1287,19 @@ var BABYLON;
         Size.Zero = function () {
             return new Size(0, 0);
         };
+        Size.prototype.add = function (otherSize) {
+            var r = new Size(this.width + otherSize.width, this.height + otherSize.height);
+            return r;
+        };
+        Size.prototype.substract = function (otherSize) {
+            var r = new Size(this.width - otherSize.width, this.height - otherSize.height);
+            return r;
+        };
+        Size.Lerp = function (start, end, amount) {
+            var w = start.width + ((end.width - start.width) * amount);
+            var h = start.height + ((end.height - start.height) * amount);
+            return new Size(w, h);
+        };
         return Size;
     })();
     BABYLON.Size = Size;

+ 1 - 0
src/Particles/babylon.solidParticle.js

@@ -9,6 +9,7 @@ var BABYLON;
             this.uvs = new BABYLON.Vector4(0, 0, 1, 1); // uvs
             this.velocity = BABYLON.Vector3.Zero(); // velocity
             this.alive = true; // alive
+            this.isVisible = true; // visibility
             this.idx = particleIndex;
             this._pos = positionIndex;
             this._model = model;

+ 1 - 0
src/Particles/babylon.solidParticle.ts

@@ -10,6 +10,7 @@ module BABYLON {
         public uvs = new Vector4(0, 0, 1, 1);   // uvs
         public velocity = Vector3.Zero();       // velocity
         public alive = true;                    // alive
+        public isVisible = true;                // visibility
         public _pos: number;                    // index of this particle in the global "positions" array
         public _model: ModelShape;              // model shape reference
         public shapeId: number;                 // model shape id

+ 102 - 73
src/Particles/babylon.solidParticleSystem.js

@@ -460,6 +460,7 @@ var BABYLON;
             var colorIndex = 0;
             var uvidx = 0;
             var uvIndex = 0;
+            var pt = 0;
             if (this._computeBoundingBox) {
                 BABYLON.Vector3.FromFloatsToRef(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, this._minimum);
                 BABYLON.Vector3.FromFloatsToRef(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE, this._maximum);
@@ -472,92 +473,120 @@ var BABYLON;
                 this._shapeUV = this._particle._model._shapeUV;
                 // call to custom user function to update the particle properties
                 this.updateParticle(this._particle);
-                // particle rotation matrix
-                if (this.billboard) {
-                    this._particle.rotation.x = 0.0;
-                    this._particle.rotation.y = 0.0;
-                }
-                if (this._computeParticleRotation) {
-                    if (this._particle.rotationQuaternion) {
-                        this._quaternion.copyFrom(this._particle.rotationQuaternion);
-                    }
-                    else {
-                        this._yaw = this._particle.rotation.y;
-                        this._pitch = this._particle.rotation.x;
-                        this._roll = this._particle.rotation.z;
-                        this._quaternionRotationYPR();
-                    }
-                    this._quaternionToRotationMatrix();
-                }
-                for (var pt = 0; pt < this._shape.length; pt++) {
-                    idx = index + pt * 3;
-                    colidx = colorIndex + pt * 4;
-                    uvidx = uvIndex + pt * 2;
-                    this._vertex.x = this._shape[pt].x;
-                    this._vertex.y = this._shape[pt].y;
-                    this._vertex.z = this._shape[pt].z;
-                    if (this._computeParticleVertex) {
-                        this.updateParticleVertex(this._particle, this._vertex, pt);
+                if (this._particle.isVisible) {
+                    // particle rotation matrix
+                    if (this.billboard) {
+                        this._particle.rotation.x = 0.0;
+                        this._particle.rotation.y = 0.0;
                     }
-                    // positions
-                    this._vertex.x *= this._particle.scaling.x;
-                    this._vertex.y *= this._particle.scaling.y;
-                    this._vertex.z *= this._particle.scaling.z;
-                    this._w = (this._vertex.x * this._rotMatrix.m[3]) + (this._vertex.y * this._rotMatrix.m[7]) + (this._vertex.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
-                    this._rotated.x = ((this._vertex.x * this._rotMatrix.m[0]) + (this._vertex.y * this._rotMatrix.m[4]) + (this._vertex.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
-                    this._rotated.y = ((this._vertex.x * this._rotMatrix.m[1]) + (this._vertex.y * this._rotMatrix.m[5]) + (this._vertex.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
-                    this._rotated.z = ((this._vertex.x * this._rotMatrix.m[2]) + (this._vertex.y * this._rotMatrix.m[6]) + (this._vertex.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
-                    this._positions32[idx] = this._particle.position.x + this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
-                    this._positions32[idx + 1] = this._particle.position.y + this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
-                    this._positions32[idx + 2] = this._particle.position.z + this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
-                    if (this._computeBoundingBox) {
-                        if (this._positions32[idx] < this._minimum.x) {
-                            this._minimum.x = this._positions32[idx];
+                    if (this._computeParticleRotation) {
+                        if (this._particle.rotationQuaternion) {
+                            this._quaternion.copyFrom(this._particle.rotationQuaternion);
                         }
-                        if (this._positions32[idx] > this._maximum.x) {
-                            this._maximum.x = this._positions32[idx];
+                        else {
+                            this._yaw = this._particle.rotation.y;
+                            this._pitch = this._particle.rotation.x;
+                            this._roll = this._particle.rotation.z;
+                            this._quaternionRotationYPR();
                         }
-                        if (this._positions32[idx + 1] < this._minimum.y) {
-                            this._minimum.y = this._positions32[idx + 1];
+                        this._quaternionToRotationMatrix();
+                    }
+                    // particle vertex loop
+                    for (pt = 0; pt < this._shape.length; pt++) {
+                        idx = index + pt * 3;
+                        colidx = colorIndex + pt * 4;
+                        uvidx = uvIndex + pt * 2;
+                        this._vertex.x = this._shape[pt].x;
+                        this._vertex.y = this._shape[pt].y;
+                        this._vertex.z = this._shape[pt].z;
+                        if (this._computeParticleVertex) {
+                            this.updateParticleVertex(this._particle, this._vertex, pt);
                         }
-                        if (this._positions32[idx + 1] > this._maximum.y) {
-                            this._maximum.y = this._positions32[idx + 1];
+                        // positions
+                        this._vertex.x *= this._particle.scaling.x;
+                        this._vertex.y *= this._particle.scaling.y;
+                        this._vertex.z *= this._particle.scaling.z;
+                        this._w = (this._vertex.x * this._rotMatrix.m[3]) + (this._vertex.y * this._rotMatrix.m[7]) + (this._vertex.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
+                        this._rotated.x = ((this._vertex.x * this._rotMatrix.m[0]) + (this._vertex.y * this._rotMatrix.m[4]) + (this._vertex.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
+                        this._rotated.y = ((this._vertex.x * this._rotMatrix.m[1]) + (this._vertex.y * this._rotMatrix.m[5]) + (this._vertex.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
+                        this._rotated.z = ((this._vertex.x * this._rotMatrix.m[2]) + (this._vertex.y * this._rotMatrix.m[6]) + (this._vertex.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
+                        this._positions32[idx] = this._particle.position.x + this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
+                        this._positions32[idx + 1] = this._particle.position.y + this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
+                        this._positions32[idx + 2] = this._particle.position.z + this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
+                        if (this._computeBoundingBox) {
+                            if (this._positions32[idx] < this._minimum.x) {
+                                this._minimum.x = this._positions32[idx];
+                            }
+                            if (this._positions32[idx] > this._maximum.x) {
+                                this._maximum.x = this._positions32[idx];
+                            }
+                            if (this._positions32[idx + 1] < this._minimum.y) {
+                                this._minimum.y = this._positions32[idx + 1];
+                            }
+                            if (this._positions32[idx + 1] > this._maximum.y) {
+                                this._maximum.y = this._positions32[idx + 1];
+                            }
+                            if (this._positions32[idx + 2] < this._minimum.z) {
+                                this._minimum.z = this._positions32[idx + 2];
+                            }
+                            if (this._positions32[idx + 2] > this._maximum.z) {
+                                this._maximum.z = this._positions32[idx + 2];
+                            }
                         }
-                        if (this._positions32[idx + 2] < this._minimum.z) {
-                            this._minimum.z = this._positions32[idx + 2];
+                        // normals : if the particles can't be morphed then just rotate the normals, what if much more faster than ComputeNormals()
+                        if (!this._computeParticleVertex && !this.billboard) {
+                            this._normal.x = this._fixedNormal32[idx];
+                            this._normal.y = this._fixedNormal32[idx + 1];
+                            this._normal.z = this._fixedNormal32[idx + 2];
+                            this._w = (this._normal.x * this._rotMatrix.m[3]) + (this._normal.y * this._rotMatrix.m[7]) + (this._normal.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
+                            this._rotated.x = ((this._normal.x * this._rotMatrix.m[0]) + (this._normal.y * this._rotMatrix.m[4]) + (this._normal.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
+                            this._rotated.y = ((this._normal.x * this._rotMatrix.m[1]) + (this._normal.y * this._rotMatrix.m[5]) + (this._normal.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
+                            this._rotated.z = ((this._normal.x * this._rotMatrix.m[2]) + (this._normal.y * this._rotMatrix.m[6]) + (this._normal.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
+                            this._normals32[idx] = this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
+                            this._normals32[idx + 1] = this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
+                            this._normals32[idx + 2] = this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
                         }
-                        if (this._positions32[idx + 2] > this._maximum.z) {
-                            this._maximum.z = this._positions32[idx + 2];
+                        if (this._computeParticleColor) {
+                            this._colors32[colidx] = this._particle.color.r;
+                            this._colors32[colidx + 1] = this._particle.color.g;
+                            this._colors32[colidx + 2] = this._particle.color.b;
+                            this._colors32[colidx + 3] = this._particle.color.a;
+                        }
+                        if (this._computeParticleTexture) {
+                            this._uvs32[uvidx] = this._shapeUV[pt * 2] * (this._particle.uvs.z - this._particle.uvs.x) + this._particle.uvs.x;
+                            this._uvs32[uvidx + 1] = this._shapeUV[pt * 2 + 1] * (this._particle.uvs.w - this._particle.uvs.y) + this._particle.uvs.y;
                         }
                     }
-                    // normals : if the particles can't be morphed then just rotate the normals
-                    if (!this._computeParticleVertex && !this.billboard) {
-                        this._normal.x = this._fixedNormal32[idx];
-                        this._normal.y = this._fixedNormal32[idx + 1];
-                        this._normal.z = this._fixedNormal32[idx + 2];
-                        this._w = (this._normal.x * this._rotMatrix.m[3]) + (this._normal.y * this._rotMatrix.m[7]) + (this._normal.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
-                        this._rotated.x = ((this._normal.x * this._rotMatrix.m[0]) + (this._normal.y * this._rotMatrix.m[4]) + (this._normal.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
-                        this._rotated.y = ((this._normal.x * this._rotMatrix.m[1]) + (this._normal.y * this._rotMatrix.m[5]) + (this._normal.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
-                        this._rotated.z = ((this._normal.x * this._rotMatrix.m[2]) + (this._normal.y * this._rotMatrix.m[6]) + (this._normal.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
-                        this._normals32[idx] = this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
-                        this._normals32[idx + 1] = this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
-                        this._normals32[idx + 2] = this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
-                    }
-                    if (this._computeParticleColor) {
-                        this._colors32[colidx] = this._particle.color.r;
-                        this._colors32[colidx + 1] = this._particle.color.g;
-                        this._colors32[colidx + 2] = this._particle.color.b;
-                        this._colors32[colidx + 3] = this._particle.color.a;
-                    }
-                    if (this._computeParticleTexture) {
-                        this._uvs32[uvidx] = this._shapeUV[pt * 2] * (this._particle.uvs.z - this._particle.uvs.x) + this._particle.uvs.x;
-                        this._uvs32[uvidx + 1] = this._shapeUV[pt * 2 + 1] * (this._particle.uvs.w - this._particle.uvs.y) + this._particle.uvs.y;
+                }
+                else {
+                    for (pt = 0; pt < this._shape.length; pt++) {
+                        idx = index + pt * 3;
+                        colidx = colorIndex + pt * 4;
+                        uvidx = uvIndex + pt * 2;
+                        this._positions32[idx] = this._camera.position.x;
+                        this._positions32[idx + 1] = this._camera.position.y;
+                        this._positions32[idx + 2] = this._camera.position.z;
+                        this._normals32[idx] = 0.0;
+                        this._normals32[idx + 1] = 0.0;
+                        this._normals32[idx + 2] = 0.0;
+                        if (this._computeParticleColor) {
+                            this._colors32[colidx] = this._particle.color.r;
+                            this._colors32[colidx + 1] = this._particle.color.g;
+                            this._colors32[colidx + 2] = this._particle.color.b;
+                            this._colors32[colidx + 3] = this._particle.color.a;
+                        }
+                        if (this._computeParticleTexture) {
+                            this._uvs32[uvidx] = this._shapeUV[pt * 2] * (this._particle.uvs.z - this._particle.uvs.x) + this._particle.uvs.x;
+                            this._uvs32[uvidx + 1] = this._shapeUV[pt * 2 + 1] * (this._particle.uvs.w - this._particle.uvs.y) + this._particle.uvs.y;
+                        }
                     }
                 }
+                // increment indexes for the next particle
                 index = idx + 3;
                 colorIndex = colidx + 4;
                 uvIndex = uvidx + 2;
             }
+            // if the VBO must be updated
             if (update) {
                 if (this._computeParticleColor) {
                     this.mesh.updateVerticesData(BABYLON.VertexBuffer.ColorKind, this._colors32, false, false);
@@ -568,7 +597,7 @@ var BABYLON;
                 this.mesh.updateVerticesData(BABYLON.VertexBuffer.PositionKind, this._positions32, false, false);
                 if (!this.mesh.areNormalsFrozen) {
                     if (this._computeParticleVertex || this.billboard) {
-                        // recompute the normals only if the particles can be morphed, update then the normal reference array
+                        // recompute the normals only if the particles can be morphed, update then also the normal reference array _fixedNormal32[]
                         BABYLON.VertexData.ComputeNormals(this._positions32, this._indices, this._normals32);
                         for (var i = 0; i < this._normals32.length; i++) {
                             this._fixedNormal32[i] = this._normals32[i];

+ 112 - 79
src/Particles/babylon.solidParticleSystem.ts

@@ -532,6 +532,7 @@
             var colorIndex = 0;
             var uvidx = 0;
             var uvIndex = 0;
+            var pt = 0;
 
             if (this._computeBoundingBox) {
                 Vector3.FromFloatsToRef(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, this._minimum);
@@ -547,105 +548,137 @@
 
                 // call to custom user function to update the particle properties
                 this.updateParticle(this._particle);
+                
+                if (this._particle.isVisible) {
 
-                // particle rotation matrix
-                if (this.billboard) {
-                    this._particle.rotation.x = 0.0;
-                    this._particle.rotation.y = 0.0;
-                }
-                if (this._computeParticleRotation) {
-                    if (this._particle.rotationQuaternion) {
-                        this._quaternion.copyFrom(this._particle.rotationQuaternion);
-                    } else {
-                        this._yaw = this._particle.rotation.y;
-                        this._pitch = this._particle.rotation.x;
-                        this._roll = this._particle.rotation.z;
-                        this._quaternionRotationYPR();
+                    // particle rotation matrix
+                    if (this.billboard) {
+                        this._particle.rotation.x = 0.0;
+                        this._particle.rotation.y = 0.0;
                     }
-                    this._quaternionToRotationMatrix();
-                }
-
-                for (var pt = 0; pt < this._shape.length; pt++) {
-                    idx = index + pt * 3;
-                    colidx = colorIndex + pt * 4;
-                    uvidx = uvIndex + pt * 2;
-
-                    this._vertex.x = this._shape[pt].x;
-                    this._vertex.y = this._shape[pt].y;
-                    this._vertex.z = this._shape[pt].z;
-
-                    if (this._computeParticleVertex) {
-                        this.updateParticleVertex(this._particle, this._vertex, pt);
+                    if (this._computeParticleRotation) {
+                        if (this._particle.rotationQuaternion) {
+                            this._quaternion.copyFrom(this._particle.rotationQuaternion);
+                        } else {
+                            this._yaw = this._particle.rotation.y;
+                            this._pitch = this._particle.rotation.x;
+                            this._roll = this._particle.rotation.z;
+                            this._quaternionRotationYPR();
+                        }
+                        this._quaternionToRotationMatrix();
                     }
 
-                    // positions
-                    this._vertex.x *= this._particle.scaling.x;
-                    this._vertex.y *= this._particle.scaling.y;
-                    this._vertex.z *= this._particle.scaling.z;
-
-                    this._w = (this._vertex.x * this._rotMatrix.m[3]) + (this._vertex.y * this._rotMatrix.m[7]) + (this._vertex.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
-                    this._rotated.x = ((this._vertex.x * this._rotMatrix.m[0]) + (this._vertex.y * this._rotMatrix.m[4]) + (this._vertex.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
-                    this._rotated.y = ((this._vertex.x * this._rotMatrix.m[1]) + (this._vertex.y * this._rotMatrix.m[5]) + (this._vertex.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
-                    this._rotated.z = ((this._vertex.x * this._rotMatrix.m[2]) + (this._vertex.y * this._rotMatrix.m[6]) + (this._vertex.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
+                    // particle vertex loop
+                    for (pt = 0; pt < this._shape.length; pt++) {
+                        idx = index + pt * 3;
+                        colidx = colorIndex + pt * 4;
+                        uvidx = uvIndex + pt * 2;
 
-                    this._positions32[idx] = this._particle.position.x + this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
-                    this._positions32[idx + 1] = this._particle.position.y + this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
-                    this._positions32[idx + 2] = this._particle.position.z + this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
+                        this._vertex.x = this._shape[pt].x;
+                        this._vertex.y = this._shape[pt].y;
+                        this._vertex.z = this._shape[pt].z;
 
-                    if (this._computeBoundingBox) {
-                        if (this._positions32[idx] < this._minimum.x) {
-                            this._minimum.x = this._positions32[idx];
-                        }
-                        if (this._positions32[idx] > this._maximum.x) {
-                            this._maximum.x = this._positions32[idx];
-                        }
-                        if (this._positions32[idx + 1] < this._minimum.y) {
-                            this._minimum.y = this._positions32[idx + 1];
-                        }
-                        if (this._positions32[idx + 1] > this._maximum.y) {
-                            this._maximum.y = this._positions32[idx + 1];
+                        if (this._computeParticleVertex) {
+                            this.updateParticleVertex(this._particle, this._vertex, pt);
                         }
-                        if (this._positions32[idx + 2] < this._minimum.z) {
-                            this._minimum.z = this._positions32[idx + 2];
-                        }
-                        if (this._positions32[idx + 2] > this._maximum.z) {
-                            this._maximum.z = this._positions32[idx + 2];
+
+                        // positions
+                        this._vertex.x *= this._particle.scaling.x;
+                        this._vertex.y *= this._particle.scaling.y;
+                        this._vertex.z *= this._particle.scaling.z;
+
+                        this._w = (this._vertex.x * this._rotMatrix.m[3]) + (this._vertex.y * this._rotMatrix.m[7]) + (this._vertex.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
+                        this._rotated.x = ((this._vertex.x * this._rotMatrix.m[0]) + (this._vertex.y * this._rotMatrix.m[4]) + (this._vertex.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
+                        this._rotated.y = ((this._vertex.x * this._rotMatrix.m[1]) + (this._vertex.y * this._rotMatrix.m[5]) + (this._vertex.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
+                        this._rotated.z = ((this._vertex.x * this._rotMatrix.m[2]) + (this._vertex.y * this._rotMatrix.m[6]) + (this._vertex.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
+
+                        this._positions32[idx] = this._particle.position.x + this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
+                        this._positions32[idx + 1] = this._particle.position.y + this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
+                        this._positions32[idx + 2] = this._particle.position.z + this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
+
+                        if (this._computeBoundingBox) {
+                            if (this._positions32[idx] < this._minimum.x) {
+                                this._minimum.x = this._positions32[idx];
+                            }
+                            if (this._positions32[idx] > this._maximum.x) {
+                                this._maximum.x = this._positions32[idx];
+                            }
+                            if (this._positions32[idx + 1] < this._minimum.y) {
+                                this._minimum.y = this._positions32[idx + 1];
+                            }
+                            if (this._positions32[idx + 1] > this._maximum.y) {
+                                this._maximum.y = this._positions32[idx + 1];
+                            }
+                            if (this._positions32[idx + 2] < this._minimum.z) {
+                                this._minimum.z = this._positions32[idx + 2];
+                            }
+                            if (this._positions32[idx + 2] > this._maximum.z) {
+                                this._maximum.z = this._positions32[idx + 2];
+                            }
                         }
-                    }
 
-                    // normals : if the particles can't be morphed then just rotate the normals
-                    if (!this._computeParticleVertex && !this.billboard) {
-                        this._normal.x = this._fixedNormal32[idx];
-                        this._normal.y = this._fixedNormal32[idx + 1];
-                        this._normal.z = this._fixedNormal32[idx + 2];
+                        // normals : if the particles can't be morphed then just rotate the normals, what if much more faster than ComputeNormals()
+                        if (!this._computeParticleVertex && !this.billboard) {
+                            this._normal.x = this._fixedNormal32[idx];
+                            this._normal.y = this._fixedNormal32[idx + 1];
+                            this._normal.z = this._fixedNormal32[idx + 2];
 
-                        this._w = (this._normal.x * this._rotMatrix.m[3]) + (this._normal.y * this._rotMatrix.m[7]) + (this._normal.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
-                        this._rotated.x = ((this._normal.x * this._rotMatrix.m[0]) + (this._normal.y * this._rotMatrix.m[4]) + (this._normal.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
-                        this._rotated.y = ((this._normal.x * this._rotMatrix.m[1]) + (this._normal.y * this._rotMatrix.m[5]) + (this._normal.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
-                        this._rotated.z = ((this._normal.x * this._rotMatrix.m[2]) + (this._normal.y * this._rotMatrix.m[6]) + (this._normal.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
+                            this._w = (this._normal.x * this._rotMatrix.m[3]) + (this._normal.y * this._rotMatrix.m[7]) + (this._normal.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
+                            this._rotated.x = ((this._normal.x * this._rotMatrix.m[0]) + (this._normal.y * this._rotMatrix.m[4]) + (this._normal.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
+                            this._rotated.y = ((this._normal.x * this._rotMatrix.m[1]) + (this._normal.y * this._rotMatrix.m[5]) + (this._normal.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
+                            this._rotated.z = ((this._normal.x * this._rotMatrix.m[2]) + (this._normal.y * this._rotMatrix.m[6]) + (this._normal.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
 
-                        this._normals32[idx] = this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
-                        this._normals32[idx + 1] = this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
-                        this._normals32[idx + 2] = this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
-                    }
+                            this._normals32[idx] = this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
+                            this._normals32[idx + 1] = this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
+                            this._normals32[idx + 2] = this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
+                        }
 
-                    if (this._computeParticleColor) {
-                        this._colors32[colidx] = this._particle.color.r;
-                        this._colors32[colidx + 1] = this._particle.color.g;
-                        this._colors32[colidx + 2] = this._particle.color.b;
-                        this._colors32[colidx + 3] = this._particle.color.a;
+                        if (this._computeParticleColor) {
+                            this._colors32[colidx] = this._particle.color.r;
+                            this._colors32[colidx + 1] = this._particle.color.g;
+                            this._colors32[colidx + 2] = this._particle.color.b;
+                            this._colors32[colidx + 3] = this._particle.color.a;
+                        }
+
+                        if (this._computeParticleTexture) {
+                            this._uvs32[uvidx] = this._shapeUV[pt * 2] * (this._particle.uvs.z - this._particle.uvs.x) + this._particle.uvs.x;
+                            this._uvs32[uvidx + 1] = this._shapeUV[pt * 2 + 1] * (this._particle.uvs.w - this._particle.uvs.y) + this._particle.uvs.y;
+                        }
                     }
 
-                    if (this._computeParticleTexture) {
-                        this._uvs32[uvidx] = this._shapeUV[pt * 2] * (this._particle.uvs.z - this._particle.uvs.x) + this._particle.uvs.x;
-                        this._uvs32[uvidx + 1] = this._shapeUV[pt * 2 + 1] * (this._particle.uvs.w - this._particle.uvs.y) + this._particle.uvs.y;
+                } 
+                // particle not visible : scaled to zero and positioned to the camera position
+                else {
+                    for (pt = 0; pt < this._shape.length; pt++) {
+                        idx = index + pt * 3;
+                        colidx = colorIndex + pt * 4;
+                        uvidx = uvIndex + pt * 2;
+                        this._positions32[idx] = this._camera.position.x;
+                        this._positions32[idx + 1] = this._camera.position.y;
+                        this._positions32[idx + 2] = this._camera.position.z;
+                        this._normals32[idx] = 0.0;
+                        this._normals32[idx + 1] = 0.0;
+                        this._normals32[idx + 2] = 0.0;
+                        if (this._computeParticleColor) {
+                            this._colors32[colidx] = this._particle.color.r;
+                            this._colors32[colidx + 1] = this._particle.color.g;
+                            this._colors32[colidx + 2] = this._particle.color.b;
+                            this._colors32[colidx + 3] = this._particle.color.a;
+                        }
+                        if (this._computeParticleTexture) {
+                            this._uvs32[uvidx] = this._shapeUV[pt * 2] * (this._particle.uvs.z - this._particle.uvs.x) + this._particle.uvs.x;
+                            this._uvs32[uvidx + 1] = this._shapeUV[pt * 2 + 1] * (this._particle.uvs.w - this._particle.uvs.y) + this._particle.uvs.y;
+                        }
                     }
                 }
+                
+                // increment indexes for the next particle
                 index = idx + 3;
                 colorIndex = colidx + 4;
                 uvIndex = uvidx + 2;
             }
 
+            // if the VBO must be updated
             if (update) {
                 if (this._computeParticleColor) {
                     this.mesh.updateVerticesData(VertexBuffer.ColorKind, this._colors32, false, false);
@@ -656,7 +689,7 @@
                 this.mesh.updateVerticesData(VertexBuffer.PositionKind, this._positions32, false, false);
                 if (!this.mesh.areNormalsFrozen) {
                     if (this._computeParticleVertex || this.billboard) {
-                        // recompute the normals only if the particles can be morphed, update then the normal reference array
+                        // recompute the normals only if the particles can be morphed, update then also the normal reference array _fixedNormal32[]
                         VertexData.ComputeNormals(this._positions32, this._indices, this._normals32);
                         for (var i = 0; i < this._normals32.length; i++) {
                             this._fixedNormal32[i] = this._normals32[i];

+ 4 - 1
src/Shaders/ShadersInclude/bumpFragmentFunctions.fx

@@ -27,7 +27,10 @@
 	{
 		vec3 map = texture2D(bumpSampler, uv).xyz;
 
-	#ifdef OPENGLNORMALMAP
+	#ifdef INVERTNORMALMAPX
+		map.x = 1.0 - map.x;
+	#endif
+	#ifdef INVERTNORMALMAPY
 		map.y = 1.0 - map.y;
 	#endif
 

+ 2 - 2
src/Shaders/rect2d.vertex.fx

@@ -102,7 +102,7 @@ void main(void) {
 		}
 #endif
 	}
-	else 
+	else
 	{
 #ifdef Border
 		float w = properties.x;
@@ -114,7 +114,7 @@ void main(void) {
 
 		float segi = index;
 		if (index < rsub) {
-			borderOffset = vec2(1.0-(borderThickness*2.0 / w), 1.0-(borderThickness*2.0 / h));
+			borderOffset = vec2(1.0 - (borderThickness*2.0 / w), 1.0 - (borderThickness*2.0 / h));
 		}
 		else {
 			segi -= rsub;