Switch entity anims to use references to assets

This commit is contained in:
Doyle Thai 2016-07-19 03:47:32 +10:00
parent fff9e6e5b2
commit b8b76cecd3
6 changed files with 181 additions and 142 deletions

View File

@ -39,6 +39,18 @@ TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, const enum TexList t
return NULL; return NULL;
} }
Animation *asset_getAnim(AssetManager *assetManager, i32 type)
{
if (type < animlist_count)
return &assetManager->anims[type];
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
return NULL;
}
const i32 asset_loadTextureImage(AssetManager *assetManager, const i32 asset_loadTextureImage(AssetManager *assetManager,
const char *const path, const enum TexList type) const char *const path, const enum TexList type)
{ {
@ -400,3 +412,27 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
return 0; return 0;
} }
void asset_addAnimation(AssetManager *assetManager, i32 texId,
i32 animId, i32 *atlasIndexes, i32 numFrames,
f32 frameDuration)
{
#ifdef DENGINE_DEBUG
ASSERT(assetManager && atlasIndexes)
ASSERT(!assetManager->anims[animId].atlasIndexes);
#endif
Animation anim = {0};
// TODO(doyle): Do we need texture to be bound to animation?
anim.tex = asset_getTexture(assetManager, texId);
anim.atlas = asset_getTextureAtlas(assetManager, texId);
anim.atlasIndexes = PLATFORM_MEM_ALLOC(numFrames, i32);
for (i32 i = 0; i < numFrames; i++)
anim.atlasIndexes[i] = atlasIndexes[i];
anim.numFrames = numFrames;
anim.frameDuration = frameDuration;
assetManager->anims[animId] = anim;
}

View File

@ -227,8 +227,10 @@ void renderer_entity(Renderer *renderer, v4 cameraBounds, Entity *entity,
if ((leftAlignedP.x < cameraBounds.z && rightAlignedP.x >= cameraBounds.x) && if ((leftAlignedP.x < cameraBounds.z && rightAlignedP.x >= cameraBounds.x) &&
(leftAlignedP.y < cameraBounds.y && rightAlignedP.y >= cameraBounds.w)) (leftAlignedP.y < cameraBounds.y && rightAlignedP.y >= cameraBounds.w))
{ {
EntityAnim *anim = &entity->anim[entity->currAnimId]; EntityAnim_ *entityAnim = &entity->anim[entity->currAnimId];
v4 animTexRect = anim->rect[anim->currRectIndex]; Animation *anim = entityAnim->anim;
i32 atlasIndex = anim->atlasIndexes[entityAnim->currFrame];
v4 animTexRect = anim->atlas->texRect[atlasIndex];
if (entity->direction == direction_east) if (entity->direction == direction_east)
{ {

View File

@ -56,24 +56,6 @@ INTERNAL Entity *addEntity(World *world, v2 pos, v2 size, enum EntityType type,
return result; return result;
} }
INTERNAL void addAnim(Entity *entity, enum EntityAnimId animId, v4 *rects,
i32 numRects, f32 duration)
{
#ifdef DENGINE_DEBUG
ASSERT(rects && numRects >= 0)
ASSERT(animId < entityanimid_count);
#endif
EntityAnim result = {0};
result.rect = rects;
result.numRects = numRects;
result.duration = duration;
result.currDuration = duration;
entity->anim[animId] = result;
}
INTERNAL void rendererInit(GameState *state, v2 windowSize) INTERNAL void rendererInit(GameState *state, v2 windowSize)
{ {
AssetManager *assetManager = &state->assetManager; AssetManager *assetManager = &state->assetManager;
@ -113,6 +95,14 @@ INTERNAL void rendererInit(GameState *state, v2 windowSize)
glCheckError(); glCheckError();
} }
INTERNAL void addAnim(AssetManager *assetManager, i32 animId, Entity *entity)
{
Animation *anim = asset_getAnim(assetManager, animId);
entity->anim[animId].anim = anim;
entity->anim[animId].currFrame = 0;
entity->anim[animId].currDuration = anim->frameDuration;
}
void worldTraveller_gameInit(GameState *state, v2 windowSize) void worldTraveller_gameInit(GameState *state, v2 windowSize)
{ {
AssetManager *assetManager = &state->assetManager; AssetManager *assetManager = &state->assetManager;
@ -146,7 +136,7 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
f32 atlasTileSize = 128.0f; f32 atlasTileSize = 128.0f;
const i32 texSize = 1024; const i32 texSize = 1024;
v2 texOrigin = V2(0, CAST(f32)(texSize - 128)); v2 texOrigin = V2(0, CAST(f32)(texSize - 128));
terrainAtlas->texRect[terraincoords_ground] = terrainAtlas->texRect[terrainrects_ground] =
V4(texOrigin.x, texOrigin.y, texOrigin.x + atlasTileSize, V4(texOrigin.x, texOrigin.y, texOrigin.x + atlasTileSize,
texOrigin.y - atlasTileSize); texOrigin.y - atlasTileSize);
@ -158,6 +148,55 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
asset_loadTTFont(assetManager, "C:/Windows/Fonts/Arialbd.ttf"); asset_loadTTFont(assetManager, "C:/Windows/Fonts/Arialbd.ttf");
glCheckError(); glCheckError();
/* Load animations */
f32 duration = 1.0f;
i32 numRects = 1;
v4 *animRects = PLATFORM_MEM_ALLOC(numRects, v4);
i32 terrainAnimAtlasIndexes[1] = {terrainrects_ground};
// TODO(doyle): Optimise animation storage, we waste space having 1:1 with
// animlist when some textures don't have certain animations
asset_addAnimation(assetManager, texlist_terrain, animlist_terrain,
terrainAnimAtlasIndexes, numRects, duration);
// Idle animation
duration = 1.0f;
numRects = 1;
i32 idleAnimAtlasIndexes[1] = {herorects_idle};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_idle,
idleAnimAtlasIndexes, numRects, duration);
// Walk animation
duration = 0.10f;
numRects = 3;
i32 walkAnimAtlasIndexes[3] = {herorects_walkA, herorects_idle,
herorects_walkB};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_walk,
walkAnimAtlasIndexes, numRects, duration);
// Wave animation
duration = 0.30f;
numRects = 2;
i32 waveAnimAtlasIndexes[2] = {herorects_waveA, herorects_waveB};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_wave,
waveAnimAtlasIndexes, numRects, duration);
// Battle Stance animation
duration = 1.0f;
numRects = 1;
i32 battleStanceAnimAtlasIndexes[1] = {herorects_battlePose};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_battlePose,
battleStanceAnimAtlasIndexes, numRects, duration);
// Battle tackle animation
duration = 0.15f;
numRects = 3;
i32 tackleAnimAtlasIndexes[3] = {herorects_castA, herorects_castB,
herorects_castC};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_tackle,
tackleAnimAtlasIndexes, numRects, duration);
state->state = state_active; state->state = state_active;
state->currWorldIndex = 0; state->currWorldIndex = 0;
state->tileSize = 64; state->tileSize = 64;
@ -205,11 +244,8 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
Entity *tile = Entity *tile =
addEntity(world, pos, size, type, dir, tex, collides); addEntity(world, pos, size, type, dir, tex, collides);
f32 duration = 1.0f; addAnim(assetManager, animlist_terrain, tile);
i32 numRects = 1; tile->currAnimId = animlist_terrain;
v4 *animRects = PLATFORM_MEM_ALLOC(numRects, v4);
animRects[0] = atlas->texRect[terraincoords_ground];
addAnim(tile, entityanimid_idle, animRects, numRects, duration);
} }
} }
} }
@ -229,48 +265,13 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
b32 collides = TRUE; b32 collides = TRUE;
Entity *hero = addEntity(world, pos, size, type, dir, tex, collides); Entity *hero = addEntity(world, pos, size, type, dir, tex, collides);
/* Add idle animation */ /* Populate hero animation references */
f32 duration = 1.0f; addAnim(assetManager, animlist_hero_idle, hero);
i32 numRects = 1; addAnim(assetManager, animlist_hero_walk, hero);
v4 *heroIdleRects = PLATFORM_MEM_ALLOC(numRects, v4); addAnim(assetManager, animlist_hero_wave, hero);
heroIdleRects[0] = heroAtlas->texRect[herorects_idle]; addAnim(assetManager, animlist_hero_battlePose, hero);
addAnim(hero, entityanimid_idle, heroIdleRects, numRects, duration); addAnim(assetManager, animlist_hero_tackle, hero);
hero->currAnimId = entityanimid_idle; hero->currAnimId = animlist_hero_idle;
/* Add walking animation */
duration = 0.10f;
numRects = 3;
v4 *heroWalkRects = PLATFORM_MEM_ALLOC(numRects, v4);
heroWalkRects[0] = heroAtlas->texRect[herorects_walkA];
heroWalkRects[1] = heroAtlas->texRect[herorects_idle];
heroWalkRects[2] = heroAtlas->texRect[herorects_walkB];
addAnim(hero, entityanimid_walk, heroWalkRects, numRects, duration);
/* Add hero waving animation */
duration = 0.30f;
numRects = 2;
v4 *heroWaveRects = PLATFORM_MEM_ALLOC(numRects, v4);
heroWaveRects[0] = heroAtlas->texRect[herorects_waveA];
heroWaveRects[1] = heroAtlas->texRect[herorects_waveB];
addAnim(hero, entityanimid_wave, heroWaveRects, numRects, duration);
/* Add hero battle stance animation */
duration = 1.0f;
numRects = 1;
v4 *heroBattlePoseRects = PLATFORM_MEM_ALLOC(numRects, v4);
heroBattlePoseRects[0] = heroAtlas->texRect[herorects_battlePose];
addAnim(hero, entityanimid_battlePose, heroBattlePoseRects, numRects,
duration);
/* Add hero battle tackle animation */
duration = 0.15f;
numRects = 3;
v4 *heroTackleRects = PLATFORM_MEM_ALLOC(numRects, v4);
heroTackleRects[0] = heroAtlas->texRect[herorects_castA];
heroTackleRects[1] = heroAtlas->texRect[herorects_castB];
heroTackleRects[2] = heroAtlas->texRect[herorects_castC];
addAnim(hero, entityanimid_tackle, heroTackleRects, numRects,
duration);
/* Create a NPC */ /* Create a NPC */
pos = V2(hero->pos.x * 3, CAST(f32) state->tileSize); pos = V2(hero->pos.x * 3, CAST(f32) state->tileSize);
@ -281,14 +282,9 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
collides = FALSE; collides = FALSE;
Entity *npc = addEntity(world, pos, size, type, dir, tex, collides); Entity *npc = addEntity(world, pos, size, type, dir, tex, collides);
/* Add npc waving animation */ /* Populate npc animation references */
duration = 0.30f; addAnim(assetManager, animlist_hero_wave, npc);
numRects = 2; npc->currAnimId = animlist_hero_wave;
v4 *npcWavingRects = PLATFORM_MEM_ALLOC(numRects, v4);
npcWavingRects[0] = heroAtlas->texRect[herorects_waveA];
npcWavingRects[1] = heroAtlas->texRect[herorects_waveB];
addAnim(npc, entityanimid_wave, npcWavingRects, numRects, duration);
npc->currAnimId = entityanimid_wave;
/* Create a Mob */ /* Create a Mob */
pos = V2(renderer->size.w - (renderer->size.w / 3.0f), pos = V2(renderer->size.w - (renderer->size.w / 3.0f),
@ -300,36 +296,24 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
collides = TRUE; collides = TRUE;
Entity *mob = addEntity(world, pos, size, type, dir, tex, collides); Entity *mob = addEntity(world, pos, size, type, dir, tex, collides);
/* Add mob idle animation */ /* Populate mob animation references */
duration = 1.0f; addAnim(assetManager, animlist_hero_idle, mob);
numRects = 1; addAnim(assetManager, animlist_hero_walk, mob);
v4 *mobIdleRects = PLATFORM_MEM_ALLOC(numRects, v4); mob->currAnimId = animlist_hero_idle;
mobIdleRects[0] = heroIdleRects[0];
addAnim(mob, entityanimid_idle, mobIdleRects, numRects, duration);
mob->currAnimId = entityanimid_idle;
/* Add mob walking animation */
duration = 0.10f;
numRects = 3;
v4 *mobWalkRects = PLATFORM_MEM_ALLOC(numRects, v4);
mobWalkRects[0] = heroWalkRects[0];
mobWalkRects[1] = heroWalkRects[1];
mobWalkRects[2] = heroWalkRects[2];
addAnim(mob, entityanimid_walk, mobWalkRects, numRects, duration);
} }
INTERNAL inline void setActiveEntityAnim(Entity *entity, INTERNAL inline void setActiveEntityAnim(Entity *entity,
enum EntityAnimId animId) enum EntityAnimId animId)
{ {
#ifdef DENGINE_DEBUG #ifdef DENGINE_DEBUG
ASSERT(animId < entityanimid_count); ASSERT(animId < animlist_count);
ASSERT(entity->anim[animId].rect); ASSERT(entity->anim[animId].anim);
#endif #endif
/* Reset current anim data */ /* Reset current anim data */
EntityAnim *currAnim = &entity->anim[entity->currAnimId]; EntityAnim_ *currAnim = &entity->anim[entity->currAnimId];
currAnim->currDuration = currAnim->duration; currAnim->currDuration = currAnim->anim->frameDuration;
currAnim->currRectIndex = 0; currAnim->currFrame = 0;
/* Set entity active animation */ /* Set entity active animation */
entity->currAnimId = animId; entity->currAnimId = animId;
@ -412,14 +396,14 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
if (epsilonDpos.x >= 0.0f && epsilonDpos.y >= 0.0f) if (epsilonDpos.x >= 0.0f && epsilonDpos.y >= 0.0f)
{ {
hero->dPos = V2(0.0f, 0.0f); hero->dPos = V2(0.0f, 0.0f);
if (hero->currAnimId == entityanimid_walk) if (hero->currAnimId == animlist_hero_walk)
{ {
setActiveEntityAnim(hero, entityanimid_idle); setActiveEntityAnim(hero, animlist_hero_idle);
} }
} }
else if (hero->currAnimId == entityanimid_idle) else if (hero->currAnimId == animlist_hero_idle)
{ {
setActiveEntityAnim(hero, entityanimid_walk); setActiveEntityAnim(hero, animlist_hero_walk);
} }
f32 heroSpeed = 6.2f * METERS_TO_PIXEL; f32 heroSpeed = 6.2f * METERS_TO_PIXEL;
@ -489,18 +473,22 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
INTERNAL void updateEntityAnim(Entity *entity, f32 dt) INTERNAL void updateEntityAnim(Entity *entity, f32 dt)
{ {
EntityAnim *anim = &entity->anim[entity->currAnimId]; // TODO(doyle): Recheck why we have this twice
v4 texRect = anim->rect[anim->currRectIndex]; EntityAnim_ *entityAnim = &entity->anim[entity->currAnimId];
Animation anim = *entityAnim->anim;
i32 atlasIndex = anim.atlasIndexes[entityAnim->currFrame];
v4 texRect = anim.atlas->texRect[atlasIndex];
anim->currDuration -= dt; entityAnim->currDuration -= dt;
if (anim->currDuration <= 0.0f) if (entityAnim->currDuration <= 0.0f)
{ {
if (++anim->currRectIndex >= anim->numRects) if (++entityAnim->currFrame >= anim.numFrames)
entity->currAnimCyclesCompleted++; entity->currAnimCyclesCompleted++;
anim->currRectIndex = anim->currRectIndex % anim->numRects; entityAnim->currFrame = entityAnim->currFrame % anim.numFrames;
texRect = anim->rect[anim->currRectIndex]; atlasIndex = entityAnim->anim->atlasIndexes[entityAnim->currFrame];
anim->currDuration = anim->duration; texRect = anim.atlas->texRect[atlasIndex];
entityAnim->currDuration = anim.frameDuration;
} }
// NOTE(doyle): If humanoid entity, let animation dictate render size which // NOTE(doyle): If humanoid entity, let animation dictate render size which
@ -522,11 +510,12 @@ INTERNAL void beginAttack(Entity *attacker)
switch (attacker->stats->queuedAttack) switch (attacker->stats->queuedAttack)
{ {
case entityattack_tackle: case entityattack_tackle:
EntityAnim attackAnim = attacker->anim[entityanimid_tackle]; EntityAnim_ attackAnim = attacker->anim[animlist_hero_tackle];
f32 busyDuration = attackAnim.duration * CAST(f32) attackAnim.numRects; f32 busyDuration = attackAnim.anim->frameDuration *
CAST(f32) attackAnim.anim->numFrames;
attacker->stats->busyDuration = busyDuration; attacker->stats->busyDuration = busyDuration;
setActiveEntityAnim(attacker, entityanimid_tackle); setActiveEntityAnim(attacker, animlist_hero_tackle);
if (attacker->direction == direction_east) if (attacker->direction == direction_east)
attacker->dPos.x += (1.0f * METERS_TO_PIXEL); attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
@ -565,7 +554,7 @@ INTERNAL void endAttack(World *world, Entity *attacker)
attacker->stats->actionTimer = attacker->stats->actionRate; attacker->stats->actionTimer = attacker->stats->actionRate;
attacker->stats->busyDuration = 0; attacker->stats->busyDuration = 0;
setActiveEntityAnim(attacker, entityanimid_battlePose); setActiveEntityAnim(attacker, animlist_hero_battlePose);
Entity *defender = &world->entities[attacker->stats->entityIdToAttack]; Entity *defender = &world->entities[attacker->stats->entityIdToAttack];
defender->stats->health--; defender->stats->health--;
@ -682,7 +671,7 @@ INTERNAL void updateEntityAndRender(Renderer *renderer, World *world, f32 dt)
if (hero->state == entitystate_battle) if (hero->state == entitystate_battle)
{ {
hero->state = entitystate_idle; hero->state = entitystate_idle;
setActiveEntityAnim(hero, entityanimid_idle); setActiveEntityAnim(hero, animlist_hero_idle);
} }
hero->stats->entityIdToAttack = -1; hero->stats->entityIdToAttack = -1;
hero->stats->actionTimer = hero->stats->actionRate; hero->stats->actionTimer = hero->stats->actionRate;

View File

@ -13,21 +13,22 @@ typedef struct AssetManager
Texture textures[32]; Texture textures[32];
TexAtlas texAtlas[32]; TexAtlas texAtlas[32];
Shader shaders[32]; Shader shaders[32];
Animation anims[32];
Font font; Font font;
} AssetManager; } AssetManager;
GLOBAL_VAR AssetManager assetManager; GLOBAL_VAR AssetManager assetManager;
/* Texture */
Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type); Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type);
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type);
TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, TexAtlas *asset_getTextureAtlas(AssetManager *assetManager,
const enum TexList type); const enum TexList type);
Animation *asset_getAnim(AssetManager *assetManager, i32 type);
const i32 asset_loadTextureImage(AssetManager *assetManager, const i32 asset_loadTextureImage(AssetManager *assetManager,
const char *const path, const char *const path,
const enum TexList type); const enum TexList type);
/* Shaders */
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type);
const i32 asset_loadShaderFiles(AssetManager *assetManager, const i32 asset_loadShaderFiles(AssetManager *assetManager,
const char *const vertexPath, const char *const vertexPath,
const char *const fragmentPath, const char *const fragmentPath,
@ -42,4 +43,8 @@ inline i32 asset_getVFontSpacing(FontMetrics metrics)
return result; return result;
} }
void asset_addAnimation(AssetManager *assetManager, i32 texId,
i32 animId, i32 *atlasIndexes, i32 numFrames,
f32 frameDuration);
#endif #endif

View File

@ -19,10 +19,10 @@ enum ShaderList
shaderlist_count, shaderlist_count,
}; };
enum TerrainCoords enum TerrainRects
{ {
terraincoords_ground, terrainrects_ground,
terraincoords_count, terrainrects_count,
}; };
enum HeroRects enum HeroRects
@ -40,12 +40,33 @@ enum HeroRects
herorects_count, herorects_count,
}; };
enum AnimList
{
animlist_hero_idle,
animlist_hero_walk,
animlist_hero_wave,
animlist_hero_battlePose,
animlist_hero_tackle,
animlist_terrain,
animlist_count,
animlist_invalid,
};
typedef struct TexAtlas typedef struct TexAtlas
{ {
// TODO(doyle): String hash based lookup // TODO(doyle): String hash based lookup
v4 texRect[128]; v4 texRect[128];
} TexAtlas; } TexAtlas;
typedef struct Animation
{
Texture *tex;
TexAtlas *atlas;
i32 *atlasIndexes;
i32 numFrames;
f32 frameDuration;
} Animation;
// TODO(doyle): We only use the offset and advance metric at the moment, remove? // TODO(doyle): We only use the offset and advance metric at the moment, remove?
typedef struct FontMetrics typedef struct FontMetrics
{ {

View File

@ -25,17 +25,6 @@ enum EntityType
entitytype_count, entitytype_count,
}; };
enum EntityAnimId
{
entityanimid_idle,
entityanimid_walk,
entityanimid_wave,
entityanimid_battlePose,
entityanimid_tackle,
entityanimid_count,
entityanimid_invalid,
};
enum EntityState enum EntityState
{ {
entitystate_idle, entitystate_idle,
@ -46,16 +35,6 @@ enum EntityState
entitystate_invalid, entitystate_invalid,
}; };
typedef struct EntityAnim
{
v4 *rect;
i32 numRects;
i32 currRectIndex;
f32 duration;
f32 currDuration;
} EntityAnim;
enum EntityAttack enum EntityAttack
{ {
entityattack_tackle, entityattack_tackle,
@ -77,6 +56,13 @@ typedef struct EntityStats
i32 queuedAttack; i32 queuedAttack;
} EntityStats; } EntityStats;
typedef struct EntityAnim_
{
Animation *anim;
i32 currFrame;
f32 currDuration;
} EntityAnim_;
typedef struct Entity typedef struct Entity
{ {
v2 pos; // Position v2 pos; // Position
@ -92,7 +78,7 @@ typedef struct Entity
b32 collides; b32 collides;
// TODO(doyle): String based access // TODO(doyle): String based access
EntityAnim anim[16]; EntityAnim_ anim[16];
i32 currAnimId; i32 currAnimId;
u32 currAnimCyclesCompleted; u32 currAnimCyclesCompleted;