Text rendering to screen, assetmanager merged to gamestate

This commit is contained in:
Doyle Thai 2016-06-29 18:23:51 +10:00
parent ef112fa4cb
commit fa83daac60
7 changed files with 173 additions and 70 deletions

View File

@ -9,25 +9,24 @@
#include "Dengine/Platform.h" #include "Dengine/Platform.h"
#include "Dengine/AssetManager.h" #include "Dengine/AssetManager.h"
GLOBAL_VAR AssetManager assetManager; Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type)
Texture *asset_getTexture(const enum TexList type)
{ {
if (type < texlist_count) if (type < texlist_count)
return &assetManager.textures[type]; return &assetManager->textures[type];
return NULL; return NULL;
} }
TexAtlas *asset_getTextureAtlas(const enum TexList type) TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, const enum TexList type)
{ {
if (type < texlist_count) if (type < texlist_count)
return &assetManager.texAtlas[type]; return &assetManager->texAtlas[type];
return NULL; 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 */ /* Open the texture image */
i32 imgWidth, imgHeight, bytesPerPixel; i32 imgWidth, imgHeight, bytesPerPixel;
@ -46,14 +45,14 @@ const i32 asset_loadTextureImage(const char *const path, const enum TexList type
glCheckError(); glCheckError();
stbi_image_free(image); stbi_image_free(image);
assetManager.textures[type] = tex; assetManager->textures[type] = tex;
return 0; return 0;
} }
Shader *asset_getShader(const enum ShaderList type) Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type)
{ {
if (type < shaderlist_count) if (type < shaderlist_count)
return &assetManager.shaders[type]; return &assetManager->shaders[type];
return NULL; return NULL;
} }
@ -86,7 +85,8 @@ INTERNAL GLuint createShaderFromPath(const char *const path, GLuint shadertype)
return result; 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 char *const fragmentPath,
const enum ShaderList type) const enum ShaderList type)
{ {
@ -99,11 +99,19 @@ const i32 asset_loadShaderFiles(const char *const vertexPath,
if (result) if (result)
return result; return result;
assetManager.shaders[type] = shader; assetManager->shaders[type] = shader;
return 0; 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}; PlatformFileRead fontFileRead = {0};
platform_readFileToBuffer(filePath, &fontFileRead); platform_readFileToBuffer(filePath, &fontFileRead);
@ -112,7 +120,8 @@ const i32 asset_loadTTFont(const char *filePath)
stbtt_InitFont(&fontInfo, fontFileRead.buffer, stbtt_InitFont(&fontInfo, fontFileRead.buffer,
stbtt_GetFontOffsetForIndex(fontFileRead.buffer, 0)); 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; const i32 numGlyphs = codepointRange.y - codepointRange.x;
i32 glyphIndex = 0; i32 glyphIndex = 0;
@ -120,7 +129,7 @@ const i32 asset_loadTTFont(const char *filePath)
CAST(GlyphBitmap *) calloc(numGlyphs, sizeof(GlyphBitmap)); CAST(GlyphBitmap *) calloc(numGlyphs, sizeof(GlyphBitmap));
v2i largestGlyphDimension = V2i(0, 0); v2i largestGlyphDimension = V2i(0, 0);
f32 targetFontHeight = 64.0f; const f32 targetFontHeight = 64.0f;
f32 scaleY = stbtt_ScaleForPixelHeight(&fontInfo, targetFontHeight); f32 scaleY = stbtt_ScaleForPixelHeight(&fontInfo, targetFontHeight);
/* Use STB_TrueType to generate a series of bitmap characters */ /* 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 // NOTE(doyle): ScaleX if not specified, is then calculated based on the
// ScaleY component // ScaleY component
i32 width, height, xOffset, yOffset; i32 width, height, xOffset, yOffset;
u8 *monoBitmap = u8 *const monoBitmap =
stbtt_GetCodepointBitmap(&fontInfo, 0, scaleY, codepoint, &width, stbtt_GetCodepointBitmap(&fontInfo, 0, scaleY, codepoint, &width,
&height, &xOffset, &yOffset); &height, &xOffset, &yOffset);
@ -152,6 +161,7 @@ const i32 asset_loadTTFont(const char *filePath)
stbtt_FreeBitmap(monoBitmap, NULL); stbtt_FreeBitmap(monoBitmap, NULL);
glyphBitmaps[glyphIndex].dimensions = V2i(width, height); glyphBitmaps[glyphIndex].dimensions = V2i(width, height);
glyphBitmaps[glyphIndex].codepoint = codepoint;
glyphBitmaps[glyphIndex++].pixels = colorBitmap; glyphBitmaps[glyphIndex++].pixels = colorBitmap;
if (height > largestGlyphDimension.h) if (height > largestGlyphDimension.h)
@ -186,7 +196,7 @@ const i32 asset_loadTTFont(const char *filePath)
if ((largestGlyphDimension.h & 1) == 1) if ((largestGlyphDimension.h & 1) == 1)
largestGlyphDimension.h += 1; largestGlyphDimension.h += 1;
i32 glyphsPerRow = MAX_TEXTURE_SIZE / largestGlyphDimension.w; i32 glyphsPerRow = (MAX_TEXTURE_SIZE / largestGlyphDimension.w) + 1;
#ifdef WT_DEBUG #ifdef WT_DEBUG
i32 glyphsPerCol = MAX_TEXTURE_SIZE / largestGlyphDimension.h; i32 glyphsPerCol = MAX_TEXTURE_SIZE / largestGlyphDimension.h;
@ -199,9 +209,10 @@ const i32 asset_loadTTFont(const char *filePath)
} }
#endif #endif
u32 *fontBitmap = (u32 *)calloc( #if 1
u32 *fontBitmap = CAST(u32 *)calloc(
squared(TARGET_TEXTURE_SIZE) * TARGET_BYTES_PER_PIXEL, sizeof(u32)); 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 // Check value to determine when a row of glyphs is completely printed
i32 verticalPixelsBlitted = 0; i32 verticalPixelsBlitted = 0;
@ -210,7 +221,8 @@ const i32 asset_loadTTFont(const char *filePath)
i32 glyphsRemaining = numGlyphs; i32 glyphsRemaining = numGlyphs;
i32 glyphsOnCurrRow = glyphsPerRow; 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); u32 *destRow = fontBitmap + (row * MAX_TEXTURE_SIZE);
for (i32 glyphIndex = 0; glyphIndex < glyphsOnCurrRow; for (i32 glyphIndex = 0; glyphIndex < glyphsOnCurrRow;
@ -219,9 +231,26 @@ const i32 asset_loadTTFont(const char *filePath)
i32 activeGlyphIndex = startingGlyphIndex + glyphIndex; i32 activeGlyphIndex = startingGlyphIndex + glyphIndex;
GlyphBitmap activeGlyph = glyphBitmaps[activeGlyphIndex]; 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 */ /* Copy over exactly one row of pixels */
i32 numPixelsToPad = largestGlyphDimension.w;
if (verticalPixelsBlitted < activeGlyph.dimensions.h) if (verticalPixelsBlitted < activeGlyph.dimensions.h)
{ {
const i32 srcPitch = const i32 srcPitch =
@ -262,12 +291,17 @@ const i32 asset_loadTTFont(const char *filePath)
glyphsOnCurrRow = glyphsRemaining; glyphsOnCurrRow = glyphsRemaining;
} }
} }
} }
Texture tex = Texture tex = genTexture(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4,
genTexture(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4, (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; assetManager.textures[texlist_font] = tex;
#endif
for (i32 i = 0; i < numGlyphs; i++) for (i32 i = 0; i < numGlyphs; i++)
free(glyphBitmaps[i].pixels); free(glyphBitmaps[i].pixels);

View File

@ -21,21 +21,25 @@ INTERNAL void updateBufferObject(Renderer *const renderer,
void worldTraveller_gameInit(GameState *state) void worldTraveller_gameInit(GameState *state)
{ {
AssetManager *assetManager = &state->assetManager;
/* Initialise assets */ /* Initialise assets */
asset_loadTextureImage( asset_loadTextureImage(assetManager,
"data/textures/WorldTraveller/TerraSprite1024.png", texlist_hero); "data/textures/WorldTraveller/TerraSprite1024.png",
texlist_hero);
asset_loadTextureImage( asset_loadTextureImage(assetManager,
"data/textures/WorldTraveller/Terrain.png", texlist_terrain); "data/textures/WorldTraveller/Terrain.png",
TexAtlas *terrainAtlas = asset_getTextureAtlas(texlist_terrain); texlist_terrain);
TexAtlas *terrainAtlas =
asset_getTextureAtlas(assetManager, texlist_terrain);
f32 atlasTileSize = 128.0f; f32 atlasTileSize = 128.0f;
terrainAtlas->texRect[terraincoords_ground] = terrainAtlas->texRect[terraincoords_ground] =
V4(384.0f, 512.0f, 384.0f + atlasTileSize, 512.0f + atlasTileSize); 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); "data/shaders/sprite.frag.glsl", shaderlist_sprite);
asset_loadTTFont("C:/Windows/Fonts/Arial.ttf"); asset_loadTTFont(assetManager, "C:/Windows/Fonts/Arial.ttf");
glCheckError(); glCheckError();
state->state = state_active; state->state = state_active;
@ -71,7 +75,7 @@ void worldTraveller_gameInit(GameState *state)
V2(0.0f, 0.0f), V2(0.0f, 0.0f),
V2(58.0f, 98.0f), V2(58.0f, 98.0f),
direction_east, direction_east,
asset_getTexture(texlist_hero), asset_getTexture(assetManager, texlist_hero),
TRUE, TRUE,
0, 0,
0, 0,
@ -127,7 +131,7 @@ void worldTraveller_gameInit(GameState *state)
/* Init renderer */ /* Init renderer */
Renderer *renderer = &state->renderer; Renderer *renderer = &state->renderer;
renderer->shader = asset_getShader(shaderlist_sprite); renderer->shader = asset_getShader(assetManager, shaderlist_sprite);
shader_use(renderer->shader); shader_use(renderer->shader);
const mat4 projection = mat4_ortho(0.0f, CAST(f32) state->width, 0.0f, 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); parseInput(state, dt);
glCheckError(); glCheckError();
World *const world = &state->world[state->currWorldIndex]; AssetManager *assetManager = &state->assetManager;
TexAtlas *const worldAtlas = asset_getTextureAtlas(world->texType);
Texture *const worldTex = asset_getTexture(world->texType);
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}; RenderQuad worldQuads[ARRAY_COUNT(world->tiles)] = {0};
i32 quadIndex = 0; i32 quadIndex = 0;
@ -338,7 +345,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
V3(0, 0, 0), worldTex); V3(0, 0, 0), worldTex);
/* Render font sheet */ /* 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); v4 fontTexRect = V4(0.0f, 1.0f, 1.0f, 0.0);
RenderQuad fontQuad = renderer_createDefaultQuad(fontTexRect); RenderQuad fontQuad = renderer_createDefaultQuad(fontTexRect);
updateBufferObject(&state->renderer, &fontQuad, 1); 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, V2(CAST(f32)font->width, CAST(f32)font->height), 0.0f,
V3(0, 0, 0), font); 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 */ /* Render entities */
// NOTE(doyle): Factor to normalise sprite sheet rect coords to -1, 1 // NOTE(doyle): Factor to normalise sprite sheet rect coords to -1, 1
Entity *const hero = &state->entityList[state->heroIndex]; Entity *const hero = &state->entityList[state->heroIndex];
texNdcFactor = 1.0f / CAST(f32) hero->tex->width;
ASSERT(state->freeEntityIndex < ARRAY_COUNT(state->entityList)); ASSERT(state->freeEntityIndex < ARRAY_COUNT(state->entityList));
for (i32 i = 0; i < state->freeEntityIndex; i++) 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) if (entity->direction == direction_east)
{ {
// NOTE(doyle): Flip the x coordinates to flip the tex // NOTE(doyle): Flip the x coordinates to flip the tex
v4 tmp = texRectNdc; renderer_flipTexCoord(&texRectNdc, TRUE, FALSE);
texRectNdc.x = tmp.z;
texRectNdc.z = tmp.x;
} }
RenderQuad quad = renderer_createDefaultQuad(texRectNdc); RenderQuad quad = renderer_createDefaultQuad(texRectNdc);

View File

@ -29,38 +29,35 @@ enum TerrainCoords
typedef struct TexAtlas typedef struct TexAtlas
{ {
// TODO(doyle): String hash based lookup // TODO(doyle): String hash based lookup
v4 texRect[16]; v4 texRect[128];
} TexAtlas; } TexAtlas;
typedef struct GlyphBitmap
{
v2i dimensions;
u32 *pixels;
} GlyphBitmap;
// TODO(doyle): Switch to hash based lookup // TODO(doyle): Switch to hash based lookup
typedef struct AssetManager typedef struct AssetManager
{ {
Texture textures[256]; Texture textures[256];
TexAtlas texAtlas[256]; TexAtlas texAtlas[256];
Shader shaders[256]; Shader shaders[256];
Texture font;
v2i codepointRange;
} AssetManager; } AssetManager;
extern AssetManager assetManager; GLOBAL_VAR AssetManager assetManager;
/* Texture */ /* Texture */
Texture *asset_getTexture(const enum TexList type); Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type);
TexAtlas *asset_getTextureAtlas(const enum TexList type); TexAtlas *asset_getTextureAtlas(AssetManager *assetManager,
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); const enum TexList type);
/* Shaders */ /* Shaders */
Shader *asset_getShader(const enum ShaderList type); Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type);
const i32 asset_loadShaderFiles(const char *const vertexPath, const i32 asset_loadShaderFiles(AssetManager *assetManager,
const char *const vertexPath,
const char *const fragmentPath, const char *const fragmentPath,
const enum ShaderList type); const enum ShaderList type);
const i32 asset_loadTTFont(const char *filePath); const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath);
#endif #endif

View File

@ -39,17 +39,6 @@ typedef struct Entity
i32 currAnimIndex; i32 currAnimIndex;
} Entity; } 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) INTERNAL inline v4 getEntityScreenRect(Entity entity)
{ {
v4 result = getRect(entity.pos, entity.size); v4 result = getRect(entity.pos, entity.size);

View File

@ -258,4 +258,25 @@ INTERNAL inline v4 mat4_mul_v4(const mat4 a, const v4 b)
return result; 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 #endif

View File

@ -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, void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v3 color,
Texture *tex); 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) INTERNAL inline RenderQuad renderer_createQuad(v4 quadRectNdc, v4 texRectNdc)
{ {
// NOTE(doyle): Draws a series of triangles (three-sided polygons) using // NOTE(doyle): Draws a series of triangles (three-sided polygons) using

View File

@ -42,6 +42,8 @@ typedef struct GameState
// TODO(doyle): Make size of list dynamic // TODO(doyle): Make size of list dynamic
Entity entityList[256]; Entity entityList[256];
i32 freeEntityIndex; i32 freeEntityIndex;
AssetManager assetManager;
} GameState; } GameState;
void worldTraveller_gameInit(GameState *state); void worldTraveller_gameInit(GameState *state);