浏览代码

Add margin support to RectPackingMap and MapTexture

nockawa 8 年之前
父节点
当前提交
d22cf6ca31
共有 2 个文件被更改,包括 67 次插入26 次删除
  1. 2 2
      src/Materials/Textures/babylon.mapTexture.ts
  2. 65 24
      src/Tools/babylon.rectPackingMap.ts

+ 2 - 2
src/Materials/Textures/babylon.mapTexture.ts

@@ -6,7 +6,7 @@
 
 
         private _replacedViewport: Viewport;
         private _replacedViewport: Viewport;
 
 
-        constructor(name: string, scene: Scene, size: ISize, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, useMipMap: boolean=false) {
+        constructor(name: string, scene: Scene, size: ISize, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, useMipMap: boolean=false, margin=0) {
             super(null, scene, !useMipMap, false, samplingMode);
             super(null, scene, !useMipMap, false, samplingMode);
 
 
             this.name = name;
             this.name = name;
@@ -15,7 +15,7 @@
             this.wrapV = Texture.CLAMP_ADDRESSMODE;
             this.wrapV = Texture.CLAMP_ADDRESSMODE;
 
 
             // Create the rectPackMap that will allocate portion of the texture
             // Create the rectPackMap that will allocate portion of the texture
-            this._rectPackingMap = new RectPackingMap(new Size(size.width, size.height));
+            this._rectPackingMap = new RectPackingMap(new Size(size.width, size.height), margin);
 
 
             // Create the texture that will store the content
             // Create the texture that will store the content
             this._texture = scene.getEngine().createRenderTargetTexture(size, { generateMipMaps: !this.noMipmap, type: Engine.TEXTURETYPE_UNSIGNED_INT });
             this._texture = scene.getEngine().createRenderTargetTexture(size, { generateMipMaps: !this.noMipmap, type: Engine.TEXTURETYPE_UNSIGNED_INT });

+ 65 - 24
src/Tools/babylon.rectPackingMap.ts

@@ -1,11 +1,11 @@
 module BABYLON {
 module BABYLON {
 
 
     /**
     /**
-  * This class describe a rectangle that were added to the map.
-  * You have access to its coordinates either in pixel or normalized (UV)
-  */
+     * This class describe a rectangle that were added to the map.
+     * You have access to its coordinates either in pixel or normalized (UV)
+     */
     export class PackedRect {
     export class PackedRect {
-        constructor(root: PackedRect, parent: PackedRect, pos: Vector2, size: Size) {
+        constructor(root: RectPackingMap, parent: PackedRect, pos: Vector2, size: Size) {
             this._pos         = pos;
             this._pos         = pos;
             this._size        = size;
             this._size        = size;
             this._root        = root;
             this._root        = root;
@@ -20,7 +20,7 @@
         /**
         /**
          * @returns the position of this node into the map
          * @returns the position of this node into the map
          */
          */
-        public get pos() {
+        public get pos(): Vector2 {
             return this._pos;
             return this._pos;
         }
         }
 
 
@@ -32,25 +32,49 @@
         }
         }
 
 
         /**
         /**
+         * Retrieve the inner position (considering the margin) and stores it into the res object
+         * @param res must be a valid Vector2 that will contain the inner position after this call
+         */
+        public getInnerPosToRef(res: Vector2) {
+            let m = this._root._margin;
+            res.x = this._pos.x + m;
+            res.y = this._pos.y + m;
+        }
+
+        /**
+         * Retrieve the inner size (considering the margin) and stores it into the res object
+         * @param res must be a valid Size that will contain the inner size after this call
+         */
+        public getInnerSizeToRef(res: Size) {
+            let m = this._root._margin;
+            res.width = this._contentSize.width - (m*2);
+            res.height = this._contentSize.height - (m*2);
+        }
+
+        /**
          * Compute the UV of the top/left, top/right, bottom/right, bottom/left points of the rectangle this node handles into the map
          * Compute the UV of the top/left, top/right, bottom/right, bottom/left points of the rectangle this node handles into the map
          * @returns And array of 4 Vector2, containing UV coordinates for the four corners of the Rectangle into the map
          * @returns And array of 4 Vector2, containing UV coordinates for the four corners of the Rectangle into the map
          */
          */
         public get UVs(): Vector2[] {
         public get UVs(): Vector2[] {
-            return this.getUVsForCustomSize(this._root._size);
+            if (!this._contentSize) {
+                throw new Error("Can't compute UVs for this object because it's nor allocated");
+            }
+            return this.getUVsForCustomSize(this._contentSize);
         }
         }
 
 
-
         /**
         /**
-         * You may have allocated the PackedRect using over-provisioning (you allocated more than you need in order to prevent frequent deallocations/reallocations) and then using only a part of the PackRect.
+         * You may have allocated the PackedRect using over-provisioning (you allocated more than you need in order to prevent frequent deallocations/reallocations) 
+         * and then using only a part of the PackRect.
          * This method will return the UVs for this part by given the custom size of what you really use
          * This method will return the UVs for this part by given the custom size of what you really use
          * @param customSize must be less/equal to the allocated size, UV will be compute from this 
          * @param customSize must be less/equal to the allocated size, UV will be compute from this 
          */
          */
         public getUVsForCustomSize(customSize: Size): Vector2[] {
         public getUVsForCustomSize(customSize: Size): Vector2[] {
             var mainWidth = this._root._size.width;
             var mainWidth = this._root._size.width;
             var mainHeight = this._root._size.height;
             var mainHeight = this._root._size.height;
+            let margin = this._root._margin;
 
 
-            var topLeft = new Vector2(this._pos.x / mainWidth, this._pos.y / mainHeight);
-            var rightBottom = new Vector2((this._pos.x + customSize.width - 1) / mainWidth, (this._pos.y + customSize.height - 1) / mainHeight);
+            var topLeft = new Vector2((this._pos.x+margin) / mainWidth, (this._pos.y+margin) / mainHeight);
+            var rightBottom = new Vector2((this._pos.x + customSize.width + margin - 1) / mainWidth, (this._pos.y + customSize.height + margin - 1) / mainHeight);
             var uvs = new Array<Vector2>();
             var uvs = new Array<Vector2>();
             uvs.push(topLeft);
             uvs.push(topLeft);
             uvs.push(new Vector2(rightBottom.x, topLeft.y));
             uvs.push(new Vector2(rightBottom.x, topLeft.y));
@@ -94,6 +118,7 @@
 
 
         private findNode(size: Size): PackedRect {
         private findNode(size: Size): PackedRect {
             var resNode: PackedRect = null;
             var resNode: PackedRect = null;
+            let margin = this._root._margin * 2;
 
 
             // If this node is used, recurse to each of his subNodes to find an available one in its branch
             // If this node is used, recurse to each of his subNodes to find an available one in its branch
             if (this.isUsed) {
             if (this.isUsed) {
@@ -110,7 +135,7 @@
 
 
             // The node is free, but was previously allocated (_initialSize is set), rely on initialSize to make the test as it's the space we have
             // The node is free, but was previously allocated (_initialSize is set), rely on initialSize to make the test as it's the space we have
             else if (this._initialSize) {
             else if (this._initialSize) {
-                if ((size.width <= this._initialSize.width) && (size.height <= this._initialSize.height))
+                if (((size.width+margin) <= this._initialSize.width) && ((size.height+margin) <= this._initialSize.height))
                 {
                 {
                     resNode = this;
                     resNode = this;
                 } else {
                 } else {
@@ -119,28 +144,35 @@
             }
             }
 
 
             // The node is free and empty, rely on its size for the test
             // The node is free and empty, rely on its size for the test
-            else if ((size.width <= this._size.width) && (size.height <= this._size.height)) {
+            else if (((size.width+margin) <= this._size.width) && ((size.height+margin) <= this._size.height)) {
                 resNode = this;
                 resNode = this;
             }
             }
             return resNode;
             return resNode;
         }
         }
 
 
+        private static  TpsSize = Size.Zero();
         private splitNode(contentSize: Size): PackedRect {
         private splitNode(contentSize: Size): PackedRect {
+            let cs = PackedRect.TpsSize;
+            let margin = this._root._margin*2;
+            cs.copyFrom(contentSize);
+            cs.width += margin;
+            cs.height += margin;
+
             // If there's no contentSize but an initialSize it means this node were previously allocated, but freed, we need to create a _leftNode as subNode and use to allocate the space we need (and this node will have a right/bottom subNode for the space left as this._initialSize may be greater than contentSize)
             // If there's no contentSize but an initialSize it means this node were previously allocated, but freed, we need to create a _leftNode as subNode and use to allocate the space we need (and this node will have a right/bottom subNode for the space left as this._initialSize may be greater than contentSize)
             if (!this._contentSize && this._initialSize) {
             if (!this._contentSize && this._initialSize) {
-                this._contentSize = contentSize.clone();
+                this._contentSize = cs.clone();
                 this._leftNode = new PackedRect(this._root, this, new Vector2(this._pos.x, this._pos.y), new Size(this._initialSize.width, this._initialSize.height));
                 this._leftNode = new PackedRect(this._root, this, new Vector2(this._pos.x, this._pos.y), new Size(this._initialSize.width, this._initialSize.height));
                 return this._leftNode.splitNode(contentSize);
                 return this._leftNode.splitNode(contentSize);
             } else {
             } else {
-                this._contentSize = contentSize.clone();
-                this._initialSize = contentSize.clone();
+                this._contentSize = cs.clone();
+                this._initialSize = cs.clone();
 
 
-                if (contentSize.width !== this._size.width) {
-                    this._rightNode = new PackedRect(this._root, this, new Vector2(this._pos.x + contentSize.width, this._pos.y), new Size(this._size.width - contentSize.width, contentSize.height));
+                if (cs.width !== this._size.width) {
+                    this._rightNode = new PackedRect(this._root, this, new Vector2(this._pos.x + cs.width, this._pos.y), new Size(this._size.width - cs.width, cs.height));
                 }
                 }
 
 
-                if (contentSize.height !== this._size.height) {
-                    this._bottomNode = new PackedRect(this._root, this, new Vector2(this._pos.x, this._pos.y + contentSize.height), new Size(this._size.width, this._size.height - contentSize.height));
+                if (cs.height !== this._size.height) {
+                    this._bottomNode = new PackedRect(this._root, this, new Vector2(this._pos.x, this._pos.y + cs.height), new Size(this._size.width, this._size.height - cs.height));
                 }
                 }
                 return this;
                 return this;
             }
             }
@@ -170,10 +202,13 @@
             var levelSize = 0;
             var levelSize = 0;
 
 
             if (!this.isUsed) {
             if (!this.isUsed) {
-                if (this._initialSize) {
-                    levelSize = this._initialSize.surface;
+                let margin = this._root._margin;
+                let is = this._initialSize;
+                if (is) {
+                    levelSize = is.surface - (is.width*margin) - (is.height*margin);
                 } else {
                 } else {
-                    levelSize = this._size.surface;
+                    let size = this._size;
+                    levelSize = size.surface - (size.width*margin) - (size.height*margin);
                 }
                 }
             }
             }
 
 
@@ -188,7 +223,7 @@
             return levelSize + size;
             return levelSize + size;
         }
         }
 
 
-        protected _root: PackedRect;
+        protected _root: RectPackingMap;
         protected _parent: PackedRect;
         protected _parent: PackedRect;
         private _contentSize: Size;
         private _contentSize: Size;
         private _initialSize: Size;
         private _initialSize: Size;
@@ -204,15 +239,19 @@
      * The purpose of this class is to pack several Rectangles into a big map, while trying to fit everything as optimally as possible.
      * The purpose of this class is to pack several Rectangles into a big map, while trying to fit everything as optimally as possible.
      * This class is typically used to build lightmaps, sprite map or to pack several little textures into a big one.
      * This class is typically used to build lightmaps, sprite map or to pack several little textures into a big one.
      * Note that this class allows allocated Rectangles to be freed: that is the map is dynamically maintained so you can add/remove rectangle based on their life-cycle.
      * Note that this class allows allocated Rectangles to be freed: that is the map is dynamically maintained so you can add/remove rectangle based on their life-cycle.
+     * In case you need a margin around the allocated rect, specify the amount in the margin argument during construction.
+     * In such case you will have to rely on innerPositionToRef and innerSizeToRef calls to get the proper size
      */
      */
     export class RectPackingMap extends PackedRect {
     export class RectPackingMap extends PackedRect {
         /**
         /**
          * Create an instance of the object with a dimension using the given size
          * Create an instance of the object with a dimension using the given size
          * @param size The dimension of the rectangle that will contain all the sub ones.
          * @param size The dimension of the rectangle that will contain all the sub ones.
+         * @param margin The margin (empty space) created (in pixels) around the allocated Rectangles
          */
          */
-        constructor(size: Size) {
+        constructor(size: Size, margin=0) {
             super(null, null, Vector2.Zero(), size);
             super(null, null, Vector2.Zero(), size);
 
 
+            this._margin = margin;
             this._root = this;
             this._root = this;
         }
         }
 
 
@@ -236,5 +275,7 @@
 
 
             return freeSize / (this._size.width * this._size.height);
             return freeSize / (this._size.width * this._size.height);
         }
         }
+
+        public _margin: number;
     }
     }
 }
 }