Browse Source

Fixed a bug in ClassTreeInfo initialization when Abstract classes are involved

CachedGroup/Sprite2DRender synchronization on pos/rot/scl change
https://trello.com/c/khQ9jdhI

Shape2D abstract class
https://trello.com/c/lWa7IsPH

Transparency Support in Primirives
https://trello.com/c/tzrPom9s
nockawa 9 years ago
parent
commit
cc39de049d

+ 12 - 21
src/Canvas2d/babylon.canvas2d.ts

@@ -1,12 +1,4 @@
 module BABYLON {
-    class GroupsCacheMap {
-        constructor() {
-            this.groupSprites = new Array<{ group: Group2D, sprite: Sprite2D }>();
-        }
-        texture: MapTexture;
-        groupSprites: Array<{ group: Group2D, sprite: Sprite2D }>;
-    }
-
     @className("Canvas2D")
     export class Canvas2D extends Group2D {
         /**
@@ -189,7 +181,7 @@
         private _hierarchyLevelZFactor: number;
         private _hierarchyLevelMaxSiblingCount: number;
         private _hierarchySiblingZDelta: number;
-        private _groupCacheMaps: GroupsCacheMap[];
+        private _groupCacheMaps: MapTexture[];
         public _renderingSize: Size;
 
         /**
@@ -219,20 +211,20 @@
          * @param group The group to allocate the cache of.
          * @return custom type with the PackedRect instance giving information about the cache location into the texture and also the MapTexture instance that stores the cache.
          */
-        public _allocateGroupCache(group: Group2D): { node: PackedRect, texture: MapTexture } {
+        public _allocateGroupCache(group: Group2D): { node: PackedRect, texture: MapTexture, sprite: Sprite2D } {
             // Determine size
             let size = group.actualSize;
             size = new Size(Math.ceil(size.width), Math.ceil(size.height));
             if (!this._groupCacheMaps) {
-                this._groupCacheMaps = new Array<GroupsCacheMap>();
+                this._groupCacheMaps = new Array<MapTexture>();
             }
 
             // Try to find a spot in one of the cached texture
             let res = null;
-            for (var g of this._groupCacheMaps) {
-                let node = g.texture.allocateRect(size);
+            for (var map of this._groupCacheMaps) {
+                let node = map.allocateRect(size);
                 if (node) {
-                    res = { node: node, texture: g.texture }
+                    res = { node: node, texture: map }
                     break;
                 }
             }
@@ -247,19 +239,18 @@
                     mapSize.height = Math.pow(2, Math.ceil(Math.log(size.height) / Math.log(2)));
                 }
 
-                g = new GroupsCacheMap();
                 let id = `groupsMapChache${this._mapCounter}forCanvas${this.id}`;
-                g.texture = new MapTexture(id, this._scene, mapSize);
-                this._groupCacheMaps.push(g);
+                map = new MapTexture(id, this._scene, mapSize);
+                this._groupCacheMaps.push(map);
 
-                let node = g.texture.allocateRect(size);
-                res = { node: node, texture: g.texture }
+                let node = map.allocateRect(size);
+                res = { node: node, texture: map }
             }
 
             // Create a Sprite that will be used to render this cache, the "__cachedSpriteOfGroup__" starting id is a hack to bypass exception throwing in case of the Canvas doesn't normally allows direct primitives
-            let sprite = Sprite2D.Create(this, `__cachedSpriteOfGroup__${group.id}`, 10, 10, g.texture, res.node.contentSize, res.node.pos, true);
+            let sprite = Sprite2D.Create(this, `__cachedSpriteOfGroup__${group.id}`, group.position.x, group.position.y, map, res.node.contentSize, res.node.pos, true);
             sprite.origin = Vector2.Zero();
-            g.groupSprites.push({ group: group, sprite: sprite });
+            res.sprite = sprite;
             return res;
         }
 

+ 25 - 8
src/Canvas2d/babylon.group2d.ts

@@ -1,9 +1,12 @@
 module BABYLON {
     @className("Group2D")
     export class Group2D extends Prim2DBase {
-        static GROUP2D_PROPCOUNT: number = Prim2DBase.PRIM2DBASE_PROPCOUNT + 10;
+        static GROUP2D_PROPCOUNT: number = Prim2DBase.PRIM2DBASE_PROPCOUNT + 5;
 
-        /**
+        public static sizeProperty: Prim2DPropInfo;
+        public static actualSizeProperty: Prim2DPropInfo;
+
+      /**
          * Default behavior, the group will use the caching strategy defined at the Canvas Level
          */
         public static GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY = 0;
@@ -65,10 +68,6 @@
             return this._isCachedGroup;
         }
 
-        public static sizeProperty: Prim2DPropInfo;
-        public static actualSizeProperty: Prim2DPropInfo;
-
-
         @instanceLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, pi => Group2D.sizeProperty = pi, false, true)
         public get size(): Size {
             return this._size;
@@ -99,7 +98,7 @@
             return this._cacheBehavior;
         }
 
-        _addPrimToDirtyList(prim: Prim2DBase) {
+        public _addPrimToDirtyList(prim: Prim2DBase) {
             this._primDirtyList.push(prim);
         }
 
@@ -132,7 +131,6 @@
                 this.updateGlobalTransVisOf(sortedDirtyList, childrenContext, true);
             }
 
-
             // Setup the size of the rendering viewport
             // In non cache mode, we're rendering directly to the rendering canvas, in this case we have to detect if the canvas size changed since the previous iteration, if it's the case all primitives must be preprared again because their transformation must be recompute
             if (!this._isCachedGroup) {
@@ -272,6 +270,7 @@
                 var res = this.owner._allocateGroupCache(this);
                 this._cacheNode = res.node;
                 this._cacheTexture = res.texture;
+                this._cacheRenderSprite = res.sprite;
             }
 
             let n = this._cacheNode;
@@ -284,6 +283,23 @@
             }
         }
 
+        protected handleGroupChanged(prop: Prim2DPropInfo) {
+            // This method is only for cachedGroup
+            if (!this.isCachedGroup || !this._cacheRenderSprite) {
+                return;
+            }
+
+            // For now we only support these property changes
+            // TODO: add more! :)
+            if (prop.id === Prim2DBase.positionProperty.id) {
+                this._cacheRenderSprite.position = this.position.clone();
+            } else if (prop.id === Prim2DBase.rotationProperty.id) {
+                this._cacheRenderSprite.rotation = this.rotation;
+            } else if (prop.id === Prim2DBase.scaleProperty.id) {
+                this._cacheRenderSprite.scale = this.scale;
+            }
+        }
+
         private detectGroupStates() {
             var isCanvas = this instanceof Canvas2D;
             var canvasStrat = this.owner.cachingStrategy;
@@ -357,6 +373,7 @@
         private _primDirtyList: Array<Prim2DBase>;
         private _cacheNode: PackedRect;
         private _cacheTexture: MapTexture;
+        private _cacheRenderSprite: Sprite2D;
         private _viewportPosition: Vector2;
         private _viewportSize: Size;
 

+ 0 - 8
src/Canvas2d/babylon.prim2dBase.ts

@@ -219,12 +219,6 @@
 
         protected onPrimBecomesDirty() {
             if (this._renderGroup) {
-                //if (this instanceof Group2D) {
-                //    var group: any= this;
-                //    if (group.isRenderableGroup) {
-                //        return;
-                //    }
-                //}
                 this._renderGroup._addPrimToDirtyList(this);
             }
         }
@@ -288,7 +282,6 @@
             }
         }
 
-
         protected updateGlobalTransVisOf(list: Prim2DBase[], context: Render2DContext, recurse: boolean) {
             for (let cur of list) {
                 cur.updateGlobalTransVis(context, recurse);
@@ -296,7 +289,6 @@
         }
 
         protected updateGlobalTransVis(context: Render2DContext, recurse: boolean) {
-
             this._globalTransformPreviousStep = this._globalTransformStep;
             this.isVisible = context.parentVisibleState && this.levelVisible;
 

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

@@ -41,13 +41,13 @@
     }
 
     @className("Rectangle2D")
-    export class Rectangle2D extends RenderablePrim2D<Rectangle2DInstanceData> {
+    export class Rectangle2D extends Shape2D<Rectangle2DInstanceData> {
 
         public static sizeProperty: Prim2DPropInfo;
         public static notRoundedProperty: Prim2DPropInfo;
         public static roundRadiusProperty: Prim2DPropInfo;
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Rectangle2D.sizeProperty = pi, false, true)
+        @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Rectangle2D.sizeProperty = pi, false, true)
         public get size(): Size {
             return this._size;
         }
@@ -56,7 +56,7 @@
             this._size = value;
         }
 
-        @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, pi => Rectangle2D.notRoundedProperty = pi)
+        @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Rectangle2D.notRoundedProperty = pi)
         public get notRounded(): boolean {
             return this._notRounded;
         }
@@ -65,7 +65,7 @@
             this._notRounded = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, pi => Rectangle2D.roundRadiusProperty = pi)
+        @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 3, pi => Rectangle2D.roundRadiusProperty = pi)
         public get roundRadius(): number {
             return this._roundRadius;
         }

+ 6 - 33
src/Canvas2d/babylon.renderablePrim2d.ts

@@ -213,42 +213,13 @@
 
     @className("RenderablePrim2D")
     export class RenderablePrim2D<TInstData extends InstanceDataBase> extends Prim2DBase {
-        static RENDERABLEPRIM2D_PROPCOUNT: number = Prim2DBase.PRIM2DBASE_PROPCOUNT + 10;
-
-        public static borderProperty: Prim2DPropInfo;
-        public static fillProperty: Prim2DPropInfo;
+        static RENDERABLEPRIM2D_PROPCOUNT: number = Prim2DBase.PRIM2DBASE_PROPCOUNT + 5;
 
         setupRenderablePrim2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, isVisible: boolean, fill: IFill2D, border: IBorder2D) {
             this.setupPrim2DBase(owner, parent, id, position);
             this._isTransparent = false;
         }
 
-        @modelLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, pi => RenderablePrim2D.borderProperty = pi, true)
-        public get border(): IBorder2D {
-            return this._border;
-        }
-
-        public set border(value: IBorder2D) {
-            if (value === this._border) {
-                return;
-            }
-
-            this._border = value;
-        }
-
-        @modelLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 2, pi => RenderablePrim2D.fillProperty = pi, true)
-        public get fill(): IFill2D {
-            return this._fill;
-        }
-
-        public set fill(value: IBorder2D) {
-            if (value === this._fill) {
-                return;
-            }
-
-            this._fill = value;
-        }
-
         public _prepareRenderPre(context: Render2DContext) {
             super._prepareRenderPre(context);
 
@@ -323,6 +294,10 @@
             }
         }
 
+        public get isTransparent(): boolean {
+            return this._isTransparent;
+        }
+
         protected createModelRenderCache(): ModelRenderCache<TInstData> {
             return null;
         }
@@ -367,9 +342,7 @@
         private _modelRenderInstanceID: string;
 
         protected _instanceData: TInstData;
-        private _border: IBorder2D;
-        private _fill: IFill2D;
-        private _isTransparent: boolean;
+        protected _isTransparent: boolean;
     }
 
 

+ 39 - 0
src/Canvas2d/babylon.shape2d.ts

@@ -0,0 +1,39 @@
+module BABYLON {
+    @className("Shape2D")
+    export class Shape2D<TInstData extends InstanceDataBase> extends RenderablePrim2D<TInstData> {
+        static SHAPE2D_PROPCOUNT: number = RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 5;
+        public static borderProperty: Prim2DPropInfo;
+        public static fillProperty: Prim2DPropInfo;
+
+        @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Shape2D.borderProperty = pi, true)
+        public get border(): IBorder2D {
+            return this._border;
+        }
+
+        public set border(value: IBorder2D) {
+            if (value === this._border) {
+                return;
+            }
+
+            this._border = value;
+        }
+
+        @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, pi => Shape2D.fillProperty = pi, true)
+        public get fill(): IFill2D {
+            return this._fill;
+        }
+
+        public set fill(value: IBorder2D) {
+            if (value === this._fill) {
+                return;
+            }
+
+            this._fill = value;
+        }
+
+        private _border: IBorder2D;
+        private _fill: IFill2D;
+    }
+
+
+}

+ 20 - 11
src/Canvas2d/babylon.smartPropertyPrim.ts

@@ -72,16 +72,12 @@
             }
 
             let baseProto = Object.getPrototypeOf(type);
-            return this.getOrAddType(baseProto, type);
-
-            //// If type is a class, this will get the base class proto, if type is an instance of a class, this will get the proto of the class
-            //let baseTypeName = Tools.getClassName(baseProto);
+            let curProtoContent = this.getOrAddType(Object.getPrototypeOf(baseProto), baseProto);
+            if (!curProtoContent) {
+                this.getLevelOf(baseProto);
+            }
 
-            //// If both name are equal we only switch from instance to class, we need to get the next proto in the hierarchy to get the base class
-            //if (baseTypeName === typeName) {
-            //    baseTypeName = Tools.getClassName(Object.getPrototypeOf(baseProto));
-            //}
-            //return this.getOrAddType(baseTypeName, typeName);
+            return this.getOrAddType(baseProto, type);
         }
 
         getOrAddType(baseType: Object, type: Object): ClassTreeInfo<TClass, TProp> {
@@ -175,6 +171,10 @@
             return modelKey;
         }
 
+        public get isDirty(): boolean {
+            return (this._instanceDirtyFlags !== 0) || this._modelDirty;
+        }
+
         protected static GetOrAddModelCache<TInstData>(key: string, factory: (key: string) => ModelRenderCache<TInstData>): ModelRenderCache<TInstData> {
             return <ModelRenderCache<TInstData>>SmartPropertyPrim.ModelCache.getOrAddWithFactory(key, factory);
         }
@@ -248,6 +248,11 @@
             let propMask = propInfo.flagId;
             this.propertyChanged.notifyObservers(info, propMask);
 
+            // If the property belong to a group, check if it's a cached one, and dirty its render sprite accordingly
+            if (this instanceof Group2D) {
+                this.handleGroupChanged(propInfo);
+            }
+
             // Check if we need to dirty only if the type change and make the test
             var skipDirty = false;
             if (typeLevelCompare && curValue != null && newValue != null) {
@@ -260,12 +265,12 @@
             // Set the dirty flags
             if (!skipDirty) {
                 if (propInfo.kind === Prim2DPropInfo.PROPKIND_MODEL) {
-                    if ((this._instanceDirtyFlags === 0) && (!this._modelDirty)) {
+                    if (!this.isDirty) {
                         this.onPrimBecomesDirty();
                     }
                     this._modelDirty = true;
                 } else if (propInfo.kind === Prim2DPropInfo.PROPKIND_INSTANCE) {
-                    if ((this._instanceDirtyFlags === 0) && (!this._modelDirty)) {
+                    if (!this.isDirty) {
                         this.onPrimBecomesDirty();
                     }
                     this._instanceDirtyFlags |= propMask;
@@ -273,6 +278,10 @@
             }
         }
 
+        protected handleGroupChanged(prop: Prim2DPropInfo) {
+
+        }
+
         public checkPropertiesDirty(flags: number): boolean {
             return (this._instanceDirtyFlags & flags) !== 0;
         }

+ 10 - 7
src/Canvas2d/babylon.sprite2d.ts

@@ -26,8 +26,10 @@
             engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
 
             engine.updateAndBindInstancesBuffer(instanceInfo._instancesBuffer, null, this.instancingAttributes);
-
+            var cur = engine.getAlphaMode();
+            engine.setAlphaMode(Engine.ALPHA_COMBINE);
             engine.draw(true, 0, 6, instanceInfo._instancesData.usedElementCount);
+            engine.setAlphaMode(cur);
 
             engine.unBindInstancesBuffer(instanceInfo._instancesBuffer, this.instancingAttributes);
 
@@ -63,7 +65,7 @@
     }
 
     @className("Sprite2D")
-    export class Sprite2D extends RenderablePrim2D<Sprite2DInstanceData> {
+    export class Sprite2D extends Shape2D<Sprite2DInstanceData> {
 
         public static textureProperty: Prim2DPropInfo;
         public static spriteSizeProperty: Prim2DPropInfo;
@@ -71,7 +73,7 @@
         public static spriteFrameProperty: Prim2DPropInfo;
         public static invertYProperty: Prim2DPropInfo;
 
-        @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Sprite2D.textureProperty = pi)
+        @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Sprite2D.textureProperty = pi)
         public get texture(): Texture {
             return this._texture;
         }
@@ -80,7 +82,7 @@
             this._texture = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, pi => Sprite2D.spriteSizeProperty = pi, false, true)
+        @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Sprite2D.spriteSizeProperty = pi, false, true)
         public get spriteSize(): Size {
             return this._size;
         }
@@ -89,7 +91,7 @@
             this._size = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, pi => Sprite2D.spriteLocationProperty = pi)
+        @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 3, pi => Sprite2D.spriteLocationProperty = pi)
         public get spriteLocation(): Vector2 {
             return this._location;
         }
@@ -98,7 +100,7 @@
             this._location = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, pi => Sprite2D.spriteFrameProperty = pi)
+        @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 4, pi => Sprite2D.spriteFrameProperty = pi)
         public get spriteFrame(): number {
             return this._spriteFrame;
         }
@@ -107,7 +109,7 @@
             this._spriteFrame = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 5, pi => Sprite2D.invertYProperty = pi)
+        @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 5, pi => Sprite2D.invertYProperty = pi)
         public get invertY(): boolean {
             return this._invertY;
         }
@@ -128,6 +130,7 @@
             this.spriteLocation = spriteLocation;
             this.spriteFrame = 0;
             this.invertY = invertY;
+            this._isTransparent = true;
         }
 
         public static Create(parent: Prim2DBase, id: string, x: number, y: number, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean = false): Sprite2D {