From 892247d44ba83283331684068164538a122e1fab Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Thu, 20 Jul 2023 02:23:23 +0300 Subject: [PATCH] add gui for controlling parameters --- .gitmodules | 3 + Makefile | 3 +- compile_flags.txt | 1 + depends/raygui | 1 + src/boid-playground.hpp | 22 +++++- src/main.cpp | 150 +++++++++++++++++++++++++++++++--------- 6 files changed, 146 insertions(+), 34 deletions(-) create mode 160000 depends/raygui diff --git a/.gitmodules b/.gitmodules index db63b3b..de43ab3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "depends/emsdk"] path = depends/emsdk url = git@github.com:emscripten-core/emsdk.git +[submodule "depends/raygui"] + path = depends/raygui + url = git@github.com:raysan5/raygui.git diff --git a/Makefile b/Makefile index 8cd6d99..871b717 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ EXECUTABLE := boids-playground WEB_SHELL := src/shell.html SUBMODULES_PATH := depends -COMPILER_FLAGS := -std=c++17 +COMPILER_FLAGS := -std=c++17 -Wno-enum-compare LINKER_FLAGS := -lraylib # SOURCES := $(wildcard src/*.cpp) @@ -20,6 +20,7 @@ EXT := EMSDK_PATH := $(SUBMODULES_PATH)/emsdk RAYLIB_PLATFORM := PLATFORM_DESKTOP +COMPILER_FLAGS += -I$(SUBMODULES_PATH)/raygui/src/ COMPILER_FLAGS += -I$(SUBMODULES_PATH)/raylib/src COMPILER_FLAGS += -I$(SUBMODULES_PATH)/raylib-cpp/include diff --git a/compile_flags.txt b/compile_flags.txt index f634c0e..009888e 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -1,2 +1,3 @@ -Idepends/raylib-cpp/include/ -Idepends/raylib/src/ +-Idepends/raygui/src/ diff --git a/depends/raygui b/depends/raygui new file mode 160000 index 0000000..848569c --- /dev/null +++ b/depends/raygui @@ -0,0 +1 @@ +Subproject commit 848569ce3399a8f6fe00091f968808f3ab042c4b diff --git a/src/boid-playground.hpp b/src/boid-playground.hpp index 9297ac9..e55ea88 100644 --- a/src/boid-playground.hpp +++ b/src/boid-playground.hpp @@ -46,16 +46,34 @@ struct World { float collision_avoidance_distance = 75; float collision_avoidance_ray_angle = PI/1.5; - int collision_avoidance_ray_count = 4; + int collision_avoidance_ray_count = 3; // TODO: Function `get_boids_in_view_cone` doesn't work as expected with looping walls bool looping_walls = false; }; struct Visuals { - float boid_edge_size = 15; + float boid_edge_size = 40; + + Color boid_color = BLACK; + Color bg_color = RAYWHITE; + + bool show_control_panel = true; bool draw_boid_direction = false; bool draw_view_cone = false; bool draw_collision_avoidance_rays = false; + bool draw_separation_radius = false; + bool draw_pulling_forces = false; +}; + +struct UI { + bool min_speed_edit = false; + bool max_speed_edit = false; + bool steer_speed_edit = false; + + bool alignment_strength_edit = false; + bool cohesion_strength_edit = false; + bool separation_strength_edit = false; + bool collision_avoidance_strength_edit = false; }; diff --git a/src/main.cpp b/src/main.cpp index f28051a..2e2f976 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,9 @@ #include #endif +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" + #include "boid-playground.hpp" #include "raycast.cpp" @@ -16,6 +19,7 @@ static World g_world; static Visuals g_visuals; +static UI g_ui; static float vector2_atan2(Vector2 a) { return std::atan2(a.y, a.x); @@ -77,7 +81,7 @@ static void draw_obstacle(Obstacle *obstacle, Color color) { rlEnd(); } -static void draw_debug_boid_obstacle_avoidance(Visuals *visuals, World *world, Boid *boid) { +static void draw_obstacle_avoidance_rays(Visuals *visuals, World *world, Boid *boid) { Vector2 pos = boid->pos; int ray_count = world->collision_avoidance_ray_count * 2 + 1; @@ -214,26 +218,20 @@ static void world_update(World *world, float dt) { flock_center = vector2_div_value(flock_center, local_boids_count); Vector2 alignment_force = Vector2Normalize(flock_heading); - alignment_force = vector2_mul_value(alignment_force, world->max_speed); - alignment_force = vector2_mul_value(alignment_force, world->alignment_strength); - acc = Vector2Add(acc, alignment_force); + acc = Vector2Add(acc, vector2_mul_value(alignment_force, world->alignment_strength)); Vector2 cohesion_force = Vector2Normalize(Vector2Subtract(flock_center, boid->pos)); - cohesion_force = vector2_mul_value(cohesion_force, world->max_speed); - cohesion_force = vector2_mul_value(cohesion_force, world->cohesion_strength); - acc = Vector2Add(acc, cohesion_force); + acc = Vector2Add(acc, vector2_mul_value(cohesion_force, world->cohesion_strength)); separation_force = Vector2Normalize(separation_force); - separation_force = vector2_mul_value(separation_force, world->max_speed); - separation_force = vector2_mul_value(separation_force, world->separation_strength); - acc = Vector2Add(acc, separation_force); + acc = Vector2Add(acc, vector2_mul_value(separation_force, world->separation_strength)); } // Apply obstacle avoidance to accelaration Vector2 collision_avoidance = get_collision_avoidance_dir(world, boid); - collision_avoidance = vector2_mul_value(collision_avoidance, world->max_speed); - collision_avoidance = vector2_mul_value(collision_avoidance, world->collision_avoidance_strength); - acc = Vector2Add(acc, collision_avoidance); + acc = Vector2Add(acc, vector2_mul_value(collision_avoidance, world->collision_avoidance_strength)); + + acc = vector2_mul_value(acc, world->max_speed); // Clamp accelaration Vector2 clamped_acc = acc; @@ -300,7 +298,11 @@ static void world_draw(World *world, Visuals *visuals) { Boid *boid = &world->boids[i]; if (visuals->draw_collision_avoidance_rays) { - draw_debug_boid_obstacle_avoidance(visuals, world, boid); + draw_obstacle_avoidance_rays(visuals, world, boid); + } + + if (visuals->draw_separation_radius) { + DrawCircleLines(boid->pos.x, boid->pos.y, world->separation_radius, MAGENTA); } Vector2 triangle[] = { @@ -314,20 +316,112 @@ static void world_draw(World *world, Visuals *visuals) { triangle[i] = Vector2Add(boid->pos, Vector2Rotate(triangle[i], facing)); } - DrawTriangle(triangle[0], triangle[1], triangle[2], BLACK); + DrawTriangle(triangle[0], triangle[1], triangle[2], visuals->boid_color); if (visuals->draw_boid_direction) { DrawCircle(boid->pos.x, boid->pos.y, visuals->boid_edge_size * 0.05, RED); - Vector2 look_pos = Vector2Add(boid->pos, Vector2Multiply(boid->dir, { 30, 30 })); + Vector2 look_pos = Vector2Add(boid->pos, vector2_mul_value(boid->dir, visuals->boid_edge_size*1.5)); DrawLine(boid->pos.x, boid->pos.y, look_pos.x, look_pos.y, RED); } } } -void UpdateDrawFrame() { - // TODO: Show this on screen - // LogTrace("%d", count_out_of_bounds_boids(&world)); +static Rectangle rect_with_offset(Rectangle rect, Vector2 offset) +{ + return { rect.x + offset.x, rect.y + offset.y, rect.width, rect.height }; +} +static Rectangle rect_with_offset(Rectangle rect, float x, float y) +{ + return { rect.x + x, rect.y + y, rect.width, rect.height }; +} + +static int gui_valuebox_float(Rectangle rect, const char *text, float *value, float min_value, float max_value, bool edit_mode) { + int int_value = *value; + int result = GuiValueBox(rect, text, &int_value, min_value, max_value, edit_mode); + *value = int_value; + return result; +} + +static void gui_valuebox_float(Rectangle rect, const char *text, float *value, float min_value, float max_value, bool *edit_mode) { + if (gui_valuebox_float(rect, text, value, min_value, max_value, *edit_mode)) { + *edit_mode = !*edit_mode; + } +} + +struct VerticalLayout { + float x, y; + float gap; +}; + +static Rectangle next_in_layout(VerticalLayout *layout, float width, float height, float offset_x = 0) { + Rectangle rect = { layout->x + offset_x, layout->y, width, height }; + layout->y += height + layout->gap; + return rect; +} + +static void ui_draw(World *world, Visuals *visuals, UI *ui) { + if (!visuals->show_control_panel) { + visuals->show_control_panel = GuiButton({ 20, 20, 30, 30 }, GuiIconText(ICON_PENCIL_BIG, "")); + return; + } + + float panel_height = 310; + visuals->show_control_panel = !GuiWindowBox({ 20, 20, 660, panel_height }, "Control panel"); + + float group_height = panel_height - 45; + + GuiGroupBox({ 30, 55, 180, group_height }, "Visuals"); + { + VerticalLayout layout = { .x = 40, .y = 65, .gap = 8 }; + + GuiCheckBox(next_in_layout(&layout, 15, 15), "Show direction", &visuals->draw_boid_direction); + GuiCheckBox(next_in_layout(&layout, 15, 15), "Show view cone", &visuals->draw_view_cone); + GuiCheckBox(next_in_layout(&layout, 15, 15), "Show separation radius", &visuals->draw_separation_radius); + GuiCheckBox(next_in_layout(&layout, 15, 15), "Show collision rays", &visuals->draw_collision_avoidance_rays); + GuiCheckBox(next_in_layout(&layout, 15, 15), "Show pulling forces", &visuals->draw_pulling_forces); + GuiSlider(next_in_layout(&layout, 100, 15), NULL, "Boid size", &visuals->boid_edge_size, 5, 150); + + Rectangle boid_color_rect = next_in_layout(&layout, 50, 50); + GuiColorPicker(boid_color_rect, NULL, &visuals->boid_color); + GuiLabel(rect_with_offset({ 80, 10, 80, 16 }, boid_color_rect.x, boid_color_rect.y), "Boid color"); + + Rectangle bg_color_rect = next_in_layout(&layout, 50, 50); + GuiColorPicker(bg_color_rect, NULL, &visuals->bg_color); + GuiLabel(rect_with_offset({ 80, 10, 80, 16 }, bg_color_rect.x, bg_color_rect.y), "BG color"); + + // TODO: Add show FPS + // TODO: Add showing out of bounds boids + } + + GuiGroupBox({ 220, 55, 220, group_height }, "Boid"); + { + VerticalLayout layout = { .x = 230, .y = 65, .gap = 8 }; + + GuiSlider(next_in_layout(&layout, 100, 15), NULL, "Separation radius", &world->separation_radius, 10, 200); + GuiSlider(next_in_layout(&layout, 100, 15), NULL, "View radius", &world->view_radius, 10, 400); + GuiSlider(next_in_layout(&layout, 100, 15), NULL, "View angle", &world->view_angle, 0, 2*PI); + + gui_valuebox_float(next_in_layout(&layout, 50, 15, 60), "Min speed", &world->min_speed, 0, world->max_speed, &ui->min_speed_edit); + gui_valuebox_float(next_in_layout(&layout, 50, 15, 60), "Max speed", &world->max_speed, 0, 1000, &ui->max_speed_edit); + gui_valuebox_float(next_in_layout(&layout, 50, 15, 60), "Steer speed", &world->max_steer_speed, 0, 1000, &ui->steer_speed_edit); + + GuiSlider(next_in_layout(&layout, 100, 15), NULL, "Collision distance", &world->collision_avoidance_distance, 10, 200); + GuiSlider(next_in_layout(&layout, 100, 15), NULL, "Collision ray angle", &world->collision_avoidance_ray_angle, 0, PI); + GuiSpinner(next_in_layout(&layout, 100, 15, 95), "Collision ray count", &world->collision_avoidance_ray_count, 1, 10, false); + } + + GuiGroupBox({ 450, 55, 220, group_height }, "Flock"); + { + VerticalLayout layout = { .x = 605, .y = 65, .gap = 8 }; + gui_valuebox_float(next_in_layout(&layout, 50, 15), "Alignment strength", &world->alignment_strength, 0, 100, &ui->alignment_strength_edit); + gui_valuebox_float(next_in_layout(&layout, 50, 15), "Cohesion strength", &world->cohesion_strength, 0, 100, &ui->cohesion_strength_edit); + gui_valuebox_float(next_in_layout(&layout, 50, 15), "Separation strength", &world->separation_strength, 0, 100, &ui->separation_strength_edit); + gui_valuebox_float(next_in_layout(&layout, 50, 15), "Collision avoidance strength", &world->collision_avoidance_strength, 0, 100, &ui->collision_avoidance_strength_edit); + } +} + +void UpdateDrawFrame() { float dt = GetFrameTime(); #ifdef PLATFORM_WEB @@ -342,9 +436,10 @@ void UpdateDrawFrame() { // Draw BeginDrawing(); - ClearBackground(RAYWHITE); + ClearBackground(g_visuals.bg_color); world_draw(&g_world, &g_visuals); + ui_draw(&g_world, &g_visuals, &g_ui); EndDrawing(); } @@ -362,22 +457,13 @@ int main() { g_world.size = { (float)screen_width, (float)screen_height }; float border = g_visuals.boid_edge_size; - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 50; i++) { Boid boid; boid_rand_init(&g_world, &boid, border); g_world.boids.push_back(boid); } - // world.boids.push_back({ - // .pos = { 100, 100 }, - // .dir = { 1, 0 }, - // .speed = world.boid_min_speed - // }); - // world.boids.push_back({ - // .pos = { 100, 120 }, - // .dir = { 1, 0 }, - // .speed = world.boid_min_speed - // }); + GuiLoadStyleDefault(); #ifdef PLATFORM_WEB emscripten_set_main_loop(UpdateDrawFrame, 0, 1); @@ -388,5 +474,7 @@ int main() { } #endif + window.Close(); + return 0; }