Allow me to be more concrete, using real snippets of code.
#include "core/game.h"
int main(void) {
unsigned long system_time = 0UL;
unsigned long simulation_time = 0UL;
GameHandle game;
Game_initialize(&game);
Game_open_window(game);
while (Game_is_running(game) == 1) {
/* Cap framerate (30 FPS). */
system_time = Game_get_milliseconds();
Game_limit_framerate(game, system_time);
Game_process_input();
/* Ensure framerate independent movement. */
while (simulation_time < system_time) {
simulation_time += 16;
Game_update(game, 16);
}
Game_render(game);
}
Game_shutdown(game);
return 0;
}
So as you can see, Game_update(game, 16)
runs multiple times before it comes to Game_render(game)
.
That means the fragment buffer “explodes” (more fragments - world transformed vertices, than original vertices), if I don’t limit the fragment buffer’s maximum range.
I don’t know any better.
Other than implementing some kind of lock-and-wait mechanism.
void Game_update(GameHandle self, int milliseconds) {
static float angle = 0.0f;
Mesh* mesh = self->meshes;
Mesh* const kMeshEnd = mesh + self->mesh_count;
/* Update rotation angle. */
angle += 1E-3f * (float)milliseconds;
/* Apply world transform, cull (if applicable) and store result into fragment
* buffer.
*/
for (mesh = self->meshes; mesh != kMeshEnd; ++mesh) {
TriangleFace* face =
(TriangleFace*)DynamicArray_begin(mesh->triangle_faces);
TriangleFace* const kFaceEnd =
(TriangleFace*)DynamicArray_end(mesh->triangle_faces);
TriangleFace fragment = *face;
/* Compute world transform. */
Transform4 const kScaling =
CreateTransform4Scaling(mesh->size.x, mesh->size.y, mesh->size.z);
Transform4 const kRotationX =
CreateTransform4RotationX(mesh->orientation.x);
Transform4 const kRotationY =
CreateTransform4RotationY(mesh->orientation.y + angle);
Transform4 const kRotationZ =
CreateTransform4RotationZ(mesh->orientation.z);
Transform4 const kTranslation =
CreateTransform4Translation(self->translation.x + mesh->position.x,
self->translation.y + mesh->position.y,
self->translation.z + mesh->position.z);
Transform4 world_transform = CreateIdentityTransform4();
world_transform = ComposeTransform4(world_transform, kTranslation);
world_transform = ComposeTransform4(world_transform, kScaling);
world_transform = ComposeTransform4(world_transform, kRotationX);
world_transform = ComposeTransform4(world_transform, kRotationY);
world_transform = ComposeTransform4(world_transform, kRotationZ);
for (; face != kFaceEnd; ++face) {
TransformTriangleFace(face, &fragment, world_transform);
fragment.texture = mesh->texture;
/* Apply painter's algorithm. */
if (self->depth_sort == 1) {
qsort(self->fragment_buffer, self->triangle_count, sizeof(TriangleFace),
CompareAverageDepth);
}
if (self->cull == 1) {
int skip =
Cull(fragment.vertices[0].position, fragment.vertices[1].position,
fragment.vertices[2].position, self->camera_position);
if (skip == 1) {
continue;
}
}
if (self->fragment_count <= self->triangle_count) {
self->fragment_buffer[self->fragment_count] = fragment;
++self->fragment_count;
}
}
}
}
The artificial limitation occurs inside
if (self->fragment_count <= self->triangle_count) {
self->fragment_buffer[self->fragment_count] = fragment;
++self->fragment_count;
}
However, this apparently leads to render artifacts.
void Game_render(GameHandle self) {
TriangleFace* fragment = self->fragment_buffer;
TriangleFace* const kFragmentBufferEnd = fragment + self->fragment_count;
int raster_points[6] = {0, 0, 0, 0, 0, 0};
Transform4 const kTransform = CreateTransform4WorldWindowToRasterDevice(
(float)self->pixel_buffer_width, (float)self->pixel_buffer_height);
PixelBuffer_clear(self->pixel_buffer, 76, 82, 78);
/* Perspectively project vertices and convert to pixel coordinates. */
for (fragment = self->fragment_buffer; fragment != kFragmentBufferEnd;
++fragment) {
size_t i = 0UL;
for (i = 0UL; i < 3UL; ++i) {
PROJECT_POINT(fragment->vertices[i].position.x,
fragment->vertices[i].position.y,
fragment->vertices[i].position.z, 1.0f);
fragment->vertices[i].position = CombineTransform4AndTuple4(
kTransform, fragment->vertices[i].position);
}
}
/* Convert fragment positions to raster points. */
for (fragment = self->fragment_buffer; fragment != kFragmentBufferEnd;
++fragment) {
raster_points[0] = (int)fragment->vertices[0].position.x;
raster_points[1] = (int)fragment->vertices[0].position.y;
raster_points[2] = (int)fragment->vertices[1].position.x;
raster_points[3] = (int)fragment->vertices[1].position.y;
raster_points[4] = (int)fragment->vertices[2].position.x;
raster_points[5] = (int)fragment->vertices[2].position.y;
{
int const kAX = CLAMP(raster_points[0], 0, self->pixel_buffer_width);
int const kAY = CLAMP(raster_points[1], 0, self->pixel_buffer_height);
int const kBX = CLAMP(raster_points[2], 0, self->pixel_buffer_width);
int const kBY = CLAMP(raster_points[3], 0, self->pixel_buffer_height);
int const kCX = CLAMP(raster_points[4], 0, self->pixel_buffer_width);
int const kCY = CLAMP(raster_points[5], 0, self->pixel_buffer_height);
if (self->fill_polygons == 0) {
DrawTriangleLines(kAX, kAY, kBX, kBY, kCX, kCY, 255, 255, 255);
} else {
RasterizeTriangle(kAX, kAY, kBX, kBY, kCX, kCY, OnComputeColor,
OnDrawPixel, fragment);
}
}
}
self->fragment_count = 0;
PixelBuffer_flip_vertically(self->pixel_buffer);
UpdateSdlRgbSurface(self->pixel_buffer_data);
UpdateSdlWindowSurface();
}
self->fragment_count = 0
is another “artificial” limitation.
The thing is, I allocate 1024 FragmentBuffer
objects.
#ifndef MODEL_H_
#define MODEL_H_
#include <stddef.h>
#include "../freestanding/tuple_algebra.h"
#include "../standalones/dynamic_array.h"
typedef struct Vertex Vertex;
typedef struct TriangleFace TriangleFace;
typedef struct TriangleMesh TriangleMesh;
struct Vertex {
Tuple4 position;
Tuple4 texture_coordinates;
Tuple4 vertex_normal;
};
struct TriangleFace {
Vertex vertices[3];
};
struct TriangleMesh {
DynamicArrayHandle triangle_faces; /* Array of `TriangleFaces[N]`. */
};
void LoadTriangleMesh(TriangleMesh* triangle_mesh, char const* obj_filename);
void FreeTriangleMesh(TriangleMesh* triangle_mesh);
size_t GetTriangleFaceCount(TriangleMesh const* triangle_mesh);
#endif /* MODEL_H_ */