#include "aliases.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 #define NORMALISED_MIN -1 #define NORMALISED_MAX 1 #define STAR_COUNT 2500 #define STAR_SPREAD 500 #define STAR_SPEED 50 #define BG_COLOR 0x060616ff #define STAR_COLOR 0xd4d4ddff #define PROJECTION_PLANE 1.0 typedef struct { u32 x; u32 y; } vec2i_t; typedef struct { f64 x; f64 y; } vec2f_t; typedef struct { f64 x; f64 y; f64 z; } vec3f_t; f64 absf(f64 val); void render_clear(SDL_Surface *surface, u32 color); void set_pixel(SDL_Surface *surface, vec2i_t pos, u32 color); #if 0 void fill_rect(SDL_Surface *surface, vec2i_t pos, u32 w, u32 h, u32 color); f64 normalise(f64 abs_val, f64 abs_min, f64 abs_max, f64 norm_min, f64 norm_max); #endif f64 denormalise(f64 norm_val, f64 norm_min, f64 norm_max, f64 abs_min, f64 abs_max); void init_starfield(vec3f_t *stars, u32 count, i32 spread); void update_startfield(vec3f_t *stars, u32 count, i32 spread, u32 speed, f64 delta); void render_starfield(SDL_Surface *surface, vec3f_t *stars, u32 count, i32 spread); int main(void) { srand(time(NULL)); SDL_Init(SDL_INIT_EVERYTHING); SDL_Window *window = SDL_CreateWindow("Starfield", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN); SDL_Surface *surface = SDL_GetWindowSurface(window); SDL_Surface *canvas = SDL_CreateRGBSurfaceWithFormat( 0, surface->w, surface->h, 32, SDL_PIXELFORMAT_ABGR32); bool running = true; SDL_Event event = {0}; vec3f_t stars[STAR_COUNT]; init_starfield(stars, STAR_COUNT, STAR_SPREAD); f64 delta = 1.0 / 60.0; // constant delta while (running) { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: running = false; break; } } update_startfield(stars, STAR_COUNT, STAR_SPREAD, STAR_SPEED, delta); render_clear(canvas, BG_COLOR); render_starfield(canvas, stars, STAR_COUNT, STAR_SPREAD); SDL_BlitSurface(canvas, NULL, surface, NULL); SDL_UpdateWindowSurface(window); } SDL_FreeSurface(canvas); SDL_DestroyWindow(window); SDL_Quit(); return EXIT_SUCCESS; } f64 absf(f64 val) { if (val < 0) { val *= -1.0; } return val; } void render_clear(SDL_Surface *surface, u32 color) { u32 length = surface->w * surface->h; SDL_LockSurface(surface); u32 *pixels = (u32 *)(surface->pixels); for (u32 i = 0; i < length; ++i) { pixels[i] = color; } SDL_UnlockSurface(surface); } void set_pixel(SDL_Surface *surface, vec2i_t pos, u32 color) { SDL_LockSurface(surface); u32 *pixels = (u32 *)(surface->pixels); u32 index = pos.y * surface->w + pos.x; if (index < surface->w * surface->h) { pixels[index] = color; } SDL_UnlockSurface(surface); } #if 0 void fill_rect(SDL_Surface *surface, vec2i_t pos, u32 w, u32 h, u32 color) { SDL_LockSurface(surface); u32 *pixels = (u32 *)(surface->pixels); for (u32 row = 0; row < h; ++row) { for (u32 col = 0; col < w; ++col) { u32 i = (pos.y + row) * surface->w + (pos.x + col); pixels[i] = color; } } SDL_UnlockSurface(surface); } f64 normalise(f64 abs_val, f64 abs_min, f64 abs_max, f64 norm_min, f64 norm_max) { if (abs_min - abs_max == 0.0) { return NAN; } f64 ratio = (abs_val - abs_max) / (abs_min - abs_max); return ratio * (norm_min - norm_max) + norm_max; } #endif f64 denormalise(f64 norm_val, f64 norm_min, f64 norm_max, f64 abs_min, f64 abs_max) { if (norm_min - norm_max == 0.0) { return NAN; } f64 ratio = (norm_val - norm_max) / (norm_min - norm_max); return ratio * (abs_min - abs_max) + abs_max; } vec3f_t init_star_position(i32 spread) { i32 spread_half = spread / 2; return (vec3f_t){.x = ((rand() % spread) - spread_half) * 2.0, .y = ((rand() % spread) - spread_half) * 2.0, .z = (rand() % spread) + PROJECTION_PLANE}; } void init_starfield(vec3f_t *stars, u32 count, i32 spread) { for (u32 i = 0; i < count; ++i) { stars[i] = init_star_position(spread); } } void update_startfield(vec3f_t *stars, u32 count, i32 spread, u32 speed, f64 delta) { for (u32 i = 0; i < count; ++i) { stars[i].z -= speed * delta; if (stars[i].z < PROJECTION_PLANE) { stars[i] = init_star_position(spread); } } } void render_starfield(SDL_Surface *surface, vec3f_t *stars, u32 count, i32 spread) { vec2f_t projected[count]; vec2i_t coords; for (u32 i = 0; i < count; ++i) { if (stars[i].z == 0.0) { continue; } projected[i] = (vec2f_t){.x = stars[i].x / stars[i].z, .y = stars[i].y / stars[i].z}; if (projected[i].x < NORMALISED_MIN || projected[i].x > NORMALISED_MAX || projected[i].y < NORMALISED_MIN || projected[i].y > NORMALISED_MAX || (projected[i].x == 0.0 && projected[i].y == 0.0)) { stars[i] = init_star_position(spread); continue; } projected[i].x = denormalise(projected[i].x, NORMALISED_MIN, NORMALISED_MAX, 0, WINDOW_WIDTH); projected[i].y = denormalise(projected[i].y, NORMALISED_MIN, NORMALISED_MAX, 0, WINDOW_HEIGHT); coords = (vec2i_t){.x = (u32)ceil(projected[i].x), .y = (u32)projected[i].y}; set_pixel(surface, coords, STAR_COLOR); } }