Add primitive triangle rendering code

This commit is contained in:
Doyle Thai 2016-11-11 21:43:03 +11:00
parent 4795fe1cb1
commit 6d67485d49
7 changed files with 229 additions and 83 deletions

Binary file not shown.

View File

@ -61,26 +61,41 @@ void initRenderer(GameState *state, v2 windowSize) {
GL_CHECK_ERROR(); GL_CHECK_ERROR();
/* Create buffers */ /* Create buffers */
glGenVertexArrays(1, &renderer->vao); glGenVertexArrays(ARRAY_COUNT(renderer->vao), renderer->vao);
glGenBuffers(1, &renderer->vbo); glGenBuffers(ARRAY_COUNT(renderer->vbo), renderer->vbo);
GL_CHECK_ERROR(); GL_CHECK_ERROR();
/* Bind buffers */ {
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo); // Bind buffers and configure vao, vao automatically intercepts
glBindVertexArray(renderer->vao); // glBindCalls and associates the state with that buffer for us
glBindVertexArray(renderer->vao[rendermode_quad]);
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[rendermode_quad]);
/* Configure VAO */ glEnableVertexAttribArray(0);
u32 numVertexElements = 4; u32 numVertexElements = 4;
u32 stride = sizeof(Vertex); u32 stride = sizeof(Vertex);
glVertexAttribPointer(0, numVertexElements, GL_FLOAT, GL_FALSE, stride,
(GLvoid *)0);
glEnableVertexAttribArray(0);
GL_CHECK_ERROR(); glVertexAttribPointer(0, numVertexElements, GL_FLOAT,
GL_FALSE, stride, (GLvoid *)0);
/* Unbind */
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0); glBindVertexArray(0);
}
{
glBindVertexArray(renderer->vao[rendermode_triangle]);
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[rendermode_triangle]);
glEnableVertexAttribArray(0);
u32 numVertexElements = 3;
u32 stride = sizeof(Vertex);
glVertexAttribPointer(0, numVertexElements, GL_FLOAT,
GL_FALSE, stride, (GLvoid *)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
/* Unbind */
GL_CHECK_ERROR(); GL_CHECK_ERROR();
// TODO(doyle): Lazy allocate render group capacity // TODO(doyle): Lazy allocate render group capacity
@ -167,6 +182,8 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
initAssetManager(state); initAssetManager(state);
initRenderer(state, windowSize); initRenderer(state, windowSize);
state->pixelsPerMeter = 70.0f;
{ // Init ship entity { // Init ship entity
Entity *ship = &state->entityList[state->entityIndex++]; Entity *ship = &state->entityList[state->entityIndex++];
ship->id = 0; ship->id = 0;
@ -183,6 +200,7 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
state->camera.pos = V2(0, 0); state->camera.pos = V2(0, 0);
state->camera.size = state->renderer.size; state->camera.size = state->renderer.size;
state->init = TRUE; state->init = TRUE;
} }
{ {
@ -217,25 +235,75 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
if (entity->type == entitytype_ship) { if (entity->type == entitytype_ship) {
{ // Parse input
if (getKeyStatus(&state->input.keys[keycode_up], v2 acceleration = {0};
readkeytype_repeat, 0.0f, dt)) if (getKeyStatus(&state->input.keys[keycode_up], readkeytype_repeat,
0.0f, dt))
{ {
entity->pos.y += 10.0f; acceleration.y = 1.0f;
} }
if (getKeyStatus(&state->input.keys[keycode_down], if (getKeyStatus(&state->input.keys[keycode_down],
readkeytype_repeat, 0.0f, dt)) readkeytype_repeat, 0.0f, dt))
{ {
entity->pos.y -= 10.0f; acceleration.y = -1.0f;
}
} }
if (getKeyStatus(&state->input.keys[keycode_left],
readkeytype_repeat, 0.0f, dt))
{
acceleration.x = -1.0f;
}
if (getKeyStatus(&state->input.keys[keycode_right],
readkeytype_repeat, 0.0f, dt))
{
acceleration.x = 1.0f;
}
if (acceleration.x != 0.0f && acceleration.y != 0.0f)
{
// NOTE(doyle): Cheese it and pre-compute the vector for
// diagonal using pythagoras theorem on a unit triangle 1^2
// + 1^2 = c^2
acceleration = v2_scale(acceleration, 0.70710678118f);
}
/*
Assuming acceleration A over t time, then integrate twice to get
newVelocity = a*t + oldVelocity
newPos = (a*t^2)/2 + oldVelocity*t + oldPos
*/
acceleration = v2_scale(acceleration, state->pixelsPerMeter * 25);
v2 oldVelocity = entity->velocity;
v2 resistance = v2_scale(oldVelocity, 4.0f);
acceleration = v2_sub(acceleration, resistance);
entity->velocity = v2_add(v2_scale(acceleration, dt), oldVelocity);
v2 halfAcceleration = v2_scale(acceleration, 0.5f);
v2 halfAccelerationDtSquared =
v2_scale(halfAcceleration, (SQUARED(dt)));
v2 oldVelocityDt = v2_scale(oldVelocity, dt);
v2 oldPos = entity->pos;
entity->pos = v2_add(
v2_add(halfAccelerationDtSquared, oldVelocityDt), oldPos);
} }
renderer_entity(&state->renderer, state->camera, entity, V2(0, 0), 0, renderer_entity(&state->renderer, state->camera, entity, V2(0, 0), 0,
V4(1.0f, 1.0f, 1.0f, 1.0f)); V4(0.0f, 1.0f, 1.0f, 1.0f));
} }
RenderTex nullRenderTex = renderer_createNullRenderTex(&state->assetManager);
TrianglePoints triangle = {0};
triangle.points[0] = V2(100, 200);
triangle.points[2] = V2(100, 100);
triangle.points[1] = V2(200, 100);
renderer_triangle(&state->renderer, state->camera, triangle, V2(0, 0), 0,
nullRenderTex, V4(1, 1, 1, 1));
renderer_renderGroups(&state->renderer); renderer_renderGroups(&state->renderer);
} }

View File

@ -10,8 +10,19 @@
#define RENDER_BOUNDING_BOX FALSE #define RENDER_BOUNDING_BOX FALSE
typedef struct RenderQuad
{
Vertex vertex[4];
} RenderQuad_;
typedef struct RenderTriangle
{
Vertex vertex[3];
} RenderTriangle_;
INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color, INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color,
Vertex *vertexList, i32 numVertexes) Vertex *vertexList, i32 numVertexes,
enum RenderMode targetRenderMode)
{ {
#ifdef DENGINE_DEBUG #ifdef DENGINE_DEBUG
@ -32,7 +43,7 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color,
/* If the textures match and have the same color modulation, we can /* If the textures match and have the same color modulation, we can
* add these vertices to the current group */ * add these vertices to the current group */
if (group->tex->id == tex->id && if (group->tex->id == tex->id &&
v4_equals(group->color, color)) v4_equals(group->color, color) && group->mode == targetRenderMode)
{ {
groupIsValid = TRUE; groupIsValid = TRUE;
} }
@ -46,6 +57,7 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color,
group->vertexIndex++; group->vertexIndex++;
group->tex = tex; group->tex = tex;
group->color = color; group->color = color;
group->mode = targetRenderMode;
#ifdef DENGINE_DEBUG #ifdef DENGINE_DEBUG
debug_countIncrement(debugcount_renderGroups); debug_countIncrement(debugcount_renderGroups);
@ -90,37 +102,6 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color,
} }
} }
INTERNAL inline void addRenderQuadToRenderGroup(Renderer *renderer,
RenderQuad_ quad,
Texture *tex, v4 color)
{
/*
NOTE(doyle): Entity rendering is always done in two pairs of
triangles, i.e. quad. To batch render quads as a triangle strip, we
need to create zero-area triangles which OGL will omit from
rendering. Render groups are initialised with 1 degenerate vertex and
then the first two vertexes sent to the render group are the same to
form 1 zero-area triangle strip.
A degenerate vertex has to be copied from the last vertex in the
rendering quad, to repeat this process as more entities are
renderered.
Alternative implementation is recognising if the rendered
entity is the first in its render group, then we don't need to init
a degenerate vertex, and only at the end of its vertex list. But on
subsequent renders, we need a degenerate vertex at the front to
create the zero-area triangle strip.
The first has been chosen for simplicity of code, at the cost of
1 degenerate vertex at the start of each render group.
*/
Vertex vertexList[6] = {quad.vertex[0], quad.vertex[0], quad.vertex[1],
quad.vertex[2], quad.vertex[3], quad.vertex[3]};
addVertexToRenderGroup(renderer, tex, color, vertexList,
ARRAY_COUNT(vertexList));
};
INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY) INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY)
{ {
if (flipX) if (flipX)
@ -138,24 +119,19 @@ INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY)
} }
} }
INTERNAL void updateBufferObject(Renderer *const renderer, INTERNAL void bufferRenderGroupToGL(Renderer *renderer, RenderGroup *group)
const Vertex *const vertexList,
const i32 numVertex)
{ {
Vertex *vertexList = group->vertexList;
i32 numVertex = group->vertexIndex;
// TODO(doyle): We assume that vbo and vao are assigned // TODO(doyle): We assume that vbo and vao are assigned
renderer->numVertexesInVbo = numVertex; renderer->numVertexesInVbo = numVertex;
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[group->mode]);
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo);
glBufferData(GL_ARRAY_BUFFER, numVertex * sizeof(Vertex), vertexList, glBufferData(GL_ARRAY_BUFFER, numVertex * sizeof(Vertex), vertexList,
GL_STREAM_DRAW); GL_STREAM_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
INTERNAL void bufferRenderGroupToGL(Renderer *renderer, RenderGroup *group)
{
updateBufferObject(renderer, group->vertexList, group->vertexIndex);
}
INTERNAL RenderQuad_ createRenderQuad(Renderer *renderer, v2 pos, v2 size, INTERNAL RenderQuad_ createRenderQuad(Renderer *renderer, v2 pos, v2 size,
v2 pivotPoint, f32 rotate, v2 pivotPoint, f32 rotate,
RenderTex renderTex) RenderTex renderTex)
@ -237,6 +213,39 @@ INTERNAL RenderQuad_ createRenderQuad(Renderer *renderer, v2 pos, v2 size,
return result; return result;
} }
INTERNAL RenderTriangle_ createRenderTriangle(Renderer *renderer,
TrianglePoints triangle,
v2 pivotPoint, f32 rotate,
RenderTex renderTex)
{
/* Convert texture coordinates to normalised texture coordinates */
v4 texRectNdc = renderTex.texRect;
if (renderTex.tex)
{
v2 texNdcFactor =
V2(1.0f / renderTex.tex->width, 1.0f / renderTex.tex->height);
texRectNdc.e[0] *= texNdcFactor.w;
texRectNdc.e[1] *= texNdcFactor.h;
texRectNdc.e[2] *= texNdcFactor.w;
texRectNdc.e[3] *= texNdcFactor.h;
}
RenderTriangle_ result = {0};
result.vertex[0].pos = triangle.points[0];
result.vertex[0].texCoord = V2(texRectNdc.x, texRectNdc.w);
result.vertex[1].pos = triangle.points[1];
result.vertex[1].texCoord = V2(texRectNdc.x, texRectNdc.y);
result.vertex[2].pos = triangle.points[2];
result.vertex[2].texCoord = V2(texRectNdc.z, texRectNdc.w);
if (rotate == 0) return result;
return result;
}
INTERNAL inline RenderQuad_ INTERNAL inline RenderQuad_
createDefaultTexQuad(Renderer *renderer, RenderTex renderTex) createDefaultTexQuad(Renderer *renderer, RenderTex renderTex)
{ {
@ -248,6 +257,7 @@ createDefaultTexQuad(Renderer *renderer, RenderTex renderTex)
INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup) INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup)
{ {
ASSERT(renderGroup->mode < rendermode_invalid);
/* Load transformation matrix */ /* Load transformation matrix */
shader_use(renderer->shader); shader_use(renderer->shader);
GL_CHECK_ERROR(); GL_CHECK_ERROR();
@ -255,10 +265,11 @@ INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup)
/* Set color modulation value */ /* Set color modulation value */
shader_uniformSetVec4f(renderer->shader, "spriteColor", shader_uniformSetVec4f(renderer->shader, "spriteColor",
renderGroup->color); renderGroup->color);
GL_CHECK_ERROR();
/* Send draw calls */ /* Send draw calls */
#if RENDER_BOUNDING_BOX #if RENDER_BOUNDING_BOX
glBindVertexArray(renderer->vao); glBindVertexArray(renderer->vao[renderGroup->mode]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo); glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
glBindVertexArray(0); glBindVertexArray(0);
#endif #endif
@ -271,8 +282,9 @@ INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup)
shader_uniformSet1i(renderer->shader, "tex", 0); shader_uniformSet1i(renderer->shader, "tex", 0);
} }
glBindVertexArray(renderer->vao); glBindVertexArray(renderer->vao[renderGroup->mode]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo); glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
GL_CHECK_ERROR();
#ifdef DENGINE_DEBUG #ifdef DENGINE_DEBUG
debug_countIncrement(debugcount_drawArrays); debug_countIncrement(debugcount_drawArrays);
@ -298,7 +310,56 @@ void renderer_rect(Renderer *const renderer, Rect camera, v2 pos, v2 size,
v2 posInCameraSpace = v2_sub(pos, camera.pos); v2 posInCameraSpace = v2_sub(pos, camera.pos);
RenderQuad_ quad = createRenderQuad(renderer, posInCameraSpace, size, RenderQuad_ quad = createRenderQuad(renderer, posInCameraSpace, size,
pivotPoint, rotate, renderTex); pivotPoint, rotate, renderTex);
addRenderQuadToRenderGroup(renderer, quad, renderTex.tex, color);
{ // addRenderQuadToRenderGroup
/*
NOTE(doyle): Entity rendering is always done in two pairs of
triangles, i.e. quad. To batch render quads as a triangle strip, we
need to create zero-area triangles which OGL will omit from
rendering. Render groups are initialised with 1 degenerate vertex and
then the first two vertexes sent to the render group are the same to
form 1 zero-area triangle strip.
A degenerate vertex has to be copied from the last vertex in the
rendering quad, to repeat this process as more entities are
renderered.
Alternative implementation is recognising if the rendered
entity is the first in its render group, then we don't need to init
a degenerate vertex, and only at the end of its vertex list. But on
subsequent renders, we need a degenerate vertex at the front to
create the zero-area triangle strip.
The first has been chosen for simplicity of code, at the cost of
1 degenerate vertex at the start of each render group.
*/
Vertex vertexList[6] = {quad.vertex[0], quad.vertex[0], quad.vertex[1],
quad.vertex[2], quad.vertex[3], quad.vertex[3]};
addVertexToRenderGroup(renderer, renderTex.tex, color, vertexList,
ARRAY_COUNT(vertexList), rendermode_quad);
}
}
void renderer_triangle(Renderer *const renderer, Rect camera,
TrianglePoints triangle, v2 pivotPoint, f32 rotate,
RenderTex renderTex, v4 color)
{
TrianglePoints triangleInCamSpace = {0};
ASSERT(ARRAY_COUNT(triangle.points) ==
ARRAY_COUNT(triangleInCamSpace.points));
for (i32 i = 0; i < ARRAY_COUNT(triangleInCamSpace.points); i++)
{
triangleInCamSpace.points[i] = v2_sub(triangle.points[i], camera.pos);
}
RenderTriangle_ renderTriangle = createRenderTriangle(
renderer, triangleInCamSpace, pivotPoint, rotate, renderTex);
addVertexToRenderGroup(
renderer, renderTex.tex, color, renderTriangle.vertex,
ARRAY_COUNT(renderTriangle.vertex), rendermode_triangle);
} }
void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera, void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera,
@ -361,7 +422,7 @@ void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera,
} }
addVertexToRenderGroup(renderer, tex, color, vertexList, addVertexToRenderGroup(renderer, tex, color, vertexList,
numVertexesToAlloc); numVertexesToAlloc, rendermode_quad);
// TODO(doyle): Mem free // TODO(doyle): Mem free
// PLATFORM_MEM_FREE(arena, vertexList, // PLATFORM_MEM_FREE(arena, vertexList,
// sizeof(Vertex) * numVertexesToAlloc); // sizeof(Vertex) * numVertexesToAlloc);

View File

@ -233,7 +233,7 @@ i32 main(void)
char textBuffer[256]; char textBuffer[256];
snprintf(textBuffer, ARRAY_COUNT(textBuffer), snprintf(textBuffer, ARRAY_COUNT(textBuffer),
"Dengine | %f ms/f | %f fps | Entity Count: %d", "Dengine | %f ms/f | %f fps | Entity Count: %d",
msPerFrame, framesPerSecond, 0); msPerFrame, framesPerSecond, gameState.entityIndex);
glfwSetWindowTitle(window, textBuffer); glfwSetWindowTitle(window, textBuffer);
titleUpdateFrequencyInSeconds = 0.5f; titleUpdateFrequencyInSeconds = 0.5f;

View File

@ -13,6 +13,7 @@ typedef struct GameState {
Entity entityList[1024]; Entity entityList[1024];
i32 entityIndex; i32 entityIndex;
f32 pixelsPerMeter;
Rect camera; Rect camera;

View File

@ -39,8 +39,8 @@ typedef struct Entity
i32 childIds[8]; i32 childIds[8];
i32 numChilds; i32 numChilds;
v2 pos; // Position v2 pos;
v2 dPos; // Velocity v2 velocity;
v2 hitbox; v2 hitbox;
v2 size; v2 size;

View File

@ -24,25 +24,36 @@ typedef struct RenderTex
v4 texRect; v4 texRect;
} RenderTex; } RenderTex;
typedef struct RenderQuad typedef struct TrianglePoints {
{ v2 points[3];
Vertex vertex[4]; } TrianglePoints;
} RenderQuad_;
typedef struct RenderGroup typedef struct RenderGroup
{ {
enum RenderMode mode;
Texture *tex; Texture *tex;
v4 color; v4 color;
Vertex *vertexList; Vertex *vertexList;
i32 vertexIndex; i32 vertexIndex;
} RenderGroup; } RenderGroup;
enum RenderMode
{
rendermode_quad,
rendermode_triangle,
rendermode_count,
rendermode_invalid,
};
typedef struct Renderer typedef struct Renderer
{ {
Shader *shader; Shader *shader;
u32 vao;
u32 vbo; u32 vao[rendermode_count];
u32 vbo[rendermode_count];
i32 numVertexesInVbo; i32 numVertexesInVbo;
v2 vertexNdcFactor; v2 vertexNdcFactor;
v2 size; v2 size;
@ -52,7 +63,8 @@ typedef struct Renderer
} Renderer; } Renderer;
// TODO(doyle): Use z-index occluding for rendering // TODO(doyle): Use z-index occluding for rendering
RenderTex renderer_createNullRenderTex(AssetManager *const assetManager); RenderTex
renderer_createNullRenderTex(AssetManager *const assetManager);
// TODO(doyle): Clean up lines // TODO(doyle): Clean up lines
// Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); } // Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); }
@ -68,6 +80,10 @@ inline void renderer_staticRect(Renderer *const renderer, v2 pos, v2 size,
renderTex, color); renderTex, color);
} }
void renderer_triangle(Renderer *const renderer, Rect camera,
TrianglePoints triangle, v2 pivotPoint, f32 rotate,
RenderTex renderTex, v4 color);
void renderer_string(Renderer *const renderer, MemoryArena_ *arena, void renderer_string(Renderer *const renderer, MemoryArena_ *arena,
Rect camera, Font *const font, Rect camera, Font *const font,
const char *const string, v2 pos, v2 pivotPoint, const char *const string, v2 pos, v2 pivotPoint,