Prechádzať zdrojové kódy

Merge pull request #1235 from nockawa/TextTrans

PR #3 opacity + transparent segment with instanced array + text2d blend
David Catuhe 9 rokov pred
rodič
commit
3617c3a903

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

@@ -5,7 +5,7 @@
 ### Updates
 ### Updates
 - Added support for texture arrays ([deltakosh](https://github.com/deltakosh)) 
 - Added support for texture arrays ([deltakosh](https://github.com/deltakosh)) 
 - Added `camera.isInFrustum` and `camera.isCompletelyInFrustum`. Can be used with meshes, submeshes and boundingInfo ([deltakosh](https://github.com/deltakosh)) 
 - Added `camera.isInFrustum` and `camera.isCompletelyInFrustum`. Can be used with meshes, submeshes and boundingInfo ([deltakosh](https://github.com/deltakosh)) 
-- Several memory allocation reduction ([benaadams](https://github.com/benaadams)) 
+- Several memory allocation reduction ([benaadams](https://github.com/benaadams))
 - Several GPU state change reduction ([benaadams](https://github.com/benaadams)) 
 - Several GPU state change reduction ([benaadams](https://github.com/benaadams)) 
 - MapTexture: add `supersample` mode to double font quality. ([nockawa](https://github.com/nockawa))
 - MapTexture: add `supersample` mode to double font quality. ([nockawa](https://github.com/nockawa))
 - New `invertUV` parameter an all ribbon based shapes : ribbon, tube, lathe, basic and custom extrusion ([jerome](https://github.com/jbousquie))
 - New `invertUV` parameter an all ribbon based shapes : ribbon, tube, lathe, basic and custom extrusion ([jerome](https://github.com/jbousquie))
@@ -13,6 +13,11 @@
 - 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))
 - 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)) 
 - 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)) 
 - New `scene.hoverCursor` property to define a custom cursor when moving mouse over meshes ([deltakosh](https://github.com/deltakosh)) 
+- Canvas2D: ([nockawa](https://github.com/nockawa)) 
+ - 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
+ - Transparent Primitives are now drawn with Instanced Array when supported
 
 
 ### Exporters
 ### Exporters
     
     
@@ -24,9 +29,21 @@
 - Fixed cross vector calculation in `_computeHeightQuads()` that affected  all the `GroundMesh.getHeightAtCoordinates()` and `GroundMesh.getNormalAtCoordinates()` methods ([jerome](https://github.com/jbousquie))
 - 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 `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))
 - 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
+	- Rendering above transparent surface is now blending correctly
+
 ### Breaking changes
 ### Breaking changes
+ - Canvas2D: ([nockawa](https://github.com/nockawa))
+  - `WorldSpaceCanvas2D`:
+	- WorldSpaceRenderScale is no longer supported (deprecated because of adaptive feature added).
 
 

+ 15 - 5
src/Canvas2d/babylon.canvas2d.ts

@@ -751,6 +751,11 @@
                 return false;
                 return false;
             }
             }
 
 
+            if (this._profilingCanvas) {
+                this._profilingCanvas.dispose();
+                this._profilingCanvas = null;
+            }
+
             if (this.interactionEnabled) {
             if (this.interactionEnabled) {
                 this._setupInteraction(false);
                 this._setupInteraction(false);
             }
             }
@@ -931,11 +936,15 @@
         }
         }
 
 
         public createCanvasProfileInfoCanvas(): Canvas2D {
         public createCanvasProfileInfoCanvas(): Canvas2D {
+            if (this._profilingCanvas) {
+                return this._profilingCanvas;
+            }
+
             let canvas = new ScreenSpaceCanvas2D(this.scene, {
             let canvas = new ScreenSpaceCanvas2D(this.scene, {
                 id: "ProfileInfoCanvas", cachingStrategy: Canvas2D.CACHESTRATEGY_DONTCACHE, children:
                 id: "ProfileInfoCanvas", cachingStrategy: Canvas2D.CACHESTRATEGY_DONTCACHE, children:
                 [
                 [
                     new Rectangle2D({
                     new Rectangle2D({
-                        id: "ProfileBorder", border: "#FFFFFFFF", borderThickness: 2, roundRadius: 5, marginAlignment: "h: left, v: top", margin: "10", padding: "10", children:
+                        id: "ProfileBorder", border: "#FFFFFFFF", borderThickness: 2, roundRadius: 5, fill: "#C04040C0", marginAlignment: "h: left, v: top", margin: "10", padding: "10", children:
                         [
                         [
                             new Text2D("Stats", { id: "ProfileInfoText", marginAlignment: "h: left, v: top", fontName: "10pt Lucida Console" })
                             new Text2D("Stats", { id: "ProfileInfoText", marginAlignment: "h: left, v: top", fontName: "10pt Lucida Console" })
                         ]
                         ]
@@ -945,7 +954,7 @@
             });
             });
 
 
             this._profileInfoText = <Text2D>canvas.findById("ProfileInfoText");
             this._profileInfoText = <Text2D>canvas.findById("ProfileInfoText");
-
+            this._profilingCanvas = canvas;
             return canvas;
             return canvas;
         }
         }
 
 
@@ -993,9 +1002,9 @@
             let format = (v: number) => (Math.round(v*100)/100).toString();
             let format = (v: number) => (Math.round(v*100)/100).toString();
 
 
             let p = `Draw Calls:\n` +
             let p = `Draw Calls:\n` +
-                    ` - Opaque:      ${this.drawCallsOpaqueCounter.current}, (avg:${format(this.drawCallsOpaqueCounter.lastSecAverage)}, t:${format(this.drawCallsOpaqueCounter.total)})\n` +
-                    ` - AlphaTest:   ${this.drawCallsAlphaTestCounter.current}, (avg:${format(this.drawCallsAlphaTestCounter.lastSecAverage)}, t:${format(this.drawCallsAlphaTestCounter.total)})\n` +
-                    ` - Transparent: ${this.drawCallsTransparentCounter.current}, (avg:${format(this.drawCallsTransparentCounter.lastSecAverage)}, t:${format(this.drawCallsTransparentCounter.total)})\n` +
+                    ` - Opaque:      ${format(this.drawCallsOpaqueCounter.current)}, (avg:${format(this.drawCallsOpaqueCounter.lastSecAverage)}, t:${format(this.drawCallsOpaqueCounter.total)})\n` +
+                    ` - AlphaTest:   ${format(this.drawCallsAlphaTestCounter.current)}, (avg:${format(this.drawCallsAlphaTestCounter.lastSecAverage)}, t:${format(this.drawCallsAlphaTestCounter.total)})\n` +
+                    ` - Transparent: ${format(this.drawCallsTransparentCounter.current)}, (avg:${format(this.drawCallsTransparentCounter.lastSecAverage)}, t:${format(this.drawCallsTransparentCounter.total)})\n` +
                     `Group Render: ${this.groupRenderCounter.current}, (avg:${format(this.groupRenderCounter.lastSecAverage)}, t:${format(this.groupRenderCounter.total)})\n` + 
                     `Group Render: ${this.groupRenderCounter.current}, (avg:${format(this.groupRenderCounter.lastSecAverage)}, t:${format(this.groupRenderCounter.total)})\n` + 
                     `Update Transparent Data: ${this.updateTransparentDataCounter.current}, (avg:${format(this.updateTransparentDataCounter.lastSecAverage)}, t:${format(this.updateTransparentDataCounter.total)})\n` + 
                     `Update Transparent Data: ${this.updateTransparentDataCounter.current}, (avg:${format(this.updateTransparentDataCounter.lastSecAverage)}, t:${format(this.updateTransparentDataCounter.total)})\n` + 
                     `Cached Group Render: ${this.cachedGroupRenderCounter.current}, (avg:${format(this.cachedGroupRenderCounter.lastSecAverage)}, t:${format(this.cachedGroupRenderCounter.total)})\n` + 
                     `Cached Group Render: ${this.cachedGroupRenderCounter.current}, (avg:${format(this.cachedGroupRenderCounter.lastSecAverage)}, t:${format(this.cachedGroupRenderCounter.total)})\n` + 
@@ -1102,6 +1111,7 @@
         private _updateLocalTransformCounter : PerfCounter;
         private _updateLocalTransformCounter : PerfCounter;
         private _boundingInfoRecomputeCounter: PerfCounter;
         private _boundingInfoRecomputeCounter: PerfCounter;
 
 
+        private _profilingCanvas: Canvas2D;
         private _profileInfoText: Text2D;
         private _profileInfoText: Text2D;
 
 
         protected onPrimBecomesDirty() {
         protected onPrimBecomesDirty() {

+ 10 - 4
src/Canvas2d/babylon.ellipse2d.ts

@@ -57,9 +57,11 @@
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
                     }
 
 
+                    let glBuffer = context.instancedBuffers ? context.instancedBuffers[partIndex] : pid._partBuffer;
+                    let count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
                     canvas._addDrawCallCount(1, context.renderMode);
                     canvas._addDrawCallCount(1, context.renderMode);
-                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
-                    engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
+                    engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, count);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 } else {
                 } else {
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
@@ -87,9 +89,11 @@
                         this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);
                         this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
                     }
 
 
+                    let glBuffer = context.instancedBuffers ? context.instancedBuffers[partIndex] : pid._partBuffer;
+                    let count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
                     canvas._addDrawCallCount(1, context.renderMode);
                     canvas._addDrawCallCount(1, context.renderMode);
-                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
-                    engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
+                    engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, count);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 } else {
                 } else {
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
@@ -225,6 +229,7 @@
          * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          * - 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
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1
          * - 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]
          * - 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].
          * - 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.
          * - subdivision: the number of subdivision to create the ellipse perimeter, default is 64.
@@ -257,6 +262,7 @@
             y                 ?: number,
             y                 ?: number,
             rotation          ?: number,
             rotation          ?: number,
             scale             ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             origin            ?: Vector2,
             size              ?: Size,
             size              ?: Size,
             width             ?: number,
             width             ?: number,

+ 77 - 42
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]
          * - 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
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1
          * - 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]
          * - 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.
          * - 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
          *  - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
@@ -64,6 +65,7 @@
             x                 ?: number,
             x                 ?: number,
             y                 ?: number,
             y                 ?: number,
             trackNode         ?: Node,
             trackNode         ?: Node,
+            opacity           ?: number,
             origin            ?: Vector2,
             origin            ?: Vector2,
             size              ?: Size,
             size              ?: Size,
             width             ?: number,
             width             ?: number,
@@ -190,7 +192,7 @@
             }
             }
 
 
             if (this._renderableData) {
             if (this._renderableData) {
-                this._renderableData.dispose();
+                this._renderableData.dispose(this.owner.engine);
                 this._renderableData = null;
                 this._renderableData = null;
             }
             }
 
 
@@ -525,11 +527,6 @@
 
 
             let rd = this._renderableData;
             let rd = this._renderableData;
 
 
-            // If null, there was no change of ZOrder, we have nothing to do
-            if (rd._firstChangedPrim === null) {
-                return;
-            }
-
             // Sort all the primitive from their depth, max (bottom) to min (top)
             // Sort all the primitive from their depth, max (bottom) to min (top)
             rd._transparentPrimitives.sort((a, b) => b._primitive.actualZOffset - a._primitive.actualZOffset);
             rd._transparentPrimitives.sort((a, b) => b._primitive.actualZOffset - a._primitive.actualZOffset);
 
 
@@ -541,21 +538,21 @@
                     return false;
                     return false;
                 }
                 }
 
 
-                let tpiZ = tpi._primitive.actualZOffset;
+                //let tpiZ = tpi._primitive.actualZOffset;
 
 
                 // We've made it so far, the tpi can be part of the segment, add it
                 // We've made it so far, the tpi can be part of the segment, add it
                 tpi._transparentSegment = seg;
                 tpi._transparentSegment = seg;
-
-                // Check if we have to update endZ, a smaller value means one above the current one
-                if (tpiZ < seg.endZ) {
-                    seg.endZ = tpiZ;
-                    seg.endDataIndex = tpi._primitive._getLastIndexInDataBuffer() + 1; // Still exclusive
-                }
+                seg.endDataIndex = tpi._primitive._getPrimitiveLastIndex();
 
 
                 return true;
                 return true;
             }
             }
 
 
+            // Free the existing TransparentSegments
+            for (let ts of rd._transparentSegments) {
+                ts.dispose(this.owner.engine);
+            }
             rd._transparentSegments.splice(0);
             rd._transparentSegments.splice(0);
+
             let prevSeg = null;
             let prevSeg = null;
 
 
             for (let tpiI = 0; tpiI < rd._transparentPrimitives.length; tpiI++) {
             for (let tpiI = 0; tpiI < rd._transparentPrimitives.length; tpiI++) {
@@ -580,8 +577,7 @@
                     ts.groupInsanceInfo = tpi._groupInstanceInfo;
                     ts.groupInsanceInfo = tpi._groupInstanceInfo;
                     let prim = tpi._primitive;
                     let prim = tpi._primitive;
                     ts.startZ = prim.actualZOffset;
                     ts.startZ = prim.actualZOffset;
-                    ts.startDataIndex = prim._getFirstIndexInDataBuffer();
-                    ts.endDataIndex = prim._getLastIndexInDataBuffer() + 1; // Make it exclusive, more natural to use in a for loop
+                    prim._updateTransparentSegmentIndices(ts);
                     ts.endZ = ts.startZ;
                     ts.endZ = ts.startZ;
                     tpi._transparentSegment = ts;
                     tpi._transparentSegment = ts;
                     rd._transparentSegments.push(ts);
                     rd._transparentSegments.push(ts);
@@ -590,7 +586,7 @@
                 prevSeg = tpi._transparentSegment;
                 prevSeg = tpi._transparentSegment;
             }
             }
 
 
-            rd._firstChangedPrim = null;
+            //rd._firstChangedPrim = null;
             rd._transparentListChanged = false;
             rd._transparentListChanged = false;
         }
         }
 
 
@@ -599,22 +595,69 @@
             let context = new Render2DContext(Render2DContext.RenderModeTransparent);
             let context = new Render2DContext(Render2DContext.RenderModeTransparent);
             let rd = this._renderableData;
             let rd = this._renderableData;
 
 
+            let useInstanced = this.owner.supportInstancedArray;
+
             let length = rd._transparentSegments.length;
             let length = rd._transparentSegments.length;
             for (let i = 0; i < length; i++) {
             for (let i = 0; i < length; i++) {
-                let ts = rd._transparentSegments[i];
-
+                context.instancedBuffers = null;
 
 
+                let ts = rd._transparentSegments[i];
                 let gii = ts.groupInsanceInfo;
                 let gii = ts.groupInsanceInfo;
                 let mrc = gii.modelRenderCache;
                 let mrc = gii.modelRenderCache;
+                let engine = this.owner.engine;
+                let count = ts.endDataIndex - ts.startDataIndex;
+
+                // Use Instanced Array if it's supported and if there's at least 5 prims to draw.
+                // We don't want to create an Instanced Buffer for less that 5 prims
+                if (useInstanced && count >= 5) {
+
+                    if (!ts.partBuffers) {
+                        let buffers = new Array<WebGLBuffer>();
+
+                        for (let j = 0; j < gii.transparentData.length; j++) {
+                            let gitd = gii.transparentData[j];
+                            let dfa = gitd._partData;
+                            let data = dfa.pack();
+                            let stride = dfa.stride;
+                            let neededSize = count * stride * 4;
+
+                            let buffer = engine.createInstancesBuffer(neededSize); // Create + bind
+                            let segData = data.subarray(ts.startDataIndex * stride, ts.endDataIndex * stride);
+                            engine.updateArrayBuffer(segData);
+                            buffers.push(buffer);
+                        }
+
+                        ts.partBuffers = buffers;
+                    } else if (gii.transparentDirty) {
+                        for (let j = 0; j < gii.transparentData.length; j++) {
+                            let gitd = gii.transparentData[j];
+                            let dfa = gitd._partData;
+                            let data = dfa.pack();
+                            let stride = dfa.stride;
+
+                            let buffer = ts.partBuffers[j];
+                            let segData = data.subarray(ts.startDataIndex * stride, ts.endDataIndex * stride);
+                            engine.bindArrayBuffer(buffer);
+                            engine.updateArrayBuffer(segData);
+                        }
+                    }
 
 
-                context.useInstancing = false;
-                context.partDataStartIndex = ts.startDataIndex;
-                context.partDataEndIndex = ts.endDataIndex;
-                context.groupInfoPartData = gii.transparentData;
+                    context.useInstancing = true;
+                    context.instancesCount = count;
+                    context.instancedBuffers = ts.partBuffers;
+                    context.groupInfoPartData = gii.transparentData;
 
 
-                let renderFailed = !mrc.render(gii, context);
+                    let renderFailed = !mrc.render(gii, context);
+                    failedCount += renderFailed ? 1 : 0;
+                } else {
+                    context.useInstancing = false;
+                    context.partDataStartIndex = ts.startDataIndex;
+                    context.partDataEndIndex = ts.endDataIndex;
+                    context.groupInfoPartData = gii.transparentData;
 
 
-                failedCount += renderFailed ? 1 : 0;
+                    let renderFailed = !mrc.render(gii, context);
+                    failedCount += renderFailed ? 1 : 0;
+                }
             }
             }
 
 
             return failedCount;
             return failedCount;
@@ -894,7 +937,6 @@
             this._renderGroupInstancesInfo = new StringDictionary<GroupInstanceInfo>();
             this._renderGroupInstancesInfo = new StringDictionary<GroupInstanceInfo>();
             this._transparentPrimitives = new Array<TransparentPrimitiveInfo>();
             this._transparentPrimitives = new Array<TransparentPrimitiveInfo>();
             this._transparentSegments = new Array<TransparentSegment>();
             this._transparentSegments = new Array<TransparentSegment>();
-            this._firstChangedPrim = null;
             this._transparentListChanged = false;
             this._transparentListChanged = false;
             this._cacheNode = null;
             this._cacheNode = null;
             this._cacheTexture = null;
             this._cacheTexture = null;
@@ -907,7 +949,7 @@
             this._anisotropicLevel = 1;
             this._anisotropicLevel = 1;
         }
         }
 
 
-        dispose() {
+        dispose(engine: Engine) {
             if (this._cacheRenderSprite) {
             if (this._cacheRenderSprite) {
                 this._cacheRenderSprite.dispose();
                 this._cacheRenderSprite.dispose();
                 this._cacheRenderSprite = null;
                 this._cacheRenderSprite = null;
@@ -935,6 +977,14 @@
                 this._cacheNodeUVsChangedObservable.clear();
                 this._cacheNodeUVsChangedObservable.clear();
                 this._cacheNodeUVsChangedObservable = null;
                 this._cacheNodeUVsChangedObservable = null;
             }
             }
+
+            if (this._transparentSegments) {
+                for (let ts of this._transparentSegments) {
+                    ts.dispose(engine);
+                }
+                this._transparentSegments.splice(0);
+                this._transparentSegments = null;
+            }
         }
         }
 
 
         addNewTransparentPrimitiveInfo(prim: RenderablePrim2D, gii: GroupInstanceInfo): TransparentPrimitiveInfo {
         addNewTransparentPrimitiveInfo(prim: RenderablePrim2D, gii: GroupInstanceInfo): TransparentPrimitiveInfo {
@@ -946,8 +996,6 @@
             this._transparentPrimitives.push(tpi);
             this._transparentPrimitives.push(tpi);
             this._transparentListChanged = true;
             this._transparentListChanged = true;
 
 
-            this.updateSmallestZChangedPrim(tpi);
-
             return tpi;
             return tpi;
         }
         }
 
 
@@ -956,24 +1004,12 @@
             if (index !== -1) {
             if (index !== -1) {
                 this._transparentPrimitives.splice(index, 1);
                 this._transparentPrimitives.splice(index, 1);
                 this._transparentListChanged = true;
                 this._transparentListChanged = true;
-
-                this.updateSmallestZChangedPrim(tpi);
             }
             }
         }
         }
 
 
         transparentPrimitiveZChanged(tpi: TransparentPrimitiveInfo) {
         transparentPrimitiveZChanged(tpi: TransparentPrimitiveInfo) {
             this._transparentListChanged = true;
             this._transparentListChanged = true;
-            this.updateSmallestZChangedPrim(tpi);
-        }
-
-        updateSmallestZChangedPrim(tpi: TransparentPrimitiveInfo) {
-            if (tpi._primitive) {
-                let newZ = tpi._primitive.actualZOffset;
-                let curZ = this._firstChangedPrim ? this._firstChangedPrim._primitive.actualZOffset : Number.MIN_VALUE;
-                if (newZ > curZ) {
-                    this._firstChangedPrim = tpi;
-                }
-            }
+            //this.updateSmallestZChangedPrim(tpi);
         }
         }
 
 
         _primDirtyList: Array<Prim2DBase>;
         _primDirtyList: Array<Prim2DBase>;
@@ -992,7 +1028,6 @@
         _transparentListChanged: boolean;
         _transparentListChanged: boolean;
         _transparentPrimitives: Array<TransparentPrimitiveInfo>;
         _transparentPrimitives: Array<TransparentPrimitiveInfo>;
         _transparentSegments: Array<TransparentSegment>;
         _transparentSegments: Array<TransparentSegment>;
-        _firstChangedPrim: TransparentPrimitiveInfo;
         _renderingScale: number;
         _renderingScale: number;
 
 
     }
     }

+ 10 - 4
src/Canvas2d/babylon.lines2d.ts

@@ -56,9 +56,11 @@
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
                     }
 
 
+                    let glBuffer = context.instancedBuffers ? context.instancedBuffers[partIndex] : pid._partBuffer;
+                    let count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
                     canvas._addDrawCallCount(1, context.renderMode);
                     canvas._addDrawCallCount(1, context.renderMode);
-                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
-                    engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
+                    engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, count);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 } else {
                 } else {
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
@@ -86,9 +88,11 @@
                         this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);
                         this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
                     }
 
 
+                    let glBuffer = context.instancedBuffers ? context.instancedBuffers[partIndex] : pid._partBuffer;
+                    let count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
                     canvas._addDrawCallCount(1, context.renderMode);
                     canvas._addDrawCallCount(1, context.renderMode);
-                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
-                    engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
+                    engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, count);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 } else {
                 } else {
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
@@ -391,6 +395,7 @@
          * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          * - 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
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1
          * - 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]
          * - 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.
          * - 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.
          * - 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 +430,7 @@
             y                 ?: number,
             y                 ?: number,
             rotation          ?: number,
             rotation          ?: number,
             scale             ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             origin            ?: Vector2,
             fillThickness     ?: number,
             fillThickness     ?: number,
             closed            ?: boolean,
             closed            ?: boolean,

+ 18 - 0
src/Canvas2d/babylon.modelRenderCache.ts

@@ -123,11 +123,29 @@
     }
     }
 
 
     export class TransparentSegment {
     export class TransparentSegment {
+        constructor() {
+            this.groupInsanceInfo = null;
+            this.startZ = 0;
+            this.endZ = 0;
+            this.startDataIndex = 0;
+            this.endDataIndex = 0;
+            this.partBuffers = null;
+        }
+
+        dispose(engine: Engine) {
+            if (this.partBuffers) {
+                this.partBuffers.forEach(b => engine._releaseBuffer(b));
+                this.partBuffers.splice(0);
+                this.partBuffers = null;
+            }
+        }
+
         groupInsanceInfo: GroupInstanceInfo;
         groupInsanceInfo: GroupInstanceInfo;
         startZ: number;
         startZ: number;
         endZ: number;
         endZ: number;
         startDataIndex: number;
         startDataIndex: number;
         endDataIndex: number;
         endDataIndex: number;
+        partBuffers: WebGLBuffer[];
     }
     }
 
 
     export class GroupInfoPartData {
     export class GroupInfoPartData {

+ 91 - 13
src/Canvas2d/babylon.prim2dBase.ts

@@ -19,6 +19,7 @@
             this.useInstancing = false;
             this.useInstancing = false;
             this.groupInfoPartData = null;
             this.groupInfoPartData = null;
             this.partDataStartIndex = this.partDataEndIndex = null;
             this.partDataStartIndex = this.partDataEndIndex = null;
+            this.instancedBuffers = null;
         }
         }
         /**
         /**
          * Define which render Mode should be used to render the primitive: one of Render2DContext.RenderModeXxxx property
          * Define which render Mode should be used to render the primitive: one of Render2DContext.RenderModeXxxx property
@@ -37,6 +38,16 @@
         useInstancing: boolean;
         useInstancing: boolean;
 
 
         /**
         /**
+         * If specified, must take precedence from the groupInfoPartData. partIndex is the same as groupInfoPardData
+         */
+        instancedBuffers: WebGLBuffer[];
+
+        /**
+         * To use when instancedBuffers is specified, gives the count of instances to draw
+         */
+        instancesCount: number;
+
+        /**
          * Contains the data related to the primitives instances to render
          * Contains the data related to the primitives instances to render
          */
          */
         groupInfoPartData: GroupInfoPartData[];
         groupInfoPartData: GroupInfoPartData[];
@@ -1280,7 +1291,7 @@
      */
      */
     export class Prim2DBase extends SmartPropertyPrim {
     export class Prim2DBase extends SmartPropertyPrim {
         static PRIM2DBASE_PROPCOUNT: number = 15;
         static PRIM2DBASE_PROPCOUNT: number = 15;
-        private static _bigInt = Math.pow(2, 30);
+        public  static _bigInt = Math.pow(2, 30);
 
 
         constructor(settings: {
         constructor(settings: {
             parent?: Prim2DBase,
             parent?: Prim2DBase,
@@ -1291,6 +1302,7 @@
             y?: number,
             y?: number,
             rotation?: number,
             rotation?: number,
             scale?: number,
             scale?: number,
+            opacity?: number,
             origin?: Vector2,
             origin?: Vector2,
             layoutEngine?: LayoutEngineBase | string,
             layoutEngine?: LayoutEngineBase | string,
             isVisible?: boolean,
             isVisible?: boolean,
@@ -1373,7 +1385,13 @@
             this._zOrder = 0;
             this._zOrder = 0;
             this._zMax = 0;
             this._zMax = 0;
             this._firstZDirtyIndex = Prim2DBase._bigInt;
             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) {
             if (settings.childrenFlatZOrder) {
                 this._setFlags(SmartPropertyPrim.flagChildrenFlatZOrder);
                 this._setFlags(SmartPropertyPrim.flagChildrenFlatZOrder);
@@ -1598,6 +1616,12 @@
          */
          */
         public static marginAlignmentProperty: Prim2DPropInfo;
         public static marginAlignmentProperty: Prim2DPropInfo;
 
 
+        /**
+         * Metadata of the opacity property
+         */
+        public static opacityProperty: Prim2DPropInfo;
+
+
         @instanceLevelProperty(1, pi => Prim2DBase.actualPositionProperty = pi, false, false, true)
         @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
          * 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 +1666,7 @@
          * Use this property to set a new Vector2 object, otherwise to change only the x/y use Prim2DBase.x or y properties.
          * 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.
          * 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 {
         public get position(): Vector2 {
             return this._position || Prim2DBase._nullPosition;
             return this._position || Prim2DBase._nullPosition;
         }
         }
@@ -1652,6 +1676,7 @@
                 return;
                 return;
             }
             }
             this._position = value;
             this._position = value;
+            this.markAsDirty("actualPosition");
         }
         }
 
 
         /**
         /**
@@ -1679,6 +1704,7 @@
 
 
             this._position.x = value;
             this._position.x = value;
             this.markAsDirty("position");
             this.markAsDirty("position");
+            this.markAsDirty("actualPosition");
         }
         }
 
 
         /**
         /**
@@ -1706,6 +1732,7 @@
 
 
             this._position.y = value;
             this._position.y = value;
             this.markAsDirty("position");
             this.markAsDirty("position");
+            this.markAsDirty("actualPosition");
         }
         }
 
 
         private static boundinbBoxReentrency = false;
         private static boundinbBoxReentrency = false;
@@ -1716,7 +1743,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.
          * 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.
          * 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 {
         public get size(): Size {
 
 
             if (!this._size || this._size.width == null || this._size.height == null) {
             if (!this._size || this._size.width == null || this._size.height == null) {
@@ -1796,7 +1823,7 @@
             this._positioningDirty();
             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
          * Rotation of the primitive, in radian, along the Z axis
          */
          */
@@ -1808,7 +1835,7 @@
             this._rotation = value;
             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
          * Uniform scale applied on the primitive
          */
          */
@@ -1896,7 +1923,7 @@
          * 0,1 means the center is top/left
          * 0,1 means the center is top/left
          * @returns The normalized center.
          * @returns The normalized center.
          */
          */
-        @dynamicLevelProperty(5, pi => Prim2DBase.originProperty = pi, false, true)
+        @dynamicLevelProperty(6, pi => Prim2DBase.originProperty = pi, false, true)
         public get origin(): Vector2 {
         public get origin(): Vector2 {
             return this._origin;
             return this._origin;
         }
         }
@@ -1905,7 +1932,7 @@
             this._origin = value;
             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.
          * 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.
          * Default is true, setting to false will hide this primitive and its children.
@@ -1918,7 +1945,7 @@
             this._changeFlags(SmartPropertyPrim.flagLevelVisible, value);
             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.
          * Use ONLY THE GETTER to determine if the primitive is visible or not.
          * The Setter is for internal purpose only!
          * The Setter is for internal purpose only!
@@ -1931,7 +1958,7 @@
             this._changeFlags(SmartPropertyPrim.flagIsVisible, value);
             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
          * You can override the default Z Order through this property, but most of the time the default behavior is acceptable
          */
          */
@@ -1955,7 +1982,7 @@
             return this._manualZOrder != null;
             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
          * 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
          * @returns the margin object, if there was none, a default one is created and returned
@@ -1976,7 +2003,7 @@
             return (this._margin !== null) || (this._marginAlignment !== null);
             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
          * 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
          * @returns the margin object, if there was none, a default one is created and returned
@@ -1997,7 +2024,7 @@
             return this._padding !== null;
             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
          * You can get/set the margin alignment through this property
          */
          */
@@ -2008,6 +2035,45 @@
             return this._marginAlignment;
             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.
          * Get/set the layout engine to use for this primitive.
          * The default layout engine is the CanvasLayoutEngine.
          * The default layout engine is the CanvasLayoutEngine.
@@ -2467,6 +2533,13 @@
             this._setFlags(SmartPropertyPrim.flagPositioningDirty);
             this._setFlags(SmartPropertyPrim.flagPositioningDirty);
         }
         }
 
 
+        protected _spreadActualOpacityChanged() {
+            for (let child of this._children) {
+                child._setFlags(SmartPropertyPrim.flagActualOpacityDirty);
+                child._spreadActualOpacityChanged();
+            }
+        }
+
         private _changeLayoutEngine(engine: LayoutEngineBase) {
         private _changeLayoutEngine(engine: LayoutEngineBase) {
             this._layoutEngine = engine;
             this._layoutEngine = engine;
         }
         }
@@ -2916,6 +2989,9 @@
             }
             }
         }
         }
 
 
+        protected _updateRenderMode() {
+        }
+
         /**
         /**
          * This method is used to alter the contentArea of the Primitive before margin is applied.
          * 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.
          * 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 +3051,8 @@
         private _rotation: number;
         private _rotation: number;
         private _scale: number;
         private _scale: number;
         private _origin: Vector2;
         private _origin: Vector2;
+        protected _opacity: number;
+        private _actualOpacity: number;
 
 
         // Stores the step of the parent for which the current global transform was computed
         // 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
         // If the parent has a new step, it means this prim's global transform must be updated

+ 10 - 4
src/Canvas2d/babylon.rectangle2d.ts

@@ -57,9 +57,11 @@
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
                     }
 
 
+                    let glBuffer = context.instancedBuffers ? context.instancedBuffers[partIndex] : pid._partBuffer;
+                    let count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
                     canvas._addDrawCallCount(1, context.renderMode);
                     canvas._addDrawCallCount(1, context.renderMode);
-                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
-                    engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
+                    engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, count);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 } else {
                 } else {
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
@@ -87,9 +89,11 @@
                         this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);
                         this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
                     }
 
 
+                    let glBuffer = context.instancedBuffers ? context.instancedBuffers[partIndex] : pid._partBuffer;
+                    let count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
                     canvas._addDrawCallCount(1, context.renderMode);
                     canvas._addDrawCallCount(1, context.renderMode);
-                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
-                    engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
+                    engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, count);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 } else {
                 } else {
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                     canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
@@ -309,6 +313,7 @@
          * - position: the X & Y positions relative to its parent. Alternatively the x and y settings can be set. Default is [0;0]
          * - 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
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1
          * - 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]
          * - 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].
          * - 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).
          * - roundRadius: if the rectangle has rounded corner, set their radius, default is 0 (to get a sharp edges rectangle).
@@ -340,6 +345,7 @@
             y                 ?: number,
             y                 ?: number,
             rotation          ?: number,
             rotation          ?: number,
             scale             ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             origin            ?: Vector2,
             size              ?: Size,
             size              ?: Size,
             width             ?: number,
             width             ?: number,

+ 114 - 21
src/Canvas2d/babylon.renderablePrim2d.ts

@@ -252,6 +252,8 @@
             this.id = partId;
             this.id = partId;
             this.curElement = 0;
             this.curElement = 0;
             this.dataElementCount = dataElementCount;
             this.dataElementCount = dataElementCount;
+            this.renderMode = 0;
+            this.arrayLengthChanged = false;
         }
         }
 
 
         id: number;
         id: number;
@@ -272,6 +274,11 @@
             return null;
             return null;
         }
         }
 
 
+        @instanceData()
+        get opacity(): number {
+            return null;
+        }
+
         getClassTreeInfo(): ClassTreeInfo<InstanceClassInfo, InstancePropInfo> {
         getClassTreeInfo(): ClassTreeInfo<InstanceClassInfo, InstancePropInfo> {
             if (!this.typeInfo) {
             if (!this.typeInfo) {
                 this.typeInfo = ClassTreeInfo.get<InstanceClassInfo, InstancePropInfo>(Object.getPrototypeOf(this));
                 this.typeInfo = ClassTreeInfo.get<InstanceClassInfo, InstancePropInfo>(Object.getPrototypeOf(this));
@@ -309,12 +316,14 @@
                 return;
                 return;
             }
             }
 
 
+            this.arrayLengthChanged = true;
             this.freeElements();
             this.freeElements();
             this._dataElementCount = value;
             this._dataElementCount = value;
             this.allocElements();
             this.allocElements();
         }
         }
-
+        arrayLengthChanged: boolean;
         curElement: number;
         curElement: number;
+        renderMode: number;
         dataElements: DynamicFloatArrayElementInfo[];
         dataElements: DynamicFloatArrayElementInfo[];
         dataBuffer: DynamicFloatArray;
         dataBuffer: DynamicFloatArray;
         typeInfo: ClassTreeInfo<InstanceClassInfo, InstancePropInfo>;
         typeInfo: ClassTreeInfo<InstanceClassInfo, InstancePropInfo>;
@@ -345,7 +354,11 @@
         }
         }
 
 
         public set isAlphaTest(value: boolean) {
         public set isAlphaTest(value: boolean) {
+            if (this._isAlphaTest === value) {
+                return;
+            }
             this._isAlphaTest = value;
             this._isAlphaTest = value;
+            this._updateRenderMode();
         }
         }
 
 
         @dynamicLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, pi => RenderablePrim2D.isTransparentProperty = pi)
         @dynamicLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, pi => RenderablePrim2D.isTransparentProperty = pi)
@@ -354,11 +367,19 @@
          * The setter should be used only by implementers of new primitive type.
          * The setter should be used only by implementers of new primitive type.
          */
          */
         public get isTransparent(): boolean {
         public get isTransparent(): boolean {
-            return this._isTransparent;
+            return this._isTransparent || (this._opacity<1);
         }
         }
 
 
         public set isTransparent(value: boolean) {
         public set isTransparent(value: boolean) {
+            if (this._isTransparent === value) {
+                return;
+            }
             this._isTransparent = value;
             this._isTransparent = value;
+            this._updateRenderMode();
+        }
+
+        public get renderMode(): number {
+            return this._renderMode;
         }
         }
 
 
         constructor(settings?: {
         constructor(settings?: {
@@ -368,6 +389,7 @@
             isVisible    ?: boolean,
             isVisible    ?: boolean,
         }) {
         }) {
             super(settings);
             super(settings);
+
             this._isTransparent            = false;
             this._isTransparent            = false;
             this._isAlphaTest              = false;
             this._isAlphaTest              = false;
             this._transparentPrimitiveInfo = null;
             this._transparentPrimitiveInfo = null;
@@ -441,10 +463,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.
             // 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._isFlagSet(SmartPropertyPrim.flagVisibilityChanged) || context.forceRefreshPrimitive || newInstance || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep)) {
 
 
-                if (this.isTransparent) {
-                    //this.renderGroup._renderableData._transparentListChanged = true;
-                }
-
                 this._updateInstanceDataParts(gii);
                 this._updateInstanceDataParts(gii);
             }
             }
         }
         }
@@ -498,13 +516,17 @@
             });
             });
 
 
             // Get the GroupInfoDataPart corresponding to the render category of the part
             // Get the GroupInfoDataPart corresponding to the render category of the part
+            let rm = 0;
             let gipd: GroupInfoPartData[] = null;
             let gipd: GroupInfoPartData[] = null;
             if (this.isTransparent) {
             if (this.isTransparent) {
                 gipd = gii.transparentData;
                 gipd = gii.transparentData;
+                rm = Render2DContext.RenderModeTransparent;
             } else if (this.isAlphaTest) {
             } else if (this.isAlphaTest) {
                 gipd = gii.alphaTestData;
                 gipd = gii.alphaTestData;
+                rm = Render2DContext.RenderModeAlphaTest;
             } else {
             } else {
                 gipd = gii.opaqueData;
                 gipd = gii.opaqueData;
+                rm = Render2DContext.RenderModeOpaque;
             }
             }
 
 
             // For each instance data part of the primitive, allocate the instanced element it needs for render
             // For each instance data part of the primitive, allocate the instanced element it needs for render
@@ -512,6 +534,7 @@
                 let part = parts[i];
                 let part = parts[i];
                 part.dataBuffer = gipd[i]._partData;
                 part.dataBuffer = gipd[i]._partData;
                 part.allocElements();
                 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
             // 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
@@ -574,22 +597,61 @@
 
 
         private _updateInstanceDataParts(gii: GroupInstanceInfo) {
         private _updateInstanceDataParts(gii: GroupInstanceInfo) {
             // Fetch the GroupInstanceInfo if we don't already have it
             // Fetch the GroupInstanceInfo if we don't already have it
+            let rd = this.renderGroup._renderableData;
             if (!gii) {
             if (!gii) {
-                gii = this.renderGroup._renderableData._renderGroupInstancesInfo.get(this.modelKey);
+                gii = rd._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
             // 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
                 // 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) {
                         if (!this._transparentPrimitiveInfo) {
                             // Add the primitive to the list of transparent ones in the group that render is
                             // Add the primitive to the list of transparent ones in the group that render is
-                            this._transparentPrimitiveInfo = this.renderGroup._renderableData.addNewTransparentPrimitiveInfo(this, gii);
+                            this._transparentPrimitiveInfo = rd.addNewTransparentPrimitiveInfo(this, gii);
                         }
                         }
                     } else {
                     } else {
                         if (this._transparentPrimitiveInfo) {
                         if (this._transparentPrimitiveInfo) {
-                            this.renderGroup._renderableData.removeTransparentPrimitiveInfo(this._transparentPrimitiveInfo);
+                            rd.removeTransparentPrimitiveInfo(this._transparentPrimitiveInfo);
                             this._transparentPrimitiveInfo = null;
                             this._transparentPrimitiveInfo = null;
                         }
                         }
                     }
                     }
@@ -597,29 +659,37 @@
                 }
                 }
             }
             }
 
 
+            let rebuildTrans = false;
+
             // For each Instance Data part, refresh it to update the data in the DynamicFloatArray
             // For each Instance Data part, refresh it to update the data in the DynamicFloatArray
             for (let part of this._instanceDataParts) {
             for (let part of this._instanceDataParts) {
                 // Check if we need to allocate data elements (hidden prim which becomes visible again)
                 // 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();
                     part.allocElements();
                 }
                 }
 
 
                 InstanceClassInfo._CurCategories = gii.usedShaderCategories[gii.partIndexFromId.get(part.id.toString())];
                 InstanceClassInfo._CurCategories = gii.usedShaderCategories[gii.partIndexFromId.get(part.id.toString())];
 
 
                 // Will return false if the instance should not be rendered (not visible or other any reasons)
                 // Will return false if the instance should not be rendered (not visible or other any reasons)
+                part.arrayLengthChanged = false;
                 if (!this.refreshInstanceDataPart(part)) {
                 if (!this.refreshInstanceDataPart(part)) {
                     // Free the data element
                     // Free the data element
                     if (part.dataElements) {
                     if (part.dataElements) {
                         part.freeElements();
                         part.freeElements();
                     }
                     }
                 }
                 }
+
+                rebuildTrans = rebuildTrans || part.arrayLengthChanged;
             }
             }
             this._instanceDirtyFlags = 0;
             this._instanceDirtyFlags = 0;
 
 
             // Make the appropriate data dirty
             // Make the appropriate data dirty
-            if (this.isTransparent) {
+            if (isTransparent) {
                 gii.transparentDirty = true;
                 gii.transparentDirty = true;
-            } else if (this.isAlphaTest) {
+                if (rebuildTrans) {
+                    rd._transparentListChanged = true;
+                }
+            } else if (isAlphaTest) {
                 gii.alphaTestDirty = true;
                 gii.alphaTestDirty = true;
             } else {
             } else {
                 gii.opaqueDirty = true;
                 gii.opaqueDirty = true;
@@ -628,22 +698,33 @@
             this._clearFlags(SmartPropertyPrim.flagVisibilityChanged);    // Reset the flag as we've handled the case            
             this._clearFlags(SmartPropertyPrim.flagVisibilityChanged);    // Reset the flag as we've handled the case            
         }
         }
 
 
-        public _getFirstIndexInDataBuffer(): number {
+        _updateTransparentSegmentIndices(ts: TransparentSegment) {
+            let minOff = Prim2DBase._bigInt;
+            let maxOff = 0;
+
             for (let part of this._instanceDataParts) {
             for (let part of this._instanceDataParts) {
                 if (part) {
                 if (part) {
-                    return part.dataElements[0].offset / part.dataBuffer.stride;
+                    for (let el of part.dataElements) {
+                        minOff = Math.min(minOff, el.offset);
+                        maxOff = Math.max(maxOff, el.offset);
+                    }
+                    ts.startDataIndex = minOff / part.dataBuffer.stride;
+                    ts.endDataIndex = (maxOff / part.dataBuffer.stride) + 1; // +1 for exclusive
                 }
                 }
             }
             }
-            return null;
         }
         }
 
 
-        public _getLastIndexInDataBuffer(): number {
+        _getPrimitiveLastIndex(): number {
+            let maxOff = 0;
+
             for (let part of this._instanceDataParts) {
             for (let part of this._instanceDataParts) {
                 if (part) {
                 if (part) {
-                    return part.dataElements[part.dataElements.length-1].offset / part.dataBuffer.stride;
+                    for (let el of part.dataElements) {
+                        maxOff = Math.max(maxOff, el.offset);
+                    }
+                    return (maxOff / part.dataBuffer.stride) + 1; // +1 for exclusive
                 }
                 }
             }
             }
-            return null;
         }
         }
 
 
         // This internal method is mainly used for transparency processing
         // This internal method is mainly used for transparency processing
@@ -812,11 +893,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);
             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.transformX = tx;
             part.transformY = ty;
             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)
             // 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);
             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 _modelRenderCache: ModelRenderCache;
         private _modelRenderInstanceID: string;
         private _modelRenderInstanceID: string;
         private _transparentPrimitiveInfo: TransparentPrimitiveInfo;
         private _transparentPrimitiveInfo: TransparentPrimitiveInfo;
@@ -824,6 +916,7 @@
         protected _instanceDataParts: InstanceDataBase[];
         protected _instanceDataParts: InstanceDataBase[];
         protected _isAlphaTest: boolean;
         protected _isAlphaTest: boolean;
         protected _isTransparent: boolean;
         protected _isTransparent: boolean;
+        private _renderMode: number;
     }
     }
 
 
 
 

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

@@ -171,7 +171,7 @@
         }
         }
 
 
         private _updateTransparencyStatus() {
         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;
         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 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 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 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   _flags             : number;
         private   _externalData      : StringDictionary<Object>;
         private   _externalData      : StringDictionary<Object>;

+ 8 - 5
src/Canvas2d/babylon.sprite2d.ts

@@ -21,14 +21,13 @@
             let canvas = instanceInfo.owner.owner;
             let canvas = instanceInfo.owner.owner;
             var engine = canvas.engine;
             var engine = canvas.engine;
 
 
+            var cur = engine.getAlphaMode();
             let effect = context.useInstancing ? this.effectInstanced : this.effect;
             let effect = context.useInstancing ? this.effectInstanced : this.effect;
 
 
             engine.enableEffect(effect);
             engine.enableEffect(effect);
             effect.setTexture("diffuseSampler", this.texture);
             effect.setTexture("diffuseSampler", this.texture);
             engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, effect);
             engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, effect);
 
 
-            var cur = engine.getAlphaMode();
-
             if (context.renderMode !== Render2DContext.RenderModeOpaque) {
             if (context.renderMode !== Render2DContext.RenderModeOpaque) {
                 engine.setAlphaMode(Engine.ALPHA_COMBINE);
                 engine.setAlphaMode(Engine.ALPHA_COMBINE);
             }
             }
@@ -38,9 +37,11 @@
                 if (!this.instancingAttributes) {
                 if (!this.instancingAttributes) {
                     this.instancingAttributes = this.loadInstancingAttributes(Sprite2D.SPRITE2D_MAINPARTID, effect);
                     this.instancingAttributes = this.loadInstancingAttributes(Sprite2D.SPRITE2D_MAINPARTID, effect);
                 }
                 }
+                let glBuffer = context.instancedBuffers ? context.instancedBuffers[0] : pid._partBuffer;
+                let count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
                 canvas._addDrawCallCount(1, context.renderMode);
                 canvas._addDrawCallCount(1, context.renderMode);
-                engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingAttributes);
-                engine.draw(true, 0, 6, pid._partData.usedElementCount);
+                engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingAttributes);
+                engine.draw(true, 0, 6, count);
                 engine.unbindInstanceAttributes();
                 engine.unbindInstanceAttributes();
             } else {
             } else {
                 canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                 canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
@@ -239,6 +240,7 @@
          * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          * - 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
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1
          * - 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]
          * - 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.
          * - 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)
          * - 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 +272,7 @@
             y                 ?: number,
             y                 ?: number,
             rotation          ?: number,
             rotation          ?: number,
             scale             ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             origin            ?: Vector2,
             spriteSize        ?: Size,
             spriteSize        ?: Size,
             spriteLocation    ?: Vector2,
             spriteLocation    ?: Vector2,
@@ -306,7 +309,7 @@
             this.spriteFrame = 0;
             this.spriteFrame = 0;
             this.invertY = (settings.invertY == null) ? false : settings.invertY;
             this.invertY = (settings.invertY == null) ? false : settings.invertY;
             this.alignToPixel = (settings.alignToPixel == null) ? true : settings.alignToPixel;
             this.alignToPixel = (settings.alignToPixel == null) ? true : settings.alignToPixel;
-            this._isTransparent = true;
+            this.isAlphaTest = true;
 
 
             if (settings.spriteSize==null) {
             if (settings.spriteSize==null) {
                 var s = texture.getSize();
                 var s = texture.getSize();

+ 7 - 3
src/Canvas2d/babylon.text2d.ts

@@ -37,9 +37,11 @@
                     this.instancingAttributes = this.loadInstancingAttributes(Text2D.TEXT2D_MAINPARTID, effect);
                     this.instancingAttributes = this.loadInstancingAttributes(Text2D.TEXT2D_MAINPARTID, effect);
                 }
                 }
 
 
+                let glBuffer = context.instancedBuffers ? context.instancedBuffers[0] : pid._partBuffer;
+                let count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
                 canvas._addDrawCallCount(1, context.renderMode);
                 canvas._addDrawCallCount(1, context.renderMode);
-                engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingAttributes);
-                engine.draw(true, 0, 6, pid._partData.usedElementCount);
+                engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingAttributes);
+                engine.draw(true, 0, 6, count);
                 engine.unbindInstanceAttributes();
                 engine.unbindInstanceAttributes();
             } else {
             } else {
                 canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
                 canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
@@ -264,6 +266,7 @@
          * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          * - 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
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1
          * - 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]
          * - 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".
          * - 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.
          * - 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 +299,7 @@
             y                 ?: number,
             y                 ?: number,
             rotation          ?: number,
             rotation          ?: number,
             scale             ?: number,
             scale             ?: number,
+            opacity           ?: number,
             origin            ?: Vector2,
             origin            ?: Vector2,
             fontName          ?: string,
             fontName          ?: string,
             fontSuperSample   ?: boolean,
             fontSuperSample   ?: boolean,
@@ -332,7 +336,7 @@
             this._textSize        = null;
             this._textSize        = null;
             this.text             = text;
             this.text             = text;
             this.size             = (settings.size==null) ? null : settings.size;
             this.size             = (settings.size==null) ? null : settings.size;
-            this.isAlphaTest      = true;
+            this.isTransparent    = true;
         }
         }
 
 
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {

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

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

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

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

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

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

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

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

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

@@ -18,12 +18,13 @@ att vec3 properties;
 att vec2 zBias;
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformX;
 att vec4 transformY;
 att vec4 transformY;
+att float opacity;
 
 
 // Uniforms
 // Uniforms
 
 
 // Output
 // Output
 varying vec2 vUV;
 varying vec2 vUV;
-varying vec4 vColor;
+varying float vOpacity;
 
 
 void main(void) {
 void main(void) {
 
 
@@ -72,6 +73,7 @@ void main(void) {
 		pos.xy = pos2.xy * sizeUV * textureSize;
 		pos.xy = pos2.xy * sizeUV * textureSize;
 	}
 	}
 
 
+	vOpacity = opacity;
 	pos.z = 1.0;
 	pos.z = 1.0;
 	pos.w = 1.0;
 	pos.w = 1.0;
 	gl_Position = vec4(dot(pos, transformX), dot(pos, transformY), zBias.x, 1);
 	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 transformX;
 att vec4 transformY;
 att vec4 transformY;
+att float opacity;
 
 
 att vec2 topLeftUV;
 att vec2 topLeftUV;
 att vec2 sizeUV;
 att vec2 sizeUV;
@@ -54,6 +55,7 @@ void main(void) {
 	vUV = (floor(vUV*textureSize) + vec2(0.0, 0.0)) / textureSize;
 	vUV = (floor(vUV*textureSize) + vec2(0.0, 0.0)) / textureSize;
 
 
 	vColor = color;
 	vColor = color;
+	vColor.a *= opacity;
 	vec4 pos;
 	vec4 pos;
 	pos.xy = floor(pos2.xy * superSampleFactor * sizeUV * textureSize);	// Align on target pixel to avoid bad interpolation
 	pos.xy = floor(pos2.xy * superSampleFactor * sizeUV * textureSize);	// Align on target pixel to avoid bad interpolation
 	pos.z = 1.0;
 	pos.z = 1.0;