- deleted the tilepacker.

This commit is contained in:
Christoph Oelckers 2019-09-18 22:16:56 +02:00
parent e0f823a492
commit 30dac6be74
2 changed files with 0 additions and 420 deletions

View file

@ -1,54 +0,0 @@
/*
* tilepacker.h
* A k-d tree based bin packer that organizes rectangular tiles to fit
* neatly into one texture.
*
* Copyright © 2018, Alex Dawson. All rights reserved.
*/
#ifndef TILEPACKER_H_
#define TILEPACKER_H_
#define MAXTILESHEETS 64
#define MAXPACKEDTILES MAXTILES+1
typedef struct
{
uint32_t u, v, width, height;
} TileRect;
typedef struct
{
uint32_t tilesheetID;
TileRect rect;
} Tile;
// Initialize the specified tilesheet
// tilesheetId must be less than MAXTILESHEETS
// Re-initializing an existing tilesheet will discard all of its contents
void tilepacker_initTilesheet(uint32_t tilesheetID, uint32_t tilesheetWidth, uint32_t tilesheetHeight);
// Adds a tile into sorted collection to be packed
// uid can be any unique key -- picnums are used for indexed colour textures
void tilepacker_addTile(uint32_t tileUID, uint32_t tileWidth, uint32_t tileHeight);
// Packs the sorted collection of tiles that have been added with tilepacker_addTile()
// Returns true if all nodes could be packed succesfully into the specified tilesheet
// tilepacker_pack can be called again with a different tilesheetId to pack the remaining tiles
char tilepacker_pack(uint32_t tilesheetID);
// Discard the rejected tiles so that they will not be carried over to the next call to tilepacker_pack
void tilepacker_discardRejects();
// Sets pOutput to contain the Tile information for a packed tile with the given tileUID
// Returns true if the Tile has been packed, false otherwise
// If pOutput is NULL, the function solely returns whether or not the Tile has been packed
char tilepacker_getTile(uint32_t tileUID, Tile *pOutput);
// Returns true if the Tile with tileUID has been packed, false otherwise
static inline char tilepacker_isTilePacked(uint32_t tileUID)
{
return tilepacker_getTile(tileUID, NULL);
};
#endif /* TILEPACKER_H_ */

View file

@ -1,366 +0,0 @@
/*
* tilepacker.cpp
* A k-d tree based bin packer that organizes rectangular tiles to fit
* neatly into one texture.
*
* Copyright © 2018, Alex Dawson. All rights reserved.
*/
#include "compat.h"
#include "build.h"
#include "tilepacker.h"
typedef struct TreeNode
{
struct TreeNode *pParent, *pChild0, *pChild1;
TileRect rect;
uint32_t maxSide;
uint32_t tileUID;
} TreeNode;
// allocate all the memory we could ever need up front to avoid dynamic allocation
#define NUM_NON_ROOT_NODES MAXPACKEDTILES*2
#define NUM_NODES NUM_NON_ROOT_NODES+MAXTILESHEETS
TreeNode nodes[NUM_NODES];
uint32_t heapNodes = 0;
uint32_t nextTreeNodeIndex = NUM_NON_ROOT_NODES-1;
Tile tiles[MAXPACKEDTILES];
// node rejection queue implemented using a circular buffer
#define MAX_REJECTS (MAXPACKEDTILES-1)
TreeNode rejectQueue[MAX_REJECTS];
uint32_t rejectQueueHeadIndex = 0;
uint32_t numRejected = 0;
#if 0
static void maxheap_bubbleUp(uint32_t nodeIndex)
{
while (true)
{
if (nodeIndex == 0)
{
return;
}
uint32_t parentIndex = (nodeIndex-1)/2;
if (nodes[parentIndex].maxSide >= nodes[nodeIndex].maxSide)
{
return;
}
// bubble up
TreeNode temp = nodes[nodeIndex];
nodes[nodeIndex] = nodes[parentIndex];
nodes[parentIndex] = temp;
nodeIndex = parentIndex;
}
}
#endif
static void maxheap_bubbleDown(uint32_t nodeIndex)
{
while (true)
{
uint32_t largestChildIndex = 2*nodeIndex + 1;
if (largestChildIndex >= heapNodes)
{
return;
}
uint32_t rightChildIndex = largestChildIndex+1;
if (rightChildIndex < heapNodes &&
nodes[rightChildIndex].maxSide > nodes[largestChildIndex].maxSide)
{
largestChildIndex = rightChildIndex;
}
if (nodes[largestChildIndex].maxSide <= nodes[nodeIndex].maxSide)
{
return;
}
// bubble down
TreeNode temp = nodes[nodeIndex];
nodes[nodeIndex] = nodes[largestChildIndex];
nodes[largestChildIndex] = temp;
nodeIndex = largestChildIndex;
}
}
static void maxheap_buildHeap()
{
for (int i = (heapNodes-2)/2; i >= 0; --i)
{
maxheap_bubbleDown(i);
}
}
static TreeNode* maxheap_pop()
{
if (heapNodes == 0)
{
return NULL;
}
// swap the root and the last node
TreeNode temp = nodes[0];
nodes[0] = nodes[heapNodes-1];
nodes[heapNodes-1] = temp;
--heapNodes;
maxheap_bubbleDown(0);
return nodes+heapNodes;
}
static TreeNode* maxheap_reserveNode(TreeNode *pParent,
TreeNode *pChild0,
TreeNode *pChild1,
TileRect rectangle,
uint32_t tileUID)
{
if (heapNodes == nextTreeNodeIndex+1)
{
// our tree and heap are going to collide
#ifdef DEBUGGINGAIDS
OSD_Printf("tilepacker: maxheap_reserveNode(): out of nodes\n");
#endif
return NULL;
}
nodes[heapNodes] = {(TreeNode*) pParent,
(TreeNode*) pChild0,
(TreeNode*) pChild1,
rectangle,
rectangle.width >= rectangle.height ? rectangle.width : rectangle.height,
tileUID};
++heapNodes;
return nodes+heapNodes-1;
}
static TreeNode* kdtree_reserveNode(TreeNode *pParent,
TreeNode *pChild0,
TreeNode *pChild1,
TileRect rectangle,
uint32_t tileUID)
{
if (nextTreeNodeIndex == heapNodes-1)
{
// our tree and heap are going to collide
#ifdef DEBUGGINGAIDS
OSD_Printf("tilepacker: kdtree_reserveNode(): out of nodes\n");
#endif
return NULL;
}
nodes[nextTreeNodeIndex] = {(TreeNode*) pParent,
(TreeNode*) pChild0,
(TreeNode*) pChild1,
rectangle,
rectangle.width >= rectangle.height ? rectangle.width : rectangle.height,
tileUID};
--nextTreeNodeIndex;
return nodes+nextTreeNodeIndex+1;
}
static char kdtree_add(uint32_t treeIndex, TreeNode *pNode)
{
TreeNode *pCurrentNode = nodes+NUM_NODES-treeIndex-1;
while (true)
{
// is this node large enough to contain the currentNode?
if (pCurrentNode->rect.width >= pNode->rect.width &&
pCurrentNode->rect.height >= pNode->rect.height)
{
if (pCurrentNode->tileUID != (uint32_t) -1)
{
// if we're not a leaf node, continue to tunnel down until we reach a leaf on the 0-side of the tree
pCurrentNode = pCurrentNode->pChild0;
continue;
}
// otherwise, we have the node we want to split to insert our new node
break;
}
// climb out until we find an unexplored 1-side branch to tunnel down
TreeNode *lastNode;
do
{
lastNode = pCurrentNode;
pCurrentNode = pCurrentNode->pParent;
if (pCurrentNode == NULL)
{
// we've fully explored the tree and asked for the root's parent
return false;
}
} while (pCurrentNode->pChild1 == lastNode || !pCurrentNode->pChild1);
pCurrentNode = pCurrentNode->pChild1;
}
// assign the empty leaf node the tileUID we want to add, then create children to split the node's remaining space
pCurrentNode->tileUID = pNode->tileUID;
tiles[pNode->tileUID].tilesheetID = treeIndex;
tiles[pNode->tileUID].rect = {pCurrentNode->rect.u,
pCurrentNode->rect.v,
pNode->rect.width,
pNode->rect.height};
uint32_t rightSideWidth = pCurrentNode->rect.width - pNode->rect.width;
uint32_t bottomSideHeight = pCurrentNode->rect.height - pNode->rect.height;
TileRect rect0 = {pCurrentNode->rect.u+pNode->rect.width, pCurrentNode->rect.v, rightSideWidth, pCurrentNode->rect.height};
TileRect rect1 = {pCurrentNode->rect.u, pCurrentNode->rect.v+pNode->rect.height, pNode->rect.width, bottomSideHeight};
// decide which way to split
if (rightSideWidth < bottomSideHeight)
{
//POGOTODO: instead of creating two new children and having the tree contain filled nodes
// I should usurp the place of pCurrentNode with the largest child,
// since the smaller child can always be contained within the larger one.
// This requires an additional sort/bubbling step and always splitting width/height
// based on level rather than max in order for the organization to work.
// we'll have a more width-confined space to our right, so chop it off horizontally
// rather than create a tall, potentially narrow empty area to have to fill later
rect0 = {pCurrentNode->rect.u, pCurrentNode->rect.v+pNode->rect.height, pCurrentNode->rect.width, bottomSideHeight};
rect1 = {pCurrentNode->rect.u+pNode->rect.width, pCurrentNode->rect.v, rightSideWidth, pNode->rect.height};
}
// it's important that the larger area become child0
pCurrentNode->pChild0 = kdtree_reserveNode(pCurrentNode,
NULL,
NULL,
rect0,
-1);
pCurrentNode->pChild1 = kdtree_reserveNode(pCurrentNode,
NULL,
NULL,
rect1,
-1);
return true;
}
static char rejectQueue_add(TreeNode *pNode)
{
if (numRejected >= MAX_REJECTS)
{
return false;
}
rejectQueue[(rejectQueueHeadIndex+numRejected) % MAX_REJECTS] = *pNode;
++numRejected;
return true;
}
static TreeNode* rejectQueue_remove()
{
Bassert(numRejected);
--numRejected;
TreeNode* pNode = rejectQueue+rejectQueueHeadIndex;
rejectQueueHeadIndex = (rejectQueueHeadIndex+1) % MAX_REJECTS;
return pNode;
}
/*static void tilepacker_deleteTree()
{
//POGOTODO: this
}*/
void tilepacker_initTilesheet(uint32_t tilesheetID, uint32_t tilesheetWidth, uint32_t tilesheetHeight)
{
//POGOTODO: delete the tree if it's already been initialized
nodes[NUM_NODES-tilesheetID-1] = {(TreeNode*) 0,
(TreeNode*) 0,
(TreeNode*) 0,
{0, 0, tilesheetWidth, tilesheetHeight},
tilesheetWidth >= tilesheetHeight ? tilesheetWidth : tilesheetHeight,
(uint32_t) -1}; // use the maximum uint32_t to indicate this node is empty space
}
void tilepacker_addTile(uint32_t tileUID, uint32_t tileWidth, uint32_t tileHeight)
{
if (nextTreeNodeIndex < heapNodes)
{
// cannot reserve any more tiles!
#ifdef DEBUGGINGAIDS
OSD_Printf("tilepacker: tilepacker_addTile(): out of nodes\n");
#endif
return;
}
if (tileWidth == 0 ||
tileHeight == 0)
{
// don't allow adding tiles with a width or height of 0
return;
}
maxheap_reserveNode((TreeNode*) 0,
(TreeNode*) 0,
(TreeNode*) 0,
{0, 0, tileWidth, tileHeight},
tileUID);
}
char tilepacker_pack(uint32_t tilesheetID)
{
if (tilesheetID >= MAXTILESHEETS)
{
return false;
}
for (uint32_t numLeft = numRejected; numLeft > 0; --numLeft)
{
TreeNode *pNode = rejectQueue_remove();
char success = kdtree_add(tilesheetID, pNode);
if (!success)
{
rejectQueue_add(pNode);
}
}
maxheap_buildHeap();
for (TreeNode *pNode = maxheap_pop(); pNode != NULL; pNode = maxheap_pop())
{
char success = kdtree_add(tilesheetID, pNode);
if (!success)
{
rejectQueue_add(pNode);
}
}
return numRejected == 0;
}
void tilepacker_discardRejects()
{
numRejected = 0;
}
char tilepacker_getTile(uint32_t tileUID, Tile *pOutput)
{
if (tileUID >= MAXPACKEDTILES)
{
return false;
}
Tile tile = tiles[tileUID];
if (tile.rect.width == 0)
{
// that tileUID has not been packed or didn't fit
return false;
}
if (pOutput)
{
*pOutput = tile;
}
return true;
}