/* Projections CC BY-SA Edouard.Thiel@univ-amu.fr - 24/12/2024 */ #include #include #include #include #include const double FRAMES_PER_SEC = 30.0; const double ANIM_DURATION = 18.0; enum CamProj { P_ORTHO, P_FRUSTUM, P_MAX }; class MyApp { bool m_ok = false; GLFWwindow* m_window = nullptr; bool m_anim_flag = false; double m_anim_angle = 0, m_start_angle = 0; double m_cam_z, m_cam_r, m_cam_near, m_cam_far; CamProj m_cam_proj; void displayGL() { //std::cout << __func__ << std::endl; // change l'angle en fonction du temps if (m_anim_flag) { double time = glfwGetTime(); // durée depuis init double slice = time / ANIM_DURATION; double a = slice - std::floor(slice); // partie fractionnaire m_anim_angle = m_start_angle + a*360.0; } glClear (GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt (0, 0, m_cam_z, 0, 0, 0, 0, 1, 0); glRotated (m_anim_angle, 0, 1.0, 0.15); draw_wire_cube (0.5); } void draw_wire_cube (double r) { int sa[] = {-1, -1, 1, 1}, sb[] = {-1, 1, -1, 1}; glBegin(GL_LINES); glColor3d (1.0, 0.0, 0.0); for (int i = 0; i < 4; i++) { glVertex3d (-r, r*sa[i], r*sb[i]); glVertex3d ( r, r*sa[i], r*sb[i]); } glColor3d (0.0, 1.0, 0.0); for (int i = 0; i < 4; i++) { glVertex3d (r*sa[i], -r, r*sb[i]); glVertex3d (r*sa[i], r, r*sb[i]); } glColor3d (0.0, 0.0, 1.0); for (int i = 0; i < 4; i++) { glVertex3d (r*sa[i], r*sb[i], -r); glVertex3d (r*sa[i], r*sb[i], r); } glEnd(); } void cam_init() { m_cam_z = 3; m_cam_r = 1; m_cam_near = 1; m_cam_far = 5; m_cam_proj = P_ORTHO; } void set_projection() { glMatrixMode (GL_PROJECTION); glLoadIdentity(); switch (m_cam_proj) { case P_ORTHO : std::cout << std::fixed << std::setprecision(1) << "glOrtho (" << -m_cam_r << ", " << m_cam_r << ", " << -m_cam_r << ", " << m_cam_r << ", " << m_cam_near << ", " << m_cam_far << ") " << "cam_z = " << m_cam_z << std::endl; glOrtho (-m_cam_r, m_cam_r, -m_cam_r, m_cam_r, m_cam_near, m_cam_far); break; case P_FRUSTUM : std::cout << std::fixed << std::setprecision(1) << "glFrustum (" << -m_cam_r << ", " << m_cam_r << ", " << -m_cam_r << ", " << m_cam_r << ", " << m_cam_near << ", " << m_cam_far << ") " << "cam_z = " << m_cam_z << std::endl; glFrustum (-m_cam_r, m_cam_r, -m_cam_r, m_cam_r, m_cam_near, m_cam_far); break; default :; } glMatrixMode (GL_MODELVIEW); } static void on_reshape_func (GLFWwindow* window, int width, int height) { std::cout << __func__ << " " << width << " " << height << std::endl; glViewport (0, 0, width, height); MyApp* that = static_cast(glfwGetWindowUserPointer (window)); that->set_projection(); } static void print_help() { std::cout << "h help i init a anim p proj zZ cam_z rR radius " << "nN near fF far dD dist" << std::endl; } static void on_key_func (GLFWwindow* window, int key, int scancode, int action, int mods) { //std::cout << __func__ << " " << key << " " << scancode << " " // << action << " " << mods << std::endl; // action = GLFW_PRESS ou GLFW_REPEAT ou GLFW_RELEASE if (action == GLFW_RELEASE) return; MyApp* that = static_cast(glfwGetWindowUserPointer (window)); int trans_key = translate_qwerty_to_azerty (key, scancode); switch (trans_key) { case GLFW_KEY_I : that->cam_init (); break; case GLFW_KEY_A : that->m_anim_flag = !that->m_anim_flag; if (that->m_anim_flag) { that->m_start_angle = that->m_anim_angle; glfwSetTime (0); } break; case GLFW_KEY_P : { int k = static_cast(that->m_cam_proj) + 1; if (k >= static_cast(P_MAX)) k = 0; that->m_cam_proj = static_cast(k); break; } case GLFW_KEY_Z : change_val_mods (that->m_cam_z, mods, 0.1, -100); break; case GLFW_KEY_R : change_val_mods (that->m_cam_r, mods, 0.1, 0.1); break; case GLFW_KEY_N : change_val_mods (that->m_cam_near, mods, 0.1, 0.1); break; case GLFW_KEY_F : change_val_mods (that->m_cam_far, mods, 0.1, 0.1); break; case GLFW_KEY_D : change_val_mods (that->m_cam_z, mods, 0.1, -100); change_val_mods (that->m_cam_near, mods, 0.1, 0.1); change_val_mods (that->m_cam_far, mods, 0.1, 0.1); break; case GLFW_KEY_H : print_help (); break; case GLFW_KEY_ESCAPE : that->m_ok = false; break; default: return; } that->set_projection(); } static void change_val_mods (double& val, int mods, double incr, double min_val) { val += (mods & GLFW_MOD_SHIFT) ? incr : -incr; if (val <= min_val) val = min_val; } static int translate_qwerty_to_azerty (int key, int scancode) { // https://www.glfw.org/docs/latest/group__keys.html // QWERTY -> AZERTY switch (key) { case GLFW_KEY_Q : return GLFW_KEY_A; case GLFW_KEY_A : return GLFW_KEY_Q; case GLFW_KEY_W : return GLFW_KEY_Z; case GLFW_KEY_Z : return GLFW_KEY_W; case GLFW_KEY_SEMICOLON : return GLFW_KEY_M; } // Détection des différences non corrigées const char* name = glfwGetKeyName (key, scancode); if (name != NULL) { int capital = toupper(name[0]); if (capital != key) { std::cout << __func__ << " DIFF " << capital << " " << key << std::endl; } } return key; } static void on_error_func (int error, const char* description) { std::cerr << "Error: " << description << std::endl; } public: MyApp() { if (!glfwInit()) { std::cerr << "GLFW: initialization failed" << std::endl; return; } glfwSetErrorCallback (on_error_func); m_window = glfwCreateWindow (640, 480, "Projections", NULL, NULL); if (!m_window) { std::cerr << "GLFW: window creation failed" << std::endl; return; } // Les callbacks pour GLFW étant statiques, on mémorise l'instance glfwSetWindowUserPointer (m_window, this); glfwSetWindowSizeCallback (m_window, on_reshape_func); glfwSetKeyCallback (m_window, on_key_func); glfwMakeContextCurrent (m_window); m_ok = true; cam_init(); set_projection(); print_help(); } void run() { while (m_ok && !glfwWindowShouldClose (m_window)) { displayGL(); glfwSwapBuffers (m_window); //glfwWaitEvents(); glfwWaitEventsTimeout (1.0/FRAMES_PER_SEC); } } ~MyApp() { if (m_window) glfwDestroyWindow (m_window); glfwTerminate(); } }; // MyApp int main() { MyApp app; app.run(); }