Seagull

Seagull is a real-time single-header software rendering library written in C for beginning graphics programmers.

seagull.h

Seagull is NOT optimized for maximum performance, and as a software renderer, it is not GPU accelerated. If speed is your primary concern, go with a proper graphics accelerator like Vulkan.

Rather, this is intended as a reference implementation designed for:

  • Ease of integration
  • Modifiability
  • Optimizability
  • Readability
  • Learning
  • Basically, a way for beginners to bootstrap 3d graphics into a project without writing 1000 lines of Vulkan setup code.

    Using seagull is simple. Just include the header in your project:

    #include "seagull.h"

    Then pass it a Texture to draw into:

    Texture tex = (Texture){ .pixels = pixel_data, width = 640, height = 480 }; set_render_target(tex);

    Now you can call draw commands to draw into the buffer. Here's a quick example using SDL as a platform layer:

    And the complete code listing:

    #include <stdio.h> #include "Dependencies/SDL_Headers/SDL.h" #include "seagull.h" #define main main void main() { SDL_Init(SDL_INIT_VIDEO); SDL_Window *window = SDL_CreateWindow("Window Go Boom", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1920, 1080, 0); SDL_Surface* surface = SDL_GetWindowSurface(window); unsigned int* pixels = surface->pixels; bool window_is_open = true; Texture tex = (Texture){ .width = 1920, .height = 1080, .pixels = pixels}; set_render_target(tex); z_buffer = malloc(4*pixel_count); Mesh conan_mesh = LoadMeshWithUVindices("conan.obj"); Texture conan_texture = load_texture("conan.texture"); RecalculateNormals(&conan_mesh); Transform camera = DefaultTransform(); camera.position.z = -25; camera.position.y = 8; light_rotation = 1; while (window_is_open) { SDL_Event event; while (SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: window_is_open = false; break; } } clear(); clear_z_buffer(); Color col = 0xFF880077; static float t = 0; Shader shaders[4] = { ShadeSolidColor, ShadeColorFlatShaded, ShadeColorGouraud, ShadeTexturedGouraud, }; Transform object_transform = DefaultTransform(); object_transform.rotation = (v3){ 0, t, 0 }; object_transform.position = (v3){ 0, 0, 0 }; void *state; int index = ((int)t)%4; state = (index < 3) ? &col : &conan_texture; RenderMesh(conan_mesh, object_transform, camera, shaders[((int)t)%4], state); t+=.05; SDL_UpdateWindowSurface(window); } }

    To keep things simple and readable, Seagull makes a few basic assumptions. Colors are stored in little-endian BGRA format. The line stride of the frame buffer is assumed to just be sizeof(Color)*width in pixels, with no padding.

    If these constraints don't suit your needs, the code is small enough to be modified without too much headache. This is still very much an early work-in-progress, and there is a lot to be done before I recommend anyone actually use it for anything.