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.
This commit is contained in:
Doyle Thai 2016-08-25 02:06:32 +10:00
parent b43754986f
commit 7ca42f781a
9 changed files with 349 additions and 220 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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);
}

View File

@ -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++)
{

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -25,6 +25,8 @@ typedef struct Renderer
typedef struct RenderTex
{
Texture *tex;
// TODO(doyle): Switch to rect
v4 texRect;
} RenderTex;