Procházet zdrojové kódy

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

Julian před 8 roky
rodič
revize
f76cfb28b8

+ 49 - 0
canvas2D/src/Engine/babylon.bounding2d.ts

@@ -155,6 +155,16 @@
         }
 
         /**
+         * return the min/max extend of the bounding info.
+         * x, y, z, w are left, bottom, right and top
+         */
+        public minMax(): Vector4 {
+            let r = Vector4.Zero();
+            this.minMaxToRef(r);
+            return r;
+        }
+
+        /**
          * Update a vector2 with the max extend of the bounding info
          * @param result must be a valid/allocated vector2 that will contain the result of the operation
          */
@@ -164,6 +174,45 @@
         }
 
         /**
+         * Update a vector4 with the min/max extend of the bounding info
+         * x, y, z, w are left, bottom, right and top
+         * @param result must be a valid/allocated vector4 that will contain the result of the operation
+         */
+        public minMaxToRef(result: Vector4) {
+            result.x = this.center.x - this.extent.x;
+            result.y = this.center.y - this.extent.y;
+            result.z = this.center.x + this.extent.x;
+            result.w = this.center.y + this.extent.y;
+        }
+
+        /**
+         * Return the size of the boundingInfo rect surface
+         */
+        public size(): Size {
+            let r = Size.Zero();
+            this.sizeToRef(r);
+            return r;
+        }
+
+        /**
+         * Stores in the result object the size of the boundingInfo rect surface
+         * @param result
+         */
+        public sizeToRef(result: Size) {
+            result.width  = this.extent.x * 2;
+            result.height = this.extent.y * 2;
+        }
+
+        /**
+         * Inflate the boundingInfo with the given vector
+         * @param offset the extent will be incremented with offset and the radius will be computed again
+         */
+        public inflate(offset: Vector2) {
+            this.extent.addInPlace(offset);
+            this.radius = this.extent.length();
+        }
+
+        /**
          * Apply a transformation matrix to this BoundingInfo2D and return a new instance containing the result
          * @param matrix the transformation matrix to apply
          * @return the new instance containing the result of the transformation applied on this BoundingInfo2D

+ 130 - 80
canvas2D/src/Engine/babylon.canvas2d.ts

@@ -575,6 +575,9 @@
             } else {
                 // The pointer is inside the Canvas, do an intersection test
                 this.intersect(ii);
+
+                // Sort primitives to get them from top to bottom
+                ii.intersectedPrimitives = ii.intersectedPrimitives.sort((a, b) => a.prim.actualZOffset - b.prim.actualZOffset);
             }
 
             {
@@ -608,16 +611,31 @@
                 // Detect if the current pointer is captured, only fire event if they belong to the capture primitive
                 let capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
 
-                // Notify the previous "over" prim that the pointer is no longer over it
-                if ((capturedPrim && capturedPrim === prevPrim) || (!capturedPrim && prevPrim && !prevPrim.isDisposed)) {
-                    this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
-                    this._bubbleNotifyPrimPointerObserver(prevPrim, PrimitivePointerInfo.PointerOut, null);
-                }
+                // See the NOTE section of: https://www.w3.org/TR/pointerevents/#setting-pointer-capture
+                if (capturedPrim) {
+                    if (capturedPrim === prevPrim) {
+                        this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
+                        this._bubbleNotifyPrimPointerObserver(prevPrim, PrimitivePointerInfo.PointerOut, null);
+                    } else if (capturedPrim === actualPrim) {
+                        this._primPointerInfo.updateRelatedTarget(actualPrim, this._actualOverPrimitive.intersectionLocation);
+                        this._bubbleNotifyPrimPointerObserver(actualPrim, PrimitivePointerInfo.PointerOver, null);
+                    }
+                } else {
+                    // Check for Out & Leave
+                    for (let prev of this._previousIntersectionList) {
+                        if (!Tools.first(this._actualIntersectionList, (pii) => pii.prim === prev.prim)) {
+                            this._primPointerInfo.updateRelatedTarget(prev.prim, prev.intersectionLocation);
+                            this._bubbleNotifyPrimPointerObserver(prev.prim, 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, PrimitivePointerInfo.PointerOver, null);
+                    // Check for Over & Enter
+                    for (let actual of this._actualIntersectionList) {
+                        if (!Tools.first(this._previousIntersectionList, (pii) => pii.prim === actual.prim)) {
+                            this._primPointerInfo.updateRelatedTarget(actual.prim, actual.intersectionLocation);
+                            this._bubbleNotifyPrimPointerObserver(actual.prim, PrimitivePointerInfo.PointerOver, null);
+                        }
+                    }
                 }
             }
 
@@ -649,7 +667,7 @@
             }
 
             let pii = this._primPointerInfo;
-            debug += `[RID:${this.scene.getRenderId()}] [${prim.hierarchyDepth}] event:${PrimitivePointerInfo.getEventTypeName(mask)}, id: ${prim.id} (${Tools.getClassName(prim)}), primPos: ${pii.primitivePointerPos.toString()}, canvasPos: ${pii.canvasPointerPos.toString()}`;
+            debug += `[RID:${this.scene.getRenderId()}] [${prim.hierarchyDepth}] event:${PrimitivePointerInfo.getEventTypeName(mask)}, id: ${prim.id} (${Tools.getClassName(prim)}), primPos: ${pii.primitivePointerPos.toString()}, canvasPos: ${pii.canvasPointerPos.toString()}, relatedTarget: ${pii.relatedTarget.id}`;
             console.log(debug);
         }
 
@@ -657,56 +675,40 @@
             let ppi = this._primPointerInfo;
             let event = eventData ? eventData.event : null;
 
-            // In case of PointerOver/Out we will first notify the parent with PointerEnter/Leave
-            if ((mask & (PrimitivePointerInfo.PointerOver | PrimitivePointerInfo.PointerOut)) !== 0) {
-                this._notifParents(prim, mask);
-            }
-
-            let bubbleCancelled = false;
             let cur = prim;
             while (cur && !cur.isDisposed) {
-                // Only trigger the observers if the primitive is intersected (except for out)
-                if (!bubbleCancelled) {
-                    this._updatePrimPointerPos(cur);
-
-                    // Exec the observers
-                    this._debugExecObserver(cur, mask);
-                    if (!cur._pointerEventObservable.notifyObservers(ppi, mask) && eventData instanceof PointerInfoPre) {
-                        eventData.skipOnPointerObservable = true;
-                        return false;
+                this._updatePrimPointerPos(cur);
+
+                // For the first level we have to fire Enter or Leave for corresponding Over or Out
+                if (cur === prim) {
+                    // Fire the proper notification
+                    if (mask === PrimitivePointerInfo.PointerOver) {
+                        this._debugExecObserver(prim, PrimitivePointerInfo.PointerEnter);
+                        prim._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerEnter);
                     }
 
-                    this._triggerActionManager(cur, ppi, mask, event);
-
-                    // 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 (ppi.cancelBubble) {
-                        if ((mask & (PrimitivePointerInfo.PointerOver | PrimitivePointerInfo.PointerOut)) === 0) {
-                            return false;
-                        }
-
-                        // We're dealing with PointerOver/Out, let's keep looping to fire PointerEnter/Leave, but not Over/Out anymore
-                        bubbleCancelled = true;
+                    // Trigger a PointerLeave corresponding to the PointerOut
+                    else if (mask === PrimitivePointerInfo.PointerOut) {
+                        this._debugExecObserver(prim, PrimitivePointerInfo.PointerLeave);
+                        prim._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerLeave);
                     }
                 }
 
-                // If bubble is cancel we didn't update the Primitive Pointer Pos yet, let's do it
-                if (bubbleCancelled) {
-                    this._updatePrimPointerPos(cur);
+                // Exec the observers
+                this._debugExecObserver(cur, mask);
+                if (!cur._pointerEventObservable.notifyObservers(ppi, mask) && eventData instanceof PointerInfoPre) {
+                    eventData.skipOnPointerObservable = true;
+                    return false;
                 }
 
-                // NOTE TO MYSELF, this is commented right now because it doesn't seemed needed but I can't figure out why I put this code in the first place
-                //// Trigger a PointerEnter corresponding to the PointerOver
-                //if (mask === PrimitivePointerInfo.PointerOver) {
-                //    this._debugExecObserver(cur, PrimitivePointerInfo.PointerEnter);
-                //    cur._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerEnter);
-                //}
+                this._triggerActionManager(cur, ppi, mask, event);
+
+                // 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 (ppi.cancelBubble) {
+                    return false;
 
-                //// Trigger a PointerLeave corresponding to the PointerOut
-                //else if (mask === PrimitivePointerInfo.PointerOut) {
-                //    this._debugExecObserver(cur, PrimitivePointerInfo.PointerLeave);
-                //    cur._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerLeave);
-                //}
+                }
 
                 // Loop to the parent
                 cur = cur.parent;
@@ -808,29 +810,6 @@
             }
         }
 
-        _notifParents(prim: Prim2DBase, mask: number) {
-            let pii = this._primPointerInfo;
-
-            let curPrim: Prim2DBase = this;
-
-            while (curPrim) {
-                this._updatePrimPointerPos(curPrim);
-
-                // Fire the proper notification
-                if (mask === PrimitivePointerInfo.PointerOver) {
-                    this._debugExecObserver(curPrim, PrimitivePointerInfo.PointerEnter);
-                    curPrim._pointerEventObservable.notifyObservers(pii, PrimitivePointerInfo.PointerEnter);
-                }
-
-                // Trigger a PointerLeave corresponding to the PointerOut
-                else if (mask === PrimitivePointerInfo.PointerOut) {
-                    this._debugExecObserver(curPrim, PrimitivePointerInfo.PointerLeave);
-                    curPrim._pointerEventObservable.notifyObservers(pii, PrimitivePointerInfo.PointerLeave);
-                }
-                curPrim = curPrim.parent;
-            }
-        }
-
         /**
          * Don't forget to call the dispose method when you're done with the Canvas instance.
          * But don't worry, if you dispose its scene, the canvas will be automatically disposed too.
@@ -1068,7 +1047,10 @@
          * Return 
          */
         public get overPrim(): Prim2DBase {
-            return this._actualOverPrimitive ? this._actualOverPrimitive.prim : null;
+            if (this._actualIntersectionList && this._actualIntersectionList.length>0) {
+                return this._actualIntersectionList[0].prim;
+            }
+            return null;
         }
 
         /**
@@ -1246,6 +1228,9 @@
         private _afterRenderObserver: Observer<Scene>;
         private _supprtInstancedArray: boolean;
         private _trackedGroups: Array<Group2D>;
+        protected _trackNode: Node;
+        protected _trackNodeOffset :Vector3;
+        protected _trackNodeBillboard : boolean;
         protected _maxAdaptiveWorldSpaceCanvasSize: number;
         private _designSize: Size;
         private _designUseHorizAxis: boolean;
@@ -1271,14 +1256,20 @@
         private static _v = Vector3.Zero(); // Must stay zero
         private static _m = Matrix.Identity();
         private static _mI = Matrix.Identity(); // Must stay identity
+        private static tS = Vector3.Zero();
+        private static tT = Vector3.Zero();
+        private static tR = Quaternion.Identity();
 
         private _updateTrackedNodes() {
+            // Get the used camera
             let cam = this.scene.cameraToUseForPointers || this.scene.activeCamera;
 
+            // Compute some matrix stuff
             cam.getViewMatrix().multiplyToRef(cam.getProjectionMatrix(), Canvas2D._m);
             let rh = this.engine.getRenderHeight();
             let v = cam.viewport.toGlobal(this.engine.getRenderWidth(), rh);
 
+            // Compute the screen position of each group that track a given scene node
             for (let group of this._trackedGroups) {
                 if (group.isDisposed) {
                     continue;
@@ -1296,6 +1287,43 @@
                 group.x = Math.round(proj.x/s);
                 group.y = Math.round((rh - proj.y)/s);
             }
+
+            // If it's a WorldSpaceCanvas and it's tracking a node, let's update the WSC transformation data
+            if (this._trackNode) {
+                let rot: Quaternion = null;
+                let scale: Vector3 = null;
+
+                let worldmtx = this._trackNode.getWorldMatrix();
+                let pos = worldmtx.getTranslation().add(this._trackNodeOffset);
+                let wsc = <WorldSpaceCanvas2D><any>this;
+                let wsn = wsc.worldSpaceCanvasNode;
+
+                if (this._trackNodeBillboard) {
+                    let viewMtx = cam.getViewMatrix().clone().invert();
+                    viewMtx.decompose(Canvas2D.tS, Canvas2D.tR, Canvas2D.tT);
+                    rot = Canvas2D.tR.clone();
+                }
+
+                worldmtx.decompose(Canvas2D.tS, Canvas2D.tR, Canvas2D.tT);
+                let mtx = Matrix.Compose(Canvas2D.tS, Canvas2D.tR, Vector3.Zero());
+                pos = worldmtx.getTranslation().add(Vector3.TransformCoordinates(this._trackNodeOffset, mtx));
+
+                if (Canvas2D.tS.lengthSquared() !== 1) {
+                    scale = Canvas2D.tS.clone();
+                }
+
+                if (!this._trackNodeBillboard) {
+                    rot = Canvas2D.tR.clone();
+                }
+
+                if (wsn instanceof AbstractMesh) {
+                    wsn.position = pos;
+                    wsn.rotationQuaternion = rot;
+                    if (scale) {
+                        wsn.scaling = scale;
+                    }
+                }
+            }
         }
 
         /**
@@ -1715,10 +1743,14 @@
          * @param scene the Scene that owns the Canvas
          * @param size the dimension of the Canvas in World Space
          * @param settings a combination of settings, possible ones are
-         *  - children: an array of direct children primitives
-         *  - id: a text identifier, for information purpose only, default is null.
-         *  - worldPosition the position of the Canvas in World Space, default is [0,0,0]
-         *  - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - children: an array of direct children primitives
+         * - id: a text identifier, for information purpose only, default is null.
+         * - unitScaleFactor: if specified the created canvas will be with a width of size.width*unitScaleFactor and a height of size.height.unitScaleFactor. If not specified, the unit of 1 is used. You can use this setting when you're dealing with a 3D world with small coordinates and you need a Canvas having bigger coordinates (typically to display text with better quality).
+         * - worldPosition the position of the Canvas in World Space, default is [0,0,0]
+         * - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - trackNode: if you want the WorldSpaceCanvas to track the position/rotation/scale of a given Scene Node, use this setting to specify the Node to track
+         * - trackNodeOffset: if you use trackNode you may want to specify a 3D Offset to apply to shift the Canvas
+         * - trackNodeBillboard: if true the WorldSpaceCanvas will always face the screen
          * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
          * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
          * - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is false (the opposite of ScreenSpace).
@@ -1739,8 +1771,12 @@
 
             children                 ?: Array<Prim2DBase>,
             id                       ?: string,
+            unitScaleFactor          ?: number,
             worldPosition            ?: Vector3,
             worldRotation            ?: Quaternion,
+            trackNode                ?: Node,
+            trackNodeOffset          ?: Vector3,
+            trackNodeBillboard       ?: boolean,
             sideOrientation          ?: number,
             cachingStrategy          ?: number,
             enableInteraction        ?: boolean,
@@ -1761,7 +1797,11 @@
             Prim2DBase._isCanvasInit = true;
             let s = <any>settings;
             s.isScreenSpace = false;
-            s.size = size.clone();
+            if (settings.unitScaleFactor != null) {
+                s.size = size.multiplyByFloats(settings.unitScaleFactor, settings.unitScaleFactor);
+            } else {
+                s.size = size.clone();
+            }
             settings.cachingStrategy = (settings.cachingStrategy == null) ? Canvas2D.CACHESTRATEGY_CANVAS : settings.cachingStrategy;
 
             if (settings.cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
@@ -1778,13 +1818,23 @@
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //}
 
+            if (settings.trackNode != null) {
+                this._trackNode = settings.trackNode;
+                this._trackNodeOffset = (settings.trackNodeOffset != null) ? settings.trackNodeOffset : Vector3.Zero();
+                this._trackNodeBillboard = (settings.trackNodeBillboard != null) ? settings.trackNodeBillboard : false;
+            } else {
+                this._trackNode = null;
+                this._trackNodeOffset = null;
+                this._trackNodeBillboard = false;
+            }
+
             let createWorldSpaceNode = !settings || (settings.customWorldSpaceNode == null);
             this._customWorldSpaceNode = !createWorldSpaceNode;
             let id = settings ? settings.id || null : null;
 
             // Set the max size of texture allowed for the adaptive render of the world space canvas cached bitmap
             let capMaxTextSize = this.engine.getCaps().maxRenderTextureSize;
-            let defaultTextSize = (Math.min(capMaxTextSize, 1024));     // Default is 4K if allowed otherwise the max allowed
+            let defaultTextSize = (Math.min(capMaxTextSize, 1024));     // Default is 1K if allowed otherwise the max allowed
             if (settings.maxAdaptiveCanvasSize == null) {
                 this._maxAdaptiveWorldSpaceCanvasSize = defaultTextSize;
             } else {

+ 1 - 1
canvas2D/src/Engine/babylon.fontTexture.ts

@@ -414,7 +414,7 @@
             this._context.textBaseline = "top";
 
             var res = this.getFontHeight(font);
-            this._lineHeightSuper = res.height+4;
+            this._lineHeightSuper = res.height; //+4;
             this._lineHeight = this._superSample ? (Math.ceil(this._lineHeightSuper / 2)) : this._lineHeightSuper;
             this._offset = res.offset - 1;
             this._xMargin = 1 + Math.ceil(this._lineHeightSuper / 15);    // Right now this empiric formula seems to work...

+ 44 - 12
canvas2D/src/Engine/babylon.prim2dBase.ts

@@ -1511,7 +1511,7 @@
                     this.addChild(child);
 
                     // Good time to patch the hierarchy, it won't go very far if there's no need to
-                    if (this.owner != null) {
+                    if (this.owner != null && this._hierarchyDepth != null) {
                         child._patchHierarchy(this.owner);
                     }
                 }
@@ -1962,7 +1962,6 @@
                 Prim2DBase.boundinbBoxReentrency = false;
 
                 return this._boundingSize;
-
             }
             return this._size;
         }
@@ -2547,7 +2546,9 @@
             return this._localTransform;
         }
 
+        private static _bMinMax = Vector4.Zero();
         private static _bMax = Vector2.Zero();
+        private static _bSize = Size.Zero();
         private static _tpsBB = new BoundingInfo2D();
         /**
          * Get the boundingInfo associated to the primitive and its children.
@@ -2558,7 +2559,9 @@
                 if (this.owner) {
                     this.owner.boundingInfoRecomputeCounter.addCount(1, false);
                 }
-                if (this.isSizedByContent) {
+                let sizedByContent = this.isSizedByContent;
+
+                if (sizedByContent) {
                     this._boundingInfo.clear();
                 } else {
                     this._boundingInfo.copyFrom(this.levelBoundingInfo);
@@ -2572,10 +2575,28 @@
                     bi.unionToRef(tps, bi);
                 }
 
-                this._boundingInfo.maxToRef(Prim2DBase._bMax);
+                // If the size is determined by the content we have to update the contentArea
+                //  and compute the size considering the padding (if any)
+                if (sizedByContent) {
+                    bi.maxToRef(Prim2DBase._bMax);
+                    this._contentArea.width = Prim2DBase._bMax.x;
+                    this._contentArea.height = Prim2DBase._bMax.y;
+
+                    if (this._hasPadding) {
+                        let padding = this.padding;
+                        let mm = Prim2DBase._bMinMax;
+                        bi.minMaxToRef(mm);
+                        mm.z += padding.leftPixels + padding.rightPixels;
+                        mm.w += padding.bottomPixels + padding.topPixels;
+                        this._paddingOffset.copyFromFloats(padding.leftPixels, padding.bottomPixels, padding.rightPixels, padding.topPixels);
+                        BoundingInfo2D.CreateFromMinMaxToRef(mm.x, mm.z, mm.y, mm.w, bi);
+                    }
+                }
+
+                this._boundingInfo.sizeToRef(Prim2DBase._bSize);
                 this._boundingSize.copyFromFloats(
-                    (!this._size || this._size.width == null) ? Math.ceil(Prim2DBase._bMax.x) : this._size.width,
-                    (!this._size || this._size.height == null) ? Math.ceil(Prim2DBase._bMax.y) : this._size.height);
+                    (!this._size || this._size.width == null) ? Math.ceil(Prim2DBase._bSize.width) : this._size.width,
+                    (!this._size || this._size.height == null) ? Math.ceil(Prim2DBase._bSize.height) : this._size.height);
 
                 this._clearFlags(SmartPropertyPrim.flagBoundingInfoDirty);
             }
@@ -3482,12 +3503,6 @@
             if (this._hasPadding) {
                 // Two cases from here: the size of the Primitive is Auto, its content can't be shrink, so we resize the primitive itself
                 if (isSizeAuto) {
-                    let content = this.size.clone();
-                    this._getActualSizeFromContentToRef(content, Prim2DBase._icArea);
-                    this.padding.enlarge(Prim2DBase._icArea, this._paddingOffset, Prim2DBase._size);
-                    this._contentArea.copyFrom(content);
-                    this.actualSize = Prim2DBase._size.clone();
-
                     // Changing the padding has resize the prim, which forces us to recompute margin again
                     if (this._hasMargin) {
                         this.margin.computeWithAlignment(this.layoutArea, Prim2DBase._size, this.marginAlignment, this._marginOffset, Prim2DBase._size);
@@ -3533,6 +3548,23 @@
          * Children of this primitive will be positioned relative to the bottom/left corner of this area.
          */
         public get contentArea(): Size {
+            if (!this._size || this._size.width == null || this._size.height == null) {
+
+                if (Prim2DBase.boundinbBoxReentrency) {
+                    return Prim2DBase.nullSize;
+                }
+
+                if (!this._isFlagSet(SmartPropertyPrim.flagBoundingInfoDirty)) {
+                    return this._boundingSize;
+                }
+
+                Prim2DBase.boundinbBoxReentrency = true;
+                let b = this.boundingInfo;
+                Prim2DBase.boundinbBoxReentrency = false;
+
+                return this._contentArea;
+            } else
+
             // Check for positioning update
             if (this._isFlagSet(SmartPropertyPrim.flagPositioningDirty)) {
                 this._updatePositioning();

+ 8 - 2
canvas2D/src/Engine/babylon.renderablePrim2d.ts

@@ -955,8 +955,8 @@
             let w = size.width;
             let h = size.height;
             let invZBias = 1 / zBias;
-            let tx = new Vector4(t.m[0] * rgScale.x * 2 / w, t.m[4] * rgScale.x * 2 / w, 0/*t.m[8]*/, ((t.m[12] + offX) * rgScale.x * 2 / w) - 1);
-            let ty = new Vector4(t.m[1] * rgScale.y * 2 / h, t.m[5] * rgScale.y * 2 / h, 0/*t.m[9]*/, ((t.m[13] + offY) * rgScale.y * 2 / h) - 1);
+            let tx = new Vector4(t.m[0] * rgScale.x * 2/* / w*/, t.m[4] * rgScale.x * 2/* / w*/, 0/*t.m[8]*/, ((t.m[12] + offX) * rgScale.x * 2 / w) - 1);
+            let ty = new Vector4(t.m[1] * rgScale.y * 2/* / h*/, t.m[5] * rgScale.y * 2/* / h*/, 0/*t.m[9]*/, ((t.m[13] + offY) * rgScale.y * 2 / h) - 1);
 
             if (!this.applyActualScaleOnTransform()) {
                 t.m[0] = tx.x, t.m[4] = tx.y, t.m[12] = tx.w;
@@ -969,6 +969,12 @@
                 ty = new Vector4(t.m[1], t.m[5], 0, t.m[13]);
             }
 
+            tx.x /= w;
+            tx.y /= w;
+
+            ty.x /= h;
+            ty.y /= h;
+
             part.transformX = tx;
             part.transformY = ty;
             part.opacity = this.actualOpacity;

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 24 - 24
dist/preview release/babylon.core.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 4931 - 4901
dist/preview release/babylon.d.ts


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 31 - 31
dist/preview release/babylon.js


+ 245 - 115
dist/preview release/babylon.max.js

@@ -8721,65 +8721,65 @@ var BABYLON;
                 isPot = (BABYLON.Tools.IsExponentOfTwo(width) && BABYLON.Tools.IsExponentOfTwo(height));
                 _this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
-                if (!noMipmap && isPot) {
-                    if (mipmmapGenerator) {
-                        var arrayTemp = [];
-                        // Data are known to be in +X +Y +Z -X -Y -Z
-                        // mipmmapGenerator data is expected to be order in +X -X +Y -Y +Z -Z
-                        arrayTemp.push(rgbeDataArrays[0]); // +X
-                        arrayTemp.push(rgbeDataArrays[3]); // -X
-                        arrayTemp.push(rgbeDataArrays[1]); // +Y
-                        arrayTemp.push(rgbeDataArrays[4]); // -Y
-                        arrayTemp.push(rgbeDataArrays[2]); // +Z
-                        arrayTemp.push(rgbeDataArrays[5]); // -Z
-                        var mipData = mipmmapGenerator(arrayTemp);
-                        for (var level = 0; level < mipData.length; level++) {
-                            var mipSize = width >> level;
-                            // mipData is order in +X -X +Y -Y +Z -Z
-                            gl.texImage2D(facesIndex[0], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][0]);
-                            gl.texImage2D(facesIndex[1], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][2]);
-                            gl.texImage2D(facesIndex[2], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][4]);
-                            gl.texImage2D(facesIndex[3], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][1]);
-                            gl.texImage2D(facesIndex[4], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][3]);
-                            gl.texImage2D(facesIndex[5], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][5]);
-                        }
+                if (mipmmapGenerator) {
+                    var arrayTemp = [];
+                    // Data are known to be in +X +Y +Z -X -Y -Z
+                    // mipmmapGenerator data is expected to be order in +X -X +Y -Y +Z -Z
+                    arrayTemp.push(rgbeDataArrays[0]); // +X
+                    arrayTemp.push(rgbeDataArrays[3]); // -X
+                    arrayTemp.push(rgbeDataArrays[1]); // +Y
+                    arrayTemp.push(rgbeDataArrays[4]); // -Y
+                    arrayTemp.push(rgbeDataArrays[2]); // +Z
+                    arrayTemp.push(rgbeDataArrays[5]); // -Z
+                    var mipData = mipmmapGenerator(arrayTemp);
+                    for (var level = 0; level < mipData.length; level++) {
+                        var mipSize = width >> level;
+                        // mipData is order in +X -X +Y -Y +Z -Z
+                        gl.texImage2D(facesIndex[0], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][0]);
+                        gl.texImage2D(facesIndex[1], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][2]);
+                        gl.texImage2D(facesIndex[2], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][4]);
+                        gl.texImage2D(facesIndex[3], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][1]);
+                        gl.texImage2D(facesIndex[4], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][3]);
+                        gl.texImage2D(facesIndex[5], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][5]);
                     }
-                    else {
-                        if (internalFormat === gl.RGB) {
-                            internalFormat = gl.RGBA;
-                            // Data are known to be in +X +Y +Z -X -Y -Z
-                            for (var index = 0; index < facesIndex.length; index++) {
-                                var faceData = rgbeDataArrays[index];
-                                // Create a new RGBA Face.
-                                var newFaceData = new Float32Array(width * height * 4);
-                                for (var x = 0; x < width; x++) {
-                                    for (var y = 0; y < height; y++) {
-                                        var index_1 = (y * width + x) * 3;
-                                        var newIndex = (y * width + x) * 4;
-                                        // Map Old Value to new value.
-                                        newFaceData[newIndex + 0] = faceData[index_1 + 0];
-                                        newFaceData[newIndex + 1] = faceData[index_1 + 1];
-                                        newFaceData[newIndex + 2] = faceData[index_1 + 2];
-                                        // Add fully opaque alpha channel.
-                                        newFaceData[newIndex + 3] = 1;
-                                    }
+                }
+                else {
+                    if (internalFormat === gl.RGB) {
+                        internalFormat = gl.RGBA;
+                        // Data are known to be in +X +Y +Z -X -Y -Z
+                        for (var index = 0; index < facesIndex.length; index++) {
+                            var faceData = rgbeDataArrays[index];
+                            // Create a new RGBA Face.
+                            var newFaceData = new Float32Array(width * height * 4);
+                            for (var x = 0; x < width; x++) {
+                                for (var y = 0; y < height; y++) {
+                                    var index_1 = (y * width + x) * 3;
+                                    var newIndex = (y * width + x) * 4;
+                                    // Map Old Value to new value.
+                                    newFaceData[newIndex + 0] = faceData[index_1 + 0];
+                                    newFaceData[newIndex + 1] = faceData[index_1 + 1];
+                                    newFaceData[newIndex + 2] = faceData[index_1 + 2];
+                                    // Add fully opaque alpha channel.
+                                    newFaceData[newIndex + 3] = 1;
                                 }
-                                // Reupload the face.
-                                gl.texImage2D(facesIndex[index], 0, internalSizedFomat, width, height, 0, internalFormat, textureType, newFaceData);
                             }
+                            // Reupload the face.
+                            gl.texImage2D(facesIndex[index], 0, internalSizedFomat, width, height, 0, internalFormat, textureType, newFaceData);
                         }
-                        else {
-                            // Data are known to be in +X +Y +Z -X -Y -Z
-                            for (var index = 0; index < facesIndex.length; index++) {
-                                var faceData = rgbeDataArrays[index];
-                                gl.texImage2D(facesIndex[index], 0, internalSizedFomat, width, height, 0, internalFormat, textureType, faceData);
-                            }
+                    }
+                    else {
+                        // Data are known to be in +X +Y +Z -X -Y -Z
+                        for (var index = 0; index < facesIndex.length; index++) {
+                            var faceData = rgbeDataArrays[index];
+                            gl.texImage2D(facesIndex[index], 0, internalSizedFomat, width, height, 0, internalFormat, textureType, faceData);
                         }
+                    }
+                    if (!noMipmap && isPot) {
                         gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                     }
-                }
-                else {
-                    noMipmap = true;
+                    else {
+                        noMipmap = true;
+                    }
                 }
                 if (textureType === gl.FLOAT && !_this._caps.textureFloatLinearFiltering) {
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
@@ -10324,6 +10324,7 @@ var BABYLON;
             if (this._show) {
                 this._show = false;
                 this._scene.unregisterBeforeRender(this._renderFunction);
+                this._scene = null;
             }
             if (this._renderLine) {
                 this._renderLine.dispose();
@@ -10333,8 +10334,9 @@ var BABYLON;
         };
         Ray.prototype._render = function () {
             var point = this._renderPoints[1];
+            var len = Math.min(this.length, 1000000);
             point.copyFrom(this.direction);
-            point.scaleInPlace(this.length);
+            point.scaleInPlace(len);
             point.addInPlace(this.origin);
             BABYLON.Mesh.CreateLines("ray", this._renderPoints, this._scene, true, this._renderLine);
         };
@@ -10460,6 +10462,105 @@ var BABYLON;
 
 //# sourceMappingURL=babylon.ray.js.map
 
+var BABYLON;
+(function (BABYLON) {
+    var RayHelper = (function () {
+        function RayHelper(ray) {
+            this.ray = ray;
+        }
+        RayHelper.prototype.show = function (scene, color) {
+            if (!this._renderFunction) {
+                var ray = this.ray;
+                this._renderFunction = this._render.bind(this);
+                this._scene = scene;
+                this._renderPoints = [ray.origin, ray.origin.add(ray.direction.scale(ray.length))];
+                this._renderLine = BABYLON.Mesh.CreateLines("ray", this._renderPoints, scene, true);
+                this._scene.registerBeforeRender(this._renderFunction);
+            }
+            if (color) {
+                this._renderLine.color.copyFrom(color);
+            }
+        };
+        RayHelper.prototype.hide = function () {
+            if (this._renderFunction) {
+                this._scene.unregisterBeforeRender(this._renderFunction);
+                this._scene = null;
+                this._renderFunction = null;
+                this._renderLine.dispose();
+                this._renderLine = null;
+                this._renderPoints = null;
+            }
+        };
+        RayHelper.prototype._render = function () {
+            var ray = this.ray;
+            var point = this._renderPoints[1];
+            var len = Math.min(ray.length, 1000000);
+            point.copyFrom(ray.direction);
+            point.scaleInPlace(len);
+            point.addInPlace(ray.origin);
+            BABYLON.Mesh.CreateLines("ray", this._renderPoints, this._scene, true, this._renderLine);
+        };
+        RayHelper.prototype.attachToMesh = function (mesh, meshSpaceDirection, meshSpaceOrigin, length) {
+            this._attachedToMesh = mesh;
+            var ray = this.ray;
+            if (!ray.direction) {
+                ray.direction = BABYLON.Vector3.Zero();
+            }
+            if (!ray.origin) {
+                ray.origin = BABYLON.Vector3.Zero();
+            }
+            if (length) {
+                ray.length = length;
+            }
+            if (!meshSpaceOrigin) {
+                meshSpaceOrigin = BABYLON.Vector3.Zero();
+            }
+            if (!meshSpaceDirection) {
+                // -1 so that this will work with Mesh.lookAt
+                meshSpaceDirection = new BABYLON.Vector3(0, 0, -1);
+            }
+            if (!this._meshSpaceDirection) {
+                this._meshSpaceDirection = meshSpaceDirection.clone();
+                this._meshSpaceOrigin = meshSpaceOrigin.clone();
+            }
+            else {
+                this._meshSpaceDirection.copyFrom(meshSpaceDirection);
+                this._meshSpaceOrigin.copyFrom(meshSpaceOrigin);
+            }
+            if (!this._updateToMeshFunction) {
+                this._updateToMeshFunction = this._updateToMesh.bind(this);
+                this._attachedToMesh.getScene().registerBeforeRender(this._updateToMeshFunction);
+            }
+            this._updateToMesh();
+        };
+        RayHelper.prototype.detachFromMesh = function () {
+            if (this._attachedToMesh) {
+                this._attachedToMesh.getScene().unregisterBeforeRender(this._updateToMeshFunction);
+                this._attachedToMesh = null;
+                this._updateToMeshFunction = null;
+            }
+        };
+        RayHelper.prototype._updateToMesh = function () {
+            var ray = this.ray;
+            if (this._attachedToMesh._isDisposed) {
+                this.detachFromMesh();
+                return;
+            }
+            this._attachedToMesh.getDirectionToRef(this._meshSpaceDirection, ray.direction);
+            BABYLON.Vector3.TransformCoordinatesToRef(this._meshSpaceOrigin, this._attachedToMesh.getWorldMatrix(), ray.origin);
+        };
+        RayHelper.prototype.dispose = function () {
+            this.hide();
+            this.detachFromMesh();
+            this.ray = null;
+        };
+        return RayHelper;
+    }());
+    BABYLON.RayHelper = RayHelper;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.rayHelper.js.map
+
 
 var BABYLON;
 (function (BABYLON) {
@@ -20069,6 +20170,14 @@ var BABYLON;
             _this._partitioningSubdivisions = 10; // number of subdivisions per axis in the partioning space  
             _this._partitioningBBoxRatio = 1.01; // the partioning array space is by default 1% bigger than the bounding box
             _this._facetDataEnabled = false; // is the facet data feature enabled on this mesh ?
+            _this._facetParameters = {}; // keep a reference to the object parameters to avoid memory re-allocation
+            _this._bbSize = BABYLON.Vector3.Zero(); // bbox size approximated for facet data
+            _this._subDiv = {
+                max: 1,
+                X: 1,
+                Y: 1,
+                Z: 1
+            };
             // Will be used to save a source mesh reference, If any
             _this._source = null;
             if (source) {
@@ -21291,6 +21400,10 @@ var BABYLON;
                     highlightLayer.removeExcludedMesh(this);
                 }
             }
+            // facet data
+            if (this._facetDataEnabled) {
+                this.disableFacetData();
+            }
             _super.prototype.dispose.call(this, doNotRecurse);
         };
         /**
@@ -21652,8 +21765,28 @@ var BABYLON;
             var positions = this.getVerticesData(BABYLON.VertexBuffer.PositionKind);
             var indices = this.getIndices();
             var normals = this.getVerticesData(BABYLON.VertexBuffer.NormalKind);
-            var options = this.getFacetDataParameters();
-            BABYLON.VertexData.ComputeNormals(positions, indices, normals, options);
+            var bInfo = this.getBoundingInfo();
+            this._bbSize.x = (bInfo.maximum.x - bInfo.minimum.x > BABYLON.Epsilon) ? bInfo.maximum.x - bInfo.minimum.x : BABYLON.Epsilon;
+            this._bbSize.y = (bInfo.maximum.y - bInfo.minimum.y > BABYLON.Epsilon) ? bInfo.maximum.y - bInfo.minimum.y : BABYLON.Epsilon;
+            this._bbSize.z = (bInfo.maximum.z - bInfo.minimum.z > BABYLON.Epsilon) ? bInfo.maximum.z - bInfo.minimum.z : BABYLON.Epsilon;
+            var bbSizeMax = (this._bbSize.x > this._bbSize.y) ? this._bbSize.x : this._bbSize.y;
+            bbSizeMax = (bbSizeMax > this._bbSize.z) ? bbSizeMax : this._bbSize.z;
+            this._subDiv.max = this._partitioningSubdivisions;
+            this._subDiv.X = Math.floor(this._subDiv.max * this._bbSize.x / bbSizeMax); // adjust the number of subdivisions per axis
+            this._subDiv.Y = Math.floor(this._subDiv.max * this._bbSize.y / bbSizeMax); // according to each bbox size per axis
+            this._subDiv.Z = Math.floor(this._subDiv.max * this._bbSize.z / bbSizeMax);
+            this._subDiv.X = this._subDiv.X < 1 ? 1 : this._subDiv.X; // at least one subdivision
+            this._subDiv.Y = this._subDiv.Y < 1 ? 1 : this._subDiv.Y;
+            this._subDiv.Z = this._subDiv.Z < 1 ? 1 : this._subDiv.Z;
+            // set the parameters for ComputeNormals()
+            this._facetParameters.facetNormals = this.getFacetLocalNormals();
+            this._facetParameters.facetPositions = this.getFacetLocalPositions();
+            this._facetParameters.facetPartitioning = this.getFacetLocalPartitioning();
+            this._facetParameters.bInfo = bInfo;
+            this._facetParameters.bbSize = this._bbSize;
+            this._facetParameters.subDiv = this._subDiv;
+            this._facetParameters.ratio = this.partitioningBBoxRatio;
+            BABYLON.VertexData.ComputeNormals(positions, indices, normals, this._facetParameters);
             return this;
         };
         /**
@@ -21719,15 +21852,8 @@ var BABYLON;
          */
         Mesh.prototype.getFacetNormalToRef = function (i, ref) {
             var localNorm = (this.getFacetLocalNormals())[i];
-            var localPos = (this.getFacetLocalPositions())[i];
-            var world = this.getWorldMatrix();
-            var x = localPos.x + localNorm.x;
-            var y = localPos.y + localNorm.y;
-            var z = localPos.z + localNorm.z;
-            BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, world, ref);
-            var worldPos = BABYLON.Tmp.Vector3[8];
-            this.getFacetPositionToRef(i, worldPos);
-            ref.subtractInPlace(worldPos);
+            (this.getWorldMatrix()).getRotationMatrixToRef(BABYLON.Tmp.Matrix[0]);
+            BABYLON.Vector3.TransformCoordinatesToRef(localNorm, BABYLON.Tmp.Matrix[0], ref);
             return this;
         };
         /**
@@ -21735,28 +21861,31 @@ var BABYLON;
          */
         Mesh.prototype.getFacetsAtLocalCoordinates = function (x, y, z) {
             var bInfo = this.getBoundingInfo();
-            var subRatio = this._partitioningSubdivisions * this._partitioningBBoxRatio;
-            var ox = Math.floor((x - bInfo.minimum.x * this._partitioningBBoxRatio) / (bInfo.maximum.x - bInfo.minimum.x) * subRatio);
-            var oy = Math.floor((y - bInfo.minimum.y * this._partitioningBBoxRatio) / (bInfo.maximum.y - bInfo.minimum.y) * subRatio);
-            var oz = Math.floor((z - bInfo.minimum.z * this._partitioningBBoxRatio) / (bInfo.maximum.z - bInfo.minimum.z) * subRatio);
-            if (ox < 0 || ox > this._partitioningSubdivisions || oy < 0 || oy > this._partitioningSubdivisions || oz < 0 || oz > this._partitioningSubdivisions) {
+            var ox = Math.floor((x - bInfo.minimum.x * this._partitioningBBoxRatio) * this._subDiv.X * this._partitioningBBoxRatio / this._bbSize.x);
+            var oy = Math.floor((y - bInfo.minimum.y * this._partitioningBBoxRatio) * this._subDiv.Y * this._partitioningBBoxRatio / this._bbSize.y);
+            var oz = Math.floor((z - bInfo.minimum.z * this._partitioningBBoxRatio) * this._subDiv.Z * this._partitioningBBoxRatio / this._bbSize.z);
+            if (ox < 0 || ox > this._subDiv.max || oy < 0 || oy > this._subDiv.max || oz < 0 || oz > this._subDiv.max) {
                 return null;
             }
-            return this._facetPartitioning[ox + this._partitioningSubdivisions * oy + this._partitioningSubdivisions * this._partitioningSubdivisions * oz];
+            return this._facetPartitioning[ox + this._subDiv.max * oy + this._subDiv.max * this._subDiv.max * oz];
         };
         /**
          * Returns the closest mesh facet index at (x,y,z) World coordinates, null if not found.
          * If the parameter projected (vector3) is passed, it is set as the (x,y,z) World projection on the facet.
-         * If onlyFacing is true, only the facet "facing" (x,y,z) are returned : positive dot normal * (x,y,z).
+         * If checkFace is true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned.
+         * If facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position.
+         * If facing si false and checkFace is true, only the facet "turning their backs" to (x, y, z) are returned : negative dot (x, y, z) * facet position.
          */
-        Mesh.prototype.getClosestFacetAtCoordinates = function (x, y, z, projected, onlyFacing) {
+        Mesh.prototype.getClosestFacetAtCoordinates = function (x, y, z, projected, checkFace, facing) {
+            if (checkFace === void 0) { checkFace = false; }
+            if (facing === void 0) { facing = true; }
             var world = this.getWorldMatrix();
             var invMat = BABYLON.Tmp.Matrix[5];
             world.invertToRef(invMat);
             var invVect = BABYLON.Tmp.Vector3[8];
             var closest = null;
             BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, invMat, invVect); // transform (x,y,z) to coordinates in the mesh local space
-            closest = this.getClosestFacetAtLocalCoordinates(invVect.x, invVect.y, invVect.z, projected, onlyFacing);
+            closest = this.getClosestFacetAtLocalCoordinates(invVect.x, invVect.y, invVect.z, projected, checkFace, facing);
             if (projected) {
                 // tranform the local computed projected vector to world coordinates
                 BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(projected.x, projected.y, projected.z, world, projected);
@@ -21766,9 +21895,13 @@ var BABYLON;
         /**
          * Returns the closest mesh facet index at (x,y,z) local coordinates, null if not found.
          * If the parameter projected (vector3) is passed, it is set as the (x,y,z) local projection on the facet.
-         * If onlyFacing is true, only the facet "facing" (x,y,z) are returned : positive dot normal * (x,y,z).
+         * If checkFace is true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned.
+         * If facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position.
+         * If facing si false and checkFace is true, only the facet "turning their backs"  to (x, y, z) are returned : negative dot (x, y, z) * facet position.
          */
-        Mesh.prototype.getClosestFacetAtLocalCoordinates = function (x, y, z, projected, onlyFacing) {
+        Mesh.prototype.getClosestFacetAtLocalCoordinates = function (x, y, z, projected, checkFace, facing) {
+            if (checkFace === void 0) { checkFace = false; }
+            if (facing === void 0) { facing = true; }
             var closest = null;
             var tmpx = 0.0;
             var tmpy = 0.0;
@@ -21797,7 +21930,7 @@ var BABYLON;
                 norm = facetNormals[fib];
                 p0 = facetPositions[fib];
                 d = (x - p0.x) * norm.x + (y - p0.y) * norm.y + (z - p0.z) * norm.z;
-                if (!onlyFacing || (onlyFacing && d >= 0)) {
+                if (!checkFace || (checkFace && facing && d >= 0.0) || (checkFace && !facing && d <= 0.0)) {
                     // compute (x,y,z) projection on the facet = (projx, projy, projz)
                     d = norm.x * p0.x + norm.y * p0.y + norm.z * p0.z;
                     t0 = -(norm.x * x + norm.y * y + norm.z * z - d) / (norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);
@@ -21825,16 +21958,6 @@ var BABYLON;
          * Returns the object "parameter" set with all the expected parameters for facetData computation by ComputeNormals()
          */
         Mesh.prototype.getFacetDataParameters = function () {
-            if (!this._facetParameters) {
-                this._facetParameters = {
-                    facetNormals: this.getFacetLocalNormals(),
-                    facetPositions: this.getFacetLocalPositions(),
-                    facetPartitioning: this.getFacetLocalPartitioning(),
-                    bInfo: this.getBoundingInfo(),
-                    partitioningSubdivisions: this.partitioningSubdivisions,
-                    ratio: this.partitioningBBoxRatio
-                };
-            }
             return this._facetParameters;
         };
         /**
@@ -21842,11 +21965,13 @@ var BABYLON;
          * Returns the mesh.
          */
         Mesh.prototype.disableFacetData = function () {
-            this._facetDataEnabled = false;
-            this._facetPositions = null;
-            this._facetNormals = null;
-            this._facetPartitioning = null;
-            this._facetParameters = null;
+            if (this._facetDataEnabled) {
+                this._facetDataEnabled = false;
+                this._facetPositions = null;
+                this._facetNormals = null;
+                this._facetPartitioning = null;
+                this._facetParameters = null;
+            }
             return this;
         };
         // Statics
@@ -35019,9 +35144,10 @@ var BABYLON;
          * facetPositions : optional array of facet positions (vector3)
          * facetNormals : optional array of facet normals (vector3)
          * facetPartitioning : optional partitioning array. facetPositions is required for facetPartitioning computation
-         * partitioningSubdivisions : optional partitioning number of subdivsions on  each axis (int), required for facetPartitioning computation
+         * subDiv : optional partitioning data about subdivsions on  each axis (int), required for facetPartitioning computation
          * ratio : optional partitioning ratio / bounding box, required for facetPartitioning computation
-         * bInfo : optional bounding box info, required for facetPartitioning computation
+         * bbSize : optional bounding box size data, required for facetPartitioning computation
+         * bInfo : optional bounding info, required for facetPartitioning computation
          */
         VertexData.ComputeNormals = function (positions, indices, normals, options) {
             // temporary scalar variables
@@ -35071,10 +35197,12 @@ var BABYLON;
                 var block_idx_v1 = 0; // v1 vertex block index
                 var block_idx_v2 = 0; // v2 vertex block index
                 var block_idx_v3 = 0; // v3 vertex block index  
-                var xSubRatio = 0.0; // tmp x divider
-                var ySubRatio = 0.0; // tmp x divider
-                var zSubRatio = 0.0; // tmp x divider  
-                var subSq = options.partitioningSubdivisions * options.partitioningSubdivisions;
+                var bbSizeMax = (options.bbSize.x > options.bbSize.y) ? options.bbSize.x : options.bbSize.y;
+                bbSizeMax = (bbSizeMax > options.bbSize.z) ? bbSizeMax : options.bbSize.z;
+                var xSubRatio = options.subDiv.X * options.ratio / options.bbSize.x;
+                var ySubRatio = options.subDiv.Y * options.ratio / options.bbSize.y;
+                var zSubRatio = options.subDiv.Z * options.ratio / options.bbSize.z;
+                var subSq = options.subDiv.max * options.subDiv.max;
                 options.facetPartitioning.length = 0;
             }
             // reset the normals
@@ -35124,9 +35252,6 @@ var BABYLON;
                 if (computeFacetPartitioning) {
                     // store the facet indexes in arrays in the main facetPartitioning array :
                     // compute each facet vertex (+ facet barycenter) index in the partiniong array
-                    xSubRatio = options.partitioningSubdivisions * options.ratio / (options.bInfo.maximum.x - options.bInfo.minimum.x);
-                    ySubRatio = options.partitioningSubdivisions * options.ratio / (options.bInfo.maximum.y - options.bInfo.minimum.y);
-                    zSubRatio = options.partitioningSubdivisions * options.ratio / (options.bInfo.maximum.z - options.bInfo.minimum.z);
                     ox = Math.floor((options.facetPositions[index].x - options.bInfo.minimum.x * options.ratio) * xSubRatio);
                     oy = Math.floor((options.facetPositions[index].y - options.bInfo.minimum.y * options.ratio) * ySubRatio);
                     oz = Math.floor((options.facetPositions[index].z - options.bInfo.minimum.z * options.ratio) * zSubRatio);
@@ -35139,10 +35264,10 @@ var BABYLON;
                     b3x = Math.floor((positions[v3x] - options.bInfo.minimum.x * options.ratio) * xSubRatio);
                     b3y = Math.floor((positions[v3y] - options.bInfo.minimum.y * options.ratio) * ySubRatio);
                     b3z = Math.floor((positions[v3z] - options.bInfo.minimum.z * options.ratio) * zSubRatio);
-                    block_idx_v1 = b1x + options.partitioningSubdivisions * b1y + subSq * b1z;
-                    block_idx_v2 = b2x + options.partitioningSubdivisions * b2y + subSq * b2z;
-                    block_idx_v3 = b3x + options.partitioningSubdivisions * b3y + subSq * b3z;
-                    block_idx_o = ox + options.partitioningSubdivisions * oy + subSq * oz;
+                    block_idx_v1 = b1x + options.subDiv.max * b1y + subSq * b1z;
+                    block_idx_v2 = b2x + options.subDiv.max * b2y + subSq * b2z;
+                    block_idx_v3 = b3x + options.subDiv.max * b3y + subSq * b3z;
+                    block_idx_o = ox + options.subDiv.max * oy + subSq * oz;
                     options.facetPartitioning[block_idx_o] = options.facetPartitioning[block_idx_o] ? options.facetPartitioning[block_idx_o] : new Array();
                     options.facetPartitioning[block_idx_v1] = options.facetPartitioning[block_idx_v1] ? options.facetPartitioning[block_idx_v1] : new Array();
                     options.facetPartitioning[block_idx_v2] = options.facetPartitioning[block_idx_v2] ? options.facetPartitioning[block_idx_v2] : new Array();
@@ -48359,6 +48484,7 @@ var BABYLON;
             if (disableDepthRender)
                 this._scene.disableDepthRenderer();
             this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
+            _super.prototype.dispose.call(this);
         };
         // Private Methods
         SSAORenderingPipeline.prototype._createBlurPostProcess = function (ratio) {
@@ -49501,17 +49627,21 @@ var BABYLON;
         * Releases the rendering pipeline and its internal effects. Detaches pipeline from cameras
         */
         HDRRenderingPipeline.prototype.dispose = function () {
-            this._originalPostProcess = undefined;
-            this._brightPassPostProcess = undefined;
-            this._downSampleX4PostProcess = undefined;
-            this._guassianBlurHPostProcess = undefined;
-            this._guassianBlurVPostProcess = undefined;
-            this._textureAdderPostProcess = undefined;
-            for (var i = HDRRenderingPipeline.LUM_STEPS - 1; i >= 0; i--) {
-                this._downSamplePostProcesses[i] = undefined;
+            for (var i = 0; i < this._scene.cameras.length; i++) {
+                var camera = this._scene.cameras[i];
+                this._originalPostProcess.dispose(camera);
+                this._brightPassPostProcess.dispose(camera);
+                this._downSampleX4PostProcess.dispose(camera);
+                this._guassianBlurHPostProcess.dispose(camera);
+                this._guassianBlurVPostProcess.dispose(camera);
+                this._textureAdderPostProcess.dispose(camera);
+                for (var j = HDRRenderingPipeline.LUM_STEPS - 1; j >= 0; j--) {
+                    this._downSamplePostProcesses[j].dispose(camera);
+                }
+                this._hdrPostProcess.dispose(camera);
             }
-            this._hdrPostProcess = undefined;
             this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
+            _super.prototype.dispose.call(this);
         };
         /**
         * Creates the HDR post-process and computes the luminance adaptation
@@ -50813,7 +50943,6 @@ var BABYLON;
                             }
                         }
                         // normals : if the particles can't be morphed then just rotate the normals, what is much more faster than ComputeNormals()
-                        // the same for the facet data
                         if (!this._computeParticleVertex) {
                             this._normal.x = this._fixedNormal32[idx];
                             this._normal.y = this._fixedNormal32[idx + 1];
@@ -53005,7 +53134,7 @@ var BABYLON;
             var texture = null;
             if (parsedTexture.name && !parsedTexture.isRenderTarget) {
                 var size = parsedTexture.isBABYLONPreprocessed ? null : parsedTexture.size;
-                texture = new BABYLON.HDRCubeTexture(rootUrl + parsedTexture.name, scene, size, parsedTexture.generateHarmonics, parsedTexture.useInGammaSpace, parsedTexture.usePMREMGenerator);
+                texture = new BABYLON.HDRCubeTexture(rootUrl + parsedTexture.name, scene, size, parsedTexture.noMipmap, parsedTexture.generateHarmonics, parsedTexture.useInGammaSpace, parsedTexture.usePMREMGenerator);
                 texture.name = parsedTexture.name;
                 texture.hasAlpha = parsedTexture.hasAlpha;
                 texture.level = parsedTexture.level;
@@ -53029,6 +53158,7 @@ var BABYLON;
             serializationObject.usePMREMGenerator = this._usePMREMGenerator;
             serializationObject.isBABYLONPreprocessed = this._isBABYLONPreprocessed;
             serializationObject.customType = "BABYLON.HDRCubeTexture";
+            serializationObject.noMipmap = this._noMipmap;
             return serializationObject;
         };
         /**

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 33 - 33
dist/preview release/babylon.noworker.js


+ 45 - 5
dist/preview release/canvas2D/babylon.canvas2d.d.ts

@@ -678,11 +678,36 @@ declare module BABYLON {
          */
         max(): Vector2;
         /**
+         * return the min/max extend of the bounding info.
+         * x, y, z, w are left, bottom, right and top
+         */
+        minMax(): Vector4;
+        /**
          * Update a vector2 with the max extend of the bounding info
          * @param result must be a valid/allocated vector2 that will contain the result of the operation
          */
         maxToRef(result: Vector2): void;
         /**
+         * Update a vector4 with the min/max extend of the bounding info
+         * x, y, z, w are left, bottom, right and top
+         * @param result must be a valid/allocated vector4 that will contain the result of the operation
+         */
+        minMaxToRef(result: Vector4): void;
+        /**
+         * Return the size of the boundingInfo rect surface
+         */
+        size(): Size;
+        /**
+         * Stores in the result object the size of the boundingInfo rect surface
+         * @param result
+         */
+        sizeToRef(result: Size): void;
+        /**
+         * Inflate the boundingInfo with the given vector
+         * @param offset the extent will be incremented with offset and the radius will be computed again
+         */
+        inflate(offset: Vector2): void;
+        /**
          * Apply a transformation matrix to this BoundingInfo2D and return a new instance containing the result
          * @param matrix the transformation matrix to apply
          * @return the new instance containing the result of the transformation applied on this BoundingInfo2D
@@ -2008,7 +2033,9 @@ declare module BABYLON {
          * Get the local transformation of the primitive
          */
         readonly localTransform: Matrix;
+        private static _bMinMax;
         private static _bMax;
+        private static _bSize;
         private static _tpsBB;
         /**
          * Get the boundingInfo associated to the primitive and its children.
@@ -3685,7 +3712,6 @@ declare module BABYLON {
         private _debugExecObserver(prim, mask);
         private _bubbleNotifyPrimPointerObserver(prim, mask, eventData);
         private _triggerActionManager(prim, ppi, mask, eventData);
-        _notifParents(prim: Prim2DBase, mask: number): void;
         /**
          * Don't forget to call the dispose method when you're done with the Canvas instance.
          * But don't worry, if you dispose its scene, the canvas will be automatically disposed too.
@@ -3821,6 +3847,9 @@ declare module BABYLON {
         private _afterRenderObserver;
         private _supprtInstancedArray;
         private _trackedGroups;
+        protected _trackNode: Node;
+        protected _trackNodeOffset: Vector3;
+        protected _trackNodeBillboard: boolean;
         protected _maxAdaptiveWorldSpaceCanvasSize: number;
         private _designSize;
         private _designUseHorizAxis;
@@ -3842,6 +3871,9 @@ declare module BABYLON {
         private static _v;
         private static _m;
         private static _mI;
+        private static tS;
+        private static tT;
+        private static tR;
         private _updateTrackedNodes();
         /**
          * Call this method change you want to have layout related data computed and up to date (layout area, primitive area, local/global transformation matrices)
@@ -3919,10 +3951,14 @@ declare module BABYLON {
          * @param scene the Scene that owns the Canvas
          * @param size the dimension of the Canvas in World Space
          * @param settings a combination of settings, possible ones are
-         *  - children: an array of direct children primitives
-         *  - id: a text identifier, for information purpose only, default is null.
-         *  - worldPosition the position of the Canvas in World Space, default is [0,0,0]
-         *  - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - children: an array of direct children primitives
+         * - id: a text identifier, for information purpose only, default is null.
+         * - unitScaleFactor: if specified the created canvas will be with a width of size.width*unitScaleFactor and a height of size.height.unitScaleFactor. If not specified, the unit of 1 is used. You can use this setting when you're dealing with a 3D world with small coordinates and you need a Canvas having bigger coordinates (typically to display text with better quality).
+         * - worldPosition the position of the Canvas in World Space, default is [0,0,0]
+         * - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - trackNode: if you want the WorldSpaceCanvas to track the position/rotation/scale of a given Scene Node, use this setting to specify the Node to track
+         * - trackNodeOffset: if you use trackNode you may want to specify a 3D Offset to apply to shift the Canvas
+         * - trackNodeBillboard: if true the WorldSpaceCanvas will always face the screen
          * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
          * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
          * - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is false (the opposite of ScreenSpace).
@@ -3942,8 +3978,12 @@ declare module BABYLON {
         constructor(scene: Scene, size: Size, settings?: {
             children?: Array<Prim2DBase>;
             id?: string;
+            unitScaleFactor?: number;
             worldPosition?: Vector3;
             worldRotation?: Quaternion;
+            trackNode?: Node;
+            trackNodeOffset?: Vector3;
+            trackNodeBillboard?: boolean;
             sideOrientation?: number;
             cachingStrategy?: number;
             enableInteraction?: boolean;

+ 220 - 85
dist/preview release/canvas2D/babylon.canvas2d.js

@@ -1260,7 +1260,7 @@ var BABYLON;
             _this._context.fillStyle = "white";
             _this._context.textBaseline = "top";
             var res = _this.getFontHeight(font);
-            _this._lineHeightSuper = res.height + 4;
+            _this._lineHeightSuper = res.height; //+4;
             _this._lineHeight = _this._superSample ? (Math.ceil(_this._lineHeightSuper / 2)) : _this._lineHeightSuper;
             _this._offset = res.offset - 1;
             _this._xMargin = 1 + Math.ceil(_this._lineHeightSuper / 15); // Right now this empiric formula seems to work...
@@ -1905,6 +1905,15 @@ var BABYLON;
             return r;
         };
         /**
+         * return the min/max extend of the bounding info.
+         * x, y, z, w are left, bottom, right and top
+         */
+        BoundingInfo2D.prototype.minMax = function () {
+            var r = BABYLON.Vector4.Zero();
+            this.minMaxToRef(r);
+            return r;
+        };
+        /**
          * Update a vector2 with the max extend of the bounding info
          * @param result must be a valid/allocated vector2 that will contain the result of the operation
          */
@@ -1913,6 +1922,41 @@ var BABYLON;
             result.y = this.center.y + this.extent.y;
         };
         /**
+         * Update a vector4 with the min/max extend of the bounding info
+         * x, y, z, w are left, bottom, right and top
+         * @param result must be a valid/allocated vector4 that will contain the result of the operation
+         */
+        BoundingInfo2D.prototype.minMaxToRef = function (result) {
+            result.x = this.center.x - this.extent.x;
+            result.y = this.center.y - this.extent.y;
+            result.z = this.center.x + this.extent.x;
+            result.w = this.center.y + this.extent.y;
+        };
+        /**
+         * Return the size of the boundingInfo rect surface
+         */
+        BoundingInfo2D.prototype.size = function () {
+            var r = BABYLON.Size.Zero();
+            this.sizeToRef(r);
+            return r;
+        };
+        /**
+         * Stores in the result object the size of the boundingInfo rect surface
+         * @param result
+         */
+        BoundingInfo2D.prototype.sizeToRef = function (result) {
+            result.width = this.extent.x * 2;
+            result.height = this.extent.y * 2;
+        };
+        /**
+         * Inflate the boundingInfo with the given vector
+         * @param offset the extent will be incremented with offset and the radius will be computed again
+         */
+        BoundingInfo2D.prototype.inflate = function (offset) {
+            this.extent.addInPlace(offset);
+            this.radius = this.extent.length();
+        };
+        /**
          * Apply a transformation matrix to this BoundingInfo2D and return a new instance containing the result
          * @param matrix the transformation matrix to apply
          * @return the new instance containing the result of the transformation applied on this BoundingInfo2D
@@ -4928,7 +4972,7 @@ var BABYLON;
                     var child = _a[_i];
                     _this.addChild(child);
                     // Good time to patch the hierarchy, it won't go very far if there's no need to
-                    if (_this.owner != null) {
+                    if (_this.owner != null && _this._hierarchyDepth != null) {
                         child._patchHierarchy(_this.owner);
                     }
                 }
@@ -5868,7 +5912,8 @@ var BABYLON;
                     if (this.owner) {
                         this.owner.boundingInfoRecomputeCounter.addCount(1, false);
                     }
-                    if (this.isSizedByContent) {
+                    var sizedByContent = this.isSizedByContent;
+                    if (sizedByContent) {
                         this._boundingInfo.clear();
                     }
                     else {
@@ -5882,8 +5927,24 @@ var BABYLON;
                         bb.transformToRef(curChild.localTransform, tps);
                         bi.unionToRef(tps, bi);
                     }
-                    this._boundingInfo.maxToRef(Prim2DBase_1._bMax);
-                    this._boundingSize.copyFromFloats((!this._size || this._size.width == null) ? Math.ceil(Prim2DBase_1._bMax.x) : this._size.width, (!this._size || this._size.height == null) ? Math.ceil(Prim2DBase_1._bMax.y) : this._size.height);
+                    // If the size is determined by the content we have to update the contentArea
+                    //  and compute the size considering the padding (if any)
+                    if (sizedByContent) {
+                        bi.maxToRef(Prim2DBase_1._bMax);
+                        this._contentArea.width = Prim2DBase_1._bMax.x;
+                        this._contentArea.height = Prim2DBase_1._bMax.y;
+                        if (this._hasPadding) {
+                            var padding = this.padding;
+                            var mm = Prim2DBase_1._bMinMax;
+                            bi.minMaxToRef(mm);
+                            mm.z += padding.leftPixels + padding.rightPixels;
+                            mm.w += padding.bottomPixels + padding.topPixels;
+                            this._paddingOffset.copyFromFloats(padding.leftPixels, padding.bottomPixels, padding.rightPixels, padding.topPixels);
+                            BABYLON.BoundingInfo2D.CreateFromMinMaxToRef(mm.x, mm.z, mm.y, mm.w, bi);
+                        }
+                    }
+                    this._boundingInfo.sizeToRef(Prim2DBase_1._bSize);
+                    this._boundingSize.copyFromFloats((!this._size || this._size.width == null) ? Math.ceil(Prim2DBase_1._bSize.width) : this._size.width, (!this._size || this._size.height == null) ? Math.ceil(Prim2DBase_1._bSize.height) : this._size.height);
                     this._clearFlags(BABYLON.SmartPropertyPrim.flagBoundingInfoDirty);
                 }
                 return this._boundingInfo;
@@ -6666,11 +6727,6 @@ var BABYLON;
             if (this._hasPadding) {
                 // Two cases from here: the size of the Primitive is Auto, its content can't be shrink, so we resize the primitive itself
                 if (isSizeAuto) {
-                    var content = this.size.clone();
-                    this._getActualSizeFromContentToRef(content, Prim2DBase_1._icArea);
-                    this.padding.enlarge(Prim2DBase_1._icArea, this._paddingOffset, Prim2DBase_1._size);
-                    this._contentArea.copyFrom(content);
-                    this.actualSize = Prim2DBase_1._size.clone();
                     // Changing the padding has resize the prim, which forces us to recompute margin again
                     if (this._hasMargin) {
                         this.margin.computeWithAlignment(this.layoutArea, Prim2DBase_1._size, this.marginAlignment, this._marginOffset, Prim2DBase_1._size);
@@ -6715,6 +6771,19 @@ var BABYLON;
              * Children of this primitive will be positioned relative to the bottom/left corner of this area.
              */
             get: function () {
+                if (!this._size || this._size.width == null || this._size.height == null) {
+                    if (Prim2DBase_1.boundinbBoxReentrency) {
+                        return Prim2DBase_1.nullSize;
+                    }
+                    if (!this._isFlagSet(BABYLON.SmartPropertyPrim.flagBoundingInfoDirty)) {
+                        return this._boundingSize;
+                    }
+                    Prim2DBase_1.boundinbBoxReentrency = true;
+                    var b = this.boundingInfo;
+                    Prim2DBase_1.boundinbBoxReentrency = false;
+                    return this._contentArea;
+                }
+                else 
                 // Check for positioning update
                 if (this._isFlagSet(BABYLON.SmartPropertyPrim.flagPositioningDirty)) {
                     this._updatePositioning();
@@ -6921,7 +6990,9 @@ var BABYLON;
     Prim2DBase._nullPosition = BABYLON.Vector2.Zero();
     Prim2DBase.boundinbBoxReentrency = false;
     Prim2DBase.nullSize = BABYLON.Size.Zero();
+    Prim2DBase._bMinMax = BABYLON.Vector4.Zero();
     Prim2DBase._bMax = BABYLON.Vector2.Zero();
+    Prim2DBase._bSize = BABYLON.Size.Zero();
     Prim2DBase._tpsBB = new BABYLON.BoundingInfo2D();
     /**
      * Make an intersection test with the primitive, all inputs/outputs are stored in the IntersectInfo2D class, see its documentation for more information.
@@ -8208,8 +8279,8 @@ var BABYLON;
             var w = size.width;
             var h = size.height;
             var invZBias = 1 / zBias;
-            var tx = new BABYLON.Vector4(t.m[0] * rgScale.x * 2 / w, t.m[4] * rgScale.x * 2 / w, 0 /*t.m[8]*/, ((t.m[12] + offX) * rgScale.x * 2 / w) - 1);
-            var ty = new BABYLON.Vector4(t.m[1] * rgScale.y * 2 / h, t.m[5] * rgScale.y * 2 / h, 0 /*t.m[9]*/, ((t.m[13] + offY) * rgScale.y * 2 / h) - 1);
+            var tx = new BABYLON.Vector4(t.m[0] * rgScale.x * 2 /* / w*/, t.m[4] * rgScale.x * 2 /* / w*/, 0 /*t.m[8]*/, ((t.m[12] + offX) * rgScale.x * 2 / w) - 1);
+            var ty = new BABYLON.Vector4(t.m[1] * rgScale.y * 2 /* / h*/, t.m[5] * rgScale.y * 2 /* / h*/, 0 /*t.m[9]*/, ((t.m[13] + offY) * rgScale.y * 2 / h) - 1);
             if (!this.applyActualScaleOnTransform()) {
                 t.m[0] = tx.x, t.m[4] = tx.y, t.m[12] = tx.w;
                 t.m[1] = ty.x, t.m[5] = ty.y, t.m[13] = ty.w;
@@ -8220,6 +8291,10 @@ var BABYLON;
                 tx = new BABYLON.Vector4(t.m[0], t.m[4], 0, t.m[12]);
                 ty = new BABYLON.Vector4(t.m[1], t.m[5], 0, t.m[13]);
             }
+            tx.x /= w;
+            tx.y /= w;
+            ty.x /= h;
+            ty.y /= h;
             part.transformX = tx;
             part.transformY = ty;
             part.opacity = this.actualOpacity;
@@ -13000,7 +13075,9 @@ var BABYLON;
             _this._renderingSize = new BABYLON.Size(0, 0);
             _this._designSize = settings.designSize || null;
             _this._designUseHorizAxis = settings.designUseHorizAxis === true;
-            _this._trackedGroups = new Array();
+            if (!_this._trackedGroups) {
+                _this._trackedGroups = new Array();
+            }
             _this._maxAdaptiveWorldSpaceCanvasSize = null;
             _this._groupCacheMaps = new BABYLON.StringDictionary();
             _this._patchHierarchy(_this);
@@ -13356,6 +13433,8 @@ var BABYLON;
             else {
                 // The pointer is inside the Canvas, do an intersection test
                 this.intersect(ii);
+                // Sort primitives to get them from top to bottom
+                ii.intersectedPrimitives = ii.intersectedPrimitives.sort(function (a, b) { return a.prim.actualZOffset - b.prim.actualZOffset; });
             }
             {
                 // Update prev/actual intersection info, fire "overPrim" property change if needed
@@ -13382,15 +13461,42 @@ var BABYLON;
             if (prevPrim !== actualPrim) {
                 // Detect if the current pointer is captured, only fire event if they belong to the capture primitive
                 var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
-                // Notify the previous "over" prim that the pointer is no longer over it
-                if ((capturedPrim && capturedPrim === prevPrim) || (!capturedPrim && prevPrim && !prevPrim.isDisposed)) {
-                    this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
-                    this._bubbleNotifyPrimPointerObserver(prevPrim, BABYLON.PrimitivePointerInfo.PointerOut, null);
+                // See the NOTE section of: https://www.w3.org/TR/pointerevents/#setting-pointer-capture
+                if (capturedPrim) {
+                    if (capturedPrim === prevPrim) {
+                        this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
+                        this._bubbleNotifyPrimPointerObserver(prevPrim, BABYLON.PrimitivePointerInfo.PointerOut, null);
+                    }
+                    else if (capturedPrim === actualPrim) {
+                        this._primPointerInfo.updateRelatedTarget(actualPrim, this._actualOverPrimitive.intersectionLocation);
+                        this._bubbleNotifyPrimPointerObserver(actualPrim, BABYLON.PrimitivePointerInfo.PointerOver, 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, null);
+                else {
+                    var _loop_1 = function (prev) {
+                        if (!BABYLON.Tools.first(this_1._actualIntersectionList, function (pii) { return pii.prim === prev.prim; })) {
+                            this_1._primPointerInfo.updateRelatedTarget(prev.prim, prev.intersectionLocation);
+                            this_1._bubbleNotifyPrimPointerObserver(prev.prim, BABYLON.PrimitivePointerInfo.PointerOut, null);
+                        }
+                    };
+                    var this_1 = this;
+                    // Check for Out & Leave
+                    for (var _i = 0, _a = this._previousIntersectionList; _i < _a.length; _i++) {
+                        var prev = _a[_i];
+                        _loop_1(prev);
+                    }
+                    var _loop_2 = function (actual) {
+                        if (!BABYLON.Tools.first(this_2._previousIntersectionList, function (pii) { return pii.prim === actual.prim; })) {
+                            this_2._primPointerInfo.updateRelatedTarget(actual.prim, actual.intersectionLocation);
+                            this_2._bubbleNotifyPrimPointerObserver(actual.prim, BABYLON.PrimitivePointerInfo.PointerOver, null);
+                        }
+                    };
+                    var this_2 = this;
+                    // Check for Over & Enter
+                    for (var _b = 0, _c = this._actualIntersectionList; _b < _c.length; _b++) {
+                        var actual = _c[_b];
+                        _loop_2(actual);
+                    }
                 }
             }
             this._hoverStatusRenderId = this.scene.getRenderId();
@@ -13418,54 +13524,39 @@ var BABYLON;
                 debug += "  ";
             }
             var pii = this._primPointerInfo;
-            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();
+            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() + ", relatedTarget: " + pii.relatedTarget.id;
             console.log(debug);
         };
         Canvas2D.prototype._bubbleNotifyPrimPointerObserver = function (prim, mask, eventData) {
             var ppi = this._primPointerInfo;
             var event = eventData ? eventData.event : null;
-            // In case of PointerOver/Out we will first notify the parent with PointerEnter/Leave
-            if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) !== 0) {
-                this._notifParents(prim, mask);
-            }
-            var bubbleCancelled = false;
             var cur = prim;
             while (cur && !cur.isDisposed) {
-                // Only trigger the observers if the primitive is intersected (except for out)
-                if (!bubbleCancelled) {
-                    this._updatePrimPointerPos(cur);
-                    // Exec the observers
-                    this._debugExecObserver(cur, mask);
-                    if (!cur._pointerEventObservable.notifyObservers(ppi, mask) && eventData instanceof BABYLON.PointerInfoPre) {
-                        eventData.skipOnPointerObservable = true;
-                        return false;
+                this._updatePrimPointerPos(cur);
+                // For the first level we have to fire Enter or Leave for corresponding Over or Out
+                if (cur === prim) {
+                    // Fire the proper notification
+                    if (mask === BABYLON.PrimitivePointerInfo.PointerOver) {
+                        this._debugExecObserver(prim, BABYLON.PrimitivePointerInfo.PointerEnter);
+                        prim._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerEnter);
                     }
-                    this._triggerActionManager(cur, ppi, mask, event);
-                    // 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 (ppi.cancelBubble) {
-                        if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) === 0) {
-                            return false;
-                        }
-                        // We're dealing with PointerOver/Out, let's keep looping to fire PointerEnter/Leave, but not Over/Out anymore
-                        bubbleCancelled = true;
-                    }
-                }
-                // If bubble is cancel we didn't update the Primitive Pointer Pos yet, let's do it
-                if (bubbleCancelled) {
-                    this._updatePrimPointerPos(cur);
-                }
-                // NOTE TO MYSELF, this is commented right now because it doesn't seemed needed but I can't figure out why I put this code in the first place
-                //// Trigger a PointerEnter corresponding to the PointerOver
-                //if (mask === PrimitivePointerInfo.PointerOver) {
-                //    this._debugExecObserver(cur, PrimitivePointerInfo.PointerEnter);
-                //    cur._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerEnter);
-                //}
-                //// Trigger a PointerLeave corresponding to the PointerOut
-                //else if (mask === PrimitivePointerInfo.PointerOut) {
-                //    this._debugExecObserver(cur, PrimitivePointerInfo.PointerLeave);
-                //    cur._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerLeave);
-                //}
+                    else if (mask === BABYLON.PrimitivePointerInfo.PointerOut) {
+                        this._debugExecObserver(prim, BABYLON.PrimitivePointerInfo.PointerLeave);
+                        prim._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerLeave);
+                    }
+                }
+                // Exec the observers
+                this._debugExecObserver(cur, mask);
+                if (!cur._pointerEventObservable.notifyObservers(ppi, mask) && eventData instanceof BABYLON.PointerInfoPre) {
+                    eventData.skipOnPointerObservable = true;
+                    return false;
+                }
+                this._triggerActionManager(cur, ppi, mask, event);
+                // 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 (ppi.cancelBubble) {
+                    return false;
+                }
                 // Loop to the parent
                 cur = cur.parent;
             }
@@ -13551,23 +13642,6 @@ var BABYLON;
                 }
             }
         };
-        Canvas2D.prototype._notifParents = function (prim, mask) {
-            var pii = this._primPointerInfo;
-            var curPrim = this;
-            while (curPrim) {
-                this._updatePrimPointerPos(curPrim);
-                // Fire the proper notification
-                if (mask === BABYLON.PrimitivePointerInfo.PointerOver) {
-                    this._debugExecObserver(curPrim, BABYLON.PrimitivePointerInfo.PointerEnter);
-                    curPrim._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerEnter);
-                }
-                else if (mask === BABYLON.PrimitivePointerInfo.PointerOut) {
-                    this._debugExecObserver(curPrim, BABYLON.PrimitivePointerInfo.PointerLeave);
-                    curPrim._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerLeave);
-                }
-                curPrim = curPrim.parent;
-            }
-        };
         /**
          * Don't forget to call the dispose method when you're done with the Canvas instance.
          * But don't worry, if you dispose its scene, the canvas will be automatically disposed too.
@@ -13831,7 +13905,10 @@ var BABYLON;
              * Return
              */
             get: function () {
-                return this._actualOverPrimitive ? this._actualOverPrimitive.prim : null;
+                if (this._actualIntersectionList && this._actualIntersectionList.length > 0) {
+                    return this._actualIntersectionList[0].prim;
+                }
+                return null;
             },
             enumerable: true,
             configurable: true
@@ -13955,10 +14032,13 @@ var BABYLON;
             this._updateGlobalTransformCounter.addCount(count, false);
         };
         Canvas2D.prototype._updateTrackedNodes = function () {
+            // Get the used camera
             var cam = this.scene.cameraToUseForPointers || this.scene.activeCamera;
+            // Compute some matrix stuff
             cam.getViewMatrix().multiplyToRef(cam.getProjectionMatrix(), Canvas2D_1._m);
             var rh = this.engine.getRenderHeight();
             var v = cam.viewport.toGlobal(this.engine.getRenderWidth(), rh);
+            // Compute the screen position of each group that track a given scene node
             for (var _i = 0, _a = this._trackedGroups; _i < _a.length; _i++) {
                 var group = _a[_i];
                 if (group.isDisposed) {
@@ -13973,6 +14053,36 @@ var BABYLON;
                 group.x = Math.round(proj.x / s);
                 group.y = Math.round((rh - proj.y) / s);
             }
+            // If it's a WorldSpaceCanvas and it's tracking a node, let's update the WSC transformation data
+            if (this._trackNode) {
+                var rot = null;
+                var scale = null;
+                var worldmtx = this._trackNode.getWorldMatrix();
+                var pos = worldmtx.getTranslation().add(this._trackNodeOffset);
+                var wsc = this;
+                var wsn = wsc.worldSpaceCanvasNode;
+                if (this._trackNodeBillboard) {
+                    var viewMtx = cam.getViewMatrix().clone().invert();
+                    viewMtx.decompose(Canvas2D_1.tS, Canvas2D_1.tR, Canvas2D_1.tT);
+                    rot = Canvas2D_1.tR.clone();
+                }
+                worldmtx.decompose(Canvas2D_1.tS, Canvas2D_1.tR, Canvas2D_1.tT);
+                var mtx = BABYLON.Matrix.Compose(Canvas2D_1.tS, Canvas2D_1.tR, BABYLON.Vector3.Zero());
+                pos = worldmtx.getTranslation().add(BABYLON.Vector3.TransformCoordinates(this._trackNodeOffset, mtx));
+                if (Canvas2D_1.tS.lengthSquared() !== 1) {
+                    scale = Canvas2D_1.tS.clone();
+                }
+                if (!this._trackNodeBillboard) {
+                    rot = Canvas2D_1.tR.clone();
+                }
+                if (wsn instanceof BABYLON.AbstractMesh) {
+                    wsn.position = pos;
+                    wsn.rotationQuaternion = rot;
+                    if (scale) {
+                        wsn.scaling = scale;
+                    }
+                }
+            }
         };
         /**
          * Call this method change you want to have layout related data computed and up to date (layout area, primitive area, local/global transformation matrices)
@@ -14204,6 +14314,9 @@ var BABYLON;
             if (group._isFlagSet(BABYLON.SmartPropertyPrim.flagTrackedGroup)) {
                 return;
             }
+            if (!this._trackedGroups) {
+                this._trackedGroups = new Array();
+            }
             this._trackedGroups.push(group);
             group._setFlags(BABYLON.SmartPropertyPrim.flagTrackedGroup);
         };
@@ -14348,6 +14461,9 @@ var BABYLON;
     Canvas2D._v = BABYLON.Vector3.Zero(); // Must stay zero
     Canvas2D._m = BABYLON.Matrix.Identity();
     Canvas2D._mI = BABYLON.Matrix.Identity(); // Must stay identity
+    Canvas2D.tS = BABYLON.Vector3.Zero();
+    Canvas2D.tT = BABYLON.Vector3.Zero();
+    Canvas2D.tR = BABYLON.Quaternion.Identity();
     /**
      * Define the default size used for both the width and height of a MapTexture to allocate.
      * Note that some MapTexture might be bigger than this size if the first node to allocate is bigger in width or height
@@ -14367,10 +14483,14 @@ var BABYLON;
          * @param scene the Scene that owns the Canvas
          * @param size the dimension of the Canvas in World Space
          * @param settings a combination of settings, possible ones are
-         *  - children: an array of direct children primitives
-         *  - id: a text identifier, for information purpose only, default is null.
-         *  - worldPosition the position of the Canvas in World Space, default is [0,0,0]
-         *  - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - children: an array of direct children primitives
+         * - id: a text identifier, for information purpose only, default is null.
+         * - unitScaleFactor: if specified the created canvas will be with a width of size.width*unitScaleFactor and a height of size.height.unitScaleFactor. If not specified, the unit of 1 is used. You can use this setting when you're dealing with a 3D world with small coordinates and you need a Canvas having bigger coordinates (typically to display text with better quality).
+         * - worldPosition the position of the Canvas in World Space, default is [0,0,0]
+         * - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - trackNode: if you want the WorldSpaceCanvas to track the position/rotation/scale of a given Scene Node, use this setting to specify the Node to track
+         * - trackNodeOffset: if you use trackNode you may want to specify a 3D Offset to apply to shift the Canvas
+         * - trackNodeBillboard: if true the WorldSpaceCanvas will always face the screen
          * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
          * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
          * - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is false (the opposite of ScreenSpace).
@@ -14392,7 +14512,12 @@ var BABYLON;
             BABYLON.Prim2DBase._isCanvasInit = true;
             var s = settings;
             s.isScreenSpace = false;
-            s.size = size.clone();
+            if (settings.unitScaleFactor != null) {
+                s.size = size.multiplyByFloats(settings.unitScaleFactor, settings.unitScaleFactor);
+            }
+            else {
+                s.size = size.clone();
+            }
             settings.cachingStrategy = (settings.cachingStrategy == null) ? Canvas2D.CACHESTRATEGY_CANVAS : settings.cachingStrategy;
             if (settings.cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
                 throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
@@ -14404,12 +14529,22 @@ var BABYLON;
             //if (cachingStrategy === Canvas2D.CACHESTRATEGY_DONTCACHE) {
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //}
+            if (settings.trackNode != null) {
+                _this._trackNode = settings.trackNode;
+                _this._trackNodeOffset = (settings.trackNodeOffset != null) ? settings.trackNodeOffset : BABYLON.Vector3.Zero();
+                _this._trackNodeBillboard = (settings.trackNodeBillboard != null) ? settings.trackNodeBillboard : false;
+            }
+            else {
+                _this._trackNode = null;
+                _this._trackNodeOffset = null;
+                _this._trackNodeBillboard = false;
+            }
             var createWorldSpaceNode = !settings || (settings.customWorldSpaceNode == null);
             _this._customWorldSpaceNode = !createWorldSpaceNode;
             var id = settings ? settings.id || null : null;
             // Set the max size of texture allowed for the adaptive render of the world space canvas cached bitmap
             var capMaxTextSize = _this.engine.getCaps().maxRenderTextureSize;
-            var defaultTextSize = (Math.min(capMaxTextSize, 1024)); // Default is 4K if allowed otherwise the max allowed
+            var defaultTextSize = (Math.min(capMaxTextSize, 1024)); // Default is 1K if allowed otherwise the max allowed
             if (settings.maxAdaptiveCanvasSize == null) {
                 _this._maxAdaptiveWorldSpaceCanvasSize = defaultTextSize;
             }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 10 - 10
dist/preview release/canvas2D/babylon.canvas2d.min.js


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

@@ -25,7 +25,7 @@
 #### Minor Updates
  - Fix Rotation issue when the Parent's Primitive hadn't a identity scale. ([nockawa](https://github.com/nockawa))
  - Primitive's position computed from TrackedNode are now hidden when the node is out of the Viewing Frustum ([nockawa](https://github.com/nockawa))
-
+ - WorldSpaceCanvas: TrackNode feature, a WSC can follow a Scene Node with an optional billboarding feature (always facing the camera)[Demo](http://babylonjs-playground.com/#1KYG17#1)
 
 ### Exporters
     

+ 29 - 11
src/Animations/babylon.animatable.ts

@@ -102,23 +102,41 @@
             var idx = this._scene._activeAnimatables.indexOf(this);
 
             if (idx > -1) {
-                var animations = this._animations;
-                var numberOfAnimationsStopped = 0;
-                for (var index = animations.length - 1; index >= 0; index--) {
-                    if (typeof animationName === "string" && animations[index].name != animationName) {
-                        continue;
+
+                if(animationName){
+
+                    var animations = this._animations;
+                    var numberOfAnimationsStopped = 0;
+                    for (var index = animations.length - 1; index >= 0; index--) {
+                        if (typeof animationName === "string" && animations[index].name != animationName) {
+                            continue;
+                        }
+                        animations[index].reset();
+                        animations.splice(index, 1);
+                        numberOfAnimationsStopped ++;
+                    }
+
+                    if (animations.length == numberOfAnimationsStopped) {
+                        this._scene._activeAnimatables.splice(idx, 1);
+
+                        if (this.onAnimationEnd) {
+                            this.onAnimationEnd();
+                        }
                     }
-                    animations[index].reset();
-                    animations.splice(index, 1);
-                    numberOfAnimationsStopped ++;
-                }
 
-                if (animations.length == numberOfAnimationsStopped) {
-                    this._scene._activeAnimatables.splice(idx, 1);
+                } else {
+
+                    this._scene._activeAnimatables.splice(index, 1);
+                    var animations = this._animations;
+
+                    for (var index = 0; index < animations.length; index++) {
+                        animations[index].reset();
+                    }
 
                     if (this.onAnimationEnd) {
                         this.onAnimationEnd();
                     }
+
                 }
             }
         }