Switch developing game to asteroids, start project

Switch to something of smaller scope in aim to be able to finish a project from
start to finish. Also allow refreshed eyes to review the existing API and see
what can be improved on after taking a short break from the project.
This commit is contained in:
Doyle Thai 2016-11-09 22:36:41 +11:00
parent ed6b961b61
commit 4795fe1cb1
19 changed files with 358 additions and 3160 deletions

View File

@ -121,18 +121,17 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\AssetManager.c" />
<ClCompile Include="src\Asteroid.c" />
<ClCompile Include="src\Audio.c" />
<ClCompile Include="src\Common.c" />
<ClCompile Include="src\Debug.c" />
<ClCompile Include="src\dengine.c" />
<ClCompile Include="src\Entity.c" />
<ClCompile Include="src\MemoryArena.c" />
<ClCompile Include="src\Platform.c" />
<ClCompile Include="src\Renderer.c" />
<ClCompile Include="src\Shader.c" />
<ClCompile Include="src\String.c" />
<ClCompile Include="src\UserInterface.c" />
<ClCompile Include="src\WorldTraveller.c" />
<ClCompile Include="src\Texture.c" />
</ItemGroup>
<ItemGroup>
@ -143,14 +142,15 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\include\Dengine\Assets.h" />
<ClInclude Include="src\include\Dengine\Asteroid.h" />
<ClInclude Include="src\include\Dengine\Audio.h" />
<ClInclude Include="src\include\Dengine\Debug.h" />
<ClInclude Include="src\include\Dengine\Entity.h" />
<ClInclude Include="src\include\Dengine\MemoryArena.h" />
<ClInclude Include="src\include\Dengine\Platform.h" />
<ClInclude Include="src\include\Dengine\AssetManager.h" />
<ClInclude Include="src\include\Dengine\Common.h" />
<ClInclude Include="src\include\Breakout\Game.h" />
<ClInclude Include="src\include\Dengine\Entity.h" />
<ClInclude Include="src\include\Dengine\Math.h" />
<ClInclude Include="src\include\Dengine\OpenGL.h" />
<ClInclude Include="src\include\Dengine\Renderer.h" />

View File

@ -15,9 +15,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\WorldTraveller.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Texture.c">
<Filter>Source Files</Filter>
</ClCompile>
@ -42,9 +39,6 @@
<ClCompile Include="src\Audio.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Entity.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\dengine.c">
<Filter>Source Files</Filter>
</ClCompile>
@ -57,6 +51,9 @@
<ClCompile Include="src\MemoryArena.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Asteroid.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="data\shaders\default.vert.glsl" />
@ -86,9 +83,6 @@
<ClInclude Include="src\include\Dengine\Renderer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\include\Dengine\Entity.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\include\Dengine\Math.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -116,5 +110,11 @@
<ClInclude Include="src\include\Dengine\String.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\include\Dengine\Asteroid.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\include\Dengine\Entity.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

241
src/Asteroid.c Normal file
View File

@ -0,0 +1,241 @@
#include "Dengine/Asteroid.h"
#include "Dengine/Debug.h"
void initAssetManager(GameState *state)
{
AssetManager *assetManager = &state->assetManager;
MemoryArena_ *arena = &state->persistentArena;
i32 audioEntries = 32;
assetManager->audio.size = audioEntries;
assetManager->audio.entries =
memory_pushBytes(arena, audioEntries * sizeof(HashTableEntry));
i32 texAtlasEntries = 8;
assetManager->texAtlas.size = texAtlasEntries;
assetManager->texAtlas.entries =
memory_pushBytes(arena, texAtlasEntries * sizeof(HashTableEntry));
i32 texEntries = 32;
assetManager->textures.size = texEntries;
assetManager->textures.entries =
memory_pushBytes(arena, texEntries * sizeof(HashTableEntry));
i32 animEntries = 1024;
assetManager->anims.size = animEntries;
assetManager->anims.entries =
memory_pushBytes(arena, animEntries * sizeof(HashTableEntry));
/* Create empty 1x1 4bpp black texture */
u32 bitmap = (0xFF << 24) | (0xFF << 16) | (0xFF << 8) | (0xFF << 0);
Texture *tex = asset_getFreeTexSlot(assetManager, arena, "nullTex");
*tex = texture_gen(1, 1, 4, CAST(u8 *)(&bitmap));
/* Load shaders */
asset_loadShaderFiles(assetManager, arena, "data/shaders/sprite.vert.glsl",
"data/shaders/sprite.frag.glsl",
shaderlist_sprite);
i32 result =
asset_loadTTFont(assetManager, arena, "C:/Windows/Fonts/Arialbd.ttf");
if (result) {
ASSERT(TRUE);
}
}
void initRenderer(GameState *state, v2 windowSize) {
AssetManager *assetManager = &state->assetManager;
Renderer *renderer = &state->renderer;
renderer->size = windowSize;
// NOTE(doyle): Value to map a screen coordinate to NDC coordinate
renderer->vertexNdcFactor =
V2(1.0f / renderer->size.w, 1.0f / renderer->size.h);
renderer->shader = asset_getShader(assetManager, shaderlist_sprite);
shader_use(renderer->shader);
const mat4 projection =
mat4_ortho(0.0f, renderer->size.w, 0.0f, renderer->size.h, 0.0f, 1.0f);
shader_uniformSetMat4fv(renderer->shader, "projection", projection);
GL_CHECK_ERROR();
/* Create buffers */
glGenVertexArrays(1, &renderer->vao);
glGenBuffers(1, &renderer->vbo);
GL_CHECK_ERROR();
/* Bind buffers */
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo);
glBindVertexArray(renderer->vao);
/* Configure VAO */
u32 numVertexElements = 4;
u32 stride = sizeof(Vertex);
glVertexAttribPointer(0, numVertexElements, GL_FLOAT, GL_FALSE, stride,
(GLvoid *)0);
glEnableVertexAttribArray(0);
GL_CHECK_ERROR();
/* Unbind */
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
GL_CHECK_ERROR();
// TODO(doyle): Lazy allocate render group capacity
renderer->groupCapacity = 4096;
for (i32 i = 0; i < ARRAY_COUNT(renderer->groups); i++)
{
renderer->groups[i].vertexList = memory_pushBytes(
&state->persistentArena, renderer->groupCapacity * sizeof(Vertex));
}
}
enum ReadKeyType
{
readkeytype_oneShot,
readkeytype_delayedRepeat,
readkeytype_repeat,
readkeytype_count,
};
#define KEY_DELAY_NONE 0.0f
INTERNAL b32 getKeyStatus(KeyState *key, enum ReadKeyType readType,
f32 delayInterval, f32 dt)
{
if (!key->endedDown) return FALSE;
switch(readType)
{
case readkeytype_oneShot:
{
if (key->newHalfTransitionCount > key->oldHalfTransitionCount)
return TRUE;
break;
}
case readkeytype_repeat:
case readkeytype_delayedRepeat:
{
if (key->newHalfTransitionCount > key->oldHalfTransitionCount)
{
if (readType == readkeytype_delayedRepeat)
{
// TODO(doyle): Let user set arbitrary delay after initial input
key->delayInterval = 2 * delayInterval;
}
else
{
key->delayInterval = delayInterval;
}
return TRUE;
}
else if (key->delayInterval <= 0.0f)
{
key->delayInterval = delayInterval;
return TRUE;
}
else
{
key->delayInterval -= dt;
}
break;
}
default:
#ifdef DENGINE_DEBUG
DEBUG_LOG("getKeyStatus() error: Invalid ReadKeyType enum");
ASSERT(INVALID_CODE_PATH);
#endif
break;
}
return FALSE;
}
void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
v2 windowSize, f32 dt)
{
if (!state->init) {
memory_arenaInit(&state->persistentArena, memory->persistent,
memory->persistentSize);
memory_arenaInit(&state->transientArena, memory->transient,
memory->transientSize);
initAssetManager(state);
initRenderer(state, windowSize);
{ // Init ship entity
Entity *ship = &state->entityList[state->entityIndex++];
ship->id = 0;
ship->pos = V2(100.0f, 100.0f);
ship->hitbox = V2(100.0f, 100.0f);
ship->size = V2(100.0f, 100.0f);
ship->scale = 1;
ship->type = entitytype_ship;
ship->direction = direction_null;
ship->tex = asset_getTex(&state->assetManager, "nullTex");
ship->collides = FALSE;
}
state->camera.pos = V2(0, 0);
state->camera.size = state->renderer.size;
state->init = TRUE;
}
{
KeyState *keys = state->input.keys;
for (enum KeyCode code = 0; code < keycode_count; code++)
{
KeyState *keyState = &keys[code];
u32 halfTransitionCount = keyState->newHalfTransitionCount -
keyState->oldHalfTransitionCount;
if (halfTransitionCount > 0)
{
b32 transitionCountIsOdd = ((halfTransitionCount & 1) == 1);
if (transitionCountIsOdd)
{
if (keyState->endedDown) keyState->endedDown = FALSE;
else keyState->endedDown = TRUE;
}
keyState->oldHalfTransitionCount =
keyState->newHalfTransitionCount;
}
}
}
for (i32 i = 0; i < state->entityIndex; i++)
{
Entity *entity = &state->entityList[i];
ASSERT(entity->type != entitytype_invalid);
if (entity->type == entitytype_ship) {
{ // Parse input
if (getKeyStatus(&state->input.keys[keycode_up],
readkeytype_repeat, 0.0f, dt))
{
entity->pos.y += 10.0f;
}
if (getKeyStatus(&state->input.keys[keycode_down],
readkeytype_repeat, 0.0f, dt))
{
entity->pos.y -= 10.0f;
}
}
}
renderer_entity(&state->renderer, state->camera, entity, V2(0, 0), 0,
V4(1.0f, 1.0f, 1.0f, 1.0f));
}
renderer_renderGroups(&state->renderer);
}

View File

@ -1,10 +1,9 @@
#include "Dengine/Debug.h"
#include "Dengine/AssetManager.h"
#include "Dengine/Entity.h"
#include "Dengine/MemoryArena.h"
#include "Dengine/Platform.h"
#include "Dengine/Renderer.h"
#include "Dengine/WorldTraveller.h"
#include "Dengine/Asteroid.h"
typedef struct DebugState
{
@ -29,52 +28,6 @@ typedef struct DebugState
GLOBAL_VAR DebugState GLOBAL_debug;
inline char *debug_entitystate_string(i32 val)
{
char *string;
switch(val)
{
case entitystate_idle:
string = "EntityState_Idle";
break;
case entitystate_battle:
string = "EntityState_Battle";
break;
case entitystate_attack:
string = "EntityState_Attack";
break;
case entitystate_dead:
string = "EntityState_Dead";
break;
case entitystate_count:
string = "EntityState_Count (Error!)";
break;
case entitystate_invalid:
string = "EntityState_Invalid (Error!)";
break;
default:
string = "EntityState Unknown (NOT DEFINED)";
}
return string;
}
inline char *debug_entityattack_string(i32 val)
{
char *string;
switch(val)
{
case entityattack_count:
string = "EntityAttack_Count (Error!)";
break;
case entityattack_invalid:
string = "EntityAttack_Invalid";
break;
default:
string = "EntityAttack Unknown (NOT DEFINED)";
}
return string;
}
void debug_init(MemoryArena_ *arena, v2 windowSize, Font font)
{
GLOBAL_debug.font = font;
@ -317,238 +270,5 @@ INTERNAL void renderConsole(Renderer *renderer, MemoryArena_ *arena)
void debug_drawUi(GameState *state, f32 dt)
{
AssetManager *assetManager = &state->assetManager;
Renderer *renderer = &state->renderer;
World *const world = &state->world[state->currWorldIndex];
Entity *hero = &world->entities[entity_getIndex(world, world->heroId)];
MemoryArena_ *transientArena = &state->transientArena;
// TODO(doyle): Dumb copy function from game so we don't expose api
Rect camera = {world->cameraPos, renderer->size};
// NOTE(doyle): Lock camera if it passes the bounds of the world
if (camera.pos.x <= world->bounds.x)
camera.pos.x = world->bounds.x;
// TODO(doyle): Allow Y panning when we need it
f32 cameraTopBoundInPixels = camera.pos.y + camera.size.h;
if (cameraTopBoundInPixels >= world->bounds.w)
camera.pos.y = (world->bounds.w - camera.size.h);
f32 cameraRightBoundInPixels = camera.pos.x + camera.size.w;
if (cameraRightBoundInPixels >= world->bounds.z)
camera.pos.x = (world->bounds.z - camera.size.w);
if (camera.pos.y <= world->bounds.y) camera.pos.y = world->bounds.y;
Font *font = &GLOBAL_debug.font;
if (world->numEntitiesInBattle > 0)
{
v4 color = V4(1.0f, 0, 0, 1);
char *battleStr = "IN-BATTLE RANGE";
f32 strLenInPixels =
CAST(f32)(font->maxSize.w * common_strlen(battleStr));
v2 strPos = V2((renderer->size.w * 0.5f) - (strLenInPixels * 0.5f),
renderer->size.h - 300.0f);
renderer_staticString(&state->renderer, transientArena, font, battleStr,
strPos, V2(0, 0), 0, color);
}
for (i32 i = 0; i < world->freeEntityIndex; i++)
{
Entity *const entity = &world->entities[i];
if (entity->state == entitystate_dead) continue;
/* Render debug markers on entities */
v4 color = V4(1, 1, 1, 1);
char *debugString = NULL;
switch (entity->type)
{
case entitytype_mob:
color = V4(1, 0, 0, 1);
debugString = "MOB";
break;
case entitytype_hero:
color = V4(0, 0, 1.0f, 1);
debugString = "HERO";
break;
case entitytype_npc:
color = V4(0, 1.0f, 0, 1);
debugString = "NPC";
break;
default:
break;
}
if (debugString)
{
v2 strPos = v2_add(entity->pos, entity->hitbox);
i32 indexOfLowerAInMetrics = 'a' - CAST(i32) font->codepointRange.x;
strPos.y += font->charMetrics[indexOfLowerAInMetrics].offset.y;
renderer_string(&state->renderer, transientArena, camera, font,
debugString, strPos, V2(0, 0), 0, color);
f32 stringLineGap = 1.1f * font->verticalSpacing;
strPos.y -= GLOBAL_debug.stringLineGap;
char entityPosStr[128];
snprintf(entityPosStr, ARRAY_COUNT(entityPosStr), "%06.2f, %06.2f",
entity->pos.x, entity->pos.y);
renderer_string(&state->renderer, transientArena, camera, font,
entityPosStr, strPos, V2(0, 0), 0, color);
strPos.y -= GLOBAL_debug.stringLineGap;
char entityIDStr[32];
snprintf(entityIDStr, ARRAY_COUNT(entityIDStr), "ID: %4d/%d", entity->id,
world->uniqueIdAccumulator-1);
renderer_string(&state->renderer, transientArena, camera, font,
entityIDStr, strPos, V2(0, 0), 0, color);
if (entity->stats)
{
strPos.y -= GLOBAL_debug.stringLineGap;
char entityHealth[32];
snprintf(entityHealth, ARRAY_COUNT(entityHealth), "HP: %3.0f/%3.0f",
entity->stats->health, entity->stats->maxHealth);
renderer_string(&state->renderer, transientArena, camera,
font, entityHealth, strPos, V2(0, 0), 0, color);
strPos.y -= GLOBAL_debug.stringLineGap;
char entityTimer[32];
snprintf(entityTimer, ARRAY_COUNT(entityTimer), "ATB: %3.0f/%3.0f",
entity->stats->actionTimer, entity->stats->actionRate);
renderer_string(&state->renderer, transientArena, camera,
font, entityTimer, strPos, V2(0, 0), 0, color);
strPos.y -= GLOBAL_debug.stringLineGap;
char entityIdTarget[32];
snprintf(entityIdTarget, ARRAY_COUNT(entityIdTarget),
"Targetting ID: %d", entity->stats->entityIdToAttack);
renderer_string(&state->renderer, transientArena, camera,
font, entityIdTarget, strPos, V2(0, 0), 0, color);
}
strPos.y -= GLOBAL_debug.stringLineGap;
char *entityStateStr = debug_entitystate_string(entity->state);
renderer_string(&state->renderer, transientArena, camera, font,
entityStateStr, strPos, V2(0, 0), 0, color);
if (entity->audioRenderer)
{
strPos.y -= GLOBAL_debug.stringLineGap;
char entityAudioSourceIndex[32];
snprintf(entityAudioSourceIndex,
ARRAY_COUNT(entityAudioSourceIndex),
"AudioSource Index: %d",
entity->audioRenderer->sourceIndex);
renderer_string(&state->renderer, transientArena, camera,
font, entityAudioSourceIndex, strPos, V2(0, 0),
0, color);
}
}
}
/* Render debug info stack */
DEBUG_PUSH_STRING("== Controls ==");
DEBUG_PUSH_STRING(" [: Spawn a mob");
DEBUG_PUSH_STRING("<TAB>: Switch UI element");
DEBUG_PUSH_STRING("<ESC>: Close program");
DEBUG_PUSH_STRING("");
DEBUG_PUSH_STRING("== Config == ");
DEBUG_PUSH_VAR("Toggle World Audio: %d", state->config.playWorldAudio,
"i32");
DEBUG_PUSH_VAR("Toggle Debug Display: %d", state->config.showDebugDisplay,
"i32");
DEBUG_PUSH_STRING("");
DEBUG_PUSH_STRING("== Hero Properties == ");
DEBUG_PUSH_VAR("Hero Pos: %06.2f, %06.2f", hero->pos, "v2");
DEBUG_PUSH_VAR("Hero dPos: %06.2f, %06.2f", hero->dPos, "v2");
DEBUG_PUSH_VAR("Hero Busy Duration: %05.3f", hero->stats->busyDuration, "f32");
char *heroStateString = debug_entitystate_string(hero->state);
DEBUG_PUSH_VAR("Hero State: %s", *heroStateString, "char");
char *heroQueuedAttackStr =
debug_entityattack_string(hero->stats->queuedAttack);
DEBUG_PUSH_VAR("Hero QueuedAttack: %s", *heroQueuedAttackStr, "char");
DEBUG_PUSH_STRING("");
DEBUG_PUSH_STRING("== State Properties == ");
DEBUG_PUSH_VAR("FreeEntityIndex: %d", world->freeEntityIndex, "i32");
DEBUG_PUSH_VAR("GLDrawArray Calls: %d",
GLOBAL_debug.callCount[debugcount_drawArrays], "i32");
DEBUG_PUSH_VAR("PlatformMemAlloc Calls: %d",
GLOBAL_debug.callCount[debugcount_platformMemAlloc], "i32");
DEBUG_PUSH_VAR("PlatformMemFree Calls: %d",
GLOBAL_debug.callCount[debugcount_platformMemFree], "i32");
i32 vertexesUsed = GLOBAL_debug.callCount[debugcount_numVertex];
i32 vertexesAvail =
(ARRAY_COUNT(state->renderer.groups) * state->renderer.groupCapacity);
i32 vertexesLeft = vertexesAvail - vertexesUsed;
v2 vertexData = V2i(vertexesUsed, vertexesAvail);
DEBUG_PUSH_VAR("Vertexes Rendered: %1.0f/%1.0f", vertexData, "v2");
DEBUG_PUSH_VAR("Vertexes Left: %d", vertexesLeft, "i32");
i32 groupsUsed = GLOBAL_debug.callCount[debugcount_renderGroups];
i32 groupsAvail = ARRAY_COUNT(state->renderer.groups);
v2 groupData = V2i(groupsUsed, groupsAvail);
DEBUG_PUSH_VAR("Render Groups Used: %1.0f/%1.0f", groupData, "v2");
DEBUG_PUSH_VAR("Mouse Pos: %06.2f, %06.2f", state->input.mouseP, "v2");
/*
*****************
* MEMORY DISPLAY
*****************
*/
i32 debug_bAllocated = transientArena->used;
i32 debug_kbAllocated = debug_bAllocated / 1024;
i32 debug_mbAllocated = debug_kbAllocated / 1024;
DEBUG_PUSH_VAR("TransientArena Used: %db", debug_bAllocated, "i32");
DEBUG_PUSH_VAR("TransientArena Used: %dkb", debug_kbAllocated, "i32");
DEBUG_PUSH_VAR("TransientArena Used: %dmb", debug_mbAllocated, "i32");
DEBUG_PUSH_STRING("");
debug_bAllocated = state->arena_.used;
debug_kbAllocated = debug_bAllocated / 1024;
debug_mbAllocated = debug_kbAllocated / 1024;
DEBUG_PUSH_VAR("PersistentArena Used: %db", debug_bAllocated, "i32");
DEBUG_PUSH_VAR("PersistentArena Used: %dkb", debug_kbAllocated, "i32");
DEBUG_PUSH_VAR("PersistentArena Used: %dmb", debug_mbAllocated, "i32");
DEBUG_PUSH_STRING("");
AudioManager *audioManager = &state->audioManager;
DEBUG_PUSH_STRING("== Audio System ==");
for (i32 i = 0; i < ARRAY_COUNT(audioManager->sourceList); i++)
{
if (audioManager->sourceList[i].isFree) continue;
v3 tmp = V3i(i, audioManager->sourceList[i].id,
audioManager->sourceList[i].isFree);
DEBUG_PUSH_VAR("Source ID[%02.0f].id[%02.0f].isFree: %1.0f", tmp, "v3");
}
DEBUG_PUSH_STRING("");
DEBUG_PUSH_STRING("== EntityIDs in Battle List == ");
DEBUG_PUSH_VAR("NumEntitiesInBattle: %d", world->numEntitiesInBattle,
"i32");
if (world->numEntitiesInBattle > 0)
{
for (i32 i = 0; i < world->maxEntities; i++)
{
if (world->entityIdInBattle[i])
DEBUG_PUSH_VAR("Entity ID: %d", i, "i32");
}
}
else
{
DEBUG_PUSH_STRING("-none-");
}
updateAndRenderDebugStack(&state->renderer, transientArena, dt);
renderConsole(&state->renderer, transientArena);
debug_clearCounter();
}

View File

@ -1,12 +1,10 @@
#include "Dengine/Entity.h"
#include "Dengine/AssetManager.h"
#include "Dengine/Debug.h"
#include "Dengine/MemoryArena.h"
#include "Dengine/WorldTraveller.h"
SubTexture entity_getActiveSubTexture(Entity *const entity)
{
EntityAnim *entityAnim = &entity->animList[entity->currAnimId];
EntityAnim *entityAnim = &entity->animList[entity->animListIndex];
Animation *anim = entityAnim->anim;
char *frameName = anim->frameList[entityAnim->currFrame];
@ -14,11 +12,10 @@ SubTexture entity_getActiveSubTexture(Entity *const entity)
return result;
}
void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity,
const char *const animName)
void entity_setActiveAnim(Entity *const entity, const char *const animName)
{
/* Reset current anim data */
EntityAnim *currEntityAnim = &entity->animList[entity->currAnimId];
EntityAnim *currEntityAnim = &entity->animList[entity->animListIndex];
currEntityAnim->currDuration = currEntityAnim->anim->frameDuration;
currEntityAnim->currFrame = 0;
currEntityAnim->timesCompleted = 0;
@ -32,14 +29,11 @@ void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity,
// TODO(doyle): Linear search, but not a problem if list is small
if (common_strcmp(anim->key, animName) == 0)
{
entity->currAnimId = i;
entity->animListIndex = i;
EntityAnim *newEntityAnim = &entity->animList[i];
newEntityAnim->currDuration =
newEntityAnim->anim->frameDuration;
newEntityAnim->currFrame = 0;
worldTraveller_registerEvent(eventQueue, eventtype_start_anim,
newEntityAnim);
return;
}
}
@ -48,13 +42,12 @@ void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity,
DEBUG_LOG("Entity does not have access to desired anim");
}
void entity_updateAnim(EventQueue *eventQueue, Entity *const entity,
const f32 dt)
void entity_updateAnim(Entity *const entity, const f32 dt)
{
if (!entity->tex)
return;
EntityAnim *currEntityAnim = &entity->animList[entity->currAnimId];
EntityAnim *currEntityAnim = &entity->animList[entity->animListIndex];
Animation *anim = currEntityAnim->anim;
currEntityAnim->currDuration -= dt;
@ -64,30 +57,15 @@ void entity_updateAnim(EventQueue *eventQueue, Entity *const entity,
currEntityAnim->currFrame = currEntityAnim->currFrame % anim->numFrames;
if (currEntityAnim->currFrame == 0)
{
worldTraveller_registerEvent(eventQueue, eventtype_end_anim,
currEntityAnim);
currEntityAnim->timesCompleted++;
}
currEntityAnim->currDuration = anim->frameDuration;
}
// NOTE(doyle): If humanoid entity, let animation dictate render size which
// may exceed the hitbox size of the entity
switch (entity->type)
{
case entitytype_hero:
case entitytype_mob:
case entitytype_npc:
case entitytype_weapon:
case entitytype_projectile:
char *frameName = anim->frameList[currEntityAnim->currFrame];
SubTexture texRect =
asset_getAtlasSubTex(anim->atlas, frameName);
entity->size = v2_scale(texRect.rect.size, entity->scale);
default:
break;
}
char *frameName = anim->frameList[currEntityAnim->currFrame];
SubTexture texRect = asset_getAtlasSubTex(anim->atlas, frameName);
entity->size = v2_scale(texRect.rect.size, entity->scale);
}
void entity_addAnim(AssetManager *const assetManager, Entity *const entity,
@ -108,127 +86,3 @@ void entity_addAnim(AssetManager *const assetManager, Entity *const entity,
DEBUG_LOG("No more free entity animation slots");
}
Entity *const entity_add(MemoryArena_ *const arena, World *const world,
const v2 pos, const v2 size, const f32 scale,
const enum EntityType type,
const enum Direction direction, Texture *const tex,
const b32 collides)
{
#ifdef DENGINE_DEBUG
ASSERT(world);
ASSERT(world->freeEntityIndex < world->maxEntities);
ASSERT(type < entitytype_count);
#endif
Entity entity = {0};
entity.id = world->uniqueIdAccumulator++;
entity.pos = pos;
entity.size = size;
entity.hitbox = size;
entity.scale = scale;
entity.type = type;
entity.direction = direction;
entity.tex = tex;
entity.collides = collides;
switch (type)
{
case entitytype_hero:
entity.stats = MEMORY_PUSH_STRUCT(arena, EntityStats);
entity.stats->maxHealth = 100;
entity.stats->health = entity.stats->maxHealth;
entity.stats->actionRate = 100;
entity.stats->actionTimer = entity.stats->actionRate;
entity.stats->actionSpdMul = 100;
entity.stats->entityIdToAttack = -1;
entity.stats->queuedAttack = entityattack_invalid;
entity.state = entitystate_idle;
entity.collidesWith[entitytype_mob] = TRUE;
break;
case entitytype_mob:
{
entity.stats = MEMORY_PUSH_STRUCT(arena, EntityStats);
entity.stats->maxHealth = 100;
entity.stats->health = entity.stats->maxHealth;
entity.stats->actionRate = 80;
entity.stats->actionTimer = entity.stats->actionRate;
entity.stats->actionSpdMul = 100;
entity.stats->entityIdToAttack = -1;
entity.stats->queuedAttack = entityattack_invalid;
entity.state = entitystate_idle;
entity.collidesWith[entitytype_hero] = TRUE;
break;
}
case entitytype_projectile:
case entitytype_attackObject:
entity.stats = MEMORY_PUSH_STRUCT(arena, EntityStats);
entity.stats->maxHealth = 100;
entity.stats->health = entity.stats->maxHealth;
entity.stats->actionRate = 100;
entity.stats->actionTimer = entity.stats->actionRate;
entity.stats->actionSpdMul = 100;
entity.stats->entityIdToAttack = -1;
entity.stats->queuedAttack = entityattack_invalid;
entity.state = entitystate_idle;
break;
default:
break;
}
world->entities[world->freeEntityIndex++] = entity;
Entity *result = &world->entities[world->freeEntityIndex - 1];
return result;
}
void entity_clearData(MemoryArena_ *const arena, World *const world,
Entity *const entity)
{
// TODO(doyle): Mem free// memory leak!!
/*
if (entity->stats)
PLATFORM_MEM_FREE(arena, entity->stats, sizeof(EntityStats));
if (entity->audioRenderer)
PLATFORM_MEM_FREE(arena, entity->audioRenderer,
sizeof(AudioRenderer) * entity->numAudioRenderers);
*/
entity->type = entitytype_null;
}
i32 entity_getIndex(World *const world, const i32 entityId)
{
i32 first = 0;
i32 last = world->freeEntityIndex - 1;
while (first <= last)
{
i32 middle = (first + last) / 2;
if (world->entities[middle].id > entityId)
last = middle - 1;
else if (world->entities[middle].id < entityId)
first = middle + 1;
else
return middle;
}
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
return -1;
}
Entity *entity_get(World *const world, const i32 entityId)
{
Entity *result = NULL;
i32 worldIndex = entity_getIndex(world, entityId);
if (worldIndex != -1) result = &world->entities[worldIndex];
return result;
}

View File

@ -6,7 +6,7 @@
#include "Dengine/Debug.h"
#include "Dengine/MemoryArena.h"
void platform_memoryFree(MemoryArena_ *arena, void *data, i32 numBytes)
void platform_memoryFree(MemoryArena_ *arena, void *data, size_t numBytes)
{
if (data) free(data);
@ -16,7 +16,7 @@ void platform_memoryFree(MemoryArena_ *arena, void *data, i32 numBytes)
#endif
}
void *platform_memoryAlloc(MemoryArena_ *arena, i32 numBytes)
void *platform_memoryAlloc(MemoryArena_ *arena, size_t numBytes)
{
void *result = calloc(1, numBytes);

View File

@ -381,23 +381,31 @@ void renderer_entity(Renderer *renderer, Rect camera, Entity *entity,
if (math_pointInRect(camera, leftAlignedP) ||
math_pointInRect(camera, rightAlignedP))
{
EntityAnim *entityAnim = &entity->animList[entity->currAnimId];
Animation *anim = entityAnim->anim;
char *frameName = anim->frameList[entityAnim->currFrame];
SubTexture animRect = asset_getAtlasSubTex(anim->atlas, frameName);
EntityAnim *entityAnim = &entity->animList[entity->animListIndex];
v4 animTexRect = {0};
animTexRect.vec2[0] = animRect.rect.pos;
animTexRect.vec2[1] = v2_add(animRect.rect.pos, animRect.rect.size);
v4 texRect = {0};
if (entityAnim->anim)
{
Animation *anim = entityAnim->anim;
char *frameName = anim->frameList[entityAnim->currFrame];
SubTexture subTex = asset_getAtlasSubTex(anim->atlas, frameName);
flipTexCoord(&animTexRect, entity->flipX, entity->flipY);
texRect.vec2[0] = subTex.rect.pos;
texRect.vec2[1] = v2_add(subTex.rect.pos, subTex.rect.size);
flipTexCoord(&texRect, entity->flipX, entity->flipY);
}
else
{
texRect = V4(0.0f, 0.0f, (f32)entity->tex->width,
(f32)entity->tex->height);
}
if (entity->direction == direction_east)
{
flipTexCoord(&animTexRect, TRUE, FALSE);
flipTexCoord(&texRect, TRUE, FALSE);
}
RenderTex renderTex = {entity->tex, animTexRect};
RenderTex renderTex = {entity->tex, texRect};
renderer_rect(renderer, camera, entity->pos, entity->size, pivotPoint,
entity->rotation + rotate, renderTex, color);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,13 @@
@echo OFF
REM Build tags file
ctags -R
REM Check if build tool is on path
REM >nul, 2>nul will remove the output text from the where command
where cl.exe >nul 2>nul
if %errorlevel%==1 call msvc86.bat
REM Drop compilation files into build folder
IF NOT EXIST ..\bin mkdir ..\bin
pushd ..\bin

View File

@ -3,7 +3,8 @@
#include "Dengine/Debug.h"
#include "Dengine/Math.h"
#include "Dengine/OpenGL.h"
#include "Dengine/WorldTraveller.h"
#include "Dengine/MemoryArena.h"
#include "Dengine/Asteroid.h"
INTERNAL inline void processKey(KeyState *state, int action)
{
@ -142,6 +143,7 @@ i32 main(void)
i32 frameBufferWidth, frameBufferHeight;
glfwGetFramebufferSize(window, &frameBufferWidth, &frameBufferHeight);
glViewport(0, 0, frameBufferWidth, frameBufferHeight);
v2 windowSize = V2i(frameBufferWidth, frameBufferHeight);
glfwSetKeyCallback(window, keyCallback);
glfwSetCursorPosCallback(window, mouseCallback);
@ -161,9 +163,8 @@ i32 main(void)
*******************
*/
Memory memory = {0};
size_t persistentSize = MEGABYTES(128);
size_t transientSize = MEGABYTES(128);
MemoryIndex persistentSize = MEGABYTES(128);
MemoryIndex transientSize = MEGABYTES(128);
memory.persistentSize = persistentSize;
memory.persistent = PLATFORM_MEM_ALLOC_(NULL, persistentSize, u8);
@ -171,15 +172,8 @@ i32 main(void)
memory.transientSize = transientSize;
memory.transient = PLATFORM_MEM_ALLOC_(NULL, transientSize, u8);
GameState worldTraveller = {0};
worldTraveller_gameInit(&worldTraveller,
V2i(frameBufferWidth, frameBufferHeight), &memory);
#ifdef DENGINE_DEBUG
debug_init(&worldTraveller.arena_, V2i(windowWidth, windowHeight),
worldTraveller.assetManager.font);
#endif
glfwSetWindowUserPointer(window, CAST(void *)(&worldTraveller));
GameState gameState = {0};
glfwSetWindowUserPointer(window, CAST(void *)(&gameState));
/*
*******************
@ -206,10 +200,11 @@ i32 main(void)
glfwPollEvents();
/* Rendering commands here*/
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
worldTraveller_gameUpdateAndRender(&worldTraveller, secondsElapsed);
asteroid_gameUpdateAndRender(&gameState, &memory, windowSize,
secondsElapsed);
GL_CHECK_ERROR();
/* Swap the buffers */
@ -235,14 +230,10 @@ i32 main(void)
f32 msPerFrame = secondsElapsed * 1000.0f;
f32 framesPerSecond = 1.0f / secondsElapsed;
i32 entityCount =
worldTraveller.world[worldTraveller.currWorldIndex]
.freeEntityIndex;
char textBuffer[256];
snprintf(textBuffer, ARRAY_COUNT(textBuffer),
"Dengine | %f ms/f | %f fps | Entity Count: %d",
msPerFrame, framesPerSecond, entityCount);
msPerFrame, framesPerSecond, 0);
glfwSetWindowTitle(window, textBuffer);
titleUpdateFrequencyInSeconds = 0.5f;

View File

@ -94,7 +94,6 @@ typedef struct AudioVorbis
* Texture Assets
*********************************
*/
typedef struct SubTexture
{
Rect rect;

View File

@ -0,0 +1,31 @@
#ifndef ASTEROID_H
#define ASTEROID_H
#include "Dengine/AssetManager.h"
#include "Dengine/Common.h"
#include "Dengine/Entity.h"
#include "Dengine/MemoryArena.h"
#include "Dengine/Platform.h"
#include "Dengine/Renderer.h"
typedef struct GameState {
b32 init;
Entity entityList[1024];
i32 entityIndex;
Rect camera;
AssetManager assetManager;
KeyInput input;
MemoryArena_ transientArena;
MemoryArena_ persistentArena;
Renderer renderer;
} GameState;
void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
v2 windowSize, f32 dt);
#endif

View File

@ -14,6 +14,8 @@ typedef i32 b32;
typedef float f32;
typedef double f64;
typedef size_t MemoryIndex;
#define TRUE 1
#define FALSE 0

View File

@ -1,17 +1,10 @@
#ifndef DENGINE_ENTITY_H
#define DENGINE_ENTITY_H
#ifndef ENTITY_H
#define ENTITY_H
#include "Dengine/Common.h"
#include "Dengine/Math.h"
#include "Dengine/Assets.h"
#include "Dengine/Common.h"
typedef struct AssetManager AssetManager;
typedef struct AudioRenderer AudioRenderer;
typedef struct MemoryArena MemoryArena_;
typedef struct World World;
typedef struct EventQueue EventQueue;
typedef struct Entity Entity;
enum Direction
{
@ -19,63 +12,17 @@ enum Direction
direction_west,
direction_south,
direction_east,
direction_num,
direction_null,
direction_num,
};
enum EntityType
{
entitytype_null,
entitytype_hero,
entitytype_weapon,
entitytype_projectile,
entitytype_attackObject,
entitytype_npc,
entitytype_mob,
entitytype_tile,
entitytype_soundscape,
entitytype_invalid,
entitytype_ship,
entitytype_count,
};
enum EntityState
{
entitystate_idle,
entitystate_battle,
entitystate_attack,
entitystate_dead,
entitystate_count,
entitystate_invalid,
};
enum EntityAttack
{
entityattack_claudeAttack,
entityattack_claudeAttackUp,
entityattack_claudeAttackDown,
entityattack_claudeDragonHowl,
entityattack_claudeEnergySword,
entityattack_claudeRipperBlast,
entityattack_claudeAirSlash,
entityattack_count,
entityattack_invalid,
};
typedef struct EntityStats
{
f32 maxHealth;
f32 health;
f32 actionRate;
f32 actionTimer;
f32 actionSpdMul;
f32 busyDuration;
i32 entityIdToAttack;
enum EntityAttack queuedAttack;
Entity *weapon;
} EntityStats;
typedef struct EntityAnim
{
Animation *anim;
@ -85,7 +32,7 @@ typedef struct EntityAnim
u32 timesCompleted;
} EntityAnim;
struct Entity
typedef struct Entity
{
i32 id;
@ -102,7 +49,6 @@ struct Entity
b32 invisible;
enum EntityState state;
enum EntityType type;
enum Direction direction;
@ -116,32 +62,13 @@ struct Entity
// allow for early-exit in collision check if the entity doesn't collide at
// all
b32 collides;
enum EntityType collidesWith[entitytype_count];
EntityAnim animList[16];
i32 currAnimId;
EntityStats *stats;
i32 animListIndex;
// TODO(doyle): Audio mixing instead of multiple renderers
AudioRenderer *audioRenderer;
i32 numAudioRenderers;
};
} Entity;
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,
const f32 dt);
void entity_addAnim(AssetManager *const assetManager, Entity *const entity,
const char *const animName);
Entity *const entity_add(MemoryArena_ *const arena, World *const world,
const v2 pos, const v2 size, const f32 scale,
const enum EntityType type,
const enum Direction direction, Texture *const tex,
const b32 collides);
void entity_clearData(MemoryArena_ *const arena, World *const world,
Entity *const entity);
Entity *entity_get(World *const world, const i32 entityId);
i32 entity_getIndex(World *const world, const i32 entityId);
#endif

View File

@ -5,25 +5,25 @@
typedef struct MemoryArena
{
size_t size;
size_t used;
MemoryIndex size;
MemoryIndex used;
u8 *base;
} MemoryArena_;
typedef struct Memory
{
void *persistent;
size_t persistentSize;
MemoryIndex persistentSize;
void *transient;
size_t transientSize;
MemoryIndex transientSize;
b32 init;
} Memory;
#define MEMORY_PUSH_STRUCT(arena, type) (type *)memory_pushBytes(arena, sizeof(type))
#define MEMORY_PUSH_ARRAY(arena, count, type) (type *)memory_pushBytes(arena, (count)*sizeof(type))
inline void *memory_pushBytes(MemoryArena_ *arena, size_t size)
inline void *memory_pushBytes(MemoryArena_ *arena, MemoryIndex size)
{
ASSERT((arena->used + size) <= arena->size);
void *result = arena->base + arena->used;
@ -32,6 +32,6 @@ inline void *memory_pushBytes(MemoryArena_ *arena, size_t size)
return result;
}
void memory_arenaInit(MemoryArena_ *arena, void *base, size_t size);
void memory_arenaInit(MemoryArena_ *arena, void *base, MemoryIndex size);
#endif

View File

@ -140,14 +140,16 @@ typedef struct PlatformFileRead
} PlatformFileRead;
// TODO(doyle): Create own custom memory allocator
#define PLATFORM_MEM_FREE_(arena, ptr, bytes) platform_memoryFree(arena, CAST(void *) ptr, bytes)
#define PLATFORM_MEM_FREE_(arena, ptr, bytes) \
platform_memoryFree(arena, CAST(void *) ptr, bytes)
// TODO(doyle): numBytes in mem free is temporary until we create custom
// allocator since we haven't put in a system to track memory usage per
// allocation
void platform_memoryFree(MemoryArena_ *arena, void *data, i32 numBytes);
void platform_memoryFree(MemoryArena_ *arena, void *data, MemoryIndex numBytes);
#define PLATFORM_MEM_ALLOC_(arena, num, type) CAST(type *) platform_memoryAlloc(arena, num * sizeof(type))
void *platform_memoryAlloc(MemoryArena_ *arena, i32 numBytes);
#define PLATFORM_MEM_ALLOC_(arena, num, type) \
CAST(type *) platform_memoryAlloc(arena, num * sizeof(type))
void *platform_memoryAlloc(MemoryArena_ *arena, MemoryIndex numBytes);
void platform_closeFileRead(MemoryArena_ *arena, PlatformFileRead *file);
i32 platform_readFileToBuffer(MemoryArena_ *arena, const char *const filePath,

View File

@ -6,8 +6,8 @@
/* Forward Declaration */
typedef struct AssetManager AssetManager;
typedef struct Entity Entity;
typedef struct Font Font;
typedef struct Entity Entity;
typedef struct MemoryArena MemoryArena_;
typedef struct Shader Shader;
typedef struct Texture Texture;

View File

@ -1,109 +0,0 @@
#ifndef WORLDTRAVELLER_GAME_H
#define WORLDTRAVELLER_GAME_H
#include "Dengine/AssetManager.h"
#include "Dengine/Audio.h"
#include "Dengine/Common.h"
#include "Dengine/Math.h"
#include "Dengine/MemoryArena.h"
#include "Dengine/Platform.h"
#include "Dengine/Renderer.h"
#include "Dengine/UserInterface.h"
#define NUM_KEYS 1024
#define METERS_TO_PIXEL 240
/* Forward declaration */
typedef struct Entity Entity;
enum EventType
{
eventtype_null = 0,
eventtype_start_attack,
eventtype_end_attack,
eventtype_start_anim,
eventtype_end_anim,
eventtype_entity_died,
eventtype_count,
};
typedef struct Event
{
enum EventType type;
void *data;
} Event;
typedef struct EventQueue
{
Event queue[1024];
i32 numEvents;
} EventQueue;
enum RectBaseline
{
rectbaseline_top,
rectbaseline_topLeft,
rectbaseline_topRight,
rectbaseline_bottom,
rectbaseline_bottomRight,
rectbaseline_bottomLeft,
rectbaseline_left,
rectbaseline_right,
rectbaseline_center,
rectbaseline_count,
};
typedef struct Config
{
b32 playWorldAudio;
b32 showStatMenu;
b32 showDebugDisplay;
} Config;
typedef struct World
{
Entity *entities;
i32 maxEntities;
b32 *entityIdInBattle;
i32 numEntitiesInBattle;
i32 cameraFollowingId;
v2 cameraPos; // In pixels
v4 bounds; // In pixels
i32 heroId;
i32 freeEntityIndex;
u32 uniqueIdAccumulator;
Entity *soundscape;
} World;
typedef struct GameState
{
Memory *memory;
MemoryArena_ arena_;
MemoryArena_ transientArena;
enum State state;
KeyInput input;
v2 mouse;
Renderer renderer;
World world[4];
i32 currWorldIndex;
i32 tileSize;
AssetManager assetManager;
AudioManager audioManager;
Config config;
UiState uiState;
EventQueue eventQueue;
} GameState;
void worldTraveller_gameInit(GameState *state, v2 windowSize, Memory *memory);
void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt);
void worldTraveller_registerEvent(EventQueue *eventQueue, enum EventType type,
void *data);
#endif