commit a3076ec14c1e8c79ee73fec8e853be796b9f47df
Author: amin <dev@aminmesbah.com>
Date: Wed, 18 Oct 2017 05:41:36 +0000
Render a beautiful black screen
Starting with some basic code from previous projects.
FossilOrigin-Name: f712023f97d7a0141db773796b04a4b183a223a7e082b1e3f9851135e5afde91
Diffstat:
A | .gitignore | | | 12 | ++++++++++++ |
A | Makefile | | | 51 | +++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/game.c | | | 77 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/game.h | | | 59 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/platform_sdl.c | | | 287 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/platform_sdl.h | | | 22 | ++++++++++++++++++++++ |
6 files changed, 508 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,12 @@
+profile_output
+debug/*
+release/*
+*.out
+*.png
+*.swp
+
+# visual studio files
+.vs/*
+packages/*
+x64/*
+*.vcxproj.filters
diff --git a/Makefile b/Makefile
@@ -0,0 +1,51 @@
+CC = gcc
+CFLAGS = -std=c99 -Wall -Wextra -Wpedantic -Wshadow -Wno-unused-parameter
+LDFLAGS = $(SDL_LDFLAGS) -lm
+
+SDL_CFLAGS := $(shell sdl2-config --cflags)
+SDL_LDFLAGS := $(shell sdl2-config --libs)
+
+override CFLAGS += $(SDL_CFLAGS)
+
+SRC_FILES = platform_sdl.c game.c
+SRC = $(addprefix src/, $(SRC_FILES))
+EXE = ohsp
+
+DBGDIR = debug
+DBGEXE = $(DBGDIR)/$(EXE)
+DBGCFLAGS = -g -Og -Werror
+
+RELDIR = release
+RELEXE = $(RELDIR)/$(EXE)
+RELCFLAGS = -O2 -Os
+
+.PHONY: all clean compile_debug compile_release debug memcheck prep run todo
+
+all: compile_debug compile_release
+
+clean:
+ rm -f $(RELDIR)/* $(DBGDIR)/*
+
+compile_debug: prep
+ $(CC) $(CFLAGS) $(DBGCFLAGS) $(SRC) -o $(DBGEXE) $(LDFLAGS)
+
+compile_release: prep
+ $(CC) $(CFLAGS) $(RELCFLAGS) $(SRC) -o $(RELEXE) $(LDFLAGS)
+
+debug: compile_debug
+ gdb $(DBGEXE)
+
+memcheck: compile_debug
+ valgrind --track-origins=yes ./$(DBGEXE)
+
+prep:
+ @mkdir -p $(DBGDIR) $(RELDIR)
+
+run: compile_release
+ ./$(DBGEXE)
+
+todo:
+ @grep -FIR --colour=never --ignore-case --line-number todo src/ \
+ | sed -re 's/^([^:]+):[[:space:]]*(.*)/\1\x01\2/' \
+ | sed -re 's/^([^:]+):[[:space:]]*(.*)/\1\x01\2/' \
+ | column -s $$'\x01' -t
diff --git a/src/game.c b/src/game.c
@@ -0,0 +1,77 @@
+#include "game.h"
+
+#include <stdbool.h>
+
+bool PAUSED = false;
+
+
+void game_init(struct GameState *game_state, int field_width, int field_height)
+{
+ if (!game_state)
+ {
+ // TODO: handle invalid pointer error
+ return;
+ }
+
+ game_state->view.dx = 0;
+ game_state->view.dy = 0;
+ game_state->view.zoom = 1;
+}
+
+
+void game_update(struct GameState *game_state, int field_width, int field_height)
+{
+ if (!game_state)
+ {
+ // TODO: handle invalid pointer error
+ return;
+ }
+}
+
+
+void game_render(struct OffscreenBuffer *buffer, float dt, struct GameState *game_state)
+{
+ if (!buffer || !game_state)
+ {
+ // TODO: handle invalid pointer error
+ return;
+ }
+}
+
+
+float game_calc_render_offset(float zoom, float delta, float pos, float center)
+{
+ return ((1 - zoom) * center) + (pos + delta) * zoom;
+}
+
+
+void game_set_pixel(struct OffscreenBuffer *buffer, uint32_t x, uint32_t y, uint32_t color)
+{
+ if (!buffer)
+ {
+ // TODO: handle invalid pointer error
+ return;
+ }
+
+ /* Origin is (0, 0) on the upper left.
+ * To go one pixel right, increment by 32 bits.
+ * To go one pixel down, increment by (buffer.width * 32) bits.
+ */
+ if (x < buffer->width && y < buffer->height)
+ {
+ uint8_t *pixel_pos = (uint8_t *)buffer->memory;
+ pixel_pos += ((BYTES_PER_PIXEL*x) + (buffer->pitch * y));
+ uint32_t *pixel = (uint32_t *)pixel_pos;
+ *pixel = color;
+ }
+}
+
+
+void game_cleanup(struct GameState *game_state)
+{
+ if (!game_state)
+ {
+ // TODO: handle invalid pointer error
+ return;
+ }
+}
diff --git a/src/game.h b/src/game.h
@@ -0,0 +1,59 @@
+#ifndef GAME_H
+
+#include <stdint.h>
+
+#define TITLE "Obsolete Human Space Pilot"
+#define SCREEN_WIDTH 640
+#define SCREEN_HEIGHT 480
+#define BYTES_PER_PIXEL 4
+
+#define SECOND 1000.0f
+#define FPS 60
+#define MS_PER_FRAME (SECOND / FPS)
+#define UPDATES_PER_SECOND 120
+#define MS_PER_UPDATE (SECOND / UPDATES_PER_SECOND)
+
+#ifndef M_PI
+#define M_PI (3.14159265358979323846264338327950288)
+#endif
+
+#define COLOR_BACKGROUND 0x000000
+
+struct GameBounds
+{
+ float center_x;
+ float center_y;
+ float side_length_x;
+ float side_length_y;
+};
+
+struct GameView
+{
+ float dx;
+ float dy;
+ float zoom;
+};
+
+struct GameState
+{
+ struct GameView view;
+};
+
+struct OffscreenBuffer
+{
+ // NOTE(amin): pixels are always 32-bits wide. Memory order: BB GG RR XX.
+ void *memory;
+ unsigned int width;
+ unsigned int height;
+ unsigned int pitch;
+};
+
+void game_init(struct GameState *game_state, int field_width, int field_height);
+void game_update(struct GameState *game_state, int field_width, int field_height);
+void game_render(struct OffscreenBuffer *buffer, float dt, struct GameState *game_state);
+float game_calc_render_offset(float zoom, float delta, float pos, float center);
+void game_set_pixel(struct OffscreenBuffer *buffer, uint32_t x, uint32_t y, uint32_t color);
+void game_cleanup(struct GameState *game_state);
+
+#define GAME_H
+#endif
diff --git a/src/platform_sdl.c b/src/platform_sdl.c
@@ -0,0 +1,287 @@
+#include "platform_sdl.h"
+
+#include "game.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <time.h>
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+extern bool PAUSED;
+
+static struct SDLOffscreenBuffer global_back_buffer;
+
+
+struct SDLWindowDimension sdl_get_window_dimension(SDL_Window *window)
+{
+ struct SDLWindowDimension result;
+ SDL_GetWindowSize(window, &result.width, &result.height);
+ return(result);
+}
+
+
+void sdl_resize_texture(struct SDLOffscreenBuffer *buffer, SDL_Renderer *renderer, int width, int height)
+{
+ if (buffer->memory)
+ {
+ free(buffer->memory);
+ }
+
+ if (buffer->texture)
+ {
+ SDL_DestroyTexture(buffer->texture);
+ }
+
+ buffer->texture = SDL_CreateTexture(
+ renderer,
+ SDL_PIXELFORMAT_ARGB8888,
+ SDL_TEXTUREACCESS_STREAMING,
+ width, height);
+
+ buffer->width = width;
+ buffer->height = height;
+ buffer->pitch = width * BYTES_PER_PIXEL;
+
+ buffer->memory = malloc(width * height * BYTES_PER_PIXEL);
+}
+
+
+void sdl_update_window(SDL_Renderer *renderer, struct SDLOffscreenBuffer *buffer)
+{
+ if (SDL_UpdateTexture(buffer->texture, 0, buffer->memory, buffer->pitch))
+ {
+ // TODO(amin): Handle this error
+ }
+
+ SDL_RenderCopy(renderer, buffer->texture, 0, 0);
+ SDL_RenderPresent(renderer);
+}
+
+
+void clear_screen(struct SDLOffscreenBuffer *buffer, uint32_t pixel_value)
+{
+ // NOTE(amin): Memset is faster than nested for loops, but can only set
+ // pixels to single byte values
+ memset(buffer->memory, pixel_value, buffer->height * buffer->width * BYTES_PER_PIXEL);
+}
+
+
+bool handle_event(SDL_Event *event)
+{
+ bool should_quit = false;
+
+ switch(event->type)
+ {
+ case SDL_QUIT:
+ {
+ printf("SDL_QUIT\n");
+ should_quit = true;
+ } break;
+
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ {
+ SDL_Keycode key_code = event->key.keysym.sym;
+ bool is_down = (event->key.state == SDL_PRESSED);
+ if (is_down)
+ {
+ if (key_code == SDLK_p)
+ {
+ PAUSED = !PAUSED;
+ }
+ }
+ } break;
+ case SDL_WINDOWEVENT:
+ {
+ switch(event->window.event)
+ {
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ {
+ SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
+ SDL_Renderer *renderer = SDL_GetRenderer(window);
+ printf("SDL_WINDOWEVENT_SIZE_CHANGED (%d, %d)\n", event->window.data1, event->window.data2);
+ sdl_resize_texture(&global_back_buffer, renderer, event->window.data1, event->window.data2);
+ } break;
+
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ {
+ printf("SDL_WINDOWEVENT_FOCUS_GAINED\n");
+ } break;
+
+ case SDL_WINDOWEVENT_EXPOSED:
+ {
+ SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
+ SDL_Renderer *renderer = SDL_GetRenderer(window);
+ sdl_update_window(renderer, &global_back_buffer);
+ } break;
+ }
+ } break;
+ }
+ return(should_quit);
+}
+
+
+int main(int argc, char *argv[])
+{
+ if (SDL_Init(SDL_INIT_VIDEO))
+ {
+ // TODO(amin): log SDL_Init error
+ }
+
+ SDL_Window *window = SDL_CreateWindow(
+ TITLE,
+ SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ SCREEN_WIDTH,
+ SCREEN_HEIGHT,
+ SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
+
+#ifdef FULLSCREEN
+ SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+#endif
+
+ if (window)
+ {
+ SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
+
+ if (renderer)
+ {
+ struct SDLWindowDimension dimension = sdl_get_window_dimension(window);
+ sdl_resize_texture(&global_back_buffer, renderer, dimension.width, dimension.height);
+
+ bool running = true;
+
+#ifdef USE_TEST_SEED
+ srand((uint32_t)0);
+#else
+ srand((uint32_t)time(NULL));
+#endif
+
+ uint64_t lag = 0;
+ uint64_t previous_ms = (SDL_GetPerformanceCounter() * SECOND) / SDL_GetPerformanceFrequency();
+
+ struct GameState game_state;
+ game_init(&game_state, dimension.width, dimension.height);
+
+ while (running)
+ {
+ uint64_t current_ms = (SDL_GetPerformanceCounter() * SECOND) / SDL_GetPerformanceFrequency();
+ uint64_t elapsed_ms = current_ms - previous_ms;
+ previous_ms = current_ms;
+ lag += elapsed_ms;
+ //printf("%" PRIu64 ", %" PRIu64 ", %f\n", elapsed_ms, lag, MS_PER_UPDATE);
+
+ SDL_Event event;
+
+ while (SDL_PollEvent(&event))
+ {
+ running = !handle_event(&event);
+ }
+
+ SDL_PumpEvents();
+
+ dimension = sdl_get_window_dimension(window);
+
+ const uint8_t *keystate = SDL_GetKeyboardState(0);
+
+ // TODO: move this to a function
+ if (keystate[SDL_SCANCODE_A] || keystate[SDL_SCANCODE_H])
+ {
+ game_state.view.dx += 5 / game_state.view.zoom;
+ }
+ if (keystate[SDL_SCANCODE_D] || keystate[SDL_SCANCODE_L])
+ {
+ game_state.view.dx -= 5 / game_state.view.zoom;
+ }
+ if (keystate[SDL_SCANCODE_W] || keystate[SDL_SCANCODE_K])
+ {
+ game_state.view.dy += 5 / game_state.view.zoom;
+ }
+ if (keystate[SDL_SCANCODE_S] || keystate[SDL_SCANCODE_J])
+ {
+ game_state.view.dy -= 5 / game_state.view.zoom;
+ }
+ if (keystate[SDL_SCANCODE_LEFT])
+ {
+ game_state.view.dx += 1 / game_state.view.zoom;
+ }
+ if (keystate[SDL_SCANCODE_RIGHT])
+ {
+ game_state.view.dx -= 1 / game_state.view.zoom;
+ }
+ if (keystate[SDL_SCANCODE_UP])
+ {
+ game_state.view.dy += 1 / game_state.view.zoom;
+ }
+ if (keystate[SDL_SCANCODE_DOWN])
+ {
+ game_state.view.dy -= 1 / game_state.view.zoom;
+ }
+ if (keystate[SDL_SCANCODE_EQUALS])
+ {
+ game_state.view.zoom *= 1.01;
+ }
+ if (keystate[SDL_SCANCODE_MINUS])
+ {
+ game_state.view.zoom *= 0.99;
+ }
+ if (keystate[SDL_SCANCODE_0])
+ {
+ game_state.view.zoom = 1;
+ }
+ if (keystate[SDL_SCANCODE_HOME])
+ {
+ game_state.view.dx = 0;
+ game_state.view.dy = 0;
+ game_state.view.zoom = 1;
+ }
+
+ struct OffscreenBuffer buffer;
+ // WARNING: these pointers are aliased until the end of the
+ // loop
+ buffer.memory = global_back_buffer.memory;
+ buffer.width = global_back_buffer.width;
+ buffer.height = global_back_buffer.height;
+ buffer.pitch = global_back_buffer.pitch;
+
+ if (PAUSED)
+ {
+ lag = 0;
+ }
+ else
+ {
+ while (lag >= MS_PER_UPDATE)
+ {
+ game_update(&game_state, buffer.width, buffer.height);
+ //printf("\t%" PRIu64 ", %f\n", lag, MS_PER_UPDATE);
+ lag -= MS_PER_UPDATE;
+ }
+ }
+
+ clear_screen(&global_back_buffer, COLOR_BACKGROUND);
+ game_render(&buffer, lag/SECOND, &game_state);
+ sdl_update_window(renderer, &global_back_buffer);
+ if (elapsed_ms <= MS_PER_FRAME)
+ {
+ SDL_Delay(MS_PER_FRAME - elapsed_ms);
+ }
+ }
+
+ game_cleanup(&game_state);
+ }
+ else
+ {
+ // TODO(amin): log SDL_Renderer error
+ }
+ }
+ else
+ {
+ // TODO(amin): log SDL_Window error
+ }
+
+ SDL_Quit();
+ return(0);
+}
diff --git a/src/platform_sdl.h b/src/platform_sdl.h
@@ -0,0 +1,22 @@
+#ifndef PLATFORM_SDL_H
+
+#include "SDL.h"
+
+struct SDLOffscreenBuffer
+{
+ // NOTE(amin): pixels are always 32-bits wide. Memory order: BB GG RR XX.
+ SDL_Texture *texture;
+ void *memory;
+ unsigned int width;
+ unsigned int height;
+ unsigned int pitch;
+};
+
+struct SDLWindowDimension
+{
+ int width;
+ int height;
+};
+
+#define PLATFORM_SDL_H
+#endif