From 7ca42f781a6479695f2bc3894e2f10910e9de14c Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Thu, 25 Aug 2016 02:06:32 +1000 Subject: [PATCH] Start merging hash table access into assets Basic implementation with many pre-existing features disabled untill all parts of the system that interact with the assets transitions over. --- src/AssetManager.c | 199 ++++++++++++++++++++++------- src/Entity.c | 79 +++++++----- src/Renderer.c | 47 ++++--- src/WorldTraveller.c | 160 +++++++++++------------ src/include/Dengine/AssetManager.h | 24 ++-- src/include/Dengine/Assets.h | 31 +++-- src/include/Dengine/Common.h | 10 ++ src/include/Dengine/Entity.h | 17 +-- src/include/Dengine/Renderer.h | 2 + 9 files changed, 349 insertions(+), 220 deletions(-) diff --git a/src/AssetManager.c b/src/AssetManager.c index 8a2307c..0f38b3b 100644 --- a/src/AssetManager.c +++ b/src/AssetManager.c @@ -21,6 +21,76 @@ #include "Dengine/OpenGL.h" #include "Dengine/Platform.h" +INTERNAL AtlasSubTexture *getAtlasSubTexture(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 *makeAtlasSubTexture(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 *getFreeAnimSlot(Animation *table, u32 tableSize, + MemoryArena *arena, char *key) +{ + u32 hashIndex = common_getHashIndex(key, tableSize); + Animation *result = &table[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, Animation); + result = result->next; + } + + return result; +} + +Rect asset_getAtlasSubTexRect(TexAtlas *atlas, char *key) +{ + AtlasSubTexture *subTex = getAtlasSubTexture(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, @@ -49,27 +119,64 @@ Texture *asset_getTexture(AssetManager *const assetManager, return NULL; } -TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, const enum TexList type) +TexAtlas *asset_makeTexAtlas(AssetManager *const assetManager, + MemoryArena *arena, const char *const key) { - if (type < texlist_count) - return &assetManager->texAtlas[type]; + u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(assetManager->texAtlas)); + TexAtlas *result = &assetManager->texAtlas[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; + } -#ifdef DENGINE_DEBUG - ASSERT(INVALID_CODE_PATH); -#endif - return NULL; + result->next = PLATFORM_MEM_ALLOC(arena, 1, TexAtlas); + result = result->next; + } + + return result; } -Animation *asset_getAnim(AssetManager *assetManager, const enum AnimList type) +TexAtlas *asset_getTexAtlas(AssetManager *const assetManager, + const char *const key) { - if (type < animlist_count) - return &assetManager->anims[type]; + u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(assetManager->texAtlas)); + TexAtlas *result = &assetManager->texAtlas[hashIndex]; + if (result->key) + { + while (result && common_strcmp(result->key, key) != 0) + result = result->next; + } + else + { + return NULL; + } -#ifdef DENGINE_DEBUG - ASSERT(INVALID_CODE_PATH); -#endif + return result; +} - return NULL; +Animation *asset_getAnim(AssetManager *assetManager, char *key) +{ + u32 hashIndex = common_getHashIndex(key, ARRAY_COUNT(assetManager->anims)); + Animation *result = &assetManager->anims[hashIndex]; + if (result->key) + { + while (result && common_strcmp(result->key, key) != 0) + result = result->next; + } + else + { + return NULL; + } + + return result; } const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena, @@ -364,7 +471,8 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena, // TODO(doyle): We copy over the bitmap direct to the font sheet, should we // align the baselines up so we don't need to do baseline adjusting at // render? - i32 atlasIndex = 0; + char charToEncode = CAST(char)codepointRange.x; + TexAtlas *fontAtlas = asset_makeTexAtlas(assetManager, arena, "font"); for (i32 row = 0; row < MAX_TEXTURE_SIZE; row++) { u32 *destRow = fontBitmap + (row * MAX_TEXTURE_SIZE); @@ -378,23 +486,26 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena, /* Store the location of glyph into atlas */ if (verticalPixelsBlitted == 0) { - TexAtlas *fontAtlas = &assetManager->texAtlas[texlist_font]; #ifdef DENGINE_DEBUG - ASSERT(activeGlyph.codepoint < ARRAY_COUNT(fontAtlas->texRect)); + ASSERT(activeGlyph.codepoint < ARRAY_COUNT(fontAtlas->subTex)); #endif v2 origin = V2(CAST(f32)(glyphIndex * font->maxSize.w), CAST(f32) row); -#if 1 - fontAtlas->texRect[atlasIndex++] = - math_getRect(origin, V2(CAST(f32) font->maxSize.w, - CAST(f32) font->maxSize.h)); -#else - v2i fontSize = - font->charMetrics[activeGlyph.codepoint - 32].trueSize; - fontAtlas->texRect[atlasIndex++] = math_getRect( - origin, V2(CAST(f32) fontSize.x, CAST(f32) fontSize.y)); -#endif + + // 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 = + makeAtlasSubTexture(fontAtlas, arena, charTmp); + + subTex->key = PLATFORM_MEM_ALLOC(arena, 1, char); + subTex->key[0] = charToEncode; + + subTex->rect = CAST(Rect){origin, font->maxSize}; + charToEncode++; } /* Copy over exactly one row of pixels */ @@ -452,8 +563,8 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena, #endif PLATFORM_MEM_FREE(arena, fontBitmap, bitmapSize); - font->tex = &assetManager->textures[texlist_font]; - font->atlas = &assetManager->texAtlas[texlist_font]; + fontAtlas->tex = &assetManager->textures[texlist_font]; + font->atlas = fontAtlas; // NOTE(doyle): Formula derived from STB Font font->verticalSpacing = @@ -472,26 +583,28 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena, return 0; } - void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena, - i32 texId, i32 animId, i32 *frameIndex, i32 numFrames, - f32 frameDuration) + char *animName, TexAtlas *atlas, char **subTextureNames, + i32 numSubTextures, f32 frameDuration) { -#ifdef DENGINE_DEBUG - ASSERT(assetManager && frameIndex) - ASSERT(!assetManager->anims[animId].frameIndex); -#endif + Animation *anim = getFreeAnimSlot( + assetManager->anims, ARRAY_COUNT(assetManager->anims), arena, animName); - Animation anim = {0}; - anim.atlas = asset_getTextureAtlas(assetManager, texId); + anim->atlas = atlas; + anim->frameDuration = frameDuration; + anim->numFrames = numSubTextures; - anim.frameIndex = PLATFORM_MEM_ALLOC(arena, numFrames, i32); - for (i32 i = 0; i < numFrames; i++) anim.frameIndex[i] = frameIndex[i]; + // NOTE(doyle): +1 for the null terminator + anim->name = PLATFORM_MEM_ALLOC(arena, common_strlen(animName) + 1, char); + common_strncpy(anim->name, animName, common_strlen(animName)); - anim.numFrames = numFrames; - anim.frameDuration = frameDuration; + anim->frameList = PLATFORM_MEM_ALLOC(arena, numSubTextures, char*); + for (i32 i = 0; i < numSubTextures; i++) + { + AtlasSubTexture *subTex = getAtlasSubTexture(atlas, subTextureNames[i]); + anim->frameList[i] = subTex->key; + } - assetManager->anims[animId] = anim; } v2 asset_stringDimInPixels(const Font *const font, const char *const string) diff --git a/src/Entity.c b/src/Entity.c index 4bb8752..68d0743 100644 --- a/src/Entity.c +++ b/src/Entity.c @@ -3,20 +3,28 @@ #include "Dengine/Platform.h" #include "Dengine/WorldTraveller.h" -void entity_setActiveAnim(Entity *entity, enum AnimList animId) +void entity_setActiveAnim(Entity *entity, char *animName) { -#ifdef DENGINE_DEBUG - ASSERT(animId < animlist_count); - ASSERT(entity->anim[animId].anim); -#endif - /* Reset current anim data */ - EntityAnim_ *currAnim = &entity->anim[entity->currAnimId]; - currAnim->currDuration = currAnim->anim->frameDuration; - currAnim->currFrame = 0; + EntityAnim *currEntityAnim = &entity->animList[entity->currAnimId]; + currEntityAnim->currDuration = currEntityAnim->anim->frameDuration; + currEntityAnim->currFrame = 0; /* Set entity active animation */ - entity->currAnimId = animId; + for (i32 i = 0; i < ARRAY_COUNT(entity->animList); i++) + { + Animation *anim = entity->animList[i].anim; + if (anim) + { + if (common_strcmp(anim->key, animName) == 0) + { + entity->currAnimId = i; + return; + } + } + } + + DEBUG_LOG("Entity does not have access to desired anim"); } void entity_updateAnim(Entity *entity, f32 dt) @@ -24,20 +32,15 @@ void entity_updateAnim(Entity *entity, f32 dt) if (!entity->tex) return; - // TODO(doyle): Recheck why we have this twice - EntityAnim_ *entityAnim = &entity->anim[entity->currAnimId]; - Animation anim = *entityAnim->anim; - i32 frameIndex = anim.frameIndex[entityAnim->currFrame]; - v4 texRect = anim.atlas->texRect[frameIndex]; + EntityAnim *currEntityAnim = &entity->animList[entity->currAnimId]; + Animation *anim = currEntityAnim->anim; - entityAnim->currDuration -= dt; - if (entityAnim->currDuration <= 0.0f) + currEntityAnim->currDuration -= dt; + if (currEntityAnim->currDuration <= 0.0f) { - entityAnim->currFrame++; - entityAnim->currFrame = entityAnim->currFrame % anim.numFrames; - frameIndex = entityAnim->anim->frameIndex[entityAnim->currFrame]; - texRect = anim.atlas->texRect[frameIndex]; - entityAnim->currDuration = anim.frameDuration; + currEntityAnim->currFrame++; + currEntityAnim->currFrame = currEntityAnim->currFrame % anim->numFrames; + currEntityAnim->currDuration = anim->frameDuration; } // NOTE(doyle): If humanoid entity, let animation dictate render size which @@ -47,18 +50,31 @@ void entity_updateAnim(Entity *entity, f32 dt) case entitytype_hero: case entitytype_mob: case entitytype_npc: - entity->renderSize = math_getRectSize(texRect); + char *frameName = anim->frameList[currEntityAnim->currFrame]; + Rect texRect = + asset_getAtlasSubTexRect(anim->atlas, frameName); + entity->renderSize = texRect.size; default: break; } } -void entity_addAnim(AssetManager *assetManager, Entity *entity, i32 animId) +void entity_addAnim(AssetManager *assetManager, Entity *entity, char *animName) { - Animation *anim = asset_getAnim(assetManager, animId); - entity->anim[animId].anim = anim; - entity->anim[animId].currFrame = 0; - entity->anim[animId].currDuration = anim->frameDuration; + i32 freeAnimIndex = 0; + for (i32 i = 0; i < ARRAY_COUNT(entity->animList); i++) + { + EntityAnim *entityAnim = &entity->animList[i]; + if (!entityAnim->anim) + { + entityAnim->anim = asset_getAnim(assetManager, animName); + entityAnim->currFrame = 0; + entityAnim->currDuration = entityAnim->anim->frameDuration; + return; + } + } + + DEBUG_LOG("No more free entity animation slots"); } void entity_addGenericMob(MemoryArena *arena, AssetManager *assetManager, @@ -81,12 +97,7 @@ void entity_addGenericMob(MemoryArena *arena, AssetManager *assetManager, mob->audioRenderer->sourceIndex = AUDIO_SOURCE_UNASSIGNED; /* Populate mob animation references */ - entity_addAnim(assetManager, mob, animlist_hero_idle); - entity_addAnim(assetManager, mob, animlist_hero_walk); - entity_addAnim(assetManager, mob, animlist_hero_wave); - entity_addAnim(assetManager, mob, animlist_hero_battlePose); - entity_addAnim(assetManager, mob, animlist_hero_tackle); - mob->currAnimId = animlist_hero_idle; + entity_addAnim(assetManager, mob, "Claude idle"); } Entity *entity_add(MemoryArena *arena, World *world, v2 pos, v2 size, diff --git a/src/Renderer.c b/src/Renderer.c index c832e6c..903080a 100644 --- a/src/Renderer.c +++ b/src/Renderer.c @@ -175,16 +175,13 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera, v2 pivotPoint, f32 rotate, v4 color) { i32 strLen = common_strlen(string); - // TODO(doyle): Scale, not too important .. but rudimentary infrastructure - // laid out here - f32 scale = 1.0f; // TODO(doyle): Slightly incorrect string length in pixels calculation, // because we use the advance metric of each character for length not // maximum character size in rendering v2 rightAlignedP = - v2_add(pos, V2(scale *(CAST(f32) font->maxSize.w * CAST(f32) strLen), - scale * CAST(f32) font->maxSize.h)); + v2_add(pos, V2((CAST(f32) font->maxSize.w * CAST(f32) strLen), + CAST(f32) font->maxSize.h)); v2 leftAlignedP = pos; if (math_pointInRect(camera, leftAlignedP) || math_pointInRect(camera, rightAlignedP)) @@ -197,27 +194,32 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera, pos = posInCameraSpace; // TODO(doyle): Find why font is 1px off, might be arial font semantics + Texture *tex = font->atlas->tex; f32 baseline = pos.y - font->verticalSpacing + 1; for (i32 i = 0; i < strLen; i++) { - // NOTE(doyle): Atlas packs fonts tightly, so offset the codepoint - // to its actual atlas index, i.e. we skip the first 31 glyphs i32 codepoint = string[i]; i32 relativeIndex = CAST(i32)(codepoint - font->codepointRange.x); CharMetrics charMetric = font->charMetrics[relativeIndex]; - pos.y = baseline - (scale * charMetric.offset.y); + pos.y = baseline - (charMetric.offset.y); const v4 charRectOnScreen = - math_getRect(pos, V2(scale * CAST(f32) font->maxSize.w, - scale * CAST(f32) font->maxSize.h)); + math_getRect(pos, V2(CAST(f32) font->maxSize.w, + CAST(f32) font->maxSize.h)); - pos.x += scale * charMetric.advance; + pos.x += charMetric.advance; /* Get texture out */ - v4 charTexRect = font->atlas->texRect[relativeIndex]; - flipTexCoord(&charTexRect, FALSE, TRUE); - RenderTex renderTex = {font->tex, charTexRect}; + Rect charTexRect = + asset_getAtlasSubTexRect(font->atlas, &CAST(char)codepoint); + v4 deprecatedTexRect = {0}; + deprecatedTexRect.vec2[0] = charTexRect.pos; + deprecatedTexRect.vec2[1] = v2_add(charTexRect.pos, charTexRect.size); + + flipTexCoord(&deprecatedTexRect, FALSE, TRUE); + + RenderTex renderTex = {tex, deprecatedTexRect}; RenderQuad charQuad = createTexQuad(renderer, charRectOnScreen, renderTex); stringQuads[quadIndex++] = charQuad; @@ -228,7 +230,7 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera, // we're rendering a window sized buffer updateBufferObject(renderer, stringQuads, quadIndex); renderObject(renderer, V2(0.0f, 0.0f), renderer->size, pivotPoint, - rotate, color, font->tex); + rotate, color, tex); PLATFORM_MEM_FREE(arena, stringQuads, strLen * sizeof(RenderQuad)); } } @@ -246,12 +248,17 @@ void renderer_entity(Renderer *renderer, Rect camera, Entity *entity, if (math_pointInRect(camera, leftAlignedP) || math_pointInRect(camera, rightAlignedP)) { - EntityAnim_ *entityAnim = &entity->anim[entity->currAnimId]; - Animation *anim = entityAnim->anim; - i32 frameIndex = anim->frameIndex[entityAnim->currFrame]; - v4 animTexRect = anim->atlas->texRect[frameIndex]; + EntityAnim *entityAnim = &entity->animList[entity->currAnimId]; + Animation *anim = entityAnim->anim; + char *frameName = anim->frameList[entityAnim->currFrame]; + Rect animRect = asset_getAtlasSubTexRect(anim->atlas, frameName); - if (entity->direction == direction_east) + // TODO(doyle): Switch to rect + v4 animTexRect = {0}; + animTexRect.vec2[0] = animRect.pos; + animTexRect.vec2[1] = v2_add(animRect.pos, animRect.size); + + if (entity->direction == direction_east) { flipTexCoord(&animTexRect, TRUE, FALSE); } diff --git a/src/WorldTraveller.c b/src/WorldTraveller.c index 392bbf2..4281695 100644 --- a/src/WorldTraveller.c +++ b/src/WorldTraveller.c @@ -420,9 +420,6 @@ INTERNAL void assetInit(GameState *state) } } -// TODO(doyle): Use a proper random seed -#define RANDOM_SEED 0xDEADBEEF - #if 1 DEBUG_RECURSIVE_PRINT_XML_TREE(&root); #endif @@ -433,31 +430,11 @@ INTERNAL void assetInit(GameState *state) if(common_strcmp(node->name, "TextureAtlas") == 0) { XmlNode *atlasXmlNode = node; - TexAtlasEntry *atlasEntry = NULL; + TexAtlas *atlasEntry = NULL; if (common_strcmp(node->attribute.name, "imagePath") == 0) { char *imageName = atlasXmlNode->attribute.value; - u32 atlasHashIndex = common_murmurHash2( - imageName, common_strlen(imageName), RANDOM_SEED); - atlasHashIndex = - atlasHashIndex % ARRAY_COUNT(assetManager->texAtlas_); - - atlasEntry = &assetManager->texAtlas_[atlasHashIndex]; - if (atlasEntry->name) - { -#ifdef DENGINE_DEBUG - - // NOTE(doyle): Two atlas textures have the same access name - ASSERT(common_strcmp(atlasEntry->name, imageName) != 0); -#endif - - while (atlasEntry->next) - atlasEntry = atlasEntry->next; - - atlasEntry->next = - PLATFORM_MEM_ALLOC(arena, 1, TexAtlasEntry); - atlasEntry = atlasEntry->next; - } + atlasEntry = asset_makeTexAtlas(assetManager, arena, imageName); char *dataDir = "data/textures/WorldTraveller/"; char imagePath[512] = {0}; @@ -466,9 +443,9 @@ INTERNAL void assetInit(GameState *state) asset_loadTextureImage(assetManager, imagePath, texlist_claude); - atlasEntry->name = - PLATFORM_MEM_ALLOC(arena, common_strlen(imageName), char); - common_strncpy(atlasEntry->name, imageName, + atlasEntry->key = + PLATFORM_MEM_ALLOC(arena, common_strlen(imageName)+1, char); + common_strncpy(atlasEntry->key, imageName, common_strlen(imageName)); atlasEntry->tex = @@ -495,39 +472,43 @@ INTERNAL void assetInit(GameState *state) 0) { char *value = subTextureAttrib->value; - newSubTexEntry.name = value; + newSubTexEntry.key = value; } else if (common_strcmp(subTextureAttrib->name, "x") == 0) { char *value = subTextureAttrib->value; i32 valueLen = common_strlen(value); - newSubTexEntry.rect.pos.x = - CAST(f32) common_atoi(value, valueLen); + 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); - newSubTexEntry.rect.pos.y = - CAST(f32) common_atoi(value, valueLen); + + 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); - newSubTexEntry.rect.size.w = - CAST(f32) common_atoi(value, valueLen); + 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); - newSubTexEntry.rect.size.h = - CAST(f32) common_atoi(value, valueLen); + i32 intValue = common_atoi(value, valueLen); + + newSubTexEntry.rect.size.h = CAST(f32) intValue; } else { @@ -536,32 +517,37 @@ INTERNAL void assetInit(GameState *state) "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.name) + ASSERT(newSubTexEntry.key) #endif - u32 subTexHashIndex = common_murmurHash2( - newSubTexEntry.name, common_strlen(newSubTexEntry.name), - RANDOM_SEED); + newSubTexEntry.key, + common_strlen(newSubTexEntry.key), 0xDEADBEEF); subTexHashIndex = subTexHashIndex % ARRAY_COUNT(atlasEntry->subTex); // NOTE(doyle): Hash collision AtlasSubTexture *subTexEntry = &atlasEntry->subTex[subTexHashIndex]; - if (subTexEntry->name) + if (subTexEntry->key) { #ifdef DENGINE_DEBUG // NOTE(doyle): Two textures have the same access // name - ASSERT(common_strcmp(subTexEntry->name, - newSubTexEntry.name) != 0); + ASSERT(common_strcmp(subTexEntry->key, + newSubTexEntry.key) != 0); #endif while (subTexEntry->next) subTexEntry = subTexEntry->next; @@ -572,8 +558,12 @@ INTERNAL void assetInit(GameState *state) } *subTexEntry = newSubTexEntry; - common_strncpy(subTexEntry->name, newSubTexEntry.name, - common_strlen(newSubTexEntry.name)); + i32 keyLen = common_strlen(newSubTexEntry.key); + + subTexEntry->key = + PLATFORM_MEM_ALLOC(arena, keyLen+1, char); + common_strncpy(subTexEntry->key, newSubTexEntry.key, + keyLen); } else { @@ -601,34 +591,6 @@ INTERNAL void assetInit(GameState *state) node = node->next; } - /* Load textures */ - asset_loadTextureImage(assetManager, - "data/textures/WorldTraveller/TerraSprite1024.png", - texlist_hero); - TexAtlas *heroAtlas = asset_getTextureAtlas(assetManager, texlist_hero); - heroAtlas->texRect[herorects_idle] = V4(746, 920, 804, 1018); - heroAtlas->texRect[herorects_walkA] = V4(641, 920, 699, 1018); - heroAtlas->texRect[herorects_walkB] = V4(849, 920, 904, 1018); - heroAtlas->texRect[herorects_head] = V4(108, 975, 159, 1024); - heroAtlas->texRect[herorects_waveA] = V4(944, 816, 1010, 918); - heroAtlas->texRect[herorects_waveB] = V4(944, 710, 1010, 812); - heroAtlas->texRect[herorects_battlePose] = V4(8, 814, 71, 910); - heroAtlas->texRect[herorects_castA] = V4(428, 814, 493, 910); - heroAtlas->texRect[herorects_castB] = V4(525, 816, 590, 919); - heroAtlas->texRect[herorects_castC] = V4(640, 816, 698, 916); - - asset_loadTextureImage(assetManager, - "data/textures/WorldTraveller/Terrain.png", - texlist_terrain); - TexAtlas *terrainAtlas = - asset_getTextureAtlas(assetManager, texlist_terrain); - f32 atlasTileSize = 128.0f; - const i32 texSize = 1024; - v2 texOrigin = V2(0, 768); - terrainAtlas->texRect[terrainrects_ground] = - V4(texOrigin.x, texOrigin.y, texOrigin.x + atlasTileSize, - texOrigin.y + atlasTileSize); - /* Load shaders */ asset_loadShaderFiles(assetManager, arena, "data/shaders/sprite.vert.glsl", "data/shaders/sprite.frag.glsl", @@ -643,6 +605,7 @@ INTERNAL void assetInit(GameState *state) DEBUG_LOG("Assets loaded"); #endif +#if 0 /* Load animations */ f32 duration = 1.0f; i32 numRects = 1; @@ -660,7 +623,20 @@ INTERNAL void assetInit(GameState *state) i32 idleAnimAtlasIndexes[1] = {herorects_idle}; asset_addAnimation(assetManager, arena, texlist_hero, animlist_hero_idle, idleAnimAtlasIndexes, numRects, duration); +#else + f32 duration = 1.0f; + i32 numRects = 1; + TexAtlas *claudeAtlas = + asset_getTexAtlas(assetManager, "ClaudeSpriteSheet.png"); + duration = 1.0f; + numRects = 1; + char *subTextureNames = {"ClaudeSprite_001"}; + asset_addAnimation(assetManager, arena, "Claude_idle", claudeAtlas, + &subTextureNames, 1, 1.0f); +#endif + +#if 0 // Walk animation duration = 0.10f; numRects = 3; @@ -690,6 +666,8 @@ INTERNAL void assetInit(GameState *state) herorects_castC}; asset_addAnimation(assetManager, arena, texlist_hero, animlist_hero_tackle, tackleAnimAtlasIndexes, numRects, duration); +#endif + #ifdef DENGINE_DEBUG DEBUG_LOG("Animations created"); #endif @@ -739,6 +717,7 @@ INTERNAL void entityInit(GameState *state, v2 windowSize) CAST(f32) state->tileSize)); world->uniqueIdAccumulator = 0; +#if 0 TexAtlas *const atlas = asset_getTextureAtlas(assetManager, world->texType); @@ -765,6 +744,7 @@ INTERNAL void entityInit(GameState *state, v2 windowSize) tile->currAnimId = animlist_terrain; } } +#endif } World *const world = &state->world[state->currWorldIndex]; @@ -790,7 +770,7 @@ INTERNAL void entityInit(GameState *state, v2 windowSize) pos = V2(size.x, CAST(f32) state->tileSize); type = entitytype_hero; dir = direction_east; - tex = asset_getTexture(assetManager, texlist_hero); + tex = asset_getTexture(assetManager, texlist_claude); collides = TRUE; Entity *hero = entity_add(arena, world, pos, size, type, dir, tex, collides); @@ -801,13 +781,10 @@ INTERNAL void entityInit(GameState *state, v2 windowSize) world->cameraFollowingId = hero->id; /* Populate hero animation references */ - entity_addAnim(assetManager, hero, animlist_hero_idle); - entity_addAnim(assetManager, hero, animlist_hero_walk); - entity_addAnim(assetManager, hero, animlist_hero_wave); - entity_addAnim(assetManager, hero, animlist_hero_battlePose); - entity_addAnim(assetManager, hero, animlist_hero_tackle); - hero->currAnimId = animlist_hero_idle; + entity_addAnim(assetManager, hero, "Claude_idle"); + entity_setActiveAnim(hero, "Claude_idle"); +#if 0 /* Create a NPC */ pos = V2(hero->pos.x * 3, CAST(f32) state->tileSize); size = hero->hitboxSize; @@ -825,6 +802,7 @@ INTERNAL void entityInit(GameState *state, v2 windowSize) pos = V2(renderer->size.w - (renderer->size.w / 3.0f), CAST(f32) state->tileSize); entity_addGenericMob(arena, assetManager, world, pos); +#endif #ifdef DENGINE_DEBUG DEBUG_LOG("World populated"); @@ -1152,7 +1130,7 @@ INTERNAL inline void updateWorldBattleEntities(World *world, Entity *entity, INTERNAL inline void resetEntityState(World *world, Entity *entity) { updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE); - entity_setActiveAnim(entity, animlist_hero_idle); + entity_setActiveAnim(entity, "Claude_idle"); entity->stats->busyDuration = 0; entity->stats->actionTimer = entity->stats->actionRate; entity->stats->queuedAttack = entityattack_invalid; @@ -1196,7 +1174,7 @@ INTERNAL void entityStateSwitch(EventQueue *eventQueue, World *world, // or not (i.e. has moved out of frame last frame). case entitystate_dead: registerEvent(eventQueue, eventtype_entity_died, CAST(void *)entity); - entity_setActiveAnim(entity, animlist_hero_idle); + entity_setActiveAnim(entity, "Claude_idle"); entity->stats->busyDuration = 0; entity->stats->actionTimer = entity->stats->actionRate; entity->stats->queuedAttack = entityattack_invalid; @@ -1236,7 +1214,7 @@ INTERNAL void entityStateSwitch(EventQueue *eventQueue, World *world, switch (newState) { case entitystate_battle: - entity_setActiveAnim(entity, animlist_hero_battlePose); + entity_setActiveAnim(entity, "Claude_idle"); entity->stats->actionTimer = entity->stats->actionRate; entity->stats->busyDuration = 0; break; @@ -1288,6 +1266,7 @@ typedef struct AttackSpec INTERNAL void beginAttack(EventQueue *eventQueue, World *world, Entity *attacker) { +#if 0 #ifdef DENGINE_DEBUG ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID); ASSERT(attacker->state == entitystate_battle); @@ -1313,6 +1292,7 @@ INTERNAL void beginAttack(EventQueue *eventQueue, World *world, #endif break; } +#endif } // TODO(doyle): MemArena here is temporary until we incorporate AttackSpec to @@ -1320,6 +1300,7 @@ INTERNAL void beginAttack(EventQueue *eventQueue, World *world, INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue, World *world, Entity *attacker) { +#if 0 #ifdef DENGINE_DEBUG ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID); #endif @@ -1398,6 +1379,7 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue, entityStateSwitch(eventQueue, world, attacker, entitystate_idle); } } +#endif } INTERNAL void sortWorldEntityList(World *world) @@ -1735,12 +1717,12 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) hero->dPos = V2(0.0f, 0.0f); if (hero->currAnimId == animlist_hero_walk) { - entity_setActiveAnim(hero, animlist_hero_idle); + entity_setActiveAnim(hero, "Claude_idle"); } } else if (hero->currAnimId == animlist_hero_idle) { - entity_setActiveAnim(hero, animlist_hero_walk); + entity_setActiveAnim(hero, "Claude_idle"); } f32 heroSpeed = 6.2f * METERS_TO_PIXEL; @@ -1978,7 +1960,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) { hero->state = entitystate_idle; world->entityIdInBattle[hero->id] = FALSE; - entity_setActiveAnim(hero, animlist_hero_idle); + entity_setActiveAnim(hero, "Claude_idle"); } hero->stats->entityIdToAttack = -1; hero->stats->actionTimer = hero->stats->actionRate; @@ -2058,6 +2040,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) state->uiState.keyChar = keycode_null; /* Draw hero avatar */ +#if 0 TexAtlas *heroAtlas = asset_getTextureAtlas(assetManager, texlist_hero); v4 heroAvatarTexRect = heroAtlas->texRect[herorects_head]; v2 heroAvatarSize = math_getRectSize(heroAvatarTexRect); @@ -2079,6 +2062,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) renderer_staticString(&state->renderer, &state->arena, font, heroAvatarStr, strPos, V2(0, 0), 0, V4(0, 0, 1, 1)); +#endif for (i32 i = 0; i < world->maxEntities; i++) { diff --git a/src/include/Dengine/AssetManager.h b/src/include/Dengine/AssetManager.h index 691068c..d324cbd 100644 --- a/src/include/Dengine/AssetManager.h +++ b/src/include/Dengine/AssetManager.h @@ -11,25 +11,33 @@ typedef struct MemoryArena MemoryArena; typedef struct AssetManager { Texture textures[32]; - TexAtlas texAtlas[32]; Shader shaders[32]; - Animation anims[32]; + Animation anims[1024]; AudioVorbis audio[32]; Font font; - TexAtlasEntry texAtlas_[8]; + 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_getTexture(AssetManager *const assetManager, const enum TexList type); + +TexAtlas *asset_makeTexAtlas(AssetManager *const assetManager, + MemoryArena *arena, const char *const key); + Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type); -TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, - const enum TexList type); -Animation *asset_getAnim(AssetManager *assetManager, i32 type); + +TexAtlas *asset_getTexAtlas(AssetManager *const assetManager, + 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); @@ -46,8 +54,8 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena, const char *filePath); void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena, - i32 texId, i32 animId, i32 *atlasIndexes, i32 numFrames, - f32 frameDuration); + char *animName, TexAtlas *atlas, char **subTextureNames, + i32 numSubTextures, f32 frameDuration); v2 asset_stringDimInPixels(const Font *const font, const char *const string); diff --git a/src/include/Dengine/Assets.h b/src/include/Dengine/Assets.h index e8a1a35..bad480b 100644 --- a/src/include/Dengine/Assets.h +++ b/src/include/Dengine/Assets.h @@ -83,36 +83,37 @@ typedef struct AudioVorbis typedef struct AtlasSubTexture { - char *name; + // NOTE(doyle): Key used to arrive to hash entry + char *key; Rect rect; // NOTE(doyle): For hashing collisions struct AtlasSubTexture *next; } AtlasSubTexture; -typedef struct TexAtlasEntry -{ - char *name; - Texture *tex; - AtlasSubTexture subTex[1024]; - - // NOTE(doyle): For hashing collisions - struct TexAtlasEntry *next; -} TexAtlasEntry; - typedef struct TexAtlas { - // TODO(doyle): String hash based lookup - v4 texRect[128]; + char *key; + Texture *tex; + AtlasSubTexture subTex[512]; + + struct TexAtlas *next; } TexAtlas; typedef struct Animation { + union { + char *name; + char *key; + }; + TexAtlas *atlas; - i32 *frameIndex; + char **frameList; i32 numFrames; f32 frameDuration; + + struct Animation *next; } Animation; // TODO(doyle): We only use the offset and advance metric at the moment, remove? @@ -136,8 +137,6 @@ typedef struct CharMetrics typedef struct Font { TexAtlas *atlas; - Texture *tex; - FontMetrics metrics; // NOTE(doyle): Array of character's by ASCII value starting from diff --git a/src/include/Dengine/Common.h b/src/include/Dengine/Common.h index 3ce4263..399d9ef 100644 --- a/src/include/Dengine/Common.h +++ b/src/include/Dengine/Common.h @@ -54,4 +54,14 @@ i32 common_atoi(const char *string, const i32 len); // machines. u32 common_murmurHash2(const void *key, i32 len, u32 seed); +// TODO(doyle): Use a proper random seed +#define RANDOM_SEED 0xDEADBEEF +inline u32 common_getHashIndex(const char *const key, u32 tableSize) +{ + u32 result = common_murmurHash2(key, common_strlen(key), RANDOM_SEED); + result = result % tableSize; + return result; +} + + #endif diff --git a/src/include/Dengine/Entity.h b/src/include/Dengine/Entity.h index 07741a0..53d06bd 100644 --- a/src/include/Dengine/Entity.h +++ b/src/include/Dengine/Entity.h @@ -63,12 +63,12 @@ typedef struct EntityStats enum EntityAttack queuedAttack; } EntityStats; -typedef struct EntityAnim_ +typedef struct EntityAnim { Animation *anim; i32 currFrame; f32 currDuration; -} EntityAnim_; +} EntityAnim; typedef struct Entity { @@ -86,22 +86,17 @@ typedef struct Entity Texture *tex; b32 collides; - // TODO(doyle): String based access - // TODO(doyle): Separate animation refs from curr animation state, entity - // only ever has one active current animation. ATM every entity animation - // has a currframe and duration .. either that or we stop resetting - // animation on entity context switch - EntityAnim_ anim[16]; - enum AnimList currAnimId; + EntityAnim animList[16]; + i32 currAnimId; EntityStats *stats; AudioRenderer *audioRenderer; i32 numAudioRenderers; } Entity; -void entity_setActiveAnim(Entity *entity, enum AnimList animId); +void entity_setActiveAnim(Entity *entity, char *animName); void entity_updateAnim(Entity *entity, f32 dt); -void entity_addAnim(AssetManager *assetManager, Entity *entity, i32 animId); +void entity_addAnim(AssetManager *assetManager, Entity *entity, char *animName); void entity_addGenericMob(MemoryArena *arena, AssetManager *assetManager, World *world, v2 pos); Entity *entity_add(MemoryArena *arena, World *world, v2 pos, v2 size, diff --git a/src/include/Dengine/Renderer.h b/src/include/Dengine/Renderer.h index e81b0bc..209662a 100644 --- a/src/include/Dengine/Renderer.h +++ b/src/include/Dengine/Renderer.h @@ -25,6 +25,8 @@ typedef struct Renderer typedef struct RenderTex { Texture *tex; + + // TODO(doyle): Switch to rect v4 texRect; } RenderTex;