mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-25 11:10:47 +00:00
- deleted the tilepacker.
This commit is contained in:
parent
e0f823a492
commit
30dac6be74
2 changed files with 0 additions and 420 deletions
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue