Bladeren bron

Canvas2D: Opacity added + bug fixes

Primitives have now an opacity property to set its opacity. The actualOpacity property gives the result of the accumulated opacity through the parent hierarchy.

Bug fixes:
 - Profiling Canvas is now disposed and can be created only once per Canvas
 - actualPosition were sharing the same PropertyID than position...
nockawa 9 jaren geleden
bovenliggende
commit
3bb7c89192

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

@@ -13,6 +13,10 @@
 - PerfCounter class added to monitor time/counter and expose min/max/average/lastSecondAverage/current metrics. Updated engine/scene current counter to use this class, exposing new properties as well to access the PerfCounter object ([nockawa](https://github.com/nockawa))
 - Better keyboard event handling which is now done at canvas level and not at window level ([deltakosh](https://github.com/deltakosh)) 
 - New `scene.hoverCursor` property to define a custom cursor when moving mouse over meshes ([deltakosh](https://github.com/deltakosh)) 
+- Canvas2D:
+ - Performance metrics added
+ - Text2D super sampling to enhance quality in World Space Canvas
+ - World Space Canvas is now rendering in an adaptive way for its resolution to fit the on screen projected one to achieve a good rendering quality
 
 ### Exporters
     
@@ -24,9 +28,20 @@
 - Fixed cross vector calculation in `_computeHeightQuads()` that affected  all the `GroundMesh.getHeightAtCoordinates()` and `GroundMesh.getNormalAtCoordinates()` methods ([jerome](https://github.com/jbousquie))
 - Fixed `Mesh.CreateDashedLines()` missing `instance` parameter on update ([jerome](https://github.com/jbousquie))
 - Fixed model shape initial red vertex color set to zero not formerly being taken in account in the `SolidParticleSystem` ([jerome](https://github.com/jbousquie))
-- Canvas2D:
- - `Sprite2D`: texture size is now set by default as expected
- - `Sprite2D`: can have no `id` set
- - `ZOrder` fixed in Primitives created inline
+- Canvas2D: ([nockawa](https://github.com/nockawa))
+ - `WorldSpaceCanvas2D`:
+	- Intersection/interaction now works on non squared canvas
+ - Primitive:
+	- `ZOrder` fixed in Primitives created inline
+	- Z-Order is now correctly distributed along the whole canvas object graph
+ - `Sprite2D`: 
+	- texture size is now set by default as expected
+	- can have no `id` set
+ - `Text2D`: 
+	- Fix bad rendering quality on Chrome
+
 ### Breaking changes
+ - Canvas2D: ([nockawa](https://github.com/nockawa))
+  - `WorldSpaceCanvas2D`:
+	- WorldSpaceRenderScale is no longer supported (deprecated because of adaptive feature added).
 

+ 11 - 1
src/Canvas2d/babylon.canvas2d.ts

@@ -751,6 +751,11 @@
                 return false;
             }
 
+            if (this._profilingCanvas) {
+                this._profilingCanvas.dispose();
+                this._profilingCanvas = null;
+            }
+
             if (this.interactionEnabled) {
                 this._setupInteraction(false);
             }
@@ -931,6 +936,10 @@
         }
 
         public createCanvasProfileInfoCanvas(): Canvas2D {
+            if (this._profilingCanvas) {
+                return this._profilingCanvas;
+            }
+
             let canvas = new ScreenSpaceCanvas2D(this.scene, {
                 id: "ProfileInfoCanvas", cachingStrategy: Canvas2D.CACHESTRATEGY_DONTCACHE, children:
                 [
@@ -945,7 +954,7 @@
             });
 
             this._profileInfoText = <Text2D>canvas.findById("ProfileInfoText");
-
+            this._profilingCanvas = canvas;
             return canvas;
         }
 
@@ -1102,6 +1111,7 @@
         private _updateLocalTransformCounter : PerfCounter;
         private _boundingInfoRecomputeCounter: PerfCounter;
 
+        private _profilingCanvas: Canvas2D;
         private _profileInfoText: Text2D;
 
         protected onPrimBecomesDirty() {

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

@@ -225,6 +225,7 @@
          * - 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
+         * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - 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.
@@ -257,6 +258,7 @@
             y                 ?: number,
             rotation          ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             size              ?: Size,
             width             ?: number,

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

@@ -35,6 +35,7 @@
          * - 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
+         * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - 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
@@ -64,6 +65,7 @@
             x                 ?: number,
             y                 ?: number,
             trackNode         ?: Node,
+            opacity           ?: number,
             origin            ?: Vector2,
             size              ?: Size,
             width             ?: number,

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

@@ -391,6 +391,7 @@
          * - 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
+         * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - 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.
@@ -425,6 +426,7 @@
             y                 ?: number,
             rotation          ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             fillThickness     ?: number,
             closed            ?: boolean,

+ 79 - 12
src/Canvas2d/babylon.prim2dBase.ts

@@ -1291,6 +1291,7 @@
             y?: number,
             rotation?: number,
             scale?: number,
+            opacity?: number,
             origin?: Vector2,
             layoutEngine?: LayoutEngineBase | string,
             isVisible?: boolean,
@@ -1373,7 +1374,13 @@
             this._zOrder = 0;
             this._zMax = 0;
             this._firstZDirtyIndex = Prim2DBase._bigInt;
-            this._setFlags(SmartPropertyPrim.flagIsPickable | SmartPropertyPrim.flagBoundingInfoDirty);
+            this._setFlags(SmartPropertyPrim.flagIsPickable | SmartPropertyPrim.flagBoundingInfoDirty | SmartPropertyPrim.flagActualOpacityDirty);
+
+            if (settings.opacity != null) {
+                this._opacity = settings.opacity;
+            } else {
+                this._opacity = 1;
+            }
 
             if (settings.childrenFlatZOrder) {
                 this._setFlags(SmartPropertyPrim.flagChildrenFlatZOrder);
@@ -1598,6 +1605,12 @@
          */
         public static marginAlignmentProperty: Prim2DPropInfo;
 
+        /**
+         * Metadata of the opacity property
+         */
+        public static opacityProperty: Prim2DPropInfo;
+
+
         @instanceLevelProperty(1, pi => Prim2DBase.actualPositionProperty = pi, false, false, true)
         /**
          * Return the position where the primitive is rendered in the Canvas, this position may be different than the one returned by the position property due to layout/alignment/margin/padding computing
@@ -1642,7 +1655,7 @@
          * Use this property to set a new Vector2 object, otherwise to change only the x/y use Prim2DBase.x or y properties.
          * Setting this property may have no effect is specific alignment are in effect.
          */
-        @dynamicLevelProperty(1, pi => Prim2DBase.positionProperty = pi, false, false, true)
+        @dynamicLevelProperty(2, pi => Prim2DBase.positionProperty = pi, false, false, true)
         public get position(): Vector2 {
             return this._position || Prim2DBase._nullPosition;
         }
@@ -1652,6 +1665,7 @@
                 return;
             }
             this._position = value;
+            this.markAsDirty("actualPosition");
         }
 
         /**
@@ -1679,6 +1693,7 @@
 
             this._position.x = value;
             this.markAsDirty("position");
+            this.markAsDirty("actualPosition");
         }
 
         /**
@@ -1706,6 +1721,7 @@
 
             this._position.y = value;
             this.markAsDirty("position");
+            this.markAsDirty("actualPosition");
         }
 
         private static boundinbBoxReentrency = false;
@@ -1716,7 +1732,7 @@
          * BEWARE: if you change only size.width or height it won't trigger a property change and you won't have the expected behavior.
          * Use this property to set a new Size object, otherwise to change only the width/height use Prim2DBase.width or height properties.
          */
-        @dynamicLevelProperty(2, pi => Prim2DBase.sizeProperty = pi, false, true)
+        @dynamicLevelProperty(3, pi => Prim2DBase.sizeProperty = pi, false, true)
         public get size(): Size {
 
             if (!this._size || this._size.width == null || this._size.height == null) {
@@ -1796,7 +1812,7 @@
             this._positioningDirty();
         }
 
-        @instanceLevelProperty(3, pi => Prim2DBase.rotationProperty = pi, false, true)
+        @instanceLevelProperty(4, pi => Prim2DBase.rotationProperty = pi, false, true)
         /**
          * Rotation of the primitive, in radian, along the Z axis
          */
@@ -1808,7 +1824,7 @@
             this._rotation = value;
         }
 
-        @instanceLevelProperty(4, pi => Prim2DBase.scaleProperty = pi, false, true)
+        @instanceLevelProperty(5, pi => Prim2DBase.scaleProperty = pi, false, true)
         /**
          * Uniform scale applied on the primitive
          */
@@ -1896,7 +1912,7 @@
          * 0,1 means the center is top/left
          * @returns The normalized center.
          */
-        @dynamicLevelProperty(5, pi => Prim2DBase.originProperty = pi, false, true)
+        @dynamicLevelProperty(6, pi => Prim2DBase.originProperty = pi, false, true)
         public get origin(): Vector2 {
             return this._origin;
         }
@@ -1905,7 +1921,7 @@
             this._origin = value;
         }
 
-        @dynamicLevelProperty(6, pi => Prim2DBase.levelVisibleProperty = pi)
+        @dynamicLevelProperty(7, pi => Prim2DBase.levelVisibleProperty = pi)
         /**
          * Let the user defines if the Primitive is hidden or not at its level. As Primitives inherit the hidden status from their parent, only the isVisible property give properly the real visible state.
          * Default is true, setting to false will hide this primitive and its children.
@@ -1918,7 +1934,7 @@
             this._changeFlags(SmartPropertyPrim.flagLevelVisible, value);
         }
 
-        @instanceLevelProperty(7, pi => Prim2DBase.isVisibleProperty = pi)
+        @instanceLevelProperty(8, pi => Prim2DBase.isVisibleProperty = pi)
         /**
          * Use ONLY THE GETTER to determine if the primitive is visible or not.
          * The Setter is for internal purpose only!
@@ -1931,7 +1947,7 @@
             this._changeFlags(SmartPropertyPrim.flagIsVisible, value);
         }
 
-        @instanceLevelProperty(8, pi => Prim2DBase.zOrderProperty = pi)
+        @instanceLevelProperty(9, pi => Prim2DBase.zOrderProperty = pi)
         /**
          * You can override the default Z Order through this property, but most of the time the default behavior is acceptable
          */
@@ -1955,7 +1971,7 @@
             return this._manualZOrder != null;
         }
 
-        @dynamicLevelProperty(9, pi => Prim2DBase.marginProperty = pi)
+        @dynamicLevelProperty(10, pi => Prim2DBase.marginProperty = pi)
         /**
          * You can get/set a margin on the primitive through this property
          * @returns the margin object, if there was none, a default one is created and returned
@@ -1976,7 +1992,7 @@
             return (this._margin !== null) || (this._marginAlignment !== null);
         }
 
-        @dynamicLevelProperty(10, pi => Prim2DBase.paddingProperty = pi)
+        @dynamicLevelProperty(11, pi => Prim2DBase.paddingProperty = pi)
         /**
          * You can get/set a margin on the primitive through this property
          * @returns the margin object, if there was none, a default one is created and returned
@@ -1997,7 +2013,7 @@
             return this._padding !== null;
         }
 
-        @dynamicLevelProperty(11, pi => Prim2DBase.marginAlignmentProperty = pi)
+        @dynamicLevelProperty(12, pi => Prim2DBase.marginAlignmentProperty = pi)
         /**
          * You can get/set the margin alignment through this property
          */
@@ -2008,6 +2024,45 @@
             return this._marginAlignment;
         }
 
+        @instanceLevelProperty(13, pi => Prim2DBase.opacityProperty = pi)
+        /**
+         * Get/set the opacity of the whole primitive
+         */
+        public get opacity(): number {
+            return this._opacity;
+        }
+
+        public set opacity(value: number) {
+            if (value < 0) {
+                value = 0;
+            } else if (value > 1) {
+                value = 1;
+            }
+
+            if (this._opacity === value) {
+                return;
+            }
+
+            this._opacity = value;
+            this._updateRenderMode();
+            this._spreadActualOpacityChanged();
+        }
+
+        public get actualOpacity(): number {
+            if (this._isFlagSet(SmartPropertyPrim.flagActualOpacityDirty)) {
+                let cur = this.parent;
+                let op = this.opacity;
+                while (cur) {
+                    op *= cur.opacity;
+                    cur = cur.parent;
+                }
+
+                this._actualOpacity = op;
+                this._clearFlags(SmartPropertyPrim.flagActualOpacityDirty);
+            }
+            return this._actualOpacity;
+        }
+
         /**
          * Get/set the layout engine to use for this primitive.
          * The default layout engine is the CanvasLayoutEngine.
@@ -2467,6 +2522,13 @@
             this._setFlags(SmartPropertyPrim.flagPositioningDirty);
         }
 
+        protected _spreadActualOpacityChanged() {
+            for (let child of this._children) {
+                child._setFlags(SmartPropertyPrim.flagActualOpacityDirty);
+                child._spreadActualOpacityChanged();
+            }
+        }
+
         private _changeLayoutEngine(engine: LayoutEngineBase) {
             this._layoutEngine = engine;
         }
@@ -2916,6 +2978,9 @@
             }
         }
 
+        protected _updateRenderMode() {
+        }
+
         /**
          * This method is used to alter the contentArea of the Primitive before margin is applied.
          * In most of the case you won't need to override this method, but it can prove some usefulness, check the Rectangle2D class for a concrete application.
@@ -2975,6 +3040,8 @@
         private _rotation: number;
         private _scale: number;
         private _origin: Vector2;
+        protected _opacity: number;
+        private _actualOpacity: number;
 
         // Stores the step of the parent for which the current global transform was computed
         // If the parent has a new step, it means this prim's global transform must be updated

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

@@ -309,6 +309,7 @@
          * - 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
+         * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - 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).
@@ -340,6 +341,7 @@
             y                 ?: number,
             rotation          ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             size              ?: Size,
             width             ?: number,

+ 82 - 11
src/Canvas2d/babylon.renderablePrim2d.ts

@@ -252,6 +252,7 @@
             this.id = partId;
             this.curElement = 0;
             this.dataElementCount = dataElementCount;
+            this.renderMode = 0;
         }
 
         id: number;
@@ -272,6 +273,11 @@
             return null;
         }
 
+        @instanceData()
+        get opacity(): number {
+            return null;
+        }
+
         getClassTreeInfo(): ClassTreeInfo<InstanceClassInfo, InstancePropInfo> {
             if (!this.typeInfo) {
                 this.typeInfo = ClassTreeInfo.get<InstanceClassInfo, InstancePropInfo>(Object.getPrototypeOf(this));
@@ -315,6 +321,7 @@
         }
 
         curElement: number;
+        renderMode: number;
         dataElements: DynamicFloatArrayElementInfo[];
         dataBuffer: DynamicFloatArray;
         typeInfo: ClassTreeInfo<InstanceClassInfo, InstancePropInfo>;
@@ -345,7 +352,11 @@
         }
 
         public set isAlphaTest(value: boolean) {
+            if (this._isAlphaTest === value) {
+                return;
+            }
             this._isAlphaTest = value;
+            this._updateRenderMode();
         }
 
         @dynamicLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, pi => RenderablePrim2D.isTransparentProperty = pi)
@@ -354,11 +365,19 @@
          * The setter should be used only by implementers of new primitive type.
          */
         public get isTransparent(): boolean {
-            return this._isTransparent;
+            return this._isTransparent || (this._opacity<1);
         }
 
         public set isTransparent(value: boolean) {
+            if (this._isTransparent === value) {
+                return;
+            }
             this._isTransparent = value;
+            this._updateRenderMode();
+        }
+
+        public get renderMode(): number {
+            return this._renderMode;
         }
 
         constructor(settings?: {
@@ -368,6 +387,7 @@
             isVisible    ?: boolean,
         }) {
             super(settings);
+
             this._isTransparent            = false;
             this._isAlphaTest              = false;
             this._transparentPrimitiveInfo = null;
@@ -441,10 +461,6 @@
             // The last thing to do is check if the instanced related data must be updated because a InstanceLevel property had changed or the primitive visibility changed.
             if (this._isFlagSet(SmartPropertyPrim.flagVisibilityChanged) || context.forceRefreshPrimitive || newInstance || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep)) {
 
-                if (this.isTransparent) {
-                    //this.renderGroup._renderableData._transparentListChanged = true;
-                }
-
                 this._updateInstanceDataParts(gii);
             }
         }
@@ -498,13 +514,17 @@
             });
 
             // Get the GroupInfoDataPart corresponding to the render category of the part
+            let rm = 0;
             let gipd: GroupInfoPartData[] = null;
             if (this.isTransparent) {
                 gipd = gii.transparentData;
+                rm = Render2DContext.RenderModeTransparent;
             } else if (this.isAlphaTest) {
                 gipd = gii.alphaTestData;
+                rm = Render2DContext.RenderModeAlphaTest;
             } else {
                 gipd = gii.opaqueData;
+                rm = Render2DContext.RenderModeOpaque;
             }
 
             // For each instance data part of the primitive, allocate the instanced element it needs for render
@@ -512,6 +532,7 @@
                 let part = parts[i];
                 part.dataBuffer = gipd[i]._partData;
                 part.allocElements();
+                part.renderMode = rm;
             }
 
             // Add the instance data parts in the ModelRenderCache they belong, track them by storing their ID in the primitive in case we need to change the model later on, so we'll have to release the allocated instance data parts because they won't fit anymore
@@ -578,11 +599,49 @@
                 gii = this.renderGroup._renderableData._renderGroupInstancesInfo.get(this.modelKey);
             }
 
+            let isTransparent = this.isTransparent;
+            let isAlphaTest = this.isAlphaTest;
+            let wereTransparent = false;
+
+            // Check a render mode change
+            let rmChanged = false;
+            if (this._instanceDataParts.length>0) {
+                let firstPart = this._instanceDataParts[0];
+                let partRM = firstPart.renderMode;
+                let curRM = this.renderMode;
+
+                if (partRM !== curRM) {
+                    wereTransparent = partRM === Render2DContext.RenderModeTransparent;
+                    rmChanged = true;
+                    let gipd: TransparentGroupInfoPartData[];
+                    switch (curRM) {
+                        case Render2DContext.RenderModeTransparent:
+                            gipd = gii.transparentData;
+                            break;
+                        case Render2DContext.RenderModeAlphaTest:
+                            gipd = gii.alphaTestData;
+                            break;
+                        default:
+                            gipd = gii.opaqueData;
+                    }
+
+                    for (let i = 0; i < this._instanceDataParts.length; i++) {
+                        let part = this._instanceDataParts[i];
+                        part.freeElements();
+                        part.dataBuffer = gipd[i]._partData;
+                        part.renderMode = curRM;
+                    }
+
+                }
+            }
+
             // Handle changes related to ZOffset
-            if (this.isTransparent) {
+            let visChanged = this._isFlagSet(SmartPropertyPrim.flagVisibilityChanged);
+
+            if (isTransparent || wereTransparent) {
                 // Handle visibility change, which is also triggered when the primitive just got created
-                if (this._isFlagSet(SmartPropertyPrim.flagVisibilityChanged)) {
-                    if (this.isVisible) {
+                if (visChanged || rmChanged) {
+                    if (this.isVisible && !wereTransparent) {
                         if (!this._transparentPrimitiveInfo) {
                             // Add the primitive to the list of transparent ones in the group that render is
                             this._transparentPrimitiveInfo = this.renderGroup._renderableData.addNewTransparentPrimitiveInfo(this, gii);
@@ -600,7 +659,7 @@
             // For each Instance Data part, refresh it to update the data in the DynamicFloatArray
             for (let part of this._instanceDataParts) {
                 // Check if we need to allocate data elements (hidden prim which becomes visible again)
-                if (this._isFlagSet(SmartPropertyPrim.flagVisibilityChanged) && !part.dataElements) {
+                if ((visChanged && !part.dataElements) || rmChanged) {
                     part.allocElements();
                 }
 
@@ -617,9 +676,9 @@
             this._instanceDirtyFlags = 0;
 
             // Make the appropriate data dirty
-            if (this.isTransparent) {
+            if (isTransparent) {
                 gii.transparentDirty = true;
-            } else if (this.isAlphaTest) {
+            } else if (isAlphaTest) {
                 gii.alphaTestDirty = true;
             } else {
                 gii.opaqueDirty = true;
@@ -812,11 +871,22 @@
             let ty = new Vector4(t.m[1] * 2 / h, t.m[5] * 2 / h, 0/*t.m[9]*/, ((t.m[13] + offY) * 2 / h) - 1);
             part.transformX = tx;
             part.transformY = ty;
+            part.opacity = this.actualOpacity;
 
             // Stores zBias and it's inverse value because that's needed to compute the clip space W coordinate (which is 1/Z, so 1/zBias)
             part.zBias = new Vector2(zBias, invZBias);
         }
 
+        protected _updateRenderMode() {
+            if (this.isTransparent) {
+                this._renderMode = Render2DContext.RenderModeTransparent;
+            } else if (this.isAlphaTest) {
+                this._renderMode = Render2DContext.RenderModeAlphaTest;
+            } else {
+                this._renderMode = Render2DContext.RenderModeOpaque;
+            }
+        }
+
         private _modelRenderCache: ModelRenderCache;
         private _modelRenderInstanceID: string;
         private _transparentPrimitiveInfo: TransparentPrimitiveInfo;
@@ -824,6 +894,7 @@
         protected _instanceDataParts: InstanceDataBase[];
         protected _isAlphaTest: boolean;
         protected _isTransparent: boolean;
+        private _renderMode: number;
     }
 
 

+ 1 - 1
src/Canvas2d/babylon.shape2d.ts

@@ -171,7 +171,7 @@
         }
 
         private _updateTransparencyStatus() {
-            this.isTransparent = (this._border && this._border.isTransparent()) || (this._fill && this._fill.isTransparent());
+            this.isTransparent = (this._border && this._border.isTransparent()) || (this._fill && this._fill.isTransparent()) || (this.actualOpacity<1);
         }
 
         private _border: IBrush2D;

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

@@ -628,6 +628,7 @@
         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
+        public static flagActualOpacityDirty     = 0x0004000;    // set if the actualOpactity should be recomputed
 
         private   _flags             : number;
         private   _externalData      : StringDictionary<Object>;

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

@@ -21,14 +21,13 @@
             let canvas = instanceInfo.owner.owner;
             var engine = canvas.engine;
 
+            var cur = engine.getAlphaMode();
             let effect = context.useInstancing ? this.effectInstanced : this.effect;
 
             engine.enableEffect(effect);
             effect.setTexture("diffuseSampler", this.texture);
             engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, effect);
 
-            var cur = engine.getAlphaMode();
-
             if (context.renderMode !== Render2DContext.RenderModeOpaque) {
                 engine.setAlphaMode(Engine.ALPHA_COMBINE);
             }
@@ -239,6 +238,7 @@
          * - 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
+         * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - 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)
@@ -270,6 +270,7 @@
             y                 ?: number,
             rotation          ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             spriteSize        ?: Size,
             spriteLocation    ?: Vector2,
@@ -306,7 +307,7 @@
             this.spriteFrame = 0;
             this.invertY = (settings.invertY == null) ? false : settings.invertY;
             this.alignToPixel = (settings.alignToPixel == null) ? true : settings.alignToPixel;
-            this._isTransparent = true;
+            this.isAlphaTest = true;
 
             if (settings.spriteSize==null) {
                 var s = texture.getSize();

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

@@ -264,6 +264,7 @@
          * - 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
+         * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - 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.
@@ -296,6 +297,7 @@
             y                 ?: number,
             rotation          ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             fontName          ?: string,
             fontSuperSample   ?: boolean,

+ 2 - 0
src/Shaders/ellipse2d.vertex.fx

@@ -9,6 +9,7 @@ attribute float index;
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformY;
+att float opacity;
 
 #ifdef Border
 att float borderThickness;
@@ -98,6 +99,7 @@ void main(void) {
 	vColor = mix(borderGradientColor2, borderGradientColor1, v);	// As Y is inverted, Color2 first, then Color1
 #endif
 
+	vColor.a *= opacity;
 	vec4 pos;
 	pos.xy = pos2.xy * properties.xy;
 	pos.z = 1.0;

+ 2 - 0
src/Shaders/lines2d.vertex.fx

@@ -9,6 +9,7 @@ attribute vec2 position;
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformY;
+att float opacity;
 
 #ifdef FillSolid
 att vec4 fillSolidColor;
@@ -58,6 +59,7 @@ void main(void) {
 	vColor = mix(borderGradientColor2, borderGradientColor1, v);	// As Y is inverted, Color2 first, then Color1
 #endif
 
+	vColor.a *= opacity;
 	vec4 pos;
 	pos.xy = position.xy;
 	pos.z = 1.0;

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

@@ -9,6 +9,7 @@ attribute float index;
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformY;
+att float opacity;
 
 #ifdef Border
 att float borderThickness;
@@ -196,6 +197,7 @@ void main(void) {
 	vColor = mix(borderGradientColor2, borderGradientColor1, v);	// As Y is inverted, Color2 first, then Color1
 #endif
 
+	vColor.a *= opacity;
 	vec4 pos;
 	pos.xy = pos2.xy * properties.xy;
 	pos.z = 1.0;

+ 2 - 0
src/Shaders/sprite2d.fragment.fx

@@ -1,4 +1,5 @@
 varying vec2 vUV;
+varying float vOpacity;
 uniform sampler2D diffuseSampler;
 
 void main(void) {
@@ -6,5 +7,6 @@ void main(void) {
 	if (color.a == 0.05) {
 		discard;
 	}
+	color.a *= vOpacity;
 	gl_FragColor = color;
 }

+ 3 - 1
src/Shaders/sprite2d.vertex.fx

@@ -18,12 +18,13 @@ att vec3 properties;
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformY;
+att float opacity;
 
 // Uniforms
 
 // Output
 varying vec2 vUV;
-varying vec4 vColor;
+varying float vOpacity;
 
 void main(void) {
 
@@ -72,6 +73,7 @@ void main(void) {
 		pos.xy = pos2.xy * sizeUV * textureSize;
 	}
 
+	vOpacity = opacity;
 	pos.z = 1.0;
 	pos.w = 1.0;
 	gl_Position = vec4(dot(pos, transformX), dot(pos, transformY), zBias.x, 1);

+ 2 - 0
src/Shaders/text2d.vertex.fx

@@ -11,6 +11,7 @@ att vec2 zBias;
 
 att vec4 transformX;
 att vec4 transformY;
+att float opacity;
 
 att vec2 topLeftUV;
 att vec2 sizeUV;
@@ -54,6 +55,7 @@ void main(void) {
 	vUV = (floor(vUV*textureSize) + vec2(0.0, 0.0)) / textureSize;
 
 	vColor = color;
+	vColor.a *= opacity;
 	vec4 pos;
 	pos.xy = floor(pos2.xy * superSampleFactor * sizeUV * textureSize);	// Align on target pixel to avoid bad interpolation
 	pos.z = 1.0;