Fix broken t-strip rendering logic

Triangle strips were originally by chance were rendering correct for most cases
until collision detection was implemented. Incorrect assumptions about
specifying triangle strip winding order meant that on occasion the last triangle
in a polygon would not be rendered.

This has been amended by strictly ensuring that on rendering of polygon the
order is correct from the vertices that are passed in.
This commit is contained in:
Doyle Thai 2016-11-23 23:47:23 +11:00
parent 0c7824bf18
commit 614fa53487
6 changed files with 125 additions and 84 deletions

Binary file not shown.

View File

@ -172,8 +172,7 @@ v2 *createAsteroidVertexList(MemoryArena_ *arena, i32 iterations,
{
f32 iterationAngle = 360.0f / iterations;
iterationAngle = DEGREES_TO_RADIANS(iterationAngle);
v2 *result =
memory_pushBytes(arena, iterations * sizeof(v2));
v2 *result = memory_pushBytes(arena, iterations * sizeof(v2));
for (i32 i = 0; i < iterations; i++)
{
@ -285,7 +284,8 @@ b32 checkEdgeProjectionOverlap(v2 *vertexList, i32 listSize,
return result;
}
b32 moveEntity(GameState *state, Entity *entity, v2 ddP, f32 dt, f32 ddPSpeed)
b32 moveEntity(GameState *state, Entity *entity, i32 entityIndex, v2 ddP,
f32 dt, f32 ddPSpeed)
{
ASSERT(ABS(ddP.x) <= 1.0f && ABS(ddP.y) <= 1.0f);
/*
@ -311,13 +311,14 @@ b32 moveEntity(GameState *state, Entity *entity, v2 ddP, f32 dt, f32 ddPSpeed)
b32 willCollide = FALSE;
// TODO(doyle): Collision for rects, (need to create vertex list for it)
#if 1
if (entity->renderMode == rendermode_polygon && entity->collides)
{
for (i32 i = 0; i < state->entityIndex; i++)
for (i32 i = entityIndex + 1; i < state->entityIndex; i++)
{
Entity *checkEntity = &state->entityList[i];
if (checkEntity->id == entity->id) continue;
ASSERT(checkEntity->id != entity->id);
if (checkEntity->renderMode == rendermode_polygon &&
checkEntity->collides)
@ -325,6 +326,7 @@ b32 moveEntity(GameState *state, Entity *entity, v2 ddP, f32 dt, f32 ddPSpeed)
/* Create entity edge lists */
v2 *entityVertexListOffsetToP =
entity_createVertexList(&state->transientArena, entity);
v2 *checkEntityVertexListOffsetToP = entity_createVertexList(
&state->transientArena, checkEntity);
@ -368,6 +370,7 @@ b32 moveEntity(GameState *state, Entity *entity, v2 ddP, f32 dt, f32 ddPSpeed)
}
#endif
#if 0
if (!willCollide)
{
entity->dP = newDp;
@ -378,6 +381,11 @@ b32 moveEntity(GameState *state, Entity *entity, v2 ddP, f32 dt, f32 ddPSpeed)
entity->dP = v2_scale(newDp, -1.0f);
}
#else
entity->dP = newDp;
entity->pos = newPos;
#endif
return willCollide;
}
@ -389,7 +397,7 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
if (!state->init)
{
srand(time(NULL));
srand((u32)time(NULL));
memory_arenaInit(&state->persistentArena, memory->persistent,
memory->persistentSize);
initAssetManager(state);
@ -397,12 +405,43 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
state->pixelsPerMeter = 70.0f;
{ // Init asteroid entities
i32 numAsteroids = 1;
for (i32 i = 0; i < numAsteroids; i++)
{
Entity *asteroid = &state->entityList[state->entityIndex];
asteroid->id = state->entityIndex++;
i32 randValue = rand();
i32 randX = (randValue % (i32)windowSize.w);
i32 randY = (randValue % (i32)windowSize.h);
asteroid->pos = V2i(100 + (i * 100), 500);
asteroid->size = V2(100.0f, 100.0f);
asteroid->hitbox = asteroid->size;
asteroid->offset = V2(asteroid->size.w * -0.5f, 0);
asteroid->scale = 1;
asteroid->rotation = 45;
asteroid->type = entitytype_asteroid;
asteroid->direction = direction_null;
asteroid->renderMode = rendermode_polygon;
asteroid->numVertexPoints = i + 10;
asteroid->vertexPoints = createAsteroidVertexList(
&state->persistentArena, asteroid->numVertexPoints,
(i32)(asteroid->size.x * 0.5f));
asteroid->tex = NULL;
asteroid->collides = TRUE;
}
}
#if 1
{ // Init ship entity
Entity *ship = &state->entityList[state->entityIndex];
ship->id = state->entityIndex++;
ship->pos = V2(100, 100);
ship->size = V2(25.0f, 50.0f);
ship->rotation = 90.0f;
ship->hitbox = ship->size;
ship->offset = v2_scale(ship->size, 0.5f);
@ -425,47 +464,9 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
ship->tex = NULL;
ship->collides = TRUE;
i32 numAsteroids = 1;
for (i32 i = 0; i < numAsteroids; i++)
{
Entity *asteroid = &state->entityList[state->entityIndex];
asteroid->id = state->entityIndex++;
i32 randValue = rand();
i32 randX = (randValue % (i32)windowSize.w);
i32 randY = (randValue % (i32)windowSize.h);
asteroid->pos = V2i(500, 500);
asteroid->size = V2(100.0f, 100.0f);
asteroid->hitbox = asteroid->size;
asteroid->offset = V2(asteroid->size.w * -0.5f, 0);
asteroid->scale = 1;
asteroid->type = entitytype_asteroid;
asteroid->direction = direction_null;
asteroid->renderMode = rendermode_polygon;
asteroid->numVertexPoints = 8;
asteroid->vertexPoints = createAsteroidVertexList(
&state->persistentArena, asteroid->numVertexPoints,
(i32)(asteroid->size.x * 0.5f));
asteroid->tex = NULL;
asteroid->collides = TRUE;
}
Entity *square = &state->entityList[state->entityIndex];
square->id = state->entityIndex++;
square->pos = V2(350, 350);
square->size = V2(75.0f, 75.0f);
square->hitbox = square->size;
square->offset = v2_scale(square->size, 0.5f);
square->scale = 1;
square->type = entitytype_asteroid;
square->direction = direction_null;
square->renderMode = rendermode_quad;
square->tex = NULL;
square->collides = TRUE;
}
#endif
state->camera.min = V2(0, 0);
state->camera.max = state->renderer.size;
@ -639,26 +640,31 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
v2_scale(ddP, dirOffset);
// NOTE(doyle): Make asteroids start and move at constant speed
ddPSpeedInMs = 2;
ddPSpeedInMs = 1;
entity->dP = v2_scale(ddP, state->pixelsPerMeter * ddPSpeedInMs);
#endif
entity->rotation += (randValue % 20) * dt;
entity->rotation += (60 * dt);
}
b32 willCollide = moveEntity(state, entity, ddP, dt, ddPSpeedInMs);
v4 entityCollisionColor = V4(1.0f, 1.0f, 1.0f, 1.0f);
if (willCollide) entityCollisionColor = V4(1.0f, 0, 0, 1.0f);
b32 willCollide = moveEntity(state, entity, i, ddP, dt, ddPSpeedInMs);
v4 entityColor = V4(1.0f, 1.0f, 1.0f, 1.0f);
#if 1
if (willCollide)
{
entityColor = V4(1.0f, 1.0f, 0, 1.0f);
}
#endif
RenderFlags flags = renderflag_wireframe | renderflag_no_texture;
renderer_entity(&state->renderer, &state->transientArena, state->camera,
entity, V2(0, 0), 0,
entityCollisionColor, flags);
entityColor, flags);
}
#if 0
debug_drawUi(state, dt);
debug_clearCounter();
#endif
renderer_renderGroups(&state->renderer);
}

View File

@ -287,5 +287,8 @@ void debug_drawUi(GameState *state, f32 dt)
DEBUG_PUSH_VAR("Permanent Size: %.0f", persistent->size, "f32");
DEBUG_PUSH_VAR("Permanent Usage: %.0f/%.0f", persistentUsage, "v2");
DEBUG_PUSH_VAR("Num Vertex: %d",
GLOBAL_debug.callCount[debugcount_numVertex], "i32");
debug_clearCounter();
}

View File

@ -96,7 +96,7 @@ v2 *entity_createVertexList(MemoryArena_ *transientArena, Entity *entity)
for (i32 i = 0; i < entity->numVertexPoints; i++)
{
result[i] = v2_sub(entity->vertexPoints[i], entity->offset);
result[i] = v2_add(entity->vertexPoints[i], entity->offset);
result[i] = v2_add(result[i], entity->pos);
}

View File

@ -26,31 +26,20 @@ INTERNAL void beginVertexBatch(Renderer *renderer)
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.
rendering.
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
The 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. The initial degenrate
vertexes are added in the add to vertex group and the ending degenerates at
on ending a vertex batch.
*/
INTERNAL void endVertexBatch(Renderer *renderer)
{
ASSERT(renderer->vertexBatchState != vertexbatchstate_off);
ASSERT(renderer->groupIndexForVertexBatch != -1);
i32 numDegenerateVertexes = 2;
i32 numDegenerateVertexes = 1;
RenderGroup *group = &renderer->groups[renderer->groupIndexForVertexBatch];
i32 freeVertexSlots = renderer->groupCapacity - group->vertexIndex;
@ -59,7 +48,6 @@ INTERNAL void endVertexBatch(Renderer *renderer)
RenderVertex degenerateVertex =
group->vertexList[group->vertexIndex - 1];
group->vertexList[group->vertexIndex++] = degenerateVertex;
group->vertexList[group->vertexIndex++] = degenerateVertex;
}
renderer->vertexBatchState = vertexbatchstate_off;
@ -168,10 +156,9 @@ INTERNAL void addVertexToRenderGroup_(Renderer *renderer, Texture *tex,
// NOTE(doyle): Two at start, two at end
i32 numDegenerateVertexes = 0;
if (renderer->vertexBatchState == vertexbatchstate_initial_add)
{
numDegenerateVertexes = 2;
}
numDegenerateVertexes = 1;
if ((numDegenerateVertexes + numVertexes) < freeVertexSlots)
{
@ -190,10 +177,37 @@ INTERNAL void addVertexToRenderGroup_(Renderer *renderer, Texture *tex,
/* Valid group, add to the render group for rendering */
if (targetGroup)
{
// 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;
}
if (renderer->vertexBatchState == vertexbatchstate_initial_add)
{
targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[0];
targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[0];
if (targetGroup->vertexIndex != 0)
{
targetGroup->vertexList[targetGroup->vertexIndex++] =
vertexList[0];
}
renderer->vertexBatchState = vertexbatchstate_active;
// NOTE(doyle): We swap groups to the front if it is valid, so
@ -202,6 +216,7 @@ INTERNAL void addVertexToRenderGroup_(Renderer *renderer, Texture *tex,
renderer->groupIndexForVertexBatch = 0;
}
for (i32 i = 0; i < numVertexes; i++)
{
targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[i];
@ -345,9 +360,13 @@ INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *group)
ASSERT(group->mode < rendermode_invalid);
if (group->flags & renderflag_wireframe)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
GL_CHECK_ERROR();
if (group->flags & renderflag_no_texture)
@ -370,6 +389,10 @@ INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *group)
}
}
#if 0
glDisable(GL_CULL_FACE);
#endif
/* Set color modulation value */
shader_uniformSetVec4f(renderer->activeShaderId, "spriteColor",
group->color);
@ -420,35 +443,39 @@ void renderer_polygon(Renderer *const renderer, Rect camera,
{
ASSERT(numPoints >= 3);
for (i32 i = 0; i < numPoints; i++)
for (i32 i = 0; i < numPoints; i++)
polygonPoints[i] = v2_sub(polygonPoints[i], camera.min);
// TODO(doyle): Do something with render texture
RenderTex emptyRenderTex = {0};
if (!renderTex) renderTex = &emptyRenderTex;
v2 triangulationBaseP = polygonPoints[0];
v2 triangulationBaseP = polygonPoints[0];
RenderVertex triangulationBaseVertex = {0};
triangulationBaseVertex.pos = triangulationBaseP;
triangulationBaseVertex.pos = triangulationBaseP;
i32 numTrisInTriangulation = numPoints - 2;
i32 triangulationIndex = 0;
beginVertexBatch(renderer);
for (i32 i = 1; triangulationIndex < numTrisInTriangulation; i++)
{
ASSERT((i + 1) < numPoints);
v2 vertexList[3] = {triangulationBaseP, polygonPoints[i],
polygonPoints[i + 1]};
beginVertexBatch(renderer);
RenderVertex triangle[3] = {0};
triangle[0].pos = triangulationBaseP;
triangle[1].pos = polygonPoints[i];
triangle[2].pos = polygonPoints[i + 1];
triangle[0].pos = vertexList[0];
triangle[1].pos = vertexList[1];
triangle[2].pos = vertexList[2];
addVertexToRenderGroup_(renderer, renderTex->tex, color, triangle,
ARRAY_COUNT(triangle), rendermode_polygon,
flags);
endVertexBatch(renderer);
triangulationIndex++;
}
endVertexBatch(renderer);
}
void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera,
@ -560,6 +587,7 @@ void renderer_entity(Renderer *renderer, MemoryArena_ *transientArena,
v2 *offsetVertexPoints =
entity_createVertexList(transientArena, entity);
renderer_polygon(renderer, camera, offsetVertexPoints,
entity->numVertexPoints,
v2_add(entity->offset, pivotPoint), totalRotation,

View File

@ -48,6 +48,10 @@ typedef struct RenderGroup
RenderFlags flags;
enum RenderMode mode;
// NOTE(doyle): Only for when adding singular triangles in triangle strip
// mode
b32 clockwiseWinding;
Texture *tex;
v4 color;