Pull out xml loading to assetmanager code
This commit is contained in:
parent
1d09bdfa20
commit
c93e6ca37c
@ -18,10 +18,525 @@
|
||||
|
||||
#include "Dengine/AssetManager.h"
|
||||
#include "Dengine/Debug.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
#include "Dengine/OpenGL.h"
|
||||
#include "Dengine/Platform.h"
|
||||
|
||||
INTERNAL AtlasSubTexture *getAtlasSubTexture(TexAtlas *atlas, char *key)
|
||||
/*
|
||||
*********************************
|
||||
* XML Operations
|
||||
*********************************
|
||||
*/
|
||||
enum XmlTokenType
|
||||
{
|
||||
xmltokentype_unknown,
|
||||
xmltokentype_openArrow,
|
||||
xmltokentype_closeArrow,
|
||||
xmltokentype_name,
|
||||
xmltokentype_value,
|
||||
xmltokentype_equals,
|
||||
xmltokentype_quotes,
|
||||
xmltokentype_backslash,
|
||||
xmltokentype_count,
|
||||
};
|
||||
|
||||
typedef struct XmlToken
|
||||
{
|
||||
// TODO(doyle): Dynamic size string in tokens maybe.
|
||||
enum XmlTokenType type;
|
||||
char string[128];
|
||||
i32 len;
|
||||
} XmlToken;
|
||||
|
||||
INTERNAL XmlToken *tokeniseXmlBuffer(MemoryArena *arena, char *buffer,
|
||||
i32 bufferSize, int *numTokens)
|
||||
{
|
||||
XmlToken *xmlTokens = PLATFORM_MEM_ALLOC(arena, 8192, XmlToken);
|
||||
i32 tokenIndex = 0;
|
||||
for (i32 i = 0; i < bufferSize; i++)
|
||||
{
|
||||
char c = (CAST(char *) buffer)[i];
|
||||
switch (c)
|
||||
{
|
||||
case '<':
|
||||
case '>':
|
||||
case '=':
|
||||
case '/':
|
||||
{
|
||||
|
||||
enum XmlTokenType type = xmltokentype_unknown;
|
||||
if (c == '<')
|
||||
{
|
||||
type = xmltokentype_openArrow;
|
||||
}
|
||||
else if (c == '>')
|
||||
{
|
||||
type = xmltokentype_closeArrow;
|
||||
}
|
||||
else if (c == '=')
|
||||
{
|
||||
type = xmltokentype_equals;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = xmltokentype_backslash;
|
||||
}
|
||||
|
||||
xmlTokens[tokenIndex].type = type;
|
||||
xmlTokens[tokenIndex].len = 1;
|
||||
tokenIndex++;
|
||||
break;
|
||||
}
|
||||
|
||||
case '"':
|
||||
{
|
||||
xmlTokens[tokenIndex].type = xmltokentype_value;
|
||||
for (i32 j = i + 1; j < bufferSize; j++)
|
||||
{
|
||||
char c = (CAST(char *) buffer)[j];
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlTokens[tokenIndex].string[xmlTokens[tokenIndex].len++] =
|
||||
c;
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(xmlTokens[tokenIndex].len <
|
||||
ARRAY_COUNT(xmlTokens[tokenIndex].string));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(doyle): +1 to skip the closing quotes
|
||||
i += (xmlTokens[tokenIndex].len + 1);
|
||||
tokenIndex++;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if ((c >= 'a' && c <= 'z') || c >= 'A' && c <= 'Z')
|
||||
{
|
||||
xmlTokens[tokenIndex].type = xmltokentype_name;
|
||||
for (i32 j = i; j < bufferSize; j++)
|
||||
{
|
||||
char c = (CAST(char *) buffer)[j];
|
||||
|
||||
if (c == ' ' || c == '=' || c == '>' || c == '<' ||
|
||||
c == '\\')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlTokens[tokenIndex]
|
||||
.string[xmlTokens[tokenIndex].len++] = c;
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(xmlTokens[tokenIndex].len <
|
||||
ARRAY_COUNT(xmlTokens[tokenIndex].string));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
i += xmlTokens[tokenIndex].len;
|
||||
tokenIndex++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(doyle): Dynamic token allocation
|
||||
*numTokens = 8192;
|
||||
return xmlTokens;
|
||||
}
|
||||
|
||||
INTERNAL XmlNode *buildXmlTree(MemoryArena *arena, XmlToken *xmlTokens,
|
||||
i32 numTokens)
|
||||
{
|
||||
XmlNode *root = PLATFORM_MEM_ALLOC(arena, 1, XmlNode);
|
||||
if (!root) return NULL;
|
||||
|
||||
XmlNode *node = root;
|
||||
node->parent = node;
|
||||
|
||||
// NOTE(doyle): Used for when closing a node with many children. We
|
||||
// automatically assign the next child after each child close within
|
||||
// a group. Hence on the last child, we open another node but the next
|
||||
// token indicates the group is closing- we need to set the last child's
|
||||
// next reference to NULL
|
||||
XmlNode *prevNode = NULL;
|
||||
for (i32 i = 0; i < numTokens; i++)
|
||||
{
|
||||
XmlToken *token = &xmlTokens[i];
|
||||
|
||||
switch (token->type)
|
||||
{
|
||||
|
||||
case xmltokentype_openArrow:
|
||||
{
|
||||
/* Open arrows indicate closing parent node or new node name */
|
||||
XmlToken *nextToken = &xmlTokens[++i];
|
||||
if (nextToken->type == xmltokentype_backslash)
|
||||
{
|
||||
nextToken = &xmlTokens[++i];
|
||||
if (common_strcmp(nextToken->string, node->parent->name) == 0)
|
||||
{
|
||||
node->parent->isClosed = TRUE;
|
||||
|
||||
if (prevNode)
|
||||
{
|
||||
prevNode->next = NULL;
|
||||
}
|
||||
|
||||
XmlNode *parent = node->parent;
|
||||
PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode));
|
||||
node = node->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG(
|
||||
"Closing xml node name does not match parent name");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (nextToken->type == xmltokentype_name)
|
||||
{
|
||||
node->name = nextToken->string;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unexpected token type after open arrow");
|
||||
#endif
|
||||
}
|
||||
|
||||
token = nextToken;
|
||||
break;
|
||||
}
|
||||
|
||||
case xmltokentype_name:
|
||||
{
|
||||
// TODO(doyle): Store latest attribute pointer so we aren't always
|
||||
// chasing the linked list each iteration. Do the same for children
|
||||
// node
|
||||
|
||||
/* Xml Attributes are a linked list, get first free entry */
|
||||
XmlAttribute *attribute = &node->attribute;
|
||||
if (attribute->init)
|
||||
{
|
||||
while (attribute->next)
|
||||
attribute = attribute->next;
|
||||
|
||||
attribute->next = PLATFORM_MEM_ALLOC(arena, 1, XmlAttribute);
|
||||
attribute = attribute->next;
|
||||
}
|
||||
|
||||
/* Just plain text is a node attribute name */
|
||||
attribute->name = token->string;
|
||||
|
||||
/* Followed by the value */
|
||||
token = &xmlTokens[++i];
|
||||
attribute->value = token->string;
|
||||
attribute->init = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
case xmltokentype_closeArrow:
|
||||
{
|
||||
XmlToken prevToken = xmlTokens[i - 1];
|
||||
prevNode = node;
|
||||
|
||||
/* Closed node means we can return to parent */
|
||||
if (prevToken.type == xmltokentype_backslash)
|
||||
{
|
||||
node->isClosed = TRUE;
|
||||
node = node->parent;
|
||||
}
|
||||
|
||||
if (!node->isClosed)
|
||||
{
|
||||
/* Unclosed node means next fields will be children of node */
|
||||
|
||||
/* If the first child is free allocate, otherwise we have to
|
||||
* iterate through the child's next node(s) */
|
||||
if (!node->child)
|
||||
{
|
||||
// TODO(doyle): Mem alloc error checking
|
||||
node->child = PLATFORM_MEM_ALLOC(arena, 1, XmlNode);
|
||||
node->child->parent = node;
|
||||
node = node->child;
|
||||
}
|
||||
else
|
||||
{
|
||||
XmlNode *nodeToCheck = node->child;
|
||||
while (nodeToCheck->next)
|
||||
nodeToCheck = nodeToCheck->next;
|
||||
|
||||
nodeToCheck->next = PLATFORM_MEM_ALLOC(arena, 1, XmlNode);
|
||||
nodeToCheck->next->parent = node;
|
||||
node = nodeToCheck->next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
|
||||
XmlNode *root)
|
||||
{
|
||||
XmlNode *node = root;
|
||||
while (node)
|
||||
{
|
||||
if (common_strcmp(node->name, "TextureAtlas") == 0)
|
||||
{
|
||||
XmlNode *atlasXmlNode = node;
|
||||
TexAtlas *atlasEntry = NULL;
|
||||
if (common_strcmp(node->attribute.name, "imagePath") == 0)
|
||||
{
|
||||
char *imageName = atlasXmlNode->attribute.value;
|
||||
atlasEntry = asset_makeTexAtlas(assetManager, arena, imageName);
|
||||
|
||||
char *dataDir = "data/textures/WorldTraveller/";
|
||||
char imagePath[512] = {0};
|
||||
common_strncat(imagePath, dataDir, common_strlen(dataDir));
|
||||
common_strncat(imagePath, imageName, common_strlen(imageName));
|
||||
|
||||
asset_loadTextureImage(assetManager, arena, imagePath,
|
||||
imageName);
|
||||
|
||||
atlasEntry->key = PLATFORM_MEM_ALLOC(
|
||||
arena, common_strlen(imageName) + 1, char);
|
||||
common_strncpy(atlasEntry->key, imageName,
|
||||
common_strlen(imageName));
|
||||
|
||||
atlasEntry->tex = asset_getTex(assetManager, imageName);
|
||||
|
||||
XmlNode *atlasChildNode = atlasXmlNode->child;
|
||||
while (atlasChildNode)
|
||||
{
|
||||
if (common_strcmp(atlasChildNode->name, "SubTexture") == 0)
|
||||
{
|
||||
XmlAttribute *subTextureAttrib =
|
||||
&atlasChildNode->attribute;
|
||||
|
||||
AtlasSubTexture newSubTexEntry = {0};
|
||||
while (subTextureAttrib)
|
||||
{
|
||||
|
||||
// TODO(doyle): Work around for now in xml reading,
|
||||
// reading the last node closing node not being
|
||||
// merged to the parent
|
||||
if (!subTextureAttrib->name) continue;
|
||||
|
||||
if (common_strcmp(subTextureAttrib->name, "name") ==
|
||||
0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
newSubTexEntry.key = value;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"x") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
|
||||
newSubTexEntry.rect.pos.x = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"y") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
newSubTexEntry.rect.pos.y = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"width") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
|
||||
newSubTexEntry.rect.size.w = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"height") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
|
||||
newSubTexEntry.rect.size.h = CAST(f32) intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG(
|
||||
"Unsupported xml attribute in SubTexture");
|
||||
#endif
|
||||
}
|
||||
subTextureAttrib = subTextureAttrib->next;
|
||||
}
|
||||
|
||||
// TODO(doyle): XML specifies 0,0 top left, we
|
||||
// prefer 0,0 bottom right, so offset by size since 0,0
|
||||
// is top left and size creates a bounding box below it
|
||||
newSubTexEntry.rect.pos.y =
|
||||
1024 - newSubTexEntry.rect.pos.y;
|
||||
newSubTexEntry.rect.pos.y -= newSubTexEntry.rect.size.h;
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(newSubTexEntry.key)
|
||||
#endif
|
||||
|
||||
u32 subTexHashIndex = common_murmurHash2(
|
||||
newSubTexEntry.key,
|
||||
common_strlen(newSubTexEntry.key), 0xDEADBEEF);
|
||||
subTexHashIndex =
|
||||
subTexHashIndex % ARRAY_COUNT(atlasEntry->subTex);
|
||||
|
||||
// NOTE(doyle): Hash collision
|
||||
AtlasSubTexture *subTexEntry =
|
||||
&atlasEntry->subTex[subTexHashIndex];
|
||||
if (subTexEntry->key)
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
|
||||
// NOTE(doyle): Two textures have the same access
|
||||
// name
|
||||
ASSERT(common_strcmp(subTexEntry->key,
|
||||
newSubTexEntry.key) != 0);
|
||||
#endif
|
||||
while (subTexEntry->next)
|
||||
subTexEntry = subTexEntry->next;
|
||||
|
||||
subTexEntry->next =
|
||||
PLATFORM_MEM_ALLOC(arena, 1, AtlasSubTexture);
|
||||
subTexEntry = subTexEntry->next;
|
||||
}
|
||||
|
||||
*subTexEntry = newSubTexEntry;
|
||||
i32 keyLen = common_strlen(newSubTexEntry.key);
|
||||
|
||||
subTexEntry->key =
|
||||
PLATFORM_MEM_ALLOC(arena, keyLen + 1, char);
|
||||
common_strncpy(subTexEntry->key, newSubTexEntry.key,
|
||||
keyLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unsupported xml node name not parsed");
|
||||
#endif
|
||||
}
|
||||
|
||||
atlasChildNode = atlasChildNode->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unsupported xml node");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unsupported xml node name not parsed");
|
||||
#endif
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL void recursiveFreeXmlTree(MemoryArena *arena, XmlNode *node)
|
||||
{
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
} else
|
||||
{
|
||||
// NOTE(doyle): First attribute is statically allocated, only if there's
|
||||
// more attributes do we dynamically allocate
|
||||
XmlAttribute *attrib = node->attribute.next;
|
||||
|
||||
while (attrib)
|
||||
{
|
||||
XmlAttribute *next = attrib->next;
|
||||
|
||||
attrib->name = NULL;
|
||||
attrib->value = NULL;
|
||||
PLATFORM_MEM_FREE(arena, attrib, sizeof(XmlAttribute));
|
||||
attrib = next;
|
||||
}
|
||||
|
||||
recursiveFreeXmlTree(arena, node->child);
|
||||
recursiveFreeXmlTree(arena, node->next);
|
||||
|
||||
node->name = NULL;
|
||||
node->isClosed = FALSE;
|
||||
PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode));
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL void freeXmlData(MemoryArena *arena, XmlToken *tokens, i32 numTokens,
|
||||
XmlNode *tree)
|
||||
{
|
||||
if (tree) recursiveFreeXmlTree(arena, tree);
|
||||
if (tokens) PLATFORM_MEM_FREE(arena, tokens, numTokens * sizeof(XmlToken));
|
||||
}
|
||||
|
||||
i32 asset_loadXmlFile(AssetManager *assetManager, MemoryArena *arena,
|
||||
PlatformFileRead *fileRead)
|
||||
{
|
||||
i32 result = 0;
|
||||
/* Tokenise buffer */
|
||||
i32 numTokens = 0;
|
||||
XmlToken *xmlTokens =
|
||||
tokeniseXmlBuffer(arena, fileRead->buffer, fileRead->size, &numTokens);
|
||||
|
||||
/* Build XML tree from tokens */
|
||||
XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens);
|
||||
|
||||
if (xmlTree)
|
||||
{
|
||||
/* Parse XML tree to game structures */
|
||||
parseXmlTreeToGame(assetManager, arena, xmlTree);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = -1;
|
||||
}
|
||||
|
||||
/* Free data */
|
||||
freeXmlData(arena, xmlTokens, numTokens, xmlTree);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
*********************************
|
||||
* Texture Operations
|
||||
*********************************
|
||||
*/
|
||||
INTERNAL AtlasSubTexture *getAtlasSubTex(TexAtlas *atlas, char *key)
|
||||
{
|
||||
u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(atlas->subTex));
|
||||
AtlasSubTexture *result = &atlas->subTex[hashIndex];
|
||||
@ -34,7 +549,7 @@ INTERNAL AtlasSubTexture *getAtlasSubTexture(TexAtlas *atlas, char *key)
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL AtlasSubTexture *makeAtlasSubTexture(TexAtlas *atlas,
|
||||
INTERNAL AtlasSubTexture *getFreeAtlasSubTexSlot(TexAtlas *atlas,
|
||||
MemoryArena *arena, char *key)
|
||||
{
|
||||
u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(atlas->subTex));
|
||||
@ -59,7 +574,7 @@ INTERNAL AtlasSubTexture *makeAtlasSubTexture(TexAtlas *atlas,
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL Animation *getFreeAnimSlot(Animation *table, u32 tableSize,
|
||||
INTERNAL Animation *getFreeAnimationSlot(Animation *table, u32 tableSize,
|
||||
MemoryArena *arena, char *key)
|
||||
{
|
||||
u32 hashIndex = common_getHashIndex(key, tableSize);
|
||||
@ -84,9 +599,9 @@ INTERNAL Animation *getFreeAnimSlot(Animation *table, u32 tableSize,
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect asset_getAtlasSubTexRect(TexAtlas *atlas, char *key)
|
||||
Rect asset_getSubTexRect(TexAtlas *atlas, char *key)
|
||||
{
|
||||
AtlasSubTexture *subTex = getAtlasSubTexture(atlas, key);
|
||||
AtlasSubTexture *subTex = getAtlasSubTex(atlas, key);
|
||||
Rect result = subTex->rect;
|
||||
return result;
|
||||
}
|
||||
@ -527,7 +1042,7 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
|
||||
char charTmp[2] = {0};
|
||||
charTmp[0] = charToEncode;
|
||||
AtlasSubTexture *subTex =
|
||||
makeAtlasSubTexture(fontAtlas, arena, charTmp);
|
||||
getFreeAtlasSubTexSlot(fontAtlas, arena, charTmp);
|
||||
|
||||
subTex->key = PLATFORM_MEM_ALLOC(arena, 1, char);
|
||||
subTex->key[0] = charToEncode;
|
||||
@ -615,7 +1130,7 @@ void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
|
||||
char *animName, TexAtlas *atlas, char **subTextureNames,
|
||||
i32 numSubTextures, f32 frameDuration)
|
||||
{
|
||||
Animation *anim = getFreeAnimSlot(
|
||||
Animation *anim = getFreeAnimationSlot(
|
||||
assetManager->anims, ARRAY_COUNT(assetManager->anims), arena, animName);
|
||||
|
||||
anim->atlas = atlas;
|
||||
@ -629,7 +1144,7 @@ void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
|
||||
anim->frameList = PLATFORM_MEM_ALLOC(arena, numSubTextures, char*);
|
||||
for (i32 i = 0; i < numSubTextures; i++)
|
||||
{
|
||||
AtlasSubTexture *subTex = getAtlasSubTexture(atlas, subTextureNames[i]);
|
||||
AtlasSubTexture *subTex = getAtlasSubTex(atlas, subTextureNames[i]);
|
||||
anim->frameList[i] = subTex->key;
|
||||
}
|
||||
|
||||
@ -656,3 +1171,29 @@ v2 asset_stringDimInPixels(const Font *const font, const char *const string)
|
||||
|
||||
return stringDim;
|
||||
}
|
||||
|
||||
void asset_unitTest(MemoryArena *arena)
|
||||
{
|
||||
PlatformFileRead xmlFileRead = {0};
|
||||
i32 result = platform_readFileToBuffer(
|
||||
arena, "data/textures/WorldTraveller/ClaudeSprite.xml", &xmlFileRead);
|
||||
if (result)
|
||||
{
|
||||
DEBUG_LOG(
|
||||
"unitTest() error: Could not load XML file for memory free test");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Tokenise buffer */
|
||||
i32 memBefore = arena->bytesAllocated;
|
||||
i32 numTokens = 0;
|
||||
XmlToken *xmlTokens = tokeniseXmlBuffer(arena, xmlFileRead.buffer,
|
||||
xmlFileRead.size, &numTokens);
|
||||
/* Build XML tree from tokens */
|
||||
XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens);
|
||||
freeXmlData(arena, xmlTokens, numTokens, xmlTree);
|
||||
i32 memAfter = arena->bytesAllocated;
|
||||
|
||||
ASSERT(memBefore == memAfter);
|
||||
}
|
||||
}
|
||||
|
30
src/Debug.c
30
src/Debug.c
@ -103,6 +103,36 @@ void debug_init(MemoryArena *arena, v2 windowSize, Font font)
|
||||
GLOBAL_debug.initialConsoleP = V2(consoleXPos, consoleYPos);
|
||||
}
|
||||
|
||||
void debug_recursivePrintXmlTree(XmlNode *root, i32 levelsDeep)
|
||||
{
|
||||
if (!root)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i32 i = 0; i < levelsDeep; i++)
|
||||
{
|
||||
printf("-");
|
||||
}
|
||||
|
||||
printf("%s ", root->name);
|
||||
|
||||
XmlAttribute *attribute = &root->attribute;
|
||||
printf("| %s = %s", attribute->name, attribute->value);
|
||||
|
||||
while (attribute->next)
|
||||
{
|
||||
attribute = attribute->next;
|
||||
printf("| %s = %04s", attribute->name, attribute->value);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
debug_recursivePrintXmlTree(root->child, levelsDeep+1);
|
||||
debug_recursivePrintXmlTree(root->next, levelsDeep);
|
||||
}
|
||||
}
|
||||
|
||||
void debug_callCountIncrement(i32 id)
|
||||
{
|
||||
ASSERT(id < debugcallcount_num);
|
||||
|
@ -56,7 +56,7 @@ void entity_updateAnim(Entity *entity, f32 dt)
|
||||
case entitytype_npc:
|
||||
char *frameName = anim->frameList[currEntityAnim->currFrame];
|
||||
Rect texRect =
|
||||
asset_getAtlasSubTexRect(anim->atlas, frameName);
|
||||
asset_getSubTexRect(anim->atlas, frameName);
|
||||
entity->renderSize = texRect.size;
|
||||
default:
|
||||
break;
|
||||
|
@ -211,7 +211,7 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera,
|
||||
|
||||
/* Get texture out */
|
||||
Rect charTexRect =
|
||||
asset_getAtlasSubTexRect(font->atlas, &CAST(char)codepoint);
|
||||
asset_getSubTexRect(font->atlas, &CAST(char)codepoint);
|
||||
|
||||
v4 deprecatedTexRect = {0};
|
||||
deprecatedTexRect.vec2[0] = charTexRect.pos;
|
||||
@ -251,7 +251,7 @@ void renderer_entity(Renderer *renderer, Rect camera, Entity *entity,
|
||||
EntityAnim *entityAnim = &entity->animList[entity->currAnimId];
|
||||
Animation *anim = entityAnim->anim;
|
||||
char *frameName = anim->frameList[entityAnim->currFrame];
|
||||
Rect animRect = asset_getAtlasSubTexRect(anim->atlas, frameName);
|
||||
Rect animRect = asset_getSubTexRect(anim->atlas, frameName);
|
||||
|
||||
// TODO(doyle): Switch to rect
|
||||
v4 animTexRect = {0};
|
||||
|
@ -111,541 +111,6 @@ INTERNAL void rendererInit(GameState *state, v2 windowSize)
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef struct XmlAttribute
|
||||
{
|
||||
b32 init;
|
||||
char *name;
|
||||
char *value;
|
||||
|
||||
struct XmlAttribute *next;
|
||||
} XmlAttribute;
|
||||
|
||||
typedef struct XmlNode
|
||||
{
|
||||
char *name;
|
||||
XmlAttribute attribute;
|
||||
|
||||
// NOTE(doyle): Track if node has more children
|
||||
b32 isClosed;
|
||||
|
||||
// NOTE(doyle): Direct child/parent
|
||||
struct XmlNode *parent;
|
||||
struct XmlNode *child;
|
||||
|
||||
// NOTE(doyle): Else all other nodes
|
||||
struct XmlNode *next;
|
||||
} XmlNode;
|
||||
|
||||
enum XmlTokenType
|
||||
{
|
||||
xmltokentype_unknown,
|
||||
xmltokentype_openArrow,
|
||||
xmltokentype_closeArrow,
|
||||
xmltokentype_name,
|
||||
xmltokentype_value,
|
||||
xmltokentype_equals,
|
||||
xmltokentype_quotes,
|
||||
xmltokentype_backslash,
|
||||
xmltokentype_count,
|
||||
};
|
||||
|
||||
typedef struct XmlToken
|
||||
{
|
||||
// TODO(doyle): Dynamic size string in tokens maybe.
|
||||
enum XmlTokenType type;
|
||||
char string[128];
|
||||
i32 len;
|
||||
} XmlToken;
|
||||
|
||||
#define DEBUG_RECURSIVE_PRINT_XML_TREE(sig) debug_recursivePrintXmlTree(sig, 1)
|
||||
INTERNAL void debug_recursivePrintXmlTree(XmlNode *root, i32 levelsDeep)
|
||||
{
|
||||
if (!root)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i32 i = 0; i < levelsDeep; i++)
|
||||
{
|
||||
printf("-");
|
||||
}
|
||||
|
||||
printf("%s ", root->name);
|
||||
|
||||
XmlAttribute *attribute = &root->attribute;
|
||||
printf("| %s = %s", attribute->name, attribute->value);
|
||||
|
||||
while (attribute->next)
|
||||
{
|
||||
attribute = attribute->next;
|
||||
printf("| %s = %04s", attribute->name, attribute->value);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
debug_recursivePrintXmlTree(root->child, levelsDeep+1);
|
||||
debug_recursivePrintXmlTree(root->next, levelsDeep);
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL XmlToken *tokeniseXmlBuffer(MemoryArena *arena, char *buffer,
|
||||
i32 bufferSize, int *numTokens)
|
||||
{
|
||||
/*
|
||||
**********************
|
||||
* Tokenise XML Buffer
|
||||
**********************
|
||||
*/
|
||||
XmlToken *xmlTokens = PLATFORM_MEM_ALLOC(arena, 8192, XmlToken);
|
||||
i32 tokenIndex = 0;
|
||||
for (i32 i = 0; i < bufferSize; i++)
|
||||
{
|
||||
char c = (CAST(char *) buffer)[i];
|
||||
switch (c)
|
||||
{
|
||||
case '<':
|
||||
case '>':
|
||||
case '=':
|
||||
case '/':
|
||||
{
|
||||
|
||||
enum XmlTokenType type = xmltokentype_unknown;
|
||||
if (c == '<')
|
||||
{
|
||||
type = xmltokentype_openArrow;
|
||||
}
|
||||
else if (c == '>')
|
||||
{
|
||||
type = xmltokentype_closeArrow;
|
||||
}
|
||||
else if (c == '=')
|
||||
{
|
||||
type = xmltokentype_equals;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = xmltokentype_backslash;
|
||||
}
|
||||
|
||||
xmlTokens[tokenIndex].type = type;
|
||||
xmlTokens[tokenIndex].len = 1;
|
||||
tokenIndex++;
|
||||
break;
|
||||
}
|
||||
|
||||
case '"':
|
||||
{
|
||||
xmlTokens[tokenIndex].type = xmltokentype_value;
|
||||
for (i32 j = i + 1; j < bufferSize; j++)
|
||||
{
|
||||
char c = (CAST(char *) buffer)[j];
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlTokens[tokenIndex].string[xmlTokens[tokenIndex].len++] =
|
||||
c;
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(xmlTokens[tokenIndex].len <
|
||||
ARRAY_COUNT(xmlTokens[tokenIndex].string));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(doyle): +1 to skip the closing quotes
|
||||
i += (xmlTokens[tokenIndex].len + 1);
|
||||
tokenIndex++;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if ((c >= 'a' && c <= 'z') || c >= 'A' && c <= 'Z')
|
||||
{
|
||||
xmlTokens[tokenIndex].type = xmltokentype_name;
|
||||
for (i32 j = i; j < bufferSize; j++)
|
||||
{
|
||||
char c = (CAST(char *) buffer)[j];
|
||||
|
||||
if (c == ' ' || c == '=' || c == '>' || c == '<' ||
|
||||
c == '\\')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlTokens[tokenIndex]
|
||||
.string[xmlTokens[tokenIndex].len++] = c;
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(xmlTokens[tokenIndex].len <
|
||||
ARRAY_COUNT(xmlTokens[tokenIndex].string));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
i += xmlTokens[tokenIndex].len;
|
||||
tokenIndex++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(doyle): Dynamic token allocation
|
||||
*numTokens = 8192;
|
||||
return xmlTokens;
|
||||
}
|
||||
|
||||
INTERNAL XmlNode *buildXmlTree(MemoryArena *arena, XmlToken *xmlTokens,
|
||||
i32 numTokens)
|
||||
{
|
||||
XmlNode *root = PLATFORM_MEM_ALLOC(arena, 1, XmlNode);
|
||||
XmlNode *node = root;
|
||||
node->parent = node;
|
||||
|
||||
// NOTE(doyle): Used for when closing a node with many children. We
|
||||
// automatically assign the next child after each child close within
|
||||
// a group. Hence on the last child, we open another node but the next
|
||||
// token indicates the group is closing- we need to set the last child's
|
||||
// next reference to NULL
|
||||
XmlNode *prevNode = NULL;
|
||||
for (i32 i = 0; i < numTokens; i++)
|
||||
{
|
||||
XmlToken *token = &xmlTokens[i];
|
||||
|
||||
switch (token->type)
|
||||
{
|
||||
|
||||
case xmltokentype_openArrow:
|
||||
{
|
||||
/* Open arrows indicate closing parent node or new node name */
|
||||
XmlToken *nextToken = &xmlTokens[++i];
|
||||
if (nextToken->type == xmltokentype_backslash)
|
||||
{
|
||||
nextToken = &xmlTokens[++i];
|
||||
if (common_strcmp(nextToken->string, node->parent->name) == 0)
|
||||
{
|
||||
node->parent->isClosed = TRUE;
|
||||
|
||||
if (prevNode)
|
||||
{
|
||||
prevNode->next = NULL;
|
||||
}
|
||||
|
||||
XmlNode *parent = node->parent;
|
||||
PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode));
|
||||
|
||||
node = node->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG(
|
||||
"Closing xml node name does not match parent name");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (nextToken->type == xmltokentype_name)
|
||||
{
|
||||
node->name = nextToken->string;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unexpected token type after open arrow");
|
||||
#endif
|
||||
}
|
||||
|
||||
token = nextToken;
|
||||
break;
|
||||
}
|
||||
|
||||
case xmltokentype_name:
|
||||
{
|
||||
// TODO(doyle): Store latest attribute pointer so we aren't always
|
||||
// chasing the linked list each iteration. Do the same for children
|
||||
// node
|
||||
|
||||
/* Xml Attributes are a linked list, get first free entry */
|
||||
XmlAttribute *attribute = &node->attribute;
|
||||
if (attribute->init)
|
||||
{
|
||||
while (attribute->next)
|
||||
attribute = attribute->next;
|
||||
|
||||
attribute->next = PLATFORM_MEM_ALLOC(arena, 1, XmlAttribute);
|
||||
attribute = attribute->next;
|
||||
}
|
||||
|
||||
/* Just plain text is a node attribute name */
|
||||
attribute->name = token->string;
|
||||
|
||||
/* Followed by the value */
|
||||
token = &xmlTokens[++i];
|
||||
attribute->value = token->string;
|
||||
attribute->init = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
case xmltokentype_closeArrow:
|
||||
{
|
||||
XmlToken prevToken = xmlTokens[i - 1];
|
||||
prevNode = node;
|
||||
|
||||
/* Closed node means we can return to parent */
|
||||
if (prevToken.type == xmltokentype_backslash)
|
||||
{
|
||||
node->isClosed = TRUE;
|
||||
node = node->parent;
|
||||
}
|
||||
|
||||
if (!node->isClosed)
|
||||
{
|
||||
/* Unclosed node means next fields will be children of node */
|
||||
|
||||
/* If the first child is free allocate, otherwise we have to
|
||||
* iterate through the child's next node(s) */
|
||||
if (!node->child)
|
||||
{
|
||||
node->child = PLATFORM_MEM_ALLOC(arena, 1, XmlNode);
|
||||
node->child->parent = node;
|
||||
node = node->child;
|
||||
}
|
||||
else
|
||||
{
|
||||
XmlNode *nodeToCheck = node->child;
|
||||
while (nodeToCheck->next)
|
||||
nodeToCheck = nodeToCheck->next;
|
||||
|
||||
nodeToCheck->next = PLATFORM_MEM_ALLOC(arena, 1, XmlNode);
|
||||
nodeToCheck->next->parent = node;
|
||||
node = nodeToCheck->next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
|
||||
XmlNode *root)
|
||||
{
|
||||
XmlNode *node = root;
|
||||
while (node)
|
||||
{
|
||||
if (common_strcmp(node->name, "TextureAtlas") == 0)
|
||||
{
|
||||
XmlNode *atlasXmlNode = node;
|
||||
TexAtlas *atlasEntry = NULL;
|
||||
if (common_strcmp(node->attribute.name, "imagePath") == 0)
|
||||
{
|
||||
char *imageName = atlasXmlNode->attribute.value;
|
||||
atlasEntry = asset_makeTexAtlas(assetManager, arena, imageName);
|
||||
|
||||
char *dataDir = "data/textures/WorldTraveller/";
|
||||
char imagePath[512] = {0};
|
||||
common_strncat(imagePath, dataDir, common_strlen(dataDir));
|
||||
common_strncat(imagePath, imageName, common_strlen(imageName));
|
||||
|
||||
asset_loadTextureImage(assetManager, arena, imagePath,
|
||||
imageName);
|
||||
|
||||
atlasEntry->key = PLATFORM_MEM_ALLOC(
|
||||
arena, common_strlen(imageName) + 1, char);
|
||||
common_strncpy(atlasEntry->key, imageName,
|
||||
common_strlen(imageName));
|
||||
|
||||
atlasEntry->tex = asset_getTex(assetManager, imageName);
|
||||
|
||||
XmlNode *atlasChildNode = atlasXmlNode->child;
|
||||
while (atlasChildNode)
|
||||
{
|
||||
if (common_strcmp(atlasChildNode->name, "SubTexture") == 0)
|
||||
{
|
||||
XmlAttribute *subTextureAttrib =
|
||||
&atlasChildNode->attribute;
|
||||
|
||||
AtlasSubTexture newSubTexEntry = {0};
|
||||
while (subTextureAttrib)
|
||||
{
|
||||
|
||||
// TODO(doyle): Work around for now in xml reading,
|
||||
// reading the last node closing node not being
|
||||
// merged to the parent
|
||||
if (!subTextureAttrib->name) continue;
|
||||
|
||||
if (common_strcmp(subTextureAttrib->name, "name") ==
|
||||
0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
newSubTexEntry.key = value;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"x") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
|
||||
newSubTexEntry.rect.pos.x = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"y") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
newSubTexEntry.rect.pos.y = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"width") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
|
||||
newSubTexEntry.rect.size.w = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTextureAttrib->name,
|
||||
"height") == 0)
|
||||
{
|
||||
char *value = subTextureAttrib->value;
|
||||
i32 valueLen = common_strlen(value);
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
|
||||
newSubTexEntry.rect.size.h = CAST(f32) intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG(
|
||||
"Unsupported xml attribute in SubTexture");
|
||||
#endif
|
||||
}
|
||||
subTextureAttrib = subTextureAttrib->next;
|
||||
}
|
||||
|
||||
// TODO(doyle): XML specifies 0,0 top left, we
|
||||
// prefer 0,0 bottom right, so offset by size since 0,0
|
||||
// is top left and size creates a bounding box below it
|
||||
newSubTexEntry.rect.pos.y =
|
||||
1024 - newSubTexEntry.rect.pos.y;
|
||||
newSubTexEntry.rect.pos.y -= newSubTexEntry.rect.size.h;
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(newSubTexEntry.key)
|
||||
#endif
|
||||
|
||||
u32 subTexHashIndex = common_murmurHash2(
|
||||
newSubTexEntry.key,
|
||||
common_strlen(newSubTexEntry.key), 0xDEADBEEF);
|
||||
subTexHashIndex =
|
||||
subTexHashIndex % ARRAY_COUNT(atlasEntry->subTex);
|
||||
|
||||
// NOTE(doyle): Hash collision
|
||||
AtlasSubTexture *subTexEntry =
|
||||
&atlasEntry->subTex[subTexHashIndex];
|
||||
if (subTexEntry->key)
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
|
||||
// NOTE(doyle): Two textures have the same access
|
||||
// name
|
||||
ASSERT(common_strcmp(subTexEntry->key,
|
||||
newSubTexEntry.key) != 0);
|
||||
#endif
|
||||
while (subTexEntry->next)
|
||||
subTexEntry = subTexEntry->next;
|
||||
|
||||
subTexEntry->next =
|
||||
PLATFORM_MEM_ALLOC(arena, 1, AtlasSubTexture);
|
||||
subTexEntry = subTexEntry->next;
|
||||
}
|
||||
|
||||
*subTexEntry = newSubTexEntry;
|
||||
i32 keyLen = common_strlen(newSubTexEntry.key);
|
||||
|
||||
subTexEntry->key =
|
||||
PLATFORM_MEM_ALLOC(arena, keyLen + 1, char);
|
||||
common_strncpy(subTexEntry->key, newSubTexEntry.key,
|
||||
keyLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unsupported xml node name not parsed");
|
||||
#endif
|
||||
}
|
||||
|
||||
atlasChildNode = atlasChildNode->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unsupported xml node");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Unsupported xml node name not parsed");
|
||||
#endif
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL void recursiveFreeXmlTree(MemoryArena *arena, XmlNode *node)
|
||||
{
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
} else
|
||||
{
|
||||
// NOTE(doyle): First attribute is statically allocated, only if there's
|
||||
// more attributes do we dynamically allocate
|
||||
XmlAttribute *attrib = node->attribute.next;
|
||||
|
||||
while (attrib)
|
||||
{
|
||||
XmlAttribute *next = attrib->next;
|
||||
|
||||
attrib->name = NULL;
|
||||
attrib->value = NULL;
|
||||
PLATFORM_MEM_FREE(arena, attrib, sizeof(XmlAttribute));
|
||||
attrib = next;
|
||||
}
|
||||
|
||||
recursiveFreeXmlTree(arena, node->child);
|
||||
recursiveFreeXmlTree(arena, node->next);
|
||||
|
||||
node->name = NULL;
|
||||
node->isClosed = FALSE;
|
||||
PLATFORM_MEM_FREE(arena, node, sizeof(XmlNode));
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL void freeXmlData(MemoryArena *arena, XmlToken *tokens, i32 numTokens,
|
||||
XmlNode *tree)
|
||||
{
|
||||
if (tree) recursiveFreeXmlTree(arena, tree);
|
||||
if (tokens) PLATFORM_MEM_FREE(arena, tokens, numTokens * sizeof(XmlToken));
|
||||
}
|
||||
|
||||
INTERNAL void assetInit(GameState *state)
|
||||
{
|
||||
AssetManager *assetManager = &state->assetManager;
|
||||
@ -656,40 +121,51 @@ INTERNAL void assetInit(GameState *state)
|
||||
Texture *tex = asset_getAndAllocFreeTexSlot(assetManager, arena, "nullTex");
|
||||
*tex = texture_gen(1, 1, 4, CAST(u8 *)(&bitmap));
|
||||
|
||||
//assetManager->texAtlas.type = hashtabletype_textureAtlas;
|
||||
|
||||
/*
|
||||
*********************************
|
||||
* Load terrain texture atlas data
|
||||
*********************************
|
||||
*/
|
||||
PlatformFileRead terrainXml = {0};
|
||||
i32 result = platform_readFileToBuffer(
|
||||
arena, "data/textures/WorldTraveller/terrain.xml", &terrainXml);
|
||||
|
||||
if (result)
|
||||
{
|
||||
DEBUG_LOG("Failed to read sprite sheet xml");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Tokenise buffer */
|
||||
i32 numTokens = 0;
|
||||
XmlToken *xmlTokens = tokeniseXmlBuffer(arena, terrainXml.buffer,
|
||||
terrainXml.size, &numTokens);
|
||||
result = asset_loadXmlFile(assetManager, arena, &terrainXml);
|
||||
|
||||
/* Build XML tree from tokens */
|
||||
XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens);
|
||||
|
||||
/* Parse XML tree to game structures */
|
||||
parseXmlTreeToGame(assetManager, arena, xmlTree);
|
||||
|
||||
/* Free data */
|
||||
freeXmlData(arena, xmlTokens, numTokens, xmlTree);
|
||||
platform_closeFileRead(arena, &terrainXml);
|
||||
|
||||
TexAtlas *terrainAtlas = asset_getTexAtlas(assetManager, "terrain.png");
|
||||
if (!result)
|
||||
{
|
||||
TexAtlas *terrainAtlas =
|
||||
asset_getTexAtlas(assetManager, "terrain.png");
|
||||
|
||||
i32 numSubTextures = 1;
|
||||
f32 duration = 1.0f;
|
||||
char *grassTerrain[1] = {"grass.png"};
|
||||
asset_addAnimation(assetManager, arena, "terrainGrass", terrainAtlas,
|
||||
grassTerrain, numSubTextures, duration);
|
||||
asset_addAnimation(assetManager, arena, "terrainGrass",
|
||||
terrainAtlas, grassTerrain, numSubTextures,
|
||||
duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Failed to load terrain sprite xml data");
|
||||
#endif
|
||||
}
|
||||
|
||||
platform_closeFileRead(arena, &terrainXml);
|
||||
}
|
||||
|
||||
/*
|
||||
*********************************
|
||||
* Load Claude texture atlas data
|
||||
*********************************
|
||||
*/
|
||||
PlatformFileRead claudeXml = {0};
|
||||
result = platform_readFileToBuffer(
|
||||
arena, "data/textures/WorldTraveller/ClaudeSprite.xml", &claudeXml);
|
||||
@ -700,21 +176,10 @@ INTERNAL void assetInit(GameState *state)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Tokenise buffer */
|
||||
i32 numTokens = 0;
|
||||
XmlToken *xmlTokens = tokeniseXmlBuffer(arena, claudeXml.buffer,
|
||||
claudeXml.size, &numTokens);
|
||||
|
||||
/* Build XML tree from tokens */
|
||||
XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens);
|
||||
|
||||
/* Parse XML tree to game structures */
|
||||
parseXmlTreeToGame(assetManager, arena, xmlTree);
|
||||
|
||||
/* Free data */
|
||||
freeXmlData(arena, xmlTokens, numTokens, xmlTree);
|
||||
platform_closeFileRead(arena, &claudeXml);
|
||||
result = asset_loadXmlFile(assetManager, arena, &claudeXml);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
TexAtlas *claudeAtlas =
|
||||
asset_getTexAtlas(assetManager, "ClaudeSprite.png");
|
||||
|
||||
@ -740,8 +205,9 @@ INTERNAL void assetInit(GameState *state)
|
||||
"ClaudeSprite_BattleIdle_Left_03"};
|
||||
numRects = ARRAY_COUNT(claudeBattleIdle);
|
||||
duration = 0.2f;
|
||||
asset_addAnimation(assetManager, arena, "claudeBattleIdle", claudeAtlas,
|
||||
claudeBattleIdle, numRects, duration);
|
||||
asset_addAnimation(assetManager, arena, "claudeBattleIdle",
|
||||
claudeAtlas, claudeBattleIdle, numRects,
|
||||
duration);
|
||||
|
||||
// Attack Left animation
|
||||
char *claudeAttack[6] = {
|
||||
@ -754,20 +220,34 @@ INTERNAL void assetInit(GameState *state)
|
||||
claudeAttack, numRects, duration);
|
||||
|
||||
// Victory animation
|
||||
char *claudeVictory[8] = {
|
||||
"ClaudeSprite_Battle_Victory_01", "ClaudeSprite_Battle_Victory_02",
|
||||
"ClaudeSprite_Battle_Victory_03", "ClaudeSprite_Battle_Victory_04",
|
||||
"ClaudeSprite_Battle_Victory_05", "ClaudeSprite_Battle_Victory_06",
|
||||
"ClaudeSprite_Battle_Victory_07", "ClaudeSprite_Battle_Victory_08"};
|
||||
char *claudeVictory[8] = {"ClaudeSprite_Battle_Victory_01",
|
||||
"ClaudeSprite_Battle_Victory_02",
|
||||
"ClaudeSprite_Battle_Victory_03",
|
||||
"ClaudeSprite_Battle_Victory_04",
|
||||
"ClaudeSprite_Battle_Victory_05",
|
||||
"ClaudeSprite_Battle_Victory_06",
|
||||
"ClaudeSprite_Battle_Victory_07",
|
||||
"ClaudeSprite_Battle_Victory_08"};
|
||||
numRects = ARRAY_COUNT(claudeVictory);
|
||||
duration = 0.1f;
|
||||
asset_addAnimation(assetManager, arena, "claudeVictory", claudeAtlas,
|
||||
claudeVictory, numRects, duration);
|
||||
asset_addAnimation(assetManager, arena, "claudeVictory",
|
||||
claudeAtlas, claudeVictory, numRects, duration);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Failed to load claude sprite xml data");
|
||||
#endif
|
||||
}
|
||||
|
||||
platform_closeFileRead(arena, &claudeXml);
|
||||
}
|
||||
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Animations created");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Load shaders */
|
||||
asset_loadShaderFiles(assetManager, arena, "data/shaders/sprite.vert.glsl",
|
||||
@ -981,30 +461,7 @@ INTERNAL void unitTest(MemoryArena *arena)
|
||||
ASSERT(common_atoi(" 32", common_strlen(" 32")) == -1);
|
||||
ASSERT(common_atoi("+32", common_strlen("+32")) == 32);
|
||||
ASSERT(common_atoi("+ 32", common_strlen("+ 32")) == 0);
|
||||
|
||||
|
||||
PlatformFileRead xmlFileRead = {0};
|
||||
i32 result = platform_readFileToBuffer(
|
||||
arena, "data/textures/WorldTraveller/ClaudeSprite.xml", &xmlFileRead);
|
||||
if (result)
|
||||
{
|
||||
DEBUG_LOG(
|
||||
"unitTest() error: Could not load XML file for memory free test");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Tokenise buffer */
|
||||
i32 memBefore = arena->bytesAllocated;
|
||||
i32 numTokens = 0;
|
||||
XmlToken *xmlTokens = tokeniseXmlBuffer(arena, xmlFileRead.buffer,
|
||||
xmlFileRead.size, &numTokens);
|
||||
/* Build XML tree from tokens */
|
||||
XmlNode *xmlTree = buildXmlTree(arena, xmlTokens, numTokens);
|
||||
freeXmlData(arena, xmlTokens, numTokens, xmlTree);
|
||||
i32 memAfter = arena->bytesAllocated;
|
||||
|
||||
ASSERT(memBefore == memAfter);
|
||||
}
|
||||
asset_unitTest(arena);
|
||||
}
|
||||
|
||||
// TODO(doyle): Remove and implement own random generator!
|
||||
@ -2172,7 +1629,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
TexAtlas *heroAtlas =
|
||||
asset_getTexAtlas(assetManager, "ClaudeSprite.png");
|
||||
Rect heroAvatarRect =
|
||||
asset_getAtlasSubTexRect(heroAtlas, "ClaudeSprite_Avatar_01");
|
||||
asset_getSubTexRect(heroAtlas, "ClaudeSprite_Avatar_01");
|
||||
v2 heroAvatarP =
|
||||
V2(10.0f, (renderer->size.h * 0.5f) - (0.5f * heroAvatarRect.size.h));
|
||||
|
||||
|
@ -7,59 +7,94 @@
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct MemoryArena MemoryArena;
|
||||
typedef struct PlatformFileRead PlatformFileRead;
|
||||
|
||||
enum HashTableType
|
||||
{
|
||||
hashtabletype_unknown,
|
||||
hashtabletype_texture,
|
||||
hashtabletype_textureAtlas,
|
||||
hashtabletype_animation,
|
||||
hashtabletype_count,
|
||||
};
|
||||
|
||||
typedef struct HashTableEntry
|
||||
{
|
||||
void *data;
|
||||
char *key;
|
||||
|
||||
void *next;
|
||||
} HashTableEntry;
|
||||
|
||||
typedef struct HashTable
|
||||
{
|
||||
HashTableEntry *entries;
|
||||
i32 size;
|
||||
enum HashTableType type;
|
||||
} HashTable;
|
||||
|
||||
typedef struct AssetManager
|
||||
{
|
||||
/* Hash Tables */
|
||||
TexAtlas texAtlas[8];
|
||||
Texture textures[32];
|
||||
Shader shaders[32];
|
||||
Animation anims[1024];
|
||||
|
||||
/* Primitive Array */
|
||||
Shader shaders[32];
|
||||
AudioVorbis audio[32];
|
||||
Font font;
|
||||
|
||||
TexAtlas texAtlas[8];
|
||||
} AssetManager;
|
||||
|
||||
#define MAX_TEXTURE_SIZE 1024
|
||||
|
||||
Rect asset_getAtlasSubTexRect(TexAtlas *atlas, char *key);
|
||||
|
||||
AudioVorbis *asset_getVorbis(AssetManager *assetManager,
|
||||
const enum AudioList type);
|
||||
|
||||
/*
|
||||
*********************************
|
||||
* Texture Asset Managing
|
||||
*********************************
|
||||
*/
|
||||
Rect asset_getSubTexRect(TexAtlas *atlas, char *key);
|
||||
Texture *asset_getTex(AssetManager *const assetManager, const char *const key);
|
||||
|
||||
TexAtlas *asset_makeTexAtlas(AssetManager *const assetManager,
|
||||
MemoryArena *arena, const char *const key);
|
||||
|
||||
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type);
|
||||
|
||||
TexAtlas *asset_getTexAtlas(AssetManager *const assetManager,
|
||||
const char *const key);
|
||||
|
||||
Texture *asset_getAndAllocFreeTexSlot(AssetManager *assetManager,
|
||||
MemoryArena *arena,
|
||||
const char *const key);
|
||||
|
||||
Animation *asset_getAnim(AssetManager *assetManager, char *key);
|
||||
|
||||
const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena,
|
||||
const char *const path, const enum AudioList type);
|
||||
const i32 asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena,
|
||||
const char *const path,
|
||||
const char *const key);
|
||||
|
||||
/*
|
||||
*********************************
|
||||
* Animation Asset Managing
|
||||
*********************************
|
||||
*/
|
||||
void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
|
||||
char *animName, TexAtlas *atlas, char **subTextureNames,
|
||||
i32 numSubTextures, f32 frameDuration);
|
||||
Animation *asset_getAnim(AssetManager *assetManager, char *key);
|
||||
|
||||
/*
|
||||
*********************************
|
||||
* Everything else
|
||||
*********************************
|
||||
*/
|
||||
i32 asset_loadXmlFile(AssetManager *assetManager, MemoryArena *arena,
|
||||
PlatformFileRead *fileRead);
|
||||
AudioVorbis *asset_getVorbis(AssetManager *assetManager,
|
||||
const enum AudioList type);
|
||||
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type);
|
||||
const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena,
|
||||
const char *const path, const enum AudioList type);
|
||||
const i32 asset_loadShaderFiles(AssetManager *assetManager, MemoryArena *arena,
|
||||
const char *const vertexPath,
|
||||
const char *const fragmentPath,
|
||||
const enum ShaderList type);
|
||||
|
||||
const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
|
||||
const char *filePath);
|
||||
|
||||
void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
|
||||
char *animName, TexAtlas *atlas, char **subTextureNames,
|
||||
i32 numSubTextures, f32 frameDuration);
|
||||
|
||||
v2 asset_stringDimInPixels(const Font *const font, const char *const string);
|
||||
void asset_unitTest(MemoryArena *arena);
|
||||
|
||||
#endif
|
||||
|
@ -10,6 +10,32 @@
|
||||
/* Forward Declaration */
|
||||
typedef struct Texture Texture;
|
||||
|
||||
typedef struct XmlAttribute
|
||||
{
|
||||
b32 init;
|
||||
char *name;
|
||||
char *value;
|
||||
|
||||
struct XmlAttribute *next;
|
||||
} XmlAttribute;
|
||||
|
||||
typedef struct XmlNode
|
||||
{
|
||||
char *name;
|
||||
XmlAttribute attribute;
|
||||
|
||||
// NOTE(doyle): Track if node has more children
|
||||
b32 isClosed;
|
||||
|
||||
// NOTE(doyle): Direct child/parent
|
||||
struct XmlNode *parent;
|
||||
struct XmlNode *child;
|
||||
|
||||
// NOTE(doyle): Else all other nodes
|
||||
struct XmlNode *next;
|
||||
} XmlNode;
|
||||
|
||||
|
||||
enum TexList
|
||||
{
|
||||
texlist_empty,
|
||||
|
@ -18,6 +18,9 @@ enum DebugCallCount
|
||||
|
||||
void debug_init(MemoryArena *arena, v2 windowSize, Font font);
|
||||
|
||||
#define DEBUG_RECURSIVE_PRINT_XML_TREE(sig) debug_recursivePrintXmlTree(sig, 1)
|
||||
void debug_recursivePrintXmlTree(XmlNode *root, i32 levelsDeep);
|
||||
|
||||
void debug_callCountIncrement(enum DebugCallCount id);
|
||||
void debug_clearCallCounter();
|
||||
#define DEBUG_LOG(string) debug_consoleLog(string, __FILE__, __LINE__);
|
||||
|
Loading…
Reference in New Issue
Block a user