From 46629a489a2943250db6282cd2f6117e2ce4bee9 Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Fri, 8 Jul 2016 02:45:37 +1000 Subject: [PATCH] Add debug rendering, fix misaligned pos render bug Rendering using a non-default quad specifies normalised coordinates relative to the frame buffer size. Hence each render of these frames should origin from (0,0) any other value is invalid. --- Dengine.vcxproj | 1 + Dengine.vcxproj.filters | 3 + src/AssetManager.c | 4 +- src/Common.c | 8 ++ src/Renderer.c | 146 +++++++++++++++++++- src/WorldTraveller.c | 122 +++------------- src/include/Dengine/AssetManager.h | 8 ++ src/include/Dengine/Common.h | 1 + src/include/Dengine/Renderer.h | 27 +++- src/include/WorldTraveller/WorldTraveller.h | 1 + 10 files changed, 210 insertions(+), 111 deletions(-) create mode 100644 src/Common.c diff --git a/Dengine.vcxproj b/Dengine.vcxproj index b6f3ba0..1e69025 100644 --- a/Dengine.vcxproj +++ b/Dengine.vcxproj @@ -121,6 +121,7 @@ + diff --git a/Dengine.vcxproj.filters b/Dengine.vcxproj.filters index 36489f8..4f6acca 100644 --- a/Dengine.vcxproj.filters +++ b/Dengine.vcxproj.filters @@ -39,6 +39,9 @@ Source Files + + Source Files + diff --git a/src/AssetManager.c b/src/AssetManager.c index eb4ce07..3d76b72 100644 --- a/src/AssetManager.c +++ b/src/AssetManager.c @@ -9,7 +9,7 @@ #include "Dengine/Platform.h" #include "Dengine/AssetManager.h" -#define WT_RENDER_FONT_FILE +//#define WT_RENDER_FONT_FILE #ifdef WT_RENDER_FONT_FILE #define STB_IMAGE_WRITE_IMPLEMENTATION #include @@ -136,7 +136,7 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath) CAST(GlyphBitmap *) calloc(numGlyphs, sizeof(GlyphBitmap)); v2i largestGlyphDimension = V2i(0, 0); - const f32 targetFontHeight = 32.0f; + const f32 targetFontHeight = 20.0f; f32 scaleY = stbtt_ScaleForPixelHeight(&fontInfo, targetFontHeight); i32 ascent, descent, lineGap; diff --git a/src/Common.c b/src/Common.c new file mode 100644 index 0000000..00c26c7 --- /dev/null +++ b/src/Common.c @@ -0,0 +1,8 @@ +#include "Dengine\Common.h" + +i32 common_strlen(const char *const string) +{ + i32 result = 0; + while (string[result]) result++; + return result; +} diff --git a/src/Renderer.c b/src/Renderer.c index 4b8038e..9531e06 100644 --- a/src/Renderer.c +++ b/src/Renderer.c @@ -1,10 +1,140 @@ +#include "Dengine/Platform.h" #include "Dengine/OpenGL.h" #include "Dengine/Renderer.h" #define RENDER_BOUNDING_BOX FALSE -void renderer_entity(Renderer *renderer, Entity *entity, f32 rotate, v3 color) +DebugRenderer debugRenderer = {0}; + +INTERNAL void updateBufferObject(Renderer *const renderer, + RenderQuad *const quads, const i32 numQuads) { + // TODO(doyle): We assume that vbo and vao are assigned + const i32 numVertexesInQuad = 4; + renderer->numVertexesInVbo = numQuads * numVertexesInQuad; + + glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo); + glBufferData(GL_ARRAY_BUFFER, numQuads * sizeof(RenderQuad), quads, + GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +#if 0 +void renderer_backgroundTiles(Renderer *const renderer, const v2 tileSize, + World *const world, TexAtlas *const atlas, + Texture *const tex) +{ + RenderQuad worldQuads[ARRAY_COUNT(world->tiles)] = {0}; + i32 quadIndex = 0; + + for (i32 i = 0; i < ARRAY_COUNT(world->tiles); i++) + { + Tile tile = world->tiles[i]; + v2 tilePosInPixel = v2_scale(tile.pos, tileSize.x); + + if ((tilePosInPixel.x < renderer->size.w && tilePosInPixel.x >= 0) && + (tilePosInPixel.y < renderer->size.h && tilePosInPixel.y >= 0)) + { + const v4 texRect = atlas->texRect[terraincoords_ground]; + const v4 tileRect = getRect(tilePosInPixel, tileSize); + + RenderQuad tileQuad = + renderer_createQuad(renderer, tileRect, texRect, tex); + worldQuads[quadIndex++] = tileQuad; + } + } + + updateBufferObject(renderer, worldQuads, quadIndex); + renderer_object(renderer, V2(0.0f, 0.0f), renderer->size, 0.0f, + V3(0, 0, 0), tex); +} +#endif + +void renderer_string(Renderer *const renderer, Font *const font, + const char *const string, v2 pos, f32 rotate, + v3 color) +{ + i32 quadIndex = 0; + i32 strLen = common_strlen(string); + RenderQuad *stringQuads = CAST(RenderQuad *)calloc(strLen, sizeof(RenderQuad)); + + f32 baseline = pos.y; + for (i32 i = 0; i < strLen; i++) + { + // NOTE(doyle): Atlas packs fonts tightly, so offset the codepoint to + // its actual atlas index, i.e. we skip the first 31 glyphs + i32 codepoint = string[i]; + i32 relativeIndex = codepoint - font->codepointRange.x; + CharMetrics charMetric = font->charMetrics[relativeIndex]; + pos.y = baseline - charMetric.offset.y; + + const v4 charRectOnScreen = getRect( + pos, V2(CAST(f32) font->maxSize.w, CAST(f32) font->maxSize.h)); + + pos.x += charMetric.advance; + + /* Get texture out */ + v4 charTexRect = font->atlas->texRect[relativeIndex]; + renderer_flipTexCoord(&charTexRect, FALSE, TRUE); + + RenderQuad charQuad = renderer_createQuad(renderer, charRectOnScreen, + charTexRect, font->tex); + stringQuads[quadIndex++] = charQuad; + } + + // NOTE(doyle): We render at the renderer's size because we create quads + // relative to the window size, hence we also render at the origin since + // we're rendering a window sized buffer + updateBufferObject(renderer, stringQuads, quadIndex); + renderer_object(renderer, V2(0.0f, 0.0f), renderer->size, rotate, color, + font->tex); + free(stringQuads); + +} + +void renderer_debugString(Renderer *const renderer, Font *const font, + const char *const string) +{ + /* Intialise debug object */ + if (!debugRenderer.init) + { + debugRenderer.stringPos = + V2(0.0f, renderer->size.y - + (1.8f * asset_getVFontSpacing(font->metrics))); + debugRenderer.init = TRUE; + } + + f32 rotate = 0; + v3 color = V3(0, 0, 0); + renderer_string(renderer, font, string, debugRenderer.stringPos, rotate, + color); + debugRenderer.stringPos.y -= (0.9f * asset_getVFontSpacing(font->metrics)); +} + +void renderer_entity(Renderer *renderer, Entity *entity, f32 dt, f32 rotate, + v3 color) +{ + SpriteAnim *anim = &entity->anim[entity->currAnimIndex]; + v4 texRect = anim->rect[anim->currRectIndex]; + + anim->currDuration -= dt; + if (anim->currDuration <= 0.0f) + { + anim->currRectIndex++; + anim->currRectIndex = anim->currRectIndex % anim->numRects; + texRect = anim->rect[anim->currRectIndex]; + anim->currDuration = anim->duration; + } + + if (entity->direction == direction_east) + { + // NOTE(doyle): Flip the x coordinates to flip the tex + renderer_flipTexCoord(&texRect, TRUE, FALSE); + } + + RenderQuad entityQuad = + renderer_createDefaultQuad(renderer, texRect, entity->tex); + updateBufferObject(renderer, &entityQuad, 1); renderer_object(renderer, entity->pos, entity->size, rotate, color, entity->tex); } @@ -67,15 +197,19 @@ RenderQuad renderer_createQuad(Renderer *renderer, v4 quadRect, v4 texRect, quadRectNdc.e[3] *= renderer->vertexNdcFactor.h; v2 texNdcFactor = V2(1.0f / tex->width, 1.0f / tex->height); - v4 texRectNdc = texRect; + v4 texRectNdc = texRect; texRectNdc.e[0] *= texNdcFactor.w; texRectNdc.e[1] *= texNdcFactor.h; texRectNdc.e[2] *= texNdcFactor.w; texRectNdc.e[3] *= texNdcFactor.h; - result.vertex[0] = V4(quadRectNdc.x, quadRectNdc.y, texRectNdc.x, texRectNdc.y); // Top left - result.vertex[1] = V4(quadRectNdc.x, quadRectNdc.w, texRectNdc.x, texRectNdc.w); // Bottom left - result.vertex[2] = V4(quadRectNdc.z, quadRectNdc.y, texRectNdc.z, texRectNdc.y); // Top right - result.vertex[3] = V4(quadRectNdc.z, quadRectNdc.w, texRectNdc.z, texRectNdc.w); // Bottom right + result.vertex[0] = V4(quadRectNdc.x, quadRectNdc.y, texRectNdc.x, + texRectNdc.y); // Top left + result.vertex[1] = V4(quadRectNdc.x, quadRectNdc.w, texRectNdc.x, + texRectNdc.w); // Bottom left + result.vertex[2] = V4(quadRectNdc.z, quadRectNdc.y, texRectNdc.z, + texRectNdc.y); // Top right + result.vertex[3] = V4(quadRectNdc.z, quadRectNdc.w, texRectNdc.z, + texRectNdc.w); // Bottom right return result; } diff --git a/src/WorldTraveller.c b/src/WorldTraveller.c index 871631f..f89baa2 100644 --- a/src/WorldTraveller.c +++ b/src/WorldTraveller.c @@ -6,19 +6,6 @@ //choose to load assets outside of WorldTraveller! #include -INTERNAL void updateBufferObject(Renderer *const renderer, - RenderQuad *const quads, const i32 numQuads) -{ - // TODO(doyle): We assume that vbo and vao are assigned - const i32 numVertexesInQuad = 4; - renderer->numVertexesInVbo = numQuads * numVertexesInQuad; - - glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo); - glBufferData(GL_ARRAY_BUFFER, numQuads * sizeof(RenderQuad), quads, - GL_STREAM_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - void worldTraveller_gameInit(GameState *state, v2i windowSize) { AssetManager *assetManager = &state->assetManager; @@ -143,7 +130,6 @@ void worldTraveller_gameInit(GameState *state, v2i windowSize) shader_uniformSetMat4fv(renderer->shader, "projection", projection); glCheckError(); - /* Create buffers */ glGenVertexArrays(1, &renderer->vao); glGenBuffers(1, &renderer->vbo); @@ -165,6 +151,7 @@ void worldTraveller_gameInit(GameState *state, v2i windowSize) glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); glCheckError(); + } INTERNAL void parseInput(GameState *state, const f32 dt) @@ -308,106 +295,43 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt) AssetManager *assetManager = &state->assetManager; Renderer *renderer = &state->renderer; + /* Render background tiles */ +#if 0 World *const world = &state->world[state->currWorldIndex]; TexAtlas *const worldAtlas = asset_getTextureAtlas(assetManager, world->texType); Texture *const worldTex = asset_getTexture(assetManager, world->texType); + v2 tileSize = (CAST(f32)state->tileSize.w, CAST(f32)state->tileSize.h); + renderer_backgroundTiles(&state->renderer, tileSize, world, atlas, tex); +#endif - f32 texNdcFactor = 1.0f / MAX_TEXTURE_SIZE; - - RenderQuad worldQuads[ARRAY_COUNT(world->tiles)] = {0}; - i32 quadIndex = 0; - - /* Render background tiles */ - const v2 tileSize = V2(CAST(f32) state->tileSize, CAST(f32) state->tileSize); - for (i32 i = 0; i < ARRAY_COUNT(world->tiles); i++) - { - Tile tile = world->tiles[i]; - v2 tilePosInPixel = v2_scale(tile.pos, tileSize.x); - - if ((tilePosInPixel.x < renderer->size.w && tilePosInPixel.x >= 0) && - (tilePosInPixel.y < renderer->size.h && tilePosInPixel.y >= 0)) - { - const v4 texRect = worldAtlas->texRect[terraincoords_ground]; - const v4 tileRect = getRect(tilePosInPixel, tileSize); - - RenderQuad tileQuad = renderer_createQuad( - &state->renderer, tileRect, texRect, worldTex); - worldQuads[quadIndex++] = tileQuad; - } - } - - updateBufferObject(renderer, worldQuads, quadIndex); - renderer_object(renderer, V2(0.0f, 0.0f), renderer->size, 0.0f, - V3(0, 0, 0), worldTex); - +#ifdef WT_DEBUG + Entity *hero = &state->entityList[state->heroIndex]; Font *font = &assetManager->font; - char *string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - i32 strLen = 52; - quadIndex = 0; - RenderQuad *stringQuads = CAST(RenderQuad *)calloc(strLen, sizeof(RenderQuad)); + char textBuffer[256]; + snprintf(textBuffer, ARRAY_COUNT(textBuffer), "Hero Pos: %.2f,%.2f", + hero->pos.x, hero->pos.y); + renderer_debugString(&state->renderer, font, textBuffer); - f32 baseline = 100.0f; - f32 xPosOnScreen = 20.0f; - f32 yPosOnScreen = baseline; - for (i32 i = 0; i < strLen; i++) - { - // NOTE(doyle): Atlas packs fonts tightly, so offset the codepoint to - // its actual atlas index, i.e. we skip the first 31 glyphs - i32 codepoint = string[i]; - i32 relativeIndex = codepoint - font->codepointRange.x; - CharMetrics charMetric = font->charMetrics[relativeIndex]; - yPosOnScreen = baseline - charMetric.offset.y; + snprintf(textBuffer, ARRAY_COUNT(textBuffer), "Hero dPos: %.2f,%.2f", + hero->dPos.x, hero->dPos.y); + renderer_debugString(&state->renderer, font, textBuffer); - const v4 charRectOnScreen = getRect( - V2(xPosOnScreen, yPosOnScreen), - V2(CAST(f32) font->maxSize.w, CAST(f32) font->maxSize.h)); - - xPosOnScreen += charMetric.advance; - - /* Get texture out */ - v4 charTexRect = font->atlas->texRect[relativeIndex]; - renderer_flipTexCoord(&charTexRect, FALSE, TRUE); - - RenderQuad charQuad = renderer_createQuad( - &state->renderer, charRectOnScreen, charTexRect, font->tex); - stringQuads[quadIndex++] = charQuad; - } - - updateBufferObject(&state->renderer, stringQuads, quadIndex); - renderer_object(&state->renderer, V2(0.0f, 100.0f), renderer->size, 0.0f, - V3(0, 0, 0), font->tex); - free(stringQuads); + snprintf(textBuffer, ARRAY_COUNT(textBuffer), "FreeEntityIndex: %d", + state->freeEntityIndex); + renderer_debugString(&state->renderer, font, textBuffer); +#endif /* Render entities */ ASSERT(state->freeEntityIndex < ARRAY_COUNT(state->entityList)); for (i32 i = 0; i < state->freeEntityIndex; i++) { - Entity *const entity = &state->entityList[i]; - SpriteAnim *anim = &entity->anim[entity->currAnimIndex]; - - v4 texRect = anim->rect[anim->currRectIndex]; - anim->currDuration -= dt; - if (anim->currDuration <= 0.0f) - { - anim->currRectIndex++; - anim->currRectIndex = anim->currRectIndex % anim->numRects; - texRect = anim->rect[anim->currRectIndex]; - anim->currDuration = anim->duration; - } - - if (entity->direction == direction_east) - { - // NOTE(doyle): Flip the x coordinates to flip the tex - renderer_flipTexCoord(&texRect, TRUE, FALSE); - } - - RenderQuad entityQuad = - renderer_createDefaultQuad(&state->renderer, texRect, entity->tex); - updateBufferObject(&state->renderer, &entityQuad, 1); - renderer_entity(&state->renderer, entity, 0.0f, V3(0, 0, 0)); + Entity *const entity = &state->entityList[i]; + renderer_entity(&state->renderer, entity, dt, 0.0f, V3(0, 0, 0)); } // TODO(doyle): Clean up lines // Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); } + + debugRenderer.init = FALSE; } diff --git a/src/include/Dengine/AssetManager.h b/src/include/Dengine/AssetManager.h index b2ead35..1cc328e 100644 --- a/src/include/Dengine/AssetManager.h +++ b/src/include/Dengine/AssetManager.h @@ -90,4 +90,12 @@ const i32 asset_loadShaderFiles(AssetManager *assetManager, const enum ShaderList type); const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath); + +inline i32 asset_getVFontSpacing(FontMetrics metrics) +{ + i32 result = + metrics.ascent - metrics.descent + metrics.lineGap; + return result; +} + #endif diff --git a/src/include/Dengine/Common.h b/src/include/Dengine/Common.h index 893a710..2fc2742 100644 --- a/src/include/Dengine/Common.h +++ b/src/include/Dengine/Common.h @@ -26,5 +26,6 @@ typedef double f64; #define CAST(type) (type) #define ASSERT(expr) if(!(expr)) { *(int *)0 = 0; } +i32 common_strlen(const char *const string); #endif diff --git a/src/include/Dengine/Renderer.h b/src/include/Dengine/Renderer.h index 473100c..3b51598 100644 --- a/src/include/Dengine/Renderer.h +++ b/src/include/Dengine/Renderer.h @@ -1,9 +1,8 @@ #ifndef DENGINE_RENDERER_H #define DENGINE_RENDERER_H -#include "Dengine/Math.h" #include "Dengine/Entity.h" -#include "Dengine/Shader.h" +#include "Dengine/AssetManager.h" typedef struct Renderer { @@ -13,15 +12,35 @@ typedef struct Renderer i32 numVertexesInVbo; v2 vertexNdcFactor; v2 size; - } Renderer; +typedef struct DebugRenderer +{ + b32 init; + v2 stringPos; + +} DebugRenderer; + typedef struct RenderQuad { v4 vertex[4]; } RenderQuad; -void renderer_entity(Renderer *renderer, Entity *entity, f32 rotate, +extern DebugRenderer debugRenderer; + +#if 0 +void renderer_backgroundTiles(Renderer *const renderer, const v2 tileSize, + World *const world, TexAtlas *const atlasTexture, + Texture *const tex); +#endif + +void renderer_string(Renderer *const renderer, Font *const font, + const char *const string, v2 pos, f32 rotate, v3 color); + +void renderer_debugString(Renderer *const renderer, Font *const font, + const char *const string); + +void renderer_entity(Renderer *renderer, Entity *entity, f32 dt, f32 rotate, v3 color); void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v3 color, diff --git a/src/include/WorldTraveller/WorldTraveller.h b/src/include/WorldTraveller/WorldTraveller.h index cf33a47..4c41f65 100644 --- a/src/include/WorldTraveller/WorldTraveller.h +++ b/src/include/WorldTraveller/WorldTraveller.h @@ -45,6 +45,7 @@ typedef struct GameState AssetManager assetManager; } GameState; + void worldTraveller_gameInit(GameState *state, v2i windowSize); void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt); #endif