From a5fc147251a31f8ff5142138514f435e582b2ccc Mon Sep 17 00:00:00 2001 From: randy Date: Wed, 20 Sep 2023 21:21:24 -0500 Subject: [PATCH 1/1] Initial commit - beginnings of layout-based rendering, and RGB/HSL handling --- Makefile | 10 ++ project.vim | 28 +++++ source/Math_Utils.c | 58 ++++++++++ source/Math_Utils.h | 21 ++++ source/main.c | 104 ++++++++++++++++++ source/sdl/SDL_Utils.c | 242 +++++++++++++++++++++++++++++++++++++++++ source/sdl/SDL_Utils.h | 28 +++++ source/structs.h | 77 +++++++++++++ 8 files changed, 568 insertions(+) create mode 100755 Makefile create mode 100644 project.vim create mode 100644 source/Math_Utils.c create mode 100644 source/Math_Utils.h create mode 100644 source/main.c create mode 100644 source/sdl/SDL_Utils.c create mode 100644 source/sdl/SDL_Utils.h create mode 100644 source/structs.h diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..a947fbb --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +LIBS := -lX11 -lm +SOURCES := $(wildcard source/*.c) +SDL_SOURCES := $(wildcard source/sdl/*.c) +IMGUI_SOURCES = imgui/imgui.cpp imgui/imgui_impl_sdl.cpp imgui/imgui_impl_sdlrenderer.cpp imgui/imgui_tables.cpp imgui/imgui_widgets.cpp imgui/imgui_draw.cpp + +sdl: source/*.c source/*.h source/sdl/*.c source/sdl/*.h + gcc -DBUILD_SDL=1 $(SDL_SOURCES) $(SOURCES) $(LIBS) -lSDL2 -lSDL2_gfx -o bin/$@ + +d_sdl: source/*.c source/*.h source/sdl/*.c source/sdl/*.h + gcc -g -DBUILD_SDL=1 $(SDL_SOURCES) $(SOURCES) $(LIBS) -lSDL2 -lSDL2_gfx -o bin/$@ diff --git a/project.vim b/project.vim new file mode 100644 index 0000000..91cbb00 --- /dev/null +++ b/project.vim @@ -0,0 +1,28 @@ +let prj_base = expand('%:p:h') +let build = prj_base . '/bin/sdl' +let run_build = "!" . build . " -nop" + +nmap \m :wa \| :make! sdl \| execute run_build +nmap \d :wa \| :make! d_sdl + +" https://stackoverflow.com/questions/37552913/vim-how-to-keep-folds-on-save +" Save folds between vim sessions +augroup remember_folds + autocmd! + autocmd BufWinLeave * mkview + autocmd BufWinEnter * silent! loadview +augroup END + +" start up a faux-IDE view +command! Ide call s:ide() +function! s:ide() + execute("sp") + execute("\J") + execute("resize 10") + execute("term") + execute("NERDTreeToggle") +endfunction + +let $src=prj_base.'/source' +let $sdl=$src.'/source/sdl' +let $bin=prj_base.'/bin' diff --git a/source/Math_Utils.c b/source/Math_Utils.c new file mode 100644 index 0000000..847d8b9 --- /dev/null +++ b/source/Math_Utils.c @@ -0,0 +1,58 @@ +#include "Math_Utils.h" +#include + +FPoint rotate(float theta, FPoint* vec) +{ + return (FPoint){vec->x * cos(theta) - vec->y * sin(theta), + vec->x * sin(theta) + vec->y * cos(theta)}; +} + +// root of sum of squared components +float magnitude(FPoint* vec) +{ + return sqrtf(pow(vec->x,2) + pow(vec->y, 2)); +} + +FPoint normalized(FPoint* vec) +{ + float mag = magnitude(vec); + return (FPoint){vec->x/mag, vec->y/mag}; +} + +float dot(const FPoint* left, const FPoint* right) +{ + return (left->x * right->x) + (left->y * right->y); +} + +FPoint mult(FPoint* vec, float mag) +{ + return (FPoint){vec->x * mag, vec->y * mag}; +} + +void mult_left(FPoint* vec, float mag) +{ + vec->x *= mag; vec->y *= mag; +} + +FPoint fp_add(FPoint* left, FPoint* right) +{ + return (FPoint){left->x + right->x, left->y + right->y}; +} + +void fp_add_left(FPoint* left, FPoint* right) +{ + left->x += right->x; + left->y += right->y; +} + +FPoint fp_sub(FPoint* left, FPoint* right) +{ + return (FPoint){left->x - right->x, left->y - right->y}; +} + +void fp_sub_left(FPoint* left, FPoint* right) +{ + left->x -= right->x; + left->y -= right->y; +} + diff --git a/source/Math_Utils.h b/source/Math_Utils.h new file mode 100644 index 0000000..2391306 --- /dev/null +++ b/source/Math_Utils.h @@ -0,0 +1,21 @@ +#ifndef __MATH_UTILS__ +#define __MATH_UTILS__ +#include "structs.h" +#include + +#define RAD_2_DEG(a) (float)a * (180.0f/M_PI) +#define DEG_2_RAD(a) (float)a * (M_PI/180.0f) + +FPoint rotate(float theta, FPoint* vec); +float magnitude(FPoint* vec); +FPoint normalized(FPoint* vec); +float dot(const FPoint* left, const FPoint* right); +FPoint mult(FPoint* vec, float mag); +void mult_left(FPoint* vec, float mag); +FPoint fp_add(FPoint* left, FPoint* right); +void fp_add_left(FPoint* left, FPoint* right); +FPoint fp_sub(FPoint* left, FPoint* right); +void fp_sub_left(FPoint* left, FPoint* right); + +#endif //__MATH_UTILS__ + diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..6440aad --- /dev/null +++ b/source/main.c @@ -0,0 +1,104 @@ +#if BUILD_SDL +#include "sdl/SDL_Utils.h" +#endif // BUILD_SDL + +#include "structs.h" +#include "Math_Utils.h" +#include +#include +#include +#include + +Game_Info gi; +void killterm_handler(int signum); + +int init() +{ + signal(SIGINT, killterm_handler); + signal(SIGKILL, killterm_handler); + signal(SIGTERM, killterm_handler); + + gi.window = (Rect){.x = 0.0f, .y = 0.0f, .w = 512.0f, .h = 512.0f}; + // satisfying rel.[xy]*2 == .[wh] centers axis in parent container + gi.rgb_square.rel = (Rect){.x = 0.05, .y = 0.05, .w = 0.5, .h = 0.5}; + gi.hue_slider.rel = (Rect){.x = 0.65, .y = 0.05, .w = .08, .h = 0.5}; + gi.final_sample.rel = (Rect){.x = 0.05, .y = .65, .w = 0.20, .h = 0.20}; + + gi.info_container.rel = (Rect){.x = 0.05, .y = .65, .w = .9, .h = .30}; + gi.info_boxes.rel = (Rect){.x = .25, .y = 0.00, .w = 0.75, .h = 1.00}; + + gi.rgb_info.rel = (Rect){.x = 0.00, .y = 0.00, .w = 1.00, .h = 0.50}; + gi.red.rel = (Rect){.x = 0.00, .y = 0.00, .w = 0.30, .h = 1.00}; + gi.green.rel = (Rect){.x = 0.35, .y = 0.00, .w = 0.30, .h = 1.00}; + gi.blue.rel = (Rect){.x = 0.70, .y = 0.00, .w = 0.30, .h = 1.00}; + + gi.hsl_info.rel = (Rect){.x = 0.00, .y = 0.50, .w = 1.00, .h = 0.50}; + gi.hue.rel = (Rect){.x = 0.00, .y = 0.00, .w = 0.30, .h = 1.00}; + gi.saturation.rel = (Rect){.x = 0.35, .y = 0.00, .w = 0.30, .h = 1.00}; + gi.luminence.rel = (Rect){.x = 0.70, .y = 0.00, .w = 0.30, .h = 1.00}; + + // lime (120 100 50) - > (0 255 0) + gi.active_hsv = (HSL_Color){.h = 120, .s = 100, .l = 50}; + + // silver (0 0 75) - > (0 255 0) + gi.active_hsv = (HSL_Color){.h = 0, .s = 0, .l = 75}; + + Color rgb = hsl_to_rgb(gi.active_hsv); + printf("%d %d %d\n", rgb.r, rgb.g, rgb.b); + + init_renderer(&gi); + + gi.game_alive = 1; + + return 0; +}; + +int handle_collisions(Game_Info* gi) +{ +} + +int game_loop(Game_Info* gi, float time_step) +{ + + return 0; +} + +int main(void) +{ + int quit = 0; + struct timespec ts_start; + struct timespec ts_end; + float time_step = 1000.0f/30; + + if(init() != 0) + { + exit(__LINE__); + } + + while(gi.game_alive) + { + clock_gettime(CLOCK_MONOTONIC_RAW, &ts_start); + + check_inputs(&gi); + game_loop(&gi, time_step); + display(&gi); + + clock_gettime(CLOCK_MONOTONIC_RAW, &ts_end); + uint64_t frameproc_ms = (ts_end.tv_nsec - ts_start.tv_nsec) / 1000000; + frameproc_ms = MIN(time_step, frameproc_ms); + delay(time_step - frameproc_ms); + } + + killterm_handler(15); + + return 0; +} + +void killterm_handler(int signum) +{ + printf("handling sig %d, bye bye\n", signum); + shutdown_renderer(); + + exit(0); +} + diff --git a/source/sdl/SDL_Utils.c b/source/sdl/SDL_Utils.c new file mode 100644 index 0000000..9083746 --- /dev/null +++ b/source/sdl/SDL_Utils.c @@ -0,0 +1,242 @@ +#include +#include + +#include "SDL_Utils.h" +#include "../Math_Utils.h" + +#define unroll_sdl_color(color) color.r, color.g, color.b, color.a +#define lineColorFPoints(r, a, b, c) lineColor(r, a.x - camera_offset.x, a.y - camera_offset.y, b.x - camera_offset.x, b.y - camera_offset.y, c) + +sdl_group mgr; +const int keypress_delta = 4; + +int32_t init_renderer(Game_Info* gi) +{ + mgr.pad = NULL; + + // default game states + // start overriding these with configs + + //load_config(argv[1], gs); + + SDL_SetMainReady(); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER)) + { + fprintf(stderr, "SDL initialization failed\n"); + exit(__LINE__); + } + + mgr.win = SDL_CreateWindow("Color Picker", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, gi->window.w, gi->window.h, 0); + if (mgr.win == NULL) + { + fprintf(stderr, "SDL initialization failed\n"); + exit(__LINE__); + } + + mgr.rend = SDL_CreateRenderer(mgr.win, -1, SDL_RENDERER_ACCELERATED); + if (mgr.rend == NULL) + { + fprintf(stderr, "SDL initialization failed\n"); + exit(__LINE__); + } + + SDL_SetRenderDrawBlendMode(mgr.rend, SDL_BLENDMODE_BLEND); +} + +int32_t shutdown_renderer() +{ + SDL_DestroyRenderer(mgr.rend); + SDL_DestroyWindow(mgr.win); + SDL_Quit(); +} + +int32_t delay(int32_t delay_time) +{ + SDL_Delay(delay_time); + + return 0; +} + +int32_t render_rgb_square(Game_Info* gi) +{ + SDL_SetRenderDrawColor(mgr.rend, 0x00, 0x00, 0x00, 0xFF); + gi->rgb_square.real = fr_margin_adjust(gi->window, gi->rgb_square.rel); + SDL_RenderFillRectF(mgr.rend, &gi->rgb_square.real); + + return 0; +} + +// REALLY this should be "generate layout", and not a true rendering step +int32_t render_container(Game_Info* gi, SDL_FRect* parent, Layout_Rect* child, SDL_Color color) +{ + SDL_SetRenderDrawColor(mgr.rend, unroll_sdl_color(color)); + child->real = fr_margin_adjust(*parent, child->rel); + + // replace this with corresponding item's callback + // hmm maybe that can be wedged into the layout_rect? + // nahhhh + SDL_RenderFillRectF(mgr.rend, &child->real); + + return 0; +} + +int32_t display(Game_Info* gi) +{ + SDL_Color red = {255, 0, 0, 255}; + SDL_Color green = {0, 255, 0, 255}; + SDL_Color blue = {0, 0, 255, 255}; + SDL_Color black = {0, 0, 0, 255}; + SDL_Color white = {255, 255, 255, 255}; + SDL_Color magenta = {255, 0, 255, 255}; + + // This is kinda the important stuff + SDL_RenderPresent(mgr.rend); + SDL_SetRenderDrawColor(mgr.rend, 0xCB, 0xCB, 0xCB, 0xCB); + SDL_RenderClear(mgr.rend); + + // this would be really cool to turn into some stack-based type of thing + render_container(gi, &gi->window, &gi->rgb_square, green); + render_container(gi, &gi->window, &gi->hue_slider, green); + render_container(gi, &gi->window, &gi->info_container, blue); + render_container(gi, &gi->window, &gi->final_sample, green); + + render_container(gi, &gi->info_container.real, &gi->info_boxes, green); + render_container(gi, &gi->info_boxes.real, &gi->rgb_info, black); + render_container(gi, &gi->info_boxes.real, &gi->hsl_info, white); + + render_container(gi, &gi->rgb_info.real, &gi->red, red); + render_container(gi, &gi->rgb_info.real, &gi->green, green); + render_container(gi, &gi->rgb_info.real, &gi->blue, blue); + + render_container(gi, &gi->hsl_info.real, &gi->hue, green); + render_container(gi, &gi->hsl_info.real, &gi->saturation, blue); + render_container(gi, &gi->hsl_info.real, &gi->luminence, red); + return 0; +} + +int32_t check_inputs(Game_Info* gi) +{ + while(SDL_PollEvent(&(mgr.event))) + { + // wasd movement and quitting + { + if (mgr.event.type == SDL_KEYDOWN && mgr.event.key.keysym.sym == SDLK_q) + { + gi->game_alive = 0; + } + } + } + + // controller support later + if(mgr.pad) + { + // update_for_controller(mgr, gs); + } + + return 0; +} + +SDL_FRect fr_subtract(const SDL_FRect left, const SDL_FRect right) +{ + return + (SDL_FRect){ + left.x - right.x, + left.y - right.y, + left.w - right.w, + left.h - right.h + }; +} + +SDL_FRect fr_add(const SDL_FRect left, const SDL_FRect right) +{ + return + (SDL_FRect){ + left.x + right.x, + left.y + right.y, + left.w + right.w, + left.h + right.h + }; +} + +SDL_FRect fr_mult(const SDL_FRect left, const SDL_FRect right) +{ + return + (SDL_FRect){ + left.x * right.x, + left.y * right.y, + left.w * right.w, + left.h * right.h + }; +} + +// Math out placement for a relative rect onto a concrete parent +SDL_FRect fr_margin_adjust(const SDL_FRect parent, const Relative_Rect child) +{ + return + (SDL_FRect){ + .x = parent.x + ( parent.w * (child.x ) ), + .y = parent.y + ( parent.h * (child.y ) ), + + .w = parent.w * child.w, + .h = parent.h * child.h, + }; +} + +// https://www.rapidtables.com/convert/color/hsl-to-rgb.html +SDL_Color hsl_to_rgb (const HSL_Color hsl) +{ + float C, X, m; + float rP, gP, bP; + + float h = fmod(hsl.h, 360.0); + float s = hsl.s/100.0f; + float l = hsl.l/100.0f; + + C = (1 - abs((2*(l)) - 1)) * s; + X = C * (1 - abs(fmod(h/60, 2.0f) -1)); + m = l - (C/2.0); + + + if (h >= 0 && h <= 60) + { + rP = C; + gP = X; + bP = 0; + } + else if (h >= 60 && h <= 120) + { + rP = X; + gP = C; + bP = 0; + } + else if (h >= 120 && h <= 180) + { + rP = 0; + gP = C; + bP = X; + } + else if (h >= 180 && h <= 240) + { + rP = 0; + gP = X; + bP = C; + } + else if (h >= 240 && h <= 300) + { + rP = X; + gP = 0; + bP = C; + } + else if (h >= 300 && h <= 360) + { + rP = C; + gP = 0; + bP = X; + } + + return (SDL_Color){ + (rP + m) * 255, + (gP + m) * 255, + (bP + m) * 255}; +} + diff --git a/source/sdl/SDL_Utils.h b/source/sdl/SDL_Utils.h new file mode 100644 index 0000000..ddd2520 --- /dev/null +++ b/source/sdl/SDL_Utils.h @@ -0,0 +1,28 @@ +#ifndef SDL_Utils__ +#define SDL_Utils__ +#include "../structs.h" + +typedef struct sdl_group +{ + SDL_Window* win; + SDL_Renderer* rend; + SDL_Texture* text; + SDL_Event event; + + SDL_GameController* pad; +} +sdl_group; + +int32_t init_renderer(Game_Info* gi); +int32_t shutdown_renderer(); +int32_t delay(int32_t delay_time); +int32_t display(Game_Info* gi); +int32_t check_inputs(Game_Info* gi); + +SDL_FRect fr_add(const SDL_FRect left, const SDL_FRect right); +SDL_FRect fr_subtract(const SDL_FRect left, const SDL_FRect right); +SDL_FRect fr_mult(const SDL_FRect left, const SDL_FRect right); +SDL_FRect fr_margin_adjust(const SDL_FRect parent, const Relative_Rect child); +SDL_Color hsl_to_rgb (const HSL_Color hsl); +#endif // SDL_Utils__ + diff --git a/source/structs.h b/source/structs.h new file mode 100644 index 0000000..894ed70 --- /dev/null +++ b/source/structs.h @@ -0,0 +1,77 @@ +#ifndef STRUCTS__ +#define STRUCTS__ + +#if BUILD_SDL +#include +#define Point SDL_Point +#define FPoint SDL_FPoint +#define Rect SDL_FRect +#define Relative_Rect SDL_FRect +#define Color SDL_Color +#endif // BUILD_SDL + +#include + +// A placeable rect, with relative components to its parent, +// and its finalized real placement rectangle +typedef struct +{ + Relative_Rect rel; + Rect real; + + // bool + // center in parent axis (even spacing on both sides?) + // unused so far + int x_center; + int y_center; + int x_offset; + int y_offset; +} Layout_Rect; + +typedef struct +{ + // Internet says hue likes to go 0-360 + // Saturation and luminence are 0-100 + uint16_t h; + uint8_t s; + uint8_t l; + uint8_t a; + +} HSL_Color; + +typedef struct +{ + FPoint position; + uint32_t radius; + +} Circle; + +typedef struct +{ + // Tabbing these to visually indicate layout + Rect window; + Layout_Rect rgb_square; // big clicky draggy square + Layout_Rect hue_slider; // HSV slider bar + Layout_Rect info_container; + Layout_Rect final_sample; // small square showing full selected color + Layout_Rect info_boxes; + Layout_Rect rgb_info; + Layout_Rect red; + Layout_Rect blue; + Layout_Rect green; + Layout_Rect hsl_info; + Layout_Rect hue; + Layout_Rect saturation; + Layout_Rect luminence; + + Color active_rgb; + + // Hue value should correspond to value on HSV slider + HSL_Color active_hsv; + + int game_alive; + +} Game_Info; + +#endif // STRUCTS__ + -- 2.49.0