Add additional entity struct data

Attempting to add child weapon data to an entity has revealed the need for an
entity origin to which children start transformations from. Worth exploring
scene graphs in the future.
This commit is contained in:
Doyle Thai 2016-09-15 03:20:13 +10:00
parent 5c4f493979
commit 7cb13b3cf8
8 changed files with 145 additions and 90 deletions

View File

@ -82,16 +82,16 @@ INTERNAL HashTableEntry *const getEntryFromHash(HashTable *const table,
* Texture Operations
*********************************
*/
INTERNAL Rect *getFreeAtlasSubTexSlot(TexAtlas *const atlas,
MemoryArena *const arena,
const char *const key)
INTERNAL SubTexture *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;
entry->data = PLATFORM_MEM_ALLOC(arena, 1, SubTexture);
SubTexture *result = CAST(SubTexture *)entry->data;
return result;
}
else
@ -100,15 +100,15 @@ INTERNAL Rect *getFreeAtlasSubTexSlot(TexAtlas *const atlas,
}
}
const Rect asset_getAtlasSubTex(TexAtlas *const atlas, const char *const key)
const SubTexture asset_getAtlasSubTex(TexAtlas *const atlas, const char *const key)
{
HashTableEntry *entry = getEntryFromHash(&atlas->subTex, key);
Rect result = {0};
SubTexture result = {0};
if (entry)
{
result = *(CAST(Rect *) entry->data);
result = *(CAST(SubTexture *) entry->data);
return result;
}
@ -638,7 +638,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
XmlAttribute *subTexAttrib = &atlasChildNode->attribute;
char *key = NULL;
Rect subTex = {0};
SubTexture subTex = {0};
while (subTexAttrib)
{
@ -659,7 +659,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
subTex.pos.x = CAST(f32) intValue;
subTex.rect.pos.x = CAST(f32) intValue;
}
else if (common_strcmp(subTexAttrib->name, "y") ==
0)
@ -668,7 +668,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
subTex.pos.y = CAST(f32) intValue;
subTex.rect.pos.y = CAST(f32) intValue;
}
else if (common_strcmp(subTexAttrib->name,
"width") == 0)
@ -677,7 +677,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
subTex.size.w = CAST(f32) intValue;
subTex.rect.size.w = CAST(f32) intValue;
}
else if (common_strcmp(subTexAttrib->name,
"height") == 0)
@ -686,7 +686,25 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
subTex.size.h = CAST(f32) intValue;
subTex.rect.size.h = CAST(f32) intValue;
}
else if (common_strcmp(subTexAttrib->name,
"hand_offset_x") == 0)
{
char *value = subTexAttrib->value;
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
subTex.offset.x = CAST(f32) intValue;
}
else if (common_strcmp(subTexAttrib->name,
"hand_offset_y") == 0)
{
char *value = subTexAttrib->value;
i32 valueLen = common_strlen(value);
i32 intValue = common_atoi(value, valueLen);
subTex.offset.y = CAST(f32) intValue;
}
else
{
@ -701,13 +719,14 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena *arena,
// 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
subTex.pos.y = 1024 - subTex.pos.y;
subTex.pos.y -= subTex.size.h;
subTex.rect.pos.y = 1024 - subTex.rect.pos.y;
subTex.rect.pos.y -= subTex.rect.size.h;
subTex.offset.y = subTex.rect.size.h - subTex.offset.y;
#ifdef DENGINE_DEBUG
ASSERT(key);
#endif
Rect *subTexInHash =
SubTexture *subTexInHash =
getFreeAtlasSubTexSlot(atlas, arena, key);
*subTexInHash = subTex;
}
@ -1126,8 +1145,8 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
// all ascii characters, charToEncode represents the character
// 1:1
const char key[2] = {charToEncode, 0};
Rect *subTex = getFreeAtlasSubTexSlot(fontAtlas, arena, key);
*subTex = CAST(Rect){origin, font->maxSize};
SubTexture *subTex = getFreeAtlasSubTexSlot(fontAtlas, arena, key);
subTex->rect = CAST(Rect){origin, font->maxSize};
charToEncode++;
}

View File

@ -377,7 +377,7 @@ void debug_drawUi(GameState *state, f32 dt)
if (debugString)
{
v2 strPos = v2_add(entity->pos, entity->hitboxSize);
v2 strPos = v2_add(entity->pos, entity->hitbox);
i32 indexOfLowerAInMetrics = 'a' - CAST(i32) font->codepointRange.x;
strPos.y += font->charMetrics[indexOfLowerAInMetrics].offset.y;

View File

@ -3,6 +3,16 @@
#include "Dengine/Platform.h"
#include "Dengine/WorldTraveller.h"
SubTexture entity_getActiveSubTexture(Entity *const entity)
{
EntityAnim *entityAnim = &entity->animList[entity->currAnimId];
Animation *anim = entityAnim->anim;
char *frameName = anim->frameList[entityAnim->currFrame];
SubTexture result = asset_getAtlasSubTex(anim->atlas, frameName);
return result;
}
void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity,
const char *const animName)
{
@ -71,9 +81,9 @@ void entity_updateAnim(EventQueue *eventQueue, Entity *const entity,
case entitytype_weapon:
case entitytype_projectile:
char *frameName = anim->frameList[currEntityAnim->currFrame];
Rect texRect =
SubTexture texRect =
asset_getAtlasSubTex(anim->atlas, frameName);
entity->renderSize = texRect.size;
entity->size = v2_scale(texRect.rect.size, entity->scale);
default:
break;
}
@ -114,8 +124,8 @@ Entity *const entity_add(MemoryArena *const arena, World *const world,
Entity entity = {0};
entity.id = world->uniqueIdAccumulator++;
entity.pos = pos;
entity.hitboxSize = size;
entity.renderSize = size;
entity.size = size;
entity.hitbox = size;
entity.scale = scale;
entity.type = type;
entity.direction = direction;

View File

@ -210,13 +210,13 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera,
pos.x += charMetric.advance;
/* Get texture out */
Rect charTexRect =
SubTexture charTexRect =
asset_getAtlasSubTex(font->atlas, &CAST(char)codepoint);
v4 deprecatedTexRect = {0};
deprecatedTexRect.vec2[0] = charTexRect.pos;
deprecatedTexRect.vec2[0] = charTexRect.rect.pos;
deprecatedTexRect.vec2[1] =
v2_add(charTexRect.pos, charTexRect.size);
v2_add(charTexRect.rect.pos, charTexRect.rect.size);
flipTexCoord(&deprecatedTexRect, FALSE, TRUE);
@ -244,7 +244,7 @@ void renderer_entity(Renderer *renderer, Rect camera, Entity *entity,
// NOTE(doyle): Pos + Size since the origin of an entity is it's bottom left
// corner. Add the two together so that the clipping point is the far right
// side of the entity
v2 rightAlignedP = v2_add(entity->pos, entity->hitboxSize);
v2 rightAlignedP = v2_add(entity->pos, entity->hitbox);
v2 leftAlignedP = entity->pos;
if (math_pointInRect(camera, leftAlignedP) ||
math_pointInRect(camera, rightAlignedP))
@ -252,14 +252,16 @@ void renderer_entity(Renderer *renderer, Rect camera, Entity *entity,
EntityAnim *entityAnim = &entity->animList[entity->currAnimId];
Animation *anim = entityAnim->anim;
char *frameName = anim->frameList[entityAnim->currFrame];
Rect animRect = asset_getAtlasSubTex(anim->atlas, frameName);
SubTexture animRect = asset_getAtlasSubTex(anim->atlas, frameName);
// TODO(doyle): Switch to rect
v4 animTexRect = {0};
animTexRect.vec2[0] = animRect.pos;
animTexRect.vec2[1] = v2_add(animRect.pos, animRect.size);
animTexRect.vec2[0] = animRect.rect.pos;
animTexRect.vec2[1] = v2_add(animRect.rect.pos, animRect.rect.size);
if (entity->direction == direction_east)
flipTexCoord(&animTexRect, entity->flipX, entity->flipY);
if (entity->direction == direction_east)
{
flipTexCoord(&animTexRect, TRUE, FALSE);
}
@ -270,9 +272,8 @@ void renderer_entity(Renderer *renderer, Rect camera, Entity *entity,
updateBufferObject(renderer, &entityQuad, 1);
v2 posInCameraSpace = v2_sub(entity->pos, camera.pos);
// TODO(doyle): Scale temporarily
renderObject(renderer, posInCameraSpace,
v2_scale(entity->renderSize, entity->scale), pivotPoint,
entity->size, pivotPoint,
entity->rotation + rotate, color, entity->tex);
}
}

View File

@ -301,7 +301,10 @@ INTERNAL void assetInit(GameState *state)
claudeAtlas, claudeRipperBlast, numRects,
duration);
char *claudeRipperBlastVfx[9] = {
char *claudeRipperBlastVfx[12] = {
"ClaudeSprite_Attack_RipperBlast_Vfx_01",
"ClaudeSprite_Attack_RipperBlast_Vfx_02",
"ClaudeSprite_Attack_RipperBlast_Vfx_03",
"ClaudeSprite_Attack_RipperBlast_Vfx_04",
"ClaudeSprite_Attack_RipperBlast_Vfx_05",
"ClaudeSprite_Attack_RipperBlast_Vfx_06",
@ -601,19 +604,17 @@ INTERNAL void entityInit(GameState *state, v2 windowSize)
dir = direction_east;
tex = asset_getTex(assetManager, "ClaudeSprite.png");
collides = TRUE;
Entity *hero =
entity_add(arena, world, pos, size, scale, type, dir, tex, collides);
#if 0
Entity *heroWeapon =
entity_add(arena, world, pos, V2(0, 0), entitytype_weapon,
hero->direction, hero->tex, FALSE);
heroWeapon->rotation = -90.0f;
heroWeapon->invisible = TRUE;
entity_add(arena, world, pos, V2(20, 20), scale, entitytype_weapon,
dir, tex, FALSE);
heroWeapon->flipX = TRUE;
entity_addAnim(assetManager, heroWeapon, "claudeSword");
hero->stats->weapon = heroWeapon;
#endif
hero->numAudioRenderers = 4;
hero->audioRenderer =
@ -622,8 +623,8 @@ INTERNAL void entityInit(GameState *state, v2 windowSize)
for (i32 i = 0; i < hero->numAudioRenderers; i++)
hero->audioRenderer[i].sourceIndex = AUDIO_SOURCE_UNASSIGNED;
world->heroId = hero->id;
world->cameraFollowingId = hero->id;
world->heroId = hero->id;
world->cameraFollowingId = hero->id;
/* Populate hero animation references */
entity_addAnim(assetManager, hero, "claudeIdle");
@ -639,11 +640,11 @@ INTERNAL void entityInit(GameState *state, v2 windowSize)
entity_addAnim(assetManager, hero, "claudeRipperBlast");
entity_addAnim(assetManager, hero, "claudeAirSlash");
entity_setActiveAnim(eventQueue, hero, "claudeIdle");
entity_setActiveAnim(eventQueue, hero, "claudeBattleIdle");
/* Create a NPC */
pos = V2(hero->pos.x * 3, CAST(f32) state->tileSize);
size = hero->hitboxSize;
size = hero->hitbox;
type = entitytype_npc;
dir = direction_null;
tex = hero->tex;
@ -855,7 +856,7 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
INTERNAL inline v4 getEntityScreenRect(Entity entity)
{
v4 result = math_getRect(entity.pos, entity.hitboxSize);
v4 result = math_getRect(entity.pos, entity.hitbox);
return result;
}
@ -1162,12 +1163,6 @@ INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena,
case entityattack_claudeAttack:
{
entity_setActiveAnim(eventQueue, attacker, "claudeAttack");
if (attacker->stats->weapon)
{
attacker->stats->weapon->invisible = FALSE;
entity_setActiveAnim(eventQueue, attacker->stats->weapon,
"claudeAttackSlashLeft");
}
if (attacker->direction == direction_east)
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
else
@ -1178,12 +1173,6 @@ INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena,
case entityattack_claudeAttackUp:
{
entity_setActiveAnim(eventQueue, attacker, "claudeAttackUp");
if (attacker->stats->weapon)
{
attacker->stats->weapon->invisible = FALSE;
entity_setActiveAnim(eventQueue, attacker->stats->weapon,
"claudeAttackSlashLeft");
}
if (attacker->direction == direction_east)
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
else
@ -1194,12 +1183,6 @@ INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena,
case entityattack_claudeAttackDown:
{
entity_setActiveAnim(eventQueue, attacker, "claudeAttackDown");
if (attacker->stats->weapon)
{
attacker->stats->weapon->invisible = FALSE;
entity_setActiveAnim(eventQueue, attacker->stats->weapon,
"claudeAttackSlashLeft");
}
if (attacker->direction == direction_east)
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
else
@ -1330,12 +1313,6 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue,
case entityattack_claudeAttackUp:
case entityattack_claudeAttackDown:
// TODO(doyle): Move animation offsets out and into animation type
if (attacker->stats->weapon)
{
attacker->stats->weapon->invisible = TRUE;
}
if (attacker->direction == direction_east)
attacker->dPos.x -= (1.0f * METERS_TO_PIXEL);
else
@ -1515,7 +1492,7 @@ INTERNAL b32 checkCollision(Entity *a, Entity *b)
b32 result = FALSE;
if (a->collides && b->collides && a->collidesWith[b->type])
{
Rect aRect = {a->pos, a->hitboxSize};
Rect aRect = {a->pos, a->hitbox};
v2 aTopLeftP =
getPosRelativeToRect(aRect, V2(0, 0), rectbaseline_topLeft);
@ -1526,7 +1503,7 @@ INTERNAL b32 checkCollision(Entity *a, Entity *b)
v2 aBottomRightP =
getPosRelativeToRect(aRect, V2(0, 0), rectbaseline_bottomRight);
Rect bRect = {b->pos, b->hitboxSize};
Rect bRect = {b->pos, b->hitbox};
if (math_pointInRect(bRect, aTopLeftP) ||
math_pointInRect(bRect, aTopRightP) ||
@ -1730,7 +1707,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
V2((entity->pos.x - (0.5f * state->renderer.size.w)), (0.0f));
// NOTE(doyle): Account for the hero's origin being the bottom left
offsetFromEntityToCameraOrigin.x += (entity->hitboxSize.x * 0.5f);
offsetFromEntityToCameraOrigin.x += (entity->hitbox.w * 0.5f);
world->cameraPos = offsetFromEntityToCameraOrigin;
}
@ -2017,11 +1994,38 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
if (stats->weapon)
{
stats->weapon->pos = entity->pos;
Entity *weapon = stats->weapon;
weapon->pos = entity->pos;
// TODO(doyle): Add concept of entity origin and make all
// transform start from that origin point
if (entity->direction == direction_east)
{
weapon->pos.x += entity->size.w;
weapon->pos.x -= weapon->size.w;
}
weapon->direction = entity->direction;
SubTexture entitySubTexture =
entity_getActiveSubTexture(entity);
weapon->pos.y += entitySubTexture.offset.y;
if (weapon->direction == direction_west)
{
weapon->pos.x += entitySubTexture.offset.x;
// TODO(doyle): Typedef rotation to degrees for type safety
weapon->rotation = DEGREES_TO_RADIANS(60.0f);
}
else
{
weapon->pos.x -= entitySubTexture.offset.x;
weapon->rotation = DEGREES_TO_RADIANS(-60.0f);
}
}
if (entity->state == entitystate_battle)
{
if (stats->actionTimer > 0)
stats->actionTimer -= dt * stats->actionSpdMul;
@ -2067,9 +2071,19 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
{
entity_updateAnim(eventQueue, entity, dt);
/* Calculate region to render */
renderer_entity(renderer, camera, entity,
v2_scale(entity->renderSize, 0.5f), 0,
V4(1, 1, 1, 1));
if (entity->type == entitytype_weapon)
{
renderer_entity(renderer, camera, entity,
v2_scale(entity->size, 0.5f), 0,
V4(1, 1, 1, 1.0f));
}
else
{
renderer_entity(renderer, camera, entity,
v2_scale(entity->size, 0.5f), 0,
V4(1, 1, 1, 1));
}
}
}
@ -2372,18 +2386,19 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
/* Draw hero avatar */
TexAtlas *heroAtlas =
asset_getTexAtlas(assetManager, "ClaudeSprite.png");
Rect heroAvatarRect =
SubTexture heroAvatarRect =
asset_getAtlasSubTex(heroAtlas, "ClaudeSprite_Avatar_01");
v2 heroAvatarP =
V2(10.0f, (renderer->size.h * 0.5f) - (0.5f * heroAvatarRect.size.h));
v2 heroAvatarP = V2(10.0f, (renderer->size.h * 0.5f) -
(0.5f * heroAvatarRect.rect.size.h));
// TODO(doyle): Use rect in rendering not V4
v4 heroAvatarTexRect = {0};
heroAvatarTexRect.vec2[0] = heroAvatarRect.pos;
heroAvatarTexRect.vec2[1] = v2_add(heroAvatarRect.pos, heroAvatarRect.size);
heroAvatarTexRect.vec2[0] = heroAvatarRect.rect.pos;
heroAvatarTexRect.vec2[1] =
v2_add(heroAvatarRect.rect.pos, heroAvatarRect.rect.size);
RenderTex heroRenderTex = {hero->tex, heroAvatarTexRect};
renderer_staticRect(renderer, heroAvatarP, heroAvatarRect.size, V2(0, 0), 0,
renderer_staticRect(renderer, heroAvatarP, heroAvatarRect.rect.size, V2(0, 0), 0,
heroRenderTex, V4(1, 1, 1, 1));
char heroAvatarStr[20];
@ -2392,7 +2407,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
f32 strLenInPixels =
CAST(f32)(font->maxSize.w * common_strlen(heroAvatarStr));
v2 strPos =
V2(heroAvatarP.x, heroAvatarP.y - (0.5f * heroAvatarRect.size.h));
V2(heroAvatarP.x, heroAvatarP.y - (0.5f * heroAvatarRect.rect.size.h));
renderer_staticString(&state->renderer, &state->arena, font, heroAvatarStr,
strPos, V2(0, 0), 0, V4(0, 0, 1, 1));
@ -2410,9 +2425,8 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
{
v2 difference = v2_sub(entity->pos, hero->pos);
f32 angle = math_atan2f(difference.y, difference.x);
f32 angleDegrees = RADIANS_TO_DEGREES(angle);
v2 heroCenter = v2_add(hero->pos, v2_scale(hero->hitboxSize, 0.5f));
v2 heroCenter = v2_add(hero->pos, v2_scale(hero->hitbox, 0.5f));
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
f32 distance = v2_magnitude(hero->pos, entity->pos);
renderer_rect(&state->renderer, camera, heroCenter,

View File

@ -28,7 +28,8 @@ typedef struct AssetManager
* Texture Operations
*********************************
*/
const Rect asset_getAtlasSubTex(TexAtlas *const atlas, const char *const key);
const SubTexture asset_getAtlasSubTex(TexAtlas *const atlas,
const char *const key);
Texture *asset_getTex(AssetManager *const assetManager, const char *const key);
TexAtlas *asset_getFreeTexAtlasSlot(AssetManager *const assetManager,
MemoryArena *arena, const char *const key,

View File

@ -94,6 +94,13 @@ typedef struct AudioVorbis
* Texture Assets
*********************************
*/
typedef struct SubTexture
{
Rect rect;
v2 offset;
} SubTexture;
typedef struct TexAtlas
{
Texture *tex;

View File

@ -3,12 +3,11 @@
#include "Dengine/Common.h"
#include "Dengine/Math.h"
#include "Dengine/Assets.h"
typedef struct AssetManager AssetManager;
typedef struct AudioRenderer AudioRenderer;
typedef struct MemoryArena MemoryArena;
typedef struct Texture Texture;
typedef struct Animation Animation;
typedef struct World World;
typedef struct EventQueue EventQueue;
@ -95,8 +94,9 @@ struct Entity
v2 pos; // Position
v2 dPos; // Velocity
v2 hitboxSize;
v2 renderSize;
v2 hitbox;
v2 size;
f32 scale;
f32 rotation;
@ -107,6 +107,8 @@ struct Entity
enum Direction direction;
Texture *tex;
b32 flipX;
b32 flipY;
// TODO(doyle): Two collision flags, we want certain entities to collide
// with certain types of entities only (i.e. projectile from hero to enemy,
@ -126,6 +128,7 @@ struct Entity
i32 numAudioRenderers;
};
SubTexture entity_getActiveSubTexture(Entity *const entity);
void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity,
const char *const animName);
void entity_updateAnim(EventQueue *eventQueue, Entity *const entity,