generated from rpuzonas/raylib-cpp-template
add gui for controlling parameters
This commit is contained in:
parent
1478084ab6
commit
892247d44b
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||
|
3
Makefile
3
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
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
-Idepends/raylib-cpp/include/
|
||||
-Idepends/raylib/src/
|
||||
-Idepends/raygui/src/
|
||||
|
1
depends/raygui
Submodule
1
depends/raygui
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 848569ce3399a8f6fe00091f968808f3ab042c4b
|
@ -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;
|
||||
};
|
||||
|
150
src/main.cpp
150
src/main.cpp
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user