// Copyright (c) 2018 GeometryFactory Sarl (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). // // $URL$ // $Id$ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // // Author(s) : Guillaume Damiand // Mostafa Ashraf #ifndef CGAL_QT_BASIC_VIEWER_H #define CGAL_QT_BASIC_VIEWER_H #if defined(CGAL_USE_BASIC_VIEWER_QT) // TODO #include #include #include #include #include #include #ifdef __GNUC__ #if __GNUC__ >= 9 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-copy" #endif #endif #include #include #include #include #include #include #include #ifdef __GNUC__ #if __GNUC__ >= 9 # pragma GCC diagnostic pop #endif #endif #include #include #include #include #include #include #include #include #define CGAL_BASIC_VIEWER_INIT_SIZE_X 500 #define CGAL_BASIC_VIEWER_INIT_SIZE_Y 450 namespace CGAL { namespace Qt { //------------------------------------------------------------------------------ class Basic_viewer : public CGAL::QGLViewer { public: using BufferType=float; typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; typedef Local_kernel::Point_3 Local_point; typedef Local_kernel::Vector_3 Local_vector; using GS=Graphics_scene; // Constructor/Destructor Basic_viewer(QWidget* parent, const Graphics_scene& buf, const char* title="", bool draw_vertices=false, bool draw_edges=true, bool draw_faces=true, bool use_default_color=false, bool inverse_normal=false, bool draw_rays=true, bool draw_lines=true, bool draw_text=true, bool no_2D_mode=false) : CGAL::QGLViewer(parent), m_scene(buf), m_draw_vertices(draw_vertices), m_draw_edges(draw_edges), m_draw_rays(draw_rays), m_draw_lines(draw_lines), m_draw_faces(draw_faces), m_draw_text(draw_text), m_draw_normals(false), m_draw_cylinder_edge(false), m_draw_sphere_vertex(false), m_draw_mesh_triangles(false), m_flat_shading(true), m_use_default_color(use_default_color), m_use_default_color_normal(false), m_display_face_normal(false), m_inverse_normal(inverse_normal), m_no_2D_mode(no_2D_mode), m_geometry_feature_enabled(true), m_prev_scene_empty(true), m_default_color_normal(220, 60, 20), m_ambient_color(0.6f, 0.5f, 0.5f, 0.5f), m_are_buffers_initialized(false) { // Define 'Control+Q' as the new exit shortcut (default was 'Escape') setShortcut(qglviewer::EXIT_VIEWER, ::Qt::CTRL, ::Qt::Key_Q); // Add custom key description (see keyPressEvent). setKeyDescription(::Qt::Key_C, "Switch clipping plane display mode"); setKeyDescription(::Qt::AltModifier, ::Qt::Key_C, "Toggle clipping plane rendering on/off"); setKeyDescription(::Qt::Key_E, "Toggles edges display"); setKeyDescription(::Qt::Key_M, "Toggles mono color"); setKeyDescription(::Qt::Key_N, "Inverse direction of normals"); setKeyDescription(::Qt::Key_S, "Switch between flat/Gouraud shading display"); setKeyDescription(::Qt::Key_T, "Toggles text display"); setKeyDescription(::Qt::Key_U, "Move camera direction upside down"); setKeyDescription(::Qt::Key_V, "Toggles vertices display"); setKeyDescription(::Qt::Key_W, "Toggles faces display"); setKeyDescription(::Qt::Key_Plus, "Increase size of edges"); setKeyDescription(::Qt::Key_Minus, "Decrease size of edges"); setKeyDescription(::Qt::ControlModifier, ::Qt::Key_Plus, "Increase size of vertices"); setKeyDescription(::Qt::ControlModifier, ::Qt::Key_Minus, "Decrease size of vertices"); setKeyDescription(::Qt::Key_PageDown, "Increase light (all colors, use shift/alt/ctrl for one rgb component)"); setKeyDescription(::Qt::Key_PageUp, "Decrease light (all colors, use shift/alt/ctrl for one rgb component)"); setKeyDescription(::Qt::Key_O, "Toggles 2D mode only"); setKeyDescription(::Qt::ControlModifier, ::Qt::Key_V, "Toggle vertices display as sphere"); setKeyDescription(::Qt::ControlModifier, ::Qt::Key_E, "Toggle edges display as cylinder"); setKeyDescription(::Qt::ControlModifier, ::Qt::Key_N, "Toggle normals display"); setKeyDescription(::Qt::ShiftModifier, ::Qt::Key_N, "Toggle face/vertex normals for normal display"); setKeyDescription(::Qt::ControlModifier, ::Qt::Key_M, "Toggle normals mono color"); setKeyDescription(::Qt::ControlModifier, ::Qt::Key_T, "Toggle triangles display"); setKeyDescription(::Qt::Key_F2, "Take a screenshot"); // Add custom mouse description setMouseBindingDescription(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::LeftButton, "Rotate the clipping plane when enabled"); setMouseBindingDescription(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::RightButton, "Translate the clipping plane when enabled"); setMouseBindingDescription(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::MiddleButton, "Control the clipping plane transparency when enabled"); setMouseBinding(::Qt::ControlModifier, ::Qt::LeftButton, qglviewer::FRAME, qglviewer::NO_MOUSE_ACTION); setMouseBinding(::Qt::ControlModifier, ::Qt::RightButton, qglviewer::FRAME, qglviewer::NO_MOUSE_ACTION); setMouseBinding(::Qt::ControlModifier, ::Qt::MiddleButton, qglviewer::FRAME, qglviewer::NO_MOUSE_ACTION); setWheelBinding(::Qt::ControlModifier, qglviewer::FRAME, qglviewer::NO_MOUSE_ACTION); setMouseBinding(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::LeftButton, qglviewer::FRAME, qglviewer::ROTATE); setMouseBinding(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::RightButton, qglviewer::FRAME, qglviewer::TRANSLATE); setMouseBinding(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::MiddleButton, qglviewer::FRAME, qglviewer::ZOOM); setWheelBinding(::Qt::Key_C, ::Qt::ControlModifier, qglviewer::FRAME, qglviewer::ZOOM); if (title[0]==0) setWindowTitle("CGAL Basic Viewer"); else setWindowTitle(title); resize(CGAL_BASIC_VIEWER_INIT_SIZE_X, CGAL_BASIC_VIEWER_INIT_SIZE_Y); } ~Basic_viewer() { makeCurrent(); for (unsigned int i=0; iinverseTransformOf (CGAL::qglviewer::Vec(0.f, 0.f, 1.f)); const CGAL::qglviewer::Vec& pos=m_frame_plane->position(); return Local_kernel::Plane_3(n[0], n[1], n[2], -n*pos); } bool clipping_plane_enabled() const { return (m_use_clipping_plane!=CLIPPING_PLANE_OFF); } const Graphics_scene& graphics_scene() const { return m_scene; } /*****************/ virtual void redraw() { initialize_buffers(); update(); if(m_prev_scene_empty) { initialize_vertices_and_edges_size(); } m_prev_scene_empty=(m_scene.empty()); } void reverse_all_normals() { m_inverse_normal=!m_inverse_normal; m_scene.reverse_all_normals(); } // Returns true if the data structure lies on a plane bool is_two_dimensional() const { return !m_no_2D_mode && m_scene.is_two_dimensional(); } virtual void draw() { glEnable(GL_DEPTH_TEST); QMatrix4x4 clipping_mMatrix; clipping_mMatrix.setToIdentity(); for(int i=0; i< 16 ; i++) { clipping_mMatrix.data()[i] = m_frame_plane->matrix()[i]; } QVector4D clipPlane = clipping_mMatrix * QVector4D(0.0, 0.0, 1.0, 0.0); QVector4D plane_point = clipping_mMatrix * QVector4D(0,0,0,1); if(!m_are_buffers_initialized) { initialize_buffers(); } QVector3D color; attrib_buffers(this); if(m_draw_vertices) { if (m_draw_sphere_vertex && m_geometry_feature_enabled) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_sphere.bind(); if (m_use_default_color) { auto vertex_color = m_scene.get_default_color_point(); color = QVector3D((double)vertex_color.red()/(double)255, (double)vertex_color.green()/(double)255, (double)vertex_color.blue()/(double)255); rendering_program_sphere.setUniformValue("u_DefaultColor", color); rendering_program_sphere.setUniformValue("u_UseDefaultColor", static_cast(1)); } else { rendering_program_sphere.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_sphere.setUniformValue("u_Radius", static_cast(sceneRadius()*m_size_vertices*0.002)); rendering_program_sphere.setUniformValue("u_ClipPlane", clipPlane); rendering_program_sphere.setUniformValue("u_PointPlane", plane_point); rendering_program_sphere.setUniformValue("u_RenderingMode", rendering_mode); vao[VAO_POINTS].bind(); glDrawArrays(GL_POINTS, 0, static_cast(m_scene.number_of_elements(GS::POS_POINTS))); }; enum { DRAW_ALL = -1, // draw all DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane }; if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); } else { renderer(DRAW_ALL); } rendering_program_sphere.release(); } else { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_p_l.bind(); if (m_use_default_color) { auto vertex_color = m_scene.get_default_color_point(); color = QVector3D((double)vertex_color.red()/(double)255, (double)vertex_color.green()/(double)255, (double)vertex_color.blue()/(double)255); rendering_program_p_l.setUniformValue("u_DefaultColor", color); rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(1)); } else { rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_p_l.setUniformValue("u_PointSize", GLfloat(m_size_vertices)); rendering_program_p_l.setUniformValue("u_IsOrthographic", GLint(is_two_dimensional())); rendering_program_p_l.setUniformValue("u_ClipPlane", clipPlane); rendering_program_p_l.setUniformValue("u_PointPlane", plane_point); rendering_program_p_l.setUniformValue("u_RenderingMode", rendering_mode); vao[VAO_POINTS].bind(); glDrawArrays(GL_POINTS, 0, static_cast(m_scene.number_of_elements(GS::POS_POINTS))); }; enum { DRAW_ALL = -1, // draw all DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane }; if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); } else { renderer(DRAW_ALL); } rendering_program_p_l.release(); } } if(m_draw_edges && !m_draw_mesh_triangles) { if (m_draw_cylinder_edge && m_geometry_feature_enabled) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_cylinder.bind(); if (m_use_default_color) { auto edge_color = m_scene.get_default_color_segment(); color = QVector3D((double)edge_color.red()/(double)255, (double)edge_color.green()/(double)255, (double)edge_color.blue()/(double)255); rendering_program_cylinder.setUniformValue("u_DefaultColor", color); rendering_program_cylinder.setUniformValue("u_UseDefaultColor", static_cast(1)); } else { rendering_program_cylinder.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_cylinder.setUniformValue("u_Radius", static_cast(sceneRadius()*m_size_edges*0.001)); rendering_program_cylinder.setUniformValue("u_ClipPlane", clipPlane); rendering_program_cylinder.setUniformValue("u_PointPlane", plane_point); rendering_program_cylinder.setUniformValue("u_RenderingMode", rendering_mode); vao[VAO_SEGMENTS].bind(); glDrawArrays(GL_LINES, 0, static_cast(m_scene.number_of_elements(GS::POS_SEGMENTS))); }; enum { DRAW_ALL = -1, // draw all DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane }; if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); } else { renderer(DRAW_ALL); } rendering_program_cylinder.release(); } else { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { QVector2D viewport = { CGAL_BASIC_VIEWER_INIT_SIZE_X, CGAL_BASIC_VIEWER_INIT_SIZE_Y }; rendering_program_line.bind(); if (m_use_default_color) { auto edge_color = m_scene.get_default_color_segment(); color = QVector3D((double)edge_color.red()/(double)255, (double)edge_color.green()/(double)255, (double)edge_color.blue()/(double)255); rendering_program_line.setUniformValue("u_DefaultColor", color); rendering_program_line.setUniformValue("u_UseDefaultColor", static_cast(1)); } else { rendering_program_line.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_line.setUniformValue("u_PointSize", static_cast(m_size_edges)); rendering_program_line.setUniformValue("u_IsOrthographic", static_cast(is_two_dimensional())); rendering_program_line.setUniformValue("u_Viewport", viewport); rendering_program_line.setUniformValue("u_ClipPlane", clipPlane); rendering_program_line.setUniformValue("u_PointPlane", plane_point); rendering_program_line.setUniformValue("u_RenderingMode", rendering_mode); vao[VAO_SEGMENTS].bind(); glDrawArrays(GL_LINES, 0, static_cast(m_scene.number_of_elements(GS::POS_SEGMENTS))); }; enum { DRAW_ALL = -1, // draw all DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane }; if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); } else { renderer(DRAW_ALL); } rendering_program_line.release(); } } if(m_draw_rays) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_p_l.bind(); if (m_use_default_color) { auto ray_color = m_scene.get_default_color_ray(); color = QVector3D((double)ray_color.red()/(double)255, (double)ray_color.green()/(double)255, (double)ray_color.blue()/(double)255); rendering_program_p_l.setUniformValue("u_DefaultColor", color); rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(1)); } else { rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_p_l.setUniformValue("u_PointSize", GLfloat(m_size_rays)); rendering_program_p_l.setUniformValue("u_IsOrthographic", GLint(is_two_dimensional())); rendering_program_p_l.setUniformValue("u_ClipPlane", clipPlane); rendering_program_p_l.setUniformValue("u_PointPlane", plane_point); rendering_program_p_l.setUniformValue("u_RenderingMode", rendering_mode); vao[VAO_RAYS].bind(); glDrawArrays(GL_LINES, 0, static_cast(m_scene.number_of_elements(GS::POS_RAYS))); }; enum { DRAW_ALL = -1, // draw all DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane }; if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); } else { renderer(DRAW_ALL); } rendering_program_p_l.release(); } if(m_draw_lines) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_p_l.bind(); if (m_use_default_color) { auto line_color = m_scene.get_default_color_line(); color = QVector3D((double)line_color.red()/(double)255, (double)line_color.green()/(double)255, (double)line_color.blue()/(double)255); rendering_program_p_l.setUniformValue("u_DefaultColor", color); rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(1)); } else { rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_p_l.setUniformValue("u_PointSize", GLfloat(m_size_lines)); rendering_program_p_l.setUniformValue("u_IsOrthographic", GLint(is_two_dimensional())); rendering_program_p_l.setUniformValue("u_ClipPlane", clipPlane); rendering_program_p_l.setUniformValue("u_PointPlane", plane_point); rendering_program_p_l.setUniformValue("u_RenderingMode", rendering_mode); vao[VAO_LINES].bind(); glDrawArrays(GL_LINES, 0, static_cast(m_scene.number_of_elements(GS::POS_LINES))); }; enum { DRAW_ALL = -1, // draw all DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane }; if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); } else { renderer(DRAW_ALL); } rendering_program_p_l.release(); } // Fix Z-fighting by drawing faces at a depth GLfloat offset_factor; GLfloat offset_units; if (is_two_dimensional()) { glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &offset_factor); glGetFloatv(GL_POLYGON_OFFSET_UNITS, &offset_units); glPolygonOffset(0.1f, 0.9f); } if (m_draw_faces) { // reference: https://stackoverflow.com/questions/37780345/opengl-how-to-create-order-independent-transparency // rendering_mode == -1: draw all as solid; // rendering_mode == 0: draw solid only; // rendering_mode == 1: draw transparent only; auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(2.0, 2.0); glDepthFunc(GL_LESS); rendering_program_face.bind(); if (m_use_default_color) { auto face_color = m_scene.get_default_color_face(); color = QVector3D((double)face_color.red()/(double)255, (double)face_color.green()/(double)255, (double)face_color.blue()/(double)255); rendering_program_face.setUniformValue("u_DefaultColor", color); rendering_program_face.setUniformValue("u_UseDefaultColor", static_cast(1)); } else { rendering_program_face.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_face.setUniformValue("u_RenderingMode", rendering_mode); rendering_program_face.setUniformValue("u_RenderingTransparency", clipping_plane_rendering_transparency); rendering_program_face.setUniformValue("u_ClipPlane", clipPlane); rendering_program_face.setUniformValue("u_PointPlane", plane_point); vao[VAO_FACES].bind(); glDrawArrays(GL_TRIANGLES, 0, static_cast(m_scene.number_of_elements(GS::POS_FACES))); glDisable(GL_POLYGON_OFFSET_FILL); }; auto renderer_clipping_plane = [this](bool clipping_plane_rendering) { if (!isOpenGL_4_3()) return; if (!clipping_plane_rendering) return; // render clipping plane here rendering_program_clipping_plane.bind(); vao[VAO_CLIPPING_PLANE].bind(); glLineWidth(0.1f); glDrawArrays(GL_LINES, 0, static_cast((m_array_for_clipping_plane.size()/3))); glLineWidth(1.0f); vao[VAO_CLIPPING_PLANE].release(); rendering_program_clipping_plane.release(); }; enum { DRAW_SOLID_ALL = -1, // draw all mesh in solid mode DRAW_SOLID_HALF, // draw only the mesh inside the clipping plane as solid DRAW_TRANSPARENT_HALF // draw only the mesh outside the clipping plane as transparent }; if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_TRANSPARENT_HALF) { // The z-buffer will prevent transparent objects from being displayed behind other transparent objects. // Before rendering all transparent objects, disable z-testing first. // 1. draw solid first renderer(DRAW_SOLID_HALF); // 2. draw transparent layer second with back face culling to avoid messy triangles glDepthMask(false); //disable z-testing glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CW); renderer(DRAW_TRANSPARENT_HALF); // 3. draw solid again without culling and blend to make sure the solid mesh is visible glDepthMask(true); //enable z-testing glDisable(GL_CULL_FACE); glDisable(GL_BLEND); renderer(DRAW_SOLID_HALF); // 4. render clipping plane here renderer_clipping_plane(clipping_plane_rendering); } else if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_WIRE_HALF || m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { // 1. draw solid HALF renderer(DRAW_SOLID_HALF); // 2. render clipping plane here renderer_clipping_plane(clipping_plane_rendering); } else { // 1. draw solid FOR ALL renderer(DRAW_SOLID_ALL); } if (is_two_dimensional()) glPolygonOffset(offset_factor, offset_units); rendering_program_face.release(); } if (m_draw_normals) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_normal.bind(); if (m_use_default_color_normal) { color = QVector3D((double)m_default_color_normal.red()/(double)255, (double)m_default_color_normal.green()/(double)255, (double)m_default_color_normal.blue()/(double)255); rendering_program_normal.setUniformValue("u_DefaultColor", color); rendering_program_normal.setUniformValue("u_UseDefaultColor", static_cast(1)); } else { rendering_program_normal.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_normal.setUniformValue("u_Factor", static_cast(m_height_factor_normals)); rendering_program_normal.setUniformValue("u_SceneRadius", static_cast(sceneRadius())); if (m_display_face_normal) { rendering_program_normal.setUniformValue("u_DisplayFaceNormal", static_cast(1)); } else { rendering_program_normal.setUniformValue("u_DisplayFaceNormal", static_cast(0)); } rendering_program_normal.setUniformValue("u_ClipPlane", clipPlane); rendering_program_normal.setUniformValue("u_PointPlane", plane_point); rendering_program_normal.setUniformValue("u_RenderingMode", rendering_mode); vao[VAO_FACES].bind(); glLineWidth(m_size_normals); glDrawArrays(GL_TRIANGLES, 0, static_cast(m_scene.number_of_elements(GS::POS_FACES))); }; enum { DRAW_ALL = -1, // draw all DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane }; if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); } else { renderer(DRAW_ALL); } rendering_program_normal.release(); } if (m_draw_mesh_triangles) { auto renderer = [this, &clipPlane, &plane_point](float rendering_mode) { rendering_program_triangle.bind(); rendering_program_triangle.setUniformValue("u_RenderingMode", rendering_mode); rendering_program_triangle.setUniformValue("u_ClipPlane", clipPlane); rendering_program_triangle.setUniformValue("u_PointPlane", plane_point); vao[VAO_FACES].bind(); glDrawArrays(GL_TRIANGLES, 0, static_cast(m_scene.number_of_elements(GS::POS_FACES))); }; enum { DRAW_ALL = -1, // draw all DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane }; if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); } else { renderer(DRAW_ALL); } rendering_program_triangle.release(); } if (m_draw_text) { glDisable(GL_LIGHTING); for (std::size_t i=0; iprojectedCoordinatesOf (CGAL::qglviewer::Vec(std::get<0>(m_texts_vec[i]).x(), std::get<0>(m_texts_vec[i]).y(), std::get<0>(m_texts_vec[i]).z())); drawText((int)screenPos[0], (int)screenPos[1], QString(std::get<1>(m_texts_vec[i]).c_str())); } glEnable(GL_LIGHTING); } // Multiply matrix to get in the frame coordinate system. // glMultMatrixd(manipulatedFrame()->matrix()); // Linker error // Scale down the drawings // glScalef(0.3f, 0.3f, 0.3f); // Linker error // Draw an axis using the QGLViewer static function // drawAxis(); } protected: void compile_shaders() { rendering_program_face.removeAllShaders(); rendering_program_p_l.removeAllShaders(); rendering_program_line.removeAllShaders(); rendering_program_clipping_plane.removeAllShaders(); rendering_program_sphere.removeAllShaders(); rendering_program_cylinder.removeAllShaders(); rendering_program_normal.removeAllShaders(); rendering_program_triangle.removeAllShaders(); // Create the buffers for (unsigned int i=0; icompileSourceCode(source_)) { std::cerr<<"Compiling vertex source FAILED"<compileSourceCode(source_)) { std::cerr<<"Compiling fragment source FAILED"<compileSourceCode(source_)) { std::cerr<<"Compiling vertex source FAILED"<compileSourceCode(source_)) { std::cerr<<"Compiling fragment source FAILED"<compileSourceCode(source_)) { std::cerr << "Compiling vertex source for clipping plane FAILED" << std::endl; } source_ = FRAGMENT_SOURCE_CLIPPING_PLANE; QOpenGLShader *fragment_shader_clipping_plane = new QOpenGLShader(QOpenGLShader::Fragment); if (!fragment_shader_clipping_plane->compileSourceCode(source_)) { std::cerr << "Compiling fragment source for clipping plane FAILED" << std::endl; } if (!rendering_program_clipping_plane.addShader(vertex_shader_clipping_plane)) { std::cerr << "Adding vertex shader for clipping plane FAILED" << std::endl;} if (!rendering_program_clipping_plane.addShader(fragment_shader_clipping_plane)) { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } if (!rendering_program_clipping_plane.link()) { std::cerr << "Linking Program for clipping plane FAILED" << std::endl; } } // source_ = isOpenGL_4_3() // ? VERTEX_SOURCE_CLIPPING_PLANE // : vertex_source_clipping_plane_comp; // QOpenGLShader *vertex_shader_clipping_plane = new QOpenGLShader(QOpenGLShader::Vertex); // if (!vertex_shader_clipping_plane->compileSourceCode(source_)) // { std::cerr << "Compiling vertex source for clipping plane FAILED" << std::endl; } // source_ = isOpenGL_4_3() // ? FRAGMENT_SOURCE_CLIPPING_PLANE // : fragment_source_clipping_plane_comp; // QOpenGLShader *fragment_shader_clipping_plane = new QOpenGLShader(QOpenGLShader::Fragment); // if (!fragment_shader_clipping_plane->compileSourceCode(source_)) // { std::cerr << "Compiling fragment source for clipping plane FAILED" << std::endl; } // if (!rendering_program_clipping_plane.addShader(vertex_shader_clipping_plane)) // { std::cerr << "Adding vertex shader for clipping plane FAILED" << std::endl;} // if (!rendering_program_clipping_plane.addShader(fragment_shader_clipping_plane)) // { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } // if (!rendering_program_clipping_plane.link()) // { std::cerr << "Linking Program for clipping plane FAILED" << std::endl; } // Sphere shader if (isOpenGL_4_3()) { source_ = VERTEX_SOURCE_SHAPE; QOpenGLShader *vertex_shader_sphere = new QOpenGLShader(QOpenGLShader::Vertex); if (!vertex_shader_sphere->compileSourceCode(source_)) { std::cerr << "Compiling vertex source for sphere FAILED" << std::endl; } source_ = GEOMETRY_SOURCE_SPHERE; QOpenGLShader *geometry_shader_sphere = new QOpenGLShader(QOpenGLShader::Geometry); if (!geometry_shader_sphere->compileSourceCode(source_)) { std::cerr << "Compiling geometry source for sphere FAILED" << std::endl; } source_ = FRAGMENT_SOURCE_P_L; QOpenGLShader *fragment_shader_sphere = new QOpenGLShader(QOpenGLShader::Fragment); if (!fragment_shader_sphere->compileSourceCode(source_)) { std::cerr << "Compiling fragment source for sphere FAILED" << std::endl; } if (!rendering_program_sphere.addShader(vertex_shader_sphere)) { std::cerr << "Adding vertex shader for sphere FAILED" << std::endl;} if (!rendering_program_sphere.addShader(geometry_shader_sphere)) { std::cerr << "Adding geometry shader for sphere FAILED" << std::endl;} if (!rendering_program_sphere.addShader(fragment_shader_sphere)) { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } if (!rendering_program_sphere.link()) { std::cerr << "Linking Program for sphere FAILED" << std::endl; } } // Cylinder shader if (isOpenGL_4_3()) { // clipping plane shader source_ = VERTEX_SOURCE_SHAPE; QOpenGLShader *vertex_shader_cylinder = new QOpenGLShader(QOpenGLShader::Vertex); if (!vertex_shader_cylinder->compileSourceCode(source_)) { std::cerr << "Compiling vertex source for cylinder FAILED" << std::endl; } source_ = GEOMETRY_SOURCE_CYLINDER; QOpenGLShader *geometry_shader_cylinder = new QOpenGLShader(QOpenGLShader::Geometry); if (!geometry_shader_cylinder->compileSourceCode(source_)) { std::cerr << "Compiling geometry source for cylinder FAILED" << std::endl; } source_ = FRAGMENT_SOURCE_P_L; QOpenGLShader *fragment_shader_cylinder = new QOpenGLShader(QOpenGLShader::Fragment); if (!fragment_shader_cylinder->compileSourceCode(source_)) { std::cerr << "Compiling fragment source for cylinder FAILED" << std::endl; } if (!rendering_program_cylinder.addShader(vertex_shader_cylinder)) { std::cerr << "Adding vertex shader for cylinder FAILED" << std::endl;} if (!rendering_program_cylinder.addShader(geometry_shader_cylinder)) { std::cerr << "Adding geometry shader for cylinder FAILED" << std::endl;} if (!rendering_program_cylinder.addShader(fragment_shader_cylinder)) { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } if (!rendering_program_cylinder.link()) { std::cerr << "Linking Program for cylinder FAILED" << std::endl; } } // Normal shader if (isOpenGL_4_3()) { source_ = VERTEX_SOURCE_NORMAL; QOpenGLShader *vertex_shader_normal = new QOpenGLShader(QOpenGLShader::Vertex); if (!vertex_shader_normal->compileSourceCode(source_)) { std::cerr << "Compiling vertex source for normal FAILED" << std::endl; } source_ = GEOMETRY_SOURCE_NORMAL; QOpenGLShader *geometry_shader_normal = new QOpenGLShader(QOpenGLShader::Geometry); if (!geometry_shader_normal->compileSourceCode(source_)) { std::cerr << "Compiling geometry source for normal FAILED" << std::endl; } source_ = FRAGMENT_SOURCE_P_L; QOpenGLShader *fragment_shader_normal = new QOpenGLShader(QOpenGLShader::Fragment); if (!fragment_shader_normal->compileSourceCode(source_)) { std::cerr << "Compiling fragment source for normal FAILED" << std::endl; } if (!rendering_program_normal.addShader(vertex_shader_normal)) { std::cerr << "Adding vertex shader for normal FAILED" << std::endl;} if (!rendering_program_normal.addShader(geometry_shader_normal)) { std::cerr << "Adding geometry shader for normal FAILED" << std::endl;} if (!rendering_program_normal.addShader(fragment_shader_normal)) { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } if (!rendering_program_normal.link()) { std::cerr << "Linking Program for normal FAILED" << std::endl; } } // Normal shader if (isOpenGL_4_3()) { source_ = VERTEX_SOURCE_TRIANGLE; QOpenGLShader *vertex_shader_triangle = new QOpenGLShader(QOpenGLShader::Vertex); if (!vertex_shader_triangle->compileSourceCode(source_)) { std::cerr << "Compiling vertex source for triangle FAILED" << std::endl; } source_ = GEOMETRY_SOURCE_TRIANGLE; QOpenGLShader *geometry_shader_triangle = new QOpenGLShader(QOpenGLShader::Geometry); if (!geometry_shader_triangle->compileSourceCode(source_)) { std::cerr << "Compiling geometry source for triangle FAILED" << std::endl; } source_ = FRAGMENT_SOURCE_P_L; QOpenGLShader *fragment_shader_triangle = new QOpenGLShader(QOpenGLShader::Fragment); if (!fragment_shader_triangle->compileSourceCode(source_)) { std::cerr << "Compiling fragment source for triangle FAILED" << std::endl; } if (!rendering_program_triangle.addShader(vertex_shader_triangle)) { std::cerr << "Adding vertex shader for triangle FAILED" << std::endl;} if (!rendering_program_triangle.addShader(geometry_shader_triangle)) { std::cerr << "Adding geometry shader for triangle FAILED" << std::endl;} if (!rendering_program_triangle.addShader(fragment_shader_triangle)) { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } if (!rendering_program_triangle.link()) { std::cerr << "Linking Program for triangle FAILED" << std::endl; } } // Line shader if (isOpenGL_4_3()) { source_ = VERTEX_SOURCE_LINE_WIDTH; QOpenGLShader *vertex_shader_line = new QOpenGLShader(QOpenGLShader::Vertex); if (!vertex_shader_line->compileSourceCode(source_)) { std::cerr << "Compiling vertex source for line FAILED" << std::endl; } source_ = GEOMETRY_SOURCE_LINE_WIDTH; QOpenGLShader *geometry_shader_line = new QOpenGLShader(QOpenGLShader::Geometry); if (!geometry_shader_line->compileSourceCode(source_)) { std::cerr << "Compiling geometry source for line FAILED" << std::endl; } source_ = FRAGMENT_SOURCE_P_L; QOpenGLShader *fragment_shader_line = new QOpenGLShader(QOpenGLShader::Fragment); if (!fragment_shader_line->compileSourceCode(source_)) { std::cerr << "Compiling fragment source for line FAILED" << std::endl; } if (!rendering_program_line.addShader(vertex_shader_line)) { std::cerr << "Adding vertex shader for line FAILED" << std::endl;} if (!rendering_program_line.addShader(geometry_shader_line)) { std::cerr << "Adding geometry shader for line FAILED" << std::endl;} if (!rendering_program_line.addShader(fragment_shader_line)) { std::cerr << "Adding fragment shader for line FAILED" << std::endl; } if (!rendering_program_line.link()) { std::cerr << "Linking Program for line FAILED" << std::endl; } } } void initialize_buffers() { set_camera_mode(); rendering_program_p_l.bind(); unsigned int bufn = 0; std::vector positions, normals, colors; // 1) POINT SHADER vao[VAO_POINTS].bind(); positions = m_scene.get_array_of_index(GS::POS_POINTS); colors = m_scene.get_array_of_index(GS::COLOR_POINTS); CGAL_assertion(bufn(positions.size()*sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Pos"); rendering_program_p_l.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); ++bufn; CGAL_assertion(bufn(colors.size()*sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Color"); rendering_program_p_l.setAttributeBuffer("a_Color",GL_FLOAT,0,3); // 2) SEGMENT SHADER vao[VAO_SEGMENTS].bind(); positions = m_scene.get_array_of_index(GS::POS_SEGMENTS); colors = m_scene.get_array_of_index(GS::COLOR_SEGMENTS); ++bufn; CGAL_assertion(bufn(positions.size()*sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Pos"); rendering_program_p_l.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); ++bufn; CGAL_assertion(bufn(colors.size()*sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Color"); rendering_program_p_l.setAttributeBuffer("a_Color",GL_FLOAT,0,3); // 3) RAYS SHADER vao[VAO_RAYS].bind(); positions = m_scene.get_array_of_index(GS::POS_RAYS); colors = m_scene.get_array_of_index(GS::COLOR_RAYS); ++bufn; CGAL_assertion(bufn(positions.size()*sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Pos"); rendering_program_p_l.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); ++bufn; CGAL_assertion(bufn(colors.size()*sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Color"); rendering_program_p_l.setAttributeBuffer("a_Color",GL_FLOAT,0,3); // 4) LINES SHADER vao[VAO_LINES].bind(); positions = m_scene.get_array_of_index(GS::POS_LINES); colors = m_scene.get_array_of_index(GS::COLOR_LINES); ++bufn; CGAL_assertion(bufn(positions.size()*sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Pos"); rendering_program_p_l.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); ++bufn; CGAL_assertion(bufn(colors.size()*sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Color"); rendering_program_p_l.setAttributeBuffer("a_Color",GL_FLOAT,0,3); // 5) FACE SHADER vao[VAO_FACES].bind(); positions = m_scene.get_array_of_index(GS::POS_FACES); normals = m_scene.get_array_of_index( m_flat_shading ? GS::FLAT_NORMAL_FACES : GS::SMOOTH_NORMAL_FACES ); colors = m_scene.get_array_of_index(GS::COLOR_FACES); ++bufn; CGAL_assertion(bufn(positions.size()*sizeof(float))); rendering_program_face.enableAttributeArray("a_Pos"); rendering_program_face.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); ++bufn; CGAL_assertion(bufn(normals.size()*sizeof(float))); rendering_program_face.enableAttributeArray("a_Normal"); rendering_program_face.setAttributeBuffer("a_Normal",GL_FLOAT,0,3); ++bufn; CGAL_assertion(bufn(colors.size()*sizeof(float))); rendering_program_face.enableAttributeArray("a_Color"); rendering_program_face.setAttributeBuffer("a_Color",GL_FLOAT,0,3); // 6) clipping plane shader if (isOpenGL_4_3()) { generate_clipping_plane(); rendering_program_clipping_plane.bind(); vao[VAO_CLIPPING_PLANE].bind(); ++bufn; CGAL_assertion(bufn < NB_GL_BUFFERS); buffers[bufn].bind(); buffers[bufn].allocate(m_array_for_clipping_plane.data(), static_cast(m_array_for_clipping_plane.size()*sizeof(BufferType))); rendering_program_clipping_plane.enableAttributeArray("a_Pos"); rendering_program_clipping_plane.setAttributeBuffer("a_Pos", GL_FLOAT, 0, 3); buffers[bufn].release(); rendering_program_clipping_plane.release(); } m_are_buffers_initialized = true; } void attrib_buffers(CGAL::QGLViewer* viewer) { QMatrix4x4 mvpMatrix; QMatrix4x4 mvMatrix; double mat[16]; viewer->camera()->getModelViewProjectionMatrix(mat); for(unsigned int i=0; i < 16; i++) { mvpMatrix.data()[i] = (float)mat[i]; } viewer->camera()->getModelViewMatrix(mat); for(unsigned int i=0; i < 16; i++) { mvMatrix.data()[i] = (float)mat[i]; } // define material QVector4D diffuse( 0.9f, 0.9f, 0.9f, 0.9f ); QVector4D specular( 0.0f, 0.0f, 0.0f, 1.0f ); CGAL::Bbox_3 bb; if (bb==m_scene.bounding_box()) // Case of "empty" bounding box { bb=Local_point(CGAL::ORIGIN).bbox(); bb=bb + Local_point(1,1,1).bbox(); // To avoid a warning from Qglviewer } else { bb=m_scene.bounding_box(); } QVector4D position((bb.xmax()-bb.xmin())/2, (bb.ymax()-bb.ymin())/2, bb.zmax(), 0.0); GLfloat shininess = 1.0f; rendering_program_face.bind(); int mvpLocation = rendering_program_face.uniformLocation("u_Mvp"); int mvLocation = rendering_program_face.uniformLocation("u_Mv"); int lightLocation[5]; lightLocation[0] = rendering_program_face.uniformLocation("u_LightPos"); lightLocation[1] = rendering_program_face.uniformLocation("u_LightDiff"); lightLocation[2] = rendering_program_face.uniformLocation("u_LightSpec"); lightLocation[3] = rendering_program_face.uniformLocation("u_LightAmb"); lightLocation[4] = rendering_program_face.uniformLocation("u_SpecPower"); rendering_program_face.setUniformValue(lightLocation[0], position); rendering_program_face.setUniformValue(lightLocation[1], diffuse); rendering_program_face.setUniformValue(lightLocation[2], specular); rendering_program_face.setUniformValue(lightLocation[3], m_ambient_color); rendering_program_face.setUniformValue(lightLocation[4], shininess); rendering_program_face.setUniformValue(mvpLocation, mvpMatrix); rendering_program_face.setUniformValue(mvLocation, mvMatrix); rendering_program_face.release(); rendering_program_p_l.bind(); mvpLocation = rendering_program_p_l.uniformLocation("u_Mvp"); rendering_program_p_l.setUniformValue(mvpLocation, mvpMatrix); rendering_program_p_l.release(); // cylinder edge feature rendering_program_cylinder.bind(); mvpLocation = rendering_program_cylinder.uniformLocation("u_Mvp"); rendering_program_cylinder.setUniformValue(mvpLocation, mvpMatrix); rendering_program_cylinder.release(); // sphere vertex feature rendering_program_sphere.bind(); mvpLocation = rendering_program_sphere.uniformLocation("u_Mvp"); rendering_program_sphere.setUniformValue(mvpLocation, mvpMatrix); rendering_program_sphere.release(); if (isOpenGL_4_3()) { QMatrix4x4 clipping_mMatrix; clipping_mMatrix.setToIdentity(); for(int i=0; i< 16 ; i++) { clipping_mMatrix.data()[i] = m_frame_plane->matrix()[i]; } rendering_program_clipping_plane.bind(); int vpLocation = rendering_program_clipping_plane.uniformLocation("u_Vp"); int mLocation = rendering_program_clipping_plane.uniformLocation("u_M"); rendering_program_clipping_plane.setUniformValue(vpLocation, mvpMatrix); rendering_program_clipping_plane.setUniformValue(mLocation, clipping_mMatrix); rendering_program_clipping_plane.release(); } if (isOpenGL_4_3()) { rendering_program_normal.bind(); QMatrix4x4 projection; double mat[16]; viewer->camera()->getProjectionMatrix(mat); for(unsigned int i=0; i < 16; i++) { projection.data()[i] = (float)mat[i]; } int mvLocation = rendering_program_normal.uniformLocation("u_Mv"); int pLocation = rendering_program_normal.uniformLocation("u_Projection"); rendering_program_normal.setUniformValue(mvLocation, mvMatrix); rendering_program_normal.setUniformValue(pLocation, projection); rendering_program_normal.release(); } if (isOpenGL_4_3()) { rendering_program_triangle.bind(); int mvpLocation = rendering_program_triangle.uniformLocation("u_Mvp"); rendering_program_triangle.setUniformValue(mvpLocation, mvpMatrix); rendering_program_triangle.release(); } if (isOpenGL_4_3()) { rendering_program_line.bind(); int mvpLocation = rendering_program_line.uniformLocation("u_Mvp"); rendering_program_line.setUniformValue(mvpLocation, mvpMatrix); rendering_program_line.release(); } } void set_camera_mode() { if (is_two_dimensional()) { camera()->setType(CGAL::qglviewer::Camera::ORTHOGRAPHIC); // Camera Constraint: constraint.setRotationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::AXIS); constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); double cx=0., cy=0., cz=0.; if (m_scene.has_zero_x()) { cx=1.; } else if (m_scene.has_zero_y()) { cy=1.; } else { cz=1.; } camera()->setViewDirection(CGAL::qglviewer::Vec(-cx,-cy,-cz)); constraint.setRotationConstraintDirection(CGAL::qglviewer::Vec(cx, cy, cz)); camera()->frame()->setConstraint(&constraint); } else { camera()->setType(CGAL::qglviewer::Camera::PERSPECTIVE); camera()->frame()->setConstraint(nullptr); } } virtual void init() { set_camera_mode(); initializeOpenGLFunctions(); // Light default parameters glLineWidth(m_size_edges); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.f,1.f); glClearColor(1.0f,1.0f,1.0f,0.0f); glDisable(GL_BLEND); glEnable(GL_PROGRAM_POINT_SIZE); glEnable(GL_LINE_SMOOTH); glDisable(GL_POLYGON_SMOOTH_HINT); glBlendFunc(GL_ONE, GL_ZERO); glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST); compile_shaders(); CGAL::Bbox_3 bb; if (bb==m_scene.bounding_box()) // Case of "empty" bounding box { bb=Local_point(CGAL::ORIGIN).bbox(); bb=bb + Local_point(1,1,1).bbox(); // To avoid a warning from Qglviewer } else { bb=m_scene.bounding_box(); } this->camera()->setSceneBoundingBox(CGAL::qglviewer::Vec(bb.xmin(), bb.ymin(), bb.zmin()), CGAL::qglviewer::Vec(bb.xmax(), bb.ymax(), bb.zmax())); m_frame_plane=new CGAL::qglviewer::ManipulatedFrame; // Check for geometry shader availability int max_geometry_output_vertices = 0; glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &max_geometry_output_vertices); int max_geometry_output_components = 0; glGetIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &max_geometry_output_components); if (max_geometry_output_vertices < 128 || max_geometry_output_components < 1024) { std::cout << "Cylinder edge and sphere vertex feature disabled! (max_geometry_output_vertices=" << max_geometry_output_vertices << ", max_geometry_output_components=" << max_geometry_output_components << ")\n"; m_geometry_feature_enabled = false; } /// This code cannot be done in the constructor, because the Graphics_scene /// is not yet created (cf. for example LCC demo). if (m_inverse_normal) { reverse_all_normals(); } initialize_vertices_and_edges_size(); this->showEntireScene(); } void initialize_vertices_and_edges_size() { if(!m_scene.empty()) { auto& bbox=m_scene.bounding_box(); double d=CGAL::sqrt(CGAL::squared_distance (Local_point(bbox.xmin(), bbox.ymin(), bbox.zmin()), Local_point(bbox.xmax(), bbox.ymax(), bbox.zmax()))); // std::cout<<"Length of the diagonal: "<modifiers()==::Qt::ShiftModifier) && (e->button()==::Qt::LeftButton)) { if (manipulatedFrame()) { camera()->frame()->alignWithFrame(manipulatedFrame(), true); } } else { CGAL::QGLViewer::mouseDoubleClickEvent(e); } } virtual void keyPressEvent(QKeyEvent *e) { if(!on_key_pressed || !on_key_pressed(e, this)) { const ::Qt::KeyboardModifiers modifiers = e->modifiers(); if ((e->key()==::Qt::Key_C) && (modifiers==::Qt::NoButton)) { if (!isOpenGL_4_3()) return; if (!is_two_dimensional()) { // toggle clipping plane m_use_clipping_plane = (m_use_clipping_plane + 1) % CLIPPING_PLANE_END_INDEX; if (m_use_clipping_plane==CLIPPING_PLANE_OFF) { setManipulatedFrame(nullptr); } else { setManipulatedFrame(m_frame_plane); } switch(m_use_clipping_plane) { case CLIPPING_PLANE_OFF: displayMessage(QString("Draw clipping = false")); break; case CLIPPING_PLANE_SOLID_HALF_TRANSPARENT_HALF: clipping_plane_rendering=true; displayMessage(QString("Draw clipping = solid half & transparent half")); break; case CLIPPING_PLANE_SOLID_HALF_WIRE_HALF: displayMessage(QString("Draw clipping = solid half & wireframe half")); break; case CLIPPING_PLANE_SOLID_HALF_ONLY: displayMessage(QString("Draw clipping = solid half only")); break; default: break; } update(); } } else if ((e->key()==::Qt::Key_C) && (modifiers==::Qt::AltModifier)) { if (!isOpenGL_4_3()) return; if (m_use_clipping_plane!=CLIPPING_PLANE_OFF) { clipping_plane_rendering = !clipping_plane_rendering; displayMessage(QString("Draw clipping plane=%1.").arg(clipping_plane_rendering?"true":"false")); update(); } } else if ((e->key()==::Qt::Key_E) && (modifiers==::Qt::NoButton)) { m_draw_edges=!m_draw_edges; displayMessage(QString("Draw edges=%1.").arg(m_draw_edges?"true":"false")); update(); } else if ((e->key()==::Qt::Key_M) && (modifiers==::Qt::NoButton)) { m_use_default_color=!m_use_default_color; displayMessage(QString("Mono color=%1.").arg(m_use_default_color?"true":"false")); update(); } else if ((e->key()==::Qt::Key_N) && (modifiers==::Qt::NoButton)) { reverse_all_normals(); displayMessage(QString("Inverse normal=%1.").arg(m_inverse_normal?"true":"false")); redraw(); } else if ((e->key()==::Qt::Key_S) && (modifiers==::Qt::NoButton)) { m_flat_shading=!m_flat_shading; if (m_flat_shading) displayMessage("Flat shading."); else displayMessage("Gouraud shading."); redraw(); } else if ((e->key()==::Qt::Key_T) && (modifiers==::Qt::NoButton)) { m_draw_text=!m_draw_text; displayMessage(QString("Draw text=%1.").arg(m_draw_text?"true":"false")); update(); } else if ((e->key()==::Qt::Key_U) && (modifiers==::Qt::NoButton)) { if (is_two_dimensional()) { displayMessage(QString("Move camera direction upside down.")); /* CGAL::qglviewer::Vec cur=camera()->viewDirection(); // TODO ! double cx=cur.x, cy=cur.y, cz=cur.z; if (has_zero_x()) { cx=-cx; } else if (has_zero_y()) { cy=-cy; } else { cz=-cz; } double cx=0., cy=0., cz=0.; if (has_zero_x()) { cx=(cur.x<0?-1.:1); } else if (has_zero_y()) { cy=(cur.y<0?-1.:1); } else { cz=(cur.z<0?-1.:1); }*/ camera()->setUpVector(-camera()->upVector()); //camera()->frame()->setConstraint(NULL); // camera()->setViewDirection(CGAL::qglviewer::Vec(-cx,-cy,-cz)); //constraint.setRotationConstraintDirection(CGAL::qglviewer::Vec(cx, cy, cz)); //camera()->frame()->setConstraint(&constraint); //update(); redraw(); } } else if ((e->key()==::Qt::Key_V) && (modifiers==::Qt::NoButton)) { m_draw_vertices=!m_draw_vertices; displayMessage(QString("Draw vertices=%1.").arg(m_draw_vertices?"true":"false")); update(); } else if ((e->key()==::Qt::Key_W) && (modifiers==::Qt::NoButton)) { m_draw_faces=!m_draw_faces; displayMessage(QString("Draw faces=%1.").arg(m_draw_faces?"true":"false")); update(); } else if ((e->key()==::Qt::Key_Plus) && (!modifiers.testFlag(::Qt::ControlModifier))) // No ctrl { m_size_edges+=.5; displayMessage(QString("Size of edges=%1.").arg(m_size_edges)); update(); } else if ((e->key()==::Qt::Key_Minus) && (!modifiers.testFlag(::Qt::ControlModifier))) // No ctrl { if (m_size_edges>.5) m_size_edges-=.5; displayMessage(QString("Size of edges=%1.").arg(m_size_edges)); update(); } else if ((e->key()==::Qt::Key_Plus) && (modifiers.testFlag(::Qt::ControlModifier))) { m_size_vertices+=.5; displayMessage(QString("Size of points=%1.").arg(m_size_vertices)); update(); } else if ((e->key()==::Qt::Key_Minus) && (modifiers.testFlag(::Qt::ControlModifier))) { if (m_size_vertices>.5) m_size_vertices-=.5; displayMessage(QString("Size of points=%1.").arg(m_size_vertices)); update(); } else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::NoButton)) { m_ambient_color.setX(m_ambient_color.x()+.1); if (m_ambient_color.x()>1.) m_ambient_color.setX(1.); m_ambient_color.setY(m_ambient_color.x()+.1); if (m_ambient_color.y()>1.) m_ambient_color.setY(1.); m_ambient_color.setZ(m_ambient_color.x()+.1); if (m_ambient_color.z()>1.) m_ambient_color.setZ(1.); displayMessage(QString("Light color=(%1 %2 %3)."). arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); update(); } else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::NoButton)) { m_ambient_color.setX(m_ambient_color.x()-.1); if (m_ambient_color.x()<0.) m_ambient_color.setX(0.); m_ambient_color.setY(m_ambient_color.y()-.1); if (m_ambient_color.y()<0.) m_ambient_color.setY(0.); m_ambient_color.setZ(m_ambient_color.z()-.1); if (m_ambient_color.z()<0.) m_ambient_color.setZ(0.); displayMessage(QString("Light color=(%1 %2 %3)."). arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); update(); } else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::ShiftModifier)) { m_ambient_color.setX(m_ambient_color.x()+.1); if (m_ambient_color.x()>1.) m_ambient_color.setX(1.); displayMessage(QString("Light color=(%1 %2 %3)."). arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); update(); } else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::AltModifier)) { m_ambient_color.setY(m_ambient_color.y()+.1); if (m_ambient_color.y()>1.) m_ambient_color.setY(1.); displayMessage(QString("Light color=(%1 %2 %3)."). arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); update(); } else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::ControlModifier)) { m_ambient_color.setZ(m_ambient_color.z()+.1); if (m_ambient_color.z()>1.) m_ambient_color.setZ(1.); displayMessage(QString("Light color=(%1 %2 %3)."). arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); update(); } else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::ShiftModifier)) { m_ambient_color.setX(m_ambient_color.x()-.1); if (m_ambient_color.x()<0.) m_ambient_color.setX(0.); displayMessage(QString("Light color=(%1 %2 %3)."). arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); update(); } else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::AltModifier)) { m_ambient_color.setY(m_ambient_color.y()-.1); if (m_ambient_color.y()<0.) m_ambient_color.setY(0.); displayMessage(QString("Light color=(%1 %2 %3)."). arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); update(); } else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::ControlModifier)) { m_ambient_color.setZ(m_ambient_color.z()-.1); if (m_ambient_color.z()<0.) m_ambient_color.setZ(0.); displayMessage(QString("Light color=(%1 %2 %3)."). arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); update(); } else if ((e->key()==::Qt::Key_O) && (modifiers==::Qt::NoButton)) { bool old_2D=is_two_dimensional(); m_no_2D_mode=!m_no_2D_mode; if (old_2D!=is_two_dimensional()) { if (is_two_dimensional()) { displayMessage(QString("Viewer is in 2D mode.")); } else { displayMessage(QString("Viewer is in 3D mode.")); } set_camera_mode(); update(); } } else if ((e->key()==::Qt::Key_V) && (modifiers==::Qt::ControlModifier)) { m_draw_sphere_vertex = !m_draw_sphere_vertex; displayMessage(QString("Draw sphere vertex=%1.").arg(m_draw_sphere_vertex?"true":"false")); update(); } else if ((e->key()==::Qt::Key_E) && (modifiers==::Qt::ControlModifier)) { m_draw_cylinder_edge = !m_draw_cylinder_edge; displayMessage(QString("Draw cylinder edge=%1.").arg(m_draw_cylinder_edge?"true":"false")); update(); } else if ((e->key()==::Qt::Key_N) && (modifiers==::Qt::ControlModifier)) { m_draw_normals = !m_draw_normals; displayMessage(QString("Draw normals=%1.").arg(m_draw_normals?"true":"false")); update(); } else if ((e->key()==::Qt::Key_T) && (modifiers==::Qt::ControlModifier)) { m_draw_mesh_triangles = !m_draw_mesh_triangles; displayMessage(QString("Draw triangles=%1.").arg(m_draw_mesh_triangles?"true":"false")); update(); } else if ((e->key()==::Qt::Key_M) && (modifiers==::Qt::ControlModifier)) { m_use_default_color_normal = !m_use_default_color_normal; displayMessage(QString("Normal mono color=%1.").arg(m_use_default_color_normal?"true":"false")); update(); } else if ((e->key()==::Qt::Key_N) && (modifiers==::Qt::ShiftModifier)) { m_display_face_normal = !m_display_face_normal; displayMessage(QString("Display face normal=%1.").arg(m_display_face_normal?"true":"false")); update(); } else if ((e->key()==::Qt::Key_F2)) { capture_screenshot(QString("./screenshot.png")); displayMessage(QString("Screenshot saved in ./screenshot")); } else { CGAL::QGLViewer::keyPressEvent(e); } // By default call QGLViewer key press } } virtual QString helpString() const { return helpString("CGAL Basic Viewer"); } virtual QString helpString(const char* title) const { QString text(QString("

")+QString(title)+QString("

")); text += "Use the mouse to move the camera around the object. "; text += "You can respectively revolve around, zoom and translate with " "the three mouse buttons. "; text += "Left and middle buttons pressed together rotate around the " "camera view direction axis

"; text += "Pressing Alt and one of the function keys " "(F1..F12) defines a camera keyFrame. "; text += "Simply press the function key again to restore it. " "Several keyFrames define a "; text += "camera path. Paths are saved when you quit the application " "and restored at next start.

"; text += "Press F to display the frame rate, A for the " "world axis, "; text += "Alt+Return for full screen mode and Control+S " "to save a snapshot. "; text += "See the Keyboard tab in this window for a complete " "shortcut list.

"; text += "Double clicks automates single click actions: A left button " "double click aligns the closer axis with the camera (if close enough). "; text += "A middle button double click fits the zoom of the camera and " "the right button re-centers the scene.

"; text += "A left button double click while holding right button pressed " "defines the camera Revolve Around Point. "; text += "See the Mouse tab and the documentation web pages for " "details.

"; text += "Press Escape to exit the viewer."; return text; } void capture_screenshot(const QString& file_path) { QScreen *screen; screen = QApplication::primaryScreen(); // auto geom = screen->geometry(); auto qpx_pixmap = screen->grabWindow(this->winId()); qpx_pixmap.save(file_path); } public: std::function on_key_pressed; protected: const Graphics_scene& m_scene; bool m_draw_vertices; bool m_draw_edges; bool m_draw_rays; bool m_draw_lines; bool m_draw_faces; bool m_draw_text; bool m_draw_normals; bool m_draw_cylinder_edge; bool m_draw_sphere_vertex; bool m_draw_mesh_triangles; bool m_flat_shading; bool m_use_default_color; bool m_use_default_color_normal; bool m_display_face_normal; bool m_inverse_normal; bool m_no_2D_mode; bool m_geometry_feature_enabled; bool m_prev_scene_empty; enum { CLIPPING_PLANE_OFF = 0, CLIPPING_PLANE_SOLID_HALF_TRANSPARENT_HALF, CLIPPING_PLANE_SOLID_HALF_WIRE_HALF, CLIPPING_PLANE_SOLID_HALF_ONLY, CLIPPING_PLANE_END_INDEX }; int m_use_clipping_plane=CLIPPING_PLANE_OFF; CGAL::qglviewer::ManipulatedFrame* m_frame_plane=nullptr; // Buffer for clipping plane is not stored in the scene because it is not // filled by users but by the basic viewer. std::vector m_array_for_clipping_plane; double m_size_vertices=1.; double m_size_edges=1.; double m_size_rays=1.; double m_size_lines=1.; double m_size_normals=.2; double m_height_factor_normals=.02; CGAL::IO::Color m_default_color_normal; QVector4D m_ambient_color; bool m_are_buffers_initialized; // CGAL::qglviewer::LocalConstraint constraint; CGAL::qglviewer::WorldConstraint constraint; static const unsigned int NB_GL_BUFFERS=(GS::END_POS-GS::BEGIN_POS)+ (GS::END_COLOR-GS::BEGIN_COLOR)+3; // +2 for normals (mono and color), +1 for clipping plane QOpenGLBuffer buffers[NB_GL_BUFFERS]; // +1 for the buffer of clipping plane // The following enum gives the indices of the different vao. enum { VAO_POINTS=0, VAO_SEGMENTS, VAO_RAYS, VAO_LINES, VAO_FACES, VAO_CLIPPING_PLANE, NB_VAO_BUFFERS }; QOpenGLVertexArrayObject vao[NB_VAO_BUFFERS]; QOpenGLShaderProgram rendering_program_face; QOpenGLShaderProgram rendering_program_p_l; QOpenGLShaderProgram rendering_program_line; QOpenGLShaderProgram rendering_program_clipping_plane; QOpenGLShaderProgram rendering_program_sphere; QOpenGLShaderProgram rendering_program_cylinder; QOpenGLShaderProgram rendering_program_normal; QOpenGLShaderProgram rendering_program_triangle; // variables for clipping plane bool clipping_plane_rendering = true; // will be toggled when alt+c is pressed, which is used for indicating whether or not to render the clipping plane ; float clipping_plane_rendering_transparency = 0.5f; // to what extent the transparent part should be rendered; }; //------------------------------------------------------------------------------ class QApplication_and_basic_viewer { public: QApplication_and_basic_viewer(const CGAL::Graphics_scene& buffer, const char* title="CGAL Basic Viewer"): m_application(nullptr), m_basic_viewer(nullptr), m_argc(1) { m_argv[0]=new char[strlen(title)+1]; memcpy(m_argv[0], title, strlen(title)+1); m_argv[1]=nullptr; #if defined(CGAL_TEST_SUITE) bool cgal_test_suite = true; #else bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); #endif if (cgal_test_suite) { return; } Qt::init_ogl_context(4, 3); m_application=new QApplication(m_argc, const_cast(m_argv)); m_basic_viewer=new Basic_viewer(m_application->activeWindow(), buffer, title); } ~QApplication_and_basic_viewer() { delete[] m_argv[0]; delete m_basic_viewer; delete m_application; } operator bool() const { return m_application!=nullptr; } void run() { if (m_application!=nullptr) { m_basic_viewer->show(); m_application->exec(); } } Basic_viewer& basic_viewer() { CGAL_assertion(m_basic_viewer!=nullptr); return *m_basic_viewer; } protected: QApplication* m_application; Basic_viewer* m_basic_viewer; char *m_argv[2]; int m_argc; }; } // End namespace Qt // A shortcut to use directly CGAL::Basic_viewer instead of CGAL::Qt::Basic_viewer. // Can be changed later if we have several viewers. using Qt::Basic_viewer; inline void draw_graphics_scene(const Graphics_scene& graphics_scene, const char *title="CGAL Basic Viewer (Qt)") { #if defined(CGAL_TEST_SUITE) bool cgal_test_suite = true; #else bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); #endif if (!cgal_test_suite) { Qt::init_ogl_context(4, 3); int argc = 1; const char *argv[2] = {title, nullptr}; QApplication app(argc, const_cast(argv)); Basic_viewer basic_viewer(app.activeWindow(), graphics_scene, title); basic_viewer.show(); app.exec(); } } } // End namespace CGAL #endif // CGAL_USE_BASIC_VIEWER #endif // CGAL_QT_BASIC_VIEWER_H