Browse Source

Canvas2D: Z-Order feature

Z-Order is now correctly distributed and recomputed if needed

At the creation of a Primitive you can set the childrenFlatZOrder setting to specify that all children will share the same ZOrder
nockawa 9 năm trước cách đây
mục cha
commit
2bd664b53b

+ 4 - 3
src/Canvas2d/babylon.canvas2d.ts

@@ -131,8 +131,9 @@
             this._capturedPointers = new StringDictionary<Prim2DBase>();
             this._pickStartingPosition = Vector2.Zero();
             this._hierarchyLevelMaxSiblingCount = 50;
-            this._hierarchyDepthOffset = 0;
-            this._siblingDepthOffset = 1 / this._hierarchyLevelMaxSiblingCount;
+            this._hierarchyDepth = 0;
+            this._zOrder = 0;
+            this._zMax = 1;
             this._scene = scene;
             this._engine = engine;
             this._renderingSize = new Size(0, 0);
@@ -225,7 +226,7 @@
             this._isScreenSpace = (settings.isScreenSpace == null) ? true : settings.isScreenSpace;
         }
 
-        public static hierarchyLevelMaxSiblingCount: number = 50;
+        public static _zMinDelta: number = 1 / (Math.pow(2, 24) - 1);
 
         private _setupInteraction(enable: boolean) {
             // No change detection

+ 15 - 11
src/Canvas2d/babylon.ellipse2d.ts

@@ -219,17 +219,20 @@
         /**
          * Create an Ellipse 2D Shape primitive
          * @param settings a combination of settings, possible ones are
-         *  - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
-         *  - children: an array of direct children 
-         *  - id: a text identifier, for information purpose
-         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
-         *  - rotation: the initial rotation (in radian) of the primitive. default is 0
-         *  - scale: the initial scale of the primitive. default is 1
-         *  - origin: define the normalized origin point location, default [0.5;0.5]
-         *  - size: the size of the group. Alternatively the width and height properties can be set. Default will be [10;10].
-         *  - subdivision: the number of subdivision to create the ellipse perimeter, default is 64.
-         *  - fill: the brush used to draw the fill content of the ellipse, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white. can also be a string value (see Canvas2D.GetBrushFromString)
-         *  - border: the brush used to draw the border of the ellipse, you can set null to draw nothing (but you will have to set a fill brush), default is null. can be a string value (see Canvas2D.GetBrushFromString)
+         * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
+         * - children: an array of direct children 
+         * - id: a text identifier, for information purpose
+         * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
+         * - rotation: the initial rotation (in radian) of the primitive. default is 0
+         * - scale: the initial scale of the primitive. default is 1
+         * - origin: define the normalized origin point location, default [0.5;0.5]
+         * - size: the size of the group. Alternatively the width and height properties can be set. Default will be [10;10].
+         * - subdivision: the number of subdivision to create the ellipse perimeter, default is 64.
+         * - fill: the brush used to draw the fill content of the ellipse, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white. can also be a string value (see Canvas2D.GetBrushFromString)
+         * - border: the brush used to draw the border of the ellipse, you can set null to draw nothing (but you will have to set a fill brush), default is null. can be a string value (see Canvas2D.GetBrushFromString)
+         * - borderThickness: the thickness of the drawn border, default is 1.
+         * - isVisible: true if the group must be visible, false for hidden. Default is true.
+         * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -263,6 +266,7 @@
             border            ?: IBrush2D | string,
             borderThickness   ?: number,
             isVisible         ?: boolean,
+            childrenFlatZOrder?: boolean,
             marginTop         ?: number | string,
             marginLeft        ?: number | string,
             marginRight       ?: number | string,

+ 39 - 37
src/Canvas2d/babylon.group2d.ts

@@ -29,17 +29,18 @@
         /**
          * Create an Logical or Renderable Group.
          * @param settings a combination of settings, possible ones are
-         *  - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
-         *  - children: an array of direct children
-         *  - id a text identifier, for information purpose
-         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
-         *  - rotation: the initial rotation (in radian) of the primitive. default is 0
-         *  - scale: the initial scale of the primitive. default is 1
-         *  - origin: define the normalized origin point location, default [0.5;0.5]
-         *  - size: the size of the group. Alternatively the width and height properties can be set. If null the size will be computed from its content, default is null.
+         * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
+         * - children: an array of direct children
+         * - id a text identifier, for information purpose
+         * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
+         * - rotation: the initial rotation (in radian) of the primitive. default is 0
+         * - scale: the initial scale of the primitive. default is 1
+         * - origin: define the normalized origin point location, default [0.5;0.5]
+         * - size: the size of the group. Alternatively the width and height properties can be set. If null the size will be computed from its content, default is null.
          *  - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
-         *  - layoutEngine: either an instance of a layout engine based class (StackPanel.Vertical, StackPanel.Horizontal) or a string ('canvas' for Canvas layout, 'StackPanel' or 'HorizontalStackPanel' for horizontal Stack Panel layout, 'VerticalStackPanel' for vertical Stack Panel layout).
-         *  - isVisible: true if the group must be visible, false for hidden. Default is true.
+         * - layoutEngine: either an instance of a layout engine based class (StackPanel.Vertical, StackPanel.Horizontal) or a string ('canvas' for Canvas layout, 'StackPanel' or 'HorizontalStackPanel' for horizontal Stack Panel layout, 'VerticalStackPanel' for vertical Stack Panel layout).
+         * - isVisible: true if the group must be visible, false for hidden. Default is true.
+         * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED! 
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -56,33 +57,34 @@
          */
         constructor(settings?: {
 
-            parent          ?: Prim2DBase,
-            children        ?: Array<Prim2DBase>,
-            id              ?: string,
-            position        ?: Vector2,
-            x               ?: number,
-            y               ?: number,
-            trackNode       ?: Node,
-            origin          ?: Vector2,
-            size            ?: Size,
-            width           ?: number,
-            height          ?: number,
-            cacheBehavior   ?: number,
-            layoutEngine    ?: LayoutEngineBase | string,
-            isVisible       ?: boolean,
-            marginTop       ?: number | string,
-            marginLeft      ?: number | string,
-            marginRight     ?: number | string,
-            marginBottom    ?: number | string,
-            margin          ?: number | string,
-            marginHAlignment?: number,
-            marginVAlignment?: number,
-            marginAlignment ?: string,
-            paddingTop      ?: number | string,
-            paddingLeft     ?: number | string,
-            paddingRight    ?: number | string,
-            paddingBottom   ?: number | string,
-            padding         ?: string,
+            parent            ?: Prim2DBase,
+            children          ?: Array<Prim2DBase>,
+            id                ?: string,
+            position          ?: Vector2,
+            x                 ?: number,
+            y                 ?: number,
+            trackNode         ?: Node,
+            origin            ?: Vector2,
+            size              ?: Size,
+            width             ?: number,
+            height            ?: number,
+            cacheBehavior     ?: number,
+            layoutEngine      ?: LayoutEngineBase | string,
+            isVisible         ?: boolean,
+            childrenFlatZOrder?: boolean,
+            marginTop         ?: number | string,
+            marginLeft        ?: number | string,
+            marginRight       ?: number | string,
+            marginBottom      ?: number | string,
+            margin            ?: number | string,
+            marginHAlignment  ?: number,
+            marginVAlignment  ?: number,
+            marginAlignment   ?: string,
+            paddingTop        ?: number | string,
+            paddingLeft       ?: number | string,
+            paddingRight      ?: number | string,
+            paddingBottom     ?: number | string,
+            padding           ?: string,
 
         }) {
             if (settings == null) {

+ 60 - 58
src/Canvas2d/babylon.lines2d.ts

@@ -385,67 +385,69 @@
          * Create an 2D Lines Shape primitive. The defined lines may be opened or closed (see below)
          * @param points an array that describe the points to use to draw the line, must contain at least two entries.
          * @param settings a combination of settings, possible ones are
-         *  - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
-         *  - children: an array of direct children
-         *  - id a text identifier, for information purpose
-         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
-         *  - rotation: the initial rotation (in radian) of the primitive. default is 0
-         *  - scale: the initial scale of the primitive. default is 1
-         *  - origin: define the normalized origin point location, default [0.5;0.5]
-         *  - fillThickness: the thickness of the fill part of the line, can be null to draw nothing (but a border brush must be given), default is 1.
-         *  - closed: if false the lines are said to be opened, the first point and the latest DON'T connect. if true the lines are said to be closed, the first and last point will be connected by a line. For instance you can define the 4 points of a rectangle, if you set closed to true a 4 edges rectangle will be drawn. If you set false, only three edges will be drawn, the edge formed by the first and last point won't exist. Default is false.
-         *  - startCap: Draw a cap of the given type at the start of the first line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
-         *  - endCap: Draw a cap of the given type at the end of the last line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
-         *  - fill: the brush used to draw the fill content of the lines, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white. can be a string value (see Canvas2D.GetBrushFromString)
-         *  - border: the brush used to draw the border of the lines, you can set null to draw nothing (but you will have to set a fill brush), default is null. can be a string value (see Canvas2D.GetBrushFromString)
-         *  - borderThickness: the thickness of the drawn border, default is 1.
-         *  - isVisible: true if the primitive must be visible, false for hidden. Default is true.
-         *  - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
-         *  - marginHAlignment: one value of the PrimitiveAlignment type's static properties
-         *  - marginVAlignment: one value of the PrimitiveAlignment type's static properties
-         *  - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
-         *  - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
+         * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
+         * - children: an array of direct children
+         * - id a text identifier, for information purpose
+         * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
+         * - rotation: the initial rotation (in radian) of the primitive. default is 0
+         * - scale: the initial scale of the primitive. default is 1
+         * - origin: define the normalized origin point location, default [0.5;0.5]
+         * - fillThickness: the thickness of the fill part of the line, can be null to draw nothing (but a border brush must be given), default is 1.
+         * - closed: if false the lines are said to be opened, the first point and the latest DON'T connect. if true the lines are said to be closed, the first and last point will be connected by a line. For instance you can define the 4 points of a rectangle, if you set closed to true a 4 edges rectangle will be drawn. If you set false, only three edges will be drawn, the edge formed by the first and last point won't exist. Default is false.
+         * - startCap: Draw a cap of the given type at the start of the first line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
+         * - endCap: Draw a cap of the given type at the end of the last line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
+         * - fill: the brush used to draw the fill content of the lines, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white. can be a string value (see Canvas2D.GetBrushFromString)
+         * - border: the brush used to draw the border of the lines, you can set null to draw nothing (but you will have to set a fill brush), default is null. can be a string value (see Canvas2D.GetBrushFromString)
+         * - borderThickness: the thickness of the drawn border, default is 1.
+         * - isVisible: true if the primitive must be visible, false for hidden. Default is true.
+         * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
+         * - marginHAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginVAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
+         * - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
          */
         constructor(points: Vector2[], settings?: {
 
-            parent           ?: Prim2DBase, 
-            children         ?: Array<Prim2DBase>,
-            id               ?: string,
-            position         ?: Vector2,
-            x                ?: number,
-            y                ?: number,
-            rotation         ?: number,
-            scale            ?: number,
-            origin           ?: Vector2,
-            fillThickness    ?: number,
-            closed           ?: boolean,
-            startCap         ?: number,
-            endCap           ?: number,
-            fill             ?: IBrush2D | string,
-            border           ?: IBrush2D | string,
-            borderThickness  ?: number,
-            isVisible        ?: boolean,
-            marginTop        ?: number | string,
-            marginLeft       ?: number | string,
-            marginRight      ?: number | string,
-            marginBottom     ?: number | string,
-            margin           ?: number | string,
-            marginHAlignment ?: number,
-            marginVAlignment ?: number,
-            marginAlignment  ?: string,
-            paddingTop       ?: number | string,
-            paddingLeft      ?: number | string,
-            paddingRight     ?: number | string,
-            paddingBottom    ?: number | string,
-            padding          ?: string,
+            parent            ?: Prim2DBase, 
+            children          ?: Array<Prim2DBase>,
+            id                ?: string,
+            position          ?: Vector2,
+            x                 ?: number,
+            y                 ?: number,
+            rotation          ?: number,
+            scale             ?: number,
+            origin            ?: Vector2,
+            fillThickness     ?: number,
+            closed            ?: boolean,
+            startCap          ?: number,
+            endCap            ?: number,
+            fill              ?: IBrush2D | string,
+            border            ?: IBrush2D | string,
+            borderThickness   ?: number,
+            isVisible         ?: boolean,
+            childrenFlatZOrder?: boolean,
+            marginTop         ?: number | string,
+            marginLeft        ?: number | string,
+            marginRight       ?: number | string,
+            marginBottom      ?: number | string,
+            margin            ?: number | string,
+            marginHAlignment  ?: number,
+            marginVAlignment  ?: number,
+            marginAlignment   ?: string,
+            paddingTop        ?: number | string,
+            paddingLeft       ?: number | string,
+            paddingRight      ?: number | string,
+            paddingBottom     ?: number | string,
+            padding           ?: string,
         }) {
 
             if (!settings) {

+ 230 - 29
src/Canvas2d/babylon.prim2dBase.ts

@@ -1280,6 +1280,7 @@
      */
     export class Prim2DBase extends SmartPropertyPrim {
         static PRIM2DBASE_PROPCOUNT: number = 15;
+        private static _bigInt = Math.pow(2, 30);
 
         constructor(settings: {
             parent?: Prim2DBase,
@@ -1293,6 +1294,7 @@
             origin?: Vector2,
             layoutEngine?: LayoutEngineBase | string,
             isVisible?: boolean,
+            childrenFlatZOrder?: boolean,
             marginTop?: number | string,
             marginLeft?: number | string,
             marginRight?: number | string,
@@ -1351,7 +1353,6 @@
             this._lastAutoSizeArea = Size.Zero();
             this._contentArea = new Size(null, null);
             this._pointerEventObservable = new Observable<PrimitivePointerInfo>();
-            this._siblingDepthOffset = this._hierarchyDepthOffset = 0;
             this._boundingInfo = new BoundingInfo2D();
             this._owner = owner;
             this._parent = null;
@@ -1366,10 +1367,18 @@
             this._invGlobalTransform = null;
             this._globalTransformProcessStep = 0;
             this._globalTransformStep = 0;
-            this._hierarchyDepth = 0;
             this._renderGroup = null;
+            this._primLinearPosition = 0;
+            this._manualZOrder = null;
+            this._zOrder = 0;
+            this._zMax = 0;
+            this._firstZDirtyIndex = Prim2DBase._bigInt;
             this._setFlags(SmartPropertyPrim.flagIsPickable | SmartPropertyPrim.flagBoundingInfoDirty);
 
+            if (settings.childrenFlatZOrder) {
+                this._setFlags(SmartPropertyPrim.flagChildrenFlatZOrder);
+            }
+
             // If the parent is given, initialize the hierarchy/owner related data
             if (parent != null) {
                 parent.addChild(this);
@@ -1833,7 +1842,13 @@
         }
 
         public get actualZOffset(): number {
-            return this._zOrder || (1 - this._hierarchyDepthOffset);
+            if (this._manualZOrder!=null) {
+                return this._manualZOrder;
+            }
+            if (this._isFlagSet(SmartPropertyPrim.flagZOrderDirty)) {
+                this._updateZOrder();
+            }
+            return (1 - this._zOrder);
         }
 
         /**
@@ -1921,12 +1936,23 @@
          * You can override the default Z Order through this property, but most of the time the default behavior is acceptable
          */
         public get zOrder(): number {
-            return this._zOrder;
+            return this._manualZOrder;
         }
 
         public set zOrder(value: number) {
-            this._zOrder = value;
+            if (this._manualZOrder === value) {
+                return;
+            }
+
+            this._manualZOrder = value;
             this.onZOrderChanged();
+            if (this._actualZOrderChangedObservable && this._actualZOrderChangedObservable.hasObservers()) {
+                this._actualZOrderChangedObservable.notifyObservers(value);
+            }
+        }
+
+        public get isManualZOrder(): boolean {
+            return this._manualZOrder != null;
         }
 
         @dynamicLevelProperty(9, pi => Prim2DBase.marginProperty = pi)
@@ -2165,6 +2191,13 @@
             return this._pointerEventObservable;
         }
 
+        public get zActualOrderChangedObservable(): Observable<number> {
+            if (!this._actualZOrderChangedObservable) {
+                this._actualZOrderChangedObservable = new Observable<number>();
+            }
+            return this._actualZOrderChangedObservable;
+        }
+
         public findById(id: string): Prim2DBase {
             if (this._id === id) {
                 return this;
@@ -2281,29 +2314,30 @@
                 return false;
             }
 
-            let prevOffset: number, nextOffset: number;
             let childIndex = this._children.indexOf(child);
             let prevIndex = previous ? this._children.indexOf(previous) : -1;
 
-            // Move to first position
-            if (!previous) {
-                prevOffset = 1;
-                nextOffset = this._children[1]._siblingDepthOffset;
-            } else {
-                prevOffset = this._children[prevIndex]._siblingDepthOffset;
-                nextOffset = this._children[prevIndex + 1]._siblingDepthOffset;
+            if (!this._isFlagSet(SmartPropertyPrim.flagChildrenFlatZOrder)) {
+                this._setFlags(SmartPropertyPrim.flagZOrderDirty);
+                this._firstZDirtyIndex = Math.min(this._firstZDirtyIndex, prevIndex+1);
             }
 
-            child._siblingDepthOffset = (nextOffset - prevOffset) / 2;
-
             this._children.splice(prevIndex + 1, 0, this._children.splice(childIndex, 1)[0]);
         }
 
         private addChild(child: Prim2DBase) {
             child._parent = this;
             this._boundingBoxDirty();
-            this._children.push(child);
-            this._patchHierarchyDepth(child);
+            let flat = this._isFlagSet(SmartPropertyPrim.flagChildrenFlatZOrder);
+            if (flat) {
+                child._setFlags(SmartPropertyPrim.flagChildrenFlatZOrder);
+                child._setZOrder(this._zOrder, true);
+                child._zMax = this._zOrder;
+            } else {
+                this._setFlags(SmartPropertyPrim.flagZOrderDirty);
+            }
+            let length = this._children.push(child);
+            this._firstZDirtyIndex = Math.min(this._firstZDirtyIndex, length - 1);
         }
 
         /**
@@ -2445,7 +2479,9 @@
         private _updateLocalTransform(): boolean {
             let tflags = Prim2DBase.actualPositionProperty.flagId | Prim2DBase.rotationProperty.flagId | Prim2DBase.scaleProperty.flagId | Prim2DBase.originProperty.flagId;
             if (this.checkPropertiesDirty(tflags)) {
-                this.owner.addupdateLocalTransformCounter(1);
+                if (this.owner) {
+                    this.owner.addupdateLocalTransformCounter(1);
+                }
 
                 var rot = Quaternion.RotationAxis(new Vector3(0, 0, 1), this._rotation);
                 var local: Matrix;
@@ -2488,10 +2524,15 @@
             this.owner.addCachedGroupRenderCounter(1);
             
             // Check if the parent is synced
-            if (this._parent && ((this._parent._globalTransformProcessStep !== this.owner._globalTransformProcessStep) || this._parent._areSomeFlagsSet(SmartPropertyPrim.flagLayoutDirty | SmartPropertyPrim.flagPositioningDirty))) {
+            if (this._parent && ((this._parent._globalTransformProcessStep !== this.owner._globalTransformProcessStep) || this._parent._areSomeFlagsSet(SmartPropertyPrim.flagLayoutDirty | SmartPropertyPrim.flagPositioningDirty | SmartPropertyPrim.flagZOrderDirty))) {
                 this._parent.updateCachedStates(false);
             }
 
+            // Update Z-Order if needed
+            if (this._isFlagSet(SmartPropertyPrim.flagZOrderDirty)) {
+                this._updateZOrder();
+            }
+
             // Update actualSize only if there' not positioning to recompute and the size changed
             // Otherwise positioning will take care of it.
             let sizeDirty = this.checkPropertiesDirty(Prim2DBase.sizeProperty.flagId);
@@ -2597,7 +2638,9 @@
         private static _size = Size.Zero();
 
         private _updatePositioning() {
-            this.owner.addUpdatePositioningCounter(1);
+            if (this.owner) {
+                this.owner.addUpdatePositioningCounter(1);
+            }
 
             // From this point we assume that the primitive layoutArea is computed and up to date.
             // We know have to :
@@ -2707,15 +2750,170 @@
 
             // Recurse
             for (let child of this._children) {
-                this._patchHierarchyDepth(child);
+                child._hierarchyDepth = this._hierarchyDepth + 1;
                 child._patchHierarchy(owner);
             }
         }
+        private static _zOrderChangedNotifList = new Array<Prim2DBase>();
+        private static _zRebuildReentrency = false;
+
+        private _updateZOrder() {
+            let prevLinPos = this._primLinearPosition;
+            let startI = 0;
+            let startZ = this._zOrder;
+
+            // We must start rebuilding Z-Order from the Prim before the first one that changed, because we know its Z-Order is correct, so are its children, but it's better to recompute everything from this point instead of finding the last valid children
+            let childrenCount = this._children.length;
+            if (this._firstZDirtyIndex > 0) {
+                if ((this._firstZDirtyIndex - 1) < childrenCount) {
+                    let prevPrim = this._children[this._firstZDirtyIndex - 1];
+                    prevLinPos = prevPrim._primLinearPosition;
+                    startI = this._firstZDirtyIndex - 1;
+                    startZ = prevPrim._zOrder;
+                }
+            }
+
+            let startPos = prevLinPos;
+
+            // Update the linear position of the primitive from the first one to the last inside this primitive, compute the total number of prim traversed
+            Prim2DBase._totalCount = 0;
+            for (let i = startI; i < childrenCount; i++) {
+                let child = this._children[i];
+                prevLinPos = child._updatePrimitiveLinearPosition(prevLinPos);
+            }
+
+            // Compute the new Z-Order for all the primitives
+            // Add 20% to the current total count to reserve space for future insertions, except if we're rebuilding due to a zMinDelta reached
+            let zDelta = (this._zMax - startZ) / (Prim2DBase._totalCount * (Prim2DBase._zRebuildReentrency ? 1 : 1.2));
+
+            // If the computed delta is less than the smallest allowed by the depth buffer, we rebuild the Z-Order from the very beginning of the primitive's children (that is, the first) to redistribute uniformly the Z.
+            if (zDelta < Canvas2D._zMinDelta) {
+                // Check for re-entrance, if the flag is true we already attempted a rebuild but couldn't get a better zDelta, go up in the hierarchy to rebuilt one level up, hoping to get this time a decent delta, otherwise, recurse until we got it or when no parent is reached, which would mean the canvas would have more than 16 millions of primitives...
+                if (Prim2DBase._zRebuildReentrency) {
+                    let p = this._parent;
+                    if (p == null) {
+                        // Can't find a good Z delta and we're in the canvas, which mean we're dealing with too many objects (which should never happen, but well...)
+                        console.log(`Can't compute Z-Order for ${this.id}'s children, zDelta is too small, Z-Order is now in an unstable state`);
+                        Prim2DBase._zRebuildReentrency = false;
+                        return;
+                    }
+                    p._firstZDirtyIndex = 0;
+                    return p._updateZOrder();
+                }
+                Prim2DBase._zRebuildReentrency = true;
+                this._firstZDirtyIndex = 0;
+                this._updateZOrder();
+                Prim2DBase._zRebuildReentrency = false;
+            }
+
+            for (let i = startI; i < childrenCount; i++) {
+                let child = this._children[i];
+                child._updatePrimitiveZOrder(startPos, startZ, zDelta);
+            }
+
+            // Notify the Observers that we found during the Z change (we do it after to avoid any kind of re-entrance)
+            for (let p of Prim2DBase._zOrderChangedNotifList) {
+                p._actualZOrderChangedObservable.notifyObservers(p.actualZOffset);
+            }
+            Prim2DBase._zOrderChangedNotifList.splice(0);
+
+            this._firstZDirtyIndex = Prim2DBase._bigInt;
+            this._clearFlags(SmartPropertyPrim.flagZOrderDirty);
+        }
+
+        private static _totalCount: number = 0;
+
+        private _updatePrimitiveLinearPosition(prevLinPos: number): number {
+            if (this.isManualZOrder) {
+                return prevLinPos;
+            }
+
+            this._primLinearPosition = ++prevLinPos;
+            Prim2DBase._totalCount++;
+
+            // Check for the FlatZOrder, which means the children won't have a dedicated Z-Order but will all share the same (unique) one.
+            if (!this._isFlagSet(SmartPropertyPrim.flagChildrenFlatZOrder)) {
+                for (let child of this._children) {
+                    prevLinPos = child._updatePrimitiveLinearPosition(prevLinPos);
+                }
+            }
+
+            return prevLinPos;
+        }
+
+        private _updatePrimitiveZOrder(startPos: number, startZ: number, deltaZ: number): number {
+            if (this.isManualZOrder) {
+                return null;
+            }
+
+            let newZ = startZ + ((this._primLinearPosition - startPos) * deltaZ);
+            let isFlat = this._isFlagSet(SmartPropertyPrim.flagChildrenFlatZOrder);
+            this._setZOrder(newZ, false);
+
+
+            if (this._isFlagSet(SmartPropertyPrim.flagZOrderDirty)) {
+                this._firstZDirtyIndex = Prim2DBase._bigInt;
+                this._clearFlags(SmartPropertyPrim.flagZOrderDirty);
+            }
+
+            let curZ: number = newZ;
+
+            // Check for the FlatZOrder, which means the children won't have a dedicated Z-Order but will all share the same (unique) one.
+            if (isFlat) {
+                if (this._children.length > 0) {
+                    //let childrenZOrder = startZ + ((this._children[0]._primLinearPosition - startPos) * deltaZ);
+                    for (let child of this._children) {
+                        child._updatePrimitiveFlatZOrder(this._zOrder);
+                    }
+                }
+            } else {
+                for (let child of this._children) {
+                    let r = child._updatePrimitiveZOrder(startPos, startZ, deltaZ);
+                    if (r != null) {
+                        curZ = r;
+                    }
+                }
+            }
+
+            this._zMax = isFlat ? newZ : (curZ + deltaZ);
+
+            return curZ;
+        }
 
-        private _patchHierarchyDepth(child: Prim2DBase) {
-            child._hierarchyDepth = this._hierarchyDepth + 1;
-            child._hierarchyDepthOffset = this._hierarchyDepthOffset + ((this._children.indexOf(child) + 1) * this._siblingDepthOffset);
-            child._siblingDepthOffset = this._siblingDepthOffset / Canvas2D.hierarchyLevelMaxSiblingCount;
+        private _updatePrimitiveFlatZOrder(newZ: number) {
+            if (this.isManualZOrder) {
+                return;
+            }
+
+            this._setZOrder(newZ, false);
+            this._zMax = newZ;
+
+            if (this._isFlagSet(SmartPropertyPrim.flagZOrderDirty)) {
+                this._firstZDirtyIndex = Prim2DBase._bigInt;
+                this._clearFlags(SmartPropertyPrim.flagZOrderDirty);
+            }
+
+            for (let child of this._children) {
+                child._updatePrimitiveFlatZOrder(newZ);
+            }
+
+        }
+
+        private _setZOrder(newZ: number, directEmit: boolean) {
+            if (newZ !== this._zOrder) {
+                this._zOrder = newZ;
+                if (!this.isDirty) {
+                    this.onPrimBecomesDirty();
+                }
+                this.onZOrderChanged();
+                if (this._actualZOrderChangedObservable && this._actualZOrderChangedObservable.hasObservers()) {
+                    if (directEmit) {
+                        this._actualZOrderChangedObservable.notifyObservers(newZ);
+                    } else {
+                        Prim2DBase._zOrderChangedNotifList.push(this);
+                    }
+                }
+            }
         }
 
         /**
@@ -2745,14 +2943,17 @@
         private _actionManager: ActionManager;
         protected _children: Array<Prim2DBase>;
         private _renderGroup: Group2D;
-        private _hierarchyDepth: number;
-        protected _hierarchyDepthOffset: number;
-        protected _siblingDepthOffset: number;
-        private _zOrder: number;
+        protected _hierarchyDepth: number;
+        protected _zOrder: number;
+        private _manualZOrder: number;
+        protected _zMax: number;
+        private _firstZDirtyIndex: number;
+        private _primLinearPosition: number;
         private _margin: PrimitiveThickness;
         private _padding: PrimitiveThickness;
         private _marginAlignment: PrimitiveAlignment;
         public _pointerEventObservable: Observable<PrimitivePointerInfo>;
+        private _actualZOrderChangedObservable: Observable<number>;
         private _id: string;
         private _position: Vector2;
         private _actualPosition: Vector2;

+ 59 - 57
src/Canvas2d/babylon.rectangle2d.ts

@@ -303,64 +303,66 @@
         /**
          * Create an Rectangle 2D Shape primitive. May be a sharp rectangle (with sharp corners), or a rounded one.
          * @param settings a combination of settings, possible ones are
-         *  - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
-         *  - children: an array of direct children
-         *  - id a text identifier, for information purpose
-         *  - position: the X & Y positions relative to its parent. Alternatively the x and y settings can be set. Default is [0;0]
-         *  - rotation: the initial rotation (in radian) of the primitive. default is 0
-         *  - scale: the initial scale of the primitive. default is 1
-         *  - origin: define the normalized origin point location, default [0.5;0.5]
-         *  - size: the size of the group. Alternatively the width and height settings can be set. Default will be [10;10].
-         *  - roundRadius: if the rectangle has rounded corner, set their radius, default is 0 (to get a sharp edges rectangle).
-         *  - fill: the brush used to draw the fill content of the rectangle, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white. can also be a string value (see Canvas2D.GetBrushFromString)
-         *  - border: the brush used to draw the border of the rectangle, you can set null to draw nothing (but you will have to set a fill brush), default is null. can also be a string value (see Canvas2D.GetBrushFromString)
-         *  - borderThickness: the thickness of the drawn border, default is 1.
-         *  - isVisible: true if the primitive must be visible, false for hidden. Default is true.
-         *  - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
-         *  - marginHAlignment: one value of the PrimitiveAlignment type's static properties
-         *  - marginVAlignment: one value of the PrimitiveAlignment type's static properties
-         *  - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
-         *  - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
+         * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
+         * - children: an array of direct children
+         * - id a text identifier, for information purpose
+         * - position: the X & Y positions relative to its parent. Alternatively the x and y settings can be set. Default is [0;0]
+         * - rotation: the initial rotation (in radian) of the primitive. default is 0
+         * - scale: the initial scale of the primitive. default is 1
+         * - origin: define the normalized origin point location, default [0.5;0.5]
+         * - size: the size of the group. Alternatively the width and height settings can be set. Default will be [10;10].
+         * - roundRadius: if the rectangle has rounded corner, set their radius, default is 0 (to get a sharp edges rectangle).
+         * - fill: the brush used to draw the fill content of the rectangle, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white. can also be a string value (see Canvas2D.GetBrushFromString)
+         * - border: the brush used to draw the border of the rectangle, you can set null to draw nothing (but you will have to set a fill brush), default is null. can also be a string value (see Canvas2D.GetBrushFromString)
+         * - borderThickness: the thickness of the drawn border, default is 1.
+         * - isVisible: true if the primitive must be visible, false for hidden. Default is true.
+         * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
+         * - marginHAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginVAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
+         * - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
          */
-        constructor(settings ?: {
-            parent           ?: Prim2DBase, 
-            children         ?: Array<Prim2DBase>,
-            id               ?: string,
-            position         ?: Vector2,
-            x                ?: number,
-            y                ?: number,
-            rotation         ?: number,
-            scale            ?: number,
-            origin           ?: Vector2,
-            size             ?: Size,
-            width            ?: number,
-            height           ?: number,
-            roundRadius      ?: number,
-            fill             ?: IBrush2D | string,
-            border           ?: IBrush2D | string,
-            borderThickness  ?: number,
-            isVisible        ?: boolean,
-            marginTop        ?: number | string,
-            marginLeft       ?: number | string,
-            marginRight      ?: number | string,
-            marginBottom     ?: number | string,
-            margin           ?: number | string,
-            marginHAlignment ?: number,
-            marginVAlignment ?: number,
-            marginAlignment  ?: string,
-            paddingTop       ?: number | string,
-            paddingLeft      ?: number | string,
-            paddingRight     ?: number | string,
-            paddingBottom    ?: number | string,
-            padding          ?: string,
+        constructor(settings  ?: {
+            parent            ?: Prim2DBase, 
+            children          ?: Array<Prim2DBase>,
+            id                ?: string,
+            position          ?: Vector2,
+            x                 ?: number,
+            y                 ?: number,
+            rotation          ?: number,
+            scale             ?: number,
+            origin            ?: Vector2,
+            size              ?: Size,
+            width             ?: number,
+            height            ?: number,
+            roundRadius       ?: number,
+            fill              ?: IBrush2D | string,
+            border            ?: IBrush2D | string,
+            borderThickness   ?: number,
+            isVisible         ?: boolean,
+            childrenFlatZOrder?: boolean,
+            marginTop         ?: number | string,
+            marginLeft        ?: number | string,
+            marginRight       ?: number | string,
+            marginBottom      ?: number | string,
+            margin            ?: number | string,
+            marginHAlignment  ?: number,
+            marginVAlignment  ?: number,
+            marginAlignment   ?: string,
+            paddingTop        ?: number | string,
+            paddingLeft       ?: number | string,
+            paddingRight      ?: number | string,
+            paddingBottom     ?: number | string,
+            padding           ?: string,
         }) {
 
             // Avoid checking every time if the object exists

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

@@ -626,6 +626,8 @@
         public static flagPositioningDirty       = 0x0000200;    // set if the primitive positioning must be computed
         public static flagTrackedGroup           = 0x0000400;    // set if the group2D is tracking a scene node
         public static flagWorldCacheChanged      = 0x0000800;    // set if the cached bitmap of a world space canvas changed
+        public static flagChildrenFlatZOrder     = 0x0001000;    // set if all the children (direct and indirect) will share the same Z-Order
+        public static flagZOrderDirty            = 0x0002000;    // set if the Z-Order for this prim and its children must be recomputed
 
         private   _flags             : number;
         private   _externalData      : StringDictionary<Object>;

+ 54 - 52
src/Canvas2d/babylon.sprite2d.ts

@@ -233,61 +233,63 @@
          * Create an 2D Sprite primitive
          * @param texture the texture that stores the sprite to render
          * @param settings a combination of settings, possible ones are
-         *  - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
-         *  - children: an array of direct children
-         *  - id a text identifier, for information purpose
-         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
-         *  - rotation: the initial rotation (in radian) of the primitive. default is 0
-         *  - scale: the initial scale of the primitive. default is 1
-         *  - origin: define the normalized origin point location, default [0.5;0.5]
-         *  - spriteSize: the size of the sprite (in pixels), if null the size of the given texture will be used, default is null.
-         *  - spriteLocation: the location (in pixels) in the texture of the top/left corner of the Sprite to display, default is null (0,0)
-         *  - invertY: if true the texture Y will be inverted, default is false.
-         *  - alignToPixel: if true the sprite's texels will be aligned to the rendering viewport pixels, ensuring the best rendering quality but slow animations won't be done as smooth as if you set false. If false a texel could lies between two pixels, being blended by the texture sampling mode you choose, the rendering result won't be as good, but very slow animation will be overall better looking. Default is true: content will be aligned.
-         *  - isVisible: true if the sprite must be visible, false for hidden. Default is true.
-         *  - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
-         *  - marginHAlignment: one value of the PrimitiveAlignment type's static properties
-         *  - marginVAlignment: one value of the PrimitiveAlignment type's static properties
-         *  - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
-         *  - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
+         * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
+         * - children: an array of direct children
+         * - id a text identifier, for information purpose
+         * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
+         * - rotation: the initial rotation (in radian) of the primitive. default is 0
+         * - scale: the initial scale of the primitive. default is 1
+         * - origin: define the normalized origin point location, default [0.5;0.5]
+         * - spriteSize: the size of the sprite (in pixels), if null the size of the given texture will be used, default is null.
+         * - spriteLocation: the location (in pixels) in the texture of the top/left corner of the Sprite to display, default is null (0,0)
+         * - invertY: if true the texture Y will be inverted, default is false.
+         * - alignToPixel: if true the sprite's texels will be aligned to the rendering viewport pixels, ensuring the best rendering quality but slow animations won't be done as smooth as if you set false. If false a texel could lies between two pixels, being blended by the texture sampling mode you choose, the rendering result won't be as good, but very slow animation will be overall better looking. Default is true: content will be aligned.
+         * - isVisible: true if the sprite must be visible, false for hidden. Default is true.
+         * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
+         * - marginHAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginVAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
+         * - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
          */
         constructor(texture: Texture, settings?: {
 
-            parent           ?: Prim2DBase, 
-            children         ?: Array<Prim2DBase>,
-            id               ?: string,
-            position         ?: Vector2,
-            x                ?: number,
-            y                ?: number,
-            rotation         ?: number,
-            scale            ?: number,
-            origin           ?: Vector2,
-            spriteSize       ?: Size,
-            spriteLocation   ?: Vector2,
-            invertY          ?: boolean,
-            alignToPixel     ?: boolean,
-            isVisible        ?: boolean,
-            marginTop        ?: number | string,
-            marginLeft       ?: number | string,
-            marginRight      ?: number | string,
-            marginBottom     ?: number | string,
-            margin           ?: number | string,
-            marginHAlignment ?: number,
-            marginVAlignment ?: number,
-            marginAlignment  ?: string,
-            paddingTop       ?: number | string,
-            paddingLeft      ?: number | string,
-            paddingRight     ?: number | string,
-            paddingBottom    ?: number | string,
-            padding          ?: string,
+            parent            ?: Prim2DBase, 
+            children          ?: Array<Prim2DBase>,
+            id                ?: string,
+            position          ?: Vector2,
+            x                 ?: number,
+            y                 ?: number,
+            rotation          ?: number,
+            scale             ?: number,
+            origin            ?: Vector2,
+            spriteSize        ?: Size,
+            spriteLocation    ?: Vector2,
+            invertY           ?: boolean,
+            alignToPixel      ?: boolean,
+            isVisible         ?: boolean,
+            childrenFlatZOrder?: boolean,
+            marginTop         ?: number | string,
+            marginLeft        ?: number | string,
+            marginRight       ?: number | string,
+            marginBottom      ?: number | string,
+            margin            ?: number | string,
+            marginHAlignment  ?: number,
+            marginVAlignment  ?: number,
+            marginAlignment   ?: string,
+            paddingTop        ?: number | string,
+            paddingLeft       ?: number | string,
+            paddingRight      ?: number | string,
+            paddingBottom     ?: number | string,
+            padding           ?: string,
         }) {
 
             if (!settings) {

+ 56 - 54
src/Canvas2d/babylon.text2d.ts

@@ -258,63 +258,65 @@
          * Create a Text primitive
          * @param text the text to display
          * @param settings a combination of settings, possible ones are
-         *  - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
-         *  - children: an array of direct children
-         *  - id a text identifier, for information purpose
-         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
-         *  - rotation: the initial rotation (in radian) of the primitive. default is 0
-         *  - scale: the initial scale of the primitive. default is 1
-         *  - origin: define the normalized origin point location, default [0.5;0.5]
-         *  - fontName: the name/size/style of the font to use, following the CSS notation. Default is "12pt Arial".
-         *  - fontSuperSample: if true the text will be rendered with a superSampled font (the font is twice the given size). Use this settings if the text lies in world space or if it's scaled in.
-         *  - defaultColor: the color by default to apply on each letter of the text to display, default is plain white.
-         *  - areaSize: the size of the area in which to display the text, default is auto-fit from text content.
-         *  - tabulationSize: number of space character to insert when a tabulation is encountered, default is 4
-         *  - isVisible: true if the text must be visible, false for hidden. Default is true.
-         *  - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
-         *  - marginHAlignment: one value of the PrimitiveAlignment type's static properties
-         *  - marginVAlignment: one value of the PrimitiveAlignment type's static properties
-         *  - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
-         *  - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
-         *  - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
+         * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
+         * - children: an array of direct children
+         * - id a text identifier, for information purpose
+         * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
+         * - rotation: the initial rotation (in radian) of the primitive. default is 0
+         * - scale: the initial scale of the primitive. default is 1
+         * - origin: define the normalized origin point location, default [0.5;0.5]
+         * - fontName: the name/size/style of the font to use, following the CSS notation. Default is "12pt Arial".
+         * - fontSuperSample: if true the text will be rendered with a superSampled font (the font is twice the given size). Use this settings if the text lies in world space or if it's scaled in.
+         * - defaultColor: the color by default to apply on each letter of the text to display, default is plain white.
+         * - areaSize: the size of the area in which to display the text, default is auto-fit from text content.
+         * - tabulationSize: number of space character to insert when a tabulation is encountered, default is 4
+         * - isVisible: true if the text must be visible, false for hidden. Default is true.
+         * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
+         * - marginHAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginVAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
+         * - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
          */
         constructor(text: string, settings?: {
 
-            parent           ?: Prim2DBase, 
-            children         ?: Array<Prim2DBase>,
-            id               ?: string,
-            position         ?: Vector2,
-            x                ?: number,
-            y                ?: number,
-            rotation         ?: number,
-            scale            ?: number,
-            origin           ?: Vector2,
-            fontName         ?: string,
-            fontSuperSample  ?: boolean,
-            defaultFontColor ?: Color4,
-            size             ?: Size,
-            tabulationSize   ?: number,
-            isVisible        ?: boolean,
-            marginTop        ?: number | string,
-            marginLeft       ?: number | string,
-            marginRight      ?: number | string,
-            marginBottom     ?: number | string,
-            margin           ?: number | string,
-            marginHAlignment ?: number,
-            marginVAlignment ?: number,
-            marginAlignment  ?: string,
-            paddingTop       ?: number | string,
-            paddingLeft      ?: number | string,
-            paddingRight     ?: number | string,
-            paddingBottom    ?: number | string,
-            padding          ?: string,
+            parent            ?: Prim2DBase, 
+            children          ?: Array<Prim2DBase>,
+            id                ?: string,
+            position          ?: Vector2,
+            x                 ?: number,
+            y                 ?: number,
+            rotation          ?: number,
+            scale             ?: number,
+            origin            ?: Vector2,
+            fontName          ?: string,
+            fontSuperSample   ?: boolean,
+            defaultFontColor  ?: Color4,
+            size              ?: Size,
+            tabulationSize    ?: number,
+            isVisible         ?: boolean,
+            childrenFlatZOrder?: boolean,
+            marginTop         ?: number | string,
+            marginLeft        ?: number | string,
+            marginRight       ?: number | string,
+            marginBottom      ?: number | string,
+            margin            ?: number | string,
+            marginHAlignment  ?: number,
+            marginVAlignment  ?: number,
+            marginAlignment   ?: string,
+            paddingTop        ?: number | string,
+            paddingLeft       ?: number | string,
+            paddingRight      ?: number | string,
+            paddingBottom     ?: number | string,
+            padding           ?: string,
         }) {
 
             if (!settings) {