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();
/* Create buffers */
glGenVertexArrays(1, &renderer->vao);
glGenBuffers(1, &renderer->vbo);
glGenVertexArrays(ARRAY_COUNT(renderer->vao), renderer->vao);
glGenBuffers(ARRAY_COUNT(renderer->vbo), renderer->vbo);
GL_CHECK_ERROR();
/* Bind buffers */
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo);
glBindVertexArray(renderer->vao);
{
// Bind buffers and configure vao, vao automatically intercepts
// 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 stride = sizeof(Vertex);
glVertexAttribPointer(0, numVertexElements, GL_FLOAT, GL_FALSE, stride,
(GLvoid *)0);
glEnableVertexAttribArray(0);
GL_CHECK_ERROR();
/* Unbind */
glVertexAttribPointer(0, numVertexElements, GL_FLOAT,
GL_FALSE, stride, (GLvoid *)0);
glBindBuffer(GL_ARRAY_BUFFER, 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();
// TODO(doyle): Lazy allocate render group capacity
@ -167,6 +182,8 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
initAssetManager(state);
initRenderer(state, windowSize);
state->pixelsPerMeter = 70.0f;
{ // Init ship entity
Entity *ship = &state->entityList[state->entityIndex++];
ship->id = 0;
@ -183,6 +200,7 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
state->camera.pos = V2(0, 0);
state->camera.size = state->renderer.size;
state->init = TRUE;
}
{
@ -217,25 +235,75 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
if (entity->type == entitytype_ship) {
{ // Parse input
if (getKeyStatus(&state->input.keys[keycode_up],
readkeytype_repeat, 0.0f, dt))
v2 acceleration = {0};
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],
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,
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);
}

View File

@ -10,8 +10,19 @@
#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,
Vertex *vertexList, i32 numVertexes)
Vertex *vertexList, i32 numVertexes,
enum RenderMode targetRenderMode)
{
#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
* add these vertices to the current group */
if (group->tex->id == tex->id &&
v4_equals(group->color, color))
v4_equals(group->color, color) && group->mode == targetRenderMode)
{
groupIsValid = TRUE;
}
@ -46,6 +57,7 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color,
group->vertexIndex++;
group->tex = tex;
group->color = color;
group->mode = targetRenderMode;
#ifdef DENGINE_DEBUG
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)
{
if (flipX)
@ -138,24 +119,19 @@ INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY)
}
}
INTERNAL void updateBufferObject(Renderer *const renderer,
const Vertex *const vertexList,
const i32 numVertex)
INTERNAL void bufferRenderGroupToGL(Renderer *renderer, RenderGroup *group)
{
Vertex *vertexList = group->vertexList;
i32 numVertex = group->vertexIndex;
// TODO(doyle): We assume that vbo and vao are assigned
renderer->numVertexesInVbo = numVertex;
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo);
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[group->mode]);
glBufferData(GL_ARRAY_BUFFER, numVertex * sizeof(Vertex), vertexList,
GL_STREAM_DRAW);
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,
v2 pivotPoint, f32 rotate,
RenderTex renderTex)
@ -237,6 +213,39 @@ INTERNAL RenderQuad_ createRenderQuad(Renderer *renderer, v2 pos, v2 size,
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_
createDefaultTexQuad(Renderer *renderer, RenderTex renderTex)
{
@ -248,6 +257,7 @@ createDefaultTexQuad(Renderer *renderer, RenderTex renderTex)
INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup)
{
ASSERT(renderGroup->mode < rendermode_invalid);
/* Load transformation matrix */
shader_use(renderer->shader);
GL_CHECK_ERROR();
@ -255,10 +265,11 @@ INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup)
/* Set color modulation value */
shader_uniformSetVec4f(renderer->shader, "spriteColor",
renderGroup->color);
GL_CHECK_ERROR();
/* Send draw calls */
#if RENDER_BOUNDING_BOX
glBindVertexArray(renderer->vao);
glBindVertexArray(renderer->vao[renderGroup->mode]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
glBindVertexArray(0);
#endif
@ -271,8 +282,9 @@ INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup)
shader_uniformSet1i(renderer->shader, "tex", 0);
}
glBindVertexArray(renderer->vao);
glBindVertexArray(renderer->vao[renderGroup->mode]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
GL_CHECK_ERROR();
#ifdef DENGINE_DEBUG
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);
RenderQuad_ quad = createRenderQuad(renderer, posInCameraSpace, size,
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,
@ -361,7 +422,7 @@ void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera,
}
addVertexToRenderGroup(renderer, tex, color, vertexList,
numVertexesToAlloc);
numVertexesToAlloc, rendermode_quad);
// TODO(doyle): Mem free
// PLATFORM_MEM_FREE(arena, vertexList,
// sizeof(Vertex) * numVertexesToAlloc);

View File

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

View File

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

View File

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

View File

@ -24,25 +24,36 @@ typedef struct RenderTex
v4 texRect;
} RenderTex;
typedef struct RenderQuad
{
Vertex vertex[4];
} RenderQuad_;
typedef struct TrianglePoints {
v2 points[3];
} TrianglePoints;
typedef struct RenderGroup
{
enum RenderMode mode;
Texture *tex;
v4 color;
Vertex *vertexList;
i32 vertexIndex;
} RenderGroup;
enum RenderMode
{
rendermode_quad,
rendermode_triangle,
rendermode_count,
rendermode_invalid,
};
typedef struct Renderer
{
Shader *shader;
u32 vao;
u32 vbo;
u32 vao[rendermode_count];
u32 vbo[rendermode_count];
i32 numVertexesInVbo;
v2 vertexNdcFactor;
v2 size;
@ -52,7 +63,8 @@ typedef struct Renderer
} Renderer;
// 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
// Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); }
@ -68,6 +80,10 @@ inline void renderer_staticRect(Renderer *const renderer, v2 pos, v2 size,
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,
Rect camera, Font *const font,
const char *const string, v2 pos, v2 pivotPoint,