commit 3ba95ff850f4fc9a85dc0a8ab877e3241af59216 Author: Gitea Date: Sat May 28 16:03:46 2022 +0300 base diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ed4e45 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +./cloth_sim \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e208181 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +all: + g++ -lSDL2 -O2 -Wall -Werror src/*.cpp -o cloth_sim + +clean: + rm -f cloth_sim \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp new file mode 100644 index 0000000..dcaf4b7 --- /dev/null +++ b/src/app.cpp @@ -0,0 +1,95 @@ +#include "app.hpp" +#include "cloth.hpp" +#include "window.hpp" +#include +#include +#include +#include +#include + +App* app_init(App_config conf) { + App* app = new App; + + if (conf.window_dimensions.x <= 0) { + conf.window_dimensions.x = 500; + } + if (conf.window_dimensions.y <= 0) { + conf.window_dimensions.y = 500; + } + app->conf = conf; + + app->window = new_window(conf.win_name, conf.window_dimensions); + app->cloth = new_cloth(conf.cloth_startpos, conf.cloth_dimensions, conf.cloth_spacing); + + if (app->window == NULL || app->cloth == NULL) { + return NULL; + } + + return app; +} + +void app_handle_input(App* app) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + + switch (event.type) { + case SDL_QUIT: + app->is_running = false; + break; + + case SDL_KEYDOWN: + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + app->is_running = false; + break; + + case SDLK_q: + app->is_running = false; + break; + + case SDLK_SPACE: + toggle_pause(app); + break; + } + } + } +} + +void app_render(App* app) { + fill_screen(app->window, app->conf.background_color); + draw_cloth( + app->window, app->cloth, + app->conf.point_color, app->conf.connection_color, app->conf.point_selected_color); + SDL_RenderPresent(app->window->renderer); +} + +void app_update(App* app) { + if (app->paused) { + return; + } + + compute_cloth_forces(app->cloth, app->conf.gravity); + + app->conf.gravity = Vec2f{app->conf.gravity.x-0.0005f, app->conf.gravity.y-0.001f}; + + int updated_window_w; + int updated_window_h; + SDL_GetWindowSize(app->window->sdl_win, &updated_window_w, &updated_window_h); + app->window->dimensions = Vec2{(unsigned int) updated_window_w, (unsigned int) updated_window_h}; +} + +void destroy_app(App* app) { + destroy_cloth(app->cloth); + destroy_window(app->window); + app->cloth = NULL; + app->window = NULL; + app->is_running = false; +} + +void toggle_pause(App* app) { + if (app->paused) { + app->paused = false; + } else { + app->paused = true; + } +} \ No newline at end of file diff --git a/src/app.hpp b/src/app.hpp new file mode 100644 index 0000000..0cf365c --- /dev/null +++ b/src/app.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "window.hpp" +#include "cloth.hpp" +#include + +struct App_config { + const char* win_name; + Vec2 window_dimensions; + Vec2 cloth_startpos; + Vec2 cloth_dimensions; + unsigned int cloth_spacing; + Vec2f gravity; + RGB background_color; + RGB point_color; + RGB point_selected_color; + RGB connection_color; +}; + +struct App { + bool is_running; + bool paused; + App_config conf; + Window* window; + Cloth* cloth; +}; + +App* app_init(App_config conf); + +void app_update(App* app); +void app_handle_input(App* app); +void app_render(App* app); +void destroy_app(App* app); +void toggle_pause(App* app); \ No newline at end of file diff --git a/src/cloth.cpp b/src/cloth.cpp new file mode 100644 index 0000000..a99b5d5 --- /dev/null +++ b/src/cloth.cpp @@ -0,0 +1,61 @@ +#include "cloth.hpp" +#include "connection.hpp" +#include +#include +#include + +Cloth* new_cloth(Vec2 startpos, Vec2 dimensions, unsigned int spacing) { + Cloth* new_cloth = new Cloth; + new_cloth->startpos = startpos; + new_cloth->dimensions = dimensions; + new_cloth->points = std::vector(); + new_cloth->connections = std::vector(); + + for (unsigned int y = 0; y < dimensions.y; y += spacing) { + for (unsigned int x = 0; x < dimensions.x; x += spacing) { + Point* new_point = new Point{static_cast(startpos.x + x), static_cast(startpos.y + y), startpos.x + x, startpos.y + y, false}; + new_cloth->points.push_back(new_point); + + if (x > 0) { + Point* p0_left = new_cloth->points[new_cloth->points.size() - 2]; + Connection* new_conn = new Connection{*p0_left, *new_point}; + new_cloth->connections.push_back(new_conn); + } + + if (y != 0) { + Point* p0_up = new_cloth->points[new_cloth->points.size()-1 - dimensions.x/spacing]; + Connection* new_conn = new Connection{*p0_up, *new_point}; + new_cloth->connections.push_back(new_conn); + } + } + } + + return new_cloth; +} + +void destroy_cloth(Cloth* cloth) { + if (cloth == NULL) { + return; + } + + for (unsigned int i = 0; i < cloth->points.size(); i++) { + delete cloth->points[i]; + } + + + for (unsigned int i = 0; i < cloth->connections.size(); i++) { + delete cloth->connections[i]; + } + + cloth->points.clear(); + cloth->connections.clear(); + + delete cloth; +} + +void compute_cloth_forces(Cloth* cloth, Vec2f gravity_vec) { + for (Point* p : cloth->points) { + p->x += gravity_vec.x; + p->y += gravity_vec.y; + } +} \ No newline at end of file diff --git a/src/cloth.hpp b/src/cloth.hpp new file mode 100644 index 0000000..bca4de2 --- /dev/null +++ b/src/cloth.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "point.hpp" +#include "connection.hpp" +#include "vec.hpp" +#include + +struct Cloth { + Vec2 startpos; + Vec2 dimensions; + std::vector points; + std::vector connections; +}; + +Cloth* new_cloth(Vec2 startpos, Vec2 dimensions, unsigned int spacing); +void destroy_cloth(Cloth* cloth); +void compute_cloth_forces(Cloth* cloth, Vec2f gravity_vec); \ No newline at end of file diff --git a/src/color.hpp b/src/color.hpp new file mode 100644 index 0000000..4ba302d --- /dev/null +++ b/src/color.hpp @@ -0,0 +1,7 @@ +#pragma once + +struct RGB { + unsigned char r; + unsigned char g; + unsigned char b; +}; diff --git a/src/connection.hpp b/src/connection.hpp new file mode 100644 index 0000000..86450ae --- /dev/null +++ b/src/connection.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "point.hpp" + +struct Connection { + Point& p0; + Point& p1; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..f9e8c4c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,97 @@ +#include "app.hpp" +#include +#include +#include +#include +#include +#include + +const char* HELP_ARG = "--help"; +const char* HELP_ARG_LITTLE = "-h"; +const char* WINDOW_NAME_ARG = "--window_name"; +const char* WINDOW_NAME_ARG_LITTLE = "-wn"; +const char* FRAMERATE_ARG = "--framerate"; +const char* FRAMERATE_ARG_LITTLE = "-fr"; + +void print_help() { + printf("cloth_sim\n"); + + printf("\nARGS\n"); + printf("%s or %s --> print this message and exit\n", HELP_ARG, HELP_ARG_LITTLE); + printf("%s (string) or %s (string) --> set a custom window name\n", WINDOW_NAME_ARG, WINDOW_NAME_ARG_LITTLE); + printf("%s (unsigned integer) or %s (unsigned integer) --> set framerate\n", FRAMERATE_ARG, FRAMERATE_ARG_LITTLE); + + printf("\nKEYS\n"); + printf("ESC or q --> exit\n"); + printf("SPACE --> pause\n"); +} + +int main(const int argc, char** argv) { + std::string window_name = "cloth simulation"; + unsigned int FRAMERATE = 60; + const unsigned int FRAME_TIME = 1000.0f / FRAMERATE; + + unsigned int i = 0; + while (i < (unsigned int) argc) { + if (strcmp(argv[i], HELP_ARG) == 0 || strcmp(argv[i], HELP_ARG_LITTLE) == 0) { + print_help(); + return 0; + } + + if (strcmp(argv[i], WINDOW_NAME_ARG) == 0 || strcmp(argv[i], WINDOW_NAME_ARG_LITTLE) == 0) { + if (i == (unsigned int) (argc-1)) { + break; + } + + i++; + window_name = std::string(argv[i]); + } + + if (strcmp(argv[i], FRAMERATE_ARG) == 0 || strcmp(argv[i], FRAMERATE_ARG_LITTLE) == 0) { + if (i == (unsigned int) (argc-1)) { + break; + } + + i++; + FRAMERATE = std::stoi(argv[i]); + if (FRAMERATE == 0) { + FRAMERATE = 60; + } + } + + i++; + } + + App_config config = { + .win_name = window_name.c_str(), + .window_dimensions = Vec2{500, 500}, + .cloth_startpos = Vec2{50, 50}, + .cloth_dimensions = Vec2{400, 400}, + .cloth_spacing = 20, + .gravity = Vec2f{0.3f, 0.8f}, + .background_color = RGB{80, 100, 255}, + .point_color = RGB{10, 10, 10}, + .point_selected_color = RGB{200, 40, 12}, + .connection_color = RGB{100, 100, 100}, + }; + App* app = app_init(config); + + app->is_running = true; + unsigned int frame_start_time, total_frame_time; + while (app->is_running) { + frame_start_time = SDL_GetTicks(); + + app_handle_input(app); + app_update(app); + app_render(app); + + total_frame_time = SDL_GetTicks() - frame_start_time; + if (total_frame_time < FRAME_TIME) { + SDL_Delay(FRAME_TIME - total_frame_time); + } + } + + destroy_app(app); + + return 0; +} \ No newline at end of file diff --git a/src/point.hpp b/src/point.hpp new file mode 100644 index 0000000..3e1a15c --- /dev/null +++ b/src/point.hpp @@ -0,0 +1,19 @@ +#pragma once + +// struct Point { +// unsigned int x; +// unsigned int y; +// unsigned int prev_x; +// unsigned int prev_y; +// unsigned int mass; +// bool selected; +// }; + +struct Point { + float x; + float y; + unsigned int prev_x; + unsigned int prev_y; + unsigned int mass; + bool selected; +}; \ No newline at end of file diff --git a/src/vec.hpp b/src/vec.hpp new file mode 100644 index 0000000..f8c422c --- /dev/null +++ b/src/vec.hpp @@ -0,0 +1,11 @@ +#pragma once + +struct Vec2 { + unsigned int x; + unsigned int y; +}; + +struct Vec2f { + float x; + float y; +}; \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp new file mode 100644 index 0000000..564ff7e --- /dev/null +++ b/src/window.cpp @@ -0,0 +1,90 @@ +#include "window.hpp" +#include +#include +#include +#include +#include +#include +#include + +Window* new_window(const char* window_name, Vec2 dimensions) { + Window* new_win = new Window; + new_win->dimensions = dimensions; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + printf("[ERROR] Window initialization error: %s\n", SDL_GetError()); + return NULL; + } + + new_win->sdl_win = SDL_CreateWindow( + window_name, + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + dimensions.x, dimensions.y, + SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE); + + if (new_win->sdl_win == NULL) { + printf("[ERROR] Could not create SDL window: %s\n", SDL_GetError()); + return NULL; + } + + new_win->renderer = SDL_CreateRenderer(new_win->sdl_win, -1, SDL_RENDERER_PRESENTVSYNC); + + return new_win; +} + +void destroy_window(Window* window) { + if (window == NULL) { + return; + } + + if (window->sdl_win != NULL) { + SDL_DestroyWindow(window->sdl_win); + window->sdl_win = NULL; + SDL_Quit(); + } + + if (window->renderer != NULL) { + SDL_DestroyRenderer(window->renderer); + window->renderer = NULL; + } + + delete window; + window = NULL; +} + +void fill_screen(Window* window, RGB color) { + if (window->renderer != NULL) { + SDL_SetRenderDrawColor(window->renderer, color.r, color.g, color.b, 255); + SDL_RenderClear(window->renderer); + } +} + +void draw_point(Window* window, Point point, RGB color) { + if (window->renderer != NULL) { + SDL_SetRenderDrawColor(window->renderer, color.r, color.g, color.b, 255); + SDL_RenderDrawPoint(window->renderer, (int) point.x, (int) point.y); + } +} + +void draw_connection(Window* window, Connection connection, RGB color) { + if (window->renderer != NULL) { + SDL_SetRenderDrawColor(window->renderer, color.r, color.g, color.b, 255); + SDL_RenderDrawLine( + window->renderer, + (int) connection.p0.x, + (int) connection.p0.y, + (int) connection.p1.x, + (int) connection.p1.y + ); + } +} + +void draw_cloth(Window* window, Cloth* cloth, RGB point_color, RGB connection_color, RGB selected_p_color) { + for (const Connection* conn : cloth->connections) { + draw_connection(window, *conn, connection_color); + } + + for (const Point* point : cloth->points) { + draw_point(window, *point, point_color); + } +} \ No newline at end of file diff --git a/src/window.hpp b/src/window.hpp new file mode 100644 index 0000000..a4285f9 --- /dev/null +++ b/src/window.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include "cloth.hpp" +#include "color.hpp" + +struct Window { + Vec2 dimensions; + SDL_Window* sdl_win; + SDL_Renderer* renderer; +}; + +Window* new_window(const char* window_name, Vec2 dimensions); +void destroy_window(Window* window); +void render_cloth(Window* window, Cloth* cloth); +void fill_screen(Window* window, RGB color); +void draw_point(Window* window, Point point); +void draw_connection(Window* window, Connection connection); +void draw_cloth(Window* window, Cloth* cloth, RGB point_color, RGB connection_color, RGB selected_p_color); \ No newline at end of file