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:
parent
5bb7fa8c13
commit
d49811358a
@ -495,17 +495,17 @@ INTERNAL void addBullet(World *world, Entity *shooter)
|
|||||||
Entity *bullet = &world->entityList[world->entityIndex++];
|
Entity *bullet = &world->entityList[world->entityIndex++];
|
||||||
bullet->id = world->entityIdCounter++;
|
bullet->id = world->entityIdCounter++;
|
||||||
|
|
||||||
bullet->offset = v2_scale(bullet->size, -0.5f);
|
bullet->pos = shooter->pos;
|
||||||
bullet->pos = v2_add(shooter->pos, bullet->offset);
|
|
||||||
bullet->hitbox = bullet->size;
|
|
||||||
bullet->size = V2(2.0f, 20.0f);
|
bullet->size = V2(2.0f, 20.0f);
|
||||||
|
bullet->offset = v2_scale(bullet->size, -0.5f);
|
||||||
|
bullet->hitbox = bullet->size;
|
||||||
bullet->rotation = shooter->rotation;
|
bullet->rotation = shooter->rotation;
|
||||||
bullet->renderMode = rendermode_quad;
|
bullet->renderMode = rendermode_polygon;
|
||||||
|
|
||||||
if (!world->bulletVertexCache)
|
if (!world->bulletVertexCache)
|
||||||
{
|
{
|
||||||
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[0] = V2(0, bullet->size.h);
|
||||||
world->bulletVertexCache[1] = V2(0, 0);
|
world->bulletVertexCache[1] = V2(0, 0);
|
||||||
world->bulletVertexCache[2] = V2(bullet->size.w, 0);
|
world->bulletVertexCache[2] = V2(bullet->size.w, 0);
|
||||||
@ -542,6 +542,19 @@ INTERNAL AudioRenderer *getFreeAudioRenderer(World *world)
|
|||||||
return NULL;
|
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,
|
void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
|
||||||
v2 windowSize, f32 dt)
|
v2 windowSize, f32 dt)
|
||||||
{
|
{
|
||||||
@ -662,24 +675,10 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
|
|||||||
// is right facing for trig to work
|
// is right facing for trig to work
|
||||||
Radians rotation =
|
Radians rotation =
|
||||||
DEGREES_TO_RADIANS((entity->rotation + 90.0f));
|
DEGREES_TO_RADIANS((entity->rotation + 90.0f));
|
||||||
|
|
||||||
v2 direction = V2(math_cosf(rotation), math_sinf(rotation));
|
v2 direction = V2(math_cosf(rotation), math_sinf(rotation));
|
||||||
ddP = direction;
|
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],
|
if (platform_queryKey(&state->input.keys[keycode_space],
|
||||||
readkeytype_one_shot, KEY_DELAY_NONE))
|
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;
|
ddPSpeedInMs = 25;
|
||||||
DEBUG_PUSH_VAR("Pos: %5.2f, %5.2f", entity->pos, "v2");
|
DEBUG_PUSH_VAR("Pos: %5.2f, %5.2f", entity->pos, "v2");
|
||||||
DEBUG_PUSH_VAR("Velocity: %5.2f, %5.2f", entity->dP, "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))
|
if (!math_pointInRect(world->camera, entity->pos))
|
||||||
{
|
{
|
||||||
world->entityList[i--] =
|
deleteEntity(world, i--);
|
||||||
world->entityList[--world->entityIndex];
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Radians rotation = DEGREES_TO_RADIANS((entity->rotation + 90.0f));
|
Radians rotation = DEGREES_TO_RADIANS((entity->rotation + 90.0f));
|
||||||
v2 localDp = V2(math_cosf(rotation), math_sinf(rotation));
|
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 */
|
/* Loop entity around world */
|
||||||
@ -862,11 +874,12 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(colliderB->type == entitytype_bullet);
|
ASSERT(colliderB->type == entitytype_bullet);
|
||||||
world->entityList[collisionIndex] =
|
|
||||||
world->entityList[--world->entityIndex];
|
deleteEntity(world, collisionIndex);
|
||||||
world->entityList[i--] =
|
|
||||||
world->entityList[--world->entityIndex];
|
deleteEntity(world, i--);
|
||||||
world->asteroidCounter--;
|
world->asteroidCounter--;
|
||||||
|
|
||||||
ASSERT(world->asteroidCounter >= 0);
|
ASSERT(world->asteroidCounter >= 0);
|
||||||
|
|
||||||
AudioRenderer *audioRenderer = getFreeAudioRenderer(world);
|
AudioRenderer *audioRenderer = getFreeAudioRenderer(world);
|
||||||
|
@ -443,6 +443,54 @@ void renderer_polygon(Renderer *const renderer, Rect camera,
|
|||||||
{
|
{
|
||||||
ASSERT(numPoints >= 3);
|
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++)
|
for (i32 i = 0; i < numPoints; i++)
|
||||||
polygonPoints[i] = v2_sub(polygonPoints[i], camera.min);
|
polygonPoints[i] = v2_sub(polygonPoints[i], camera.min);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user