Skip to content

Commit 27134cd

Browse files
committed
add mouse binding support for camera ctl
1 parent 8eff93a commit 27134cd

File tree

9 files changed

+503
-80
lines changed

9 files changed

+503
-80
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.10)
33
if (NOT CMAKE_BUILD_TYPE)
44
set(CMAKE_BUILD_TYPE Release)
55
endif()
6-
set(CMAKE_CXX_STANDARD 14)
6+
set(CMAKE_CXX_STANDARD 17)
77
set(CMAKE_CXX_STANDARD_REQUIRED ON)
88

99
project(opengltutor

include/Game.hpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@
22

33
#include "check_gl.hpp"
44
#include <memory>
5+
#include "InputCtl.hpp"
56

67
struct Game {
78
struct Private;
89
std::unique_ptr<Private> const m_private;
9-
GLFWwindow *const m_window;
1010

11-
Game(GLFWwindow *window);
11+
InputCtl m_inputCtl;
12+
GLFWwindow *m_window;
13+
14+
Game();
1215
~Game();
1316

1417
Game(Game &&) = delete;
1518

19+
static Game &get();
20+
void set_window(GLFWwindow *window);
1621
void initialize();
1722
void render();
18-
void cursor_pos_callback(double xpos, double ypos);
19-
void mouse_button_callback(int button, int action, int mods);
20-
void scroll_callback(double xoffset, double yoffset);
21-
void key_callback(int key, int scancode, int action, int mods);
2223
};

include/InputCtl.hpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
3+
#include "check_gl.hpp"
4+
#include <memory>
5+
#include <glm/glm.hpp>
6+
#include "KeyBinding.hpp"
7+
8+
struct InputCtl {
9+
struct Private;
10+
std::unique_ptr<Private> const m_private;
11+
12+
struct InputPreference {
13+
float zoom_speed = 0.2f;
14+
float orbit_speed = 1.0f;
15+
float drift_speed = 1.0f;
16+
float pan_speed = 1.0f;
17+
MouseBinding orbit_binding = {Modifier::kNone, MouseButton::kLMB};
18+
MouseBinding drift_binding = {Modifier::kCtrl, MouseButton::kLMB};
19+
MouseBinding pan_binding = {Modifier::kShift, MouseButton::kLMB};
20+
MouseBinding zoom_binding = {Modifier::kNone, MouseButton::kWheel};
21+
MouseBinding hitchcock_binding = {Modifier::kShift, MouseButton::kWheel};
22+
// Familiar binding for Blender and Zeno users:
23+
/* MouseBinding orbit_binding = {Modifier::kNone, MouseButton::kMMB}; */
24+
/* MouseBinding drift_binding = {Modifier::kCtrl, MouseButton::kMMB}; */
25+
/* MouseBinding pan_binding = {Modifier::kShift, MouseButton::kMMB}; */
26+
/* MouseBinding zoom_binding = {Modifier::kNone, MouseButton::kWheel}; */
27+
/* MouseBinding hitchcock_binding = {Modifier::kShift, MouseButton::kWheel}; */
28+
int zoom_axis = 1;
29+
bool clamp_cursor = true;
30+
} m_inputPref;
31+
32+
private:
33+
void cursor_pos_callback(double xpos, double ypos);
34+
void mouse_button_callback(int button, int action, int mods);
35+
void scroll_callback(double xoffset, double yoffset);
36+
void key_callback(int key, int scancode, int action, int mods);
37+
void framebuffer_size_callback(int width, int height);
38+
39+
public:
40+
InputCtl();
41+
~InputCtl();
42+
InputCtl(InputCtl &&) = delete;
43+
44+
void register_callbacks(GLFWwindow *window);
45+
46+
glm::mat4x4 get_view_matrix();
47+
glm::mat4x4 get_projection_matrix();
48+
};

include/KeyBinding.hpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#pragma once
2+
3+
#include "check_gl.hpp"
4+
#include <cmath>
5+
6+
enum class Modifier {
7+
kNone,
8+
kCtrl,
9+
kShift,
10+
kAlt,
11+
};
12+
13+
enum class MouseButton {
14+
kLMB,
15+
kRMB,
16+
kMMB,
17+
kWheel,
18+
kNone,
19+
};
20+
21+
struct MouseBinding {
22+
Modifier modifier = Modifier::kNone;
23+
MouseButton mouseBtn = MouseButton::kLMB;
24+
25+
bool check_is_scrolled(GLFWwindow *window) {
26+
bool modPressed = true;
27+
bool shift = glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS;
28+
bool ctrl = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;
29+
bool alt = glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;
30+
auto mods = (shift ? GLFW_MOD_SHIFT : 0) | (ctrl ? GLFW_MOD_CONTROL : 0) | (alt ? GLFW_MOD_ALT : 0);
31+
switch (modifier) {
32+
case Modifier::kShift: modPressed = mods == GLFW_MOD_SHIFT; break;
33+
case Modifier::kCtrl: modPressed = mods == GLFW_MOD_CONTROL; break;
34+
case Modifier::kAlt: modPressed = mods == GLFW_MOD_ALT; break;
35+
default: modPressed = mods == 0; break;
36+
}
37+
bool wheelScrolled = false;
38+
switch (mouseBtn) {
39+
case MouseButton::kWheel: wheelScrolled = true; break;
40+
default: break;
41+
}
42+
return wheelScrolled && modPressed;
43+
}
44+
45+
/* bool check_is_pressed(GLFWwindow *window, int button, int action, int mods) { */
46+
/* bool modPressed = true; */
47+
/* mods &= GLFW_MOD_SHIFT | GLFW_MOD_CONTROL | GLFW_MOD_ALT; */
48+
/* switch (modifier) { */
49+
/* case Modifier::kShift: modPressed = mods == GLFW_MOD_SHIFT; break; */
50+
/* case Modifier::kCtrl: modPressed = mods == GLFW_MOD_CONTROL; break; */
51+
/* case Modifier::kAlt: modPressed = mods == GLFW_MOD_ALT; break; */
52+
/* default: modPressed = mods == 0; break; */
53+
/* } */
54+
/* bool btnPressed = false; */
55+
/* switch (mouseBtn) { */
56+
/* case MouseButton::kNone: btnPressed = false; break; */
57+
/* case MouseButton::kLMB: btnPressed = button == GLFW_MOUSE_BUTTON_LEFT; break; */
58+
/* case MouseButton::kMMB: btnPressed = button == GLFW_MOUSE_BUTTON_MIDDLE; break; */
59+
/* case MouseButton::kRMB: btnPressed = button == GLFW_MOUSE_BUTTON_RIGHT; break; */
60+
/* default: break; */
61+
/* } */
62+
/* return btnPressed && modPressed && action == GLFW_PRESS; */
63+
/* } */
64+
65+
bool check_is_pressed(GLFWwindow *window) {
66+
bool modPressed = true;
67+
bool shift = glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS;
68+
bool ctrl = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;
69+
bool alt = glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;
70+
auto mods = (shift ? GLFW_MOD_SHIFT : 0) | (ctrl ? GLFW_MOD_CONTROL : 0) | (alt ? GLFW_MOD_ALT : 0);
71+
switch (modifier) {
72+
case Modifier::kShift: modPressed = mods == GLFW_MOD_SHIFT; break;
73+
case Modifier::kCtrl: modPressed = mods == GLFW_MOD_CONTROL; break;
74+
case Modifier::kAlt: modPressed = mods == GLFW_MOD_ALT; break;
75+
default: modPressed = mods == 0; break;
76+
}
77+
int button = GLFW_MOUSE_BUTTON_LEFT;
78+
switch (mouseBtn) {
79+
case MouseButton::kLMB: button = GLFW_MOUSE_BUTTON_LEFT; break;
80+
case MouseButton::kMMB: button = GLFW_MOUSE_BUTTON_MIDDLE; break;
81+
case MouseButton::kRMB: button = GLFW_MOUSE_BUTTON_RIGHT; break;
82+
case MouseButton::kNone: return modPressed
83+
&& glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) != GLFW_PRESS
84+
&& glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) != GLFW_PRESS
85+
&& glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) != GLFW_PRESS
86+
;
87+
default: return false;
88+
}
89+
return modPressed && glfwGetMouseButton(window, button) == GLFW_PRESS;
90+
}
91+
};

include/print.hpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <optional>
1010
#include <variant>
1111

12+
/* namespace conutils { */
13+
1214
namespace _print_details {
1315
template <class T, class = void>
1416
struct _printer {
@@ -52,6 +54,42 @@ namespace _print_details {
5254
using type = U;
5355
};
5456

57+
template <class T, class U = void, class = void>
58+
struct _enable_if_glmvec {
59+
using not_type = U;
60+
};
61+
62+
template <class T, class U>
63+
struct _enable_if_glmvec<T, U, std::enable_if_t<
64+
std::is_same_v<decltype(T()[0] * T()), T> &&
65+
std::is_same_v<decltype(T() * T()[0]), T> &&
66+
std::is_same_v<decltype(T().x + T()), T> &&
67+
std::is_same_v<decltype(T().x), typename T::value_type> &&
68+
std::is_same_v<decltype(T()[0]), typename T::value_type &> &&
69+
std::is_same_v<decltype(T() == T()), bool> &&
70+
std::is_same_v<decltype(T(T()[0])), T> &&
71+
std::is_same_v<decltype(T().length()), typename T::length_type> &&
72+
std::is_same_v<decltype(std::declval<typename T::bool_type>().x), bool>
73+
>> {
74+
using type = U;
75+
};
76+
77+
template <class T, class U = void, class = void>
78+
struct _enable_if_glmmat {
79+
using not_type = U;
80+
};
81+
82+
template <class T, class U>
83+
struct _enable_if_glmmat<T, U, typename _enable_if_glmvec<typename T::col_type,
84+
typename _enable_if_glmvec<typename T::row_type, std::enable_if_t<
85+
std::is_same_v<decltype(T()[0] * T()), typename T::row_type> &&
86+
std::is_same_v<decltype(T() * T()[0]), typename T::col_type> &&
87+
std::is_same_v<decltype(T()[0][0] * T()), T> &&
88+
std::is_same_v<decltype(T()[0][0]), typename T::value_type &>
89+
>>::type>::type> {
90+
using type = U;
91+
};
92+
5593
template <class T, class U = void, class = void>
5694
struct _enable_if_map {
5795
using not_type = U;
@@ -188,6 +226,40 @@ namespace _print_details {
188226
}
189227
};
190228

229+
template <class T>
230+
struct _printer<T, typename _enable_if_has_print<T, typename _enable_if_glmvec<T>::type>::not_type> {
231+
static void print(std::ostream &os, T const &t) {
232+
os << "{";
233+
bool once = false;
234+
for (typename T::length_type i = 0; i < t.length(); i++) {
235+
if (once) {
236+
os << ", ";
237+
} else {
238+
once = true;
239+
}
240+
_printer<_rmcvref_t<typename T::value_type>>::print(os, t[i]);
241+
}
242+
os << "}";
243+
}
244+
};
245+
246+
template <class T>
247+
struct _printer<T, typename _enable_if_has_print<T, typename _enable_if_glmmat<T>::type>::not_type> {
248+
static void print(std::ostream &os, T const &t) {
249+
os << "{";
250+
bool once = false;
251+
for (typename T::length_type i = 0; i < t.length(); i++) {
252+
if (once) {
253+
os << ",\n ";
254+
} else {
255+
once = true;
256+
}
257+
_printer<_rmcvref_t<typename T::col_type>>::print(os, t[i]);
258+
}
259+
os << "}";
260+
}
261+
};
262+
191263
template <class T>
192264
struct _printer<T, typename _enable_if_has_print<T, typename _enable_if_map<T>::type>::not_type> {
193265
static void print(std::ostream &os, T const &t) {
@@ -370,3 +442,5 @@ using _print_details::is_printable;
370442
//
371443
// map<string, optional<int>> m = {{"hello", 42}, {"world", nullopt}};
372444
// print(m); // {"hello": 42, "world": nullopt}
445+
446+
/* } */

src/Game.cpp

Lines changed: 31 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,31 @@
44
#include <glm/ext.hpp>
55
#include "OBJ.hpp"
66

7-
struct Game::Private {
7+
struct Game::Private { // P-IMPL pattern
8+
glm::mat4x4 viewMat;
9+
glm::mat4x4 projMat;
10+
811
OBJ monkey;
912
};
1013

11-
Game::Game(GLFWwindow *window) : m_private(new Private), m_window(window) {
12-
glfwSetWindowUserPointer(window, this);
13-
}
14+
Game::Game() : m_private(std::make_unique<Private>()) {}
1415

1516
Game::~Game() = default;
1617

18+
Game &Game::get() {
19+
static Game game; // singleton
20+
return game;
21+
}
22+
23+
void Game::set_window(GLFWwindow *window) {
24+
m_window = window;
25+
glfwSetWindowUserPointer(window, this);
26+
m_inputCtl.register_callbacks(window);
27+
}
28+
1729
void Game::initialize() {
18-
m_private->monkey.load_obj("/home/bate/Codes/opengltutor/assets/monkey.obj");
30+
/* m_private->monkey.load_obj("/home/bate/Codes/opengltutor/assets/monkey.obj"); */
31+
m_private->monkey.load_obj("/home/bate/Codes/opengltutor/assets/cube.obj");
1932
CHECK_GL(glEnable(GL_DEPTH_TEST));
2033
CHECK_GL(glEnable(GL_MULTISAMPLE));
2134
CHECK_GL(glEnable(GL_BLEND));
@@ -35,59 +48,25 @@ void Game::render() {
3548

3649
CHECK_GL(glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT));
3750

38-
glm::mat4x4 projection = glm::perspective(glm::radians(40.0f), (float)width / height, 0.01f, 100.0f);
39-
/* projection = glm::ortho(-2.0f, 2.0f, -2.0f, 2.0f, 0.0f, 100.0f); */
40-
/* projection = glm::frustum(-0.005f, 0.005f, -0.005f, 0.005f, 0.01f, 100.0f); */
51+
auto projection = m_inputCtl.get_projection_matrix();
52+
4153
CHECK_GL(glMatrixMode(GL_PROJECTION));
4254
CHECK_GL(glLoadMatrixf(glm::value_ptr(projection)));
43-
44-
glm::vec3 eye(0, 0, 5);
45-
glm::vec3 center(0, 0, 0);
46-
glm::vec3 up(0, 1, 0);
47-
glm::mat4x4 view = glm::lookAt(eye, center, up);
48-
55+
56+
/* glm::vec3 eye(0, 0, 5); */
57+
/* glm::vec3 center(0, 0, 0); */
58+
/* glm::vec3 up(0, 1, 0); */
59+
/* glm::mat4x4 view = glm::lookAt(eye, center, up); */
60+
auto view = m_inputCtl.get_view_matrix();
61+
4962
static float angle = 0.0f;
5063
glm::mat4x4 model(1.0f);
51-
model = glm::rotate(model, glm::radians(angle), glm::vec3(0.0f, 1.0f, 0.0f));
52-
model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));
53-
angle += 0.5f;
54-
64+
/* model = glm::rotate(model, glm::radians(angle), glm::vec3(0.0f, 1.0f, 0.0f)); */
65+
/* model = glm::translate(model, glm::vec3(0.0f, 0.12f * glm::sin(glm::radians(angle) * 2.718f), 0.0f)); */
66+
/* angle += 0.5f; */
67+
5568
CHECK_GL(glMatrixMode(GL_MODELVIEW));
5669
CHECK_GL(glLoadMatrixf(glm::value_ptr(view * model)));
5770

5871
m_private->monkey.draw_obj();
5972
}
60-
61-
void Game::cursor_pos_callback(double xpos, double ypos) {
62-
int width, height;
63-
glfwGetWindowSize(m_window, &width, &height);
64-
65-
float x = (float)(2 * xpos / width - 1);
66-
float y = (float)(2 * (height - ypos) / height - 1);
67-
68-
if (glfwGetMouseButton(m_window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) {
69-
// TODO: when left mouse button is dragged
70-
}
71-
}
72-
73-
void Game::mouse_button_callback(int button, int action, int mods) {
74-
double xpos, ypos;
75-
glfwGetCursorPos(m_window, &xpos, &ypos);
76-
int width, height;
77-
glfwGetWindowSize(m_window, &width, &height);
78-
79-
float x = (float)(2 * xpos / width - 1);
80-
float y = (float)(2 * (height - ypos) / height - 1);
81-
82-
if ( button == GLFW_MOUSE_BUTTON_LEFT
83-
&& action == GLFW_PRESS
84-
) {
85-
// TODO: when left mouse button is pressed
86-
}
87-
}
88-
89-
void Game::scroll_callback(double xoffset, double yoffset) {
90-
}
91-
92-
void Game::key_callback(int key, int scancode, int action, int mods) {
93-
}

0 commit comments

Comments
 (0)