Fix bullet rendering artifacts

On delete entity, old entries were not being cleared out causing bullets being
the most commonly spawned object to inherit unusual behaviour from "dead"
entities. Fixed by ensuring on entity delete the entity entry is cleared out.

Also add rendering guard against malformed vertex points. The renderer has
a strict requirement that all polygons passed in are CCW. Bullets were being
formed with the mindset of a triangle strip causing it to render incorrectly.

Now renderer checks ordering of polygons points and asserts if incorrect. It
works based off calculating the bounding area polygons, where in CCW order this
will produce a negative result and positive for CW order.
This commit is contained in:
Doyle Thai 2016-11-25 20:43:43 +11:00
parent 5bb7fa8c13
commit d49811358a
2 changed files with 89 additions and 28 deletions

View File

@ -495,24 +495,24 @@ INTERNAL void addBullet(World *world, Entity *shooter)
Entity *bullet = &world->entityList[world->entityIndex++];
bullet->id = world->entityIdCounter++;
bullet->offset = v2_scale(bullet->size, -0.5f);
bullet->pos = v2_add(shooter->pos, bullet->offset);
bullet->hitbox = bullet->size;
bullet->pos = shooter->pos;
bullet->size = V2(2.0f, 20.0f);
bullet->offset = v2_scale(bullet->size, -0.5f);
bullet->hitbox = bullet->size;
bullet->rotation = shooter->rotation;
bullet->renderMode = rendermode_quad;
bullet->renderMode = rendermode_polygon;
if (!world->bulletVertexCache)
{
world->bulletVertexCache =
memory_pushBytes(&world->entityArena, sizeof(v2) * 4);
MEMORY_PUSH_ARRAY(&world->entityArena, 4, v2);
world->bulletVertexCache[0] = V2(0, bullet->size.h);
world->bulletVertexCache[1] = V2(0, 0);
world->bulletVertexCache[2] = V2(bullet->size.w, 0);
world->bulletVertexCache[3] = bullet->size;
}
bullet->vertexPoints = world->bulletVertexCache;
bullet->vertexPoints = world->bulletVertexCache;
bullet->numVertexPoints = 4;
bullet->type = entitytype_bullet;
@ -542,6 +542,19 @@ INTERNAL AudioRenderer *getFreeAudioRenderer(World *world)
return NULL;
}
INTERNAL void deleteEntity(World *world, i32 entityIndex)
{
ASSERT(entityIndex > 0);
ASSERT(entityIndex < ARRAY_COUNT(world->entityList));
/* Last entity replaces the entity to delete */
world->entityList[entityIndex] = world->entityList[world->entityIndex - 1];
/* Make sure the replaced entity from end of list is cleared out */
Entity emptyEntity = {0};
world->entityList[--world->entityIndex] = emptyEntity;
}
void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
v2 windowSize, f32 dt)
{
@ -649,7 +662,7 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
Entity *entity = &world->entityList[i];
ASSERT(entity->type != entitytype_invalid);
v2 pivotPoint = {0};
v2 pivotPoint = {0};
f32 ddPSpeedInMs = 0;
v2 ddP = {0};
if (entity->type == entitytype_ship)
@ -662,24 +675,10 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
// is right facing for trig to work
Radians rotation =
DEGREES_TO_RADIANS((entity->rotation + 90.0f));
v2 direction = V2(math_cosf(rotation), math_sinf(rotation));
ddP = direction;
}
Degrees rotationsPerSecond = 180.0f;
if (platform_queryKey(&state->input.keys[keycode_left],
readkeytype_repeat, 0.0f))
{
entity->rotation += (rotationsPerSecond)*dt;
}
if (platform_queryKey(&state->input.keys[keycode_right],
readkeytype_repeat, 0.0f))
{
entity->rotation -= (rotationsPerSecond)*dt;
}
if (platform_queryKey(&state->input.keys[keycode_space],
readkeytype_one_shot, KEY_DELAY_NONE))
{
@ -698,6 +697,20 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
}
}
Degrees rotationsPerSecond = 180.0f;
if (platform_queryKey(&state->input.keys[keycode_left],
readkeytype_repeat, 0.0f))
{
entity->rotation += (rotationsPerSecond)*dt;
}
if (platform_queryKey(&state->input.keys[keycode_right],
readkeytype_repeat, 0.0f))
{
entity->rotation -= (rotationsPerSecond)*dt;
}
entity->rotation = (f32)((i32)entity->rotation);
ddPSpeedInMs = 25;
DEBUG_PUSH_VAR("Pos: %5.2f, %5.2f", entity->pos, "v2");
DEBUG_PUSH_VAR("Velocity: %5.2f, %5.2f", entity->dP, "v2");
@ -792,14 +805,13 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
{
if (!math_pointInRect(world->camera, entity->pos))
{
world->entityList[i--] =
world->entityList[--world->entityIndex];
deleteEntity(world, i--);
continue;
}
Radians rotation = DEGREES_TO_RADIANS((entity->rotation + 90.0f));
v2 localDp = V2(math_cosf(rotation), math_sinf(rotation));
entity->dP = v2_scale(localDp, world->pixelsPerMeter * 10);
entity->dP = v2_scale(localDp, world->pixelsPerMeter * 5);
}
/* Loop entity around world */
@ -862,11 +874,12 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
}
ASSERT(colliderB->type == entitytype_bullet);
world->entityList[collisionIndex] =
world->entityList[--world->entityIndex];
world->entityList[i--] =
world->entityList[--world->entityIndex];
deleteEntity(world, collisionIndex);
deleteEntity(world, i--);
world->asteroidCounter--;
ASSERT(world->asteroidCounter >= 0);
AudioRenderer *audioRenderer = getFreeAudioRenderer(world);

View File

@ -443,6 +443,54 @@ void renderer_polygon(Renderer *const renderer, Rect camera,
{
ASSERT(numPoints >= 3);
{ // Validate polygon is CCW
/*
NOTE(doyle): Polygon vertexes must be specified in a CCW order!
This check utilises the equation for calculating the bounding area
of a polygon by decomposing the shapes into line segments and
calculating the area under the segment. On cartesian plane, if the
polygon is CCW, then by creating these "area" calculatings in
sequential order, we'll produce negative valued areas since we
determine line segment length by subtracting x2-x1.
Better explanation over here
http://blog.element84.com/polygon-winding.html
*/
f32 areaSum = 0.0f;
for (i32 i = 0; i < numPoints - 1; i++)
{
f32 lengthX = polygonPoints[i + 1].x - polygonPoints[i].x;
// NOTE(doyle): The height of a line segment is actually (y1 + y2)/2
// But since the (1/2) is a constant factor we can get rid of for
// checking the winding order..
// i.e. a negative number halved is still always negative.
f32 lengthY = polygonPoints[i + 1].y + polygonPoints[i].y;
areaSum += (lengthX * lengthY);
}
f32 lengthX = polygonPoints[0].x - polygonPoints[numPoints - 1].x;
f32 lengthY = polygonPoints[0].y + polygonPoints[numPoints - 1].y;
areaSum += (lengthX * lengthY);
if (areaSum < 0)
{
// NOTE(doyle): Is counter clockwise
}
else if (areaSum > 0)
{
// NOTE(doyle): Is clockwise
ASSERT(INVALID_CODE_PATH);
}
else
{
// NOTE(doyle): CW + CCW combination, i.e. figure 8 shape
ASSERT(INVALID_CODE_PATH);
}
}
for (i32 i = 0; i < numPoints; i++)
polygonPoints[i] = v2_sub(polygonPoints[i], camera.min);