extract orbital camera into separate file

This commit is contained in:
2026-02-22 14:29:04 +01:00
parent 7bc1eaae75
commit 157985df6b
8 changed files with 140 additions and 140 deletions

View File

@ -11,6 +11,7 @@ include_directories(include)
add_executable(masssprings add_executable(masssprings
src/main.cpp src/main.cpp
src/camera.cpp
src/renderer.cpp src/renderer.cpp
src/physics.cpp src/physics.cpp
src/puzzle.cpp src/puzzle.cpp

41
include/camera.hpp Normal file
View File

@ -0,0 +1,41 @@
#ifndef __CAMERA_HPP_
#define __CAMERA_HPP_
#include <raylib.h>
#include <raymath.h>
#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 &current_target) -> void;
};
#endif

View File

@ -86,7 +86,6 @@ public:
class MassSpringSystem { class MassSpringSystem {
private: private:
// TODO: Use references
std::vector<Mass *> mass_vec; std::vector<Mass *> mass_vec;
std::vector<int> indices; std::vector<int> indices;
std::vector<int64_t> cell_ids; std::vector<int64_t> cell_ids;

View File

@ -282,6 +282,8 @@ public:
std::vector<std::pair<State, State>>>; std::vector<std::pair<State, State>>>;
}; };
// Provide hash functions so we can use State and <State, State> as hash-set
// keys for masses and springs.
template <> struct std::hash<State> { template <> struct std::hash<State> {
std::size_t operator()(const State &s) const noexcept { return s.Hash(); } std::size_t operator()(const State &s) const noexcept { return s.Hash(); }
}; };

View File

@ -1,57 +1,24 @@
#ifndef __RENDERER_HPP_ #ifndef __RENDERER_HPP_
#define __RENDERER_HPP_ #define __RENDERER_HPP_
#include <immintrin.h>
#include <raylib.h> #include <raylib.h>
#include <raymath.h> #include <raymath.h>
#include <unordered_set> #include <unordered_set>
#include "camera.hpp"
#include "config.hpp" #include "config.hpp"
#include "puzzle.hpp"
#include "physics.hpp" #include "physics.hpp"
#include "puzzle.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 &current_mass) -> void;
};
class Renderer { class Renderer {
private: private:
OrbitCamera3D camera; const OrbitCamera3D &camera;
RenderTexture render_target; RenderTexture render_target;
RenderTexture klotski_target; RenderTexture klotski_target;
RenderTexture menu_target; RenderTexture menu_target;
Material vertex_mat;
// Instancing // Instancing
Material vertex_mat;
int transforms_size; int transforms_size;
Matrix *transforms; Matrix *transforms;
Mesh cube_instance; Mesh cube_instance;
@ -62,18 +29,14 @@ public:
bool connect_solutions; bool connect_solutions;
public: public:
Renderer() Renderer(const OrbitCamera3D &camera)
: camera(OrbitCamera3D(Vector3(0, 0, 0), CAMERA_DISTANCE)), : camera(camera), mark_solutions(false), connect_solutions(false),
mark_solutions(false), connect_solutions(false), transforms_size(0), transforms_size(0), transforms(nullptr) {
transforms(nullptr) {
render_target = LoadRenderTexture(GetScreenWidth() / 2.0, render_target = LoadRenderTexture(GetScreenWidth() / 2.0,
GetScreenHeight() - MENU_HEIGHT); GetScreenHeight() - MENU_HEIGHT);
klotski_target = LoadRenderTexture(GetScreenWidth() / 2.0, klotski_target = LoadRenderTexture(GetScreenWidth() / 2.0,
GetScreenHeight() - MENU_HEIGHT); GetScreenHeight() - MENU_HEIGHT);
menu_target = LoadRenderTexture(GetScreenWidth(), MENU_HEIGHT); menu_target = LoadRenderTexture(GetScreenWidth(), MENU_HEIGHT);
vertex_mat = LoadMaterialDefault();
vertex_mat.maps[MATERIAL_MAP_DIFFUSE].color = VERTEX_COLOR;
} }
Renderer(const Renderer &copy) = delete; Renderer(const Renderer &copy) = delete;
@ -86,10 +49,9 @@ public:
UnloadRenderTexture(klotski_target); UnloadRenderTexture(klotski_target);
UnloadRenderTexture(menu_target); UnloadRenderTexture(menu_target);
UnloadMaterial(vertex_mat);
// Instancing // Instancing
if (transforms != nullptr) { if (transforms != nullptr) {
UnloadMaterial(vertex_mat);
MemFree(transforms); MemFree(transforms);
UnloadMesh(cube_instance); UnloadMesh(cube_instance);
@ -106,9 +68,6 @@ private:
-> void; -> void;
public: public:
auto UpdateCamera(const MassSpringSystem &mass_springs,
const State &current_state) -> void;
auto UpdateTextureSizes() -> void; auto UpdateTextureSizes() -> void;
auto DrawMassSprings(const MassSpringSystem &mass_springs, auto DrawMassSprings(const MassSpringSystem &mass_springs,

80
src/camera.cpp Normal file
View File

@ -0,0 +1,80 @@
#include "camera.hpp"
auto OrbitCamera3D::Update(const Vector3 &current_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;
}

View File

@ -46,10 +46,11 @@ auto main(int argc, char *argv[]) -> int {
InitWindow(INITIAL_WIDTH * 2, INITIAL_HEIGHT + MENU_HEIGHT, "MassSprings"); InitWindow(INITIAL_WIDTH * 2, INITIAL_HEIGHT + MENU_HEIGHT, "MassSprings");
// Game setup // Game setup
Renderer renderer; OrbitCamera3D camera;
Renderer renderer(camera);
MassSpringSystem mass_springs; MassSpringSystem mass_springs;
StateManager state = StateManager(mass_springs); StateManager state(mass_springs);
InputHandler input = InputHandler(state, renderer); InputHandler input(state, renderer);
// Game loop // Game loop
#ifdef PRINT_TIMINGS #ifdef PRINT_TIMINGS
@ -68,7 +69,8 @@ auto main(int argc, char *argv[]) -> int {
// Input update // Input update
state.previous_state = state.current_state; state.previous_state = state.current_state;
input.HandleInput(); input.HandleInput();
state.UpdateGraph(); state.UpdateGraph(); // Add state added after user input
camera.Update(mass_springs.GetMass(state.current_state).position);
// Physics update // Physics update
#ifdef PRINT_TIMINGS #ifdef PRINT_TIMINGS
@ -98,7 +100,6 @@ auto main(int argc, char *argv[]) -> int {
std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::now();
#endif #endif
renderer.UpdateCamera(mass_springs, state.current_state);
renderer.UpdateTextureSizes(); renderer.UpdateTextureSizes();
renderer.DrawMassSprings(mass_springs, state.current_state, renderer.DrawMassSprings(mass_springs, state.current_state,
state.winning_states); state.winning_states);

View File

@ -15,91 +15,6 @@
#include <cstring> #include <cstring>
#endif #endif
auto OrbitCamera3D::Update(const Mass &current_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 &current) -> void {
const Mass &c = mass_springs.masses.at(current);
camera.Update(c);
}
auto Renderer::UpdateTextureSizes() -> void { auto Renderer::UpdateTextureSizes() -> void {
if (!IsWindowResized()) { if (!IsWindowResized()) {
return; return;
@ -128,6 +43,8 @@ auto Renderer::AllocateGraphInstancing(const MassSpringSystem &mass_springs)
instancing_shader.locs[SHADER_LOC_VECTOR_VIEW] = instancing_shader.locs[SHADER_LOC_VECTOR_VIEW] =
GetShaderLocation(instancing_shader, "viewPos"); GetShaderLocation(instancing_shader, "viewPos");
vertex_mat = LoadMaterialDefault();
vertex_mat.maps[MATERIAL_MAP_DIFFUSE].color = VERTEX_COLOR;
vertex_mat.shader = instancing_shader; vertex_mat.shader = instancing_shader;
transforms = (Matrix *)MemAlloc(mass_springs.masses.size() * sizeof(Matrix)); transforms = (Matrix *)MemAlloc(mass_springs.masses.size() * sizeof(Matrix));