diff --git a/CMakeLists.txt b/CMakeLists.txt index 0206cce..07f2deb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ include_directories(include) add_executable(masssprings src/main.cpp + src/camera.cpp src/renderer.cpp src/physics.cpp src/puzzle.cpp diff --git a/include/camera.hpp b/include/camera.hpp new file mode 100644 index 0000000..2c3666b --- /dev/null +++ b/include/camera.hpp @@ -0,0 +1,41 @@ +#ifndef __CAMERA_HPP_ +#define __CAMERA_HPP_ + +#include +#include + +#include "config.hpp" + +class OrbitCamera3D { + friend class Renderer; + +private: + Camera camera; + Vector3 target; + float distance; + float angle_x; + float angle_y; + Vector2 last_mouse; + bool dragging; + bool panning; + bool target_lock; + +public: + OrbitCamera3D() + : camera({0}), target(Vector3Zero()), distance(CAMERA_DISTANCE), + angle_x(0.0), angle_y(0.0), last_mouse(Vector2Zero()), dragging(false), + panning(false), target_lock(true) { + camera.position = Vector3(0, 0, -1.0 * distance); + camera.target = target; + camera.up = Vector3(0, 1.0, 0); + camera.fovy = CAMERA_FOV; + camera.projection = CAMERA_PERSPECTIVE; + } + + ~OrbitCamera3D() {} + +public: + auto Update(const Vector3 ¤t_target) -> void; +}; + +#endif diff --git a/include/physics.hpp b/include/physics.hpp index 4b90e5d..a3dd21b 100644 --- a/include/physics.hpp +++ b/include/physics.hpp @@ -86,7 +86,6 @@ public: class MassSpringSystem { private: - // TODO: Use references std::vector mass_vec; std::vector indices; std::vector cell_ids; diff --git a/include/puzzle.hpp b/include/puzzle.hpp index a98992a..ab912b2 100644 --- a/include/puzzle.hpp +++ b/include/puzzle.hpp @@ -282,6 +282,8 @@ public: std::vector>>; }; +// Provide hash functions so we can use State and as hash-set +// keys for masses and springs. template <> struct std::hash { std::size_t operator()(const State &s) const noexcept { return s.Hash(); } }; diff --git a/include/renderer.hpp b/include/renderer.hpp index fab6235..143af01 100644 --- a/include/renderer.hpp +++ b/include/renderer.hpp @@ -1,57 +1,24 @@ #ifndef __RENDERER_HPP_ #define __RENDERER_HPP_ -#include #include #include #include +#include "camera.hpp" #include "config.hpp" -#include "puzzle.hpp" #include "physics.hpp" - -class OrbitCamera3D { - friend class Renderer; - -private: - Camera camera; - Vector3 target; - float distance; - float angle_x; - float angle_y; - Vector2 last_mouse; - bool dragging; - bool panning; - bool target_lock; - -public: - OrbitCamera3D(Vector3 target, float distance) - : camera({0}), target(target), distance(distance), angle_x(0.0), - angle_y(0.0), last_mouse(Vector2Zero()), dragging(false), - panning(false), target_lock(true) { - camera.position = Vector3(0, 0, -1.0 * distance); - camera.target = target; - camera.up = Vector3(0, 1.0, 0); - camera.fovy = CAMERA_FOV; - camera.projection = CAMERA_PERSPECTIVE; - } - - ~OrbitCamera3D() {} - -public: - auto Update(const Mass ¤t_mass) -> void; -}; +#include "puzzle.hpp" class Renderer { private: - OrbitCamera3D camera; + const OrbitCamera3D &camera; RenderTexture render_target; RenderTexture klotski_target; RenderTexture menu_target; - Material vertex_mat; - // Instancing + Material vertex_mat; int transforms_size; Matrix *transforms; Mesh cube_instance; @@ -62,18 +29,14 @@ public: bool connect_solutions; public: - Renderer() - : camera(OrbitCamera3D(Vector3(0, 0, 0), CAMERA_DISTANCE)), - mark_solutions(false), connect_solutions(false), transforms_size(0), - transforms(nullptr) { + Renderer(const OrbitCamera3D &camera) + : camera(camera), mark_solutions(false), connect_solutions(false), + transforms_size(0), transforms(nullptr) { render_target = LoadRenderTexture(GetScreenWidth() / 2.0, GetScreenHeight() - MENU_HEIGHT); klotski_target = LoadRenderTexture(GetScreenWidth() / 2.0, GetScreenHeight() - MENU_HEIGHT); menu_target = LoadRenderTexture(GetScreenWidth(), MENU_HEIGHT); - - vertex_mat = LoadMaterialDefault(); - vertex_mat.maps[MATERIAL_MAP_DIFFUSE].color = VERTEX_COLOR; } Renderer(const Renderer ©) = delete; @@ -86,10 +49,9 @@ public: UnloadRenderTexture(klotski_target); UnloadRenderTexture(menu_target); - UnloadMaterial(vertex_mat); - // Instancing if (transforms != nullptr) { + UnloadMaterial(vertex_mat); MemFree(transforms); UnloadMesh(cube_instance); @@ -106,9 +68,6 @@ private: -> void; public: - auto UpdateCamera(const MassSpringSystem &mass_springs, - const State ¤t_state) -> void; - auto UpdateTextureSizes() -> void; auto DrawMassSprings(const MassSpringSystem &mass_springs, diff --git a/src/camera.cpp b/src/camera.cpp new file mode 100644 index 0000000..c0673f2 --- /dev/null +++ b/src/camera.cpp @@ -0,0 +1,80 @@ +#include "camera.hpp" + +auto OrbitCamera3D::Update(const Vector3 ¤t_target) -> void { + Vector2 mouse = GetMousePosition(); + if (mouse.x >= GetScreenWidth() / 2.0 && mouse.y >= MENU_HEIGHT) { + if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { + dragging = true; + last_mouse = mouse; + } else if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + panning = true; + target_lock = false; + last_mouse = mouse; + } + } + + if (IsMouseButtonReleased(MOUSE_RIGHT_BUTTON)) { + dragging = false; + } + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { + panning = false; + } + + if (IsKeyPressed(KEY_L)) { + target_lock = !target_lock; + } + + if (dragging) { + Vector2 dx = Vector2Subtract(mouse, last_mouse); + last_mouse = mouse; + + angle_x -= dx.x * ROT_SPEED / 200.0; + angle_y += dx.y * ROT_SPEED / 200.0; + + angle_y = Clamp(angle_y, -1.5, 1.5); // Prevent flipping + } + + if (panning) { + Vector2 dx = Vector2Subtract(mouse, last_mouse); + last_mouse = mouse; + + // float speed = PAN_SPEED; + float speed; + if (IsKeyDown(KEY_LEFT_SHIFT)) { + speed = distance * PAN_SPEED / 1000.0 * PAN_MULTIPLIER; + } else { + speed = distance * PAN_SPEED / 1000.0; + } + Vector3 forward = + Vector3Normalize(Vector3Subtract(camera.target, camera.position)); + Vector3 right = Vector3Normalize(Vector3CrossProduct(forward, camera.up)); + Vector3 up = Vector3Normalize(Vector3CrossProduct(right, forward)); + + Vector3 offset = Vector3Add(Vector3Scale(right, -dx.x * speed), + Vector3Scale(up, dx.y * speed)); + + target = Vector3Add(target, offset); + } + + if (target_lock) { + target = current_target; + } + + if (mouse.x >= GetScreenWidth() / 2.0 && mouse.y >= MENU_HEIGHT) { + float wheel = GetMouseWheelMove(); + if (IsKeyDown(KEY_LEFT_SHIFT)) { + distance -= wheel * ZOOM_SPEED * ZOOM_MULTIPLIER; + } else { + distance -= wheel * ZOOM_SPEED; + } + } + distance = Clamp(distance, MIN_CAMERA_DISTANCE, MAX_CAMERA_DISTANCE); + + // Spherical coordinates + float x = cos(angle_y) * sin(angle_x) * distance; + float y = sin(angle_y) * distance; + float z = cos(angle_y) * cos(angle_x) * distance; + + camera.position = Vector3Add(target, Vector3(x, y, z)); + camera.target = target; +} diff --git a/src/main.cpp b/src/main.cpp index d93db1a..37b2873 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,10 +46,11 @@ auto main(int argc, char *argv[]) -> int { InitWindow(INITIAL_WIDTH * 2, INITIAL_HEIGHT + MENU_HEIGHT, "MassSprings"); // Game setup - Renderer renderer; + OrbitCamera3D camera; + Renderer renderer(camera); MassSpringSystem mass_springs; - StateManager state = StateManager(mass_springs); - InputHandler input = InputHandler(state, renderer); + StateManager state(mass_springs); + InputHandler input(state, renderer); // Game loop #ifdef PRINT_TIMINGS @@ -68,7 +69,8 @@ auto main(int argc, char *argv[]) -> int { // Input update state.previous_state = state.current_state; input.HandleInput(); - state.UpdateGraph(); + state.UpdateGraph(); // Add state added after user input + camera.Update(mass_springs.GetMass(state.current_state).position); // Physics update #ifdef PRINT_TIMINGS @@ -98,7 +100,6 @@ auto main(int argc, char *argv[]) -> int { std::chrono::high_resolution_clock::now(); #endif - renderer.UpdateCamera(mass_springs, state.current_state); renderer.UpdateTextureSizes(); renderer.DrawMassSprings(mass_springs, state.current_state, state.winning_states); diff --git a/src/renderer.cpp b/src/renderer.cpp index a3a32ed..6c6f42c 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -15,91 +15,6 @@ #include #endif -auto OrbitCamera3D::Update(const Mass ¤t_mass) -> void { - Vector2 mouse = GetMousePosition(); - if (mouse.x >= GetScreenWidth() / 2.0 && mouse.y >= MENU_HEIGHT) { - if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { - dragging = true; - last_mouse = mouse; - } else if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - panning = true; - target_lock = false; - last_mouse = mouse; - } - } - - if (IsMouseButtonReleased(MOUSE_RIGHT_BUTTON)) { - dragging = false; - } - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { - panning = false; - } - - if (IsKeyPressed(KEY_L)) { - target_lock = !target_lock; - } - - if (dragging) { - Vector2 dx = Vector2Subtract(mouse, last_mouse); - last_mouse = mouse; - - angle_x -= dx.x * ROT_SPEED / 200.0; - angle_y += dx.y * ROT_SPEED / 200.0; - - angle_y = Clamp(angle_y, -1.5, 1.5); // Prevent flipping - } - - if (panning) { - Vector2 dx = Vector2Subtract(mouse, last_mouse); - last_mouse = mouse; - - // float speed = PAN_SPEED; - float speed; - if (IsKeyDown(KEY_LEFT_SHIFT)) { - speed = distance * PAN_SPEED / 1000.0 * PAN_MULTIPLIER; - } else { - speed = distance * PAN_SPEED / 1000.0; - } - Vector3 forward = - Vector3Normalize(Vector3Subtract(camera.target, camera.position)); - Vector3 right = Vector3Normalize(Vector3CrossProduct(forward, camera.up)); - Vector3 up = Vector3Normalize(Vector3CrossProduct(right, forward)); - - Vector3 offset = Vector3Add(Vector3Scale(right, -dx.x * speed), - Vector3Scale(up, dx.y * speed)); - - target = Vector3Add(target, offset); - } - - if (target_lock) { - target = current_mass.position; - } - - if (mouse.x >= GetScreenWidth() / 2.0 && mouse.y >= MENU_HEIGHT) { - float wheel = GetMouseWheelMove(); - if (IsKeyDown(KEY_LEFT_SHIFT)) { - distance -= wheel * ZOOM_SPEED * ZOOM_MULTIPLIER; - } else { - distance -= wheel * ZOOM_SPEED; - } - } - distance = Clamp(distance, MIN_CAMERA_DISTANCE, MAX_CAMERA_DISTANCE); - - // Spherical coordinates - float x = cos(angle_y) * sin(angle_x) * distance; - float y = sin(angle_y) * distance; - float z = cos(angle_y) * cos(angle_x) * distance; - - camera.position = Vector3Add(target, Vector3(x, y, z)); - camera.target = target; -} - -auto Renderer::UpdateCamera(const MassSpringSystem &mass_springs, - const State ¤t) -> void { - const Mass &c = mass_springs.masses.at(current); - camera.Update(c); -} - auto Renderer::UpdateTextureSizes() -> void { if (!IsWindowResized()) { return; @@ -128,6 +43,8 @@ auto Renderer::AllocateGraphInstancing(const MassSpringSystem &mass_springs) instancing_shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(instancing_shader, "viewPos"); + vertex_mat = LoadMaterialDefault(); + vertex_mat.maps[MATERIAL_MAP_DIFFUSE].color = VERTEX_COLOR; vertex_mat.shader = instancing_shader; transforms = (Matrix *)MemAlloc(mass_springs.masses.size() * sizeof(Matrix));