From c1207d8b8a0f87457de9195bd8404fc319fbfc32 Mon Sep 17 00:00:00 2001 From: Sara Date: Mon, 18 Sep 2023 08:32:11 +0200 Subject: [PATCH] added source code --- src/fencer.c | 11 ++++ src/program.c | 60 +++++++++++++++++++++ src/program.h | 29 +++++++++++ src/render.c | 85 ++++++++++++++++++++++++++++++ src/render.h | 36 +++++++++++++ src/tilemap.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tilemap.h | 27 ++++++++++ src/vmath.h | 14 +++++ 8 files changed, 403 insertions(+) create mode 100644 src/fencer.c create mode 100644 src/program.c create mode 100644 src/program.h create mode 100644 src/render.c create mode 100644 src/render.h create mode 100644 src/tilemap.c create mode 100644 src/tilemap.h create mode 100644 src/vmath.h diff --git a/src/fencer.c b/src/fencer.c new file mode 100644 index 0000000..970be49 --- /dev/null +++ b/src/fencer.c @@ -0,0 +1,11 @@ +#include "program.h" +#include "tilemap.h" + +int main(int argc, char* argv[]) { + struct ProgramSettings config = { + .target_fps = 0, // unbounded speed + .title = "fencer", + .view_resolution = {160, 140} + }; + return program_run(&config); +} diff --git a/src/program.c b/src/program.c new file mode 100644 index 0000000..9109b0d --- /dev/null +++ b/src/program.c @@ -0,0 +1,60 @@ +#include "program.h" +#include + +struct Program g_program; + +#define INITFLAGS SDL_INIT_EVENTS | SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER + +int program_run(const struct ProgramSettings* settings) { + SDL_Init(INITFLAGS); + + g_program.window = SDL_CreateWindow( + settings->title, + SDL_WINDOWPOS_CENTERED_DISPLAY(0), + SDL_WINDOWPOS_CENTERED_DISPLAY(0), + settings->view_resolution.x, + settings->view_resolution.y, + SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE); + + g_program.render_context = render_init(&g_program, settings); + + for(;;) { + SDL_Delay(1); + program_handle_events(&g_program); + render_present(g_program.render_context); + } + + return 1; +} + +void program_quit(struct Program* self) { + render_destroy(self->render_context); + SDL_DestroyWindow(self->window); + SDL_Quit(); + exit(0); +} + +void program_handle_events(struct Program* self) { + SDL_Event event; + while(SDL_PollEvent(&event)) { + switch(event.type) { + default: break; + case SDL_WINDOWEVENT: + if(event.window.windowID == SDL_GetWindowID(self->window)) { + program_handle_windowevent(self, &event.window); + } + break; + case SDL_QUIT: + program_quit(self); + break; + } + } +} + +void program_handle_windowevent(struct Program* self, SDL_WindowEvent* event) { + switch(event->type) { + default: + render_handle_resize(self->render_context); + break; + } +} diff --git a/src/program.h b/src/program.h new file mode 100644 index 0000000..d3f8e85 --- /dev/null +++ b/src/program.h @@ -0,0 +1,29 @@ +#ifndef _fencer_program_h +#define _fencer_program_h + +#include +#include +#include "vmath.h" +#include "render.h" + +extern struct Program g_program; + +struct ProgramSettings { + const char* title; + IVector view_resolution; + int target_fps; +}; + +struct Program { + SDL_Window* window; + struct RenderContext* render_context; + double delta_time; + double start_frame; +}; + +extern int program_run(const struct ProgramSettings* settings); +extern void program_quit(struct Program* self); +extern void program_handle_events(struct Program* self); +extern void program_handle_windowevent(struct Program* self, SDL_WindowEvent* event); + +#endif // !_fencer_program_h diff --git a/src/render.c b/src/render.c new file mode 100644 index 0000000..7d8e0be --- /dev/null +++ b/src/render.c @@ -0,0 +1,85 @@ +#include "render.h" +#include "program.h" +#include +#include + +struct RenderContext* render_init(const struct Program* program, const struct ProgramSettings* settings) { + struct RenderContext* self = malloc(sizeof(struct RenderContext)); + + // create renderer, needs to be able to target textures, preferably hardware accelerated + self->renderer = SDL_CreateRenderer(program->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + // create render target texture + self->render_target = SDL_CreateTexture( + self->renderer, + SDL_PIXELFORMAT_ARGB32, + SDL_TEXTUREACCESS_TARGET, + settings->view_resolution.x, settings->view_resolution.y); + + // store the desired view resolution + self->render_resolution = settings->view_resolution; + + // get the destination area for rendering the target onto the window + render_calculate_render_area(self); + + // clear target black + SDL_SetRenderTarget(self->renderer, self->render_target); + SDL_SetRenderDrawColor(self->renderer, 0, 0, 0, 255); + SDL_RenderClear(self->renderer); + + return self; +} + +void render_destroy(struct RenderContext* self) { + // clear up allocated SDL objects + SDL_DestroyTexture(self->render_target); + SDL_DestroyRenderer(self->renderer); +} + +void render_present(struct RenderContext* self) { + // clear window white + SDL_SetRenderTarget(self->renderer, NULL); + SDL_SetRenderDrawColor(self->renderer, 0, 0, 0, 255); + SDL_RenderClear(self->renderer); + // copy render target + SDL_Rect source_rect = {0, 0, self->render_resolution.x, self->render_resolution.y}; + SDL_RenderCopy(self->renderer, self->render_target, &source_rect, &self->render_area); + SDL_RenderPresent(self->renderer); + // clear render target + SDL_SetRenderTarget(self->renderer, self->render_target); + SDL_SetRenderDrawColor(self->renderer, 0, 0, 0, 255); + SDL_RenderClear(self->renderer); +} + +void render_calculate_render_area(struct RenderContext* self) { + // get aspect ratios of both the window and the rendertexture + IVector window_resolution = render_get_window_size(self); + float window_aspect = (float)window_resolution.x / (float)window_resolution.y; + float target_aspect = (float)self->render_resolution.x / (float)self->render_resolution.y; + + // calculate the largest area that will fit the entire rendertexture into the window space + self->render_area = (SDL_Rect) {0, 0, window_resolution.x, window_resolution.y}; + if(window_aspect <= target_aspect) { + self->render_area.h = window_resolution.x / target_aspect; + self->render_area.y = (window_resolution.y - self->render_area.h) / 2; + } else { + self->render_area.w = window_resolution.y * target_aspect; + self->render_area.x += (window_resolution.x - self->render_area.w) / 2; + } +} + +IVector render_get_window_size(struct RenderContext* self) { + // select window as target (store target to reset) + SDL_Texture* target = SDL_GetRenderTarget(self->renderer); + SDL_SetRenderTarget(self->renderer, NULL); + // fetch output size (= window size) of renderer + int window_width, window_height; + SDL_GetRendererOutputSize(self->renderer, &window_width, &window_height); + // reset render target + SDL_SetRenderTarget(self->renderer, target); + // construct IVector from fetched output size + return (IVector){window_width, window_height}; +} + +void render_handle_resize(struct RenderContext* self) { + render_calculate_render_area(self); +} diff --git a/src/render.h b/src/render.h new file mode 100644 index 0000000..240eecd --- /dev/null +++ b/src/render.h @@ -0,0 +1,36 @@ +#ifndef _fencer_render_h +#define _fencer_render_h + +#include +#include +#include "vmath.h" + +struct Program; +struct ProgramSettings; + +struct RenderContext { + // renderer created from window in Program passed to render_init + SDL_Renderer* renderer; + + // render target + SDL_Texture* render_target; + // area of window to render render_target to + SDL_Rect render_area; + + // size of render_target + IVector render_resolution; + + // current world location of camera + IVector camera_offset; +}; + +extern struct RenderContext* render_init(const struct Program* program, const struct ProgramSettings* settings); +extern void render_destroy(struct RenderContext* self); +extern void render_present(struct RenderContext* self); + +extern void render_calculate_render_area(struct RenderContext* self); +extern IVector render_get_window_size(struct RenderContext* self); +extern void render_handle_resize(struct RenderContext* self); + + +#endif // !_fencer_render_h diff --git a/src/tilemap.c b/src/tilemap.c new file mode 100644 index 0000000..1d0dd99 --- /dev/null +++ b/src/tilemap.c @@ -0,0 +1,141 @@ +#include "tilemap.h" +#include "program.h" +#include +#include + +struct Tilemap tilemap_load(const char* tilemap_file, const char* tileset_file) { + struct Tilemap self = { + .dimensions = {1,1}, + .tiles = NULL + }; + + FILE* fs = fopen(tilemap_file, "r"); + int n = 0; + do { + n = fgetc(fs); + if(n == ',') { + self.dimensions.x++; + } + } while(n != '\n'); + do { + n = fgetc(fs); + if(n == '\n') { + self.dimensions.y++; + } + } while(n != '\0'); + + rewind(fs); + + self.tiles = malloc(self.dimensions.x * self.dimensions.y * sizeof(int)); + + char csv_buffer[6]; + csv_buffer[5] = '\0'; + char* writer = csv_buffer; + int* tile = self.tiles; + + do { + n = fgetc(fs); + if(isalnum(n)) { + *writer = n; + ++writer; + } else if(n == ',' || n == '\0' || n == '\n') { + *writer = '\0'; + writer = csv_buffer; + *tile = atoi(csv_buffer); + ++tile; + } + } while(n != '\0'); + + fclose(fs); + + self.tileset = tileset_load(tileset_file); + + return self; +} + +static inline +int is_path_char(int n) { + return isalnum(n) || n == '/'; +} + +struct Tileset tileset_load(const char* filename) { + char* texture_path = NULL; + char* writer = NULL; + char num_buffer[6]; + num_buffer[5] = '\0'; + int n = 0; + struct Tileset self = { + .texture = NULL, + .tile_size = {0,0} + }; + size_t len = 1; + + FILE* fs = fopen(filename, "r"); + + do { + n = fgetc(fs); + if(is_path_char(n)) { + ++len; + } + } while(n != ','); + + texture_path = malloc(len * sizeof(char)); + + rewind(fs); + writer = texture_path; + + do { + n = fgetc(fs); + if(is_path_char(n)) { + *writer = n; + ++writer; + } else if(n == ',') { + *writer = '\0'; + } + } while(n != ','); + + self.texture = IMG_LoadTexture(g_program.render_context->renderer, texture_path); + SDL_QueryTexture(self.texture, NULL, NULL, &self.texture_resolution.x, &self.texture_resolution.y); + self.shear = floor((float)self.texture_resolution.x / self.tile_size.x); + + writer = num_buffer; + + do { + n = fgetc(fs); + if(isdigit(n) || n == '-') { + *writer= n; + ++writer; + } else if(n == ',' || feof(fs) || n == '\n') { + *writer = '\0'; + writer = num_buffer; + if(self.tile_size.x == 0) { + self.tile_size.x = atoi(num_buffer); + } else { + self.tile_size.y = atoi(num_buffer); + } + } + } while(!feof(fs)); + + fclose(fs); + + return self; +} + +SDL_Rect tileset_index_to_rect(struct Tileset* self, size_t index) { + SDL_Rect rect = {0,0, self->tile_size.x, self->tile_size.y}; + rect.x = (index % self->shear) * rect.w; + rect.y = (index / self->shear) * rect.h; + return rect; +} + +void tilemap_render(struct Tilemap* self, struct RenderContext* render) { + size_t num_tiles = self->dimensions.x * self->dimensions.y; + SDL_Rect source_rect = {0, 0, self->tileset.tile_size.x, self->tileset.tile_size.y}; + SDL_Rect dest_rect = source_rect; + for(int i = 0; i < num_tiles; ++i) { + source_rect = tileset_index_to_rect(&self->tileset, self->tiles[i]); + dest_rect.x = (i % self->dimensions.x) * dest_rect.w; + dest_rect.y = (i / self->dimensions.y) * dest_rect.h; + SDL_RenderCopy(render->renderer, self->tileset.texture, &source_rect, &dest_rect); + } +} \ No newline at end of file diff --git a/src/tilemap.h b/src/tilemap.h new file mode 100644 index 0000000..3427d87 --- /dev/null +++ b/src/tilemap.h @@ -0,0 +1,27 @@ +#ifndef _fencer_tilemap_h +#define _fencer_tilemap_h + +#include "vmath.h" +#include "render.h" +#include + +struct Tileset { + SDL_Texture* texture; + IVector tile_size; + IVector texture_resolution; + int shear; +}; + +struct Tilemap { + IVector dimensions; + int* tiles; + struct Tileset tileset; +}; + +extern struct Tilemap tilemap_load(const char* tilemap_file, const char* tileset_file); +extern struct Tileset tileset_load(const char* filename); +extern SDL_Rect tileset_index_to_rect(struct Tileset* self, size_t index); + +extern void tilemap_render(struct Tilemap* map, struct RenderContext* render); + +#endif // !_fencer_tilemap_h diff --git a/src/vmath.h b/src/vmath.h new file mode 100644 index 0000000..b445315 --- /dev/null +++ b/src/vmath.h @@ -0,0 +1,14 @@ +#ifndef _fencer_vmath_h +#define _fencer_vmath_h + +typedef struct Vector { + float x; + float y; +} Vector; + +typedef struct IVector { + int x; + int y; +} IVector; + +#endif // !_fencer_vmath_h