2016-06-28 06:00:03 +00:00
|
|
|
#include "Dengine/Renderer.h"
|
2016-08-04 08:46:46 +00:00
|
|
|
#include "Dengine/AssetManager.h"
|
|
|
|
#include "Dengine/Assets.h"
|
2016-07-09 10:59:54 +00:00
|
|
|
#include "Dengine/Debug.h"
|
2016-07-28 05:19:23 +00:00
|
|
|
#include "Dengine/Entity.h"
|
2016-09-24 04:43:59 +00:00
|
|
|
#include "Dengine/MemoryArena.h"
|
2016-07-16 13:27:52 +00:00
|
|
|
#include "Dengine/OpenGL.h"
|
2016-07-28 05:19:23 +00:00
|
|
|
#include "Dengine/Shader.h"
|
|
|
|
#include "Dengine/Texture.h"
|
2016-06-09 05:49:03 +00:00
|
|
|
|
2016-11-11 10:43:03 +00:00
|
|
|
typedef struct RenderQuad
|
|
|
|
{
|
2016-11-23 02:42:46 +00:00
|
|
|
RenderVertex vertexList[4];
|
|
|
|
} RenderQuad;
|
2016-11-11 10:43:03 +00:00
|
|
|
|
2016-11-17 10:03:55 +00:00
|
|
|
// NOTE(doyle): A vertex batch is the batch of vertexes comprised to make one
|
|
|
|
// shape
|
|
|
|
INTERNAL void beginVertexBatch(Renderer *renderer)
|
2016-07-16 13:27:52 +00:00
|
|
|
{
|
2016-11-17 10:03:55 +00:00
|
|
|
ASSERT(renderer->vertexBatchState == vertexbatchstate_off);
|
|
|
|
ASSERT(renderer->groupIndexForVertexBatch == -1);
|
|
|
|
renderer->vertexBatchState = vertexbatchstate_initial_add;
|
|
|
|
}
|
2016-09-23 09:36:18 +00:00
|
|
|
|
2016-11-17 10:03:55 +00:00
|
|
|
/*
|
|
|
|
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
|
2016-11-23 12:47:23 +00:00
|
|
|
rendering.
|
2016-11-17 10:03:55 +00:00
|
|
|
|
2016-11-23 12:47:23 +00:00
|
|
|
The implementation is recognising if the rendered
|
2016-11-17 10:03:55 +00:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
INTERNAL void endVertexBatch(Renderer *renderer)
|
|
|
|
{
|
|
|
|
ASSERT(renderer->vertexBatchState != vertexbatchstate_off);
|
|
|
|
ASSERT(renderer->groupIndexForVertexBatch != -1);
|
|
|
|
|
2016-11-23 12:47:23 +00:00
|
|
|
i32 numDegenerateVertexes = 1;
|
2016-11-17 10:03:55 +00:00
|
|
|
RenderGroup *group = &renderer->groups[renderer->groupIndexForVertexBatch];
|
|
|
|
|
|
|
|
i32 freeVertexSlots = renderer->groupCapacity - group->vertexIndex;
|
|
|
|
if (numDegenerateVertexes < freeVertexSlots)
|
|
|
|
{
|
2016-11-23 02:42:46 +00:00
|
|
|
RenderVertex degenerateVertex =
|
|
|
|
group->vertexList[group->vertexIndex - 1];
|
2016-11-17 10:03:55 +00:00
|
|
|
group->vertexList[group->vertexIndex++] = degenerateVertex;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->vertexBatchState = vertexbatchstate_off;
|
|
|
|
renderer->groupIndexForVertexBatch = -1;
|
|
|
|
}
|
|
|
|
|
2016-11-23 02:42:46 +00:00
|
|
|
INTERNAL void applyRotationToVertexes(v2 pos, v2 pivotPoint, Radians rotate,
|
|
|
|
RenderVertex *vertexList,
|
|
|
|
i32 vertexListSize)
|
|
|
|
{
|
|
|
|
if (rotate == 0) return;
|
|
|
|
// NOTE(doyle): Move the world origin to the base position of the object.
|
|
|
|
// Then move the origin to the pivot point (e.g. center of object) and
|
|
|
|
// rotate from that point.
|
|
|
|
v2 pointOfRotation = v2_add(pivotPoint, pos);
|
|
|
|
|
|
|
|
mat4 rotateMat = mat4_translate(pointOfRotation.x, pointOfRotation.y, 0.0f);
|
|
|
|
rotateMat = mat4_mul(rotateMat, mat4_rotate(rotate, 0.0f, 0.0f, 1.0f));
|
|
|
|
rotateMat = mat4_mul(rotateMat, mat4_translate(-pointOfRotation.x,
|
|
|
|
-pointOfRotation.y, 0.0f));
|
|
|
|
for (i32 i = 0; i < vertexListSize; i++)
|
|
|
|
{
|
|
|
|
// NOTE(doyle): Manual matrix multiplication since vertex pos is 2D and
|
|
|
|
// matrix is 4D
|
|
|
|
v2 oldP = vertexList[i].pos;
|
|
|
|
v2 newP = {0};
|
|
|
|
|
|
|
|
newP.x = (oldP.x * rotateMat.e[0][0]) + (oldP.y * rotateMat.e[1][0]) +
|
|
|
|
(rotateMat.e[3][0]);
|
|
|
|
newP.y = (oldP.x * rotateMat.e[0][1]) + (oldP.y * rotateMat.e[1][1]) +
|
|
|
|
(rotateMat.e[3][1]);
|
|
|
|
|
|
|
|
vertexList[i].pos = newP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-17 10:03:55 +00:00
|
|
|
INTERNAL void addVertexToRenderGroup_(Renderer *renderer, Texture *tex,
|
2016-11-23 02:42:46 +00:00
|
|
|
v4 color, RenderVertex *vertexList,
|
2016-11-17 10:03:55 +00:00
|
|
|
i32 numVertexes,
|
|
|
|
enum RenderMode targetRenderMode,
|
|
|
|
RenderFlags flags)
|
|
|
|
{
|
|
|
|
ASSERT(renderer->vertexBatchState != vertexbatchstate_off);
|
2016-09-23 09:36:18 +00:00
|
|
|
ASSERT(numVertexes > 0);
|
2016-09-23 11:15:22 +00:00
|
|
|
|
2016-11-17 10:03:55 +00:00
|
|
|
#ifdef DENGINE_DEBUG
|
2016-09-23 11:15:22 +00:00
|
|
|
for (i32 i = 0; i < numVertexes; i++)
|
|
|
|
debug_countIncrement(debugcount_numVertex);
|
2016-09-23 09:36:18 +00:00
|
|
|
#endif
|
|
|
|
|
2016-09-21 09:21:27 +00:00
|
|
|
/* Find vacant/matching render group */
|
|
|
|
RenderGroup *targetGroup = NULL;
|
|
|
|
for (i32 i = 0; i < ARRAY_COUNT(renderer->groups); i++)
|
|
|
|
{
|
2016-09-23 11:15:22 +00:00
|
|
|
RenderGroup *group = &renderer->groups[i];
|
2016-09-23 09:19:47 +00:00
|
|
|
b32 groupIsValid = FALSE;
|
2016-11-11 13:22:09 +00:00
|
|
|
if (group->init)
|
2016-09-23 09:19:47 +00:00
|
|
|
{
|
|
|
|
/* If the textures match and have the same color modulation, we can
|
|
|
|
* add these vertices to the current group */
|
2016-11-11 13:22:09 +00:00
|
|
|
|
|
|
|
b32 renderModeMatches = FALSE;
|
|
|
|
if (group->mode == targetRenderMode) renderModeMatches = TRUE;
|
|
|
|
|
|
|
|
b32 colorMatches = FALSE;
|
|
|
|
if (v4_equals(group->color, color)) colorMatches = TRUE;
|
|
|
|
|
|
|
|
b32 flagsMatches = FALSE;
|
|
|
|
if (group->flags == flags) flagsMatches = TRUE;
|
|
|
|
|
|
|
|
b32 texMatches = TRUE;
|
|
|
|
if (!tex && !group->tex)
|
2016-09-23 09:19:47 +00:00
|
|
|
{
|
2016-11-11 13:22:09 +00:00
|
|
|
texMatches = TRUE;
|
2016-09-23 09:19:47 +00:00
|
|
|
}
|
2016-11-11 13:22:09 +00:00
|
|
|
else if (tex && group->tex)
|
|
|
|
{
|
|
|
|
if (group->tex->id == tex->id)
|
|
|
|
{
|
|
|
|
texMatches = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (texMatches && colorMatches && renderModeMatches && flagsMatches)
|
|
|
|
groupIsValid = TRUE;
|
2016-09-23 09:19:47 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* New group, unused so initialise it */
|
|
|
|
groupIsValid = TRUE;
|
|
|
|
|
2016-11-11 13:22:09 +00:00
|
|
|
group->init = TRUE;
|
2016-09-23 09:19:47 +00:00
|
|
|
group->tex = tex;
|
|
|
|
group->color = color;
|
2016-11-11 10:43:03 +00:00
|
|
|
group->mode = targetRenderMode;
|
2016-11-11 13:22:09 +00:00
|
|
|
group->flags = flags;
|
2016-09-23 09:19:47 +00:00
|
|
|
|
2016-09-23 11:15:22 +00:00
|
|
|
#ifdef DENGINE_DEBUG
|
|
|
|
debug_countIncrement(debugcount_renderGroups);
|
|
|
|
#endif
|
2016-09-23 09:19:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (groupIsValid)
|
2016-09-21 09:21:27 +00:00
|
|
|
{
|
2016-09-21 16:16:14 +00:00
|
|
|
i32 freeVertexSlots = renderer->groupCapacity - group->vertexIndex;
|
2016-11-17 10:03:55 +00:00
|
|
|
|
|
|
|
// NOTE(doyle): Two at start, two at end
|
|
|
|
i32 numDegenerateVertexes = 0;
|
2016-11-23 12:47:23 +00:00
|
|
|
|
2016-11-17 10:03:55 +00:00
|
|
|
if (renderer->vertexBatchState == vertexbatchstate_initial_add)
|
2016-11-23 12:47:23 +00:00
|
|
|
numDegenerateVertexes = 1;
|
2016-11-17 10:03:55 +00:00
|
|
|
|
|
|
|
if ((numDegenerateVertexes + numVertexes) < freeVertexSlots)
|
2016-09-21 16:16:14 +00:00
|
|
|
{
|
2016-09-23 09:36:18 +00:00
|
|
|
if (i != 0)
|
|
|
|
{
|
|
|
|
RenderGroup tmp = renderer->groups[0];
|
|
|
|
renderer->groups[0] = renderer->groups[i];
|
|
|
|
renderer->groups[i] = tmp;
|
|
|
|
}
|
|
|
|
targetGroup = &renderer->groups[0];
|
2016-09-22 08:46:53 +00:00
|
|
|
break;
|
2016-09-21 16:16:14 +00:00
|
|
|
}
|
2016-09-21 09:21:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Valid group, add to the render group for rendering */
|
|
|
|
if (targetGroup)
|
|
|
|
{
|
2016-11-23 12:47:23 +00:00
|
|
|
// NOTE(doyle): If we are adding 3 vertexes, then we are adding a
|
|
|
|
// triangle to the triangle strip. If so, then depending on which "n-th"
|
|
|
|
// triangle it is we're adding, the winding order in a t-strip
|
|
|
|
// alternates with each triangle (including degenerates). Hence we track
|
|
|
|
// so we know the last winding order in the group.
|
|
|
|
|
|
|
|
// For this to work, we must ensure all incoming vertexes are winding in
|
|
|
|
// ccw order initially. There is also the presumption that, other
|
|
|
|
// rendering methods, such as the quad, consists of an even number of
|
|
|
|
// triangles such that the winding order gets alternated back to the
|
|
|
|
// same order it started with.
|
|
|
|
if (numVertexes == 3)
|
|
|
|
{
|
|
|
|
if (targetGroup->clockwiseWinding)
|
|
|
|
{
|
|
|
|
RenderVertex tmp = vertexList[0];
|
|
|
|
vertexList[0] = vertexList[2];
|
|
|
|
vertexList[2] = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
targetGroup->clockwiseWinding =
|
|
|
|
(targetGroup->clockwiseWinding) ? FALSE : TRUE;
|
|
|
|
}
|
|
|
|
|
2016-11-17 10:03:55 +00:00
|
|
|
if (renderer->vertexBatchState == vertexbatchstate_initial_add)
|
|
|
|
{
|
2016-11-23 12:47:23 +00:00
|
|
|
if (targetGroup->vertexIndex != 0)
|
|
|
|
{
|
|
|
|
targetGroup->vertexList[targetGroup->vertexIndex++] =
|
|
|
|
vertexList[0];
|
|
|
|
}
|
2016-11-17 10:03:55 +00:00
|
|
|
renderer->vertexBatchState = vertexbatchstate_active;
|
|
|
|
|
|
|
|
// NOTE(doyle): We swap groups to the front if it is valid, so
|
|
|
|
// target group should always be 0
|
|
|
|
ASSERT(renderer->groupIndexForVertexBatch == -1);
|
|
|
|
renderer->groupIndexForVertexBatch = 0;
|
|
|
|
}
|
|
|
|
|
2016-11-23 12:47:23 +00:00
|
|
|
|
2016-09-21 16:16:14 +00:00
|
|
|
for (i32 i = 0; i < numVertexes; i++)
|
2016-09-21 09:21:27 +00:00
|
|
|
{
|
2016-09-21 16:16:14 +00:00
|
|
|
targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[i];
|
2016-09-21 09:21:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO(doyle): Log no remaining render groups
|
2016-09-21 16:16:14 +00:00
|
|
|
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");
|
2016-09-21 09:21:27 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-16 13:27:52 +00:00
|
|
|
|
|
|
|
INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY)
|
|
|
|
{
|
|
|
|
if (flipX)
|
|
|
|
{
|
|
|
|
v4 tmp = *texCoords;
|
|
|
|
texCoords->x = tmp.z;
|
|
|
|
texCoords->z = tmp.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flipY)
|
|
|
|
{
|
|
|
|
v4 tmp = *texCoords;
|
|
|
|
texCoords->y = tmp.w;
|
|
|
|
texCoords->w = tmp.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-11 10:43:03 +00:00
|
|
|
INTERNAL void bufferRenderGroupToGL(Renderer *renderer, RenderGroup *group)
|
2016-06-25 11:23:15 +00:00
|
|
|
{
|
2016-11-23 02:42:46 +00:00
|
|
|
RenderVertex *vertexList = group->vertexList;
|
2016-11-11 10:43:03 +00:00
|
|
|
i32 numVertex = group->vertexIndex;
|
|
|
|
|
2016-07-07 16:45:37 +00:00
|
|
|
// TODO(doyle): We assume that vbo and vao are assigned
|
2016-09-21 16:16:14 +00:00
|
|
|
renderer->numVertexesInVbo = numVertex;
|
2016-11-11 10:43:03 +00:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[group->mode]);
|
2016-11-23 02:42:46 +00:00
|
|
|
glBufferData(GL_ARRAY_BUFFER, numVertex * sizeof(RenderVertex), vertexList,
|
2016-07-07 16:45:37 +00:00
|
|
|
GL_STREAM_DRAW);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
}
|
|
|
|
|
2016-11-11 13:22:09 +00:00
|
|
|
INTERNAL v4 getTexRectNormaliseDeviceCoords(RenderTex renderTex)
|
|
|
|
{
|
|
|
|
/* Convert texture coordinates to normalised texture coordinates */
|
|
|
|
v4 result = renderTex.texRect;
|
|
|
|
if (renderTex.tex)
|
|
|
|
{
|
|
|
|
v2 texNdcFactor =
|
|
|
|
V2(1.0f / renderTex.tex->width, 1.0f / renderTex.tex->height);
|
|
|
|
result.e[0] *= texNdcFactor.w;
|
|
|
|
result.e[1] *= texNdcFactor.h;
|
|
|
|
result.e[2] *= texNdcFactor.w;
|
|
|
|
result.e[3] *= texNdcFactor.h;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-11-23 02:42:46 +00:00
|
|
|
INTERNAL RenderQuad createRenderQuad(Renderer *renderer, v2 pos, v2 size,
|
|
|
|
v2 pivotPoint, Radians rotate,
|
|
|
|
RenderTex renderTex)
|
2016-07-16 07:15:03 +00:00
|
|
|
{
|
2016-09-21 16:16:14 +00:00
|
|
|
/*
|
|
|
|
* Rendering order
|
|
|
|
*
|
|
|
|
* 0 2
|
|
|
|
* +---+
|
|
|
|
* + /+
|
|
|
|
* + / +
|
|
|
|
* +/ +
|
|
|
|
* +---+
|
|
|
|
* 1 3
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-09-23 08:02:53 +00:00
|
|
|
v4 vertexPair = {0};
|
|
|
|
vertexPair.vec2[0] = pos;
|
|
|
|
vertexPair.vec2[1] = v2_add(pos, size);
|
2016-11-11 13:22:09 +00:00
|
|
|
v4 texRectNdc = getTexRectNormaliseDeviceCoords(renderTex);
|
2016-07-16 07:15:03 +00:00
|
|
|
|
2016-09-23 08:02:53 +00:00
|
|
|
// NOTE(doyle): Create a quad composed of 4 vertexes to be rendered as
|
|
|
|
// a triangle strip using vertices v0, v1, v2, then v2, v1, v3 (note the
|
|
|
|
// order)
|
2016-11-23 02:42:46 +00:00
|
|
|
v2 vertexList[4] = {0};
|
|
|
|
v2 texCoordList[4] = {0};
|
|
|
|
|
|
|
|
// Top left
|
|
|
|
vertexList[0] = V2(vertexPair.x, vertexPair.w);
|
|
|
|
texCoordList[0] = V2(texRectNdc.x, texRectNdc.w);
|
2016-09-23 08:02:53 +00:00
|
|
|
|
2016-11-23 02:42:46 +00:00
|
|
|
// Bottom left
|
|
|
|
vertexList[1] = V2(vertexPair.x, vertexPair.y);
|
|
|
|
texCoordList[1] = V2(texRectNdc.x, texRectNdc.y);
|
2016-09-23 08:02:53 +00:00
|
|
|
|
2016-11-23 02:42:46 +00:00
|
|
|
// Top right
|
|
|
|
vertexList[2] = V2(vertexPair.z, vertexPair.w);
|
|
|
|
texCoordList[2] = V2(texRectNdc.z, texRectNdc.w);
|
2016-09-23 08:02:53 +00:00
|
|
|
|
2016-11-23 02:42:46 +00:00
|
|
|
// Bottom right
|
|
|
|
vertexList[3] = V2(vertexPair.z, vertexPair.y);
|
|
|
|
texCoordList[3] = V2(texRectNdc.z, texRectNdc.y);
|
2016-09-23 08:02:53 +00:00
|
|
|
|
|
|
|
// NOTE(doyle): Precalculate rotation on vertex positions
|
|
|
|
// NOTE(doyle): No translation/scale matrix as we pre-calculate it from
|
|
|
|
// entity data and work in world space until GLSL uses the projection matrix
|
2016-11-23 02:42:46 +00:00
|
|
|
|
|
|
|
math_applyRotationToVertexes(pos, pivotPoint, rotate, vertexList,
|
|
|
|
ARRAY_COUNT(vertexList));
|
|
|
|
|
|
|
|
RenderQuad result = {0};
|
|
|
|
|
|
|
|
ASSERT(ARRAY_COUNT(vertexList) == ARRAY_COUNT(result.vertexList));
|
|
|
|
for (i32 i = 0; i < ARRAY_COUNT(vertexList); i++)
|
|
|
|
{
|
|
|
|
result.vertexList[i].pos = vertexList[i];
|
|
|
|
result.vertexList[i].texCoord = texCoordList[i];
|
|
|
|
}
|
|
|
|
|
2016-07-16 07:15:03 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-11-23 02:42:46 +00:00
|
|
|
INTERNAL inline RenderQuad
|
2016-11-11 13:22:09 +00:00
|
|
|
createDefaultTexQuad(Renderer *renderer, RenderTex *renderTex)
|
2016-07-16 07:15:03 +00:00
|
|
|
{
|
2016-11-23 02:42:46 +00:00
|
|
|
RenderQuad result = {0};
|
|
|
|
result = createRenderQuad(renderer, V2(0, 0), V2(0, 0), V2(0, 0), 0.0f,
|
|
|
|
*renderTex);
|
2016-07-16 07:15:03 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-11-11 13:22:09 +00:00
|
|
|
INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *group)
|
2016-07-16 07:15:03 +00:00
|
|
|
{
|
2016-11-11 13:22:09 +00:00
|
|
|
ASSERT(group->mode < rendermode_invalid);
|
2016-11-11 14:17:30 +00:00
|
|
|
|
|
|
|
if (group->flags & renderflag_wireframe)
|
2016-11-23 12:47:23 +00:00
|
|
|
{
|
2016-11-16 02:00:59 +00:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
2016-11-23 12:47:23 +00:00
|
|
|
}
|
2016-11-16 02:00:59 +00:00
|
|
|
else
|
2016-11-23 12:47:23 +00:00
|
|
|
{
|
2016-11-16 02:00:59 +00:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
2016-11-23 12:47:23 +00:00
|
|
|
}
|
2016-11-16 02:00:59 +00:00
|
|
|
GL_CHECK_ERROR();
|
|
|
|
|
|
|
|
if (group->flags & renderflag_no_texture)
|
2016-11-11 14:17:30 +00:00
|
|
|
{
|
|
|
|
renderer->activeShaderId =
|
|
|
|
renderer->shaderList[shaderlist_default_no_tex];
|
2016-11-16 02:00:59 +00:00
|
|
|
shader_use(renderer->activeShaderId);
|
2016-11-11 14:17:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
renderer->activeShaderId = renderer->shaderList[shaderlist_default];
|
2016-11-16 02:00:59 +00:00
|
|
|
shader_use(renderer->activeShaderId);
|
|
|
|
Texture *tex = group->tex;
|
|
|
|
if (tex)
|
|
|
|
{
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex->id);
|
|
|
|
shader_uniformSet1i(renderer->activeShaderId, "tex", 0);
|
|
|
|
GL_CHECK_ERROR();
|
|
|
|
}
|
2016-11-11 14:17:30 +00:00
|
|
|
}
|
|
|
|
|
2016-11-23 12:47:23 +00:00
|
|
|
#if 0
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
#endif
|
|
|
|
|
2016-07-16 07:15:03 +00:00
|
|
|
/* Set color modulation value */
|
2016-11-11 14:17:30 +00:00
|
|
|
shader_uniformSetVec4f(renderer->activeShaderId, "spriteColor",
|
2016-11-11 13:22:09 +00:00
|
|
|
group->color);
|
2016-07-16 07:15:03 +00:00
|
|
|
|
2016-11-11 13:22:09 +00:00
|
|
|
glBindVertexArray(renderer->vao[group->mode]);
|
2016-11-16 02:00:59 +00:00
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
|
2016-11-11 13:22:09 +00:00
|
|
|
GL_CHECK_ERROR();
|
2016-09-23 11:15:22 +00:00
|
|
|
debug_countIncrement(debugcount_drawArrays);
|
2016-07-16 07:15:03 +00:00
|
|
|
|
|
|
|
/* Unbind */
|
|
|
|
glBindVertexArray(0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2016-07-24 12:19:25 +00:00
|
|
|
GL_CHECK_ERROR();
|
2016-11-16 02:00:59 +00:00
|
|
|
|
2016-07-16 07:15:03 +00:00
|
|
|
}
|
|
|
|
|
2016-08-17 08:04:51 +00:00
|
|
|
RenderTex renderer_createNullRenderTex(AssetManager *const assetManager)
|
2016-08-04 08:46:46 +00:00
|
|
|
{
|
2016-08-26 18:15:13 +00:00
|
|
|
Texture *emptyTex = asset_getTex(assetManager, "nullTex");
|
2016-08-04 08:46:46 +00:00
|
|
|
RenderTex result = {emptyTex, V4(0, 1, 1, 0)};
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-08-04 12:17:14 +00:00
|
|
|
void renderer_rect(Renderer *const renderer, Rect camera, v2 pos, v2 size,
|
2016-11-11 13:22:09 +00:00
|
|
|
v2 pivotPoint, Radians rotate, RenderTex *renderTex, v4 color,
|
|
|
|
RenderFlags flags)
|
2016-07-16 07:15:03 +00:00
|
|
|
{
|
2016-09-23 08:02:53 +00:00
|
|
|
// NOTE(doyle): Bottom left and top right position of quad in world space
|
2016-11-16 05:57:24 +00:00
|
|
|
v2 posInCameraSpace = v2_sub(pos, camera.min);
|
2016-11-11 10:43:03 +00:00
|
|
|
|
2016-11-11 13:22:09 +00:00
|
|
|
RenderTex emptyRenderTex = {0};
|
|
|
|
if (!renderTex) renderTex = &emptyRenderTex;
|
|
|
|
|
2016-11-23 02:42:46 +00:00
|
|
|
RenderQuad quad = createRenderQuad(renderer, posInCameraSpace, size,
|
|
|
|
pivotPoint, rotate, *renderTex);
|
2016-11-11 13:22:09 +00:00
|
|
|
|
2016-11-17 10:03:55 +00:00
|
|
|
beginVertexBatch(renderer);
|
2016-11-23 02:42:46 +00:00
|
|
|
addVertexToRenderGroup_(renderer, renderTex->tex, color, quad.vertexList,
|
|
|
|
ARRAY_COUNT(quad.vertexList), rendermode_quad,
|
|
|
|
flags);
|
2016-11-17 10:03:55 +00:00
|
|
|
endVertexBatch(renderer);
|
2016-11-11 10:43:03 +00:00
|
|
|
}
|
|
|
|
|
2016-11-17 07:15:36 +00:00
|
|
|
void renderer_polygon(Renderer *const renderer, Rect camera,
|
2016-11-16 13:50:49 +00:00
|
|
|
v2 *polygonPoints, i32 numPoints, v2 pivotPoint,
|
|
|
|
Radians rotate, RenderTex *renderTex, v4 color,
|
|
|
|
RenderFlags flags)
|
|
|
|
{
|
2016-11-18 03:33:11 +00:00
|
|
|
ASSERT(numPoints >= 3);
|
2016-11-16 13:50:49 +00:00
|
|
|
|
2016-11-23 12:47:23 +00:00
|
|
|
for (i32 i = 0; i < numPoints; i++)
|
2016-11-16 13:50:49 +00:00
|
|
|
polygonPoints[i] = v2_sub(polygonPoints[i], camera.min);
|
|
|
|
|
2016-11-18 03:33:11 +00:00
|
|
|
// TODO(doyle): Do something with render texture
|
2016-11-16 13:50:49 +00:00
|
|
|
RenderTex emptyRenderTex = {0};
|
|
|
|
if (!renderTex) renderTex = &emptyRenderTex;
|
|
|
|
|
2016-11-23 12:47:23 +00:00
|
|
|
v2 triangulationBaseP = polygonPoints[0];
|
2016-11-23 02:42:46 +00:00
|
|
|
RenderVertex triangulationBaseVertex = {0};
|
2016-11-23 12:47:23 +00:00
|
|
|
triangulationBaseVertex.pos = triangulationBaseP;
|
2016-11-17 10:03:55 +00:00
|
|
|
|
2016-11-18 03:33:11 +00:00
|
|
|
i32 numTrisInTriangulation = numPoints - 2;
|
|
|
|
i32 triangulationIndex = 0;
|
2016-11-16 13:50:49 +00:00
|
|
|
for (i32 i = 1; triangulationIndex < numTrisInTriangulation; i++)
|
|
|
|
{
|
2016-11-18 03:33:11 +00:00
|
|
|
ASSERT((i + 1) < numPoints);
|
2016-11-17 10:03:55 +00:00
|
|
|
|
2016-11-23 12:47:23 +00:00
|
|
|
v2 vertexList[3] = {triangulationBaseP, polygonPoints[i],
|
|
|
|
polygonPoints[i + 1]};
|
|
|
|
|
|
|
|
beginVertexBatch(renderer);
|
|
|
|
|
2016-11-23 02:42:46 +00:00
|
|
|
RenderVertex triangle[3] = {0};
|
2016-11-23 12:47:23 +00:00
|
|
|
triangle[0].pos = vertexList[0];
|
|
|
|
triangle[1].pos = vertexList[1];
|
|
|
|
triangle[2].pos = vertexList[2];
|
2016-11-16 13:50:49 +00:00
|
|
|
|
2016-11-19 05:42:13 +00:00
|
|
|
addVertexToRenderGroup_(renderer, renderTex->tex, color, triangle,
|
|
|
|
ARRAY_COUNT(triangle), rendermode_polygon,
|
2016-11-17 10:03:55 +00:00
|
|
|
flags);
|
2016-11-23 12:47:23 +00:00
|
|
|
endVertexBatch(renderer);
|
2016-11-17 10:03:55 +00:00
|
|
|
triangulationIndex++;
|
2016-11-16 13:50:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-24 04:43:59 +00:00
|
|
|
void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera,
|
2016-08-04 12:17:14 +00:00
|
|
|
Font *const font, const char *const string, v2 pos,
|
2016-11-11 13:22:09 +00:00
|
|
|
v2 pivotPoint, Radians rotate, v4 color, RenderFlags flags)
|
2016-07-07 16:45:37 +00:00
|
|
|
{
|
2016-08-04 12:17:14 +00:00
|
|
|
i32 strLen = common_strlen(string);
|
2016-09-23 09:36:18 +00:00
|
|
|
if (strLen <= 0) return;
|
2016-07-15 08:34:23 +00:00
|
|
|
|
2016-07-12 08:11:31 +00:00
|
|
|
// TODO(doyle): Slightly incorrect string length in pixels calculation,
|
|
|
|
// because we use the advance metric of each character for length not
|
|
|
|
// maximum character size in rendering
|
|
|
|
v2 rightAlignedP =
|
2016-08-24 16:06:32 +00:00
|
|
|
v2_add(pos, V2((CAST(f32) font->maxSize.w * CAST(f32) strLen),
|
|
|
|
CAST(f32) font->maxSize.h));
|
2016-09-23 08:02:53 +00:00
|
|
|
v2 leftAlignedP = pos;
|
2016-08-04 12:17:14 +00:00
|
|
|
if (math_pointInRect(camera, leftAlignedP) ||
|
|
|
|
math_pointInRect(camera, rightAlignedP))
|
2016-07-07 16:45:37 +00:00
|
|
|
{
|
2016-09-23 08:02:53 +00:00
|
|
|
i32 vertexIndex = 0;
|
|
|
|
i32 numVertexPerQuad = 4;
|
|
|
|
i32 numVertexesToAlloc = (strLen * (numVertexPerQuad + 2));
|
2016-11-23 02:42:46 +00:00
|
|
|
RenderVertex *vertexList =
|
|
|
|
memory_pushBytes(arena, numVertexesToAlloc * sizeof(RenderVertex));
|
2016-07-12 08:11:31 +00:00
|
|
|
|
2016-11-16 05:57:24 +00:00
|
|
|
v2 posInCameraSpace = v2_sub(pos, camera.min);
|
2016-08-18 14:56:02 +00:00
|
|
|
pos = posInCameraSpace;
|
2016-08-18 14:25:43 +00:00
|
|
|
|
|
|
|
// TODO(doyle): Find why font is 1px off, might be arial font semantics
|
2016-08-24 16:06:32 +00:00
|
|
|
Texture *tex = font->atlas->tex;
|
2016-08-18 14:25:43 +00:00
|
|
|
f32 baseline = pos.y - font->verticalSpacing + 1;
|
2016-07-12 08:11:31 +00:00
|
|
|
for (i32 i = 0; i < strLen; i++)
|
|
|
|
{
|
2016-07-17 14:54:21 +00:00
|
|
|
i32 codepoint = string[i];
|
2016-07-17 13:45:59 +00:00
|
|
|
i32 relativeIndex = CAST(i32)(codepoint - font->codepointRange.x);
|
2016-09-23 08:02:53 +00:00
|
|
|
CharMetrics metric = font->charMetrics[relativeIndex];
|
|
|
|
pos.y = baseline - (metric.offset.y);
|
2016-07-12 08:11:31 +00:00
|
|
|
|
|
|
|
/* Get texture out */
|
2016-09-23 08:02:53 +00:00
|
|
|
SubTexture subTexture =
|
2016-08-30 08:42:37 +00:00
|
|
|
asset_getAtlasSubTex(font->atlas, &CAST(char)codepoint);
|
2016-07-12 08:11:31 +00:00
|
|
|
|
2016-09-23 08:02:53 +00:00
|
|
|
v4 charTexRect = {0};
|
2016-11-16 05:57:24 +00:00
|
|
|
charTexRect.vec2[0] = subTexture.rect.min;
|
2016-09-22 09:00:44 +00:00
|
|
|
charTexRect.vec2[1] =
|
2016-11-16 05:57:24 +00:00
|
|
|
v2_add(subTexture.rect.min, subTexture.rect.max);
|
2016-09-22 09:00:44 +00:00
|
|
|
flipTexCoord(&charTexRect, FALSE, TRUE);
|
2016-08-24 16:06:32 +00:00
|
|
|
|
2016-09-22 09:00:44 +00:00
|
|
|
RenderTex renderTex = {tex, charTexRect};
|
2016-11-23 02:42:46 +00:00
|
|
|
RenderQuad quad = createRenderQuad(renderer, pos, font->maxSize,
|
|
|
|
pivotPoint, rotate, renderTex);
|
2016-09-22 08:46:53 +00:00
|
|
|
|
2016-11-17 10:03:55 +00:00
|
|
|
beginVertexBatch(renderer);
|
2016-11-23 02:42:46 +00:00
|
|
|
addVertexToRenderGroup_(renderer, tex, color, quad.vertexList,
|
|
|
|
ARRAY_COUNT(quad.vertexList),
|
|
|
|
rendermode_quad, flags);
|
2016-11-17 10:03:55 +00:00
|
|
|
endVertexBatch(renderer);
|
2016-09-23 08:02:53 +00:00
|
|
|
pos.x += metric.advance;
|
2016-07-12 08:11:31 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-07 16:45:37 +00:00
|
|
|
}
|
|
|
|
|
2016-11-17 07:15:36 +00:00
|
|
|
void renderer_entity(Renderer *renderer, MemoryArena_ *transientArena,
|
|
|
|
Rect camera, Entity *entity, v2 pivotPoint, Degrees rotate,
|
|
|
|
v4 color, RenderFlags flags)
|
2016-07-07 16:45:37 +00:00
|
|
|
{
|
2016-11-16 13:50:49 +00:00
|
|
|
// TODO(doyle): Add early exit on entities out of camera bounds
|
2016-11-16 05:57:24 +00:00
|
|
|
Radians totalRotation = DEGREES_TO_RADIANS((entity->rotation + rotate));
|
|
|
|
RenderTex renderTex = {0};
|
|
|
|
if (entity->tex)
|
2016-07-07 16:45:37 +00:00
|
|
|
{
|
2016-11-16 05:57:24 +00:00
|
|
|
EntityAnim *entityAnim = &entity->animList[entity->animListIndex];
|
|
|
|
v4 texRect = {0};
|
|
|
|
if (entityAnim->anim)
|
2016-11-09 11:36:41 +00:00
|
|
|
{
|
2016-11-16 05:57:24 +00:00
|
|
|
Animation *anim = entityAnim->anim;
|
|
|
|
char *frameName = anim->frameList[entityAnim->currFrame];
|
|
|
|
SubTexture subTex = asset_getAtlasSubTex(anim->atlas, frameName);
|
2016-09-14 17:20:13 +00:00
|
|
|
|
2016-11-16 05:57:24 +00:00
|
|
|
texRect.vec2[0] = subTex.rect.min;
|
|
|
|
texRect.vec2[1] = v2_add(subTex.rect.min, subTex.rect.max);
|
|
|
|
flipTexCoord(&texRect, entity->flipX, entity->flipY);
|
2016-07-08 06:09:18 +00:00
|
|
|
}
|
2016-11-16 05:57:24 +00:00
|
|
|
else
|
2016-11-16 02:00:59 +00:00
|
|
|
{
|
2016-11-16 05:57:24 +00:00
|
|
|
texRect = V4(0.0f, 0.0f, (f32)entity->tex->width,
|
|
|
|
(f32)entity->tex->height);
|
2016-11-16 02:00:59 +00:00
|
|
|
}
|
2016-11-16 05:57:24 +00:00
|
|
|
|
|
|
|
if (entity->direction == direction_east)
|
2016-11-16 02:00:59 +00:00
|
|
|
{
|
2016-11-16 05:57:24 +00:00
|
|
|
flipTexCoord(&texRect, TRUE, FALSE);
|
|
|
|
}
|
2016-11-16 02:00:59 +00:00
|
|
|
|
2016-11-16 05:57:24 +00:00
|
|
|
renderTex.tex = entity->tex;
|
|
|
|
renderTex.texRect = texRect;
|
|
|
|
}
|
2016-11-16 02:00:59 +00:00
|
|
|
|
2016-11-24 07:57:44 +00:00
|
|
|
// TODO(doyle): Proper blending
|
|
|
|
v4 renderColor = color;
|
|
|
|
if (v4_equals(color, V4(0, 0, 0, 0))) renderColor = entity->color;
|
|
|
|
|
2016-11-16 05:57:24 +00:00
|
|
|
if (entity->renderMode == rendermode_quad)
|
|
|
|
{
|
2016-11-23 02:42:46 +00:00
|
|
|
renderer_rect(renderer, camera, entity->pos, entity->size,
|
|
|
|
v2_add(entity->offset, pivotPoint), totalRotation,
|
2016-11-24 07:57:44 +00:00
|
|
|
&renderTex, entity->color, flags);
|
2016-11-16 05:57:24 +00:00
|
|
|
}
|
2016-11-17 07:15:36 +00:00
|
|
|
else if (entity->renderMode == rendermode_polygon)
|
|
|
|
{
|
2016-11-18 03:33:11 +00:00
|
|
|
ASSERT(entity->numVertexPoints >= 3);
|
2016-11-17 07:15:36 +00:00
|
|
|
ASSERT(entity->vertexPoints);
|
|
|
|
|
2016-11-23 02:42:46 +00:00
|
|
|
v2 *offsetVertexPoints =
|
2016-11-24 07:57:44 +00:00
|
|
|
entity_generateUpdatedVertexList(transientArena, entity);
|
2016-11-23 12:47:23 +00:00
|
|
|
|
2016-11-17 07:15:36 +00:00
|
|
|
renderer_polygon(renderer, camera, offsetVertexPoints,
|
2016-11-19 05:42:13 +00:00
|
|
|
entity->numVertexPoints,
|
|
|
|
v2_add(entity->offset, pivotPoint), totalRotation,
|
2016-11-24 07:57:44 +00:00
|
|
|
&renderTex, renderColor, flags);
|
2016-11-17 07:15:36 +00:00
|
|
|
}
|
2016-11-16 05:57:24 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
ASSERT(INVALID_CODE_PATH);
|
2016-09-21 09:21:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void renderer_renderGroups(Renderer *renderer)
|
|
|
|
{
|
|
|
|
for (i32 i = 0; i < ARRAY_COUNT(renderer->groups); i++)
|
|
|
|
{
|
|
|
|
RenderGroup *currGroup = &renderer->groups[i];
|
2016-11-11 13:22:09 +00:00
|
|
|
if (currGroup->init)
|
2016-09-21 09:21:27 +00:00
|
|
|
{
|
|
|
|
bufferRenderGroupToGL(renderer, currGroup);
|
2016-09-23 09:19:47 +00:00
|
|
|
renderGLBufferedData(renderer, currGroup);
|
2016-09-21 09:21:27 +00:00
|
|
|
|
2016-09-21 16:16:14 +00:00
|
|
|
RenderGroup cleanGroup = {0};
|
|
|
|
cleanGroup.vertexList = currGroup->vertexList;
|
|
|
|
*currGroup = cleanGroup;
|
2016-09-21 09:21:27 +00:00
|
|
|
}
|
2016-09-23 09:19:47 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2016-07-07 16:45:37 +00:00
|
|
|
}
|
2016-06-25 11:23:15 +00:00
|
|
|
}
|