From d8ee46611f92a97348cbe63d2921fd1e1b2e60bf Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Wed, 29 Jun 2016 20:44:35 +1000 Subject: [PATCH] Renderer calculates NDC coordinates from screen coords --- src/AssetManager.c | 39 +++++---- src/Renderer.c | 27 ++++++ src/WorldTraveller.c | 93 ++++++++------------- src/dengine.c | 21 ++--- src/include/Dengine/AssetManager.h | 11 ++- src/include/Dengine/Renderer.h | 25 +++--- src/include/WorldTraveller/WorldTraveller.h | 3 +- 7 files changed, 108 insertions(+), 111 deletions(-) diff --git a/src/AssetManager.c b/src/AssetManager.c index 84bafb8..370d00d 100644 --- a/src/AssetManager.c +++ b/src/AssetManager.c @@ -120,8 +120,10 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath) stbtt_InitFont(&fontInfo, fontFileRead.buffer, stbtt_GetFontOffsetForIndex(fontFileRead.buffer, 0)); - assetManager->codepointRange = V2i(32, 127); - v2i codepointRange = assetManager->codepointRange; + Font *font = &assetManager->font; + font->codepointRange = V2i(32, 127); + + v2i codepointRange = font->codepointRange; const i32 numGlyphs = codepointRange.y - codepointRange.x; i32 glyphIndex = 0; @@ -178,6 +180,7 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath) "unoptimal\n"); } #endif + } /* @@ -196,10 +199,12 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath) if ((largestGlyphDimension.h & 1) == 1) largestGlyphDimension.h += 1; - i32 glyphsPerRow = (MAX_TEXTURE_SIZE / largestGlyphDimension.w) + 1; + font->charSize = largestGlyphDimension; + + i32 glyphsPerRow = (MAX_TEXTURE_SIZE / font->charSize.w) + 1; #ifdef WT_DEBUG - i32 glyphsPerCol = MAX_TEXTURE_SIZE / largestGlyphDimension.h; + i32 glyphsPerCol = MAX_TEXTURE_SIZE / font->charSize.h; if ((glyphsPerRow * glyphsPerCol) <= numGlyphs) { printf( @@ -209,7 +214,6 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath) } #endif -#if 1 u32 *fontBitmap = CAST(u32 *)calloc( squared(TARGET_TEXTURE_SIZE) * TARGET_BYTES_PER_PIXEL, sizeof(u32)); const i32 pitch = MAX_TEXTURE_SIZE * TARGET_BYTES_PER_PIXEL; @@ -237,20 +241,18 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath) { TexAtlas *fontAtlas = &assetManager->texAtlas[texlist_font]; #ifdef WT_DEBUG - printf("codepoint: %d\n", activeGlyph.codepoint); - printf("atlasIndex: %d\n", atlasIndex); ASSERT(activeGlyph.codepoint < ARRAY_COUNT(fontAtlas->texRect)); #endif - v2 origin = V2(CAST(f32)(glyphIndex * largestGlyphDimension.w), + v2 origin = V2(CAST(f32)(glyphIndex * font->charSize.w), CAST(f32) row); fontAtlas->texRect[atlasIndex++] = - getRect(origin, V2(CAST(f32) largestGlyphDimension.w, - CAST(f32) largestGlyphDimension.h)); + getRect(origin, V2(CAST(f32) font->charSize.w, + CAST(f32) font->charSize.h)); } /* Copy over exactly one row of pixels */ - i32 numPixelsToPad = largestGlyphDimension.w; + i32 numPixelsToPad = font->charSize.w; if (verticalPixelsBlitted < activeGlyph.dimensions.h) { const i32 srcPitch = @@ -269,13 +271,13 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath) * pointer by the remaining distance */ numPixelsToPad = - largestGlyphDimension.w - activeGlyph.dimensions.w; + font->charSize.w - activeGlyph.dimensions.w; } destRow += numPixelsToPad; } /* A row of glyphs has been fully formed on the atlas */ - if (verticalPixelsBlitted++ >= largestGlyphDimension.h) + if (verticalPixelsBlitted++ >= font->charSize.h) { verticalPixelsBlitted = 0; startingGlyphIndex += glyphsPerRow; @@ -294,14 +296,11 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath) } Texture tex = genTexture(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4, - CAST(u8 *) fontBitmap); + CAST(u8 *)fontBitmap); assetManager->textures[texlist_font] = tex; -#else - i32 letter = 1; - Texture tex = genTexture(glyphBitmaps[letter].dimensions.w, glyphBitmaps[letter].dimensions.h, 4, - CAST(u8 *)glyphBitmaps[letter].pixels); - assetManager.textures[texlist_font] = tex; -#endif + + font->tex = &assetManager->textures[texlist_font]; + font->atlas = &assetManager->texAtlas[texlist_font]; for (i32 i = 0; i < numGlyphs; i++) free(glyphBitmaps[i].pixels); diff --git a/src/Renderer.c b/src/Renderer.c index a43f5e9..4d1e50c 100644 --- a/src/Renderer.c +++ b/src/Renderer.c @@ -43,3 +43,30 @@ void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v3 color, glBindTexture(GL_TEXTURE_2D, 0); glCheckError(); } + +RenderQuad renderer_createQuad(Renderer *renderer, v4 quadRect, v4 texRect, + Texture *tex) +{ + // NOTE(doyle): Draws a series of triangles (three-sided polygons) using + // vertices v0, v1, v2, then v2, v1, v3 (note the order) + RenderQuad result = {0}; + + v4 quadRectNdc = quadRect; + quadRectNdc.e[0] *= renderer->vertexNdcFactor.w; + quadRectNdc.e[1] *= renderer->vertexNdcFactor.h; + quadRectNdc.e[2] *= renderer->vertexNdcFactor.w; + quadRectNdc.e[3] *= renderer->vertexNdcFactor.h; + + v2 texNdcFactor = V2(1.0f / tex->width, 1.0f / tex->height); + 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 + return result; +} diff --git a/src/WorldTraveller.c b/src/WorldTraveller.c index 09ef50d..d90dfc3 100644 --- a/src/WorldTraveller.c +++ b/src/WorldTraveller.c @@ -19,7 +19,7 @@ INTERNAL void updateBufferObject(Renderer *const renderer, glBindBuffer(GL_ARRAY_BUFFER, 0); } -void worldTraveller_gameInit(GameState *state) +void worldTraveller_gameInit(GameState *state, v2i windowSize) { AssetManager *assetManager = &state->assetManager; /* Initialise assets */ @@ -131,14 +131,19 @@ void worldTraveller_gameInit(GameState *state) /* Init renderer */ Renderer *renderer = &state->renderer; + renderer->size = V2(CAST(f32)windowSize.x, CAST(f32)windowSize.y); + // 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, CAST(f32) state->width, 0.0f, - CAST(f32) state->height, 0.0f, 1.0f); + 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); glCheckError(); + /* Create buffers */ glGenVertexArrays(1, &renderer->vao); glGenBuffers(1, &renderer->vbo); @@ -301,8 +306,9 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt) glCheckError(); AssetManager *assetManager = &state->assetManager; + Renderer *renderer = &state->renderer; - World *const world = &state->world[state->currWorldIndex]; + World *const world = &state->world[state->currWorldIndex]; TexAtlas *const worldAtlas = asset_getTextureAtlas(assetManager, world->texType); Texture *const worldTex = asset_getTexture(assetManager, world->texType); @@ -312,119 +318,90 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt) 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); - const v2 vertexNdcFactor = V2(1.0f / state->width, 1.0f / state->height); 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 < state->width && tilePosInPixel.x >= 0) && - (tilePosInPixel.y < state->height && tilePosInPixel.y >= 0)) + if ((tilePosInPixel.x < renderer->size.w && tilePosInPixel.x >= 0) && + (tilePosInPixel.y < renderer->size.h && tilePosInPixel.y >= 0)) { - const v4 tileTexRect = worldAtlas->texRect[terraincoords_ground]; - const v4 texRectNdc = v4_scale(tileTexRect, texNdcFactor); - + const v4 texRect = worldAtlas->texRect[terraincoords_ground]; const v4 tileRect = getRect(tilePosInPixel, tileSize); - v4 tileRectNdc = tileRect; - tileRectNdc.e[0] *= vertexNdcFactor.w; - tileRectNdc.e[1] *= vertexNdcFactor.h; - tileRectNdc.e[2] *= vertexNdcFactor.w; - tileRectNdc.e[3] *= vertexNdcFactor.h; - - RenderQuad tileQuad = renderer_createQuad(tileRectNdc, texRectNdc); + RenderQuad tileQuad = renderer_createQuad( + &state->renderer, tileRect, texRect, worldTex); worldQuads[quadIndex++] = tileQuad; } } - const v2 screenSize = V2(CAST(f32)state->width, CAST(f32)state->height); - updateBufferObject(&state->renderer, worldQuads, quadIndex); - renderer_object(&state->renderer, V2(0.0f, 0.0f), screenSize, 0.0f, + updateBufferObject(renderer, worldQuads, quadIndex); + renderer_object(renderer, V2(0.0f, 0.0f), renderer->size, 0.0f, V3(0, 0, 0), worldTex); - /* Render font sheet */ - Texture *font = asset_getTexture(assetManager, texlist_font); - v4 fontTexRect = V4(0.0f, 1.0f, 1.0f, 0.0); - RenderQuad fontQuad = renderer_createDefaultQuad(fontTexRect); - updateBufferObject(&state->renderer, &fontQuad, 1); - renderer_object(&state->renderer, V2(300.0f, -300.0f), - V2(CAST(f32)font->width, CAST(f32)font->height), 0.0f, - V3(0, 0, 0), font); - + Font *font = &assetManager->font; char *string = "hello world"; - i32 strLen = 11; quadIndex = 0; RenderQuad *stringQuads = CAST(RenderQuad *)calloc(strLen, sizeof(RenderQuad)); - TexAtlas *fontAtlas = asset_getTextureAtlas(assetManager, texlist_font); - v2 eachCharSize = getRectSize(fontAtlas->texRect[0]); f32 xPosOnScreen = 20.0f; 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 atlasIndex = string[i] - assetManager->codepointRange.x; + i32 atlasIndex = string[i] - font->codepointRange.x; - const v4 charTexRect = fontAtlas->texRect[atlasIndex]; - v4 charTexRectNdc = v4_scale(charTexRect, texNdcFactor); - renderer_flipTexCoord(&charTexRectNdc, FALSE, TRUE); + v4 charTexRect = font->atlas->texRect[atlasIndex]; + renderer_flipTexCoord(&charTexRect, FALSE, TRUE); const v4 charRectOnScreen = - getRect(V2(xPosOnScreen, 100.0f), eachCharSize); - xPosOnScreen += eachCharSize.w; - v4 charRectOnScreenNdc = charRectOnScreen; + getRect(V2(xPosOnScreen, 100.0f), + V2(CAST(f32) font->charSize.w, CAST(f32) font->charSize.w)); + xPosOnScreen += font->charSize.w; - charRectOnScreenNdc.e[0] *= vertexNdcFactor.w; - charRectOnScreenNdc.e[1] *= vertexNdcFactor.h; - charRectOnScreenNdc.e[2] *= vertexNdcFactor.w; - charRectOnScreenNdc.e[3] *= vertexNdcFactor.h; - - RenderQuad charQuad = - renderer_createQuad(charRectOnScreenNdc, charTexRectNdc); + 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), screenSize, 0.0f, - V3(0, 0, 0), font); + renderer_object(&state->renderer, V2(0.0f, 100.0f), renderer->size, 0.0f, + V3(0, 0, 0), font->tex); free(stringQuads); /* Render entities */ - // NOTE(doyle): Factor to normalise sprite sheet rect coords to -1, 1 - Entity *const hero = &state->entityList[state->heroIndex]; - 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 currFrameRect = anim->rect[anim->currRectIndex]; + v4 texRect = anim->rect[anim->currRectIndex]; anim->currDuration -= dt; if (anim->currDuration <= 0.0f) { anim->currRectIndex++; anim->currRectIndex = anim->currRectIndex % anim->numRects; - currFrameRect = anim->rect[anim->currRectIndex]; + texRect = anim->rect[anim->currRectIndex]; anim->currDuration = anim->duration; } - v4 texRectNdc = v4_scale(currFrameRect, texNdcFactor); if (entity->direction == direction_east) { // NOTE(doyle): Flip the x coordinates to flip the tex - renderer_flipTexCoord(&texRectNdc, TRUE, FALSE); + renderer_flipTexCoord(&texRect, TRUE, FALSE); } - RenderQuad quad = renderer_createDefaultQuad(texRectNdc); - updateBufferObject(&state->renderer, &quad, 1); + 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)); } // TODO(doyle): Clean up lines // Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); } } - diff --git a/src/dengine.c b/src/dengine.c index e4e5fef..3a2d575 100644 --- a/src/dengine.c +++ b/src/dengine.c @@ -35,10 +35,10 @@ int main() glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); - v2 windowSize = V2(1600.0f, 900.0f); + v2i windowSize = V2i(1600, 900); - GLFWwindow *window = glfwCreateWindow( - CAST(i32) windowSize.x, CAST(i32) windowSize.y, "Dengine", NULL, NULL); + GLFWwindow *window = + glfwCreateWindow(windowSize.x, windowSize.y, "Dengine", NULL, NULL); if (!window) { @@ -60,13 +60,9 @@ int main() // regardless of success. Catch it once by calling glGetError glGetError(); - i32 frameBufferSizeX; - i32 frameBufferSizeY; - glfwGetFramebufferSize(window, &frameBufferSizeX, &frameBufferSizeY); - const v2 frameBufferSize = - V2(CAST(f32) frameBufferSizeX, CAST(f32) frameBufferSizeY); - - glViewport(0, 0, CAST(i32)frameBufferSize.x, CAST(i32)frameBufferSize.y); + v2i frameBufferSize = V2i(0, 0); + glfwGetFramebufferSize(window, &frameBufferSize.w, &frameBufferSize.h); + glViewport(0, 0, frameBufferSize.x, frameBufferSize.y); glfwSetKeyCallback(window, key_callback); glfwSetCursorPosCallback(window, mouse_callback); @@ -80,10 +76,7 @@ int main() glCullFace(GL_BACK); GameState worldTraveller = {0}; - worldTraveller.width = CAST(i32)frameBufferSize.x; - worldTraveller.height = CAST(i32)frameBufferSize.y; - - worldTraveller_gameInit(&worldTraveller); + worldTraveller_gameInit(&worldTraveller, frameBufferSize); glfwSetWindowUserPointer(window, CAST(void *)(&worldTraveller)); diff --git a/src/include/Dengine/AssetManager.h b/src/include/Dengine/AssetManager.h index 3677d83..ebdca30 100644 --- a/src/include/Dengine/AssetManager.h +++ b/src/include/Dengine/AssetManager.h @@ -32,14 +32,21 @@ typedef struct TexAtlas v4 texRect[128]; } TexAtlas; +typedef struct Font +{ + TexAtlas *atlas; + Texture *tex; + v2i codepointRange; + v2i charSize; +} Font; + // TODO(doyle): Switch to hash based lookup typedef struct AssetManager { Texture textures[256]; TexAtlas texAtlas[256]; Shader shaders[256]; - - v2i codepointRange; + Font font; } AssetManager; GLOBAL_VAR AssetManager assetManager; diff --git a/src/include/Dengine/Renderer.h b/src/include/Dengine/Renderer.h index 29b7b1b..473100c 100644 --- a/src/include/Dengine/Renderer.h +++ b/src/include/Dengine/Renderer.h @@ -11,6 +11,9 @@ typedef struct Renderer GLuint vao; GLuint vbo; i32 numVertexesInVbo; + v2 vertexNdcFactor; + v2 size; + } Renderer; typedef struct RenderQuad @@ -24,6 +27,9 @@ void renderer_entity(Renderer *renderer, Entity *entity, f32 rotate, void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v3 color, Texture *tex); +RenderQuad renderer_createQuad(Renderer *renderer, v4 quadRect, v4 texRect, + Texture *tex); + INTERNAL inline void renderer_flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY) { if (flipX) @@ -41,23 +47,12 @@ INTERNAL inline void renderer_flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY) } } -INTERNAL inline RenderQuad renderer_createQuad(v4 quadRectNdc, v4 texRectNdc) -{ - // NOTE(doyle): Draws a series of triangles (three-sided polygons) using - // vertices v0, v1, v2, then v2, v1, v3 (note the order) - RenderQuad result = {0}; - 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; -} - -INTERNAL inline RenderQuad renderer_createDefaultQuad(v4 texRectNdc) +INTERNAL inline RenderQuad renderer_createDefaultQuad(Renderer *renderer, + v4 texRect, Texture *tex) { RenderQuad result = {0}; - v4 defaultVertices = V4(0.0f, 1.0f, 1.0f, 0.0f); - result = renderer_createQuad(defaultVertices, texRectNdc); + v4 defaultQuad = V4(0.0f, renderer->size.h, renderer->size.w, 0.0f); + result = renderer_createQuad(renderer, defaultQuad, texRect, tex); return result; } diff --git a/src/include/WorldTraveller/WorldTraveller.h b/src/include/WorldTraveller/WorldTraveller.h index 2cc3471..cf33a47 100644 --- a/src/include/WorldTraveller/WorldTraveller.h +++ b/src/include/WorldTraveller/WorldTraveller.h @@ -30,7 +30,6 @@ typedef struct GameState { enum State state; b32 keys[NUM_KEYS]; - i32 width, height; Renderer renderer; i32 heroIndex; @@ -46,6 +45,6 @@ typedef struct GameState AssetManager assetManager; } GameState; -void worldTraveller_gameInit(GameState *state); +void worldTraveller_gameInit(GameState *state, v2i windowSize); void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt); #endif