Create empty texture idea, change function scopes

Some functions which should not be exposed in the API have been switched
to INTERNAL scope. We have a notion of an empty texture in World Traveller
that we can pass in situations where we just want to render a solid
colour with no associated texture.

The alternative to this was creating a separate shader for rendering
primitives but would require at some point to expose the AssetManager to
the renderer or the user on behalf has to manually switch shaders before
rendering (non-intuitive).
This commit is contained in:
Doyle Thai 2016-07-16 17:15:03 +10:00
parent 86b4d1e206
commit d0b4c99787
13 changed files with 198 additions and 164 deletions

View File

@ -49,8 +49,8 @@
<ItemGroup>
<None Include="data\shaders\default.vert.glsl" />
<None Include="data\shaders\default.frag.glsl" />
<None Include="data\shaders\sprite.vert.glsl" />
<None Include="data\shaders\sprite.frag.glsl" />
<None Include="data\shaders\sprite.vert.glsl" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\include\Dengine\Common.h">

Binary file not shown.

View File

@ -63,8 +63,8 @@ const i32 asset_loadTextureImage(AssetManager *assetManager,
return -1;
}
Texture tex = genTexture(CAST(GLuint)(imgWidth), CAST(GLuint)(imgHeight),
CAST(GLint)(bytesPerPixel), image);
Texture tex = texture_gen(CAST(GLuint)(imgWidth), CAST(GLuint)(imgHeight),
CAST(GLint)(bytesPerPixel), image);
glCheckError();
stbi_image_free(image);
@ -111,6 +111,30 @@ INTERNAL GLuint createShaderFromPath(const char *const path, GLuint shadertype)
return result;
}
INTERNAL i32 shaderLoadProgram(Shader *const shader, const GLuint vertexShader,
const GLuint fragmentShader)
{
shader->id = glCreateProgram();
glAttachShader(shader->id, vertexShader);
glAttachShader(shader->id, fragmentShader);
glLinkProgram(shader->id);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
GLint success;
GLchar infoLog[512];
glGetProgramiv(shader->id, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader->id, 512, NULL, infoLog);
printf("glLinkProgram failed: %s\n", infoLog);
return -1;
}
return 0;
}
const i32 asset_loadShaderFiles(AssetManager *assetManager,
const char *const vertexPath,
const char *const fragmentPath,
@ -121,7 +145,7 @@ const i32 asset_loadShaderFiles(AssetManager *assetManager,
createShaderFromPath(fragmentPath, GL_FRAGMENT_SHADER);
Shader shader;
i32 result = shader_loadProgram(&shader, vertexShader, fragmentShader);
i32 result = shaderLoadProgram(&shader, vertexShader, fragmentShader);
if (result)
return result;
@ -295,13 +319,13 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
V2(CAST(f32)(glyphIndex * font->maxSize.w), CAST(f32) row);
#if 1
fontAtlas->texRect[atlasIndex++] =
getRect(origin, V2(CAST(f32) font->maxSize.w,
CAST(f32) font->maxSize.h));
math_getRect(origin, V2(CAST(f32) font->maxSize.w,
CAST(f32) font->maxSize.h));
#else
v2i fontSize =
font->charMetrics[activeGlyph.codepoint - 32].trueSize;
fontAtlas->texRect[atlasIndex++] =
getRect(origin, V2(CAST(f32)fontSize.x, CAST(f32)fontSize.y));
fontAtlas->texRect[atlasIndex++] = math_getRect(
origin, V2(CAST(f32) fontSize.x, CAST(f32) fontSize.y));
#endif
}
@ -348,8 +372,8 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
}
}
Texture tex = genTexture(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4,
CAST(u8 *)fontBitmap);
Texture tex = texture_gen(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4,
CAST(u8 *) fontBitmap);
assetManager->textures[texlist_font] = tex;
#ifdef WT_RENDER_FONT_FILE

View File

@ -18,6 +18,125 @@ INTERNAL void updateBufferObject(Renderer *const renderer,
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
INTERNAL RenderQuad createTexQuad(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};
/* Convert screen coordinates to normalised device coordinates */
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;
/* Convert texture coordinates to normalised texture coordinates */
v4 texRectNdc = texRect;
if (tex)
{
v2 texNdcFactor = V2(1.0f / tex->width, 1.0f / tex->height);
texRectNdc.e[0] *= texNdcFactor.w;
texRectNdc.e[1] *= texNdcFactor.h;
texRectNdc.e[2] *= texNdcFactor.w;
texRectNdc.e[3] *= texNdcFactor.h;
}
/* Form the quad */
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 createQuad(Renderer *renderer, v4 quadRect)
{
v4 texRect = V4(0, 0, 0, 0);
RenderQuad result =
createTexQuad(renderer, quadRect, texRect, NULL);
return result;
}
INTERNAL inline RenderQuad
createDefaultTexQuad(Renderer *renderer, v4 texRect, Texture *tex)
{
RenderQuad result = {0};
v4 defaultQuad = V4(0.0f, renderer->size.h, renderer->size.w, 0.0f);
result = createTexQuad(renderer, defaultQuad, texRect, tex);
return result;
}
INTERNAL void renderObject(Renderer *renderer, v2 pos, v2 size, f32 rotate,
v4 color, Texture *tex)
{
mat4 transMatrix = mat4_translate(pos.x, pos.y, 0.0f);
// NOTE(doyle): Rotate from the center of the object, not its' origin (i.e.
// top left)
mat4 rotateMatrix = mat4_translate((size.x * 0.5f), (size.y * 0.5f), 0.0f);
rotateMatrix = mat4_mul(rotateMatrix, mat4_rotate(rotate, 0.0f, 0.0f, 1.0f));
rotateMatrix = mat4_mul(rotateMatrix, mat4_translate((size.x * -0.5f), (size.y * -0.5f), 0.0f));
// NOTE(doyle): We draw everything as a unit square in OGL. Scale it to size
// TODO(doyle): We should have a notion of hitbox size and texture size
// we're going to render so we can draw textures that may be bigger than the
// entity, (slightly) but keep a consistent bounding box
mat4 scaleMatrix = mat4_scale(size.x, size.y, 1.0f);
mat4 model = mat4_mul(transMatrix, mat4_mul(rotateMatrix, scaleMatrix));
/* Load transformation matrix */
shader_use(renderer->shader);
shader_uniformSetMat4fv(renderer->shader, "model", model);
glCheckError();
/* Set color modulation value */
shader_uniformSetVec4f(renderer->shader, "spriteColor", color);
/* Send draw calls */
#if RENDER_BOUNDING_BOX
glBindVertexArray(renderer->vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
glBindVertexArray(0);
#endif
if (tex)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex->id);
shader_uniformSet1i(renderer->shader, "tex", 0);
}
glBindVertexArray(renderer->vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
#ifdef DENGINE_DEBUG
debug_callCountIncrement(debugcallcount_drawArrays);
#endif
/* Unbind */
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glCheckError();
}
void renderer_rect(Renderer *const renderer, v4 cameraBounds, v2 pos, v2 size,
f32 rotate, Texture *tex, v4 texRect, v4 color)
{
v4 quadRect = math_getRect(pos, size);
RenderQuad quad = createTexQuad(renderer, quadRect, texRect, tex);
updateBufferObject(renderer, &quad, 1);
// NOTE(doyle): Get the origin of cameraBounds in world space, bottom left
v2 offsetFromCamOrigin = V2(cameraBounds.x, cameraBounds.w);
v2 rectRelativeToCamera = v2_sub(pos, offsetFromCamOrigin);
renderObject(renderer, rectRelativeToCamera, size, rotate, color, tex);
}
void renderer_string(Renderer *const renderer, v4 cameraBounds,
Font *const font, const char *const string, v2 pos,
f32 rotate, v4 color)
@ -48,15 +167,15 @@ void renderer_string(Renderer *const renderer, v4 cameraBounds,
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
// 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 - (scale * charMetric.offset.y);
const v4 charRectOnScreen = getRect(
pos, V2(scale * CAST(f32) font->maxSize.w, scale * CAST(f32) font->maxSize.h));
const v4 charRectOnScreen =
math_getRect(pos, V2(scale * CAST(f32) font->maxSize.w,
scale * CAST(f32) font->maxSize.h));
pos.x += scale * charMetric.advance;
@ -64,8 +183,8 @@ void renderer_string(Renderer *const renderer, v4 cameraBounds,
v4 charTexRect = font->atlas->texRect[relativeIndex];
renderer_flipTexCoord(&charTexRect, FALSE, TRUE);
RenderQuad charQuad = renderer_createQuad(
renderer, charRectOnScreen, charTexRect, font->tex);
RenderQuad charQuad = createTexQuad(renderer, charRectOnScreen,
charTexRect, font->tex);
stringQuads[quadIndex++] = charQuad;
}
@ -73,7 +192,7 @@ void renderer_string(Renderer *const renderer, v4 cameraBounds,
// 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,
renderObject(renderer, V2(0.0f, 0.0f), renderer->size, rotate, color,
font->tex);
PLATFORM_MEM_FREE(stringQuads, strLen * sizeof(RenderQuad));
}
@ -110,97 +229,13 @@ void renderer_entity(Renderer *renderer, v4 cameraBounds, Entity *entity,
renderer_flipTexCoord(&texRect, TRUE, FALSE);
}
RenderQuad entityQuad =
renderer_createDefaultQuad(renderer, texRect, entity->tex);
createDefaultTexQuad(renderer, texRect, entity->tex);
updateBufferObject(renderer, &entityQuad, 1);
// NOTE(doyle): The camera origin is 0,0 in world positions
v2 offsetFromCamOrigin = V2(cameraBounds.x, cameraBounds.w);
v2 entityRelativeToCamera = v2_sub(entity->pos, offsetFromCamOrigin);
renderer_object(renderer, entityRelativeToCamera, entity->size, rotate,
color, entity->tex);
renderObject(renderer, entityRelativeToCamera, entity->size, rotate,
color, entity->tex);
}
}
void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v4 color,
Texture *tex)
{
shader_use(renderer->shader);
mat4 transMatrix = mat4_translate(pos.x, pos.y, 0.0f);
// NOTE(doyle): Rotate from the center of the object, not its' origin (i.e.
// top left)
mat4 rotateMatrix = mat4_translate((size.x * 0.5f), (size.y * 0.5f), 0.0f);
rotateMatrix = mat4_mul(rotateMatrix, mat4_rotate(rotate, 0.0f, 0.0f, 1.0f));
rotateMatrix = mat4_mul(rotateMatrix, mat4_translate((size.x * -0.5f), (size.y * -0.5f), 0.0f));
// NOTE(doyle): We draw everything as a unit square in OGL. Scale it to size
// TODO(doyle): We should have a notion of hitbox size and texture size
// we're going to render so we can draw textures that may be bigger than the
// entity, (slightly) but keep a consistent bounding box
mat4 scaleMatrix = mat4_scale(size.x, size.y, 1.0f);
mat4 model = mat4_mul(transMatrix, mat4_mul(rotateMatrix, scaleMatrix));
shader_uniformSetMat4fv(renderer->shader, "model", model);
glCheckError();
shader_uniformSetVec4f(renderer->shader, "spriteColor", color);
#if RENDER_BOUNDING_BOX
glBindVertexArray(renderer->vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
glBindVertexArray(0);
#endif
glActiveTexture(GL_TEXTURE0);
if (tex)
{
glBindTexture(GL_TEXTURE_2D, tex->id);
shader_uniformSet1i(renderer->shader, "tex", 0);
}
glBindVertexArray(renderer->vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
#ifdef DENGINE_DEBUG
debug_callCountIncrement(debugcallcount_drawArrays);
#endif
glBindVertexArray(0);
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

@ -1,29 +1,5 @@
#include "Dengine/Shader.h"
const i32 shader_loadProgram(Shader *const shader, const GLuint vertexShader,
const GLuint fragmentShader)
{
shader->id = glCreateProgram();
glAttachShader(shader->id, vertexShader);
glAttachShader(shader->id, fragmentShader);
glLinkProgram(shader->id);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
GLint success;
GLchar infoLog[512];
glGetProgramiv(shader->id, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader->id, 512, NULL, infoLog);
printf("glLinkProgram failed: %s\n", infoLog);
return -1;
}
return 0;
}
void shader_uniformSet1i(Shader *const shader, const GLchar *name,
const GLuint data)
{

View File

@ -28,8 +28,8 @@ INTERNAL GLint getGLFormat(i32 bytesPerPixel, b32 srgb)
}
}
Texture genTexture(const GLuint width, const GLuint height,
const GLint bytesPerPixel, const u8 *const image)
Texture texture_gen(const GLuint width, const GLuint height,
const GLint bytesPerPixel, const u8 *const image)
{
// TODO(doyle): Let us set the parameters gl params as well
glCheckError();

View File

@ -92,6 +92,12 @@ void worldTraveller_gameInit(GameState *state, v2i windowSize)
{
AssetManager *assetManager = &state->assetManager;
/* Initialise assets */
/* Create empty 1x1 4bpp black texture */
u32 bitmap = (0xFF << 24) | (0xFF << 16) | (0xFF << 8) | (0xFF << 0);
Texture emptyTex = texture_gen(1, 1, 4, CAST(u8 *)(&bitmap));
assetManager->textures[texlist_empty] = emptyTex;
/* Load textures */
asset_loadTextureImage(assetManager,
"data/textures/WorldTraveller/TerraSprite1024.png",
texlist_hero);
@ -105,10 +111,13 @@ void worldTraveller_gameInit(GameState *state, v2i windowSize)
const i32 texSize = 1024;
v2 texOrigin = V2(0, CAST(f32)(texSize - 128));
terrainAtlas->texRect[terraincoords_ground] =
V4(texOrigin.x, texOrigin.y, texOrigin.x + atlasTileSize, texOrigin.y - atlasTileSize);
V4(texOrigin.x, texOrigin.y, texOrigin.x + atlasTileSize,
texOrigin.y - atlasTileSize);
/* Load shaders */
asset_loadShaderFiles(assetManager, "data/shaders/sprite.vert.glsl",
"data/shaders/sprite.frag.glsl", shaderlist_sprite);
"data/shaders/sprite.frag.glsl",
shaderlist_sprite);
asset_loadTTFont(assetManager, "C:/Windows/Fonts/Arialbd.ttf");
glCheckError();
@ -135,8 +144,9 @@ void worldTraveller_gameInit(GameState *state, v2i windowSize)
v2 worldDimensionInTilesf = V2(CAST(f32) worldDimensionInTiles.x,
CAST(f32) worldDimensionInTiles.y);
world->bounds = getRect(V2(0, 0), v2_scale(worldDimensionInTilesf,
CAST(f32) state->tileSize));
world->bounds =
math_getRect(V2(0, 0), v2_scale(worldDimensionInTilesf,
CAST(f32) state->tileSize));
TexAtlas *const atlas =
asset_getTextureAtlas(assetManager, world->texType);
@ -403,7 +413,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
#endif
/* Recalculate rendering bounds */
v4 cameraBounds = getRect(world->cameraPos, renderer->size);
v4 cameraBounds = math_getRect(world->cameraPos, renderer->size);
// NOTE(doyle): Lock camera if it passes the bounds of the world
if (cameraBounds.x <= world->bounds.x)
{
@ -441,12 +451,9 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
renderer_entity(&state->renderer, cameraBounds, entity, dt, rotate,
V4(1, 1, 1, 1));
// TODO(doyle): Clean up lines
// Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); }
/* Game logic */
if (entity->type == entitytype_hero) continue;
else if (entity->type == entitytype_mob)
if (entity->type == entitytype_mob)
{
// TODO(doyle): Currently calculated in pixels, how about meaningful
// game units?
@ -468,6 +475,11 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
renderer_staticString(&state->renderer, font, battleStr, strPos,
0, color);
#endif
Texture *emptyTex =
asset_getTexture(assetManager, texlist_empty);
renderer_rect(renderer, cameraBounds, hero->pos,
V2(distance, 100.0f), 0, emptyTex, V4(0, 1, 1, 0),
V4(1, 0, 0, 1));
}
}

View File

@ -8,6 +8,7 @@
enum TexList
{
texlist_empty,
texlist_hero,
texlist_terrain,
texlist_font,

View File

@ -54,7 +54,7 @@ typedef struct Entity
INTERNAL inline v4 getEntityScreenRect(Entity entity)
{
v4 result = getRect(entity.pos, entity.size);
v4 result = math_getRect(entity.pos, entity.size);
return result;
}

View File

@ -269,7 +269,7 @@ INTERNAL inline v4 mat4_mul_v4(const mat4 a, const v4 b)
return result;
}
INTERNAL inline v4 getRect(v2 origin, v2 size)
INTERNAL inline v4 math_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));
@ -280,7 +280,7 @@ INTERNAL inline v4 getRect(v2 origin, v2 size)
return result;
}
INTERNAL inline v2 getRectSize(v4 rect)
INTERNAL inline v2 math_getRectSize(v4 rect)
{
f32 width = ABS(rect.x - rect.z);
f32 height = ABS(rect.y - rect.w);

View File

@ -19,6 +19,12 @@ typedef struct RenderQuad
v4 vertex[4];
} RenderQuad;
// TODO(doyle): Clean up lines
// Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); }
void renderer_rect(Renderer *const renderer, v4 cameraBounds, v2 pos, v2 size,
f32 rotate, Texture *tex, v4 texRect, v4 color);
void renderer_string(Renderer *const renderer, v4 cameraBounds,
Font *const font, const char *const string, v2 pos,
f32 rotate, v4 color);
@ -34,12 +40,6 @@ inline void renderer_staticString(Renderer *const renderer, Font *const font,
void renderer_entity(Renderer *renderer, v4 cameraBounds, Entity *entity,
f32 dt, f32 rotate, v4 color);
void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v4 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)
@ -56,14 +56,4 @@ INTERNAL inline void renderer_flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY)
texCoords->w = tmp.y;
}
}
INTERNAL inline RenderQuad renderer_createDefaultQuad(Renderer *renderer,
v4 texRect, Texture *tex)
{
RenderQuad result = {0};
v4 defaultQuad = V4(0.0f, renderer->size.h, renderer->size.w, 0.0f);
result = renderer_createQuad(renderer, defaultQuad, texRect, tex);
return result;
}
#endif

View File

@ -9,10 +9,6 @@ typedef struct Shader
GLuint id;
} Shader;
const i32 shader_loadProgram(Shader *const shader,
const GLuint vertexShader,
const GLuint fragmentShader);
void shader_uniformSet1i(Shader *const shader, const GLchar *name,
const GLuint data);
void shader_uniformSetMat4fv(Shader *const shader, const GLchar *name,

View File

@ -32,7 +32,7 @@ typedef struct Texture
} Texture;
// Generates texture from image data
Texture genTexture(const GLuint width, const GLuint height,
const GLint bytesPerPixel, const u8 *const image);
Texture texture_gen(const GLuint width, const GLuint height,
const GLint bytesPerPixel, const u8 *const image);
#endif