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:
parent
0c7824bf18
commit
614fa53487
Binary file not shown.
116
src/Asteroid.c
116
src/Asteroid.c
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user