Renderer calculates NDC coordinates from screen coords

This commit is contained in:
Doyle Thai 2016-06-29 20:44:35 +10:00
parent fa83daac60
commit d8ee46611f
7 changed files with 108 additions and 111 deletions

View File

@ -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;
@ -296,12 +298,9 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
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
font->tex = &assetManager->textures[texlist_font];
font->atlas = &assetManager->texAtlas[texlist_font];
for (i32 i = 0; i < numGlyphs; i++)
free(glyphBitmaps[i].pixels);

View File

@ -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;
}

View File

@ -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,6 +306,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
glCheckError();
AssetManager *assetManager = &state->assetManager;
Renderer *renderer = &state->renderer;
World *const world = &state->world[state->currWorldIndex];
TexAtlas *const worldAtlas =
@ -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); }
}

View File

@ -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));

View File

@ -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;

View File

@ -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;
}

View File

@ -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