Gitea
3 years ago
commit
3ba95ff850
13 changed files with 465 additions and 0 deletions
@ -0,0 +1,5 @@
|
||||
all: |
||||
g++ -lSDL2 -O2 -Wall -Werror src/*.cpp -o cloth_sim
|
||||
|
||||
clean: |
||||
rm -f cloth_sim
|
@ -0,0 +1,95 @@
|
||||
#include "app.hpp" |
||||
#include "cloth.hpp" |
||||
#include "window.hpp" |
||||
#include <SDL2/SDL_events.h> |
||||
#include <SDL2/SDL_keycode.h> |
||||
#include <SDL2/SDL_render.h> |
||||
#include <SDL2/SDL_timer.h> |
||||
#include <SDL2/SDL_video.h> |
||||
|
||||
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; |
||||
} |
||||
} |
@ -0,0 +1,34 @@
|
||||
#pragma once |
||||
|
||||
#include "window.hpp" |
||||
#include "cloth.hpp" |
||||
#include <SDL2/SDL_render.h> |
||||
|
||||
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); |
@ -0,0 +1,61 @@
|
||||
#include "cloth.hpp" |
||||
#include "connection.hpp" |
||||
#include <cstddef> |
||||
#include <cstdio> |
||||
#include <vector> |
||||
|
||||
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<Point*>(); |
||||
new_cloth->connections = std::vector<Connection*>(); |
||||
|
||||
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<float>(startpos.x + x), static_cast<float>(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;
|
||||
} |
||||
} |
@ -0,0 +1,17 @@
|
||||
#pragma once |
||||
|
||||
#include "point.hpp" |
||||
#include "connection.hpp" |
||||
#include "vec.hpp" |
||||
#include <vector> |
||||
|
||||
struct Cloth { |
||||
Vec2 startpos; |
||||
Vec2 dimensions; |
||||
std::vector<Point*> points; |
||||
std::vector<Connection*> 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); |
@ -0,0 +1,7 @@
|
||||
#pragma once |
||||
|
||||
struct RGB { |
||||
unsigned char r; |
||||
unsigned char g; |
||||
unsigned char b; |
||||
}; |
@ -0,0 +1,8 @@
|
||||
#pragma once |
||||
|
||||
#include "point.hpp" |
||||
|
||||
struct Connection { |
||||
Point& p0; |
||||
Point& p1; |
||||
}; |
@ -0,0 +1,97 @@
|
||||
#include "app.hpp" |
||||
#include <SDL2/SDL_error.h> |
||||
#include <cstdlib> |
||||
#include <ostream> |
||||
#include <string> |
||||
#include <thread> |
||||
#include <SDL2/SDL_timer.h> |
||||
|
||||
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; |
||||
} |
@ -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; |
||||
}; |
@ -0,0 +1,11 @@
|
||||
#pragma once |
||||
|
||||
struct Vec2 { |
||||
unsigned int x; |
||||
unsigned int y; |
||||
}; |
||||
|
||||
struct Vec2f { |
||||
float x; |
||||
float y; |
||||
}; |
@ -0,0 +1,90 @@
|
||||
#include "window.hpp" |
||||
#include <SDL2/SDL.h> |
||||
#include <SDL2/SDL_error.h> |
||||
#include <SDL2/SDL_rect.h> |
||||
#include <SDL2/SDL_render.h> |
||||
#include <SDL2/SDL_video.h> |
||||
#include <cstdio> |
||||
#include <stdexcept> |
||||
|
||||
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); |
||||
} |
||||
} |
@ -0,0 +1,20 @@
|
||||
#pragma once |
||||
|
||||
#include <SDL2/SDL.h> |
||||
#include <SDL2/SDL_render.h> |
||||
#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); |
Loading…
Reference in new issue