Improved render groups, use degenerate triangles
Degenerate triangles allows a single triangle strip call to be called on multiple independent triangles without "connecting" strips inbetween the triangles.
This commit is contained in:
parent
ec7a9e41ff
commit
ebe09bd345
164
src/Renderer.c
164
src/Renderer.c
@ -10,8 +10,8 @@
|
||||
|
||||
#define RENDER_BOUNDING_BOX FALSE
|
||||
|
||||
INTERNAL addToRenderGroup(Renderer *renderer, Texture *texture,
|
||||
RenderQuad renderQuad)
|
||||
INTERNAL void addToRenderGroup(Renderer *renderer, Texture *texture,
|
||||
Vertex *vertexList, i32 numVertexes)
|
||||
{
|
||||
/* Find vacant/matching render group */
|
||||
RenderGroup *targetGroup = NULL;
|
||||
@ -20,8 +20,19 @@ INTERNAL addToRenderGroup(Renderer *renderer, Texture *texture,
|
||||
RenderGroup *group = &renderer->groups[i];
|
||||
if (group->tex == NULL || group->tex->id == texture->id)
|
||||
{
|
||||
if (!group->tex) group->tex = texture;
|
||||
i32 freeVertexSlots = renderer->groupCapacity - group->vertexIndex;
|
||||
if (numVertexes < freeVertexSlots)
|
||||
{
|
||||
if (!group->tex)
|
||||
{
|
||||
// NOTE(doyle): Mark first vertex as degenerate vertex
|
||||
group->vertexIndex++;
|
||||
|
||||
group->tex = texture;
|
||||
}
|
||||
|
||||
targetGroup = &renderer->groups[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -29,18 +40,21 @@ INTERNAL addToRenderGroup(Renderer *renderer, Texture *texture,
|
||||
/* Valid group, add to the render group for rendering */
|
||||
if (targetGroup)
|
||||
{
|
||||
if (targetGroup->quadIndex < ARRAY_COUNT(targetGroup->quads))
|
||||
for (i32 i = 0; i < numVertexes; i++)
|
||||
{
|
||||
targetGroup->quads[targetGroup->quadIndex++] = renderQuad;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(doyle): Log no remaining render quad slots in group
|
||||
targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(doyle): Log no remaining render groups
|
||||
DEBUG_LOG(
|
||||
"WARNING: All render groups used up, some items will not be "
|
||||
"rendered!");
|
||||
|
||||
printf(
|
||||
"WARNING: All render groups used up, some items will not be "
|
||||
"rendered!\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,29 +76,42 @@ INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY)
|
||||
}
|
||||
|
||||
INTERNAL void updateBufferObject(Renderer *const renderer,
|
||||
RenderQuad *const quads, const i32 numQuads)
|
||||
const Vertex *const vertexList,
|
||||
const i32 numVertex)
|
||||
{
|
||||
// TODO(doyle): We assume that vbo and vao are assigned
|
||||
const i32 numVertexesInQuad = 4;
|
||||
renderer->numVertexesInVbo = numQuads * numVertexesInQuad;
|
||||
renderer->numVertexesInVbo = numVertex;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, numQuads * sizeof(RenderQuad), quads,
|
||||
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->quads, group->quadIndex);
|
||||
updateBufferObject(renderer, group->vertexList, group->vertexIndex);
|
||||
}
|
||||
|
||||
INTERNAL RenderQuad createTexQuad(Renderer *renderer, v4 quadRect,
|
||||
INTERNAL RenderQuad_ createTexQuad(Renderer *renderer, v4 quadRect,
|
||||
RenderTex renderTex)
|
||||
{
|
||||
/*
|
||||
* Rendering order
|
||||
*
|
||||
* 0 2
|
||||
* +---+
|
||||
* + /+
|
||||
* + / +
|
||||
* +/ +
|
||||
* +---+
|
||||
* 1 3
|
||||
*
|
||||
*/
|
||||
|
||||
// NOTE(doyle): Draws a series of triangles using vertices v0, v1, v2, then
|
||||
// v2, v1, v3 (note the order)
|
||||
RenderQuad result = {0};
|
||||
RenderQuad_ result = {0};
|
||||
|
||||
/* Convert screen coordinates to normalised device coordinates */
|
||||
v4 quadRectNdc = quadRect;
|
||||
@ -106,21 +133,21 @@ INTERNAL RenderQuad createTexQuad(Renderer *renderer, v4 quadRect,
|
||||
}
|
||||
|
||||
/* Form the quad */
|
||||
result.vertex[0] = V4(quadRectNdc.x, quadRectNdc.w, texRectNdc.x,
|
||||
result.vertex[0].e = V4(quadRectNdc.x, quadRectNdc.w, texRectNdc.x,
|
||||
texRectNdc.w); // Top left
|
||||
result.vertex[1] = V4(quadRectNdc.x, quadRectNdc.y, texRectNdc.x,
|
||||
result.vertex[1].e = V4(quadRectNdc.x, quadRectNdc.y, texRectNdc.x,
|
||||
texRectNdc.y); // Bottom left
|
||||
result.vertex[2] = V4(quadRectNdc.z, quadRectNdc.w, texRectNdc.z,
|
||||
result.vertex[2].e = V4(quadRectNdc.z, quadRectNdc.w, texRectNdc.z,
|
||||
texRectNdc.w); // Top right
|
||||
result.vertex[3] = V4(quadRectNdc.z, quadRectNdc.y, texRectNdc.z,
|
||||
result.vertex[3].e = V4(quadRectNdc.z, quadRectNdc.y, texRectNdc.z,
|
||||
texRectNdc.y); // Bottom right
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline RenderQuad
|
||||
INTERNAL inline RenderQuad_
|
||||
createDefaultTexQuad(Renderer *renderer, RenderTex renderTex)
|
||||
{
|
||||
RenderQuad result = {0};
|
||||
RenderQuad_ result = {0};
|
||||
v4 defaultQuad = V4(0.0f, 0.0f, renderer->size.w, renderer->size.h);
|
||||
result = createTexQuad(renderer, defaultQuad, renderTex);
|
||||
return result;
|
||||
@ -193,10 +220,11 @@ RenderTex renderer_createNullRenderTex(AssetManager *const assetManager)
|
||||
void renderer_rect(Renderer *const renderer, Rect camera, v2 pos, v2 size,
|
||||
v2 pivotPoint, f32 rotate, RenderTex renderTex, v4 color)
|
||||
{
|
||||
RenderQuad quad = createDefaultTexQuad(renderer, renderTex);
|
||||
updateBufferObject(renderer, &quad, 1);
|
||||
|
||||
// TODO(doyle): Use render groups
|
||||
v2 posInCameraSpace = v2_sub(pos, camera.pos);
|
||||
RenderQuad_ quad = createDefaultTexQuad(renderer, renderTex);
|
||||
updateBufferObject(renderer, quad.vertex, ARRAY_COUNT(quad.vertex));
|
||||
|
||||
renderObject(renderer, posInCameraSpace, size, pivotPoint, rotate,
|
||||
color, renderTex.tex);
|
||||
}
|
||||
@ -217,8 +245,21 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera,
|
||||
if (math_pointInRect(camera, leftAlignedP) ||
|
||||
math_pointInRect(camera, rightAlignedP))
|
||||
{
|
||||
i32 quadIndex = 0;
|
||||
RenderQuad *stringQuads = PLATFORM_MEM_ALLOC(arena, strLen, RenderQuad);
|
||||
|
||||
#define DISABLE_TEXT_RENDER_GROUPS TRUE
|
||||
#if RENDERER_USE_RENDER_GROUPS && !DISABLE_TEXT_RENDER_GROUPS
|
||||
// NOTE(doyle): 2 degenerate vertexes, at start and end of string since-
|
||||
// chars are rendered side by side. Reserve first vertex as degenerate.
|
||||
i32 vertexIndex = 1;
|
||||
const i32 numVertexPerQuad = 4;
|
||||
const i32 numVertexesToAlloc = (strLen * numVertexPerQuad) + 2;
|
||||
#else
|
||||
i32 vertexIndex = 0;
|
||||
const i32 numVertexPerQuad = 4;
|
||||
const i32 numVertexesToAlloc = (strLen * numVertexPerQuad);
|
||||
#endif
|
||||
Vertex *vertexList =
|
||||
PLATFORM_MEM_ALLOC(arena, numVertexesToAlloc, Vertex);
|
||||
|
||||
v2 posInCameraSpace = v2_sub(pos, camera.pos);
|
||||
|
||||
@ -250,18 +291,32 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera,
|
||||
flipTexCoord(&deprecatedTexRect, FALSE, TRUE);
|
||||
|
||||
RenderTex renderTex = {tex, deprecatedTexRect};
|
||||
RenderQuad charQuad =
|
||||
RenderQuad_ charQuad =
|
||||
createTexQuad(renderer, charRectOnScreen, renderTex);
|
||||
stringQuads[quadIndex++] = charQuad;
|
||||
|
||||
for (i32 i = 0; i < ARRAY_COUNT(charQuad.vertex); i++)
|
||||
{
|
||||
vertexList[vertexIndex++] = charQuad.vertex[i];
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(doyle): We render at the renderer's size because we create quads
|
||||
// relative to the window size, hence we also render at the origin since
|
||||
// we're rendering a window sized buffer
|
||||
updateBufferObject(renderer, stringQuads, quadIndex);
|
||||
|
||||
// TODO(doyle): Render group differentiate between null tex and colors
|
||||
#if RENDERER_USE_RENDER_GROUPS && !DISABLE_TEXT_RENDER_GROUPS
|
||||
// NOTE(doyle): Degenerate vertex at end of string
|
||||
vertexList[vertexIndex++] = vertexList[vertexIndex - 1];
|
||||
addToRenderGroup(renderer, tex, vertexList, numVertexesToAlloc);
|
||||
#else
|
||||
updateBufferObject(renderer, vertexList, vertexIndex);
|
||||
renderObject(renderer, V2(0.0f, 0.0f), renderer->size, pivotPoint,
|
||||
rotate, color, tex);
|
||||
PLATFORM_MEM_FREE(arena, stringQuads, strLen * sizeof(RenderQuad));
|
||||
#endif
|
||||
|
||||
PLATFORM_MEM_FREE(arena, vertexList,
|
||||
sizeof(Vertex) * numVertexesToAlloc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,19 +351,49 @@ void renderer_entity(Renderer *renderer, Rect camera, Entity *entity,
|
||||
|
||||
RenderTex renderTex = {entity->tex, animTexRect};
|
||||
|
||||
#if RENDERER_USE_RENDER_GROUPS
|
||||
// TODO(doyle): getRect needs a better name
|
||||
v2 posInCameraSpace = v2_sub(entity->pos, camera.pos);
|
||||
|
||||
#if RENDERER_USE_RENDER_GROUPS
|
||||
v4 entityVertexOnScreen = math_getRect(posInCameraSpace, entity->size);
|
||||
RenderQuad entityQuad =
|
||||
RenderQuad_ entityQuad =
|
||||
createTexQuad(renderer, entityVertexOnScreen, renderTex);
|
||||
|
||||
addToRenderGroup(renderer, entity->tex, entityQuad);
|
||||
/*
|
||||
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
|
||||
2 degenerate vertexes at the start of each render group.
|
||||
*/
|
||||
Vertex degenerateVertexes[2] = {entityQuad.vertex[0],
|
||||
entityQuad.vertex[3]};
|
||||
|
||||
Vertex vertexList[6] = {degenerateVertexes[0], entityQuad.vertex[0],
|
||||
entityQuad.vertex[1], entityQuad.vertex[2],
|
||||
entityQuad.vertex[3], degenerateVertexes[1]};
|
||||
|
||||
addToRenderGroup(renderer, entity->tex, vertexList,
|
||||
ARRAY_COUNT(vertexList));
|
||||
#else
|
||||
RenderQuad entityQuad = createDefaultTexQuad(renderer, renderTex);
|
||||
RenderQuad_ entityQuad = createDefaultTexQuad(renderer, renderTex);
|
||||
// TODO(doyle): getRect needs a better name
|
||||
updateBufferObject(renderer, &entityQuad, 1);
|
||||
v2 posInCameraSpace = v2_sub(entity->pos, camera.pos);
|
||||
updateBufferObject(renderer, entityQuad.vertex,
|
||||
ARRAY_COUNT(entityQuad.vertex));
|
||||
renderObject(renderer, posInCameraSpace,
|
||||
entity->size, pivotPoint,
|
||||
entity->rotation + rotate, color, entity->tex);
|
||||
@ -331,8 +416,9 @@ void renderer_renderGroups(Renderer *renderer)
|
||||
renderObject(renderer, V2(0.0f, 0.0f), renderer->size, pivotPoint,
|
||||
rotate, color, currGroup->tex);
|
||||
|
||||
RenderGroup clear = {0};
|
||||
*currGroup = clear;
|
||||
RenderGroup cleanGroup = {0};
|
||||
cleanGroup.vertexList = currGroup->vertexList;
|
||||
*currGroup = cleanGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ INTERNAL void rendererInit(GameState *state, v2 windowSize)
|
||||
|
||||
/* Configure VAO */
|
||||
const GLuint numVertexElements = 4;
|
||||
const GLuint vertexSize = sizeof(v4);
|
||||
const GLuint vertexSize = sizeof(Vertex);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, numVertexElements, GL_FLOAT, GL_FALSE, vertexSize,
|
||||
(GLvoid *)0);
|
||||
@ -99,6 +99,14 @@ INTERNAL void rendererInit(GameState *state, v2 windowSize)
|
||||
glBindVertexArray(0);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
// TODO(doyle): Lazy allocate render group capacity
|
||||
renderer->groupCapacity = 1024;
|
||||
for (i32 i = 0; i < ARRAY_COUNT(renderer->groups); i++)
|
||||
{
|
||||
renderer->groups[i].vertexList =
|
||||
PLATFORM_MEM_ALLOC(&state->arena, renderer->groupCapacity, Vertex);
|
||||
}
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Renderer initialised");
|
||||
#endif
|
||||
@ -1673,7 +1681,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
}
|
||||
|
||||
if (getKeyStatus(&keys[keycode_left_square_bracket],
|
||||
readkeytype_delayedRepeat, 0.25f, dt))
|
||||
readkeytype_delayedRepeat, 0.0f, dt))
|
||||
{
|
||||
|
||||
Renderer *renderer = &state->renderer;
|
||||
|
@ -12,13 +12,22 @@ typedef struct MemoryArena MemoryArena;
|
||||
typedef struct Shader Shader;
|
||||
typedef struct Texture Texture;
|
||||
|
||||
typedef union Vertex
|
||||
{
|
||||
|
||||
struct
|
||||
{
|
||||
v2 pos;
|
||||
v2 texCoords;
|
||||
};
|
||||
|
||||
v4 e;
|
||||
} Vertex;
|
||||
|
||||
typedef struct RenderQuad
|
||||
{
|
||||
// Vertex composition
|
||||
// x, y: Coordinates - of entity on screen
|
||||
// z, w: Texture Coords - of texture for this quad
|
||||
v4 vertex[4];
|
||||
} RenderQuad;
|
||||
Vertex vertex[4];
|
||||
} RenderQuad_;
|
||||
|
||||
typedef struct RenderTex
|
||||
{
|
||||
@ -30,8 +39,9 @@ typedef struct RenderTex
|
||||
typedef struct RenderGroup
|
||||
{
|
||||
Texture *tex;
|
||||
RenderQuad quads[100];
|
||||
i32 quadIndex;
|
||||
|
||||
Vertex *vertexList;
|
||||
i32 vertexIndex;
|
||||
} RenderGroup;
|
||||
|
||||
typedef struct Renderer
|
||||
@ -43,7 +53,8 @@ typedef struct Renderer
|
||||
v2 vertexNdcFactor;
|
||||
v2 size;
|
||||
|
||||
RenderGroup groups[100];
|
||||
RenderGroup groups[16];
|
||||
i32 groupCapacity;
|
||||
} Renderer;
|
||||
|
||||
#define RENDERER_USE_RENDER_GROUPS TRUE
|
||||
|
Loading…
Reference in New Issue
Block a user