diff --git a/src/boid-playground.hpp b/src/boid-playground.hpp index e73dbab..6be2d03 100644 --- a/src/boid-playground.hpp +++ b/src/boid-playground.hpp @@ -64,6 +64,8 @@ struct Visuals { Color boid_color = BLACK; Color bg_color = RAYWHITE; + Camera2D camera; + bool show_control_panel = true; bool draw_boid_direction = false; diff --git a/src/main.cpp b/src/main.cpp index f53817f..aa924df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,8 @@ static World g_world; static Visuals g_visuals; static UI g_ui; +// TODO: Add controls window + // TODO: Make boids form specific shapes defined by user, options: // Circles, triangles, by image, text. @@ -41,7 +43,7 @@ int main() { int screen_height = 720; raylib::Window window(screen_width, screen_height, "Boid Playground"); - window.SetState(FLAG_VSYNC_HINT); + window.SetState(FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE); GuiLoadStyleDefault(); @@ -56,6 +58,7 @@ int main() { g_world.boids.push_back(boid); } g_ui.target_boid_count = g_world.boids.size(); + visuals_init(&g_world, &g_visuals); #ifdef __EMSCRIPTEN__ emscripten_set_main_loop(UpdateDrawFrame, 0, 1); @@ -111,6 +114,9 @@ static void profiling_test() { void UpdateDrawFrame() { float dt = GetFrameTime(); + g_world.size.x = GetScreenWidth(); + g_world.size.y = GetScreenHeight(); + RPROF_START("Update"); #ifdef PLATFORM_WEB // If user goes to another tab and comes back, the time that the user was gone needs to be ignored. @@ -121,6 +127,7 @@ void UpdateDrawFrame() { #else world_update(&g_world, dt); #endif + visuals_update(&g_world, &g_visuals); RPROF_STOP(); // Draw diff --git a/src/world.cpp b/src/world.cpp index a9add17..62cd40e 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -117,6 +117,11 @@ static void world_init(World *world, float width, float height) { world->size = { width, height }; } +static void visuals_init(World *world, Visuals *visuals) { + visuals->camera = { 0 }; + visuals->camera.zoom = 1; +} + static void world_free(World *world) { arena_free(&world->frame_arena); } @@ -305,7 +310,7 @@ static void world_compute_local_boids(BoidList *local_boids, World *world, Chunk int boid_count = world->boids.size(); MemoryArena *arena = &world->frame_arena; - boid_pair b2b_cmps[B2B_CAPACITY + SIMD_32B_LANES]; + boid_pair *b2b_cmps = (boid_pair*)arena_malloc(arena, (B2B_CAPACITY + SIMD_32B_LANES)*sizeof(boid_pair)); int b2b_cmps_count = 0; RPROF_START("Move chunk data to static arrays"); @@ -415,6 +420,27 @@ static BoidList* world_compute_local_boids(World *world) { return all_local_boids; } +static void aggregate_flock_modifiers(World *world, BoidList *flock, Boid *boid, Vector2 *center, Vector2 *heading, Vector2 *separation_force) { + *center = { 0, 0 }; + *heading = { 0, 0 }; + *separation_force = { 0, 0 }; + + uboid_t boid_id; + BoidsListNodeIterator it = boid_list_get_iterator(flock); + while (boid_list_iterator_next(&it, &boid_id)) { + Boid *flock_boid = &world->boids[boid_id]; + *heading = Vector2Add(*heading, flock_boid->dir); + *center = Vector2Add(*center , flock_boid->pos); + + Vector2 pos_diff = Vector2Subtract(boid->pos, flock_boid->pos); + float dist_sqr = Vector2LengthSqr(pos_diff); + if (dist_sqr <= world->separation_radius * world->separation_radius) { + *separation_force = Vector2Add(*separation_force, vector2_div_value(pos_diff, dist_sqr)); + } + } + *center = vector2_div_value(*center, flock->count); +} + static void world_update(World *world, float dt) { if (world->freeze) return; @@ -422,7 +448,7 @@ static void world_update(World *world, float dt) { Boid *boids = world->boids.data(); int boid_count = world->boids.size(); - assert(boid_count <= MAX_BOIDS); + DEBUG_ASSERT(boid_count <= MAX_BOIDS); BoidList *list_of_local_boids = world_compute_local_boids(world); @@ -438,24 +464,8 @@ static void world_update(World *world, float dt) { BoidList *local_boids = &list_of_local_boids[i]; if (local_boids->count > 0) { - Vector2 separation_force = { 0, 0 }; - Vector2 flock_center = { 0, 0 }; - Vector2 flock_heading = { 0, 0 }; - - uboid_t local_boid_id; - BoidsListNodeIterator it = boid_list_get_iterator(local_boids); - while (boid_list_iterator_next(&it, &local_boid_id)) { - Boid *local_boid = &boids[local_boid_id]; - flock_heading = Vector2Add(flock_heading, local_boid->dir); - flock_center = Vector2Add(flock_center , local_boid->pos); - - Vector2 pos_diff = Vector2Subtract(boid->pos, local_boid->pos); - float dist_sqr = Vector2LengthSqr(pos_diff); - if (dist_sqr <= world->separation_radius * world->separation_radius) { - separation_force = Vector2Add(separation_force, vector2_div_value(pos_diff, dist_sqr)); - } - } - flock_center = vector2_div_value(flock_center, local_boids->count); + Vector2 separation_force, flock_center, flock_heading; + aggregate_flock_modifiers(world, local_boids, boid, &flock_center, &flock_heading, &separation_force); Vector2 alignment_force = Vector2Normalize(flock_heading); acc = Vector2Add(acc, vector2_mul_value(alignment_force, world->alignment_strength)); @@ -633,11 +643,55 @@ static void draw_boids(World *world, Visuals *visuals) { rlEnd(); } +static void visuals_update(World *world, Visuals *visuals) { + Camera2D *camera = &visuals->camera; + + if (IsKeyPressed(KEY_R)) { + *camera = { 0 }; + camera->zoom = 1; + } + + if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE)) { + Vector2 delta = GetMouseDelta(); + delta = Vector2Scale(delta, -1.0f/camera->zoom); + + camera->target = Vector2Add(camera->target, delta); + } + + float wheel = GetMouseWheelMove(); + if (wheel != 0) { + // Get the world point that is under the mouse + Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), *camera); + + // Set the offset to where the mouse is + camera->offset = GetMousePosition(); + + // Set the target to match, so that the camera maps the world space point + // under the cursor to the screen space point under the cursor at any zoom + camera->target = mouseWorldPos; + + // Zoom increment + const float zoom_speed = 0.125f; + + camera->zoom = camera->zoom*(1+zoom_speed*wheel); + if (camera->zoom < zoom_speed) camera->zoom = zoom_speed; + } +} + static void world_draw(World *world, Visuals *visuals) { + BeginMode2D(visuals->camera); for (int i = 0; i < world->obstacles.size(); i++) { draw_obstacle(&world->obstacles[i], GRAY); } + Color border_color = RED; + float world_width = world->size.x; + float world_height = world->size.y; + DrawLine(0, -1, world_width, -1, RED); + DrawLine(-1, 0, -1, world_height, RED); + DrawLine(0, world_height+1, world_width, world_height+1, RED); + DrawLine(world_width+1, 0, world_width+1, world_height, RED); + if (visuals->draw_view_cone) { Color view_cone_color = Fade(GRAY, 0.4); for (int i = 0; i < world->boids.size(); i++) { @@ -673,4 +727,5 @@ static void world_draw(World *world, Visuals *visuals) { DrawLine(boid->pos.x, boid->pos.y, look_pos.x, look_pos.y, RED); } } + EndMode2D(); }