|
@@ -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;
|
|
}
|
|
}
|
|
}
|
|
}
|