implement option to lock camera to graph's center of mass
This commit is contained in:
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -68,13 +68,13 @@ public:
|
||||
|
||||
class MassSpringSystem {
|
||||
private:
|
||||
Octree octree;
|
||||
|
||||
#ifdef THREADPOOL
|
||||
BS::thread_pool<BS::tp::none> threads;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Octree octree;
|
||||
|
||||
// This is the main ownership of all the states/masses/springs.
|
||||
std::vector<Mass> masses;
|
||||
std::vector<Spring> 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<Vector3> masses; // Read by renderer
|
||||
bool data_ready = false;
|
||||
|
||||
@ -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;
|
||||
|
||||
32
src/gui.cpp
32
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();
|
||||
|
||||
@ -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
|
||||
|
||||
11
src/main.cpp
11
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<Vector3> masses; // Read from physics
|
||||
unsigned int ups = 0; // Read from physics
|
||||
Vector3 mass_center = Vector3Zero(); // Read from physics
|
||||
std::vector<Vector3> 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
|
||||
|
||||
@ -276,6 +276,11 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state)
|
||||
loop_iterations = 0;
|
||||
ups_accumulator = std::chrono::duration<double>(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());
|
||||
|
||||
@ -129,13 +129,13 @@ auto Renderer::DrawMassSprings(const std::vector<Vector3> &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<Vector3> &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<Vector3> &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<Vector3> &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<Vector3> &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();
|
||||
|
||||
Reference in New Issue
Block a user