From 6e2c6c0fc5d76df5e3ff1abc4de20cdd790497ca Mon Sep 17 00:00:00 2001 From: Caleb Burke Date: Sun, 13 Sep 2026 00:52:08 -0700 Subject: [PATCH] 2d boids example done --- examples/boids_2d/main.cpp | 2 +- testbed/main.cpp | 193 +------------------------------------ 2 files changed, 4 insertions(+), 191 deletions(-) diff --git a/examples/boids_2d/main.cpp b/examples/boids_2d/main.cpp index 3d89b12..1eaba8b 100644 --- a/examples/boids_2d/main.cpp +++ b/examples/boids_2d/main.cpp @@ -91,7 +91,7 @@ class Boids2D : public sophia::Application { {{-BOID_LENGTH * 0.5f, BOID_WIDTH * 0.5f}, {1.0f, 1.0f, 1.0f}}, {{-BOID_LENGTH * 0.5f, -BOID_WIDTH * 0.5f}, {1.0f, 1.0f, 1.0f}}, }; - boidMesh = createMesh(std::move(vertices), {}); + boidMesh = assets().loadMesh(std::move(vertices), {}); } void onUpdate(const sophia::FrameInfo& frameInfo) override { diff --git a/testbed/main.cpp b/testbed/main.cpp index 2f73775..c6047ea 100644 --- a/testbed/main.cpp +++ b/testbed/main.cpp @@ -1,200 +1,13 @@ -#include #include -#include #include -#include -#include -#include -#include -#include - -#define GLM_FORCE_RADIANS -#define GLM_FORCE_DEPTH_ZERO_TO_ONE -#include -#include - -struct Boid { - glm::vec2 position; - glm::vec2 velocity; -}; - -constexpr uint32_t DEFAULT_NUMBER_OF_BOIDS = 25; class Testbed : public sophia::Application { public: - static constexpr float SCROLL_SPEED = 200.0f; + Testbed() : Application{{.width = 750, .height = 1000, .name = "Testbed"}} {} - // World is a fixed box independent of the camera; boids wrap at its edges. - static constexpr float WORLD_HALF_WIDTH = 375.0f; - static constexpr float WORLD_HALF_HEIGHT = 500.0f; + void onCreate() override { std::cout << "Testbed created" << std::endl; } - static constexpr float MAX_SPEED = 120.0f; - static constexpr float MAX_FORCE = 200.0f; - static constexpr float PERCEPTION_RADIUS = 150.0f; - static constexpr float SEPARATION_RADIUS = 40.0f; - static constexpr float ALIGNMENT_WEIGHT = 1.0f; - static constexpr float COHESION_WEIGHT = 1.0f; - static constexpr float SEPARATION_WEIGHT = 1.5f; - - Testbed() : Application{{.width = 750, .height = 1000, .name = "Testbed"}} { - uint32_t numberOfBoids = DEFAULT_NUMBER_OF_BOIDS; - if (args.size() > 1) { - numberOfBoids = static_cast(std::stoul(args[1])); - } - boids.resize(numberOfBoids); - - sophia::EventSystem::get().addListener( - [this](sophia::KeyPressedEvent& e) { - setScrollKeyHeld(e.getKeyCode(), true); - }); - sophia::EventSystem::get().addListener( - [this](sophia::KeyReleasedEvent& e) { - setScrollKeyHeld(e.getKeyCode(), false); - }); - } - - void onCreate() override { - std::cout << "Testbed created" << std::endl; - - std::mt19937 rng{std::random_device{}()}; - std::uniform_real_distribution posX{-WORLD_HALF_WIDTH, - WORLD_HALF_WIDTH}; - std::uniform_real_distribution posY{-WORLD_HALF_HEIGHT, - WORLD_HALF_HEIGHT}; - std::uniform_real_distribution angle{0.0f, glm::two_pi()}; - - for (Boid& boid : boids) { - boid.position = {posX(rng), posY(rng)}; - float theta = angle(rng); - boid.velocity = glm::vec2{std::cos(theta), std::sin(theta)} * MAX_SPEED; - } - } - - void onUpdate(const sophia::FrameInfo& frameInfo) override { - float scrollDistance = - SCROLL_SPEED * static_cast(frameInfo.deltaTime); - if (scrollLeft) camera.position.x -= scrollDistance; - if (scrollRight) camera.position.x += scrollDistance; - if (scrollUp) camera.position.y -= scrollDistance; - if (scrollDown) camera.position.y += scrollDistance; - - updateBoids(static_cast(frameInfo.deltaTime)); - - canvas.clear(); - for (const Boid& boid : boids) { - drawBoid(boid); - } - } - - private: - void updateBoids(float deltaTime) { - std::vector accelerations(boids.size()); - - for (size_t i = 0; i < boids.size(); ++i) { - glm::vec2 separation{0.0f}; - glm::vec2 averageVelocity{0.0f}; - glm::vec2 averagePosition{0.0f}; - int neighborCount = 0; - - for (size_t j = 0; j < boids.size(); ++j) { - if (i == j) continue; - - glm::vec2 offset = boids[i].position - boids[j].position; - float dist = glm::length(offset); - if (dist >= PERCEPTION_RADIUS) continue; - - averageVelocity += boids[j].velocity; - averagePosition += boids[j].position; - ++neighborCount; - - if (dist > 0.0f && dist < SEPARATION_RADIUS) { - separation += offset / (dist * dist); - } - } - - glm::vec2 acceleration = - steerTowards(boids[i], separation) * SEPARATION_WEIGHT; - - if (neighborCount > 0) { - averageVelocity /= static_cast(neighborCount); - acceleration += - steerTowards(boids[i], averageVelocity) * ALIGNMENT_WEIGHT; - - averagePosition /= static_cast(neighborCount); - glm::vec2 towardsGroup = averagePosition - boids[i].position; - acceleration += steerTowards(boids[i], towardsGroup) * COHESION_WEIGHT; - } - - accelerations[i] = acceleration; - } - - for (size_t i = 0; i < boids.size(); ++i) { - Boid& boid = boids[i]; - boid.velocity += accelerations[i] * deltaTime; - - float speed = glm::length(boid.velocity); - if (speed > MAX_SPEED) { - boid.velocity = boid.velocity / speed * MAX_SPEED; - } - - boid.position += boid.velocity * deltaTime; - wrapPosition(boid.position); - } - } - - // Steers boid towards desiredDirection at up to MAX_SPEED, clamped to - // MAX_FORCE. - glm::vec2 steerTowards(const Boid& boid, glm::vec2 desiredDirection) { - float directionLength = glm::length(desiredDirection); - if (directionLength < 1e-5f) return glm::vec2{0.0f}; - - glm::vec2 desired = desiredDirection / directionLength * MAX_SPEED; - glm::vec2 steer = desired - boid.velocity; - - float steerLength = glm::length(steer); - if (steerLength > MAX_FORCE) { - steer = steer / steerLength * MAX_FORCE; - } - return steer; - } - - void wrapPosition(glm::vec2& position) { - if (position.x > WORLD_HALF_WIDTH) position.x = -WORLD_HALF_WIDTH; - if (position.x < -WORLD_HALF_WIDTH) position.x = WORLD_HALF_WIDTH; - if (position.y > WORLD_HALF_HEIGHT) position.y = -WORLD_HALF_HEIGHT; - if (position.y < -WORLD_HALF_HEIGHT) position.y = WORLD_HALF_HEIGHT; - } - - void drawBoid(const Boid& boid) { - constexpr float LENGTH = 20.0f; - constexpr float WIDTH = 12.0f; - - glm::vec2 direction = boid.velocity; - float speed = glm::length(direction); - direction = speed > 1e-5f ? direction / speed : glm::vec2{0.0f, -1.0f}; - glm::vec2 perpendicular{-direction.y, direction.x}; - - glm::vec2 nose = boid.position + direction * (LENGTH * 0.5f); - glm::vec2 tailLeft = boid.position - direction * (LENGTH * 0.5f) + - perpendicular * (WIDTH * 0.5f); - glm::vec2 tailRight = boid.position - direction * (LENGTH * 0.5f) - - perpendicular * (WIDTH * 0.5f); - - canvas.triangle(nose, tailLeft, tailRight, {1.0f, 1.0f, 1.0f}); - } - - void setScrollKeyHeld(sophia::KeyCode keycode, bool held) { - if (keycode == sophia::Key::Left) scrollLeft = held; - if (keycode == sophia::Key::Right) scrollRight = held; - if (keycode == sophia::Key::Up) scrollUp = held; - if (keycode == sophia::Key::Down) scrollDown = held; - } - - std::vector boids; - bool scrollLeft = false; - bool scrollRight = false; - bool scrollUp = false; - bool scrollDown = false; + void onUpdate(const sophia::FrameInfo& frameInfo) override {} }; SOPHIA_MAIN(Testbed)