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
178
src/Renderer.c
178
src/Renderer.c
@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
#define RENDER_BOUNDING_BOX FALSE
|
#define RENDER_BOUNDING_BOX FALSE
|
||||||
|
|
||||||
INTERNAL addToRenderGroup(Renderer *renderer, Texture *texture,
|
INTERNAL void addToRenderGroup(Renderer *renderer, Texture *texture,
|
||||||
RenderQuad renderQuad)
|
Vertex *vertexList, i32 numVertexes)
|
||||||
{
|
{
|
||||||
/* Find vacant/matching render group */
|
/* Find vacant/matching render group */
|
||||||
RenderGroup *targetGroup = NULL;
|
RenderGroup *targetGroup = NULL;
|
||||||
@ -20,8 +20,19 @@ INTERNAL addToRenderGroup(Renderer *renderer, Texture *texture,
|
|||||||
RenderGroup *group = &renderer->groups[i];
|
RenderGroup *group = &renderer->groups[i];
|
||||||
if (group->tex == NULL || group->tex->id == texture->id)
|
if (group->tex == NULL || group->tex->id == texture->id)
|
||||||
{
|
{
|
||||||
if (!group->tex) group->tex = texture;
|
i32 freeVertexSlots = renderer->groupCapacity - group->vertexIndex;
|
||||||
targetGroup = &renderer->groups[i];
|
if (numVertexes < freeVertexSlots)
|
||||||
|
{
|
||||||
|
if (!group->tex)
|
||||||
|
{
|
||||||
|
// NOTE(doyle): Mark first vertex as degenerate vertex
|
||||||
|
group->vertexIndex++;
|
||||||
|
|
||||||
|
group->tex = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetGroup = &renderer->groups[i];
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,18 +40,21 @@ INTERNAL addToRenderGroup(Renderer *renderer, Texture *texture,
|
|||||||
/* Valid group, add to the render group for rendering */
|
/* Valid group, add to the render group for rendering */
|
||||||
if (targetGroup)
|
if (targetGroup)
|
||||||
{
|
{
|
||||||
if (targetGroup->quadIndex < ARRAY_COUNT(targetGroup->quads))
|
for (i32 i = 0; i < numVertexes; i++)
|
||||||
{
|
{
|
||||||
targetGroup->quads[targetGroup->quadIndex++] = renderQuad;
|
targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[i];
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO(doyle): Log no remaining render quad slots in group
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO(doyle): Log no remaining render groups
|
// 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,
|
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
|
// TODO(doyle): We assume that vbo and vao are assigned
|
||||||
const i32 numVertexesInQuad = 4;
|
renderer->numVertexesInVbo = numVertex;
|
||||||
renderer->numVertexesInVbo = numQuads * numVertexesInQuad;
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo);
|
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);
|
GL_STREAM_DRAW);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL void bufferRenderGroupToGL(Renderer *renderer, RenderGroup *group)
|
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)
|
RenderTex renderTex)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Rendering order
|
||||||
|
*
|
||||||
|
* 0 2
|
||||||
|
* +---+
|
||||||
|
* + /+
|
||||||
|
* + / +
|
||||||
|
* +/ +
|
||||||
|
* +---+
|
||||||
|
* 1 3
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
// NOTE(doyle): Draws a series of triangles using vertices v0, v1, v2, then
|
// NOTE(doyle): Draws a series of triangles using vertices v0, v1, v2, then
|
||||||
// v2, v1, v3 (note the order)
|
// v2, v1, v3 (note the order)
|
||||||
RenderQuad result = {0};
|
RenderQuad_ result = {0};
|
||||||
|
|
||||||
/* Convert screen coordinates to normalised device coordinates */
|
/* Convert screen coordinates to normalised device coordinates */
|
||||||
v4 quadRectNdc = quadRect;
|
v4 quadRectNdc = quadRect;
|
||||||
@ -106,21 +133,21 @@ INTERNAL RenderQuad createTexQuad(Renderer *renderer, v4 quadRect,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Form the quad */
|
/* 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
|
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
|
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
|
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
|
texRectNdc.y); // Bottom right
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL inline RenderQuad
|
INTERNAL inline RenderQuad_
|
||||||
createDefaultTexQuad(Renderer *renderer, RenderTex renderTex)
|
createDefaultTexQuad(Renderer *renderer, RenderTex renderTex)
|
||||||
{
|
{
|
||||||
RenderQuad result = {0};
|
RenderQuad_ result = {0};
|
||||||
v4 defaultQuad = V4(0.0f, 0.0f, renderer->size.w, renderer->size.h);
|
v4 defaultQuad = V4(0.0f, 0.0f, renderer->size.w, renderer->size.h);
|
||||||
result = createTexQuad(renderer, defaultQuad, renderTex);
|
result = createTexQuad(renderer, defaultQuad, renderTex);
|
||||||
return result;
|
return result;
|
||||||
@ -193,10 +220,11 @@ RenderTex renderer_createNullRenderTex(AssetManager *const assetManager)
|
|||||||
void renderer_rect(Renderer *const renderer, Rect camera, v2 pos, v2 size,
|
void renderer_rect(Renderer *const renderer, Rect camera, v2 pos, v2 size,
|
||||||
v2 pivotPoint, f32 rotate, RenderTex renderTex, v4 color)
|
v2 pivotPoint, f32 rotate, RenderTex renderTex, v4 color)
|
||||||
{
|
{
|
||||||
RenderQuad quad = createDefaultTexQuad(renderer, renderTex);
|
// TODO(doyle): Use render groups
|
||||||
updateBufferObject(renderer, &quad, 1);
|
|
||||||
|
|
||||||
v2 posInCameraSpace = v2_sub(pos, camera.pos);
|
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,
|
renderObject(renderer, posInCameraSpace, size, pivotPoint, rotate,
|
||||||
color, renderTex.tex);
|
color, renderTex.tex);
|
||||||
}
|
}
|
||||||
@ -217,8 +245,21 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera,
|
|||||||
if (math_pointInRect(camera, leftAlignedP) ||
|
if (math_pointInRect(camera, leftAlignedP) ||
|
||||||
math_pointInRect(camera, rightAlignedP))
|
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);
|
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);
|
flipTexCoord(&deprecatedTexRect, FALSE, TRUE);
|
||||||
|
|
||||||
RenderTex renderTex = {tex, deprecatedTexRect};
|
RenderTex renderTex = {tex, deprecatedTexRect};
|
||||||
RenderQuad charQuad =
|
RenderQuad_ charQuad =
|
||||||
createTexQuad(renderer, charRectOnScreen, renderTex);
|
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
|
// 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
|
// relative to the window size, hence we also render at the origin since
|
||||||
// we're rendering a window sized buffer
|
// 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,
|
renderObject(renderer, V2(0.0f, 0.0f), renderer->size, pivotPoint,
|
||||||
rotate, color, tex);
|
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};
|
RenderTex renderTex = {entity->tex, animTexRect};
|
||||||
|
|
||||||
#if RENDERER_USE_RENDER_GROUPS
|
|
||||||
// TODO(doyle): getRect needs a better name
|
// TODO(doyle): getRect needs a better name
|
||||||
v2 posInCameraSpace = v2_sub(entity->pos, camera.pos);
|
v2 posInCameraSpace = v2_sub(entity->pos, camera.pos);
|
||||||
|
|
||||||
|
#if RENDERER_USE_RENDER_GROUPS
|
||||||
v4 entityVertexOnScreen = math_getRect(posInCameraSpace, entity->size);
|
v4 entityVertexOnScreen = math_getRect(posInCameraSpace, entity->size);
|
||||||
RenderQuad entityQuad =
|
RenderQuad_ entityQuad =
|
||||||
createTexQuad(renderer, entityVertexOnScreen, renderTex);
|
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
|
#else
|
||||||
RenderQuad entityQuad = createDefaultTexQuad(renderer, renderTex);
|
RenderQuad_ entityQuad = createDefaultTexQuad(renderer, renderTex);
|
||||||
// TODO(doyle): getRect needs a better name
|
// TODO(doyle): getRect needs a better name
|
||||||
updateBufferObject(renderer, &entityQuad, 1);
|
updateBufferObject(renderer, entityQuad.vertex,
|
||||||
v2 posInCameraSpace = v2_sub(entity->pos, camera.pos);
|
ARRAY_COUNT(entityQuad.vertex));
|
||||||
renderObject(renderer, posInCameraSpace,
|
renderObject(renderer, posInCameraSpace,
|
||||||
entity->size, pivotPoint,
|
entity->size, pivotPoint,
|
||||||
entity->rotation + rotate, color, entity->tex);
|
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,
|
renderObject(renderer, V2(0.0f, 0.0f), renderer->size, pivotPoint,
|
||||||
rotate, color, currGroup->tex);
|
rotate, color, currGroup->tex);
|
||||||
|
|
||||||
RenderGroup clear = {0};
|
RenderGroup cleanGroup = {0};
|
||||||
*currGroup = clear;
|
cleanGroup.vertexList = currGroup->vertexList;
|
||||||
|
*currGroup = cleanGroup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ INTERNAL void rendererInit(GameState *state, v2 windowSize)
|
|||||||
|
|
||||||
/* Configure VAO */
|
/* Configure VAO */
|
||||||
const GLuint numVertexElements = 4;
|
const GLuint numVertexElements = 4;
|
||||||
const GLuint vertexSize = sizeof(v4);
|
const GLuint vertexSize = sizeof(Vertex);
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
glVertexAttribPointer(0, numVertexElements, GL_FLOAT, GL_FALSE, vertexSize,
|
glVertexAttribPointer(0, numVertexElements, GL_FLOAT, GL_FALSE, vertexSize,
|
||||||
(GLvoid *)0);
|
(GLvoid *)0);
|
||||||
@ -99,6 +99,14 @@ INTERNAL void rendererInit(GameState *state, v2 windowSize)
|
|||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
GL_CHECK_ERROR();
|
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
|
#ifdef DENGINE_DEBUG
|
||||||
DEBUG_LOG("Renderer initialised");
|
DEBUG_LOG("Renderer initialised");
|
||||||
#endif
|
#endif
|
||||||
@ -1673,7 +1681,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (getKeyStatus(&keys[keycode_left_square_bracket],
|
if (getKeyStatus(&keys[keycode_left_square_bracket],
|
||||||
readkeytype_delayedRepeat, 0.25f, dt))
|
readkeytype_delayedRepeat, 0.0f, dt))
|
||||||
{
|
{
|
||||||
|
|
||||||
Renderer *renderer = &state->renderer;
|
Renderer *renderer = &state->renderer;
|
||||||
|
@ -12,13 +12,22 @@ typedef struct MemoryArena MemoryArena;
|
|||||||
typedef struct Shader Shader;
|
typedef struct Shader Shader;
|
||||||
typedef struct Texture Texture;
|
typedef struct Texture Texture;
|
||||||
|
|
||||||
|
typedef union Vertex
|
||||||
|
{
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
v2 pos;
|
||||||
|
v2 texCoords;
|
||||||
|
};
|
||||||
|
|
||||||
|
v4 e;
|
||||||
|
} Vertex;
|
||||||
|
|
||||||
typedef struct RenderQuad
|
typedef struct RenderQuad
|
||||||
{
|
{
|
||||||
// Vertex composition
|
Vertex vertex[4];
|
||||||
// x, y: Coordinates - of entity on screen
|
} RenderQuad_;
|
||||||
// z, w: Texture Coords - of texture for this quad
|
|
||||||
v4 vertex[4];
|
|
||||||
} RenderQuad;
|
|
||||||
|
|
||||||
typedef struct RenderTex
|
typedef struct RenderTex
|
||||||
{
|
{
|
||||||
@ -30,8 +39,9 @@ typedef struct RenderTex
|
|||||||
typedef struct RenderGroup
|
typedef struct RenderGroup
|
||||||
{
|
{
|
||||||
Texture *tex;
|
Texture *tex;
|
||||||
RenderQuad quads[100];
|
|
||||||
i32 quadIndex;
|
Vertex *vertexList;
|
||||||
|
i32 vertexIndex;
|
||||||
} RenderGroup;
|
} RenderGroup;
|
||||||
|
|
||||||
typedef struct Renderer
|
typedef struct Renderer
|
||||||
@ -43,7 +53,8 @@ typedef struct Renderer
|
|||||||
v2 vertexNdcFactor;
|
v2 vertexNdcFactor;
|
||||||
v2 size;
|
v2 size;
|
||||||
|
|
||||||
RenderGroup groups[100];
|
RenderGroup groups[16];
|
||||||
|
i32 groupCapacity;
|
||||||
} Renderer;
|
} Renderer;
|
||||||
|
|
||||||
#define RENDERER_USE_RENDER_GROUPS TRUE
|
#define RENDERER_USE_RENDER_GROUPS TRUE
|
||||||
|
Loading…
Reference in New Issue
Block a user