From 10891ab56e28ae5bb625752e553d2bd10dbb2852 Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Tue, 29 Nov 2016 01:41:33 +1100 Subject: [PATCH] Add resolution changer in options menu --- src/Asteroid.c | 234 ++++++++++++++++++++++----------- src/Common.c | 209 +++++++++++++++++++++++++---- src/Debug.c | 22 ++-- src/Renderer.c | 10 +- src/String.c | 117 ----------------- src/dengine.c | 86 ++++++++++-- src/include/Dengine/Asteroid.h | 16 ++- src/include/Dengine/Common.h | 41 ++++++ src/include/Dengine/Debug.h | 2 +- src/include/Dengine/Math.h | 1 + src/include/Dengine/Platform.h | 2 + src/include/Dengine/Renderer.h | 1 + src/include/Dengine/String.h | 11 -- 13 files changed, 497 insertions(+), 255 deletions(-) diff --git a/src/Asteroid.c b/src/Asteroid.c index d61d211..a0f9b82 100644 --- a/src/Asteroid.c +++ b/src/Asteroid.c @@ -82,7 +82,7 @@ INTERNAL v2 *createAsteroidVertexList(MemoryArena_ *arena, i32 iterations, for (i32 i = 0; i < iterations; i++) { i32 randValue = rand(); - + // NOTE(doyle): Sin/cos generate values from +-1, we want to create // vertices that start from 0, 0 (i.e. strictly positive) result[i] = V2(((math_cosf(iterationAngle * i) + 1) * asteroidRadius), @@ -91,7 +91,7 @@ INTERNAL v2 *createAsteroidVertexList(MemoryArena_ *arena, i32 iterations, ASSERT(result[i].x >= 0 && result[i].y >= 0); #if 1 - f32 displacementDist = 0.50f * asteroidRadius; + f32 displacementDist = 0.50f * asteroidRadius; i32 vertexDisplacement = randValue % (i32)displacementDist + (i32)(displacementDist * 0.25f); @@ -240,16 +240,15 @@ INTERNAL u32 moveEntity(GameWorldState *world, MemoryArena_ *transientArena, ASSERT(checkEntity->vertexPoints); /* Create entity edge lists */ - v2 *entityVertexListOffsetToP = entity_generateUpdatedVertexList( - transientArena, entity); + v2 *entityVertexListOffsetToP = + entity_generateUpdatedVertexList(transientArena, entity); v2 *checkEntityVertexListOffsetToP = - entity_generateUpdatedVertexList(transientArena, - checkEntity); + entity_generateUpdatedVertexList(transientArena, checkEntity); - v2 *entityEdgeList = createNormalEdgeList(transientArena, - entityVertexListOffsetToP, - entity->numVertexPoints); + v2 *entityEdgeList = + createNormalEdgeList(transientArena, entityVertexListOffsetToP, + entity->numVertexPoints); v2 *checkEntityEdgeList = createNormalEdgeList( transientArena, checkEntityVertexListOffsetToP, @@ -296,7 +295,8 @@ enum AsteroidSize asteroidsize_count, }; -typedef struct { +typedef struct +{ v2 pos; v2 dP; } AsteroidSpec; @@ -377,12 +377,12 @@ INTERNAL void addAsteroidWithSpec(GameWorldState *world, { ASSERT(INVALID_CODE_PATH); } - asteroid->pos = newP; + asteroid->pos = newP; } else { - asteroid->pos = spec->pos; - asteroid->dP = spec->dP; + asteroid->pos = spec->pos; + asteroid->dP = spec->dP; } asteroid->size = size; @@ -406,7 +406,7 @@ INTERNAL void addAsteroidWithSpec(GameWorldState *world, } asteroid->vertexPoints = vertexCache[cacheIndex]; - asteroid->color = V4(1.0f, 1.0f, 1.0f, 1.0f); + asteroid->color = V4(1.0f, 1.0f, 1.0f, 1.0f); } INTERNAL void addAsteroid(GameWorldState *world, enum AsteroidSize asteroidSize) @@ -456,7 +456,7 @@ INTERNAL AudioRenderer *getFreeAudioRenderer(GameWorldState *world, AudioVorbis *vorbis, i32 maxSimultaneousPlayers) { - i32 freeIndex = -1; + i32 freeIndex = -1; i32 sameAudioPlayingCount = 0; AudioRenderer *result = NULL; @@ -512,13 +512,13 @@ INTERNAL void addPlayer(GameWorldState *world) INTERNAL void deleteEntity(GameWorldState *world, i32 entityIndex) { ASSERT(entityIndex > 0); - ASSERT(entityIndex < ARRAY_COUNT(world->entityList)); + ASSERT(entityIndex < world->entityListSize); /* Last entity replaces the entity to delete */ world->entityList[entityIndex] = world->entityList[world->entityIndex - 1]; /* Make sure the replaced entity from end of list is cleared out */ - Entity emptyEntity = {0}; + Entity emptyEntity = {0}; world->entityList[--world->entityIndex] = emptyEntity; } @@ -581,6 +581,10 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) world->camera.max = state->renderer.size; world->size = state->renderer.size; + world->entityListSize = 1024; + world->entityList = MEMORY_PUSH_ARRAY(&world->entityArena, + world->entityListSize, Entity); + { // Init null entity Entity *nullEntity = &world->entityList[world->entityIndex++]; nullEntity->id = world->entityIdCounter++; @@ -613,7 +617,7 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) world->numStarP = 100; world->starPList = - MEMORY_PUSH_ARRAY(&state->persistentArena, world->numStarP, v2); + MEMORY_PUSH_ARRAY(&world->entityArena, world->numStarP, v2); for (i32 i = 0; i < world->numStarP; i++) { @@ -630,7 +634,7 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) addAsteroid(world, (rand() % asteroidsize_count)); Radians starRotation = DEGREES_TO_RADIANS(45.0f); - v2 starSize = V2(2, 2); + v2 starSize = V2(2, 2); for (i32 i = 0; i < world->numStarP; i++) { renderer_rect(&state->renderer, world->camera, world->starPList[i], @@ -640,7 +644,7 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) } if (platform_queryKey(&state->input.keys[keycode_left_square_bracket], - readkeytype_repeat, 0.2f)) + readkeytype_repeat, 0.2f)) { addAsteroid(world, (rand() % asteroidsize_count)); } @@ -673,17 +677,17 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) { addBullet(world, entity); - AudioVorbis *fire = - asset_vorbisGet(&state->assetManager, "fire"); - AudioRenderer *audioRenderer = - getFreeAudioRenderer(world, fire, 2); - if (audioRenderer) - { - // TODO(doyle): Atm transient arena is not used, this is - // just to fill out the arguments - audio_vorbisPlay(&state->transientArena, - &state->audioManager, audioRenderer, - fire, 1); + AudioVorbis *fire = + asset_vorbisGet(&state->assetManager, "fire"); + AudioRenderer *audioRenderer = + getFreeAudioRenderer(world, fire, 2); + if (audioRenderer) + { + // TODO(doyle): Atm transient arena is not used, this is + // just to fill out the arguments + audio_vorbisPlay(&state->transientArena, + &state->audioManager, audioRenderer, fire, + 1); } } @@ -768,11 +772,15 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) // direction by extrapolating from it's current dp else { - if (entity->dP.x >= 0) localDp.x = 1.0f; - else localDp.x = -1.0f; + if (entity->dP.x >= 0) + localDp.x = 1.0f; + else + localDp.x = -1.0f; - if (entity->dP.y >= 0) localDp.y = 1.0f; - else localDp.y = -1.0f; + if (entity->dP.y >= 0) + localDp.y = 1.0f; + else + localDp.y = -1.0f; } /* @@ -814,10 +822,9 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) f32 divisor = MAX(entity->particleInitDp.x, entity->particleInitDp.y); - f32 maxDp = MAX(entity->dP.x, entity->dP.y); + f32 maxDp = MAX(entity->dP.x, entity->dP.y); entity->color.a = maxDp / divisor; - } /* Loop entity around world */ @@ -876,10 +883,10 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) spec.dP = v2_scale(colliderA->dP, -4.0f); addAsteroidWithSpec(world, asteroidsize_medium, &spec); - spec.dP = v2_perpendicular(spec.dP); + spec.dP = v2_perpendicular(spec.dP); addAsteroidWithSpec(world, asteroidsize_small, &spec); - spec.dP = v2_perpendicular(colliderA->dP); + spec.dP = v2_perpendicular(colliderA->dP); addAsteroidWithSpec(world, asteroidsize_small, &spec); numParticles = 16; @@ -892,8 +899,8 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) &world->entityList[world->entityIndex++]; particle->id = world->entityIdCounter++; - particle->pos = colliderA->pos; - particle->size = V2(4.0f, 4.0f); + particle->pos = colliderA->pos; + particle->size = V2(4.0f, 4.0f); i32 randValue = rand(); Radians rotation = @@ -977,11 +984,9 @@ INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt) RenderFlags flags = renderflag_wireframe | renderflag_no_texture; renderer_entity(&state->renderer, &state->transientArena, world->camera, - entity, V2(0, 0), 0, - collideColor, flags); + entity, V2(0, 0), 0, collideColor, flags); } - for (i32 i = 0; i < world->numAudioRenderers; i++) { AudioRenderer *audioRenderer = &world->audioRenderer[i]; @@ -1000,6 +1005,45 @@ INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt) StartMenuState *menuState = GET_STATE_DATA(state, &state->persistentArena, StartMenuState); + if (!menuState->init) + { + MemoryArena_ *persistentArena = &state->persistentArena; + OptimalArrayV2 *resolutionArray = inputBuffer->resolutionList; + v2 currRes = renderer->size; + i32 resIndex = -1; + + menuState->resStrings = memory_pushBytes( + persistentArena, resolutionArray->index * sizeof(String *)); + + for (i32 i = 0; i < resolutionArray->index; i++) + { + v2 res = resolutionArray->ptr[i]; + if (v2_equals(res, currRes)) resIndex = i; + + char widthString[8] = {0}; + char heightString[8] = {0}; + common_itoa((i32)res.w, widthString, ARRAY_COUNT(widthString)); + common_itoa((i32)res.h, heightString, ARRAY_COUNT(heightString)); + + String *resString = common_stringMake(transientArena, widthString); + resString = common_stringAppend(transientArena, resString, "x", 1); + resString = + common_stringAppend(transientArena, resString, heightString, + ARRAY_COUNT(heightString)); + + menuState->resStrings[i] = MEMORY_PUSH_ARRAY( + persistentArena, common_stringLen(resString), char); + common_strncpy(menuState->resStrings[i], resString, + common_stringLen(resString)); + } + + if (resIndex == -1) ASSERT(INVALID_CODE_PATH); + + menuState->init = TRUE; + menuState->numResStrings = resolutionArray->index; + menuState->resStringDisplayIndex = resIndex; + } + Font *arial15 = asset_fontGetOrCreateOnDemand( assetManager, &state->persistentArena, transientArena, "Arial", 15); Font *arial25 = asset_fontGetOrCreateOnDemand( @@ -1008,7 +1052,6 @@ INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt) v2 screenCenter = v2_scale(renderer->size, 0.5f); ui_beginState(uiState); - if (menuState->optionsShow) { if (platform_queryKey(&inputBuffer->keys[keycode_o], @@ -1020,39 +1063,78 @@ INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt) } else { - f32 textYOffset = arial25->size * 1.5f;; - const char *const title = "Options"; - v2 p = v2_add(screenCenter, V2(0, textYOffset)); - renderer_stringFixedCentered(renderer, transientArena, arial25, - title, p, V2(0, 0), 0, V4(1, 0, 1, 1), - 0); - - const char *const resolutionLabel = "Resolution"; - p = v2_add(screenCenter, V2(0, 0)); - - renderer_stringFixedCentered(renderer, transientArena, arial25, - resolutionLabel, p, V2(0, 0), 0, - V4(1, 0, 1, 1), 0); - - const char *const resSizeLabel = "< 800x600 >"; - p = v2_add(screenCenter, V2(0, -textYOffset)); - - renderer_stringFixedCentered(renderer, transientArena, arial25, - resSizeLabel, p, V2(0, 0), 0, - V4(1, 0, 1, 1), 0); - if (platform_queryKey(&inputBuffer->keys[keycode_enter], readkeytype_one_shot, KEY_DELAY_NONE)) { - menuState->newResolutionRequest = TRUE; - menuState->newResolution = V2(800, 600); + OptimalArrayV2 *resolutionArray = inputBuffer->resolutionList; - renderer->size = menuState->newResolution; + menuState->newResolutionRequest = TRUE; + v2 newSize = + resolutionArray->ptr[menuState->resStringDisplayIndex]; GameWorldState *world = GET_STATE_DATA( state, &state->persistentArena, GameWorldState); - world->size = menuState->newResolution; - world->camera.max = menuState->newResolution; + + renderer_updateSize(renderer, &state->assetManager, newSize); + + // TODO(doyle): reset world arena instead of zeroing out struct + common_memset((u8 *)world, 0, sizeof(GameWorldState)); + debug_init(newSize, *arial15); + } + else + { + if (platform_queryKey(&inputBuffer->keys[keycode_left], + readkeytype_one_shot, KEY_DELAY_NONE)) + { + menuState->resStringDisplayIndex--; + } + else if (platform_queryKey(&inputBuffer->keys[keycode_right], + readkeytype_one_shot, + KEY_DELAY_NONE)) + { + menuState->resStringDisplayIndex++; + } + + if (menuState->resStringDisplayIndex < 0) + { + menuState->resStringDisplayIndex = 0; + } + else if (menuState->resStringDisplayIndex >= + menuState->numResStrings) + { + menuState->resStringDisplayIndex = + menuState->numResStrings - 1; + } + } + + f32 textYOffset = arial25->size * 1.5f; + { // Options Title String Display + const char *const title = "Options"; + + v2 p = v2_add(screenCenter, V2(0, textYOffset)); + renderer_stringFixedCentered(renderer, transientArena, arial25, + title, p, V2(0, 0), 0, + V4(1, 0, 1, 1), 0); + } + + { // Resolution String Display + + /* Draw label */ + const char *const resolutionLabel = "Resolution"; + + v2 p = v2_add(screenCenter, V2(0, 0)); + renderer_stringFixedCentered(renderer, transientArena, arial25, + resolutionLabel, p, V2(0, 0), 0, + V4(1, 0, 1, 1), 0); + + /* Draw label value */ + char *resStringToDisplay = + menuState->resStrings[menuState->resStringDisplayIndex]; + + p = v2_add(screenCenter, V2(0, -textYOffset)); + renderer_stringFixedCentered(renderer, transientArena, arial25, + resStringToDisplay, p, V2(0, 0), 0, + V4(1, 0, 1, 1), 0); } } } @@ -1084,10 +1166,10 @@ INTERNAL void startMenuUpdate(GameState *state, Memory *memory, f32 dt) { // Draw show options prompt const char *const optionPrompt = "Press [o] for options "; - v2 p = v2_add(screenCenter, V2(0, -120)); + v2 p = v2_add(screenCenter, V2(0, -120)); renderer_stringFixedCentered(renderer, transientArena, arial25, - optionPrompt, p, V2(0, 0), 0, V4(1, 1, 0, 1), - 0); + optionPrompt, p, V2(0, 0), 0, + V4(1, 1, 0, 1), 0); } if (platform_queryKey(&inputBuffer->keys[keycode_enter], @@ -1131,7 +1213,7 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, srand((u32)time(NULL)); asset_init(&state->assetManager, &state->persistentArena); audio_init(&state->audioManager); - + // NOTE(doyle): Load game assets must be before init_renderer so that // shaders are available for the renderer configuration loadGameAssets(state); @@ -1139,7 +1221,7 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, &state->persistentArena, windowSize); Font *arial15 = asset_fontGet(&state->assetManager, "Arial", 15); - debug_init(&state->persistentArena, windowSize, *arial15); + debug_init(windowSize, *arial15); state->currState = appstate_StartMenuState; state->init = TRUE; diff --git a/src/Common.c b/src/Common.c index 08c230a..b60cd29 100644 --- a/src/Common.c +++ b/src/Common.c @@ -1,10 +1,173 @@ +#include + #include "Dengine/Common.h" -#include "Dengine/Math.h" +#include "Dengine/MemoryArena.h" + +void common_optimalArrayV2Create(OptimalArrayV2 *array) +{ + array->ptr = array->fastStorage; + array->size = ARRAY_COUNT(array->fastStorage); +} + +i32 common_optimalArrayV2Push(OptimalArrayV2 *array, v2 data) +{ + if (array->index + 1 > array->size) + { + array->size += ARRAY_COUNT(array->fastStorage); + i32 newSizeInBytes = array->size * sizeof(v2); + + /* If first time expanding, we need to manually malloc and copy */ + if (array->ptr == array->fastStorage) + { + array->ptr = malloc(newSizeInBytes); + for (i32 i = 0; i < ARRAY_COUNT(array->fastStorage); i++) + { + array->ptr[i] = array->fastStorage[i]; + } + } + else + { + array->ptr = realloc(array->ptr, newSizeInBytes); + } + + if (!array->ptr) return optimalarrayerror_out_of_memory; + } + + array->ptr[array->index++] = data; + + return 0; +} + +void common_optimalArrayV2Destroy(OptimalArrayV2 *array) +{ + if (array->ptr != array->fastStorage) + { + free(array->ptr); + } +} + +/* + * +-------------------------------------+ + * | Header | C-String | Null Terminator | + * +-------------------------------------+ + * | + * +--> Functions return the c-string for compatibility with other + * string libraries + * + * Headers are retrieved using pointer arithmetric from the C string. These + * strings are typechecked by their own typedef char String. + */ + +typedef struct StringHeader +{ + i32 len; + + // NOTE(doyle): A string is stored as one contiguous chunk of memory. We + // don't use a pointer for storing the string as this'd require an extra + // 4 bytes to store the pointer, which we don't need if everything is + // contiguous. The string follows on from the len, and we return the address + // of the string to simulate a pointer. + String string; +} StringHeader; + +// TODO(doyle): string capacity- append if already enough space +INTERNAL StringHeader *stringGetHeader(String *const string) +{ + StringHeader *result = NULL; + + // NOTE(doyle): C-String must be located at end of struct type for offset to + // be correct! We cannot just subtract the string-header since we start at + // the string ptr position + if (string) + { + i32 byteOffsetToHeader = sizeof(StringHeader) - sizeof(String *); + result = CAST(StringHeader *)((CAST(u8 *) string) - byteOffsetToHeader); + } + + return result; +} + +i32 common_stringLen(String *const string) +{ + if (!string) return -1; + + StringHeader *header = stringGetHeader(string); + i32 result = header->len; + return result; +} + +String *const common_stringAppend(MemoryArena_ *const arena, String *oldString, + char *appendString, i32 appendLen) + +{ + if (!oldString || !appendString || !arena) return oldString; + + /* Calculate size of new string */ + StringHeader *oldHeader = stringGetHeader(oldString); + i32 newLen = oldHeader->len + appendLen; + String *newString = common_stringMakeLen(arena, newLen); + + /* Append strings together */ + String *insertPtr = newString; + common_strncpy(insertPtr, oldString, oldHeader->len); + insertPtr += oldHeader->len; + common_strncpy(insertPtr, appendString, appendLen); + + /* Free old string */ + common_stringFree(arena, oldString); + + return newString; +} + +void common_stringFree(MemoryArena_ *arena, String *string) +{ + if (!string || !arena) return; + + StringHeader *header = stringGetHeader(string); + i32 bytesToFree = sizeof(StringHeader) + header->len; + + common_memset((u8 *)header, 0, bytesToFree); + + // TODO(doyle): Mem free + // PLATFORM_MEM_FREE(arena, header, bytesToFree); + + string = NULL; +} + +String *const common_stringMake(MemoryArena_ *const arena, char *string) +{ + if (!arena) return NULL; + + i32 len = common_strlen(string); + String *result = common_stringMakeLen(arena, len); + common_strncpy(result, string, len); + + return result; +} + +String *const common_stringMakeLen(MemoryArena_ *const arena, i32 len) +{ + if (!arena) return NULL; + + // NOTE(doyle): Allocate the string header size plus the len. But _note_ + // that StringHeader contains a single String character. This has + // a side-effect of already preallocating a byte for the null-terminating + // character. Whilst the len of a string counts up to the last character + // _not_ including null-terminator. + i32 bytesToAllocate = sizeof(StringHeader) + len; + void *chunk = memory_pushBytes(arena, bytesToAllocate * sizeof(u8)); + if (!chunk) return NULL; + + StringHeader *header = CAST(StringHeader *) chunk; + header->len = len; + return &header->string; +} i32 common_strlen(const char *const string) { i32 result = 0; - while (string[result]) result++; + while (string[result]) + result++; return result; } @@ -12,8 +175,7 @@ i32 common_strcmp(const char *a, const char *b) { while (*a == *b) { - if (!*a) - return 0; + if (!*a) return 0; a++; b++; } @@ -27,13 +189,13 @@ void common_strncat(char *dest, const char *src, i32 numChars) while (*stringPtr) stringPtr++; - for (i32 i = 0; i < numChars; i++) + for (i32 i = 0; i < numChars; i++) *(stringPtr++) = src[i]; } char *common_strncpy(char *dest, const char *src, i32 numChars) { - for (i32 i = 0; i < numChars; i++) + for (i32 i = 0; i < numChars; i++) dest[i] = src[i]; return dest; @@ -54,9 +216,9 @@ INTERNAL void reverseString(char *const buf, const i32 bufSize) for (i32 i = 0; i < mid; i++) { - char tmp = buf[i]; - buf[i] = buf[(bufSize-1) - i]; - buf[(bufSize-1) - i] = tmp; + char tmp = buf[i]; + buf[i] = buf[(bufSize - 1) - i]; + buf[(bufSize - 1) - i] = tmp; } } @@ -65,9 +227,9 @@ void common_itoa(i32 value, char *buf, i32 bufSize) if (!buf || bufSize == 0) return; // NOTE(doyle): Max 32bit integer (+-)2147483647 - i32 charIndex = 0; + i32 charIndex = 0; - b32 negative = FALSE; + b32 negative = FALSE; if (value < 0) negative = TRUE; if (negative) buf[charIndex++] = '-'; @@ -75,7 +237,7 @@ void common_itoa(i32 value, char *buf, i32 bufSize) i32 val = ABS(value); while (val != 0 && charIndex < bufSize) { - i32 rem = val % 10; + i32 rem = val % 10; buf[charIndex++] = rem + '0'; val /= 10; } @@ -137,31 +299,34 @@ u32 common_murmurHash2(const void *key, i32 len, u32 seed) // Mix 4 bytes at a time into the hash - const unsigned char * data = (const unsigned char *)key; + const unsigned char *data = (const unsigned char *)key; - while(len >= 4) + while (len >= 4) { u32 k = *(u32 *)data; k *= m; k ^= k >> r; k *= m; - + h *= m; h ^= k; data += 4; len -= 4; } - + // Handle the last few bytes of the input array - switch(len) + switch (len) { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; + case 3: + h ^= data[2] << 16; + case 2: + h ^= data[1] << 8; + case 1: + h ^= data[0]; + h *= m; }; // Do a few final mixes of the hash to ensure the last few @@ -172,4 +337,4 @@ u32 common_murmurHash2(const void *key, i32 len, u32 seed) h ^= h >> 15; return h; -} +} diff --git a/src/Debug.c b/src/Debug.c index 565c542..dae0e7a 100644 --- a/src/Debug.c +++ b/src/Debug.c @@ -9,7 +9,7 @@ typedef struct DebugState { b32 init; Font font; - i32 *callCount; + i32 callCount[debugcount_num]; f32 stringLineGap; /* Debug strings rendered in top left corner */ @@ -28,11 +28,12 @@ typedef struct DebugState GLOBAL_VAR DebugState GLOBAL_debug; -void debug_init(MemoryArena_ *arena, v2 windowSize, Font font) +void debug_init(v2 windowSize, Font font) { - GLOBAL_debug.font = font; - GLOBAL_debug.callCount = - memory_pushBytes(arena, debugcount_num * sizeof(i32)); + GLOBAL_debug.font = font; + + for (i32 i = 0; i < debugcount_num; i++) GLOBAL_debug.callCount[i] = 0; + GLOBAL_debug.stringLineGap = CAST(f32) font.verticalSpacing; /* Init debug string stack */ @@ -40,18 +41,17 @@ void debug_init(MemoryArena_ *arena, v2 windowSize, Font font) GLOBAL_debug.stringUpdateTimer = 0.0f; GLOBAL_debug.stringUpdateRate = 0.15f; - GLOBAL_debug.initialStringP = - V2(0.0f, (windowSize.h - 1.8f * GLOBAL_debug.stringLineGap)); - GLOBAL_debug.currStringP = GLOBAL_debug.initialStringP; + GLOBAL_debug.initialStringP = V2(0.0f, (windowSize.h - 1.8f * GLOBAL_debug.stringLineGap)); + GLOBAL_debug.currStringP = GLOBAL_debug.initialStringP; /* Init gui console */ - i32 maxConsoleStrLen = ARRAY_COUNT(GLOBAL_debug.console[0]); + i32 maxConsoleStrLen = ARRAY_COUNT(GLOBAL_debug.console[0]); GLOBAL_debug.consoleIndex = 0; // TODO(doyle): Font max size not entirely correct? using 1 * font.maxSize.w // reveals around 4 characters .. - f32 consoleXPos = font.maxSize.w * 20; - f32 consoleYPos = windowSize.h - 1.8f * GLOBAL_debug.stringLineGap; + f32 consoleXPos = font.maxSize.w * 20; + f32 consoleYPos = windowSize.h - 1.8f * GLOBAL_debug.stringLineGap; GLOBAL_debug.initialConsoleP = V2(consoleXPos, consoleYPos); GLOBAL_debug.init = TRUE; diff --git a/src/Renderer.c b/src/Renderer.c index 4af61b2..ea61258 100644 --- a/src/Renderer.c +++ b/src/Renderer.c @@ -32,8 +32,7 @@ void shaderUniformSetVec4f(u32 shaderId, const GLchar *name, void shaderUse(u32 shaderId) { glUseProgram(shaderId); } -void renderer_init(Renderer *renderer, AssetManager *assetManager, - MemoryArena_ *persistentArena, v2 windowSize) +void renderer_updateSize(Renderer *renderer, AssetManager *assetManager, v2 windowSize) { renderer->size = windowSize; // NOTE(doyle): Value to map a screen coordinate to NDC coordinate @@ -43,6 +42,7 @@ void renderer_init(Renderer *renderer, AssetManager *assetManager, const mat4 projection = mat4_ortho(0.0f, renderer->size.w, 0.0f, renderer->size.h, 0.0f, 1.0f); + for (i32 i = 0; i < shaderlist_count; i++) { renderer->shaderList[i] = asset_shaderGet(assetManager, i); @@ -54,6 +54,12 @@ void renderer_init(Renderer *renderer, AssetManager *assetManager, renderer->activeShaderId = renderer->shaderList[shaderlist_default]; GL_CHECK_ERROR(); +} + +void renderer_init(Renderer *renderer, AssetManager *assetManager, + MemoryArena_ *persistentArena, v2 windowSize) +{ + renderer_updateSize(renderer, assetManager, windowSize); /* Create buffers */ glGenVertexArrays(ARRAY_COUNT(renderer->vao), renderer->vao); diff --git a/src/String.c b/src/String.c index 9e63ee6..808c04e 100644 --- a/src/String.c +++ b/src/String.c @@ -1,120 +1,3 @@ #include "Dengine/String.h" #include "Dengine/MemoryArena.h" -/* - * +-------------------------------------+ - * | Header | C-String | Null Terminator | - * +-------------------------------------+ - * | - * +--> Functions return the c-string for compatibility with other - * string libraries - * - * Headers are retrieved using pointer arithmetric from the C string. These - * strings are typechecked by their own typedef char String. - */ - -typedef struct StringHeader -{ - i32 len; - - // NOTE(doyle): A string is stored as one contiguous chunk of memory. We - // don't use a pointer for storing the string as this'd require an extra - // 4 bytes to store the pointer, which we don't need if everything is - // contiguous. The string follows on from the len, and we return the address - // of the string to simulate a pointer. - String string; -} StringHeader; - -// TODO(doyle): string capacity- append if already enough space -INTERNAL StringHeader *string_getHeader(String *const string) -{ - StringHeader *result = NULL; - - // NOTE(doyle): C-String must be located at end of struct type for offset to - // be correct! We cannot just subtract the string-header since we start at - // the string ptr position - if (string) - { - i32 byteOffsetToHeader = sizeof(StringHeader) - sizeof(String *); - result = CAST(StringHeader *)((CAST(u8 *) string) - byteOffsetToHeader); - } - - return result; -} - -i32 string_len(String *const string) -{ - if (!string) return -1; - - StringHeader *header = string_getHeader(string); - i32 result = header->len; - return result; -} - -String *const string_append(MemoryArena_ *const arena, String *oldString, - char *appendString, i32 appendLen) - -{ - if (!oldString || !appendString || !arena) return oldString; - - /* Calculate size of new string */ - StringHeader *oldHeader = string_getHeader(oldString); - i32 newLen = oldHeader->len + appendLen; - String *newString = string_makeLen(arena, newLen); - - /* Append strings together */ - String *insertPtr = newString; - common_strncpy(insertPtr, oldString, oldHeader->len); - insertPtr += oldHeader->len; - common_strncpy(insertPtr, appendString, appendLen); - - /* Free old string */ - string_free(arena, oldString); - - return newString; -} - -void string_free(MemoryArena_ *arena, String *string) -{ - if (!string || !arena) return; - - StringHeader *header = string_getHeader(string); - i32 bytesToFree = sizeof(StringHeader) + header->len; - - common_memset((u8 *)header, 0, bytesToFree); - - // TODO(doyle): Mem free - // PLATFORM_MEM_FREE(arena, header, bytesToFree); - - string = NULL; -} - -String *const string_make(MemoryArena_ *const arena, char *string) -{ - if (!arena) return NULL; - - i32 len = common_strlen(string); - String *result = string_makeLen(arena, len); - common_strncpy(result, string, len); - - return result; -} - -String *const string_makeLen(MemoryArena_ *const arena, i32 len) -{ - if (!arena) return NULL; - - // NOTE(doyle): Allocate the string header size plus the len. But _note_ - // that StringHeader contains a single String character. This has - // a side-effect of already preallocating a byte for the null-terminating - // character. Whilst the len of a string counts up to the last character - // _not_ including null-terminator. - i32 bytesToAllocate = sizeof(StringHeader) + len; - void *chunk = memory_pushBytes(arena, bytesToAllocate * sizeof(u8)); - if (!chunk) return NULL; - - StringHeader *header = CAST(StringHeader *) chunk; - header->len = len; - return &header->string; -} - diff --git a/src/dengine.c b/src/dengine.c index 3e21409..3db408a 100644 --- a/src/dengine.c +++ b/src/dengine.c @@ -83,7 +83,7 @@ INTERNAL void mouseButtonCallback(GLFWwindow *window, int button, int action, { GameState *game = CAST(GameState *)(glfwGetWindowUserPointer(window)); - switch(button) + switch (button) { case GLFW_MOUSE_BUTTON_LEFT: processKey(&game->input.keys[keycode_mouseLeft], action); @@ -115,8 +115,73 @@ i32 main(void) glfwInit(); setGlfwWindowHints(); - i32 windowWidth = 1600; - i32 windowHeight = 900; + OptimalArrayV2 vidList = {0}; + common_optimalArrayV2Create(&vidList); + + i32 windowWidth = 0; + i32 windowHeight = 0; + { // Query Computer Video Resolutions + + i32 numMonitors; + GLFWmonitor **monitors = glfwGetMonitors(&numMonitors); + GLFWmonitor *primaryMonitor = monitors[0]; + + i32 numModes; + const GLFWvidmode *modes = glfwGetVideoModes(primaryMonitor, &numModes); + + i32 targetRefreshHz = 60; + f32 targetWindowRatio = 16.0f / 9.0f; + + i32 targetPixelDensity = 1280 * 720; + i32 minPixelDensityDelta = 100000000; + + printf("== Supported video modes ==\n"); + for (i32 i = 0; i < numModes; i++) + { + GLFWvidmode mode = modes[i]; + printf("width: %d, height: %d, rgb: %d, %d, %d, refresh: %d\n", + mode.width, mode.height, mode.redBits, mode.greenBits, + mode.blueBits, mode.refreshRate); + + if (mode.refreshRate == targetRefreshHz) + { + i32 result = common_optimalArrayV2Push( + &vidList, V2i(mode.width, mode.height)); + + if (result) + { + printf( + "common_optimalArrayV2Push(): Failed error code %d\n", + result); + ASSERT(INVALID_CODE_PATH); + } + + f32 sizeRatio = (f32)mode.width / (f32)mode.height; + f32 delta = targetWindowRatio - sizeRatio; + if (delta < 0.1f) + { + i32 pixelDensity = mode.width * mode.height; + i32 densityDelta = ABS((pixelDensity - targetPixelDensity)); + + if (densityDelta < minPixelDensityDelta) + { + minPixelDensityDelta = densityDelta; + windowWidth = mode.width; + windowHeight = mode.height; + } + } + } + } + printf("== ==\n"); + ASSERT(vidList.index > 0); + } + + if (windowWidth == 0 || windowHeight == 0) + { + // NOTE(doyle): In this case just fallback to some value we hope is safe + windowWidth = 800; + windowHeight = 600; + } GLFWwindow *window = glfwCreateWindow(windowWidth, windowHeight, "Dengine", NULL, NULL); @@ -168,7 +233,7 @@ i32 main(void) * INITIALISE GAME ******************* */ - Memory memory = {0}; + Memory memory = {0}; MemoryIndex persistentSize = MEGABYTES(32); MemoryIndex transientSize = MEGABYTES(64); @@ -181,12 +246,12 @@ i32 main(void) MemoryArena_ gameArena = {0}; memory_arenaInit(&gameArena, memory.persistent, memory.persistentSize); - GameState *gameState = MEMORY_PUSH_STRUCT(&gameArena, GameState); + GameState *gameState = MEMORY_PUSH_STRUCT(&gameArena, GameState); gameState->persistentArena = gameArena; glfwSetWindowUserPointer(window, CAST(void *)(gameState)); - { + { // Load game icon i32 width, height; char *iconPath = "data/textures/Asteroids/icon.png"; u8 *pixels = asset_imageLoad(&width, &height, NULL, iconPath, FALSE); @@ -198,6 +263,7 @@ i32 main(void) asset_imageFree(pixels); } } + gameState->input.resolutionList = &vidList; /* ******************* @@ -209,7 +275,6 @@ i32 main(void) #if 0 // TODO(doyle): Get actual monitor refresh rate - i32 monitorRefreshHz = 60; f32 targetSecondsPerFrame = 1.0f / CAST(f32)(monitorRefreshHz); #else // TODO(doyle): http://gafferongames.com/game-physics/fix-your-timestep/ @@ -234,7 +299,7 @@ i32 main(void) /* Swap the buffers */ glfwSwapBuffers(window); - f32 endTime = CAST(f32)glfwGetTime(); + f32 endTime = CAST(f32) glfwGetTime(); secondsElapsed = endTime - startTime; #if 0 @@ -277,8 +342,11 @@ i32 main(void) { if (menuState->newResolutionRequest) { - windowSize = menuState->newResolution; + i32 index = menuState->resStringDisplayIndex; + windowSize = gameState->input.resolutionList->ptr[index]; + glfwSetWindowSize(window, (i32)windowSize.w, (i32)windowSize.h); + glViewport(0, 0, (i32)windowSize.w, (i32)windowSize.h); menuState->newResolutionRequest = FALSE; } diff --git a/src/include/Dengine/Asteroid.h b/src/include/Dengine/Asteroid.h index 5ba5107..2d5834e 100644 --- a/src/include/Dengine/Asteroid.h +++ b/src/include/Dengine/Asteroid.h @@ -24,7 +24,8 @@ typedef struct GameWorldState MemoryArena_ entityArena; v2 *entityVertexListCache[entitytype_count]; - Entity entityList[1024]; + Entity *entityList; + i32 entityListSize; i32 entityIndex; u32 entityIdCounter; @@ -54,19 +55,23 @@ typedef struct GameWorldState typedef struct StartMenuState { + b32 init; + f32 startPromptBlinkTimer; b32 startPromptShow; - b32 optionsShow; + char **resStrings; + i32 numResStrings; + i32 resStringDisplayIndex; b32 newResolutionRequest; - v2 newResolution; + + b32 optionsShow; } StartMenuState; typedef struct GameState { b32 init; - enum AppState currState; void *appStateData[appstate_count]; @@ -81,8 +86,7 @@ typedef struct GameState UiState uiState; } GameState; -#define ASTEROID_GET_STATE_DATA(state, type) \ - (type *)asteroid_getStateData_(state, appstate_##type) +#define ASTEROID_GET_STATE_DATA(state, type) (type *)asteroid_getStateData_(state, appstate_##type) void *asteroid_getStateData_(GameState *state, enum AppState appState); void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, diff --git a/src/include/Dengine/Common.h b/src/include/Dengine/Common.h index 5b5e2bb..56a9f77 100644 --- a/src/include/Dengine/Common.h +++ b/src/include/Dengine/Common.h @@ -15,6 +15,9 @@ typedef float f32; typedef double f64; typedef size_t MemoryIndex; +typedef char String; + +typedef struct MemoryArena MemoryArena_; #define TRUE 1 #define FALSE 0 @@ -34,6 +37,44 @@ typedef size_t MemoryIndex; #define DENGINE_DEBUG +#include "Dengine/Math.h" + +/* + NOTE(doyle): Small sized optimised dynamic array that grows as required. The + array uses the stack first, only if it runs out of space does it rely on + memory allocated from the machine. + + The array->ptr is initially set to fast storage. Once we are out of space + we allocate space on the heap for the ptr and copy over the elements in + fast storage. + + The default behaviour expands the array storage by the size of fastStorage. + */ + +enum OptimalArrayError +{ + optimalarrayerror_out_of_memory = 1, + optimalarrayerror_count, +}; + +typedef struct OptimalArrayV2 +{ + v2 fastStorage[16]; + v2 *ptr; + i32 index; + i32 size; +} OptimalArrayV2; +void common_optimalArrayV2Create(OptimalArrayV2 *array); +i32 common_optimalArrayV2Push(OptimalArrayV2 *array, v2 data); +void common_optimalArrayV2Destroy(OptimalArrayV2 *array); + +i32 common_stringLen(String *const string); +String *const common_stringAppend(MemoryArena_ *const arena, String *oldString, + String *appendString, i32 appendLen); +void common_stringFree(MemoryArena_ *arena, String *string); +String *const common_stringMake(MemoryArena_ *const arena, char *string); +String *const common_stringMakeLen(MemoryArena_ *const arena, i32 len); + i32 common_strlen(const char *const string); i32 common_strcmp(const char *a, const char *b); void common_strncat(char *dest, const char *src, i32 numChars); diff --git a/src/include/Dengine/Debug.h b/src/include/Dengine/Debug.h index edac015..e161e50 100644 --- a/src/include/Dengine/Debug.h +++ b/src/include/Dengine/Debug.h @@ -20,7 +20,7 @@ enum DebugCount debugcount_num, }; -void debug_init(MemoryArena_ *arena, v2 windowSize, Font font); +void debug_init(v2 windowSize, Font font); #define DEBUG_RECURSIVE_PRINT_XML_TREE(sig) debug_recursivePrintXmlTree(sig, 1) void debug_recursivePrintXmlTree(XmlNode *root, i32 levelsDeep); diff --git a/src/include/Dengine/Math.h b/src/include/Dengine/Math.h index 1a20ee0..cb9e9c4 100644 --- a/src/include/Dengine/Math.h +++ b/src/include/Dengine/Math.h @@ -2,6 +2,7 @@ #define DENGINE_MATH_H #include + #include "Dengine/Common.h" #define MATH_PI 3.14159265359f diff --git a/src/include/Dengine/Platform.h b/src/include/Dengine/Platform.h index 904c1a2..6f130e1 100644 --- a/src/include/Dengine/Platform.h +++ b/src/include/Dengine/Platform.h @@ -143,6 +143,8 @@ typedef struct KeyState typedef struct InputBuffer { + OptimalArrayV2 *resolutionList; + v2 mouseP; KeyState keys[keycode_count]; } InputBuffer; diff --git a/src/include/Dengine/Renderer.h b/src/include/Dengine/Renderer.h index 7285712..905ce28 100644 --- a/src/include/Dengine/Renderer.h +++ b/src/include/Dengine/Renderer.h @@ -84,6 +84,7 @@ typedef struct Renderer i32 groupCapacity; } Renderer; +void renderer_updateSize(Renderer *renderer, AssetManager *assetManager, v2 windowSize); void renderer_init(Renderer *renderer, AssetManager *assetManager, MemoryArena_ *persistentArena, v2 windowSize); diff --git a/src/include/Dengine/String.h b/src/include/Dengine/String.h index 8671745..1c50e4b 100644 --- a/src/include/Dengine/String.h +++ b/src/include/Dengine/String.h @@ -1,16 +1,5 @@ #ifndef DENGINE_STRING_H #define DENGINE_STRING_H -#include "Dengine/Common.h" - -typedef struct MemoryArena MemoryArena_; -typedef char String; - -i32 string_len(String *const string); -String *const string_append(MemoryArena_ *const arena, String *oldString, - String *appendString, i32 appendLen); -void string_free(MemoryArena_ *arena, String *string); -String *const string_make(MemoryArena_ *const arena, char *string); -String *const string_makeLen(MemoryArena_ *const arena, i32 len); #endif