From fa83daac6053b3664f180cbe353d9eb48202926b Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Wed, 29 Jun 2016 18:23:51 +1000 Subject: [PATCH] Text rendering to screen, assetmanager merged to gamestate --- src/AssetManager.c | 84 +++++++++++++++------ src/WorldTraveller.c | 79 ++++++++++++++----- src/include/Dengine/AssetManager.h | 29 ++++--- src/include/Dengine/Entity.h | 11 --- src/include/Dengine/Math.h | 21 ++++++ src/include/Dengine/Renderer.h | 17 +++++ src/include/WorldTraveller/WorldTraveller.h | 2 + 7 files changed, 173 insertions(+), 70 deletions(-) diff --git a/src/AssetManager.c b/src/AssetManager.c index 9ede051..84bafb8 100644 --- a/src/AssetManager.c +++ b/src/AssetManager.c @@ -9,25 +9,24 @@ #include "Dengine/Platform.h" #include "Dengine/AssetManager.h" -GLOBAL_VAR AssetManager assetManager; - -Texture *asset_getTexture(const enum TexList type) +Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type) { if (type < texlist_count) - return &assetManager.textures[type]; + return &assetManager->textures[type]; return NULL; } -TexAtlas *asset_getTextureAtlas(const enum TexList type) +TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, const enum TexList type) { if (type < texlist_count) - return &assetManager.texAtlas[type]; + return &assetManager->texAtlas[type]; return NULL; } -const i32 asset_loadTextureImage(const char *const path, const enum TexList type) +const i32 asset_loadTextureImage(AssetManager *assetManager, + const char *const path, const enum TexList type) { /* Open the texture image */ i32 imgWidth, imgHeight, bytesPerPixel; @@ -46,14 +45,14 @@ const i32 asset_loadTextureImage(const char *const path, const enum TexList type glCheckError(); stbi_image_free(image); - assetManager.textures[type] = tex; + assetManager->textures[type] = tex; return 0; } -Shader *asset_getShader(const enum ShaderList type) +Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type) { if (type < shaderlist_count) - return &assetManager.shaders[type]; + return &assetManager->shaders[type]; return NULL; } @@ -86,7 +85,8 @@ INTERNAL GLuint createShaderFromPath(const char *const path, GLuint shadertype) return result; } -const i32 asset_loadShaderFiles(const char *const vertexPath, +const i32 asset_loadShaderFiles(AssetManager *assetManager, + const char *const vertexPath, const char *const fragmentPath, const enum ShaderList type) { @@ -99,11 +99,19 @@ const i32 asset_loadShaderFiles(const char *const vertexPath, if (result) return result; - assetManager.shaders[type] = shader; + assetManager->shaders[type] = shader; return 0; } -const i32 asset_loadTTFont(const char *filePath) +/* Individual glyph bitmap generated from STB used for creating a font sheet */ +typedef struct GlyphBitmap +{ + v2i dimensions; + u32 *pixels; + i32 codepoint; +} GlyphBitmap; + +const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath) { PlatformFileRead fontFileRead = {0}; platform_readFileToBuffer(filePath, &fontFileRead); @@ -112,7 +120,8 @@ const i32 asset_loadTTFont(const char *filePath) stbtt_InitFont(&fontInfo, fontFileRead.buffer, stbtt_GetFontOffsetForIndex(fontFileRead.buffer, 0)); - const v2i codepointRange = V2i(32, 127); + assetManager->codepointRange = V2i(32, 127); + v2i codepointRange = assetManager->codepointRange; const i32 numGlyphs = codepointRange.y - codepointRange.x; i32 glyphIndex = 0; @@ -120,7 +129,7 @@ const i32 asset_loadTTFont(const char *filePath) CAST(GlyphBitmap *) calloc(numGlyphs, sizeof(GlyphBitmap)); v2i largestGlyphDimension = V2i(0, 0); - f32 targetFontHeight = 64.0f; + const f32 targetFontHeight = 64.0f; f32 scaleY = stbtt_ScaleForPixelHeight(&fontInfo, targetFontHeight); /* Use STB_TrueType to generate a series of bitmap characters */ @@ -130,7 +139,7 @@ const i32 asset_loadTTFont(const char *filePath) // NOTE(doyle): ScaleX if not specified, is then calculated based on the // ScaleY component i32 width, height, xOffset, yOffset; - u8 *monoBitmap = + u8 *const monoBitmap = stbtt_GetCodepointBitmap(&fontInfo, 0, scaleY, codepoint, &width, &height, &xOffset, &yOffset); @@ -152,7 +161,8 @@ const i32 asset_loadTTFont(const char *filePath) stbtt_FreeBitmap(monoBitmap, NULL); glyphBitmaps[glyphIndex].dimensions = V2i(width, height); - glyphBitmaps[glyphIndex++].pixels = colorBitmap; + glyphBitmaps[glyphIndex].codepoint = codepoint; + glyphBitmaps[glyphIndex++].pixels = colorBitmap; if (height > largestGlyphDimension.h) largestGlyphDimension.h = height; @@ -186,7 +196,7 @@ const i32 asset_loadTTFont(const char *filePath) if ((largestGlyphDimension.h & 1) == 1) largestGlyphDimension.h += 1; - i32 glyphsPerRow = MAX_TEXTURE_SIZE / largestGlyphDimension.w; + i32 glyphsPerRow = (MAX_TEXTURE_SIZE / largestGlyphDimension.w) + 1; #ifdef WT_DEBUG i32 glyphsPerCol = MAX_TEXTURE_SIZE / largestGlyphDimension.h; @@ -199,9 +209,10 @@ const i32 asset_loadTTFont(const char *filePath) } #endif - u32 *fontBitmap = (u32 *)calloc( +#if 1 + u32 *fontBitmap = CAST(u32 *)calloc( squared(TARGET_TEXTURE_SIZE) * TARGET_BYTES_PER_PIXEL, sizeof(u32)); - i32 pitch = MAX_TEXTURE_SIZE * TARGET_BYTES_PER_PIXEL; + const i32 pitch = MAX_TEXTURE_SIZE * TARGET_BYTES_PER_PIXEL; // Check value to determine when a row of glyphs is completely printed i32 verticalPixelsBlitted = 0; @@ -210,7 +221,8 @@ const i32 asset_loadTTFont(const char *filePath) i32 glyphsRemaining = numGlyphs; i32 glyphsOnCurrRow = glyphsPerRow; - for (i32 row = MAX_TEXTURE_SIZE-1; row >= 0; row--) + i32 atlasIndex = 0; + for (i32 row = 0; row < MAX_TEXTURE_SIZE; row++) { u32 *destRow = fontBitmap + (row * MAX_TEXTURE_SIZE); for (i32 glyphIndex = 0; glyphIndex < glyphsOnCurrRow; @@ -219,9 +231,26 @@ const i32 asset_loadTTFont(const char *filePath) i32 activeGlyphIndex = startingGlyphIndex + glyphIndex; GlyphBitmap activeGlyph = glyphBitmaps[activeGlyphIndex]; - i32 numPixelsToPad = largestGlyphDimension.w; + + /* Store the location of glyph into atlas */ + if (verticalPixelsBlitted == 0) + { + 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), + CAST(f32) row); + fontAtlas->texRect[atlasIndex++] = + getRect(origin, V2(CAST(f32) largestGlyphDimension.w, + CAST(f32) largestGlyphDimension.h)); + } /* Copy over exactly one row of pixels */ + i32 numPixelsToPad = largestGlyphDimension.w; if (verticalPixelsBlitted < activeGlyph.dimensions.h) { const i32 srcPitch = @@ -262,12 +291,17 @@ const i32 asset_loadTTFont(const char *filePath) glyphsOnCurrRow = glyphsRemaining; } } - } - Texture tex = - genTexture(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4, (u8 *)fontBitmap); + Texture tex = genTexture(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4, + 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 for (i32 i = 0; i < numGlyphs; i++) free(glyphBitmaps[i].pixels); diff --git a/src/WorldTraveller.c b/src/WorldTraveller.c index 65764f2..09ef50d 100644 --- a/src/WorldTraveller.c +++ b/src/WorldTraveller.c @@ -21,21 +21,25 @@ INTERNAL void updateBufferObject(Renderer *const renderer, void worldTraveller_gameInit(GameState *state) { + AssetManager *assetManager = &state->assetManager; /* Initialise assets */ - asset_loadTextureImage( - "data/textures/WorldTraveller/TerraSprite1024.png", texlist_hero); + asset_loadTextureImage(assetManager, + "data/textures/WorldTraveller/TerraSprite1024.png", + texlist_hero); - asset_loadTextureImage( - "data/textures/WorldTraveller/Terrain.png", texlist_terrain); - TexAtlas *terrainAtlas = asset_getTextureAtlas(texlist_terrain); + asset_loadTextureImage(assetManager, + "data/textures/WorldTraveller/Terrain.png", + texlist_terrain); + TexAtlas *terrainAtlas = + asset_getTextureAtlas(assetManager, texlist_terrain); f32 atlasTileSize = 128.0f; terrainAtlas->texRect[terraincoords_ground] = V4(384.0f, 512.0f, 384.0f + atlasTileSize, 512.0f + atlasTileSize); - asset_loadShaderFiles("data/shaders/sprite.vert.glsl", + asset_loadShaderFiles(assetManager, "data/shaders/sprite.vert.glsl", "data/shaders/sprite.frag.glsl", shaderlist_sprite); - asset_loadTTFont("C:/Windows/Fonts/Arial.ttf"); + asset_loadTTFont(assetManager, "C:/Windows/Fonts/Arial.ttf"); glCheckError(); state->state = state_active; @@ -71,7 +75,7 @@ void worldTraveller_gameInit(GameState *state) V2(0.0f, 0.0f), V2(58.0f, 98.0f), direction_east, - asset_getTexture(texlist_hero), + asset_getTexture(assetManager, texlist_hero), TRUE, 0, 0, @@ -127,7 +131,7 @@ void worldTraveller_gameInit(GameState *state) /* Init renderer */ Renderer *renderer = &state->renderer; - renderer->shader = asset_getShader(shaderlist_sprite); + renderer->shader = asset_getShader(assetManager, shaderlist_sprite); shader_use(renderer->shader); const mat4 projection = mat4_ortho(0.0f, CAST(f32) state->width, 0.0f, @@ -296,11 +300,14 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt) parseInput(state, dt); glCheckError(); - World *const world = &state->world[state->currWorldIndex]; - TexAtlas *const worldAtlas = asset_getTextureAtlas(world->texType); - Texture *const worldTex = asset_getTexture(world->texType); + AssetManager *assetManager = &state->assetManager; - f32 texNdcFactor = 1.0f / CAST(f32)worldTex->width; + World *const world = &state->world[state->currWorldIndex]; + TexAtlas *const worldAtlas = + asset_getTextureAtlas(assetManager, world->texType); + Texture *const worldTex = asset_getTexture(assetManager, world->texType); + + f32 texNdcFactor = 1.0f / MAX_TEXTURE_SIZE; RenderQuad worldQuads[ARRAY_COUNT(world->tiles)] = {0}; i32 quadIndex = 0; @@ -338,7 +345,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt) V3(0, 0, 0), worldTex); /* Render font sheet */ - Texture *font = asset_getTexture(texlist_font); + 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); @@ -346,10 +353,48 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt) V2(CAST(f32)font->width, CAST(f32)font->height), 0.0f, V3(0, 0, 0), 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; + + const v4 charTexRect = fontAtlas->texRect[atlasIndex]; + v4 charTexRectNdc = v4_scale(charTexRect, texNdcFactor); + renderer_flipTexCoord(&charTexRectNdc, FALSE, TRUE); + + const v4 charRectOnScreen = + getRect(V2(xPosOnScreen, 100.0f), eachCharSize); + xPosOnScreen += eachCharSize.w; + v4 charRectOnScreenNdc = charRectOnScreen; + + 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); + 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); + free(stringQuads); + /* Render entities */ // NOTE(doyle): Factor to normalise sprite sheet rect coords to -1, 1 Entity *const hero = &state->entityList[state->heroIndex]; - texNdcFactor = 1.0f / CAST(f32) hero->tex->width; ASSERT(state->freeEntityIndex < ARRAY_COUNT(state->entityList)); for (i32 i = 0; i < state->freeEntityIndex; i++) @@ -371,9 +416,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt) if (entity->direction == direction_east) { // NOTE(doyle): Flip the x coordinates to flip the tex - v4 tmp = texRectNdc; - texRectNdc.x = tmp.z; - texRectNdc.z = tmp.x; + renderer_flipTexCoord(&texRectNdc, TRUE, FALSE); } RenderQuad quad = renderer_createDefaultQuad(texRectNdc); diff --git a/src/include/Dengine/AssetManager.h b/src/include/Dengine/AssetManager.h index 184f0e6..3677d83 100644 --- a/src/include/Dengine/AssetManager.h +++ b/src/include/Dengine/AssetManager.h @@ -29,38 +29,35 @@ enum TerrainCoords typedef struct TexAtlas { // TODO(doyle): String hash based lookup - v4 texRect[16]; - + v4 texRect[128]; } TexAtlas; -typedef struct GlyphBitmap -{ - v2i dimensions; - u32 *pixels; -} GlyphBitmap; - // TODO(doyle): Switch to hash based lookup typedef struct AssetManager { Texture textures[256]; TexAtlas texAtlas[256]; Shader shaders[256]; - Texture font; + + v2i codepointRange; } AssetManager; -extern AssetManager assetManager; +GLOBAL_VAR AssetManager assetManager; /* Texture */ -Texture *asset_getTexture(const enum TexList type); -TexAtlas *asset_getTextureAtlas(const enum TexList type); -const i32 asset_loadTextureImage(const char *const path, +Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type); +TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, + const enum TexList type); +const i32 asset_loadTextureImage(AssetManager *assetManager, + const char *const path, const enum TexList type); /* Shaders */ -Shader *asset_getShader(const enum ShaderList type); -const i32 asset_loadShaderFiles(const char *const vertexPath, +Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type); +const i32 asset_loadShaderFiles(AssetManager *assetManager, + const char *const vertexPath, const char *const fragmentPath, const enum ShaderList type); -const i32 asset_loadTTFont(const char *filePath); +const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath); #endif diff --git a/src/include/Dengine/Entity.h b/src/include/Dengine/Entity.h index 2f2e96b..8db92fe 100644 --- a/src/include/Dengine/Entity.h +++ b/src/include/Dengine/Entity.h @@ -39,17 +39,6 @@ typedef struct Entity i32 currAnimIndex; } Entity; -INTERNAL inline v4 getRect(v2 origin, v2 size) -{ - v2 upperLeftBound = v2_add(origin, V2(0.0f, size.y)); - v2 lowerRightBound = v2_add(origin, V2(size.x, 0.0f)); - - v4 result = V4(upperLeftBound.x, upperLeftBound.y, lowerRightBound.x, - lowerRightBound.y); - - return result; -} - INTERNAL inline v4 getEntityScreenRect(Entity entity) { v4 result = getRect(entity.pos, entity.size); diff --git a/src/include/Dengine/Math.h b/src/include/Dengine/Math.h index 1b95f24..b498007 100644 --- a/src/include/Dengine/Math.h +++ b/src/include/Dengine/Math.h @@ -258,4 +258,25 @@ INTERNAL inline v4 mat4_mul_v4(const mat4 a, const v4 b) return result; } +INTERNAL inline v4 getRect(v2 origin, v2 size) +{ + v2 upperLeftBound = v2_add(origin, V2(0.0f, size.y)); + v2 lowerRightBound = v2_add(origin, V2(size.x, 0.0f)); + + v4 result = V4(upperLeftBound.x, upperLeftBound.y, lowerRightBound.x, + lowerRightBound.y); + + return result; +} + +INTERNAL inline v2 getRectSize(v4 rect) +{ + f32 width = absolute(rect.x - rect.z); + f32 height = absolute(rect.y - rect.w); + + v2 result = V2(width, height); + + return result; +} + #endif diff --git a/src/include/Dengine/Renderer.h b/src/include/Dengine/Renderer.h index c3adb20..29b7b1b 100644 --- a/src/include/Dengine/Renderer.h +++ b/src/include/Dengine/Renderer.h @@ -24,6 +24,23 @@ void renderer_entity(Renderer *renderer, Entity *entity, f32 rotate, void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v3 color, Texture *tex); +INTERNAL inline void renderer_flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY) +{ + if (flipX) + { + v4 tmp = *texCoords; + texCoords->x = tmp.z; + texCoords->z = tmp.x; + } + + if (flipY) + { + v4 tmp = *texCoords; + texCoords->y = tmp.w; + texCoords->w = tmp.y; + } +} + INTERNAL inline RenderQuad renderer_createQuad(v4 quadRectNdc, v4 texRectNdc) { // NOTE(doyle): Draws a series of triangles (three-sided polygons) using diff --git a/src/include/WorldTraveller/WorldTraveller.h b/src/include/WorldTraveller/WorldTraveller.h index 6a5fb29..2cc3471 100644 --- a/src/include/WorldTraveller/WorldTraveller.h +++ b/src/include/WorldTraveller/WorldTraveller.h @@ -42,6 +42,8 @@ typedef struct GameState // TODO(doyle): Make size of list dynamic Entity entityList[256]; i32 freeEntityIndex; + + AssetManager assetManager; } GameState; void worldTraveller_gameInit(GameState *state);