Add primitive triangle rendering code
This commit is contained in:
parent
4795fe1cb1
commit
6d67485d49
Binary file not shown.
106
src/Asteroid.c
106
src/Asteroid.c
@ -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);
|
||||
}
|
||||
|
155
src/Renderer.c
155
src/Renderer.c
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -13,6 +13,7 @@ typedef struct GameState {
|
||||
|
||||
Entity entityList[1024];
|
||||
i32 entityIndex;
|
||||
f32 pixelsPerMeter;
|
||||
|
||||
Rect camera;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user