diff --git a/include/camera.hpp b/include/camera.hpp index 871aba9..8ce15b4 100644 --- a/include/camera.hpp +++ b/include/camera.hpp @@ -24,7 +24,8 @@ public: auto Pan(Vector2 last_mouse, Vector2 mouse) -> void; - auto Update(const Vector3 ¤t_target, bool lock) -> void; + auto Update(const Vector3 ¤t_target, const Vector3 &mass_center, + bool lock, bool mass_center_lock) -> void; }; #endif diff --git a/include/config.hpp b/include/config.hpp index 0b6bceb..1970fff 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -23,12 +23,14 @@ constexpr const char *FONT = "fonts/SpaceMono.ttf"; constexpr int FONT_SIZE = 26; // Camera Controls -constexpr float CAMERA_FOV = 120.0; +constexpr float CAMERA_FOV = 90.0; +constexpr float FOV_SPEED = 1.0; +constexpr float MIN_FOV = 10.0; +constexpr float MAX_FOV = 180.0; constexpr float CAMERA_DISTANCE = 20.0; +constexpr float ZOOM_SPEED = 2.5; constexpr float MIN_CAMERA_DISTANCE = 2.0; constexpr float MAX_CAMERA_DISTANCE = 2000.0; -constexpr float ZOOM_SPEED = 2.5; -constexpr float FOV_SPEED = 1.0; constexpr float ZOOM_MULTIPLIER = 4.0; constexpr float PAN_SPEED = 2.0; constexpr float PAN_MULTIPLIER = 10.0; @@ -42,16 +44,21 @@ constexpr float SIM_SPEED = 4.0; // How large each update should be constexpr float MASS = 1.0; // Mass spring system constexpr float SPRING_CONSTANT = 5.0; // Mass spring system constexpr float DAMPENING_CONSTANT = 1.0; // Mass spring system -constexpr float REST_LENGTH = 2.0; // Mass spring system +constexpr float REST_LENGTH = 3.0; // Mass spring system constexpr float VERLET_DAMPENING = 0.05; // [0, 1] constexpr float BH_FORCE = 2.0; // Barnes-Hut [1.0, 3.0] constexpr float THETA = 0.9; // Barnes-Hut [0.5, 1.0] constexpr float SOFTENING = 0.01; // Barnes-Hut [0.01, 1.0] // Graph Drawing +constexpr Color EDGE_COLOR = DARKBLUE; constexpr float VERTEX_SIZE = 0.5; -constexpr Color VERTEX_COLOR = GREEN; -constexpr Color EDGE_COLOR = DARKGREEN; +static const Color VERTEX_COLOR = Fade(BLUE, 0.5); +constexpr Color VERTEX_VISITED_COLOR = DARKBLUE; +constexpr Color VERTEX_PATH_COLOR = GREEN; +constexpr Color VERTEX_TARGET_COLOR = RED; +constexpr Color VERTEX_START_COLOR = ORANGE; +constexpr Color VERTEX_CURRENT_COLOR = PURPLE; constexpr int DRAW_VERTICES_LIMIT = 1000000; // Klotski Drawing @@ -62,6 +69,5 @@ constexpr Color BOARD_COLOR_FREE = RAYWHITE; constexpr Color BLOCK_COLOR = DARKBLUE; constexpr Color TARGET_BLOCK_COLOR = RED; constexpr Color WALL_COLOR = BLACK; -constexpr Color GOAL_COLOR = ORANGE; #endif diff --git a/include/input.hpp b/include/input.hpp index 0ec7de5..9774b86 100644 --- a/include/input.hpp +++ b/include/input.hpp @@ -54,6 +54,7 @@ public: // Camera bool camera_lock = true; + bool camera_mass_center_lock = false; bool camera_panning = false; bool camera_rotating = false; @@ -102,6 +103,7 @@ public: // Key actions auto ToggleCameraLock() -> void; + auto ToggleCameraMassCenterLock() -> void; auto ToggleCameraProjection() -> void; auto MoveBlockNor() -> void; auto MoveBlockWes() -> void; diff --git a/include/physics.hpp b/include/physics.hpp index a948e20..45466be 100644 --- a/include/physics.hpp +++ b/include/physics.hpp @@ -68,13 +68,13 @@ public: class MassSpringSystem { private: - Octree octree; - #ifdef THREADPOOL BS::thread_pool threads; #endif public: + Octree octree; + // This is the main ownership of all the states/masses/springs. std::vector masses; std::vector springs; @@ -151,6 +151,7 @@ class ThreadedPhysics { #endif std::condition_variable_any data_ready_cnd; std::condition_variable_any data_consumed_cnd; + Vector3 mass_center = Vector3Zero(); unsigned int ups = 0; std::vector masses; // Read by renderer bool data_ready = false; diff --git a/src/camera.cpp b/src/camera.cpp index 1cca5d6..632ea4f 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -41,12 +41,21 @@ auto OrbitCamera3D::Pan(Vector2 last_mouse, Vector2 mouse) -> void { target = Vector3Add(target, offset); } -auto OrbitCamera3D::Update(const Vector3 ¤t_target, bool lock) -> void { +auto OrbitCamera3D::Update(const Vector3 ¤t_target, + const Vector3 &mass_center, bool lock, + bool mass_center_lock) -> void { if (lock) { - target = Vector3MoveTowards( - target, current_target, - CAMERA_SMOOTH_SPEED * GetFrameTime() * - Vector3Length(Vector3Subtract(target, current_target))); + if (mass_center_lock) { + target = Vector3MoveTowards( + target, mass_center, + CAMERA_SMOOTH_SPEED * GetFrameTime() * + Vector3Length(Vector3Subtract(target, mass_center))); + } else { + target = Vector3MoveTowards( + target, current_target, + CAMERA_SMOOTH_SPEED * GetFrameTime() * + Vector3Length(Vector3Subtract(target, current_target))); + } } distance = Clamp(distance, MIN_CAMERA_DISTANCE, MAX_CAMERA_DISTANCE); @@ -60,7 +69,7 @@ auto OrbitCamera3D::Update(const Vector3 ¤t_target, bool lock) -> void { float y = sin(angle_y) * actual_distance; float z = cos(angle_y) * cos(angle_x) * actual_distance; - fov = Clamp(fov, 25.0, 155.0); + fov = Clamp(fov, MIN_FOV, MAX_FOV); camera.position = Vector3Add(target, Vector3(x, y, z)); camera.target = target; diff --git a/src/gui.cpp b/src/gui.cpp index e7b7e76..273fa57 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -482,39 +482,47 @@ auto Gui::DrawGraphInfo(Color color) const -> void { } auto Gui::DrawGraphControls(Color color) const -> void { - if (DrawMenuButton(1, 2, 1, 1, "Populate Graph (G)", color)) { + if (DrawMenuButton(0, 2, 1, 1, "Populate Graph (G)", color)) { input.FillGraph(); } - int mark_path = input.mark_path; - DrawMenuToggleSlider(2, 2, 1, 1, "Path Hidden (U)", "Path Shown (U)", - &mark_path, color); - if (mark_path != input.mark_path) { - input.ToggleMarkPath(); - } + // int mark_path = input.mark_path; + // DrawMenuToggleSlider(2, 2, 1, 1, "Path Hidden (U)", "Path Shown (U)", + // &mark_path, color); + // if (mark_path != input.mark_path) { + // input.ToggleMarkPath(); + // } - if (DrawMenuButton(1, 3, 1, 1, "Clear Graph (C)", color)) { + if (DrawMenuButton(1, 2, 1, 1, "Clear Graph (C)", color)) { input.ClearGraph(); } int mark_solutions = input.mark_solutions; - DrawMenuToggleSlider(2, 3, 1, 1, "Solutions Hidden (I)", - "Solutions Shown (I)", &mark_solutions, color); + DrawMenuToggleSlider(2, 2, 1, 1, "Solution Hidden (I)", "Solution Shown (I)", + &mark_solutions, color); if (mark_solutions != input.mark_solutions) { input.ToggleMarkSolutions(); } + input.mark_path = input.mark_solutions; } auto Gui::DrawCameraControls(Color color) const -> void { int lock_camera = input.camera_lock; - DrawMenuToggleSlider(0, 2, 1, 1, "Free Camera (L)", "Locked Camera (L)", + DrawMenuToggleSlider(0, 3, 1, 1, "Free Camera (L)", "Locked Camera (L)", &lock_camera, color); if (lock_camera != input.camera_lock) { input.ToggleCameraLock(); } + int lock_camera_mass_center = input.camera_mass_center_lock; + DrawMenuToggleSlider(1, 3, 1, 1, "Current Block (Y)", "Graph Center (Y)", + &lock_camera_mass_center, color, input.camera_lock); + if (lock_camera_mass_center != input.camera_mass_center_lock) { + input.ToggleCameraMassCenterLock(); + } + int projection = camera.projection == CAMERA_ORTHOGRAPHIC; - DrawMenuToggleSlider(0, 3, 1, 1, "Perspective (Alt)", "Orthographic (Alt)", + DrawMenuToggleSlider(2, 3, 1, 1, "Perspective (Alt)", "Orthographic (Alt)", &projection, color); if (projection != (camera.projection == CAMERA_ORTHOGRAPHIC)) { input.ToggleCameraProjection(); diff --git a/src/input.cpp b/src/input.cpp index 65eee5f..da5abe1 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -43,7 +43,7 @@ auto InputHandler::InitHandlers() -> void { RegisterKeyPressedHandler(KEY_C, &InputHandler::ClearGraph); RegisterKeyPressedHandler(KEY_I, &InputHandler::ToggleMarkSolutions); RegisterKeyPressedHandler(KEY_O, &InputHandler::ToggleConnectSolutions); - RegisterKeyPressedHandler(KEY_U, &InputHandler::ToggleMarkPath); + // RegisterKeyPressedHandler(KEY_U, &InputHandler::ToggleMarkPath); RegisterKeyPressedHandler(KEY_SPACE, &InputHandler::MakeOptimalMove); RegisterKeyPressedHandler(KEY_V, &InputHandler::GoToWorstState); RegisterKeyPressedHandler(KEY_B, &InputHandler::GoToNearestTarget); @@ -60,6 +60,7 @@ auto InputHandler::InitHandlers() -> void { RegisterKeyPressedHandler(KEY_LEFT_ALT, &InputHandler::ToggleCameraProjection); RegisterKeyPressedHandler(KEY_X, &InputHandler::ClearGoal); + RegisterKeyPressedHandler(KEY_Y, &InputHandler::ToggleCameraMassCenterLock); } auto InputHandler::MouseInMenuPane() -> bool { return mouse.y < MENU_HEIGHT; } @@ -145,6 +146,15 @@ auto InputHandler::ToggleCameraLock() -> void { camera_lock = !camera_lock; } +auto InputHandler::ToggleCameraMassCenterLock() -> void { + if (!camera_mass_center_lock) { + camera_lock = true; + camera_panning = false; + } + + camera_mass_center_lock = !camera_mass_center_lock; +} + auto InputHandler::ToggleCameraProjection() -> void { camera.projection = camera.projection == CAMERA_PERSPECTIVE ? CAMERA_ORTHOGRAPHIC diff --git a/src/main.cpp b/src/main.cpp index 80b73f7..794a9e5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,8 @@ #endif // TODO: Click states in the graph to display them in the board +// TODO: Move selection accordingly when undoing moves (need to diff two states +// and get the moved blocks) // TODO: Add some popups (my split between input.cpp/gui.cpp makes this ugly) // - Next move, goto target, goto worst: Notify that the graph needs to be @@ -83,8 +85,9 @@ auto main(int argc, char *argv[]) -> int { unsigned int loop_iterations = 0; unsigned int fps = 0; - unsigned int ups = 0; // Read from physics - std::vector masses; // Read from physics + unsigned int ups = 0; // Read from physics + Vector3 mass_center = Vector3Zero(); // Read from physics + std::vector masses; // Read from physics // Game loop while (!WindowShouldClose()) { @@ -114,6 +117,7 @@ auto main(int argc, char *argv[]) -> int { #endif ups = physics.state.ups; + mass_center = physics.state.mass_center; // Only copy data if any has been produced if (physics.state.data_ready) { @@ -135,7 +139,8 @@ auto main(int argc, char *argv[]) -> int { std::size_t current_index = state.CurrentMassIndex(); if (masses.size() > current_index) { const Mass ¤t_mass = masses.at(current_index); - camera.Update(current_mass.position, input.camera_lock); + camera.Update(current_mass.position, mass_center, input.camera_lock, + input.camera_mass_center_lock); } // Rendering diff --git a/src/physics.cpp b/src/physics.cpp index 046ba82..7cdc12d 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -276,6 +276,11 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state) loop_iterations = 0; ups_accumulator = std::chrono::duration(0); } + if (mass_springs.octree.nodes.size() > 0) { + state.mass_center = mass_springs.octree.nodes.at(0).mass_center; + } else { + state.mass_center = Vector3Zero(); + } state.masses.clear(); state.masses.reserve(mass_springs.masses.size()); diff --git a/src/renderer.cpp b/src/renderer.cpp index b8cebfe..1a9f33a 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -129,13 +129,13 @@ auto Renderer::DrawMassSprings(const std::vector &masses) -> void { const Vector3 &winning_mass = masses.at(winning_index); if (input.mark_solutions) { DrawCube(winning_mass, 2 * VERTEX_SIZE, 2 * VERTEX_SIZE, - 2 * VERTEX_SIZE, TARGET_BLOCK_COLOR); + 2 * VERTEX_SIZE, VERTEX_TARGET_COLOR); } std::size_t current_index = state.CurrentMassIndex(); if (input.connect_solutions && masses.size() > current_index) { const Vector3 ¤t_mass = masses.at(current_index); - DrawLine3D(winning_mass, current_mass, ORANGE); + DrawLine3D(winning_mass, current_mass, Fade(TARGET_BLOCK_COLOR, 0.5)); } } } @@ -148,7 +148,7 @@ auto Renderer::DrawMassSprings(const std::vector &masses) -> void { if (masses.size() > visited_index) { const Vector3 &visited_mass = masses.at(visited_index); DrawCube(visited_mass, VERTEX_SIZE * 1.5, VERTEX_SIZE * 1.5, - VERTEX_SIZE * 1.5, PURPLE); + VERTEX_SIZE * 1.5, VERTEX_VISITED_COLOR); } } @@ -158,7 +158,7 @@ auto Renderer::DrawMassSprings(const std::vector &masses) -> void { if (masses.size() > _state) { const Vector3 &path_mass = masses.at(_state); DrawCube(path_mass, VERTEX_SIZE * 1.75, VERTEX_SIZE * 1.75, - VERTEX_SIZE * 1.75, YELLOW); + VERTEX_SIZE * 1.75, VERTEX_PATH_COLOR); } } } @@ -168,7 +168,7 @@ auto Renderer::DrawMassSprings(const std::vector &masses) -> void { if (masses.size() > starting_index) { const Vector3 &starting_mass = masses.at(starting_index); DrawCube(starting_mass, VERTEX_SIZE * 2, VERTEX_SIZE * 2, VERTEX_SIZE * 2, - ORANGE); + VERTEX_START_COLOR); } // Mark current state @@ -176,7 +176,7 @@ auto Renderer::DrawMassSprings(const std::vector &masses) -> void { if (masses.size() > current_index) { const Vector3 ¤t_mass = masses.at(current_index); DrawCube(current_mass, VERTEX_SIZE * 2, VERTEX_SIZE * 2, VERTEX_SIZE * 2, - BLUE); + VERTEX_CURRENT_COLOR); } EndMode3D();