Convert subtextures to use hash tables

Also clean up and add some block comments to aid code separation mainly in
AssetManager.
This commit is contained in:
2016-08-30 18:42:37 +10:00
parent 8b0f2f93e3
commit e9db7b0570
6 changed files with 396 additions and 364 deletions
+327 -299
View File
@@ -51,7 +51,7 @@ INTERNAL HashTableEntry *getFreeHashSlot(HashTable *const table,
}
/* Check if platform_mem_alloc failed(), otherwise always true if hash table
* entries initialised*/
* entries initialised, assign key to hash entry */
if (result)
{
// +1 Null terminator
@@ -77,6 +77,205 @@ INTERNAL HashTableEntry *getEntryFromHash(HashTable *const table,
return result;
}
/*
*********************************
* Texture Operations
*********************************
*/
INTERNAL Rect *getFreeAtlasSubTexSlot(TexAtlas *const atlas,
MemoryArena *const arena,
const char *const key)
{
HashTableEntry *entry = getFreeHashSlot(&atlas->subTex, arena, key);
if (entry)
{
entry->data = PLATFORM_MEM_ALLOC(arena, 1, Rect);
Rect *result = CAST(Rect *) entry->data;
return result;
}
else
{
return NULL;
}
}
Rect *asset_getAtlasSubTex(TexAtlas *const atlas, const char *const key)
{
HashTableEntry *entry = getEntryFromHash(&atlas->subTex, key);
Rect *result = NULL;
if (entry) result = CAST(Rect *) entry->data;
return result;
}
Texture *asset_getTex(AssetManager *const assetManager, const char *const key)
{
HashTableEntry *entry = getEntryFromHash(&assetManager->textures, key);
Texture *result = NULL;
if (entry) result = CAST(Texture *)entry->data;
return result;
}
TexAtlas *asset_getFreeTexAtlasSlot(AssetManager *const assetManager,
MemoryArena *arena, const char *const key,
i32 numSubTex)
{
HashTableEntry *const entry =
getFreeHashSlot(&assetManager->texAtlas, arena, key);
if (entry)
{
entry->data = PLATFORM_MEM_ALLOC(arena, 1, TexAtlas);
TexAtlas *result = CAST(TexAtlas *) entry->data;
if (result)
{
result->subTex.size = numSubTex;
result->subTex.entries =
PLATFORM_MEM_ALLOC(arena, numSubTex, HashTableEntry);
if (!result->subTex.entries)
{
PLATFORM_MEM_FREE(arena, result, sizeof(TexAtlas));
result = NULL;
}
}
return result;
}
else
{
return NULL;
}
}
TexAtlas *asset_getTexAtlas(AssetManager *const assetManager,
const char *const key)
{
HashTableEntry *entry = getEntryFromHash(&assetManager->texAtlas, key);
TexAtlas *result = NULL;
if (entry) result = CAST(TexAtlas *)entry->data;
return result;
}
Texture *asset_getFreeTexSlot(AssetManager *const assetManager,
MemoryArena *const arena, const char *const key)
{
HashTableEntry *const entry =
getFreeHashSlot(&assetManager->textures, arena, key);
if (entry)
{
entry->data = PLATFORM_MEM_ALLOC(arena, 1, Texture);
Texture *result = CAST(Texture *) entry->data;
return result;
}
else
{
return NULL;
}
}
Texture *asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena,
const char *const path, const char *const key)
{
/* Open the texture image */
i32 imgWidth, imgHeight, bytesPerPixel;
stbi_set_flip_vertically_on_load(TRUE);
u8 *image =
stbi_load(path, &imgWidth, &imgHeight, &bytesPerPixel, 0);
#ifdef DENGINE_DEBUG
if (imgWidth != imgHeight)
{
printf(
"asset_loadTextureImage() warning: Sprite sheet is not square: "
"%dx%dpx\n", imgWidth, imgHeight);
}
#endif
if (!image)
{
printf("stdbi_load() failed: %s\n", stbi_failure_reason());
return NULL;
}
Texture *result = asset_getFreeTexSlot(assetManager, arena, key);
*result = texture_gen(CAST(GLuint)(imgWidth), CAST(GLuint)(imgHeight),
CAST(GLint)(bytesPerPixel), image);
GL_CHECK_ERROR();
stbi_image_free(image);
return result;
}
/*
*********************************
* Animation Asset Managing
*********************************
*/
INTERNAL Animation *getFreeAnimationSlot(AssetManager *assetManager,
MemoryArena *arena, char *key)
{
HashTableEntry *entry = getFreeHashSlot(&assetManager->anims, arena, key);
if (entry)
{
entry->data = PLATFORM_MEM_ALLOC(arena, 1, Animation);
Animation *result = CAST(Animation *) entry->data;
return result;
}
else
{
return NULL;
}
}
void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
char *animName, TexAtlas *atlas, char **subTextureNames,
i32 numSubTextures, f32 frameDuration)
{
Animation *anim = getFreeAnimationSlot(assetManager, arena, animName);
/* Use same animation ptr for name from entry key */
HashTableEntry *entry = getEntryFromHash(&assetManager->anims, animName);
anim->name = entry->key;
anim->atlas = atlas;
anim->frameDuration = frameDuration;
anim->numFrames = numSubTextures;
anim->frameList = PLATFORM_MEM_ALLOC(arena, numSubTextures, char*);
for (i32 i = 0; i < numSubTextures; i++)
{
anim->frameList[i] = subTextureNames[i];
}
}
Animation *asset_getAnim(AssetManager *const assetManager,
const char *const key)
{
HashTableEntry *entry = getEntryFromHash(&assetManager->anims, key);
Animation *result = NULL;
if (entry) result = CAST(Animation *)entry->data;
return result;
}
/*
*********************************
* XML Operations
@@ -356,89 +555,127 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
XmlNode *node = root;
while (node)
{
/*
*********************************
* Branch on node names
*********************************
*/
if (common_strcmp(node->name, "TextureAtlas") == 0)
{
XmlNode *atlasXmlNode = node;
TexAtlas *atlasEntry = NULL;
TexAtlas *atlas = NULL;
if (common_strcmp(node->attribute.name, "imagePath") == 0)
{
/*
**********************************************
* Create a texture atlas with imageName as key
**********************************************
*/
char *imageName = atlasXmlNode->attribute.value;
atlasEntry =
asset_getFreeTexAtlasSlot(assetManager, arena, imageName);
i32 numSubTex = 1024;
atlas = asset_getFreeTexAtlasSlot(assetManager, arena,
imageName, numSubTex);
char *dataDir = "data/textures/WorldTraveller/";
char imagePath[512] = {0};
common_strncat(imagePath, dataDir, common_strlen(dataDir));
common_strncat(imagePath, imageName, common_strlen(imageName));
if (!atlas)
{
DEBUG_LOG(
"parseXmlTreeToGame() failed: Could not get free atlas "
"entry");
return;
}
asset_loadTextureImage(assetManager, arena, imagePath,
imageName);
/*
*************************************************
* Load a texture to hash, with imageName as key
*************************************************
*/
char *dataDir = "data/textures/WorldTraveller/";
i32 dataDirLen = common_strlen(dataDir);
i32 imageNameLen = common_strlen(imageName);
i32 totalPathLen = (dataDirLen + imageNameLen) + 1;
atlasEntry->key = PLATFORM_MEM_ALLOC(
arena, common_strlen(imageName) + 1, char);
common_strncpy(atlasEntry->key, imageName,
common_strlen(imageName));
char *imagePath = PLATFORM_MEM_ALLOC(arena, totalPathLen, char);
common_strncat(imagePath, dataDir, dataDirLen);
common_strncat(imagePath, imageName, imageNameLen);
atlasEntry->tex = asset_getTex(assetManager, imageName);
Texture *tex = asset_loadTextureImage(assetManager, arena,
imagePath, imageName);
if (!tex)
{
DEBUG_LOG("parseXmlTreeToGame() failed: Could not load image");
PLATFORM_MEM_FREE(arena, imagePath,
totalPathLen * sizeof(char));
return;
}
PLATFORM_MEM_FREE(arena, imagePath,
totalPathLen * sizeof(char));
atlas->tex = tex;
/*
*************************************************
* Iterate over XML attributes
*************************************************
*/
XmlNode *atlasChildNode = atlasXmlNode->child;
while (atlasChildNode)
{
if (common_strcmp(atlasChildNode->name, "SubTexture") == 0)
{
XmlAttribute *subTextureAttrib =
&atlasChildNode->attribute;
XmlAttribute *subTexAttrib = &atlasChildNode->attribute;
AtlasSubTexture newSubTexEntry = {0};
while (subTextureAttrib)
char *key = NULL;
Rect subTex = {0};
while (subTexAttrib)
{
// 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 (!subTexAttrib->name) continue;
if (common_strcmp(subTextureAttrib->name, "name") ==
0)
if (common_strcmp(subTexAttrib->name, "name") == 0)
{
char *value = subTextureAttrib->value;
newSubTexEntry.key = value;
char *value = subTexAttrib->value;
key = value;
}
else if (common_strcmp(subTextureAttrib->name,
"x") == 0)
else if (common_strcmp(subTexAttrib->name, "x") ==
0)
{
char *value = subTextureAttrib->value;
char *value = subTexAttrib->value;
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
newSubTexEntry.rect.pos.x = CAST(f32) intValue;
subTex.pos.x = CAST(f32) intValue;
}
else if (common_strcmp(subTextureAttrib->name,
"y") == 0)
else if (common_strcmp(subTexAttrib->name, "y") ==
0)
{
char *value = subTextureAttrib->value;
char *value = subTexAttrib->value;
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
newSubTexEntry.rect.pos.y = CAST(f32) intValue;
subTex.pos.y = CAST(f32) intValue;
}
else if (common_strcmp(subTextureAttrib->name,
else if (common_strcmp(subTexAttrib->name,
"width") == 0)
{
char *value = subTextureAttrib->value;
char *value = subTexAttrib->value;
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
newSubTexEntry.rect.size.w = CAST(f32) intValue;
subTex.size.w = CAST(f32) intValue;
}
else if (common_strcmp(subTextureAttrib->name,
else if (common_strcmp(subTexAttrib->name,
"height") == 0)
{
char *value = subTextureAttrib->value;
char *value = subTexAttrib->value;
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
newSubTexEntry.rect.size.h = CAST(f32) intValue;
subTex.size.h = CAST(f32) intValue;
}
else
{
@@ -447,53 +684,21 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
"Unsupported xml attribute in SubTexture");
#endif
}
subTextureAttrib = subTextureAttrib->next;
subTexAttrib = subTexAttrib->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;
subTex.pos.y = 1024 - subTex.pos.y;
subTex.pos.y -= subTex.size.h;
#ifdef DENGINE_DEBUG
ASSERT(newSubTexEntry.key)
ASSERT(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);
Rect *subTexInHash =
getFreeAtlasSubTexSlot(atlas, arena, key);
*subTexInHash = subTex;
}
else
{
@@ -559,6 +764,11 @@ INTERNAL void freeXmlData(MemoryArena *arena, XmlToken *tokens, i32 numTokens,
if (tokens) PLATFORM_MEM_FREE(arena, tokens, numTokens * sizeof(XmlToken));
}
/*
*********************************
* Everything else
*********************************
*/
i32 asset_loadXmlFile(AssetManager *assetManager, MemoryArena *arena,
PlatformFileRead *fileRead)
{
@@ -587,73 +797,6 @@ i32 asset_loadXmlFile(AssetManager *assetManager, MemoryArena *arena,
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];
if (result->key)
{
while (result && common_strcmp(result->key, key) != 0)
result = result->next;
}
return result;
}
INTERNAL AtlasSubTexture *getFreeAtlasSubTexSlot(TexAtlas *atlas,
MemoryArena *arena, char *key)
{
u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(atlas->subTex));
AtlasSubTexture *result = &atlas->subTex[hashIndex];
if (result->key)
{
while (result->next)
{
if (common_strcmp(result->key, key) == 0)
{
// TODO(doyle): Error correction whereby if a tex atlas already
// exists
ASSERT(INVALID_CODE_PATH);
}
result = result->next;
}
result->next = PLATFORM_MEM_ALLOC(arena, 1, AtlasSubTexture);
result = result->next;
}
return result;
}
INTERNAL Animation *getFreeAnimationSlot(AssetManager *assetManager,
MemoryArena *arena, char *key)
{
HashTableEntry *entry = getFreeHashSlot(&assetManager->anims, arena, key);
if (entry)
{
entry->data = PLATFORM_MEM_ALLOC(arena, 1, Animation);
Animation *result = CAST(Animation *) entry->data;
return result;
}
else
{
return NULL;
}
}
Rect asset_getSubTexRect(TexAtlas *atlas, char *key)
{
AtlasSubTexture *subTex = getAtlasSubTex(atlas, key);
Rect result = subTex->rect;
return result;
}
// TODO(doyle): Switch to hash based lookup
// TODO(doyle): Use pointers, so we can forward declare all assets?
AudioVorbis *asset_getVorbis(AssetManager *assetManager,
@@ -669,77 +812,6 @@ AudioVorbis *asset_getVorbis(AssetManager *assetManager,
return NULL;
}
Texture *asset_getTex(AssetManager *const assetManager, const char *const key)
{
HashTableEntry *entry = getEntryFromHash(&assetManager->textures, key);
Texture *result = NULL;
if (entry) result = CAST(Texture *)entry->data;
return result;
}
Texture *asset_getFreeTexSlot(AssetManager *const assetManager,
MemoryArena *const arena, const char *const key)
{
HashTableEntry *const entry =
getFreeHashSlot(&assetManager->textures, arena, key);
if (entry)
{
entry->data = PLATFORM_MEM_ALLOC(arena, 1, Texture);
Texture *result = CAST(Texture *) entry->data;
return result;
}
else
{
return NULL;
}
}
TexAtlas *asset_getFreeTexAtlasSlot(AssetManager *const assetManager,
MemoryArena *arena, const char *const key)
{
HashTableEntry *const entry =
getFreeHashSlot(&assetManager->texAtlas, arena, key);
if (entry)
{
entry->data = PLATFORM_MEM_ALLOC(arena, 1, TexAtlas);
TexAtlas *result = CAST(TexAtlas *) entry->data;
return result;
}
else
{
return NULL;
}
}
TexAtlas *asset_getTexAtlas(AssetManager *const assetManager,
const char *const key)
{
HashTableEntry *entry = getEntryFromHash(&assetManager->texAtlas, key);
TexAtlas *result = NULL;
if (entry) result = CAST(TexAtlas *)entry->data;
return result;
}
Animation *asset_getAnim(AssetManager *const assetManager,
const char *const key)
{
HashTableEntry *entry = getEntryFromHash(&assetManager->anims, key);
Animation *result = NULL;
if (entry) result = CAST(Animation *)entry->data;
return result;
}
const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena,
const char *const path, const enum AudioList type)
{
@@ -772,52 +844,8 @@ const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena,
return 0;
}
const i32 asset_loadTextureImage(AssetManager *assetManager, MemoryArena *arena,
const char *const path, const char *const key)
{
/* Open the texture image */
i32 imgWidth, imgHeight, bytesPerPixel;
stbi_set_flip_vertically_on_load(TRUE);
u8 *image =
stbi_load(path, &imgWidth, &imgHeight, &bytesPerPixel, 0);
#ifdef DENGINE_DEBUG
if (imgWidth != imgHeight)
{
printf(
"asset_loadTextureImage() warning: Sprite sheet is not square: "
"%dx%dpx\n", imgWidth, imgHeight);
}
#endif
if (!image)
{
printf("stdbi_load() failed: %s\n", stbi_failure_reason());
return -1;
}
Texture *tex = asset_getFreeTexSlot(assetManager, arena, key);
*tex = texture_gen(CAST(GLuint)(imgWidth), CAST(GLuint)(imgHeight),
CAST(GLint)(bytesPerPixel), image);
GL_CHECK_ERROR();
stbi_image_free(image);
return 0;
}
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type)
{
if (type < shaderlist_count)
return &assetManager->shaders[type];
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
return NULL;
}
INTERNAL GLuint createShaderFromPath(MemoryArena *arena, const char *const path, GLuint shadertype)
INTERNAL GLuint createShaderFromPath(MemoryArena *arena, const char *const path,
GLuint shadertype)
{
PlatformFileRead file = {0};
@@ -869,6 +897,17 @@ INTERNAL i32 shaderLoadProgram(Shader *const shader, const GLuint vertexShader,
return 0;
}
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type)
{
if (type < shaderlist_count)
return &assetManager->shaders[type];
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
return NULL;
}
const i32 asset_loadShaderFiles(AssetManager *assetManager, MemoryArena *arena,
const char *const vertexPath,
const char *const fragmentPath,
@@ -906,7 +945,11 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
stbtt_InitFont(&fontInfo, fontFileRead.buffer,
stbtt_GetFontOffsetForIndex(fontFileRead.buffer, 0));
/* Initialise Assetmanager Font */
/*
****************************************
* Initialise assetmanager font reference
****************************************
*/
Font *font = &assetManager->font;
font->codepointRange = V2i(32, 127);
v2 codepointRange = font->codepointRange;
@@ -930,7 +973,11 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
font->charMetrics = PLATFORM_MEM_ALLOC(arena, numGlyphs, CharMetrics);
/* Use STB_TrueType to generate a series of bitmap characters */
/*
************************************************************
* Use STB_TrueType to generate a series of bitmap characters
************************************************************
*/
i32 glyphIndex = 0;
for (i32 codepoint = CAST(i32) codepointRange.x;
codepoint < CAST(i32) codepointRange.y; codepoint++)
@@ -1034,8 +1081,16 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
// align the baselines up so we don't need to do baseline adjusting at
// render?
char charToEncode = CAST(char)codepointRange.x;
i32 numSubTex = numGlyphs;
TexAtlas *fontAtlas =
asset_getFreeTexAtlasSlot(assetManager, arena, "font");
asset_getFreeTexAtlasSlot(assetManager, arena, "font", numSubTex);
/*
*********************************************************
* Load individual glyph bitmap data into one font bitmap
*********************************************************
*/
for (i32 row = 0; row < MAX_TEXTURE_SIZE; row++)
{
u32 *destRow = fontBitmap + (row * MAX_TEXTURE_SIZE);
@@ -1049,25 +1104,15 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
/* Store the location of glyph into atlas */
if (verticalPixelsBlitted == 0)
{
#ifdef DENGINE_DEBUG
ASSERT(activeGlyph.codepoint < ARRAY_COUNT(fontAtlas->subTex));
#endif
v2 origin =
V2(CAST(f32)(glyphIndex * font->maxSize.w), CAST(f32) row);
// NOTE(doyle): Since charToEncode starts from 0 and we record
// all ascii characters, charToEncode represents the character
// 1:1
char charTmp[2] = {0};
charTmp[0] = charToEncode;
AtlasSubTexture *subTex =
getFreeAtlasSubTexSlot(fontAtlas, arena, charTmp);
subTex->key = PLATFORM_MEM_ALLOC(arena, 1, char);
subTex->key[0] = charToEncode;
subTex->rect = CAST(Rect){origin, font->maxSize};
const char key[2] = {charToEncode, 0};
Rect *subTex = getFreeAtlasSubTexSlot(fontAtlas, arena, key);
*subTex = CAST(Rect){origin, font->maxSize};
charToEncode++;
}
@@ -1115,6 +1160,11 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
}
}
/*
*******************************************
* Generate and store font bitmap to assets
*******************************************
*/
Texture *tex = asset_getFreeTexSlot(assetManager, arena, "font");
*tex = texture_gen(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4,
CAST(u8 *) fontBitmap);
@@ -1146,28 +1196,6 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
return 0;
}
void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
char *animName, TexAtlas *atlas, char **subTextureNames,
i32 numSubTextures, f32 frameDuration)
{
Animation *anim = getFreeAnimationSlot(assetManager, arena, animName);
/* Use same animation ptr for name from entry key */
HashTableEntry *entry = getEntryFromHash(&assetManager->anims, animName);
anim->name = entry->key;
anim->atlas = atlas;
anim->frameDuration = frameDuration;
anim->numFrames = numSubTextures;
anim->frameList = PLATFORM_MEM_ALLOC(arena, numSubTextures, char*);
for (i32 i = 0; i < numSubTextures; i++)
{
AtlasSubTexture *subTex = getAtlasSubTex(atlas, subTextureNames[i]);
anim->frameList[i] = subTex->key;
}
}
v2 asset_stringDimInPixels(const Font *const font, const char *const string)
{