add gui for controlling parameters

This commit is contained in:
Rokas Puzonas 2023-07-20 02:23:23 +03:00
parent 1478084ab6
commit 892247d44b
6 changed files with 146 additions and 34 deletions

3
.gitmodules vendored
View File

@ -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

View File

@ -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

View File

@ -1,2 +1,3 @@
-Idepends/raylib-cpp/include/
-Idepends/raylib/src/
-Idepends/raygui/src/

1
depends/raygui Submodule

@ -0,0 +1 @@
Subproject commit 848569ce3399a8f6fe00091f968808f3ab042c4b

View File

@ -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;
};

View File

@ -8,6 +8,9 @@
#include <emscripten/emscripten.h>
#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;
}