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
|
|
|
|
{
|
|
|
|
Vertex vertex[4];
|
|
|
|
} RenderQuad_;
|
|
|
|
|
|
|
|
typedef struct RenderTriangle
|
|
|
|
{
|
|
|
|
Vertex vertex[3];
|
|
|
|
} RenderTriangle_;
|
|
|
|
|
2016-09-23 09:19:47 +00:00
|
|
|
INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color,
|
2016-11-11 10:43:03 +00:00
|
|
|
Vertex *vertexList, i32 numVertexes,
|
2016-11-11 13:22:09 +00:00
|
|
|
enum RenderMode targetRenderMode,
|
|
|
|
RenderFlags flags)
|
2016-07-16 13:27:52 +00:00
|
|
|
{
|
2016-09-23 09:36:18 +00:00
|
|
|
|
|
|
|
#ifdef DENGINE_DEBUG
|
|
|
|
ASSERT(numVertexes > 0);
|
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-17 07:15:36 +00:00
|
|
|
// NOTE(doyle): Mark first vertex as degenerate vertex
|
|
|
|
group->vertexList[group->vertexIndex++] = vertexList[0];
|
|
|
|
|
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;
|
|
|
|
if (numVertexes < freeVertexSlots)
|
|
|
|
{
|
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-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-11 10:43:03 +00:00
|
|
|
Vertex *vertexList = group->vertexList;
|
|
|
|
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-09-21 16:16:14 +00:00
|
|
|
glBufferData(GL_ARRAY_BUFFER, numVertex * sizeof(Vertex), 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 void applyRotationToVertexes(v2 pos, v2 pivotPoint, Radians rotate,
|
|
|
|
Vertex *vertexList, i32 vertexListSize)
|
|
|
|
{
|
2016-11-16 02:00:59 +00:00
|
|
|
if (rotate == 0) return;
|
2016-11-11 13:22:09 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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-09-23 08:02:53 +00:00
|
|
|
INTERNAL RenderQuad_ createRenderQuad(Renderer *renderer, v2 pos, v2 size,
|
2016-11-11 13:22:09 +00:00
|
|
|
v2 pivotPoint, Radians rotate,
|
2016-09-23 08:02:53 +00:00
|
|
|
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)
|
|
|
|
RenderQuad_ result = {0};
|
|
|
|
result.vertex[0].pos = V2(vertexPair.x, vertexPair.w); // Top left
|
|
|
|
result.vertex[0].texCoord = V2(texRectNdc.x, texRectNdc.w);
|
|
|
|
|
|
|
|
result.vertex[1].pos = V2(vertexPair.x, vertexPair.y); // Bottom left
|
|
|
|
result.vertex[1].texCoord = V2(texRectNdc.x, texRectNdc.y);
|
|
|
|
|
|
|
|
result.vertex[2].pos = V2(vertexPair.z, vertexPair.w); // Top right
|
|
|
|
result.vertex[2].texCoord = V2(texRectNdc.z, texRectNdc.w);
|
|
|
|
|
|
|
|
result.vertex[3].pos = V2(vertexPair.z, vertexPair.y); // Bottom right
|
|
|
|
result.vertex[3].texCoord = V2(texRectNdc.z, texRectNdc.y);
|
|
|
|
|
|
|
|
// 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-11 13:22:09 +00:00
|
|
|
applyRotationToVertexes(pos, pivotPoint, rotate, result.vertex,
|
|
|
|
ARRAY_COUNT(result.vertex));
|
2016-07-16 07:15:03 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-11-11 10:43:03 +00:00
|
|
|
INTERNAL RenderTriangle_ createRenderTriangle(Renderer *renderer,
|
|
|
|
TrianglePoints triangle,
|
2016-11-11 13:22:09 +00:00
|
|
|
v2 pivotPoint, Radians rotate,
|
2016-11-11 10:43:03 +00:00
|
|
|
RenderTex renderTex)
|
|
|
|
{
|
|
|
|
/* Convert texture coordinates to normalised texture coordinates */
|
2016-11-11 13:22:09 +00:00
|
|
|
v4 texRectNdc = getTexRectNormaliseDeviceCoords(renderTex);
|
2016-11-11 10:43:03 +00:00
|
|
|
|
2016-11-11 13:22:09 +00:00
|
|
|
RenderTriangle_ result = {0};
|
2016-11-11 10:43:03 +00:00
|
|
|
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);
|
|
|
|
|
2016-11-11 13:22:09 +00:00
|
|
|
applyRotationToVertexes(triangle.points[0], pivotPoint, rotate,
|
|
|
|
result.vertex, ARRAY_COUNT(result.vertex));
|
2016-11-11 10:43:03 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-09-21 16:16:14 +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-09-21 16:16:14 +00:00
|
|
|
RenderQuad_ result = {0};
|
2016-09-23 08:02:53 +00:00
|
|
|
result = createRenderQuad(renderer, V2(0, 0), V2(0, 0), V2(0, 0),
|
2016-11-11 13:22:09 +00:00
|
|
|
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-16 02:00:59 +00:00
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
else
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
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-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;
|
|
|
|
|
|
|
|
RenderQuad_ quad = createRenderQuad(renderer, posInCameraSpace, size,
|
|
|
|
pivotPoint, rotate, *renderTex);
|
|
|
|
|
2016-11-16 02:00:59 +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
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2016-11-11 13:22:09 +00:00
|
|
|
|
2016-11-11 10:43:03 +00:00
|
|
|
Vertex vertexList[6] = {quad.vertex[0], quad.vertex[0], quad.vertex[1],
|
|
|
|
quad.vertex[2], quad.vertex[3], quad.vertex[3]};
|
2016-11-11 13:22:09 +00:00
|
|
|
addVertexToRenderGroup(renderer, renderTex->tex, color, vertexList,
|
|
|
|
ARRAY_COUNT(vertexList), rendermode_quad, flags);
|
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)
|
|
|
|
{
|
|
|
|
ASSERT(numPoints > 3);
|
|
|
|
|
|
|
|
for (i32 i = 0; i < numPoints; i++)
|
|
|
|
polygonPoints[i] = v2_sub(polygonPoints[i], camera.min);
|
|
|
|
|
|
|
|
RenderTex emptyRenderTex = {0};
|
|
|
|
if (!renderTex) renderTex = &emptyRenderTex;
|
|
|
|
|
|
|
|
i32 numTrisInTriangulation = numPoints - 2;
|
|
|
|
v2 triangulationBaseP = polygonPoints[0];
|
|
|
|
i32 triangulationIndex = 0;
|
|
|
|
|
|
|
|
Vertex triangulationBaseVertex = {0};
|
|
|
|
triangulationBaseVertex.pos = triangulationBaseP;
|
|
|
|
|
|
|
|
addVertexToRenderGroup(renderer, renderTex->tex, color,
|
|
|
|
&triangulationBaseVertex, 1, rendermode_polygon,
|
|
|
|
flags);
|
2016-11-17 07:15:36 +00:00
|
|
|
RenderTriangle_ lastRenderTriForDegeneration = {0};
|
2016-11-16 13:50:49 +00:00
|
|
|
for (i32 i = 1; triangulationIndex < numTrisInTriangulation; i++)
|
|
|
|
{
|
2016-11-17 07:15:36 +00:00
|
|
|
RenderTriangle_ tri = {0};
|
|
|
|
tri.vertex[0].pos = triangulationBaseP;
|
|
|
|
tri.vertex[1].pos = polygonPoints[i + 1];
|
|
|
|
tri.vertex[2].pos = polygonPoints[i];
|
2016-11-16 13:50:49 +00:00
|
|
|
|
2016-11-17 07:15:36 +00:00
|
|
|
addVertexToRenderGroup(renderer, renderTex->tex, color, tri.vertex,
|
|
|
|
ARRAY_COUNT(tri.vertex), rendermode_polygon,
|
|
|
|
flags);
|
|
|
|
|
|
|
|
if (triangulationIndex++ >= numTrisInTriangulation)
|
|
|
|
{
|
|
|
|
lastRenderTriForDegeneration = tri;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i32 i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
addVertexToRenderGroup(renderer, renderTex->tex, color,
|
|
|
|
&lastRenderTriForDegeneration.vertex[2], 1,
|
2016-11-16 13:50:49 +00:00
|
|
|
rendermode_polygon, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-11 10:43:03 +00:00
|
|
|
void renderer_triangle(Renderer *const renderer, Rect camera,
|
2016-11-11 13:22:09 +00:00
|
|
|
TrianglePoints triangle, v2 pivotPoint, Radians rotate,
|
|
|
|
RenderTex *renderTex, v4 color, RenderFlags flags)
|
2016-11-11 10:43:03 +00:00
|
|
|
{
|
|
|
|
TrianglePoints triangleInCamSpace = {0};
|
|
|
|
ASSERT(ARRAY_COUNT(triangle.points) ==
|
|
|
|
ARRAY_COUNT(triangleInCamSpace.points));
|
|
|
|
|
|
|
|
for (i32 i = 0; i < ARRAY_COUNT(triangleInCamSpace.points); i++)
|
2016-11-16 05:57:24 +00:00
|
|
|
triangleInCamSpace.points[i] = v2_sub(triangle.points[i], camera.min);
|
2016-11-11 13:22:09 +00:00
|
|
|
|
|
|
|
RenderTex emptyRenderTex = {0};
|
|
|
|
if (!renderTex) renderTex = &emptyRenderTex;
|
2016-11-11 10:43:03 +00:00
|
|
|
|
|
|
|
RenderTriangle_ renderTriangle = createRenderTriangle(
|
2016-11-11 13:22:09 +00:00
|
|
|
renderer, triangleInCamSpace, pivotPoint, rotate, *renderTex);
|
2016-11-11 10:43:03 +00:00
|
|
|
|
2016-11-16 02:00:59 +00:00
|
|
|
// NOTE(doyle): Create degenerate vertex setup
|
|
|
|
Vertex vertexList[5] = {renderTriangle.vertex[0], renderTriangle.vertex[0],
|
|
|
|
renderTriangle.vertex[1], renderTriangle.vertex[2],
|
|
|
|
renderTriangle.vertex[2]};
|
|
|
|
|
|
|
|
addVertexToRenderGroup(renderer, renderTex->tex, color, vertexList,
|
|
|
|
ARRAY_COUNT(vertexList), rendermode_triangle, flags);
|
2016-07-16 07:15:03 +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-09-21 16:16:14 +00:00
|
|
|
Vertex *vertexList =
|
2016-09-24 04:43:59 +00:00
|
|
|
memory_pushBytes(arena, numVertexesToAlloc * sizeof(Vertex));
|
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-09-23 08:02:53 +00:00
|
|
|
RenderQuad_ quad = createRenderQuad(renderer, pos, font->maxSize,
|
|
|
|
pivotPoint, rotate, renderTex);
|
2016-09-22 08:46:53 +00:00
|
|
|
|
2016-09-23 08:02:53 +00:00
|
|
|
vertexList[vertexIndex++] = quad.vertex[0];
|
|
|
|
for (i32 i = 0; i < ARRAY_COUNT(quad.vertex); i++)
|
|
|
|
{
|
|
|
|
vertexList[vertexIndex++] = quad.vertex[i];
|
|
|
|
}
|
|
|
|
vertexList[vertexIndex++] = quad.vertex[3];
|
|
|
|
pos.x += metric.advance;
|
2016-07-12 08:11:31 +00:00
|
|
|
}
|
2016-07-07 16:45:37 +00:00
|
|
|
|
2016-09-23 09:19:47 +00:00
|
|
|
addVertexToRenderGroup(renderer, tex, color, vertexList,
|
2016-11-11 13:22:09 +00:00
|
|
|
numVertexesToAlloc, rendermode_quad, flags);
|
2016-09-24 04:43:59 +00:00
|
|
|
// TODO(doyle): Mem free
|
|
|
|
// PLATFORM_MEM_FREE(arena, vertexList,
|
|
|
|
// sizeof(Vertex) * numVertexesToAlloc);
|
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-16 05:57:24 +00:00
|
|
|
if (entity->renderMode == rendermode_quad)
|
|
|
|
{
|
|
|
|
renderer_rect(renderer, camera, entity->pos, entity->size, pivotPoint,
|
|
|
|
totalRotation, &renderTex, color, flags);
|
|
|
|
}
|
|
|
|
else if (entity->renderMode == rendermode_triangle)
|
|
|
|
{
|
|
|
|
TrianglePoints triangle = {0};
|
2016-11-16 02:00:59 +00:00
|
|
|
|
2016-11-16 05:57:24 +00:00
|
|
|
v2 entityPWithOffset = v2_sub(entity->pos, entity->offset);
|
|
|
|
v2 triangleTopPoint = V2(entityPWithOffset.x + (entity->size.w * 0.5f),
|
|
|
|
entityPWithOffset.y + entity->size.h);
|
2016-11-16 02:00:59 +00:00
|
|
|
|
2016-11-16 05:57:24 +00:00
|
|
|
v2 triangleRightSide =
|
|
|
|
V2(entityPWithOffset.x + entity->size.w, entityPWithOffset.y);
|
|
|
|
|
|
|
|
triangle.points[0] = entityPWithOffset;
|
|
|
|
triangle.points[1] = triangleRightSide;
|
|
|
|
triangle.points[2] = triangleTopPoint;
|
|
|
|
|
|
|
|
renderer_triangle(renderer, camera, triangle, pivotPoint, totalRotation,
|
|
|
|
&renderTex, color, flags);
|
|
|
|
}
|
2016-11-17 07:15:36 +00:00
|
|
|
else if (entity->renderMode == rendermode_polygon)
|
|
|
|
{
|
|
|
|
ASSERT(entity->numVertexPoints > 3);
|
|
|
|
ASSERT(entity->vertexPoints);
|
|
|
|
|
|
|
|
v2 *offsetVertexPoints = memory_pushBytes(
|
|
|
|
transientArena, entity->numVertexPoints * sizeof(v2));
|
|
|
|
for (i32 i = 0; i < entity->numVertexPoints; i++)
|
|
|
|
{
|
|
|
|
offsetVertexPoints[i] =
|
|
|
|
v2_add(entity->vertexPoints[i], entity->pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer_polygon(renderer, camera, offsetVertexPoints,
|
|
|
|
entity->numVertexPoints, pivotPoint, totalRotation,
|
|
|
|
&renderTex, color, flags);
|
|
|
|
}
|
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
|
|
|
}
|