├── CMakeLists.txt ├── OpenGP ├── CGAL │ └── AABBSearcher.h ├── Eigen │ └── Image.h ├── GL │ ├── Buffer.h │ ├── Eigen.h │ ├── GlfwFpsCounter.h │ ├── GlfwWindow.h │ ├── PointsRenderer.cpp │ ├── PointsRenderer.h │ ├── SceneGraph.h │ ├── SceneObject.h │ ├── SegmentsRenderer.h │ ├── Shader.cpp │ ├── Shader.h │ ├── Trackball.h │ ├── TrackballCamera.cpp │ ├── TrackballCamera.h │ ├── TrackballWindow.cpp │ ├── TrackballWindow.h │ ├── VertexArrayObject.h │ ├── check_error_gl.h │ ├── gl.h │ ├── glfw.h │ ├── glfw_helpers.h │ ├── glfw_trackball.h │ ├── opengl_helpers.h │ └── shader_helpers.h ├── MATLAB │ └── random.h ├── MLogger.h ├── NullStream.h ├── Qt │ ├── QGLFormat32.h │ ├── QGLWidget32.h │ └── qglviewer │ │ ├── QGLMeshLabViewer.h │ │ └── helpers.h ├── README.md ├── SurfaceMesh │ ├── Algorithm.h │ ├── Eigen.h │ ├── GL │ │ ├── SurfaceMeshRenderCloud.h │ │ ├── SurfaceMeshRenderFlat.h │ │ ├── SurfaceMeshRenderShaded.cpp │ │ ├── SurfaceMeshRenderShaded.h │ │ └── SurfaceMeshRenderVertexNormals.h │ ├── IO │ │ ├── IO.cpp │ │ ├── IO.h │ │ ├── IO_obj.cpp │ │ ├── IO_off.cpp │ │ ├── IO_poly.cpp │ │ └── IO_stl.cpp │ ├── Subdivision │ │ ├── Loop.cpp │ │ └── Loop.h │ ├── SurfaceMesh.cpp │ ├── SurfaceMesh.h │ ├── bounding_box.h │ ├── internal │ │ ├── Global_properties.h │ │ └── properties.h │ ├── remesh.cpp │ └── remesh.h ├── external │ └── nanoflann │ │ └── nanoflann.hpp ├── headeronly.h ├── types.h └── util │ └── tictoc.h ├── README.md ├── cmake ├── ConfigureCompiler.cmake ├── ConfigureEigen3.cmake ├── ConfigureGLEW.cmake ├── ConfigureGLFW3.cmake ├── ConfigureOpenGL.cmake ├── ConfigureOpenGP.cmake ├── FindEigen3.cmake ├── FindGLFW3.cmake └── FindOpenGP.cmake ├── data ├── bunny.obj ├── dodeca.obj ├── indorelax.obj ├── quad_33x33.obj ├── quad_3x3.obj ├── quad_9x9.obj ├── sphere.obj ├── tet.obj └── triangle.obj ├── images ├── graph.png └── numeric.png └── src ├── FMM.cpp ├── FMM.h ├── FMMPriorityQueue.cpp ├── FMMPriorityQueue.h ├── GeodesicInHeat.cpp ├── GeodesicInHeat.h ├── Method.h ├── SurfaceMeshRenderGeodesic.h ├── SurfaceMeshRenderLines.h └── main.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0 FATAL_ERROR) 2 | 3 | project(geodesic-computation LANGUAGES CXX) 4 | 5 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 6 | 7 | include(cmake/ConfigureCompiler.cmake) 8 | include(cmake/ConfigureOpenGL.cmake) 9 | include(cmake/ConfigureGLEW.cmake) 10 | include(cmake/ConfigureGLFW3.cmake) 11 | include(cmake/ConfigureEigen3.cmake) 12 | include(cmake/ConfigureOpenGP.cmake) 13 | 14 | file(GLOB SOURCES "src/*.cpp") 15 | file(GLOB HEADERS "src/*.h") 16 | 17 | add_executable(demo ${SOURCES} ${HEADERS}) 18 | target_link_libraries(demo ${LIBRARIES}) 19 | -------------------------------------------------------------------------------- /OpenGP/CGAL/AABBSearcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //============================================================================= 11 | namespace OpenGP{ 12 | //============================================================================= 13 | 14 | /// @brief acceleration data structure for closest point computation on a triangular mesh 15 | template 16 | class AABBSearcher{ 17 | /// CGAL TYPES 18 | typedef CGAL::Simple_cartesian K; 19 | typedef typename K::FT FT; 20 | typedef typename K::Point_3 Point_3; 21 | typedef typename K::Triangle_3 Triangle; 22 | typedef typename std::vector::iterator Iterator; 23 | typedef typename CGAL::AABB_triangle_primitive Primitive; 24 | typedef typename CGAL::AABB_traits AABB_triangle_traits; 25 | typedef typename CGAL::AABB_tree Tree; 26 | typedef typename Tree::Point_and_primitive_id Point_and_primitive_id; 27 | /// Eigen Types 28 | typedef Eigen::Matrix Face; 29 | typedef Eigen::Matrix Vertex; 30 | typedef typename FaceMatrix::Scalar FaceIndex; 31 | public: 32 | /// Conversion Eigen-CGAL 33 | static inline Point_3 tr(Vertex v){ return Point_3(v.x(), v.y(), v.z()); } 34 | static inline Vertex tr(Point_3 v){ return Vertex(v.x(), v.y(), v.z()); } 35 | private: 36 | Tree tree; 37 | std::vector triangles; 38 | public: 39 | /// @brief Builds the acceleration structure to look for closest points on a triangular mesh 40 | /// 41 | /// @param vertices vertices of the surface (one vertex per column) 42 | /// @param faces faces.col(i) contains the indexes of vertices on the face 43 | template 44 | void build( Eigen::MatrixBase& vertices, Eigen::MatrixBase& faces ){ 45 | /// Bake triangle set 46 | for(int fi=0; fi 64 | void closest_point( const Eigen::MatrixBase& queries, Eigen::MatrixBase& footpoints ){ 65 | for(int iq=0; iq 74 | Eigen::Matrix closest_point(Eigen::Matrix query){ 75 | return tr( tree.closest_point(tr(query)) ); 76 | } 77 | 78 | /// @brief Find closest points on the surface 79 | /// 80 | /// @param queries query point cloud (one point per column) 81 | /// @param footpoints fetched closest point on the input triangle set (one point per column) 82 | /// @param indexes index of triangle on which the footpoint was found (one index per query) 83 | /// @param coordinates barycentric coordinates of the footpoint in the corresponding face 84 | /// @note NO INSURANCE on degenerate triangles. Matrix inversion will fail!! 85 | template 86 | void closest_point( const Eigen::MatrixBase& queries, Eigen::MatrixBase& footpoints, Eigen::MatrixBase& indexes ){ 87 | for(int iq=0; iq 104 | void barycentric( const Eigen::MatrixBase& footpoints, const Eigen::MatrixBase& indexes, Eigen::MatrixBase& coordinates ){ 105 | Eigen::Matrix A; 106 | for(int iq=0; iq 4 | #include 5 | #include 6 | #include 7 | #include //< to read targa file 8 | 9 | //============================================================================= 10 | namespace OpenGP{ 11 | //============================================================================= 12 | 13 | template 14 | using EigenImage = Eigen::Matrix< PixelType, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor >; 15 | 16 | template 17 | class ImageRenderer : public SceneObject{ 18 | protected: 19 | EigenImage& _image; 20 | VertexArrayObject _vao; 21 | ArrayBuffer _buffer_vpos; ///< per-vertex position 22 | ArrayBuffer _buffer_vtex; ///< per-vertex uv coords 23 | GLuint _tex; ///< Texture ID 24 | 25 | public: 26 | ImageRenderer(EigenImage& image) : _image(image){} 27 | ~ImageRenderer(){ 28 | glBindTexture(GL_TEXTURE_2D, 0); 29 | glDeleteTextures(1, &_tex); 30 | } 31 | 32 | public: 33 | ///--- Generic vertex shader 34 | const GLchar* vshader = R"GLSL( 35 | #version 330 core 36 | in vec3 vpoint; 37 | in vec2 vtexcoord; 38 | out vec2 uv; 39 | void main() { 40 | uv = vtexcoord; 41 | gl_Position = vec4(vpoint, 1.0); 42 | } 43 | )GLSL"; 44 | 45 | ///--- Fragment shader for RGB textures 46 | const GLchar* fshader_rgb = R"GLSL( 47 | #version 330 core 48 | uniform sampler2D tex; 49 | in vec2 uv; 50 | out vec4 color; 51 | void main() { color = vec4(texture(tex,uv).rgb,1); } 52 | )GLSL"; 53 | 54 | ///--- Fragment shader for grayscale textures 55 | const GLchar* fshader_gray = R"GLSL( 56 | #version 330 core 57 | uniform sampler2D tex; 58 | in vec2 uv; 59 | out vec4 color; 60 | void main() { color = vec4(vec3(texture(tex,uv).r),1); } 61 | )GLSL"; 62 | 63 | public: 64 | Box3 bounding_box(){ return Box3( Vec3(-1,-1,0), Vec3(+1,+1,0) ); } 65 | void init(){ 66 | ///--- Pick the right shader 67 | const GLchar* fshader = NULL; 68 | fshader = (std::is_same::value) ? fshader_rgb : fshader; 69 | fshader = (std::is_same::value) ? fshader_gray : fshader; 70 | assert(fshader!=NULL); 71 | 72 | ///--- Compile program 73 | program.add_vshader_from_source(vshader); 74 | program.add_fshader_from_source(fshader); 75 | program.link(); 76 | 77 | ///--- Quad coordinates 78 | const GLfloat vpoint[] = { /*V0*/ -1.0f, -1.0f, 0.0f, 79 | /*V1*/ +1.0f, -1.0f, 0.0f, 80 | /*V2*/ -1.0f, +1.0f, 0.0f, 81 | /*V3*/ +1.0f, +1.0f, 0.0f }; 82 | _buffer_vpos.upload_raw(vpoint, 4); 83 | 84 | ///--- Texture coordinates 85 | const GLfloat vtexcoord[] = { /*v0*/ 0.0f, 0.0f, 86 | /*v1*/ 1.0f, 0.0f, 87 | /*v2*/ 0.0f, 1.0f, 88 | /*v3*/ 1.0f, 1.0f }; 89 | _buffer_vtex.upload_raw(vtexcoord, 4); 90 | 91 | ///--- Load texture 92 | { 93 | glGenTextures(1, &_tex); 94 | glBindTexture(GL_TEXTURE_2D, _tex); 95 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 96 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 97 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 98 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 99 | if ( std::is_same::value ){ 100 | glTexImage2D(GL_TEXTURE_2D, /*level*/ 0, GL_RGB32F, 101 | _image.cols(), _image.rows(), 0, 102 | GL_RGB, GL_FLOAT, _image.data()); 103 | } 104 | else if( std::is_same::value ){ 105 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, 106 | _image.cols(), _image.rows(), 0, 107 | GL_RED, GL_FLOAT, _image.data()); 108 | } 109 | } 110 | 111 | ///--- Specify attributes 112 | program.bind(); 113 | _vao.bind(); 114 | program.set_attribute("vpoint", _buffer_vpos); 115 | program.set_attribute("vtexcoord", _buffer_vtex); 116 | glUniform1i(glGetUniformLocation(program.programId(), "tex"), 0 /*GL_TEXTURE0*/); 117 | _vao.release(); 118 | program.release(); 119 | } 120 | void display(){ 121 | _vao.bind(); 122 | program.bind(); 123 | glActiveTexture(GL_TEXTURE0); 124 | glBindTexture(GL_TEXTURE_2D, _tex); 125 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 126 | program.release(); 127 | _vao.release(); 128 | } 129 | 130 | }; 131 | 132 | template 133 | inline int imshow( EigenImage& I, const char* title="" ){ 134 | GlfwWindow window(title, I.cols(), I.rows()); 135 | ImageRenderer renderer(I); 136 | window.scene.add(renderer); 137 | return window.run(); 138 | } 139 | 140 | // http://geekchef.com/using-targa-files 141 | // http://stackoverflow.com/questions/16538945/writing-uncompressed-tga-to-file-in-c 142 | // https://github.com/nlguillemot/JustGL/blob/f8566ed540413e7b0503a6a08396df7363891a62/ext/justgl_image.cpp#L30 143 | // http://nehe.gamedev.net/tutorial/loading_compressed_and_uncompressed_tga's/22001 144 | /// A pixel is represented as a 3-channel [0,256] tuple. 145 | struct TargaVec3b{ 146 | uchar rgb[3]; 147 | operator Vec3() const{return Vec3(rgb[2]/255.0,rgb[1]/255.0,rgb[0]/255.0);} 148 | }; 149 | 150 | /// Targa 24bits (3 channels) internal image representation 151 | typedef Eigen::Matrix TargaImage; 152 | 153 | static void imwrite(const char* path, const EigenImage& _I){ 154 | TargaImage I(_I.rows(), _I.cols()); 155 | for (int row = 0; row < I.rows(); row++){ 156 | for (int col = 0; col < I.cols(); col++){ 157 | uchar r = 255 * _I(row,col)[0]; 158 | uchar g = 255 * _I(row,col)[1]; 159 | uchar b = 255 * _I(row,col)[2]; 160 | // Note: R<==>B colors are swapped in file format 161 | I(row,col) = {b, g, r}; 162 | } 163 | } 164 | 165 | FILE* fid = fopen(path,"wb"); 166 | GLubyte header[18]={0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 167 | header[12] = (I.cols()) & 0xFF; 168 | header[13] = (I.cols() >> 8) & 0xFF; 169 | header[14] = (I.rows()) & 0xFF; 170 | header[15] = (I.rows() >> 8) & 0xFF; 171 | header[16] = 24; 172 | fwrite(header,sizeof(GLubyte),18,fid); 173 | fwrite(I.data(),sizeof(GLubyte), I.rows() * I.cols() * 3 ,fid); 174 | fclose(fid); 175 | } 176 | 177 | static void imread(const char* path, EigenImage& I_out){ 178 | /// TODO: TGA images are stored on a per row basis. *However* image can be flipped vertically. 179 | /// Its actually an option in the image format. You can encode images both from top to bottom 180 | /// or from bottom to top... And even though most programs seem to output TGA files in bottom 181 | /// to top encoding, your loader should still make sure to support both 182 | FILE* fid = fopen(path,"rb"); 183 | GLubyte header[18]; 184 | if( fread(header, sizeof(header), 1, fid) != 1 ) 185 | mFatal() << "Cannot read TGA header"; 186 | int width = (header[13] << 8) + header[12]; 187 | int height = (header[15] << 8) + header[14]; 188 | if(header[16]!=24) 189 | mFatal() << "Only 24bits TGA supported"; 190 | int channels = header[16] / 8; 191 | // mDebug("%d x %d @ %d", width, height, channels); 192 | TargaImage I(height, width); 193 | if( fread(I.data(), channels * width * height, 1, fid) != 1 ) 194 | mFatal() << "Failed to read TGA data"; 195 | fclose(fid); 196 | I_out = I.cast(); 197 | } 198 | 199 | //============================================================================= 200 | } // OpenGP:: 201 | //============================================================================= 202 | -------------------------------------------------------------------------------- /OpenGP/GL/Buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | //============================================================================= 6 | namespace OpenGP { 7 | //============================================================================= 8 | 9 | template 10 | class Buffer{ 11 | private: 12 | GLuint _buffer =0; ///< 0: invalid 13 | GLsizeiptr _num_elems = 0; ///< # of uploaded elements 14 | const GLsizeiptr _elem_size; ///< size of a single elements (bytes) 15 | 16 | public: 17 | Buffer() : _elem_size(sizeof(T)){ 18 | glGenBuffers(1, &_buffer); 19 | } 20 | ~Buffer(){ glDeleteBuffers(1, &_buffer); } 21 | 22 | void bind(){ glBindBuffer(TARGET, _buffer); } 23 | void unbind(){ glBindBuffer(TARGET, 0); } 24 | GLsizeiptr size() const{ return _num_elems; } 25 | 26 | void upload(const std::vector& data, GLenum usage=GL_STATIC_DRAW){ 27 | this->_num_elems = data.size(); 28 | upload_raw(data.data(), _num_elems, usage); 29 | } 30 | 31 | /// @note use the other upload functions whenever possible 32 | void upload_raw(const GLvoid* raw_data_ptr, GLsizeiptr num_elems, GLenum usage=GL_STATIC_DRAW){ 33 | this->_num_elems = num_elems; 34 | glBindBuffer(TARGET, _buffer); 35 | glBufferData(TARGET, num_elems*_elem_size, raw_data_ptr, usage); 36 | } 37 | }; 38 | 39 | ///--- Specializations 40 | 41 | template 42 | using ArrayBuffer = Buffer; 43 | 44 | template 45 | using ElementArrayBuffer = Buffer; 46 | 47 | //============================================================================= 48 | } // namespace OpenGP 49 | //============================================================================= 50 | -------------------------------------------------------------------------------- /OpenGP/GL/Eigen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | //============================================================================= 6 | namespace OpenGP { 7 | //============================================================================= 8 | 9 | /// @brief Returns a perspective transformation matrix like the one from gluPerspective 10 | /// @see http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml 11 | /// @see glm::perspective 12 | inline Mat4x4 perspective(Scalar fovy, Scalar aspect, Scalar zNear, Scalar zFar) { 13 | Eigen::Transform tr; 14 | tr.matrix().setZero(); 15 | assert(aspect > 0); 16 | assert(zFar > zNear); 17 | assert(zNear > 0); 18 | Scalar radf = M_PI * fovy / 180.0; 19 | Scalar tan_half_fovy = std::tan(radf / 2.0); 20 | tr(0,0) = 1.0 / (aspect * tan_half_fovy); 21 | tr(1,1) = 1.0 / (tan_half_fovy); 22 | tr(2,2) = - (zFar + zNear) / (zFar - zNear); 23 | tr(3,2) = - 1.0; 24 | tr(2,3) = - (2.0 * zFar * zNear) / (zFar - zNear); 25 | return tr.matrix(); 26 | } 27 | 28 | /// @see glm::ortho 29 | template 30 | inline Mat4x4 ortho( Scalar const& left, Scalar const& right, 31 | Scalar const& bottom, Scalar const& top, 32 | Scalar const& zNear, Scalar const& zFar ) 33 | { 34 | Eigen::Matrix mat = Eigen::Matrix::Identity(); 35 | mat(0,0) = Scalar(2) / (right - left); 36 | mat(1,1) = Scalar(2) / (top - bottom); 37 | mat(2,2) = - Scalar(2) / (zFar - zNear); 38 | mat(3,0) = - (right + left) / (right - left); 39 | mat(3,1) = - (top + bottom) / (top - bottom); 40 | mat(3,2) = - (zFar + zNear) / (zFar - zNear); 41 | return mat; 42 | } 43 | 44 | inline Mat4x4 scale(Scalar x, Scalar y, Scalar z) { 45 | Eigen::Transform tr; 46 | tr.matrix().setZero(); 47 | tr(0,0) = x; 48 | tr(1,1) = y; 49 | tr(2,2) = z; 50 | tr(3,3) = 1; 51 | return tr.matrix(); 52 | } 53 | 54 | inline Mat4x4 scale(Scalar s) { return scale(s,s,s); } 55 | 56 | inline Mat4x4 translate(Scalar tx, Scalar ty, Scalar tz){ 57 | typedef Eigen::Transform Transform; 58 | Transform M = Transform::Identity(); 59 | M = Eigen::Translation(tx,ty,tz); 60 | return M.matrix(); 61 | } 62 | 63 | /// @brief Returns a view transformation matrix like the one from glu's lookAt 64 | /// @see http://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml 65 | /// @see glm::lookAt 66 | template 67 | Eigen::Matrix lookAt(Derived const& eye, Derived const& center, Derived const& up) { 68 | typedef Eigen::Matrix Matrix4; 69 | typedef Eigen::Matrix Vector3; 70 | Vector3 f = (center - eye).normalized(); 71 | Vector3 u = up.normalized(); 72 | Vector3 s = f.cross(u).normalized(); 73 | u = s.cross(f); 74 | Matrix4 mat = Matrix4::Zero(); 75 | mat(0,0) = s.x(); 76 | mat(0,1) = s.y(); 77 | mat(0,2) = s.z(); 78 | mat(0,3) = -s.dot(eye); 79 | mat(1,0) = u.x(); 80 | mat(1,1) = u.y(); 81 | mat(1,2) = u.z(); 82 | mat(1,3) = -u.dot(eye); 83 | mat(2,0) = -f.x(); 84 | mat(2,1) = -f.y(); 85 | mat(2,2) = -f.z(); 86 | mat(2,3) = f.dot(eye); 87 | mat.row(3) << 0,0,0,1; 88 | return mat; 89 | } 90 | 91 | //============================================================================= 92 | } // namespace OpenGP 93 | //============================================================================= 94 | -------------------------------------------------------------------------------- /OpenGP/GL/GlfwFpsCounter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | //== NAMESPACE ================================================================ 6 | namespace OpenGP { 7 | //============================================================================= 8 | 9 | class GlfwFpsCounter{ 10 | /// @{ 11 | private: 12 | float _tMark = glfwGetTime(); 13 | float _tLast = _tMark; 14 | int frame_count=0; 15 | int _fps = 0; 16 | public: 17 | bool is_printed = false; ///< print to screen? 18 | float eval_fps_every=1; ///< seconds 19 | /// @} 20 | 21 | public: 22 | int fps(){ return _fps; } 23 | void tick(){ 24 | frame_count++; 25 | float time = glfwGetTime(); 26 | // float _tDelta = time - _tLast; 27 | _tLast = time; 28 | if (time - _tMark > eval_fps_every) { 29 | _tMark = time; 30 | _fps = frame_count / eval_fps_every; 31 | frame_count = 0; 32 | if(is_printed) 33 | mLogger() << "FPS: " << std::to_string(_fps); 34 | } 35 | } 36 | }; 37 | 38 | //============================================================================= 39 | } // namespace OpenGP 40 | //============================================================================= 41 | -------------------------------------------------------------------------------- /OpenGP/GL/PointsRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //============================================================================= 5 | namespace OpenGP { 6 | //============================================================================= 7 | 8 | const static GLchar* PointsRenderer_vshader = R"GLSL( 9 | #version 330 10 | uniform mat4 M; 11 | uniform mat4 MV; 12 | uniform mat4 MVP; 13 | in vec3 vpoint; 14 | in vec3 vcolor; 15 | in float vsize; 16 | out vec3 fcolor; 17 | void main() { 18 | gl_Position = MVP * vec4(vpoint, 1.0); 19 | gl_PointSize = vsize; 20 | fcolor = vcolor; 21 | } 22 | )GLSL"; 23 | 24 | const static char* PointsRenderer_fshader = R"GLSL( 25 | #version 330 26 | in vec3 fcolor; 27 | out vec4 color; 28 | void main(){ color = vec4(fcolor,1); } 29 | )GLSL"; 30 | 31 | void PointsRenderer::init() { 32 | ///--- Shader 33 | program.add_vshader_from_source(PointsRenderer_vshader); 34 | program.add_fshader_from_source(PointsRenderer_fshader); 35 | program.link(); 36 | 37 | ///--- Data 38 | _buffer_vpos.upload_raw(_data.data(), _data.cols()); 39 | 40 | ///--- Attributes 41 | program.bind(); 42 | _vao.bind(); 43 | program.set_attribute("vpoint", _buffer_vpos); 44 | program.set_attribute("vcolor", color); ///< default use object color 45 | program.set_attribute("vsize", 3.0f); ///< default point size 46 | _vao.release(); 47 | program.release(); 48 | } 49 | 50 | void PointsRenderer::display() { 51 | if (_data.cols()==0) return; 52 | _vao.bind(); 53 | program.bind(); 54 | glEnable(GL_PROGRAM_POINT_SIZE); 55 | glDrawArrays(GL_POINTS, 0, _data.cols()); 56 | glDisable(GL_PROGRAM_POINT_SIZE); 57 | program.release(); 58 | _vao.release(); 59 | } 60 | 61 | Box3 PointsRenderer::bounding_box() { 62 | /// TODO: test 63 | Vec3 _min = _data.rowwise().minCoeff(); 64 | Vec3 _max = _data.rowwise().maxCoeff(); 65 | return Box3(_min, _max); 66 | } 67 | 68 | void PointsRenderer::init_data(MatMxN& data) { 69 | _data = data; 70 | _buffer_vpos.upload_raw(_data.data(), data.cols()); 71 | } 72 | 73 | void PointsRenderer::set_colors(const MatMxN& M) { 74 | CHECK(program.is_valid()); 75 | CHECK(M.rows()==_data.rows()); 76 | CHECK(M.cols()==_data.cols()); 77 | 78 | ///--- Upload data 79 | _buffer_vcolor.upload_raw(M.data(), M.cols()); 80 | ///--- Set attribute 81 | program.bind(); 82 | _vao.bind(); 83 | program.set_attribute("vcolor", _buffer_vcolor); 84 | _vao.release(); 85 | program.release(); 86 | } 87 | 88 | void PointsRenderer::set_vsize(float val) { 89 | CHECK(program.is_valid()); 90 | program.bind(); 91 | program.set_attribute("vsize", val); 92 | program.release(); 93 | } 94 | 95 | //============================================================================= 96 | } // namespace OpenGP 97 | //============================================================================= 98 | -------------------------------------------------------------------------------- /OpenGP/GL/PointsRenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | //============================================================================= 7 | namespace OpenGP { 8 | //============================================================================= 9 | 10 | class PointsRenderer : public SceneObject { 11 | protected: 12 | VertexArrayObject _vao; 13 | MatMxN _data; ///< reference to data to be rendered 14 | ArrayBuffer _buffer_vpos; ///< per-vertex position 15 | ArrayBuffer _buffer_vcolor; ///< per-vertex color (optional) 16 | 17 | /// @{ constructors 18 | public: 19 | PointsRenderer() {} 20 | PointsRenderer(const MatMxN& data) { load(data); } 21 | void load(const MatMxN& data) { _data=data; } 22 | /// @} 23 | 24 | public: 25 | HEADERONLY_INLINE void init(); 26 | HEADERONLY_INLINE void init_data(MatMxN& data); 27 | HEADERONLY_INLINE void display(); 28 | HEADERONLY_INLINE Box3 bounding_box(); 29 | 30 | /// Sets the per-point color to the given values 31 | HEADERONLY_INLINE void set_colors(const MatMxN& M); 32 | 33 | /// Sets the size of splats to a value (same across points) 34 | HEADERONLY_INLINE void set_vsize(float val); 35 | }; 36 | 37 | //============================================================================= 38 | } // namespace OpenGP 39 | //============================================================================= 40 | 41 | #ifdef HEADERONLY 42 | #include "PointsRenderer.cpp" 43 | #endif 44 | -------------------------------------------------------------------------------- /OpenGP/GL/SceneGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | //============================================================================= 15 | namespace OpenGP { 16 | //============================================================================= 17 | 18 | class SceneGraph{ 19 | public: 20 | /// Camera for the 3D scene 21 | /// TODO: create superclass? 22 | TrackballCamera trackball_camera; 23 | 24 | /// Set of objects that needs to be rendered 25 | std::vector objects; 26 | 27 | /// Scene lights 28 | /// TODO: generate set 29 | Vec3 _light_dir = Vec3(0,0,1); 30 | 31 | void add(SceneObject& object){ 32 | object.init(); 33 | objects.push_back(&object); 34 | } 35 | 36 | void display(float time=0){ 37 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 38 | 39 | const Mat4x4& view = trackball_camera.view_matrix(); 40 | const Mat4x4& projection = trackball_camera.projection_matrix(); 41 | 42 | for(SceneObject* obj: objects) { 43 | obj->program.bind(); 44 | { 45 | ///--- Upload Light Specs TODO 46 | // obj->program.setUniformValue("LDIR", _light_dir); 47 | 48 | ///--- Upload time information 49 | obj->program.set_uniform("time", time); 50 | 51 | ///--- Update Matrix Stack 52 | obj->program.set_uniform("M", Mat4x4(obj->model)); 53 | obj->program.set_uniform("MV", Mat4x4(view*obj->model)); 54 | obj->program.set_uniform("MVP", Mat4x4(projection*view*obj->model)); 55 | 56 | ///--- Display 57 | obj->display(); 58 | } 59 | obj->program.release(); 60 | } 61 | } 62 | 63 | void screen_resize(int width, int height) { 64 | glViewport(0, 0, width, height); 65 | trackball_camera.screen_resize(width, height); 66 | } 67 | }; 68 | 69 | //============================================================================= 70 | } // namespace OpenGP 71 | //============================================================================= 72 | -------------------------------------------------------------------------------- /OpenGP/GL/SceneObject.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | //============================================================================= 13 | namespace OpenGP { 14 | //============================================================================= 15 | 16 | class SceneObject{ 17 | // TODO: split public/private 18 | public: 19 | Shader program; 20 | /// Model Transformation Matrix 21 | Mat4x4 model = Mat4x4::Identity(); 22 | /// Per-object color 23 | /// TODO upload to shader 24 | Vec3 color = Vec3(.6,.6,.6); 25 | 26 | virtual ~SceneObject(){} 27 | virtual void init() = 0; 28 | virtual void display() = 0; 29 | 30 | /// The bounding box can be used to automatically compute the parameters 31 | /// of the camera, so every drawable object has to define it. 32 | virtual Box3 bounding_box() = 0; 33 | }; 34 | 35 | //============================================================================= 36 | } // namespace OpenGP 37 | //============================================================================= 38 | -------------------------------------------------------------------------------- /OpenGP/GL/SegmentsRenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | //============================================================================= 7 | namespace OpenGP { 8 | //============================================================================= 9 | 10 | class SegmentsRenderer : public SceneObject{ 11 | public: 12 | typedef std::vector> Segments; 13 | protected: 14 | VertexArrayObject _vao; ///< OpenGL object storing data/attributes 15 | MatMxN _data; ///< reference to data to be rendered 16 | ArrayBuffer _buffer_vpos; ///< per-vertex position 17 | ArrayBuffer _buffer_vcolor; ///< per-vertex color (optional) 18 | 19 | /// @{ constructors 20 | public: 21 | SegmentsRenderer(){} 22 | SegmentsRenderer(const Segments& segments){ load(segments); } 23 | SegmentsRenderer(const MatMxN& P1, const MatMxN& P2){ load(P1,P2); } 24 | protected: 25 | HEADERONLY_INLINE void load(const MatMxN& P1, const MatMxN& P2); 26 | HEADERONLY_INLINE void load(const Segments& segments); 27 | /// @} 28 | 29 | public: 30 | HEADERONLY_INLINE void init(); 31 | HEADERONLY_INLINE void display(); 32 | HEADERONLY_INLINE Box3 bounding_box(); 33 | 34 | protected: 35 | const GLchar* vshader = R"GLSL( 36 | #version 330 37 | uniform mat4 M; 38 | uniform mat4 MV; 39 | uniform mat4 MVP; 40 | in vec3 vpoint; 41 | in vec3 vcolor; 42 | out vec3 fcolor; 43 | void main() { 44 | gl_Position = MVP * vec4(vpoint, 1.0); 45 | fcolor = vcolor; 46 | } 47 | )GLSL"; 48 | 49 | const char* fshader = R"GLSL( 50 | #version 330 51 | in vec3 fcolor; 52 | out vec4 color; 53 | void main(){ color = vec4(fcolor,1); } 54 | )GLSL"; 55 | }; 56 | 57 | //============================================================================= 58 | 59 | void SegmentsRenderer::load(const MatMxN &P1, const MatMxN &P2){ 60 | CHECK(P1.cols() == P2.cols()); 61 | CHECK((P1.rows() == 3) && (P2.rows() == 3)); 62 | _data.resize( 3, 2*P1.cols() ); 63 | for(int i=0; i 2 | 3 | //============================================================================= 4 | namespace OpenGP { 5 | //============================================================================= 6 | 7 | void Shader::set_uniform(const char* name, int scalar) { 8 | assert( check_is_current() ); 9 | GLint loc = glGetUniformLocation(pid, name); 10 | glUniform1i(loc, scalar); 11 | } 12 | 13 | void Shader::set_uniform(const char* name, float scalar) { 14 | assert( check_is_current() ); 15 | GLint loc = glGetUniformLocation(pid, name); 16 | glUniform1f(loc, scalar); 17 | } 18 | 19 | void Shader::set_uniform(const char* name, const Eigen::Vector3f& vector) { 20 | assert( check_is_current() ); 21 | GLint loc = glGetUniformLocation(pid, name); 22 | glUniform4fv(loc, 3, vector.data()); 23 | } 24 | 25 | void Shader::set_uniform(const char* name, const Eigen::Matrix4f& matrix) { 26 | assert( check_is_current() ); 27 | GLint loc = glGetUniformLocation(pid, name); 28 | glUniformMatrix4fv(loc, 1, GL_FALSE, matrix.data()); 29 | } 30 | 31 | void Shader::set_attribute(const char* name, float value) { 32 | assert( check_is_current() ); 33 | GLint loc = glGetAttribLocation(pid, name); 34 | glVertexAttrib1f(loc, value); 35 | } 36 | 37 | void OpenGP::Shader::set_attribute(const char* name, const Eigen::Vector3f& vector) { 38 | assert( check_is_current() ); 39 | GLint loc = glGetAttribLocation(pid, name); 40 | glVertexAttrib3fv(loc, vector.data()); 41 | } 42 | 43 | void OpenGP::Shader::set_attribute(const char* name, ArrayBuffer& buffer) { 44 | assert( check_is_current() ); 45 | GLint location = glGetAttribLocation(pid, name); ///< property to modify 46 | glEnableVertexAttribArray(location); ///< cached in VAO 47 | buffer.bind(); ///< memory the description below refers to 48 | glVertexAttribPointer(location, /*vec1*/ 1, GL_FLOAT, DONT_NORMALIZE, ZERO_STRIDE, ZERO_BUFFER_OFFSET); 49 | } 50 | 51 | void OpenGP::Shader::set_attribute(const char* name, ArrayBuffer& buffer) { 52 | assert( check_is_current() ); 53 | GLint location = glGetAttribLocation(pid, name); ///< property to modify 54 | glEnableVertexAttribArray(location); ///< cached in VAO 55 | buffer.bind(); ///< memory the description below refers to 56 | glVertexAttribPointer(location, /*vec3*/ 2, GL_FLOAT, DONT_NORMALIZE, ZERO_STRIDE, ZERO_BUFFER_OFFSET); 57 | } 58 | 59 | void OpenGP::Shader::set_attribute(const char* name, ArrayBuffer& buffer) { 60 | assert( check_is_current() ); 61 | GLint location = glGetAttribLocation(pid, name); ///< property to modify 62 | glEnableVertexAttribArray(location); ///< cached in VAO 63 | buffer.bind(); ///< memory the description below refers to 64 | glVertexAttribPointer(location, /*vec3*/ 3, GL_FLOAT, DONT_NORMALIZE, ZERO_STRIDE, ZERO_BUFFER_OFFSET); 65 | } 66 | 67 | bool OpenGP::Shader::add_vshader_from_source(const char* code) { 68 | if (verbose) mDebug() << "Compiling Vertex shader"; 69 | 70 | /// Create the Vertex Shader 71 | GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); 72 | 73 | /// Compile Vertex Shader 74 | glShaderSource(VertexShaderID, 1, &code, NULL); 75 | glCompileShader(VertexShaderID); 76 | 77 | /// Check Vertex Shader 78 | GLint Success = GL_FALSE; 79 | glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Success); 80 | if (!Success) { 81 | int InfoLogLength; 82 | glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 83 | std::vector VertexShaderErrorMessage(InfoLogLength); 84 | glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); 85 | // fprintf(stdout, "Failed:\n%s\n", &VertexShaderErrorMessage[0]); 86 | mDebug() << std::string(&VertexShaderErrorMessage[0]); 87 | } else { 88 | glAttachShader(pid, VertexShaderID); 89 | } 90 | return Success; 91 | } 92 | 93 | bool OpenGP::Shader::add_fshader_from_source(const char* code) { 94 | if (verbose) mDebug() << "Compiling Fragment shader"; 95 | 96 | /// Create the Fragment Shader 97 | GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); 98 | 99 | /// Compile Fragment Shader 100 | glShaderSource(FragmentShaderID, 1, &code, NULL); 101 | glCompileShader(FragmentShaderID); 102 | 103 | /// Check Fragment Shader 104 | GLint Success = GL_FALSE; 105 | glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Success); 106 | if (!Success) { 107 | int InfoLogLength; 108 | glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 109 | std::vector FragmentShaderErrorMessage(InfoLogLength); 110 | glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); 111 | mDebug() << std::string(&FragmentShaderErrorMessage[0]); 112 | } else { 113 | glAttachShader(pid, FragmentShaderID); 114 | } 115 | 116 | return Success; 117 | } 118 | 119 | bool OpenGP::Shader::link() { 120 | if (verbose) mDebug() << "Linking shader program"; 121 | glLinkProgram(pid); 122 | 123 | /// Check the program 124 | GLint Success = GL_FALSE; 125 | glGetProgramiv(pid, GL_LINK_STATUS, &Success); 126 | if (!Success) { 127 | int InfoLogLength; 128 | glGetProgramiv(pid, GL_INFO_LOG_LENGTH, &InfoLogLength); 129 | std::vector ProgramErrorMessage( std::max(InfoLogLength, int(1)) ); 130 | glGetProgramInfoLog(pid, InfoLogLength, NULL, &ProgramErrorMessage[0]); 131 | mDebug() << "Failed: " << &ProgramErrorMessage[0]; 132 | } 133 | 134 | _is_valid = true; 135 | return Success; 136 | 137 | // TODO: should this be done? 138 | // glDeleteShader(VertexShaderID); 139 | // glDeleteShader(FragmentShaderID); 140 | } 141 | 142 | //============================================================================= 143 | } // namespace OpenGP 144 | //============================================================================= 145 | -------------------------------------------------------------------------------- /OpenGP/GL/Shader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | //============================================================================= 9 | namespace OpenGP { 10 | //============================================================================= 11 | 12 | namespace{ 13 | /// Convenience constants 14 | static const int ONE = 1; 15 | static const bool DONT_NORMALIZE = false; 16 | static const bool DONT_TRANSPOSE = false; 17 | static const int ZERO_STRIDE = 0; 18 | static const void* ZERO_BUFFER_OFFSET = 0; 19 | } 20 | 21 | /// @note Inspired by QOpenGLShaderProgram 22 | /// @note Important not to use Scalar 23 | class Shader{ 24 | /// @{ 25 | private: 26 | GLuint pid = 0; ///< 0: invalid 27 | bool _is_valid = false; 28 | public: 29 | bool verbose = false; ///< prints messages 30 | /// @} 31 | 32 | public: 33 | Shader(){ pid = glCreateProgram(); } 34 | GLuint programId() const { return pid; } 35 | void bind(){ assert(_is_valid); glUseProgram(pid); } 36 | void release(){ glUseProgram(0); } 37 | bool is_valid(){ return _is_valid; } 38 | 39 | bool check_is_current(){ 40 | assert(pid!=0); 41 | return (current_program_id() == pid); 42 | } 43 | 44 | static GLuint current_program_id(){ 45 | GLint id; 46 | glGetIntegerv(GL_CURRENT_PROGRAM,&id); 47 | return (GLuint) id; 48 | } 49 | 50 | /// @{ shaders compile and link 51 | public: 52 | HEADERONLY_INLINE bool add_vshader_from_source(const char* code); 53 | HEADERONLY_INLINE bool add_fshader_from_source(const char* code); 54 | HEADERONLY_INLINE bool link(); 55 | /// @} 56 | 57 | /// @{ uniforms setters 58 | public: 59 | HEADERONLY_INLINE void set_uniform(const char* name, int scalar); 60 | HEADERONLY_INLINE void set_uniform(const char* name, float scalar); 61 | HEADERONLY_INLINE void set_uniform(const char* name, const Eigen::Vector3f& vector); 62 | HEADERONLY_INLINE void set_uniform(const char* name, const Eigen::Matrix4f& matrix); 63 | /// @} 64 | 65 | /// @{ setters for *constant* vertex attributes 66 | public: 67 | HEADERONLY_INLINE void set_attribute(const char* name, float value); 68 | HEADERONLY_INLINE void set_attribute(const char* name, const Eigen::Vector3f& vector); 69 | HEADERONLY_INLINE void set_attribute(const char* name, ArrayBuffer& buffer); 70 | HEADERONLY_INLINE void set_attribute(const char* name, ArrayBuffer& buffer); 71 | HEADERONLY_INLINE void set_attribute(const char* name, ArrayBuffer& buffer); 72 | /// @} 73 | 74 | }; 75 | 76 | //============================================================================= 77 | } // namespace OpenGP 78 | //============================================================================= 79 | 80 | #ifdef HEADERONLY 81 | #include "Shader.cpp" 82 | #endif 83 | -------------------------------------------------------------------------------- /OpenGP/GL/Trackball.h: -------------------------------------------------------------------------------- 1 | // @see http://image.diku.dk/research/trackballs/index.html. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace OpenGP{ 8 | 9 | class Trackball { 10 | public: 11 | Trackball() { 12 | radius_ = 1.0f; 13 | } 14 | explicit Trackball(float radius) : radius_(radius) {} 15 | 16 | const Eigen::Matrix4f& incremental_rotation() const { 17 | return incremental_rotation_; 18 | } 19 | 20 | void BeginDrag(float x, float y) { 21 | // TODO(stefalie): Is the identity loading required? 22 | incremental_rotation_ = Eigen::Matrix4f::Identity(); 23 | 24 | anchor_pos_= Eigen::Vector3f(x, y, 0.0f); 25 | ProjectOntoSurface(anchor_pos_); 26 | 27 | current_pos_= anchor_pos_; 28 | } 29 | void Drag(float x, float y) { 30 | current_pos_= Eigen::Vector3f(x, y, 0.0f); 31 | ProjectOntoSurface(current_pos_); 32 | ComputeIncremental(); 33 | } 34 | 35 | private: 36 | void ProjectOntoSurface(Eigen::Vector3f& p) const { 37 | // We could make this static, but maybe we want to add methods for changing 38 | // the radius later on. 39 | const float radius2 = radius_ * radius_; 40 | 41 | const float length2 = p.squaredNorm(); 42 | 43 | if (length2 <= radius2 * 0.5f) { 44 | p[2] = std::sqrt(radius2 - length2); 45 | } else { 46 | p[2] = radius2 / (2.0f * std::sqrt(length2)); 47 | } 48 | float length = p.norm(); 49 | p /= length; 50 | } 51 | 52 | void ComputeIncremental() { 53 | const float angle_boost = 10.0f; 54 | Eigen::Vector3f axis = anchor_pos_.cross(current_pos_); 55 | float angle = angle_boost * atan2(axis.norm(), anchor_pos_.dot(current_pos_)); 56 | axis.normalize(); 57 | incremental_rotation_ = Eigen::Affine3f(Eigen::AngleAxisf(angle, axis)).matrix(); 58 | } 59 | 60 | float radius_; 61 | Eigen::Vector3f anchor_pos_; 62 | Eigen::Vector3f current_pos_; 63 | Eigen::Matrix4f incremental_rotation_; 64 | }; 65 | 66 | } // OpenGP:: 67 | -------------------------------------------------------------------------------- /OpenGP/GL/TrackballCamera.cpp: -------------------------------------------------------------------------------- 1 | #include "TrackballCamera.h" 2 | #include 3 | #include 4 | 5 | void OpenGP::TrackballCamera::begin_rotate(OpenGP::Vec3 pos) { 6 | _current_pos = pos; 7 | project_onto_surface(_current_pos); 8 | _anchor_pos = _current_pos; 9 | } 10 | 11 | void OpenGP::TrackballCamera::finish_rotate() { 12 | _old_rotation = _rotation; 13 | } 14 | 15 | void OpenGP::TrackballCamera::rotate(Vec3 pos) { 16 | _current_pos= pos; 17 | project_onto_surface(_current_pos); 18 | 19 | Vec3 axis = _anchor_pos.cross(_current_pos); 20 | float angle = _angle_boost * atan2(axis.norm(), _anchor_pos.dot(_current_pos)); 21 | 22 | if (angle > 0.0f) { 23 | axis.normalize(); 24 | _rotation = Eigen::Affine3f(Eigen::AngleAxisf(angle, axis)).matrix(); 25 | _rotation *= _old_rotation; 26 | 27 | update_matrices(); 28 | } 29 | } 30 | 31 | void OpenGP::TrackballCamera::translate(OpenGP::Vec3 pos, OpenGP::Vec3 old_pos) { 32 | //TODO: Should probably check for more degenerate cases 33 | //TODO: Package lines and planes in structs? 34 | 35 | //Construct plane perpendicular to view direction and passing through trackball center 36 | Vec3 _camera_position = camera_position(); 37 | Vec3 plane_normal = (_camera_position - _center).normalized(); 38 | 39 | //Construct line from camera center to unprojected points 40 | Vec3 unprojected_pos = unproject(pos); 41 | Vec3 unprojected_old_pos = unproject(old_pos); 42 | 43 | Vec3 line_direction = unprojected_pos - _camera_position; 44 | Vec3 line_direction_old = unprojected_old_pos - _camera_position; 45 | 46 | Vec3 intersection_pos, intersection_old_pos; 47 | 48 | bool found_intersections = true; 49 | found_intersections &= plane_line_intersection(plane_normal, _center, line_direction, unprojected_pos, intersection_pos); 50 | found_intersections &= plane_line_intersection(plane_normal, _center, line_direction_old, unprojected_old_pos, intersection_old_pos); 51 | 52 | if (found_intersections) { 53 | Vec3 translation_pos = intersection_pos - intersection_old_pos; 54 | _translation *= Eigen::Affine3f(Eigen::Translation3f(translation_pos)).matrix(); 55 | } 56 | 57 | update_matrices(); 58 | } 59 | 60 | void OpenGP::TrackballCamera::scale(Scalar offset_y) { 61 | _translation_scale *= std::pow(1.2f, -offset_y); 62 | _translation(0,0) = _translation(1,1) = _translation(2,2) = _translation_scale; 63 | 64 | update_matrices(); 65 | } 66 | 67 | void OpenGP::TrackballCamera::focus(OpenGP::Vec3 pos) { 68 | pos = unproject(pos); 69 | set_center(pos); 70 | 71 | //TODO: Really should be a better way of resetting these matrices 72 | _view_matrix = OpenGP::lookAt(_camera_position, _center, _camera_up); 73 | _translation = Mat4x4::Identity(); 74 | _translation(0,0) = _translation(1,1) = _translation(2,2) = _translation_scale; 75 | 76 | update_matrices(); 77 | } 78 | 79 | void OpenGP::TrackballCamera::adjust_fov(Scalar offset){ 80 | /// TODO: this does not work like in meshlab, why? 81 | _fov += offset; 82 | /// Bound _fov [0, 90] (meshlab does the same) 83 | _fov = std::max(0, std::min(_fov, 90)); 84 | _projection_matrix = OpenGP::perspective(_fov, _aspect_ratio, _near, _far); 85 | } 86 | 87 | void OpenGP::TrackballCamera::screen_resize(int width, int height) { 88 | _aspect_ratio = (Scalar)width / (Scalar)height; 89 | _projection_matrix = OpenGP::perspective(_fov, _aspect_ratio, _near, _far); 90 | } 91 | 92 | void OpenGP::TrackballCamera::set_center(OpenGP::Vec3 center) { 93 | _center = center; 94 | _translate_center = Eigen::Affine3f(Eigen::Translation3f(_center)).matrix(); 95 | _translate_minus_center = Eigen::Affine3f(Eigen::Translation3f(-center)).matrix(); 96 | } 97 | 98 | OpenGP::TrackballCamera::TrackballCamera(Scalar aspect_ratio) { 99 | _aspect_ratio = aspect_ratio; 100 | set_center(Vec3(0, 0, 0)); 101 | set_defaults(); 102 | } 103 | 104 | void OpenGP::TrackballCamera::set_defaults() { 105 | //TODO: Make default parameters adjustable? 106 | _imodel_matrix = Mat4x4::Identity(); 107 | 108 | _rotation = Mat4x4::Identity(); 109 | _old_rotation = Mat4x4::Identity(); 110 | _translation = Mat4x4::Identity(); 111 | _translation_scale = 1.0f; 112 | 113 | //TODO: This should get resized 114 | _projection_matrix = OpenGP::perspective(_fov, _aspect_ratio, _near, _far); 115 | 116 | _camera_position = _camera_position_default; 117 | _camera_up = Vec3(0,1,0); 118 | _view_matrix = OpenGP::lookAt(_camera_position, _center, _camera_up); 119 | 120 | update_matrices(); 121 | // _view_imodel_matrix = _view_matrix * _imodel_matrix; 122 | } 123 | 124 | void OpenGP::TrackballCamera::update_matrices() { 125 | _imodel_matrix = _translate_center * _rotation * _translation * _translate_minus_center; 126 | _view_imodel_matrix = _view_matrix * _imodel_matrix; 127 | } 128 | 129 | //-------------------------------------------------------------------------------------------------- 130 | 131 | OpenGP::Vec3 OpenGP::TrackballCamera::camera_position() { 132 | // TODO: (Maybe) don't return by value? 133 | Vec4 result(0.0f, 0.0f, 0.0f, 1.0f); 134 | result = _view_imodel_matrix.inverse() * result; 135 | return Vec3(result(0), result(1), result(2)); 136 | } 137 | 138 | OpenGP::Vec3 OpenGP::TrackballCamera::unproject(Vec3 pos) { 139 | Vec4 result(pos(0), pos(1), pos(2), 1.0f); 140 | result = (_projection_matrix * _view_imodel_matrix).inverse() * result; 141 | return Vec3(result(0), result(1), result(2)) / result(3);; 142 | } 143 | 144 | void OpenGP::TrackballCamera::project_onto_surface(OpenGP::Vec3 &pos) { 145 | const float radius2 = _radius * _radius; 146 | const float length2 = pos.squaredNorm(); 147 | if (length2 <= radius2 * 0.5f) { 148 | pos[2] = std::sqrt(radius2 - length2); 149 | } else { 150 | pos[2] = radius2 / (2.0f * std::sqrt(length2)); 151 | } 152 | float length = pos.norm(); 153 | pos /= length; 154 | } 155 | 156 | //TODO: this function should really be somewhere else? 157 | bool OpenGP::TrackballCamera::plane_line_intersection(const OpenGP::Vec3 plane_normal, const OpenGP::Vec3 plane_point, const OpenGP::Vec3 line_direction, const OpenGP::Vec3 line_point, OpenGP::Vec3 &intersection_point) { 158 | const float epsilon = 1e-8; //< TODO: how to make this safe? 159 | float k = plane_normal.dot(line_direction); 160 | if (k > -epsilon && k < epsilon) 161 | return false; 162 | 163 | float s = plane_normal.dot(plane_point - line_point) / k; 164 | intersection_point = line_point + s * line_direction; 165 | 166 | return true; 167 | } 168 | -------------------------------------------------------------------------------- /OpenGP/GL/TrackballCamera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | //============================================================================= 7 | namespace OpenGP { 8 | //============================================================================= 9 | 10 | class TrackballCamera { 11 | private: 12 | Scalar _angle_boost = 1.0; 13 | float _radius = 1.0; 14 | float _translation_scale = 1.0; 15 | Vec3 _center; 16 | Vec3 _anchor_pos; 17 | Vec3 _current_pos; 18 | Vec3 _camera_position; 19 | Vec3 _camera_position_default = Vec3(0,0,2); 20 | Vec3 _camera_up; 21 | Mat4x4 _rotation, _old_rotation; 22 | Mat4x4 _translation, _translate_center, _translate_minus_center; 23 | 24 | /// @{ core matrices 25 | private: 26 | Mat4x4 _imodel_matrix = Mat4x4::Identity(); ///< internal model matrix 27 | Mat4x4 _view_matrix = Mat4x4::Identity(); 28 | Mat4x4 _view_imodel_matrix = Mat4x4::Identity(); 29 | public: 30 | HEADERONLY_INLINE const Mat4x4& view_matrix() { return _view_imodel_matrix; } 31 | /// @} 32 | 33 | /// @{ projection matrix setup 34 | public: 35 | HEADERONLY_INLINE const Mat4x4& projection_matrix() { return _projection_matrix; } 36 | private: 37 | Mat4x4 _projection_matrix = Mat4x4::Identity(); 38 | Scalar _fov = 45; ///< field of view 39 | Scalar _near = 0.1f; ///< near plane: assert(>0) 40 | Scalar _far = 10.0f; ///< far plane: assert(_far>_near) 41 | Scalar _aspect_ratio = 1.0; ///< WARNING: see screen_resize() 42 | /// @} 43 | 44 | /// @{ constructors 45 | public: 46 | HEADERONLY_INLINE TrackballCamera(Scalar aspect_ratio=1.0); 47 | TrackballCamera(int width, int height) 48 | : TrackballCamera((float)width/height){} 49 | private: 50 | HEADERONLY_INLINE void set_defaults(); 51 | /// @} 52 | 53 | public: 54 | HEADERONLY_INLINE void begin_rotate(Vec3 pos); 55 | HEADERONLY_INLINE void rotate(Vec3 pos); 56 | HEADERONLY_INLINE void finish_rotate(); 57 | HEADERONLY_INLINE void translate(Vec3 pos, Vec3 old_pos); 58 | HEADERONLY_INLINE void scale(Scalar offset_y); 59 | HEADERONLY_INLINE void focus(Vec3 pos); 60 | HEADERONLY_INLINE void adjust_fov(Scalar offset); 61 | HEADERONLY_INLINE void set_center(Vec3 center); 62 | HEADERONLY_INLINE void screen_resize(int width, int height); 63 | 64 | private: 65 | HEADERONLY_INLINE void update_matrices(); 66 | HEADERONLY_INLINE Vec3 camera_position(); 67 | HEADERONLY_INLINE Vec3 unproject(Vec3 pos); 68 | HEADERONLY_INLINE void project_onto_surface(Vec3& pos); 69 | HEADERONLY_INLINE bool plane_line_intersection(const Vec3 plane_normal, const Vec3 plane_point, 70 | const Vec3 line_direction, const Vec3 line_point, 71 | Vec3& intersection_point); 72 | }; 73 | 74 | //============================================================================= 75 | } // namespace OpenGP 76 | //============================================================================= 77 | 78 | #ifdef HEADERONLY 79 | #include "TrackballCamera.cpp" 80 | #endif 81 | -------------------------------------------------------------------------------- /OpenGP/GL/TrackballWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "TrackballWindow.h" 2 | #include ///< for double-click 3 | 4 | bool OpenGP::TrackballWindow::mouse_press_callback(int button, int action, int mods) { 5 | if( action == GLFW_RELEASE ){ 6 | static auto before = std::chrono::system_clock::now(); 7 | auto now = std::chrono::system_clock::now(); 8 | double diff_ms = std::chrono::duration (now - before).count(); 9 | before = now; 10 | if(diff_ms>10 && diff_ms<200){ 11 | action = GLFW_DOUBLECLICK; 12 | // mDebug() << "doubleclick"; 13 | } 14 | } 15 | 16 | if ((button == GLFW_MOUSE_BUTTON_LEFT) && (action == GLFW_PRESS)) { 17 | double x_window, y_window; 18 | getFramebufferCursorPos(&x_window, &y_window); 19 | Vec3 pos_window(x_window, y_window, 0.0f); 20 | Vec3 pos_clip = window_to_clip(pos_window); 21 | scene.trackball_camera.begin_rotate(pos_clip); 22 | return true; 23 | } 24 | 25 | if ((button == GLFW_MOUSE_BUTTON_LEFT) && (action == GLFW_RELEASE)) { 26 | scene.trackball_camera.finish_rotate(); 27 | return true; 28 | } 29 | 30 | if ((button == GLFW_MOUSE_BUTTON_LEFT) && (action == GLFW_DOUBLECLICK) && (mods == GLFW_MOD_NONE)) { 31 | double x_window, y_window; 32 | getFramebufferCursorPos(&x_window,&y_window); 33 | 34 | /// Fetch the depth by querying the OpenGL depth buffer 35 | float z_window = 1.0f; 36 | glReadBuffer(GL_FRONT); 37 | glReadPixels(int(x_window), _height - int(y_window), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z_window); 38 | 39 | /// If we clicked on something visible focus the trackball (center it) on the current point 40 | if (z_window != 1.0f) { 41 | Vec3 pos_window(x_window, y_window, z_window); 42 | Vec3 pos_clip = window_to_clip(pos_window); 43 | scene.trackball_camera.focus(pos_clip); 44 | } 45 | return true; 46 | } 47 | 48 | return false; 49 | } 50 | 51 | bool OpenGP::TrackballWindow::mouse_move_callback(double x_window, double y_window) { 52 | x_window *= scale_factor_retina(); 53 | y_window *= scale_factor_retina(); 54 | // mLogger() << x_window << y_window; 55 | 56 | bool left_down = (glfwGetMouseButton(_window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); 57 | bool middle_down = (glfwGetMouseButton(_window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); 58 | Vec3 pos_window(x_window, y_window, 0.0f); 59 | Vec3 pos_clip = window_to_clip(pos_window); 60 | 61 | bool managed = false; 62 | 63 | // Rotate 64 | if (left_down && _mod_current == GLFW_MOD_NONE) { 65 | scene.trackball_camera.rotate(pos_clip); 66 | managed = true; 67 | } 68 | 69 | // Pan 70 | if (middle_down || (left_down && _mod_current == GLFW_MOD_SUPER)) { 71 | scene.trackball_camera.translate(pos_clip, old_pos_clip); 72 | managed = true; 73 | } 74 | 75 | // Scale 76 | if (left_down && (_mod_current == GLFW_MOD_SHIFT)) { 77 | scene.trackball_camera.scale(5.0f * (float)(pos_clip(1) - old_pos_clip(1))); 78 | managed = true; 79 | } 80 | 81 | old_pos_clip = pos_clip; 82 | return managed; 83 | } 84 | 85 | bool OpenGP::TrackballWindow::scroll_callback(double, double y_offset) { 86 | if (_mod_current == GLFW_MOD_NONE) { 87 | scene.trackball_camera.scale(scroll_multiplier * (double)y_offset); 88 | return true; 89 | } 90 | 91 | /// BUG: when button is pressed y_offset just gives 0! 92 | if (_mod_current == GLFW_MOD_SHIFT) { 93 | scene.trackball_camera.adjust_fov(y_offset); 94 | return true; 95 | } 96 | 97 | return false; 98 | } 99 | 100 | bool OpenGP::TrackballWindow::framebuffer_size_callback(int width, int height) { 101 | _width = width; 102 | _height = height; 103 | glViewport(0, 0, _width, _height); 104 | scene.trackball_camera.screen_resize(_width, _height); 105 | return true; 106 | } 107 | 108 | bool OpenGP::TrackballWindow::key_callback(int key, int scancode, int action, int mods){ 109 | if( Super::key_callback(key, scancode, action, mods) ) 110 | return true; 111 | 112 | if(action == GLFW_PRESS) 113 | _mod_current = mods; 114 | if(action == GLFW_RELEASE && (_mod_current!=GLFW_MOD_NONE)) 115 | _mod_current = GLFW_MOD_NONE; 116 | 117 | /// Reset the camera 118 | /// TODO: check meshlab bindings 119 | if(key=='R'){ 120 | scene.trackball_camera = TrackballCamera(_width, _height); 121 | return true; 122 | } 123 | return false; 124 | } 125 | 126 | OpenGP::Vec3 OpenGP::TrackballWindow::window_to_clip(const Vec3& pos_window) { 127 | Vec3 retval; 128 | retval(0) = 2.0f * static_cast(pos_window(0)) / _width - 1.0f; 129 | retval(1) = 1.0f - 2.0f * static_cast(pos_window(1)) / _height; 130 | retval(2) = 2.0f * pos_window(2) - 1.0f; 131 | return retval; 132 | } 133 | -------------------------------------------------------------------------------- /OpenGP/GL/TrackballWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | //============================================================================= 8 | namespace OpenGP { 9 | //============================================================================= 10 | 11 | /// A-la MeshLab like window with trackball controller. 12 | /// 13 | /// Mouse controls: 14 | /// - doubleclick(mouse_left): set pivot to current 3D mouse position 15 | /// - drag(mouse_left): rotate (about pivot) 16 | /// - drag(mouse_middle): pan (translate on 2D projective plane) 17 | /// - drag(mouse_left + super): pan 18 | /// - wheel: zooming 19 | /// - drag(mouse_left + shift): zooming 20 | /// - wheel+shift: change FoV (BUGGY!!!) 21 | /// 22 | /// Keyboard controls: 23 | /// - R: reset the camera 24 | /// 25 | class TrackballWindow : public GlfwWindow { 26 | typedef GlfwWindow Super; 27 | private: 28 | const int GLFW_DOUBLECLICK = (GLFW_REPEAT+1); 29 | const int GLFW_MOD_NONE = 0x0000; 30 | int _mod_current = GLFW_MOD_NONE; 31 | Vec3 old_pos_clip = Vec3::Zero(); 32 | 33 | /// @{ simple parameters (modify at own risk) 34 | public: 35 | Scalar scroll_multiplier = .25; 36 | /// @} 37 | 38 | public: 39 | TrackballWindow(const std::string& title="glfw", int width=640, int height=480) : GlfwWindow(title, width, height) {} 40 | HEADERONLY_INLINE bool mouse_press_callback(int button, int action, int mods) override; 41 | HEADERONLY_INLINE bool mouse_move_callback(double x_window, double y_window) override; 42 | HEADERONLY_INLINE bool scroll_callback(double /*x_offset*/, double y_offset) override; 43 | HEADERONLY_INLINE bool framebuffer_size_callback(int width, int height) override; 44 | HEADERONLY_INLINE bool key_callback(int key, int scancode, int action, int mods) override; 45 | 46 | private: 47 | HEADERONLY_INLINE Vec3 window_to_clip(const Vec3& pos_window); 48 | }; 49 | 50 | //============================================================================= 51 | } // namespace OpenGP 52 | //============================================================================= 53 | 54 | #ifdef HEADERONLY 55 | #include "TrackballWindow.cpp" 56 | #endif 57 | -------------------------------------------------------------------------------- /OpenGP/GL/VertexArrayObject.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | //============================================================================= 5 | namespace OpenGP { 6 | //============================================================================= 7 | 8 | class VertexArrayObject{ 9 | GLuint VAO; 10 | public: 11 | VertexArrayObject(){ 12 | glGenVertexArrays(1, &VAO); 13 | glBindVertexArray(VAO); 14 | } 15 | ~VertexArrayObject(){ glDeleteVertexArrays(1, &VAO); } 16 | void bind(){ glBindVertexArray(VAO); } 17 | void release(){ glBindVertexArray(0); } 18 | }; 19 | 20 | //============================================================================= 21 | } // namespace OpenGP 22 | //============================================================================= 23 | -------------------------------------------------------------------------------- /OpenGP/GL/check_error_gl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | static inline const char* ErrorString(GLenum error) { 5 | const char* msg; 6 | switch (error) { 7 | #define Case(Token) case Token: msg = #Token; break; 8 | Case(GL_INVALID_ENUM); 9 | Case(GL_INVALID_VALUE); 10 | Case(GL_INVALID_OPERATION); 11 | Case(GL_INVALID_FRAMEBUFFER_OPERATION); 12 | Case(GL_NO_ERROR); 13 | Case(GL_OUT_OF_MEMORY); 14 | #undef Case 15 | } 16 | 17 | return msg; 18 | } 19 | 20 | static inline void _glCheckError(const char* file, int line) { 21 | GLenum error; 22 | while ((error = glGetError()) != GL_NO_ERROR) { 23 | fprintf(stderr, "ERROR: file %s, line %i: %s.\n", file, line, 24 | ErrorString(error)); 25 | } 26 | } 27 | 28 | #ifndef NDEBUG 29 | #define check_error_gl() _glCheckError(__FILE__, __LINE__) 30 | #else 31 | #define check_error_gl() ((void)0) 32 | #endif 33 | -------------------------------------------------------------------------------- /OpenGP/GL/gl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | ///--- Load OpenGL here (GLEW is for cross platform) 4 | #include //< must be before glfw 5 | 6 | ///--- Linux needs extensions for framebuffers 7 | #if __unix__ 8 | #define GL_GLEXT_PROTOTYPES 1 9 | #include 10 | #include 11 | #endif 12 | -------------------------------------------------------------------------------- /OpenGP/GL/glfw.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /// Ensures GLEW loaded before GLFW 4 | #include 5 | #include 6 | -------------------------------------------------------------------------------- /OpenGP/GL/glfw_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "GL/glew.h" ///< must be included before GLFW 3 | #include "GLFW/glfw3.h" 4 | #include "shader_helpers.h" 5 | 6 | #if __cplusplus <= 199711L 7 | #define nullptr NULL 8 | #endif 9 | 10 | /// Convenience constants 11 | static const int ONE = 1; 12 | static const bool DONT_NORMALIZE = false; 13 | static const bool DONT_TRANSPOSE = false; 14 | static const int ZERO_STRIDE = 0; 15 | static const void* ZERO_BUFFER_OFFSET = 0; 16 | 17 | namespace OpenGP{ 18 | 19 | static int _width = 640; 20 | static int _height = 480; 21 | static void (*_display)(void) = NULL; 22 | static GLFWwindow *window = NULL; 23 | 24 | void glfwInitWindowSize(int width, int height){ 25 | _width = width; 26 | _height = height; 27 | } 28 | 29 | void error_callback(int /*error*/, const char* description) 30 | { 31 | puts(description); 32 | } 33 | 34 | int glfwMakeWindow(const char* title){ 35 | // GLFW Initialization 36 | if( !glfwInit() ){ 37 | fprintf( stderr, "Failed to initialize GLFW\n" ); 38 | return EXIT_FAILURE; 39 | } 40 | 41 | glfwSetErrorCallback(error_callback); 42 | 43 | /// Hint GLFW that we would like an OpenGL 3 context (at least) 44 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 45 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 46 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 47 | glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); 48 | #ifdef __APPLE__ 49 | // TODO test if we can use this on Windows and Linux (makes all calls below 3.2 obsolete) 50 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 51 | #endif 52 | 53 | /// Attempt to open the window: fails if required version unavailable 54 | /// @note Intel GPUs do not support OpenGL 3.0 55 | if( !(window = glfwCreateWindow(_width, _height, title, nullptr, nullptr)) ) { 56 | fprintf( stderr, "Failed to open OpenGL 3 GLFW window.\n" ); 57 | glfwTerminate(); 58 | return EXIT_FAILURE; 59 | } 60 | 61 | /// Outputs the OpenGL version 62 | int major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR); 63 | int minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR); 64 | int revision = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION); 65 | std::cout << "Opened GLFW OpenGL " << major << "." << minor << "." << revision << std::endl; 66 | 67 | glfwMakeContextCurrent(window); 68 | if(!glfwGetCurrentContext()) { 69 | std::cerr << "Couldn't create OpenGL context" << std::endl; 70 | exit(EXIT_FAILURE); 71 | } 72 | glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); // can be GLFW_CURSOR_HIDDEN 73 | 74 | // GLEW Initialization (must have a context) 75 | glewExperimental = GL_TRUE; 76 | GLenum err = glewInit(); 77 | if( err != GLEW_OK ){ 78 | fprintf( stderr, "Failed to initialize GLEW\n"); 79 | fprintf(stderr, "Error: %s\n", glewGetErrorString(err)); 80 | return EXIT_FAILURE; 81 | } 82 | 83 | /// Wipe Startup Errors (Are they caused by GLEW?) 84 | while (glGetError() != GL_NO_ERROR) {} 85 | 86 | return EXIT_SUCCESS; 87 | } 88 | 89 | 90 | /// @see glutDisplayFunc 91 | void glfwDisplayFunc(void (*display)(void)){ 92 | _display = display; 93 | } 94 | 95 | /// @see glutMainLoop 96 | void glfwMainLoop(){ 97 | assert(_display!=NULL); 98 | 99 | /// Render loop & keyboard input 100 | while(glfwGetKey(window, GLFW_KEY_ESCAPE)!=GLFW_PRESS && !glfwWindowShouldClose(window)){ 101 | _display(); 102 | glfwSwapBuffers(window); 103 | glfwPollEvents(); 104 | } 105 | 106 | /// Close OpenGL window and terminate GLFW 107 | glfwTerminate(); 108 | } 109 | 110 | } // OpenGP:: 111 | -------------------------------------------------------------------------------- /OpenGP/GL/glfw_trackball.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define GLFW_MOD_NONE 0 6 | #warning "glfw_trackball is obsolete" 7 | 8 | namespace OpenGP{ 9 | 10 | /// @todo DOCUMENT 11 | void glfwTrackball(void (*update_matrices)(Eigen::Matrix4f), void (*update_projection_matrix)()); 12 | 13 | namespace{ 14 | static Eigen::Matrix4f scale_; 15 | static Eigen::Matrix4f rotation_; 16 | static Eigen::Matrix4f old_rotation_; 17 | static Eigen::Matrix4f translation_; 18 | static Trackball trackball_; 19 | static void (*_update_matrices)(Eigen::Matrix4f) = NULL; 20 | static void (*_update_projection_matrix)() = NULL; 21 | 22 | int current_state_ = GLFW_MOD_NONE; 23 | 24 | void update_matrices() { 25 | assert(_update_matrices!=NULL); 26 | Eigen::Matrix4f model = translation_ * rotation_ * scale_; 27 | _update_matrices(model); 28 | } 29 | 30 | void resize(GLFWwindow */*window*/, int width, int height) { 31 | assert(_update_projection_matrix!=NULL); 32 | _width = width; 33 | _height = height; 34 | glViewport(0, 0, width, height); 35 | _update_projection_matrix(); 36 | update_matrices(); 37 | } 38 | 39 | void keyboard(GLFWwindow *window, int key, int /*scancode*/, int action, int /*mods*/) { 40 | if (action == GLFW_PRESS) { 41 | switch (key) { 42 | case GLFW_KEY_ESCAPE: 43 | glfwSetWindowShouldClose(window, true); 44 | break; 45 | case GLFW_KEY_SPACE: 46 | break; 47 | } 48 | } 49 | } 50 | 51 | void mouse_button(GLFWwindow *window, int button, int action, int mods) { 52 | if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) { 53 | old_rotation_ = rotation_; 54 | double x_i, y_i; 55 | glfwGetCursorPos(window, &x_i, &y_i); 56 | const float x = 2.0f * static_cast(x_i) / _width - 1.0f; 57 | const float y = 1.0f - 2.0f * static_cast(y_i) / _height; 58 | trackball_.BeginDrag(x, y); 59 | } 60 | 61 | current_state_ = mods; 62 | } 63 | 64 | void mouse_move(GLFWwindow *window, double x, double y, double old_x, double old_y) { 65 | bool view_matrices_changed = false; 66 | 67 | bool left_down = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS; 68 | bool middle_down = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS; 69 | bool right_down = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS; 70 | 71 | // Maya style controls. 72 | if ( left_down && current_state_ == GLFW_MOD_NONE ) { 73 | const float x_f = 2.0f * static_cast(x) / _width - 1.0f; 74 | const float y_f = 1.0f - 2.0f * static_cast(y) / _height; 75 | trackball_.Drag(x_f, y_f); 76 | rotation_ = trackball_.incremental_rotation() * old_rotation_; 77 | view_matrices_changed = true; 78 | } 79 | 80 | const float dx = x - old_x; 81 | const float dy = y - old_y; 82 | 83 | // Pan 84 | if ( middle_down || (left_down && (current_state_ == GLFW_MOD_SHIFT || current_state_ == GLFW_MOD_CONTROL)) ) { 85 | const float scale = 0.05f; 86 | translation_ *= Eigen::Affine3f(Eigen::Translation3f(scale * dx, -scale * dy, 0.0f)).matrix(); 87 | view_matrices_changed = true; 88 | } 89 | 90 | // Zoom 91 | if ( right_down || (left_down && (current_state_ == GLFW_MOD_ALT || current_state_ == GLFW_MOD_SUPER)) ) { 92 | const float scale = 0.05f; 93 | translation_ *= Eigen::Affine3f(Eigen::Translation3f(0.0f, 0.0f, scale * dy)).matrix(); 94 | view_matrices_changed = true; 95 | } 96 | 97 | if (view_matrices_changed) { 98 | update_matrices(); 99 | } 100 | } 101 | 102 | void mouse_pos(GLFWwindow *window, double x, double y) { 103 | static double old_x = x; 104 | static double old_y = y; 105 | mouse_move(window, x, y, old_x, old_y); 106 | old_x = x; 107 | old_y = y; 108 | } 109 | 110 | static void mouse_wheel(GLFWwindow */*window*/, double dx, double dy) { 111 | const float scale = 0.05f; 112 | translation_ *= Eigen::Affine3f(Eigen::Translation3f(scale * dx, 0.0f, scale * dy)).matrix(); 113 | update_matrices(); 114 | } 115 | 116 | void hook_trackball_callbacks( void (*update_matrices)(Eigen::Matrix4f), void (*update_projection_matrix)() ){ 117 | /// Save the callback 118 | _update_matrices = update_matrices; 119 | assert(_update_matrices!=NULL); 120 | 121 | _update_projection_matrix = update_projection_matrix; 122 | assert(_update_projection_matrix!=NULL); 123 | 124 | translation_ = Eigen::Matrix4f::Identity(); 125 | rotation_ = Eigen::Matrix4f::Identity(); 126 | old_rotation_ = Eigen::Matrix4f::Identity(); 127 | scale_ = Eigen::Matrix4f::Identity(); 128 | 129 | glfwSetKeyCallback(window, keyboard); 130 | glfwSetMouseButtonCallback(window, mouse_button); 131 | glfwSetCursorPosCallback(window, mouse_pos); 132 | glfwSetScrollCallback(window, mouse_wheel); 133 | glfwSetWindowSizeCallback(window, resize); 134 | } 135 | } 136 | 137 | void glfwTrackball(void (*update_matrices)(Eigen::Matrix4f), void (*update_projection_matrix)()){ 138 | hook_trackball_callbacks(update_matrices, update_projection_matrix); 139 | } 140 | 141 | } // OpenGP:: 142 | -------------------------------------------------------------------------------- /OpenGP/GL/opengl_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace opengl{ 3 | inline bool is_depth_test_enabled(){ 4 | GLboolean val = 0; 5 | glGetBooleanv(GL_DEPTH_TEST,&val); 6 | return val; 7 | } 8 | } -------------------------------------------------------------------------------- /OpenGP/GL/shader_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace OpenGP{ 7 | 8 | /// Compiles the vertex, geometry and fragment shaders stored in the given strings 9 | GLuint compile_shaders(const char * vshader, const char * fshader, const char * gshader = NULL) { 10 | const int SHADER_LOAD_FAILED = 0; 11 | GLint Success = GL_FALSE; 12 | int InfoLogLength; 13 | 14 | /// Create the Vertex Shader 15 | GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); 16 | 17 | /// Compile Vertex Shader 18 | fprintf(stdout, "Compiling Vertex shader: "); 19 | char const * VertexSourcePointer = vshader; 20 | glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL); 21 | glCompileShader(VertexShaderID); 22 | 23 | /// Check Vertex Shader 24 | glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Success); 25 | glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 26 | if(!Success) { 27 | std::vector VertexShaderErrorMessage(InfoLogLength); 28 | glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); 29 | fprintf(stdout, "Failed:\n%s\n", &VertexShaderErrorMessage[0]); 30 | return SHADER_LOAD_FAILED; 31 | } 32 | else 33 | fprintf(stdout, "Success\n"); 34 | 35 | /// Create the Fragment Shader 36 | GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); 37 | 38 | /// Compile Fragment Shader 39 | fprintf(stdout, "Compiling Fragment shader: "); 40 | char const * FragmentSourcePointer = fshader; 41 | glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL); 42 | glCompileShader(FragmentShaderID); 43 | 44 | /// Check Fragment Shader 45 | glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Success); 46 | glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 47 | if(!Success) { 48 | std::vector FragmentShaderErrorMessage(InfoLogLength); 49 | glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); 50 | fprintf(stdout, "Failed:\n%s\n", &FragmentShaderErrorMessage[0]); 51 | return SHADER_LOAD_FAILED; 52 | } 53 | else 54 | fprintf(stdout, "Success\n"); 55 | 56 | GLuint GeometryShaderID = 0; 57 | if(gshader != NULL) { 58 | /// Create the Geometry Shader 59 | GeometryShaderID = glCreateShader(GL_GEOMETRY_SHADER); 60 | 61 | /// Compile Geometry Shader 62 | fprintf(stdout, "Compiling Geometry shader: "); 63 | char const * GeometrySourcePointer = gshader; 64 | glShaderSource(GeometryShaderID, 1, &GeometrySourcePointer , NULL); 65 | glCompileShader(GeometryShaderID); 66 | 67 | /// Check Geometry Shader 68 | glGetShaderiv(GeometryShaderID, GL_COMPILE_STATUS, &Success); 69 | glGetShaderiv(GeometryShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 70 | if(!Success) { 71 | std::vector GeometryShaderErrorMessage(InfoLogLength); 72 | glGetShaderInfoLog(GeometryShaderID, InfoLogLength, NULL, &GeometryShaderErrorMessage[0]); 73 | fprintf(stdout, "Failed:\n%s\n", &GeometryShaderErrorMessage[0]); 74 | return SHADER_LOAD_FAILED; 75 | } 76 | else 77 | fprintf(stdout, "Success\n"); 78 | } 79 | 80 | /// Link the program 81 | fprintf(stdout, "Linking shader program: "); 82 | GLuint ProgramID = glCreateProgram(); 83 | glAttachShader(ProgramID, VertexShaderID); 84 | glAttachShader(ProgramID, FragmentShaderID); 85 | if(gshader != NULL) glAttachShader(ProgramID, GeometryShaderID); 86 | glLinkProgram(ProgramID); 87 | 88 | /// Check the program 89 | glGetProgramiv(ProgramID, GL_LINK_STATUS, &Success); 90 | glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); 91 | std::vector ProgramErrorMessage( std::max(InfoLogLength, int(1)) ); 92 | glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); 93 | if(!Success) { 94 | fprintf(stdout, "Failed:\n%s\n", &ProgramErrorMessage[0]); 95 | return SHADER_LOAD_FAILED; 96 | } 97 | else 98 | fprintf(stdout, "Success\n"); 99 | 100 | glDeleteShader(VertexShaderID); 101 | glDeleteShader(FragmentShaderID); 102 | if(gshader != NULL) glDeleteShader(GeometryShaderID); 103 | 104 | /// make sure you see the text in terminal 105 | fflush(stdout); 106 | 107 | return ProgramID; 108 | } 109 | 110 | 111 | /// Compiles the vertex, geometry and fragment shaders using file path 112 | GLuint load_shaders(const char * vertex_file_path, const char * fragment_file_path, const char * geometry_file_path = NULL) { 113 | const int SHADER_LOAD_FAILED = 0; 114 | 115 | std::string VertexShaderCode, FragmentShaderCode, GeometryShaderCode; 116 | { 117 | /// Read the Vertex Shader code from the file 118 | std::ifstream VertexShaderStream(vertex_file_path, std::ios::in); 119 | if(VertexShaderStream.is_open()) { 120 | VertexShaderCode = std::string(std::istreambuf_iterator(VertexShaderStream), 121 | std::istreambuf_iterator()); 122 | VertexShaderStream.close(); 123 | } else { 124 | printf("Could not open file: %s\n", vertex_file_path); 125 | return SHADER_LOAD_FAILED; 126 | } 127 | 128 | /// Read the Fragment Shader code from the file 129 | std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in); 130 | if(FragmentShaderStream.is_open()) { 131 | FragmentShaderCode = std::string(std::istreambuf_iterator(FragmentShaderStream), 132 | std::istreambuf_iterator()); 133 | FragmentShaderStream.close(); 134 | } else { 135 | printf("Could not open file: %s\n", fragment_file_path); 136 | return SHADER_LOAD_FAILED; 137 | } 138 | 139 | /// Read the Geometry Shader code from the file 140 | if(geometry_file_path != NULL) { 141 | std::ifstream GeometryShaderStream(geometry_file_path, std::ios::in); 142 | if(GeometryShaderStream.is_open()) { 143 | GeometryShaderCode = std::string(std::istreambuf_iterator(GeometryShaderStream), 144 | std::istreambuf_iterator()); 145 | GeometryShaderStream.close(); 146 | } else { 147 | printf("Could not open file: %s\n", geometry_file_path); 148 | return SHADER_LOAD_FAILED; 149 | } 150 | } 151 | } 152 | 153 | /// Compile them 154 | char const * VertexSourcePointer = VertexShaderCode.c_str(); 155 | char const * FragmentSourcePointer = FragmentShaderCode.c_str(); 156 | char const * GeometrySourcePointer = NULL; 157 | if(geometry_file_path != NULL) GeometrySourcePointer = GeometryShaderCode.c_str(); 158 | return compile_shaders(VertexSourcePointer, FragmentSourcePointer, GeometrySourcePointer); 159 | } 160 | 161 | } //< OpenGP:: 162 | -------------------------------------------------------------------------------- /OpenGP/MATLAB/random.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | //== NAMESPACE ================================================================ 5 | namespace OpenGP { 6 | //============================================================================= 7 | 8 | /// @see MATLAB randn(1,1) 9 | inline Scalar _randn( const Scalar mean, const Scalar stddev ){ 10 | Scalar u1 = ( static_cast ( std::rand() ) + 1 ) / ( (Scalar)RAND_MAX + 1 ); 11 | Scalar u2 = ( static_cast ( std::rand() ) + 1 ) / ( (Scalar)RAND_MAX + 1 ); 12 | assert( -2 * std::log( u1 ) >= 0 ); 13 | Scalar t1 = std::sqrt( -2 * std::log( u1 ) ) * cos( 2 * M_PI * u2 ); 14 | return mean + stddev * t1; 15 | } 16 | 17 | /// Generates matrix whose elements have normal/gaussian distribution 18 | /// @see MATLAB randn function 19 | inline MatMxN randn(int rows, int cols){ 20 | MatMxN zeros(rows, cols); 21 | for(int i=0; i ( std::rand() ) ) / ( (Scalar)RAND_MAX ); 31 | } 32 | 33 | /// Generates matrix whose elements have uniform distribution in [0,1] 34 | /// @see MATLAB rand function 35 | inline MatMxN rand(int rows, int cols){ 36 | MatMxN zeros(rows, cols); 37 | for(int i=0; i 4 | #include 5 | #include 6 | 7 | class MLogger{ 8 | bool _with_space = true; 9 | bool _with_newline = true; 10 | bool _with_fatal = false; 11 | std::string prefix=""; 12 | std::ostream& _out = std::cout; 13 | public: 14 | enum WriterFlag{space,nospace,fatal,nofatal,newline,nonewline}; 15 | public: 16 | /// @note default redirects to std::cout 17 | MLogger(std::ostream& out = std::cout) : _out(out){} 18 | 19 | /// @note allows conditional automatic newline 20 | ~MLogger(){ 21 | if(_with_newline) _out << std::endl; 22 | if(_with_fatal) exit(255); 23 | } 24 | 25 | /// @note this allows C++ types to be sent to _out 26 | template 27 | inline MLogger& operator<<(const T& log) { 28 | _out << log; 29 | if(_with_space) _out << " "; 30 | return *this; 31 | } 32 | 33 | /// Allows manipulators 34 | inline MLogger& operator<<(const WriterFlag& flag){ 35 | switch(flag){ 36 | case space: _with_space=true; break; 37 | case nospace: _with_space=false; break; 38 | case fatal: _with_fatal=true; break; 39 | case nofatal: _with_fatal=false; break; 40 | case nonewline: _with_newline=false; break; 41 | case newline: _with_newline=true; break; 42 | } 43 | return *this; 44 | } 45 | 46 | /// @note this allows mDebug() << .. 47 | inline MLogger& operator()(){ 48 | return *this; 49 | } 50 | 51 | /// @todo possible alternative: varardic templates and type-safe printf 52 | inline MLogger& operator()(const char* fmt, ...){ 53 | this->_with_space = false; ///< matches printf 54 | char buffer[100] = ""; 55 | va_list argptr; 56 | va_start(argptr,fmt); 57 | #ifdef _MSC_VER 58 | vsprintf_s(buffer, fmt, argptr); 59 | #else 60 | vsprintf(buffer, fmt, argptr); 61 | #endif 62 | va_end(argptr); 63 | _out << buffer; 64 | return *this; 65 | } 66 | 67 | static MLogger make_mLogger(){ MLogger out(std::cout); return out; } 68 | static MLogger make_mDebug(){ MLogger out(std::cout); return out; } 69 | static MLogger make_mFatal(){ MLogger out(std::cout); out._with_fatal=true; out<<"!!!FATAL:"; return out; } 70 | static MLogger make_mWarning(){ MLogger out(std::cout); out<<"!!!WARNING:"; return out; } 71 | }; 72 | 73 | #ifdef WITH_EIGEN 74 | namespace Eigen{ 75 | /// HACK: 3D column vector shown orizontally 76 | inline MLogger operator<<(MLogger m, Eigen::Vector3f& v){ 77 | printf("[%2.2f %2.2f %2.2f]", v[0], v[1], v[2]); 78 | m << MLogger::nonewline; 79 | // m << MLogger::nospace << v.transpose(); 80 | return m; 81 | } 82 | /// HACK: 3D column vector shown orizontally 83 | inline MLogger operator<<(MLogger m, Eigen::Vector2f& v){ 84 | printf("[%2.2f %2.2f]", v[0], v[1]); 85 | m << MLogger::nonewline; 86 | // m << MLogger::nospace << v.transpose(); 87 | return m; 88 | } 89 | } 90 | #endif 91 | 92 | /// Conditionally enables Qt support 93 | #ifdef QT_CORE_LIB 94 | #include 95 | inline MLogger &operator<<(MLogger& d, const QString& t) { d< 3 | 4 | /// @brief This class allows to discard stream output, similarly to sending output 5 | /// to "/dev/null" while remaining portable. See apps/isoremesh for example usage 6 | class NullStream : public std::ostream { 7 | private: 8 | class NullBuffer : public std::streambuf{ 9 | public: 10 | int overflow(int c) { return c; } 11 | } m_sb; 12 | public: 13 | NullStream() : std::ostream(&m_sb) {} 14 | }; 15 | -------------------------------------------------------------------------------- /OpenGP/Qt/QGLFormat32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | //== NAMESPACE ================================================================ 5 | namespace OpenGP { 6 | //============================================================================= 7 | 8 | class QGLFormat32 : public QGLFormat { 9 | public: 10 | QGLFormat32() { 11 | setVersion(3,2); 12 | setProfile(QGLFormat::CoreProfile); 13 | setSampleBuffers(true); ///< anti-aliasing 14 | } 15 | }; 16 | 17 | //============================================================================= 18 | } // namespace OpenGP 19 | //============================================================================= 20 | -------------------------------------------------------------------------------- /OpenGP/Qt/QGLWidget32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | //== NAMESPACE ================================================================ 6 | namespace OpenGP { 7 | //============================================================================= 8 | 9 | class QGLWidget32 : public QGLWidget{ 10 | public: 11 | QGLWidget32(QWidget* parent=0) : 12 | QGLWidget(QGLFormat32(), parent){} 13 | }; 14 | 15 | //============================================================================= 16 | } // namespace OpenGP 17 | //============================================================================= 18 | -------------------------------------------------------------------------------- /OpenGP/Qt/qglviewer/QGLMeshLabViewer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /// @brief Specialization of QGLViewer for OpenGL4 with actions that have been modified to make its default behavior match the one of MeshLab (i.e. double click to center scene, no spinning) 12 | class QGLMeshLabViewer : public QGLViewer { 13 | protected: 14 | /// Format class to enable OpenGL4 core profile 15 | class OpenGL4Format : public QGLFormat{ 16 | public: 17 | OpenGL4Format(){ 18 | setVersion(3,3); 19 | setProfile(QGLFormat::CoreProfile); 20 | setSampleBuffers(true); 21 | } 22 | }; 23 | 24 | protected: 25 | QGLMeshLabViewer() : QGLViewer(OpenGL4Format()){ 26 | /// Disable QGLViewer's default "spin" 27 | camera()->frame()->setSpinningSensitivity(100); 28 | 29 | /// Bindings @see QGLViewer::setDefaultMouseBindings() 30 | /// Extra behavior in this->mouseDoubleClickEvent() 31 | { 32 | /// Disable double click to align scene 33 | setMouseBinding(Qt::NoModifier, Qt::LeftButton, NO_CLICK_ACTION, true); /// ALIGN_CAMERA 34 | setMouseBinding(Qt::ShiftModifier, Qt::RightButton, NO_CLICK_ACTION); /// RAP_FROM_PIXEL 35 | setMouseBinding(Qt::NoModifier, Qt::MiddleButton, NO_CLICK_ACTION, true); /// ZOOM_TO_FIT 36 | } 37 | 38 | /// Disable options that give OpenGL4 troubles 39 | { 40 | bool DISABLED = false; 41 | setShortcut(CAMERA_MODE, DISABLED); 42 | setShortcut(DRAW_AXIS, DISABLED); 43 | setShortcut(DRAW_GRID, DISABLED); 44 | setShortcut(DISPLAY_FPS, DISABLED); 45 | setShortcut(STEREO, DISABLED); 46 | setShortcut(ENABLE_TEXT, DISABLED); 47 | setShortcut(EDIT_CAMERA, DISABLED); 48 | setShortcut(ANIMATION, DISABLED); 49 | setShortcut(INCREASE_FLYSPEED, DISABLED); 50 | setShortcut(DECREASE_FLYSPEED, DISABLED); 51 | setShortcut(MOVE_CAMERA_LEFT, DISABLED); 52 | setShortcut(MOVE_CAMERA_RIGHT, DISABLED); 53 | setShortcut(MOVE_CAMERA_UP, DISABLED); 54 | setShortcut(MOVE_CAMERA_DOWN, DISABLED); 55 | } 56 | } 57 | 58 | /// Remove the default tabs of the help modal widget 59 | void help(){ 60 | QGLViewer::help(); 61 | this->helpWidget()->removeTab(3); 62 | this->helpWidget()->removeTab(0); 63 | helpWidget()->setCurrentIndex(0); 64 | } 65 | 66 | void mouseDoubleClickEvent(QMouseEvent* e){ 67 | //std::cout << __FUNCTION__ << std::endl; 68 | 69 | /// MeshLAB like double click action 70 | if(e->button() == Qt::LeftButton && e->modifiers() == Qt::NoModifier){ 71 | /// Modified version of "RAP_FROM_PIXEL" 72 | if(!camera()->setPivotPointFromPixel(e->pos())){ 73 | // std::cout << "failed" << std::endl; 74 | return; // do nothing 75 | } 76 | camera()->setSceneCenter( camera()->pivotPoint() ); 77 | /// Stolen from "centerScene" 78 | camera()->frame()->projectOnLine(sceneCenter(), camera()->viewDirection()); 79 | setVisualHintsMask(1); 80 | update(); 81 | } else { 82 | /// Forward anything else to superclass 83 | QGLViewer::mouseDoubleClickEvent(e); 84 | } 85 | } 86 | 87 | static void setup_modelview(qglviewer::Camera* camera, QGLShaderProgram& program_){ 88 | ///--- Fetch matrixes from trackball 89 | Eigen::Matrix4f MVP; 90 | camera->getModelViewProjectionMatrix(MVP.data()); 91 | MVP.transposeInPlace(); 92 | Eigen::Matrix4f MV; 93 | camera->getModelViewMatrix(MV.data()); 94 | MV.transposeInPlace(); 95 | 96 | // std::cout << "MVP:\n" << MVP << std::endl; 97 | // std::cout << "MV:\n" << MV << std::endl; 98 | 99 | ///--- Set shader variables 100 | program_.setUniformValue (program_.uniformLocation ("MVP"), QMatrix4x4(MVP.data())); 101 | program_.setUniformValue (program_.uniformLocation ("MV"), QMatrix4x4(MV.data())); 102 | } 103 | }; 104 | -------------------------------------------------------------------------------- /OpenGP/Qt/qglviewer/helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace qglviewer{ 6 | Vec tr(OpenGP::Vec3 vec){ return Vec(vec.x(), vec.y(), vec.z()); } 7 | } // qglviewer:: 8 | -------------------------------------------------------------------------------- /OpenGP/README.md: -------------------------------------------------------------------------------- 1 | #This library is forked from [OpenGP](https://github.com/OpenGP/OpenGP/tree/master/src/OpenGP), credited to Dr. Andrea Tagliasacchi. 2 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/Algorithm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | //============================================================================= 5 | namespace OpenGP { 6 | //============================================================================= 7 | 8 | /// Provides shortened access to all names of Suface_mesh; 9 | class SurfaceMeshAlgorithm{ 10 | protected: 11 | template using VertexProperty = SurfaceMesh::Vertex_property; 12 | template using EdgeProperty = SurfaceMesh::Edge_property; 13 | template using FaceProperty = SurfaceMesh::Face_property; 14 | typedef SurfaceMesh::Vertex_iterator Vertex_iterator; 15 | typedef SurfaceMesh::Edge_iterator Edge_iterator; 16 | typedef SurfaceMesh::Face_iterator Face_iterator; 17 | typedef SurfaceMesh::Vertex_around_vertex_circulator Vertex_around_vertex_circulator; 18 | typedef SurfaceMesh::Halfedge_around_vertex_circulator Halfedge_around_vertex_circulator; 19 | typedef ::OpenGP::SurfaceMesh SurfaceMesh; 20 | typedef ::OpenGP::Point Point; 21 | typedef ::OpenGP::Normal Normal; 22 | typedef ::OpenGP::Scalar Scalar; 23 | typedef SurfaceMesh::Vertex Vertex; 24 | typedef SurfaceMesh::Face Face; 25 | typedef SurfaceMesh::Edge Edge; 26 | typedef SurfaceMesh::Halfedge Halfedge; 27 | }; 28 | 29 | //============================================================================= 30 | } // namespace OpenGP 31 | //============================================================================= 32 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/Eigen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | //============================================================================= 7 | namespace OpenGP{ 8 | //============================================================================= 9 | 10 | typedef Eigen::Matrix VerticesMatrix; 11 | typedef Eigen::Map VerticesMatrixMap; 12 | 13 | typedef Eigen::Matrix NormalsMatrix; 14 | typedef Eigen::Map NormalsMatrixMap; 15 | 16 | typedef Eigen::Matrix TrianglesMatrix; 17 | 18 | inline TrianglesMatrix faces_matrix(SurfaceMesh& mesh){ 19 | /// TODO check there is no garbage 20 | CHECK(mesh.is_triangle_mesh()); 21 | 22 | /// mesh must be a triangulation 23 | assert(mesh.is_triangle_mesh()); 24 | 25 | TrianglesMatrix faces; 26 | faces.resize(3,mesh.n_faces()); 27 | for(SurfaceMesh::Face f: mesh.faces()){ 28 | int icntr = 0; 29 | for(SurfaceMesh::Vertex v: mesh.vertices(f)) 30 | faces(icntr++,f.idx()) = v.idx(); 31 | } 32 | return faces; 33 | } 34 | 35 | inline VerticesMatrixMap vertices_matrix(SurfaceMesh& mesh){ 36 | auto _vpoints = mesh.vertex_property("v:point"); 37 | return VerticesMatrixMap((Scalar *)(_vpoints.data()), 3, mesh.n_vertices()); 38 | } 39 | 40 | inline NormalsMatrixMap normals_matrix(SurfaceMesh& mesh){ 41 | auto _vnormals = mesh.vertex_property("v:normal"); 42 | return NormalsMatrixMap((Scalar*)(_vnormals.data()), 3, mesh.n_vertices()); 43 | } 44 | 45 | /// @todo const version 46 | /// VerticesMatrix vertices_matrix(const SurfaceMesh& mesh){} 47 | 48 | //============================================================================= 49 | } // OpenGP:: 50 | //============================================================================= 51 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/GL/SurfaceMeshRenderCloud.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //============================================================================= 11 | namespace OpenGP { 12 | //============================================================================= 13 | 14 | class SurfaceMeshRenderCloud : public PointsRenderer{ 15 | private: 16 | SurfaceMesh& mesh; 17 | public: 18 | SurfaceMeshRenderCloud(SurfaceMesh& mesh) : mesh(mesh){ 19 | Mat3xN mat = vertices_matrix(mesh); 20 | PointsRenderer::load(mat); 21 | } 22 | Box3 bounding_box(){ return OpenGP::bounding_box(mesh); } 23 | }; 24 | 25 | //============================================================================= 26 | } // namespace OpenGP 27 | //============================================================================= 28 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/GL/SurfaceMeshRenderFlat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | //============================================================================= 13 | namespace OpenGP { 14 | //============================================================================= 15 | 16 | class SurfaceMeshRenderFlat : public SceneObject{ 17 | private: 18 | SurfaceMesh& mesh; 19 | VertexArrayObject vao; 20 | ArrayBuffer vertexbuffer; 21 | ArrayBuffer normalbuffer; 22 | ArrayBuffer barycbuffer; 23 | 24 | private: 25 | const GLchar* vshader = R"GLSL( 26 | #version 330 core 27 | uniform mat4 M; 28 | uniform mat4 MV; 29 | uniform mat4 MVP; 30 | in vec3 vbaryc; ///< per-vertex barycentric 31 | in vec3 vposition; ///< per-vertex position 32 | in vec3 vnormal; ///< per-vertex normal 33 | out vec3 fnormal; ///< per-fragment normal 34 | out vec3 fbaryc; ///< per-fragment barycentric 35 | void main(){ 36 | gl_Position = MVP * vec4(vposition, 1.0); 37 | fnormal = normalize( inverse(transpose(mat3(MV))) * vnormal ); 38 | fbaryc = vbaryc; 39 | } 40 | )GLSL"; 41 | 42 | const char* fshader = R"GLSL( 43 | #version 330 core 44 | // uniform vec3 LDIR; ///< TODO: fix me 45 | in vec3 fnormal; 46 | in vec3 fbaryc; 47 | out vec4 fcolor; 48 | 49 | // http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates 50 | float edgeFactor(){ 51 | vec3 d = 1.5 * fwidth(fbaryc); 52 | vec3 a3 = smoothstep(vec3(0.0), d, fbaryc); 53 | return min(min(a3.x, a3.y), a3.z); 54 | } 55 | 56 | void main(){ 57 | ///--- Face flat shading 58 | vec3 LDIR = vec3(0,0,1); 59 | vec3 basecolor = vec3(1,0,0); 60 | vec3 ldir = normalize(LDIR); 61 | float albedo = max( dot( normalize(fnormal), ldir ), 0 ); 62 | vec4 face_color = vec4(basecolor*albedo, 1); 63 | 64 | ///--- colors & mix 65 | vec4 edge_color = vec4(0,0,0,1); 66 | fcolor = mix(edge_color, face_color, edgeFactor()); 67 | } 68 | )GLSL"; 69 | 70 | public: 71 | SurfaceMeshRenderFlat(SurfaceMesh& mesh) : mesh(mesh){} 72 | 73 | void init(){ 74 | ///--- Shader 75 | program.add_vshader_from_source(vshader); 76 | program.add_fshader_from_source(fshader); 77 | program.link(); 78 | 79 | ///--- Data 80 | init_data(); 81 | 82 | ///--- Attributes 83 | program.bind(); 84 | vao.bind(); 85 | program.set_attribute("vposition", vertexbuffer); 86 | program.set_attribute("vnormal", normalbuffer); 87 | program.set_attribute("vbaryc", barycbuffer); 88 | vao.release(); 89 | program.release(); 90 | } 91 | 92 | void init_data(){ 93 | CHECK(mesh.is_triangle_mesh()); 94 | CHECK(program.is_valid()); 95 | 96 | std::vector v_tri; ///< per-vertex positions 97 | std::vector n_tri; ///< per-vertex (flat) normals 98 | std::vector b_tri; ///< per-vertex barycentric [1,0,0] 99 | 100 | auto vpoints = mesh.get_vertex_property("v:point"); 101 | auto fnormals = mesh.get_face_property("f:normal"); 102 | CHECK(fnormals); 103 | CHECK(vpoints); 104 | 105 | std::vector baryc(3); 106 | baryc[0] = Vec3(1,0,0); 107 | baryc[1] = Vec3(0,1,0); 108 | baryc[2] = Vec3(0,0,1); 109 | 110 | ///--- Splits mesh in independent triangles 111 | { 112 | for(auto f: mesh.faces()){ 113 | Vec3 fnormal = fnormals[f]; 114 | int v_idx = 0; ///< cycles over baryc 115 | for(auto v: mesh.vertices(f)){ 116 | v_tri.push_back(vpoints[v]); 117 | /// slightly wasteful, can improve? 118 | n_tri.push_back(fnormal); 119 | b_tri.push_back(baryc[v_idx++]); 120 | } 121 | } 122 | } 123 | 124 | vertexbuffer.upload(v_tri); 125 | normalbuffer.upload(n_tri); 126 | barycbuffer.upload(b_tri); 127 | } 128 | 129 | Box3 bounding_box(){ return OpenGP::bounding_box(mesh); } 130 | 131 | void display(){ 132 | program.bind(); 133 | vao.bind(); 134 | glDrawArrays(GL_TRIANGLES, 0, mesh.n_faces()*3 /*#verts*/); 135 | vao.release(); 136 | program.release(); 137 | } 138 | }; 139 | 140 | //============================================================================= 141 | } // namespace OpenGP 142 | //============================================================================= 143 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/GL/SurfaceMeshRenderShaded.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //============================================================================= 5 | namespace OpenGP { 6 | //============================================================================= 7 | 8 | const static GLchar* SurfaceMeshRenderShaded_vshader = R"GLSL( 9 | #version 330 core 10 | uniform mat4 M; 11 | uniform mat4 MV; 12 | uniform mat4 MVP; 13 | 14 | ///--- Colormap 1D texture 15 | uniform sampler1D colormap; 16 | uniform int use_colormap; ///< 0 17 | uniform float colormap_min; ///< 0.0; 18 | uniform float colormap_max; ///< 1.0; 19 | 20 | ///--- Attributes 21 | in vec3 vposition; ///< per-vertex position 22 | in vec3 vnormal; ///< per-vertex normal 23 | in float vquality; ///< per-vertex quality 24 | 25 | ///--- Outputs 26 | out vec3 fnormal; ///< per-fragment normal 27 | out vec3 fcolor; ///< per-fragment color 28 | 29 | void main(){ 30 | gl_Position = MVP * vec4(vposition, 1.0); 31 | fnormal = normalize( inverse(transpose(mat3(MV))) * vnormal ); 32 | if(use_colormap==0) 33 | fcolor = vec3(1,0,0); 34 | else{ 35 | float vquality_normalized = (vquality - colormap_min) / (colormap_max - colormap_min); 36 | fcolor = texture(colormap, vquality_normalized).rgb; 37 | } 38 | } 39 | )GLSL"; 40 | 41 | const static char* SurfaceMeshRenderShaded_fshader = R"GLSL( 42 | #version 330 core 43 | // uniform vec3 LDIR; ///< TODO: fix me 44 | in vec3 fnormal; ///< normal camera coords 45 | in vec3 fcolor; 46 | out vec4 FragColor; 47 | 48 | void main(){ 49 | vec3 LDIR = vec3(0,0,1); 50 | vec3 ldir = normalize(LDIR); 51 | float albedo = max( dot( normalize(fnormal), ldir ), 0 ); 52 | vec3 basecolor = fcolor; 53 | FragColor = vec4(basecolor*albedo, 1); 54 | // FragColor = vec4(fnormal,1); ///< normal map 55 | } 56 | )GLSL"; 57 | 58 | void SurfaceMeshRenderShaded::init(){ 59 | ///--- Shaders 60 | program.add_vshader_from_source(SurfaceMeshRenderShaded_vshader); 61 | program.add_fshader_from_source(SurfaceMeshRenderShaded_fshader); 62 | program.link(); 63 | 64 | ///--- Vertex positions 65 | auto vpoints = mesh.get_vertex_property("v:point"); CHECK(vpoints); 66 | v_buffer.upload_raw(vpoints.data(), mesh.n_vertices()); 67 | 68 | ///--- Vertex normals 69 | auto vnormals = mesh.get_vertex_property("v:normal"); CHECK(vnormals); 70 | n_buffer.upload_raw(vnormals.data(), mesh.n_vertices()); 71 | 72 | ///--- Vertex quality (Optional) 73 | auto vqualitys = mesh.get_vertex_property("v:quality"); 74 | if(vqualitys) q_buffer.upload_raw(vqualitys.data(), mesh.n_vertices()); 75 | 76 | ///--- Creates index/element buffer data 77 | CHECK(mesh.n_faces()>0); 78 | std::vector triangles; 79 | for(auto f: mesh.faces()) 80 | for(auto v: mesh.vertices(f)) 81 | triangles.push_back(v.idx()); 82 | i_buffer.upload(triangles); 83 | 84 | ///--- Attributes 85 | program.bind(); 86 | vao.bind(); 87 | ///--- Defaulted attributes 88 | program.set_attribute("vquality", 0.0f); 89 | 90 | ///--- Attributes 91 | program.set_attribute("vposition", v_buffer); 92 | program.set_attribute("vnormal", n_buffer); 93 | if(vqualitys) program.set_attribute("vquality", q_buffer); 94 | 95 | ///--- Colormap texture setup 96 | { 97 | // TODO: wrap this within the Game Engine 98 | const int sz=3; 99 | GLfloat _tex_data[3*sz] = {/*red*/ 1.0, 0.0, 0.0, 100 | /*yellow*/ 1.0, 1.0, 0.0, 101 | /*green*/ 0.0, 1.0, 0.0}; 102 | glGenTextures(1, &_tex); 103 | glBindTexture(GL_TEXTURE_1D, _tex); 104 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, sz, 0, GL_RGB, GL_FLOAT, _tex_data); 105 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 106 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 107 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 108 | program.bind(); ///< note set_attribute unbound them 109 | GLint tex_id = glGetUniformLocation(program.programId(), "colormap"); 110 | glActiveTexture(GL_TEXTURE0+0); 111 | glUniform1i(tex_id, 0); 112 | program.release(); 113 | } 114 | vao.release(); 115 | program.release(); 116 | } 117 | 118 | void OpenGP::SurfaceMeshRenderShaded::display(){ 119 | program.bind(); 120 | vao.bind(); 121 | ///--- Bind textures 122 | glActiveTexture(GL_TEXTURE0+0); 123 | glBindTexture(GL_TEXTURE_1D, _tex); 124 | 125 | ///--- Upload settings 126 | program.set_uniform("use_colormap", (int) _use_colormap); 127 | program.set_uniform("colormap_min", (float)_colormap_min); 128 | program.set_uniform("colormap_max", (float)_colormap_max); 129 | 130 | ///--- Draw data 131 | glDrawElements(GL_TRIANGLES, i_buffer.size(), GL_UNSIGNED_INT, ZERO_BUFFER_OFFSET); 132 | vao.release(); 133 | program.release(); 134 | } 135 | 136 | Box3 SurfaceMeshRenderShaded::bounding_box(){ 137 | return OpenGP::bounding_box(mesh); 138 | } 139 | 140 | void SurfaceMeshRenderShaded::colormap_enabled(bool enabled){ 141 | _use_colormap = enabled; 142 | } 143 | 144 | void SurfaceMeshRenderShaded::colormap_set_range(Scalar min, Scalar max){ 145 | _colormap_min = min; 146 | _colormap_max = max; 147 | } 148 | 149 | //============================================================================= 150 | } // namespace OpenGP 151 | //============================================================================= 152 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/GL/SurfaceMeshRenderShaded.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | //============================================================================= 7 | namespace OpenGP { 8 | //============================================================================= 9 | 10 | class SurfaceMeshRenderShaded : public SceneObject{ 11 | private: 12 | SurfaceMesh& mesh; 13 | VertexArrayObject vao; 14 | ArrayBuffer v_buffer; 15 | ArrayBuffer n_buffer; 16 | ArrayBuffer q_buffer; 17 | ElementArrayBuffer i_buffer; 18 | GLuint _tex; ///< Colormap Texture ID 19 | 20 | public: 21 | SurfaceMeshRenderShaded(SurfaceMesh& mesh) : mesh(mesh){} 22 | HEADERONLY_INLINE void init(); 23 | HEADERONLY_INLINE void display(); 24 | HEADERONLY_INLINE Box3 bounding_box(); 25 | 26 | /// @{ color quality mapping 27 | private: 28 | bool _use_colormap = false; 29 | Scalar _colormap_min = 0.0f; 30 | Scalar _colormap_max = 1.0f; 31 | public: 32 | HEADERONLY_INLINE void colormap_enabled(bool); 33 | HEADERONLY_INLINE void colormap_set_range(Scalar min, Scalar max); 34 | /// @} 35 | }; 36 | 37 | //============================================================================= 38 | } // namespace OpenGP 39 | //============================================================================= 40 | 41 | #ifdef HEADERONLY 42 | #include "SurfaceMeshRenderShaded.cpp" 43 | #endif 44 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/GL/SurfaceMeshRenderVertexNormals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //============================================================================= 11 | namespace OpenGP { 12 | //============================================================================= 13 | 14 | class SurfaceMeshRenderVertexNormals : public SegmentsRenderer{ 15 | protected: 16 | SurfaceMesh& mesh; 17 | 18 | public: 19 | SurfaceMeshRenderVertexNormals(SurfaceMesh& mesh, Scalar offset=.1) : mesh(mesh){ 20 | this->color = Vec3(0,0,1); ///< blue normals 21 | Mat3xN P1 = OpenGP::vertices_matrix(mesh); 22 | Mat3xN N = OpenGP::normals_matrix(mesh); 23 | Mat3xN P2 = P1 + offset*N; 24 | SegmentsRenderer::load(P1,P2); 25 | } 26 | Box3 bounding_box(){ return OpenGP::bounding_box(mesh); } 27 | }; 28 | 29 | //============================================================================= 30 | } // namespace OpenGP 31 | //============================================================================= 32 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/IO/IO.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | //== NAMESPACE ================================================================ 9 | namespace OpenGP { 10 | //============================================================================= 11 | 12 | namespace{ 13 | inline char easytolower(char in){ 14 | static std::locale locale; 15 | return std::tolower(in, locale); 16 | } 17 | } // ::anonymous 18 | 19 | bool read_mesh(SurfaceMesh& mesh, const std::string& filename) 20 | { 21 | std::setlocale(LC_NUMERIC, "C"); 22 | 23 | // clear mesh before reading from file 24 | mesh.clear(); 25 | 26 | // extract file extension 27 | std::string::size_type dot(filename.rfind(".")); 28 | if (dot == std::string::npos) return false; 29 | std::string ext = filename.substr(dot+1, filename.length()-dot-1); 30 | std::transform(ext.begin(), ext.end(), ext.begin(), easytolower); 31 | 32 | // extension determines reader 33 | if (ext == "off") 34 | { 35 | return read_off(mesh, filename); 36 | } 37 | else if (ext == "obj") 38 | { 39 | return read_obj(mesh, filename); 40 | } 41 | else if (ext == "stl") 42 | { 43 | return read_stl(mesh, filename); 44 | } 45 | 46 | // we didn't find a reader module 47 | return false; 48 | } 49 | 50 | 51 | //----------------------------------------------------------------------------- 52 | 53 | 54 | bool write_mesh(const SurfaceMesh& mesh, const std::string& filename) 55 | { 56 | // extract file extension 57 | std::string::size_type dot(filename.rfind(".")); 58 | if (dot == std::string::npos) return false; 59 | std::string ext = filename.substr(dot+1, filename.length()-dot-1); 60 | std::transform(ext.begin(), ext.end(), ext.begin(), easytolower); 61 | 62 | 63 | // extension determines reader 64 | if (ext == "off") 65 | { 66 | return write_off(mesh, filename); 67 | } 68 | else if(ext=="obj") 69 | { 70 | return write_obj(mesh, filename); 71 | } 72 | 73 | // we didn't find a writer module 74 | return false; 75 | } 76 | 77 | //============================================================================= 78 | } // namespace OpenGP 79 | //============================================================================= 80 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/IO/IO.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | //============================================================================= 6 | namespace OpenGP { 7 | //============================================================================= 8 | 9 | HEADERONLY_INLINE bool read_mesh(SurfaceMesh& mesh, const std::string& filename); 10 | HEADERONLY_INLINE bool read_off(SurfaceMesh& mesh, const std::string& filename); 11 | HEADERONLY_INLINE bool read_obj(SurfaceMesh& mesh, const std::string& filename); 12 | HEADERONLY_INLINE bool read_stl(SurfaceMesh& mesh, const std::string& filename); 13 | HEADERONLY_INLINE bool write_mesh(const SurfaceMesh& mesh, const std::string& filename); 14 | HEADERONLY_INLINE bool write_off(const SurfaceMesh& mesh, const std::string& filename); 15 | HEADERONLY_INLINE bool write_obj(const SurfaceMesh& mesh, const std::string& filename); 16 | 17 | /// Private helper function 18 | template void read(FILE* in, T& t) 19 | { 20 | size_t n_items(0); 21 | n_items = fread((char*)&t, 1, sizeof(t), in); 22 | assert(n_items > 0); 23 | } 24 | 25 | //============================================================================= 26 | } // namespace OpenGP 27 | //============================================================================= 28 | 29 | #ifdef HEADERONLY 30 | #include "IO.cpp" 31 | #include "IO_obj.cpp" 32 | #include "IO_off.cpp" 33 | #include "IO_poly.cpp" 34 | #include "IO_stl.cpp" 35 | #endif 36 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/IO/IO_obj.cpp: -------------------------------------------------------------------------------- 1 | //== INCLUDES ================================================================= 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | //============================================================================= 8 | namespace OpenGP { 9 | //============================================================================= 10 | 11 | bool read_obj(SurfaceMesh& mesh, const std::string& filename) { 12 | char s[200]; 13 | float x, y, z; 14 | std::vector vertices; 15 | std::vector all_tex_coords; //individual texture coordinates 16 | std::vector halfedge_tex_idx; //texture coordinates sorted for halfedges 17 | SurfaceMesh::Halfedge_property tex_coords = mesh.halfedge_property("h:texcoord"); 18 | bool with_tex_coord=false; 19 | 20 | // clear mesh 21 | mesh.clear(); 22 | 23 | // open file (in ASCII mode) 24 | FILE* in = fopen(filename.c_str(), "r"); 25 | if (!in) return false; 26 | 27 | // clear line once 28 | memset(&s, 0, 200); 29 | 30 | // pre-parse to find out the number of vertices 31 | { 32 | uint vnormal_counter = 0; // number of vertex normals parsed 33 | while (in && !feof(in) && fgets(s, 200, in)) { 34 | if (s[0] == '#' || isspace(s[0])) continue; // comment 35 | else if (strncmp(s, "v ", 2) == 0) { if (sscanf(s, "v %f %f %f", &x, &y, &z)) mesh.add_vertex(Vec3(0,0,0)); } 36 | else if (strncmp(s, "vn ", 3) == 0) { if (sscanf(s, "vn %f %f %f", &x, &y, &z)) vnormal_counter++; } 37 | else continue; 38 | } 39 | 40 | // If we have read any vertex normals, it must match the number of vertices 41 | // and in this case allocate memory for normals 42 | if (vnormal_counter!=0){ 43 | assert(vnormal_counter==mesh.n_vertices()); 44 | mesh.add_vertex_property("v:normal"); 45 | } 46 | 47 | // Start from the beginning again 48 | in = freopen(filename.c_str(),"r",in); 49 | } 50 | 51 | // parse line by line (currently only supports vertex positions & faces 52 | uint vpoint_counter = 0; //< number of vertex positions parsed 53 | uint vnormal_counter = 0; //< number of vertex normals parsed 54 | auto vpoints = mesh.get_vertex_property("v:point"); 55 | auto vnormals = mesh.get_vertex_property("v:normal"); 56 | while (in && !feof(in) && fgets(s, 200, in)) { 57 | // comment 58 | if (s[0] == '#' || isspace(s[0])) continue; 59 | 60 | // vertex 61 | else if (strncmp(s, "v ", 2) == 0) { 62 | if (sscanf(s, "v %f %f %f", &x, &y, &z)) { 63 | vpoints[ SurfaceMesh::Vertex(vpoint_counter++) ] = Vec3(x,y,z); 64 | // mesh.add_vertex(Vec3(x,y,z)); 65 | } 66 | } 67 | // normal 68 | else if (strncmp(s, "vn ", 3) == 0) { 69 | int n_read = sscanf(s, "vn %f %f %f", &x, &y, &z); 70 | assert((n_read==0) || (n_read==3)); 71 | if (n_read) { 72 | // note: problematic as it can be either a vertex property when interpolated or a halfedge property for hard edges 73 | assert(vnormal_counter points = mesh.get_vertex_property("v:point"); 189 | for (SurfaceMesh::Vertex_iterator vit=mesh.vertices_begin(); vit!=mesh.vertices_end(); ++vit) { 190 | const Vec3& p = points[*vit]; 191 | fprintf(out, "v %.10f %.10f %.10f\n", p[0], p[1], p[2]); 192 | } 193 | 194 | //normals 195 | SurfaceMesh::Vertex_property normals = mesh.get_vertex_property("v:normal"); 196 | if (normals) { 197 | for (SurfaceMesh::Vertex_iterator vit=mesh.vertices_begin(); vit!=mesh.vertices_end(); ++vit) { 198 | const Vec3& p = normals[*vit]; 199 | fprintf(out, "vn %.10f %.10f %.10f\n", p[0], p[1], p[2]); 200 | } 201 | } 202 | 203 | //optionally texture coordinates 204 | // do we have them? 205 | std::vector h_props= mesh.halfedge_properties(); 206 | bool with_tex_coord = false; 207 | std::vector::iterator h_prop_end = h_props.end(); 208 | std::vector::iterator h_prop_start= h_props.begin(); 209 | while (h_prop_start!=h_prop_end) { 210 | if (0==(*h_prop_start).compare("h:texcoord")) { 211 | with_tex_coord=true; 212 | } 213 | ++h_prop_start; 214 | } 215 | 216 | //if so then add 217 | if (with_tex_coord) { 218 | SurfaceMesh::Halfedge_property tex_coord = mesh.get_halfedge_property("h:texcoord"); 219 | for (SurfaceMesh::Halfedge_iterator hit=mesh.halfedges_begin(); hit!=mesh.halfedges_end(); ++hit) { 220 | const TextureCoordinate& pt = tex_coord[*hit]; 221 | fprintf(out, "vt %.10f %.10f %.10f\n", pt[0], pt[1], pt[2]); 222 | } 223 | } 224 | 225 | //faces 226 | for (SurfaceMesh::Face_iterator fit=mesh.faces_begin(); fit!=mesh.faces_end(); ++fit) { 227 | fprintf(out, "f"); 228 | SurfaceMesh::Vertex_around_face_circulator fvit=mesh.vertices(*fit), fvend=fvit; 229 | SurfaceMesh::Halfedge_around_face_circulator fhit=mesh.halfedges(*fit); 230 | do { 231 | if (with_tex_coord) { 232 | // write vertex index, tex_coord index and normal index 233 | fprintf(out, " %d/%d/%d", (*fvit).idx()+1, (*fhit).idx()+1, (*fvit).idx()+1); 234 | ++fhit; 235 | } else { 236 | // write vertex index and normal index 237 | fprintf(out, " %d//%d", (*fvit).idx()+1, (*fvit).idx()+1); 238 | } 239 | } while (++fvit != fvend); 240 | fprintf(out, "\n"); 241 | } 242 | 243 | fclose(out); 244 | return true; 245 | } 246 | 247 | 248 | //============================================================================= 249 | } // namespace OpenGP 250 | //============================================================================= 251 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/IO/IO_poly.cpp: -------------------------------------------------------------------------------- 1 | //== INCLUDES ================================================================= 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | //== NAMESPACES =============================================================== 10 | 11 | 12 | namespace OpenGP { 13 | 14 | 15 | //== IMPLEMENTATION =========================================================== 16 | 17 | 18 | //----------------------------------------------------------------------------- 19 | 20 | 21 | bool read_poly(SurfaceMesh& mesh, const std::string& filename) 22 | { 23 | unsigned int n_items; 24 | 25 | // open file (in binary mode) 26 | FILE* in = fopen(filename.c_str(), "rb"); 27 | if (!in) return false; 28 | 29 | 30 | // clear mesh 31 | mesh.clear(); 32 | 33 | 34 | // how many elements? 35 | unsigned int nv, ne, nh, nf; 36 | read(in, nv); 37 | read(in, ne); 38 | read(in, nf); 39 | nh = 2*ne; 40 | 41 | 42 | // resize containers 43 | mesh.vprops_.resize(nv); 44 | mesh.hprops_.resize(nh); 45 | mesh.eprops_.resize(ne); 46 | mesh.fprops_.resize(nf); 47 | 48 | 49 | // get properties 50 | SurfaceMesh::Vertex_property vconn = mesh.vertex_property("v:connectivity"); 51 | SurfaceMesh::Halfedge_property hconn = mesh.halfedge_property("h:connectivity"); 52 | SurfaceMesh::Face_property fconn = mesh.face_property("f:connectivity"); 53 | SurfaceMesh::Vertex_property point = mesh.vertex_property("v:point"); 54 | 55 | // read properties from file 56 | n_items = fread((char*)vconn.data(), sizeof(SurfaceMesh::Vertex_connectivity), nv, in); 57 | n_items = fread((char*)hconn.data(), sizeof(SurfaceMesh::Halfedge_connectivity), nh, in); 58 | n_items = fread((char*)fconn.data(), sizeof(SurfaceMesh::Face_connectivity), nf, in); 59 | n_items = fread((char*)point.data(), sizeof(Vec3), nv, in); 60 | (void)n_items; //< unused warning 61 | 62 | fclose(in); 63 | return true; 64 | } 65 | 66 | //============================================================================= 67 | } // namespace OpenGP 68 | //============================================================================= 69 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/IO/IO_stl.cpp: -------------------------------------------------------------------------------- 1 | //== INCLUDES ================================================================= 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | //== NAMESPACES =============================================================== 12 | 13 | 14 | namespace OpenGP { 15 | 16 | 17 | //== IMPLEMENTATION =========================================================== 18 | 19 | 20 | //----------------------------------------------------------------------------- 21 | 22 | 23 | // helper class for STL reader 24 | class CmpVec{ 25 | typedef Vec3 Normal; 26 | typedef Vec3 TextureCoordinate; 27 | public: 28 | 29 | CmpVec(float _eps=FLT_MIN) : eps_(_eps) {} 30 | 31 | bool operator()(const Vec3& v0, const Vec3& v1) const 32 | { 33 | if (fabs(v0[0] - v1[0]) <= eps_) 34 | { 35 | if (fabs(v0[1] - v1[1]) <= eps_) 36 | { 37 | return (v0[2] < v1[2] - eps_); 38 | } 39 | else return (v0[1] < v1[1] - eps_); 40 | } 41 | else return (v0[0] < v1[0] - eps_); 42 | } 43 | 44 | private: 45 | float eps_; 46 | }; 47 | 48 | 49 | //----------------------------------------------------------------------------- 50 | 51 | 52 | bool read_stl(SurfaceMesh& mesh, const std::string& filename){ 53 | // typedef Vec3 Normal; 54 | // typedef Vec3 TextureCoordinate; 55 | 56 | char line[100], *c; 57 | unsigned int i, nT; 58 | Vec3 p; 59 | SurfaceMesh::Vertex v; 60 | std::vector vertices(3); 61 | size_t n_items(0); 62 | 63 | CmpVec comp(FLT_MIN); 64 | std::map vMap(comp); 65 | std::map::iterator vMapIt; 66 | 67 | // clear mesh 68 | mesh.clear(); 69 | 70 | // open file (in ASCII mode) 71 | FILE* in = fopen(filename.c_str(), "r"); 72 | if (!in) return false; 73 | 74 | 75 | // ASCII or binary STL? 76 | c = fgets(line, 6, in); 77 | assert(c != NULL); 78 | const bool binary = ((strncmp(line, "SOLID", 5) != 0) && 79 | (strncmp(line, "solid", 5) != 0)); 80 | 81 | 82 | // parse binary STL 83 | if (binary) 84 | { 85 | // re-open file in binary mode 86 | fclose(in); 87 | in = fopen(filename.c_str(), "rb"); 88 | if (!in) return false; 89 | 90 | // skip dummy header 91 | n_items = fread(line, 1, 80, in); 92 | assert(n_items > 0); 93 | 94 | // read number of triangles 95 | read(in, nT); 96 | 97 | // read triangles 98 | while (nT) 99 | { 100 | // skip triangle normal 101 | n_items = fread(line, 1, 12, in); 102 | assert(n_items > 0); 103 | // triangle's vertices 104 | for (i=0; i<3; ++i) 105 | { 106 | read(in, p); 107 | 108 | // has vector been referenced before? 109 | if ((vMapIt=vMap.find(p)) == vMap.end()) 110 | { 111 | // No : add vertex and remember idx/vector mapping 112 | v = mesh.add_vertex((Vec3)p); 113 | vertices[i] = v; 114 | vMap[p] = v; 115 | } 116 | else 117 | { 118 | // Yes : get index from map 119 | vertices[i] = vMapIt->second; 120 | } 121 | } 122 | 123 | // Add face only if it is not degenerated 124 | if ((vertices[0] != vertices[1]) && 125 | (vertices[0] != vertices[2]) && 126 | (vertices[1] != vertices[2])) 127 | mesh.add_face(vertices); 128 | 129 | n_items = fread(line, 1, 2, in); 130 | assert(n_items > 0); 131 | --nT; 132 | } 133 | } 134 | 135 | 136 | // parse ASCII STL 137 | else 138 | { 139 | // parse line by line 140 | while (in && !feof(in) && fgets(line, 100, in)) 141 | { 142 | // skip white-space 143 | for (c=line; isspace(*c) && *c!='\0'; ++c) {}; 144 | 145 | // face begins 146 | if ((strncmp(c, "outer", 5) == 0) || 147 | (strncmp(c, "OUTER", 5) == 0)) 148 | { 149 | // read three vertices 150 | for (i=0; i<3; ++i) 151 | { 152 | // read line 153 | c = fgets(line, 100, in); 154 | assert(c != NULL); 155 | 156 | // skip white-space 157 | for (c=line; isspace(*c) && *c!='\0'; ++c) {}; 158 | 159 | // read x, y, z 160 | sscanf(c+6, "%f %f %f", &p[0], &p[1], &p[2]); 161 | 162 | // has vector been referenced before? 163 | if ((vMapIt=vMap.find(p)) == vMap.end()) 164 | { 165 | // No : add vertex and remember idx/vector mapping 166 | v = mesh.add_vertex((Vec3)p); 167 | vertices[i] = v; 168 | vMap[p] = v; 169 | } 170 | else 171 | { 172 | // Yes : get index from map 173 | vertices[i] = vMapIt->second; 174 | } 175 | } 176 | 177 | // Add face only if it is not degenerated 178 | if ((vertices[0] != vertices[1]) && 179 | (vertices[0] != vertices[2]) && 180 | (vertices[1] != vertices[2])) 181 | mesh.add_face(vertices); 182 | } 183 | } 184 | } 185 | 186 | 187 | fclose(in); 188 | return true; 189 | } 190 | 191 | 192 | //============================================================================= 193 | } // namespace OpenGP 194 | //============================================================================= 195 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/Subdivision/Loop.cpp: -------------------------------------------------------------------------------- 1 | #include "Loop.h" 2 | #include 3 | 4 | void SurfaceMeshSubdivideLoop::exec(SurfaceMesh& mesh){ 5 | /// TODO: other pre-conditions? 6 | CHECK(mesh.is_triangle_mesh()); 7 | 8 | // reserve memory 9 | int nv = mesh.n_vertices(); 10 | int ne = mesh.n_edges(); 11 | int nf = mesh.n_faces(); 12 | mesh.reserve(nv+ne, 2*ne+3*nf, 4*nf); 13 | 14 | // get properties 15 | VertexProperty points = mesh.vertex_property("v:point"); 16 | VertexProperty vpoint = mesh.add_vertex_property("loop:vpoint"); 17 | EdgeProperty epoint = mesh.add_edge_property("loop:epoint"); 18 | VertexProperty vfeature = mesh.get_vertex_property("v:feature"); 19 | EdgeProperty efeature = mesh.get_edge_property("e:feature"); 20 | 21 | // compute vertex positions 22 | for(Vertex v: mesh.vertices()){ 23 | if ( /*isolated vertex?*/ mesh.is_isolated(v)){ 24 | vpoint[v] = points[v]; 25 | } 26 | else if (/*boundary vertex?*/ mesh.is_boundary(v) ) { 27 | Halfedge h1 = mesh.halfedge(v); 28 | Halfedge h0 = mesh.prev_halfedge(h1); 29 | Point p = points[v]; 30 | p *= 6.0; 31 | p += points[mesh.to_vertex(h1)]; 32 | p += points[mesh.from_vertex(h0)]; 33 | p *= 0.125; 34 | vpoint[v] = p; 35 | } 36 | 37 | // interior feature vertex? 38 | else if (vfeature && vfeature[v]) { 39 | Point p = points[v]; 40 | p *= 6.0; 41 | int count = 0; 42 | 43 | for(Halfedge vh: mesh.halfedges(v)){ 44 | if (efeature[mesh.edge(vh)]) { 45 | p += points[mesh.to_vertex(vh)]; 46 | ++count; 47 | } 48 | } 49 | 50 | if (count == 2) { // vertex is on feature edge 51 | p *= 0.125; 52 | vpoint[v] = p; 53 | } else { // keep fixed 54 | vpoint[v] = points[v]; 55 | } 56 | } 57 | 58 | // interior vertex 59 | else { 60 | Point p = Point::Zero(); 61 | Scalar inv_k = 1.0 / mesh.valence(v); 62 | for(Vertex vvit: mesh.vertices(v)) 63 | p += inv_k * points[vvit]; 64 | Scalar beta = (0.625 - pow(0.375 + 0.25*cos(2.0*M_PI*inv_k), 2.0)); 65 | 66 | vpoint[v] = points[v]*(Scalar)(1.0-beta) + beta*p; 67 | } 68 | } 69 | 70 | // compute edge positions 71 | for(Edge e: mesh.edges()){ 72 | if ( /*boundary or feature edge?*/ mesh.is_boundary(e) || (efeature && efeature[e])) { 73 | epoint[e] = (points[mesh.vertex(e,0)] + points[mesh.vertex(e,1)]) * Scalar(0.5); 74 | } 75 | else /*interior edge*/ { 76 | Halfedge h0 = mesh.halfedge(e, 0); 77 | Halfedge h1 = mesh.halfedge(e, 1); 78 | Point p = points[mesh.to_vertex(h0)]; 79 | p += points[mesh.to_vertex(h1)]; 80 | p *= 3.0; 81 | p += points[mesh.to_vertex(mesh.next_halfedge(h0))]; 82 | p += points[mesh.to_vertex(mesh.next_halfedge(h1))]; 83 | p *= 0.125; 84 | epoint[e] = p; 85 | } 86 | } 87 | 88 | // set new vertex positions 89 | for(Vertex v: mesh.vertices()) 90 | points[v] = vpoint[v]; 91 | 92 | // inserts new vertices on edges 93 | for(Edge e: mesh.edges()){ 94 | // feature edge? 95 | if (efeature && efeature[e]) { 96 | Halfedge e_v = mesh.insert_vertex(e, epoint[e]); 97 | Vertex v = mesh.to_vertex(e_v); 98 | Edge e = *(--mesh.edges_end()); 99 | vfeature[v] = true; 100 | efeature[e] = true; 101 | } 102 | 103 | // normal edge 104 | else { 105 | mesh.insert_vertex(e, epoint[e]); 106 | } 107 | } 108 | 109 | // split faces 110 | for(Face f: mesh.faces()){ 111 | Halfedge h = mesh.halfedge(f); 112 | mesh.insert_edge(h, mesh.next_halfedge(mesh.next_halfedge(h))); 113 | h = mesh.next_halfedge(h); 114 | mesh.insert_edge(h, mesh.next_halfedge(mesh.next_halfedge(h))); 115 | h = mesh.next_halfedge(h); 116 | mesh.insert_edge(h, mesh.next_halfedge(mesh.next_halfedge(h))); 117 | } 118 | 119 | // clean-up properties 120 | mesh.remove_vertex_property(vpoint); 121 | mesh.remove_edge_property(epoint); 122 | } 123 | 124 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/Subdivision/Loop.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class SurfaceMeshSubdivideLoop : public OpenGP::SurfaceMeshAlgorithm{ 6 | public: 7 | static HEADERONLY_INLINE void exec(SurfaceMesh& mesh); 8 | }; 9 | 10 | #ifdef HEADERONLY 11 | #include "Loop.cpp" 12 | #endif 13 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/bounding_box.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | //============================================================================= 5 | namespace Eigen{ 6 | //============================================================================= 7 | 8 | template 9 | inline std::ostream& operator<<(std::ostream& os, const Eigen::AlignedBox& box){ 10 | os << "[min[" << box.min().transpose() << "] max[" << box.max().transpose() << "]]"; 11 | return os; 12 | } 13 | 14 | //============================================================================= 15 | } // namespace eigen 16 | //============================================================================= 17 | 18 | //============================================================================= 19 | namespace OpenGP{ 20 | //============================================================================= 21 | 22 | inline Box3 bounding_box(const SurfaceMesh& mesh) 23 | { 24 | auto vpoints = mesh.get_vertex_property("v:point"); 25 | Box3 bbox; 26 | bbox.setNull(); 27 | for(auto v: mesh.vertices()) 28 | bbox.extend( vpoints[v] ); 29 | return bbox; 30 | } 31 | 32 | /// turn bounding box into a bounding cube (same edge lengths) 33 | inline Box3 bbox_cubified(const Box3& box){ 34 | // TODO: move this function to OpenGP/Types 35 | Vec3 centre = box.center(); 36 | Vec3 d2 = .5 * box.diagonal(); 37 | // find biggest sizes 38 | Scalar s = 0; 39 | s = std::max(s, d2.x()); 40 | s = std::max(s, d2.y()); 41 | s = std::max(s, d2.z()); 42 | Vec3 d2new = Vec3(s,s,s); 43 | Vec3 bMin = centre-d2new; 44 | Vec3 bMax = centre+d2new; 45 | return Box3(bMin, bMax); 46 | } 47 | 48 | /// Slightly enlarges the bounding box by a given factor (about center) 49 | inline Box3 bbox_scaled(const Box3& box, Scalar factor){ 50 | // TODO: move this function to OpenGP/Types 51 | Vec3 centre = box.center(); 52 | Vec3 d2 = .5 * box.diagonal(); 53 | Vec3 bMin = centre - factor * d2; 54 | Vec3 bMax = centre + factor * d2; 55 | return Box3(bMin, bMax); 56 | } 57 | 58 | //============================================================================= 59 | } // namespace OpenGP 60 | //============================================================================= 61 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/internal/Global_properties.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | //============================================================================= 7 | namespace OpenGP { 8 | //============================================================================= 9 | 10 | /// Class to add global properties support to OpenGP objects 11 | /// @see examples/global_properties.cpp 12 | class Global_properties{ 13 | /// Base 14 | class Base_global_property{ 15 | public: 16 | const std::type_info& mytype; 17 | Base_global_property(const std::type_info& mytype=typeid(void)) 18 | : mytype( mytype ){} 19 | }; 20 | 21 | /// Templated 22 | template 23 | class Global_property : public Base_global_property{ 24 | public: 25 | T value; ///< Properties are stored by copy 26 | Global_property() : Base_global_property( typeid(T) ){} 27 | }; 28 | 29 | private: 30 | typedef std::map PropertiesMap; 31 | PropertiesMap global_props_; 32 | 33 | public: 34 | ~Global_properties(){ 35 | // std::cout << global_props_.size() << std::endl; 36 | for(PropertiesMap::iterator it=global_props_.begin(); it!=global_props_.end(); it++) 37 | delete (it->second); 38 | } 39 | 40 | public: 41 | /// Generic 42 | template 43 | T& add_property(const std::string& name){ 44 | if(global_props_.find(name) != global_props_.end()) 45 | throw std::runtime_error("Attempted to create property with same name"); 46 | Global_property* prop = new Global_property(); 47 | global_props_[name] = prop; 48 | return prop->value; /// 53 | T& add_property(const std::string& name, const T& initval){ 54 | T& val = add_property(name); 55 | val = initval; 56 | return val; 57 | } 58 | 59 | // get a property by its name. returns invalid property if it does not exist. 60 | template 61 | T& get_property(const std::string& name){ 62 | if(global_props_.find(name) == global_props_.end()) 63 | throw std::runtime_error("Cannot find property"); 64 | if(global_props_[name]->mytype != typeid(T)) 65 | throw std::runtime_error("Property of desired type not found"); 66 | /// Now static cast is safe 67 | Global_property* prop = static_cast< Global_property* >(global_props_[name]); 68 | return prop->value; 69 | } 70 | }; 71 | 72 | //============================================================================= 73 | } // namespace OpenGP 74 | //============================================================================= 75 | -------------------------------------------------------------------------------- /OpenGP/SurfaceMesh/remesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef WITH_CGAL 8 | #include 9 | #include 10 | #endif 11 | 12 | //============================================================================= 13 | namespace OpenGP{ 14 | //============================================================================= 15 | 16 | 17 | 18 | class IsotropicRemesher{ 19 | /// @{ @todo centralize these definitions elsewhere 20 | const std::string VPOINT = "v:point"; ///< vertex coordinates 21 | const std::string VNORMAL = "v:normal"; ///< vertex normals 22 | const std::string VCOLOR = "v:color"; ///< vertex color 23 | const std::string VAREA = "v:area"; ///< vertex areas 24 | const std::string VQUALITY = "v:quality"; ///< vertex quality 25 | const std::string FNORMAL = "f:normal"; ///< face normals 26 | const std::string FAREA = "f:area"; ///< face area 27 | const std::string ELENGTH = "e:length"; ///< edge length 28 | const std::string FSELECTED = "f:selected"; ///< is face selected? 29 | /// @} 30 | 31 | private: 32 | SurfaceMesh::Vertex_property points; 33 | SurfaceMesh::Edge_property efeature; 34 | SurfaceMesh* mesh = NULL; 35 | SurfaceMesh copy; 36 | public: 37 | IsotropicRemesher(SurfaceMesh& _mesh){ 38 | this->mesh = &_mesh; 39 | efeature = mesh->edge_property("e:feature", false); 40 | points = mesh->vertex_property(VPOINT); 41 | 42 | if(reproject_to_surface) 43 | copy = *mesh; ///< deep copy 44 | 45 | #ifdef WITH_CGAL 46 | VerticesMatrixMap vertices = vertices_matrix(*mesh); 47 | TrianglesMatrix faces = faces_matrix(*mesh); 48 | std::cout << vertices.cols() << " " << vertices.rows() << std::endl; 49 | std::cout << faces.cols() << " " << faces.rows() << std::endl; 50 | searcher.build(vertices, faces); 51 | #endif 52 | } 53 | ~IsotropicRemesher(){ 54 | mesh->remove_edge_property(efeature); 55 | } 56 | 57 | /// @{ core methods 58 | public: 59 | void execute(); 60 | protected: 61 | void phase_analyze(); 62 | void phase_remesh(); 63 | /// @} 64 | 65 | /// @{ algorithm parameters 66 | private: 67 | NullStream nullstream; 68 | public: 69 | /// Where should I send the algorithm output to? 70 | std::ostream* myout = &(nullstream); 71 | /// What's the dihedral angle (degrees) of a feature? 72 | Scalar sharp_feature_deg = 44.0; ///< degrees 73 | /// How many iterations of merge/split/flip/relax should I run? 74 | Scalar num_iterations = 10; 75 | /// What's the largest admissible edge? 76 | Scalar longest_edge_length = nan(); 77 | /// Should I mark short edges as features? 78 | bool keep_short_edges = false; 79 | /// After tangentially relaxing vertices, should I reproject vertices on the tangent space 80 | /// defined by vertex + vertex normal? 81 | bool reproject_on_tanget = true; 82 | /// After tangentially relaxing vertices, should I project on the original surface (slow, query an AABB search tree) 83 | bool reproject_to_surface = false; 84 | /// @} 85 | 86 | #ifdef WITH_CGAL 87 | private: 88 | AABBSearcher searcher; 89 | #endif 90 | 91 | /// @{ utilities 92 | private: 93 | void splitLongEdges(Scalar maxEdgeLength); 94 | void collapseShortEdges(const Scalar _minEdgeLength, const Scalar _maxEdgeLength, bool keep_short_edges); 95 | void equalizeValences(); 96 | void tangentialRelaxation(); 97 | void projectToSurface(); 98 | int targetValence(const SurfaceMesh::Vertex &_vh); 99 | bool isBoundary(const SurfaceMesh::Vertex &_vh); 100 | bool isFeature(const SurfaceMesh::Vertex &_vh); 101 | Vec3 findNearestPoint(SurfaceMesh& orginal_mesh, const Vec3& _point, SurfaceMesh::Face& _fh, Scalar & _dbest); 102 | /// @} utilities 103 | }; 104 | 105 | //============================================================================= 106 | } // OpenGP:: 107 | //============================================================================= 108 | 109 | // Header only support 110 | #ifdef HEADERONLY 111 | #include "remesh.cpp" 112 | #endif 113 | -------------------------------------------------------------------------------- /OpenGP/headeronly.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /// HEADERONLY_INLINE is either nothing or the keyword "inline" 4 | #ifndef OPENGP_HEADERONLY 5 | #undef HEADERONLY 6 | #undef HEADERONLY_INLINE 7 | #define HEADERONLY_INLINE 8 | #else 9 | #undef HEADERONLY 10 | #define HEADERONLY 11 | #undef HEADERONLY_INLINE 12 | #define HEADERONLY_INLINE inline 13 | #endif 14 | -------------------------------------------------------------------------------- /OpenGP/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | //============================================================================= 6 | namespace OpenGP { 7 | //============================================================================= 8 | 9 | /// Customizable (yet global) scalar type 10 | /// WARNING: OpenGL behavior undefined if type is changed! 11 | #ifdef OPENGP_SCALAR_TYPE 12 | typedef OPENGP_SCALAR_TYPE Scalar 13 | #else 14 | typedef float Scalar; 15 | #endif 16 | 17 | typedef unsigned char uchar; 18 | typedef unsigned int uint; 19 | 20 | ///--- Vectors 21 | typedef Eigen::Matrix VecN; ///< nD vector type 22 | typedef Eigen::Matrix Vec2; ///< 2D vector type 23 | typedef Eigen::Matrix Vec3; ///< 3D vector type 24 | typedef Eigen::Matrix Vec4; ///< 4D vector type 25 | typedef Vec3 Point; ///< Point type 26 | typedef Vec3 Normal; ///< Normal type 27 | typedef Vec3 Color; ///< Color type 28 | typedef Vec3 TextureCoordinate; ///< Texture coordinate type 29 | 30 | ///--- Matrices 31 | typedef Eigen::Matrix Mat4x4; ///< 4x4 matrix 32 | typedef Eigen::Matrix Mat3x3; ///< 3x3 matrix 33 | typedef Eigen::Matrix Mat3xN; ///< 3xN matrix 34 | typedef Eigen::Matrix MatMxN; ///< MxN matrix 35 | 36 | ///--- Geometric types (Eigen/Geometry) 37 | typedef Eigen::AlignedBox Box3; 38 | 39 | ///--- Bindings for special values 40 | inline Scalar nan(){ return std::numeric_limits::quiet_NaN(); } 41 | inline bool isnan(Scalar v){ return std::isnan(v); } 42 | inline Scalar inf(){ return std::numeric_limits::max(); } 43 | 44 | //============================================================================= 45 | } // namespace OpenGP 46 | //============================================================================= 47 | -------------------------------------------------------------------------------- /OpenGP/util/tictoc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #define tic(x) auto x = std::chrono::steady_clock::now() 7 | #define toc(x) std::chrono::duration (std::chrono::steady_clock::now() - x).count() 8 | 9 | /// Helper class for TICTOC_SCOPE and TICTOC_BLOCK 10 | class TicTocTimerObject{ 11 | private: 12 | std::chrono::time_point _start; 13 | std::string _str; 14 | public: 15 | int i=0; 16 | TicTocTimerObject(const std::string& str) : _str(str){ 17 | _start = std::chrono::system_clock::now(); 18 | } 19 | ~TicTocTimerObject(){ 20 | double t_ms = std::chrono::duration (std::chrono::system_clock::now() - _start).count(); 21 | std::cout << "[" << _str << "] " << t_ms << " ms" << std::endl; 22 | } 23 | }; 24 | 25 | /// Usage: 26 | /// 27 | /// { 28 | /// ///.... Code to be timed 29 | /// TICTOC_SCOPE(objname, "scope name"); 30 | /// } //< prints out [scope name] XXms 31 | /// 32 | #define TICTOC_SCOPE(objname, string) TicTocTimerObject objname(string) 33 | 34 | /// Usage (nothing internally it is just a for loop!): 35 | /// 36 | /// TIMED_BLOCK(objname, "output string") 37 | /// { 38 | /// // 39 | /// //... Code to be timed 40 | /// // 41 | /// } //< prints out [scope name] XXms 42 | /// 43 | #define TICTOC_BLOCK(obj, blockName) for (TicTocTimerObject obj(blockName); obj.i < 1; ++obj.i) 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![alt tag](https://github.com/shengyangwei/geodesic-computation/blob/master/images/graph.png) 2 | 3 | ![alt tag](https://github.com/shengyangwei/geodesic-computation/blob/master/images/numeric.png) 4 | 5 | ## Description 6 | This program implements Fast Marching algorithm and Heat algorithm to compute geodesic distance on triangle meshes. Geodesic variation on a surface is represented by isolines and various colors. A numeric value of the distance is output to standard output. 7 | 8 | 9 | ## External Dependencies 10 | - Operating System: Linux, Mac OS, Windows 11 | - Libraries: [GLEW](http://glew.sourceforge.net), [Eigen](http://eigen.tuxfamily.org), [GLFW3](http://www.glfw.org), [OpenGP]( https://github.com/OpenGP/OpenGP) 12 | - Input file format: OBJ. The input mesh should be a triangle mesh. 13 | 14 | ## Build 15 | cd ${PROJECT_SOURCE_DIR} 16 | 17 | mkdir build && cd build 18 | 19 | cmake .. 20 | 21 | make 22 | 23 | $./demo ${model.obj} 24 | 25 | ## UI Controls 26 | - Mouse 27 | * Left drag: Rotate the model 28 | * Right click: Select start and end points to compute geodesic distance 29 | * Middle scroll: Scale the model 30 | 31 | - Key 32 | * 1: Apply Fast Marching Method 33 | * 2: Apply Head Method with mean boundary condition 34 | * 3: Apply Heat Method with Neumann boundary condition 35 | * 4: Apply Heat Method with Dirichlet boundary condition 36 | * F: Set the time factor of Heat method (standard input) 37 | * I: Display geodesic ioslines 38 | * P: Show a path between start point and end point, which is path with the minimum geodesic distance between the two points on surface. 39 | * R: Clear color, ioslines, and selected points. Reset to the initial state for next computation. 40 | 41 | - Standard Output 42 | * Display the geodesic distance between two selected points. 43 | 44 | 45 | ## Acknowledgement 46 | 47 | This program is build on [OpenGP]( https://github.com/OpenGP/OpenGP), acknowledge to Dr. Andrea Tagliasacchi 48 | 49 | 50 | -------------------------------------------------------------------------------- /cmake/ConfigureCompiler.cmake: -------------------------------------------------------------------------------- 1 | if(NOT CMAKE_BUILD_TYPE) 2 | set(CMAKE_BUILD_TYPE "Debug") 3 | message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") 4 | endif() 5 | 6 | if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") 8 | endif() 9 | 10 | if(UNIX AND NOT APPLE AND CMAKE_COMPILER_IS_GNUCXX) 11 | message(STATUS "Using gcc/g++ compiler (Linux)") 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -pedantic -Wextra -Wno-long-long") 13 | endif() 14 | 15 | if(APPLE AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") 16 | message(STATUS "Using Clang compiler on (Apple)") 17 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pedantic -Wextra -Wno-long-long") 18 | endif() 19 | 20 | if(WIN32) 21 | # Tell IDE (esp. MSVC) to use folders. 22 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 23 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 24 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_USE_MATH_DEFINES") 25 | add_definitions(-D_USE_MATH_DEFINES) 26 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 27 | endif() 28 | -------------------------------------------------------------------------------- /cmake/ConfigureEigen3.cmake: -------------------------------------------------------------------------------- 1 | find_package(Eigen3 REQUIRED) 2 | include_directories(${EIGEN3_INCLUDE_DIR}) 3 | -------------------------------------------------------------------------------- /cmake/ConfigureGLEW.cmake: -------------------------------------------------------------------------------- 1 | find_package(GLEW REQUIRED) 2 | include_directories(${GLEW_INCLUDE_DIRS}) 3 | link_directories(${GLEW_LIBRARY_DIRS}) 4 | list(APPEND LIBRARIES ${GLEW_LIBRARIES}) 5 | -------------------------------------------------------------------------------- /cmake/ConfigureGLFW3.cmake: -------------------------------------------------------------------------------- 1 | find_package(GLFW3 REQUIRED) 2 | include_directories(${GLFW3_INCLUDE_DIRS}) 3 | add_definitions(${GLFW3_DEFINITIONS}) 4 | list(APPEND LIBRARIES ${GLFW3_LIBRARIES}) 5 | 6 | if(NOT GLFW3_FOUND) 7 | message(WARNING "GLFW3 not found") 8 | endif() 9 | -------------------------------------------------------------------------------- /cmake/ConfigureOpenGL.cmake: -------------------------------------------------------------------------------- 1 | find_package(OpenGL REQUIRED) 2 | include_directories(${OpenGL_INCLUDE_DIRS}) 3 | link_directories(${OpenGL_LIBRARY_DIRS}) 4 | add_definitions(${OpenGL_DEFINITIONS}) 5 | list(APPEND LIBRARIES ${OPENGL_LIBRARIES}) 6 | 7 | if(NOT OPENGL_FOUND) 8 | message(WARNING "OpenGL not found") 9 | endif(NOT OPENGL_FOUND) 10 | -------------------------------------------------------------------------------- /cmake/ConfigureOpenGP.cmake: -------------------------------------------------------------------------------- 1 | find_package(OpenGP REQUIRED) 2 | include_directories(${OpenGP_INCLUDE_DIR}) 3 | #message(STATUS ${OpenGP_INCLUDE_DIR}) 4 | 5 | #--- Use header only implementation 6 | add_definitions(-DOPENGP_HEADERONLY) 7 | 8 | # avoid errors caused by duplicates 9 | if(NOT TARGET OPENGP) 10 | #--- Dummy target just to have sources/headers appear in IDE 11 | file(GLOB_RECURSE SOURCES "${OpenGP_INCLUDE_DIR}/*.cpp") 12 | file(GLOB_RECURSE HEADERS "${OpenGP_INCLUDE_DIR}/*.h") 13 | add_custom_target(OPENGP SOURCES ${HEADERS} ${SOURCES}) 14 | endif() 15 | -------------------------------------------------------------------------------- /cmake/FindEigen3.cmake: -------------------------------------------------------------------------------- 1 | if(EIGEN3_INCLUDE_DIRS) 2 | set(EIGEN3_FOUND TRUE) 3 | else(EIGEN3_INCLUDE_DIRS) 4 | find_path(EIGEN3_INCLUDE_DIR 5 | NAMES 6 | Eigen/Dense 7 | PATHS 8 | /usr/local/include/eigen3 9 | /usr/include/eigen3 10 | ) 11 | 12 | set (EIGEN3_INCLUDE_DIRS ${EIGEN3_INCLUDE_DIR}) 13 | 14 | if(EIGEN3_INCLUDE_DIRS) 15 | set(EIGEN3_FOUND TRUE) 16 | endif(EIGEN3_INCLUDE_DIRS) 17 | 18 | if(EIGEN3_FOUND) 19 | message(STATUS "Find Eigen3: ${EIGEN3_INCLUDE_DIRS}") 20 | endif(EIGEN3_FOUND) 21 | endif(EIGEN3_INCLUDE_DIRS) 22 | -------------------------------------------------------------------------------- /cmake/FindGLFW3.cmake: -------------------------------------------------------------------------------- 1 | find_path(GLFW3_INCLUDE_DIRS 2 | NAMES 3 | GLFW/glfw3.h 4 | PATHS 5 | /usr/local/include 6 | /usr/local/X11R6/include 7 | /usr/X11/include 8 | /usr/X11R6/include 9 | /usr/include/X11 10 | /usr/include 11 | /opt/X11/include 12 | /opt/include 13 | ) 14 | find_library(GLFW3_LIBRARIES 15 | NAMES 16 | glfw3 17 | glfw 18 | PATHS 19 | $ENV{GLFWDIR}/lib 20 | $ENV{GLFWDIR}/support/msvc80/Debug 21 | $ENV{GLFWDIR}/support/msvc80/Release 22 | /usr/lib/x86_64-linux-gnu/ 23 | /usr/local/lib 24 | /usr/local/X11R6/lib 25 | /usr/X11R6/lib 26 | /usr/X11/lib 27 | /usr/lib/X11 28 | /usr/lib 29 | /opt/X11/lib 30 | /opt/lib ) 31 | 32 | SET(GLFW3_FOUND FALSE) 33 | IF(GLFW3_LIBRARIES AND GLFW3_INCLUDE_DIRS) 34 | SET(GLFW3_FOUND TRUE) 35 | ENDIF(GLFW3_LIBRARIES AND GLFW3_INCLUDE_DIRS) 36 | -------------------------------------------------------------------------------- /cmake/FindOpenGP.cmake: -------------------------------------------------------------------------------- 1 | find_path(OpenGP_INCLUDE_DIR 2 | NAMES 3 | OpenGP/SurfaceMesh/SurfaceMesh.h 4 | PATHS 5 | ./ 6 | ./src 7 | ../src 8 | ../../src 9 | ../../../src 10 | ./OpenGP/src/ 11 | ./../OpenGP/src/ 12 | ./../../OpenGP/src 13 | ./external/opengp 14 | /usr/local/include 15 | /usr/include 16 | ) 17 | 18 | if(OpenGP_INCLUDE_DIR) 19 | set(OpenGP_FOUND TRUE) 20 | else() 21 | set(OpenGP_FOUND FALSE) 22 | endif() 23 | 24 | if(OpenGP_FOUND) 25 | if(NOT CMAKE_FIND_QUIETLY) 26 | message(STATUS "Found OpenGP: ${OpenGP_INCLUDE_DIR}") 27 | endif() 28 | else() 29 | if(OpenGP_FIND_REQUIRED) 30 | message(FATAL_ERROR "Could not find OpenGP") 31 | endif() 32 | endif() 33 | -------------------------------------------------------------------------------- /data/dodeca.obj: -------------------------------------------------------------------------------- 1 | vn -0.5773502867 -0.5773502508 0.5773502700 2 | v -0.3090169883 -0.3090169883 0.3090169883 3 | vn 0.9341723446 0.3568221273 -0.0000000000 4 | v 0.5000000000 0.1909830436 0.0000000000 5 | vn 0.9341723463 -0.3568221230 -0.0000000000 6 | v 0.5000000000 -0.1909830436 0.0000000000 7 | vn -0.9341723446 0.3568221273 0.0000000000 8 | v -0.5000000000 0.1909830436 0.0000000000 9 | vn -0.9341723463 -0.3568221230 0.0000000000 10 | v -0.5000000000 -0.1909830436 0.0000000000 11 | vn 0.0000000000 0.9341723387 0.3568221427 12 | v 0.0000000000 0.5000000000 0.1909830436 13 | vn -0.0000000000 0.9341723387 -0.3568221427 14 | v 0.0000000000 0.5000000000 -0.1909830436 15 | vn 0.3568221440 -0.0000000103 -0.9341723382 16 | v 0.1909830436 0.0000000000 -0.5000000000 17 | vn -0.3568221419 -0.0000000271 -0.9341723391 18 | v -0.1909830436 0.0000000000 -0.5000000000 19 | vn -0.0000000039 -0.9341723353 -0.3568221517 20 | v 0.0000000000 -0.5000000000 -0.1909830436 21 | vn -0.0000000000 -0.9341723387 0.3568221427 22 | v 0.0000000000 -0.5000000000 0.1909830436 23 | vn 0.3568221517 -0.0000000039 0.9341723353 24 | v 0.1909830436 0.0000000000 0.5000000000 25 | vn -0.3568221517 -0.0000000039 0.9341723353 26 | v -0.1909830436 0.0000000000 0.5000000000 27 | vn 0.5773502692 0.5773502692 -0.5773502692 28 | v 0.3090169883 0.3090169883 -0.3090169883 29 | vn 0.5773502692 0.5773502692 0.5773502692 30 | v 0.3090169883 0.3090169883 0.3090169883 31 | vn -0.5773502692 0.5773502692 -0.5773502692 32 | v -0.3090169883 0.3090169883 -0.3090169883 33 | vn -0.5773502692 0.5773502692 0.5773502692 34 | v -0.3090169883 0.3090169883 0.3090169883 35 | vn 0.5773502867 -0.5773502508 -0.5773502700 36 | v 0.3090169883 -0.3090169883 -0.3090169883 37 | vn 0.5773502867 -0.5773502508 0.5773502700 38 | v 0.3090169883 -0.3090169883 0.3090169883 39 | vn -0.5773502684 -0.5773502516 -0.5773502875 40 | v -0.3090169883 -0.3090169883 -0.3090169883 41 | f 19//19 3//3 2//2 42 | f 12//12 19//19 2//2 43 | f 15//15 12//12 2//2 44 | f 8//8 14//14 2//2 45 | f 18//18 8//8 2//2 46 | f 3//3 18//18 2//2 47 | f 20//20 5//5 4//4 48 | f 9//9 20//20 4//4 49 | f 16//16 9//9 4//4 50 | f 13//13 17//17 4//4 51 | f 1//1 13//13 4//4 52 | f 5//5 1//1 4//4 53 | f 7//7 16//16 4//4 54 | f 6//6 7//7 4//4 55 | f 17//17 6//6 4//4 56 | f 6//6 15//15 2//2 57 | f 7//7 6//6 2//2 58 | f 14//14 7//7 2//2 59 | f 10//10 18//18 3//3 60 | f 11//11 10//10 3//3 61 | f 19//19 11//11 3//3 62 | f 11//11 1//1 5//5 63 | f 10//10 11//11 5//5 64 | f 20//20 10//10 5//5 65 | f 20//20 9//9 8//8 66 | f 10//10 20//20 8//8 67 | f 18//18 10//10 8//8 68 | f 9//9 16//16 7//7 69 | f 8//8 9//9 7//7 70 | f 14//14 8//8 7//7 71 | f 12//12 15//15 6//6 72 | f 13//13 12//12 6//6 73 | f 17//17 13//13 6//6 74 | f 13//13 1//1 11//11 75 | f 12//12 13//13 11//11 76 | f 19//19 12//12 11//11 77 | -------------------------------------------------------------------------------- /data/quad_3x3.obj: -------------------------------------------------------------------------------- 1 | v -1.000000 0.000000 1.000000 2 | v -1.000000 0.000000 -1.000000 3 | v 1.000000 0.000000 -1.000000 4 | v 1.000000 0.000000 1.000000 5 | v 0.000000 0.000000 1.000000 6 | v 0.000000 0.000000 -1.000000 7 | v -1.000000 0.000000 0.000000 8 | v 1.000000 0.000000 0.000000 9 | v 0.000000 0.000000 0.000000 10 | f 5 9 8 4 11 | f 7 2 6 9 12 | f 9 6 3 8 13 | f 1 7 9 5 14 | -------------------------------------------------------------------------------- /data/quad_9x9.obj: -------------------------------------------------------------------------------- 1 | v -1.000000 0.000000 1.000000 2 | v -1.000000 0.000000 -1.000000 3 | v 1.000000 0.000000 -1.000000 4 | v 1.000000 0.000000 1.000000 5 | v 0.000000 0.000000 1.000000 6 | v 0.000000 0.000000 -1.000000 7 | v -1.000000 0.000000 0.000000 8 | v 1.000000 0.000000 0.000000 9 | v 0.000000 0.000000 0.000000 10 | v 0.000000 0.000000 -0.500000 11 | v 1.000000 0.000000 0.500000 12 | v -1.000000 0.000000 0.500000 13 | v 0.000000 0.000000 0.500000 14 | v 0.500000 0.000000 -1.000000 15 | v -1.000000 0.000000 -0.500000 16 | v -0.500000 0.000000 -1.000000 17 | v -0.500000 0.000000 0.000000 18 | v 0.500000 0.000000 0.000000 19 | v 1.000000 0.000000 -0.500000 20 | v -0.500000 0.000000 1.000000 21 | v 0.500000 0.000000 1.000000 22 | v 0.500000 0.000000 0.500000 23 | v -0.500000 0.000000 -0.500000 24 | v 0.500000 0.000000 -0.500000 25 | v -0.500000 0.000000 0.500000 26 | v 0.000000 0.000000 -0.250000 27 | v 1.000000 0.000000 0.750000 28 | v -1.000000 0.000000 0.750000 29 | v 0.000000 0.000000 0.750000 30 | v 0.750000 0.000000 -1.000000 31 | v -1.000000 0.000000 -0.750000 32 | v -0.750000 0.000000 -1.000000 33 | v -0.750000 0.000000 0.000000 34 | v 0.750000 0.000000 0.000000 35 | v 1.000000 0.000000 -0.750000 36 | v -0.750000 0.000000 1.000000 37 | v 0.750000 0.000000 1.000000 38 | v 0.000000 0.000000 -0.750000 39 | v 1.000000 0.000000 0.250000 40 | v -1.000000 0.000000 0.250000 41 | v 0.000000 0.000000 0.250000 42 | v 0.250000 0.000000 -1.000000 43 | v -1.000000 0.000000 -0.250000 44 | v -0.250000 0.000000 -1.000000 45 | v -0.250000 0.000000 0.000000 46 | v 0.250000 0.000000 0.000000 47 | v 1.000000 0.000000 -0.250000 48 | v -0.250000 0.000000 1.000000 49 | v 0.250000 0.000000 1.000000 50 | v 0.750000 0.000000 0.500000 51 | v 0.250000 0.000000 0.500000 52 | v 0.500000 0.000000 0.750000 53 | v 0.500000 0.000000 0.250000 54 | v -0.250000 0.000000 -0.500000 55 | v -0.750000 0.000000 -0.500000 56 | v -0.500000 0.000000 -0.250000 57 | v -0.500000 0.000000 -0.750000 58 | v 0.750000 0.000000 -0.500000 59 | v 0.250000 0.000000 -0.500000 60 | v 0.500000 0.000000 -0.250000 61 | v 0.500000 0.000000 -0.750000 62 | v -0.250000 0.000000 0.500000 63 | v -0.750000 0.000000 0.500000 64 | v -0.500000 0.000000 0.750000 65 | v -0.500000 0.000000 0.250000 66 | v 0.750000 0.000000 0.250000 67 | v -0.250000 0.000000 -0.750000 68 | v 0.750000 0.000000 -0.750000 69 | v -0.250000 0.000000 0.250000 70 | v 0.750000 0.000000 0.750000 71 | v 0.250000 0.000000 0.750000 72 | v 0.250000 0.000000 0.250000 73 | v -0.250000 0.000000 -0.250000 74 | v -0.750000 0.000000 -0.250000 75 | v -0.750000 0.000000 -0.750000 76 | v 0.750000 0.000000 -0.250000 77 | v 0.250000 0.000000 -0.250000 78 | v 0.250000 0.000000 -0.750000 79 | v -0.250000 0.000000 0.750000 80 | v -0.750000 0.000000 0.750000 81 | v -0.750000 0.000000 0.250000 82 | f 66 34 8 39 83 | f 67 44 6 38 84 | f 68 30 3 35 85 | f 69 45 9 41 86 | f 70 50 11 27 87 | f 71 51 22 52 88 | f 72 46 18 53 89 | f 73 54 10 26 90 | f 74 55 23 56 91 | f 75 32 16 57 92 | f 76 58 19 47 93 | f 77 59 24 60 94 | f 78 42 14 61 95 | f 79 62 13 29 96 | f 80 63 25 64 97 | f 81 33 17 65 98 | f 50 66 39 11 99 | f 22 53 66 50 100 | f 53 18 34 66 101 | f 54 67 38 10 102 | f 23 57 67 54 103 | f 57 16 44 67 104 | f 58 68 35 19 105 | f 24 61 68 58 106 | f 61 14 30 68 107 | f 62 69 41 13 108 | f 25 65 69 62 109 | f 65 17 45 69 110 | f 37 70 27 4 111 | f 21 52 70 37 112 | f 52 22 50 70 113 | f 49 71 52 21 114 | f 5 29 71 49 115 | f 29 13 51 71 116 | f 51 72 53 22 117 | f 13 41 72 51 118 | f 41 9 46 72 119 | f 45 73 26 9 120 | f 17 56 73 45 121 | f 56 23 54 73 122 | f 33 74 56 17 123 | f 7 43 74 33 124 | f 43 15 55 74 125 | f 55 75 57 23 126 | f 15 31 75 55 127 | f 31 2 32 75 128 | f 34 76 47 8 129 | f 18 60 76 34 130 | f 60 24 58 76 131 | f 46 77 60 18 132 | f 9 26 77 46 133 | f 26 10 59 77 134 | f 59 78 61 24 135 | f 10 38 78 59 136 | f 38 6 42 78 137 | f 48 79 29 5 138 | f 20 64 79 48 139 | f 64 25 62 79 140 | f 36 80 64 20 141 | f 1 28 80 36 142 | f 28 12 63 80 143 | f 63 81 65 25 144 | f 12 40 81 63 145 | f 40 7 33 81 146 | -------------------------------------------------------------------------------- /data/sphere.obj: -------------------------------------------------------------------------------- 1 | #### 2 | # 3 | # OBJ File Generated by Meshlab 4 | # 5 | #### 6 | # Object sphere.obj 7 | # 8 | # Vertices: 42 9 | # Faces: 80 10 | # 11 | #### 12 | vn -0.000002 -1.000000 0.000000 13 | v 0.000000 -1.000000 0.000000 14 | vn 0.723608 -0.447215 0.525728 15 | v 0.723607 -0.447220 0.525725 16 | vn -0.276392 -0.447217 0.850649 17 | v -0.276388 -0.447220 0.850649 18 | vn -0.894426 -0.447215 -0.000000 19 | v -0.894426 -0.447216 0.000000 20 | vn -0.276392 -0.447217 -0.850649 21 | v -0.276388 -0.447220 -0.850649 22 | vn 0.723608 -0.447215 -0.525728 23 | v 0.723607 -0.447220 -0.525725 24 | vn 0.276392 0.447217 0.850649 25 | v 0.276388 0.447220 0.850649 26 | vn -0.723608 0.447215 0.525728 27 | v -0.723607 0.447220 0.525725 28 | vn -0.723608 0.447215 -0.525728 29 | v -0.723607 0.447220 -0.525725 30 | vn 0.276392 0.447217 -0.850649 31 | v 0.276388 0.447220 -0.850649 32 | vn 0.894426 0.447215 0.000000 33 | v 0.894426 0.447216 0.000000 34 | vn 0.000002 1.000000 0.000000 35 | v 0.000000 1.000000 0.000000 36 | vn 0.425324 -0.850652 0.309015 37 | v 0.425323 -0.850654 0.309011 38 | vn 0.262868 -0.525733 0.809015 39 | v 0.262869 -0.525738 0.809012 40 | vn -0.162459 -0.850653 0.499997 41 | v -0.162456 -0.850654 0.499995 42 | vn 0.425324 -0.850652 -0.309015 43 | v 0.425323 -0.850654 -0.309011 44 | vn 0.850650 -0.525732 0.000000 45 | v 0.850648 -0.525736 0.000000 46 | vn -0.688190 -0.525732 0.500001 47 | v -0.688189 -0.525736 0.499997 48 | vn -0.525729 -0.850652 0.000000 49 | v -0.525730 -0.850652 0.000000 50 | vn -0.688190 -0.525732 -0.500001 51 | v -0.688189 -0.525736 -0.499997 52 | vn -0.162459 -0.850653 -0.499997 53 | v -0.162456 -0.850654 -0.499995 54 | vn 0.262868 -0.525733 -0.809015 55 | v 0.262869 -0.525738 -0.809012 56 | vn 0.951056 -0.000000 -0.309018 57 | v 0.951058 0.000000 -0.309013 58 | vn 0.951056 -0.000000 0.309018 59 | v 0.951058 0.000000 0.309013 60 | vn 0.587787 0.000001 0.809016 61 | v 0.587786 0.000000 0.809017 62 | vn -0.000000 -0.000000 1.000000 63 | v 0.000000 0.000000 1.000000 64 | vn -0.587787 -0.000001 0.809016 65 | v -0.587786 0.000000 0.809017 66 | vn -0.951056 0.000000 0.309018 67 | v -0.951058 0.000000 0.309013 68 | vn -0.951056 0.000000 -0.309018 69 | v -0.951058 0.000000 -0.309013 70 | vn -0.587787 -0.000001 -0.809016 71 | v -0.587786 0.000000 -0.809017 72 | vn -0.000000 -0.000000 -1.000000 73 | v 0.000000 0.000000 -1.000000 74 | vn 0.587787 0.000001 -0.809016 75 | v 0.587786 0.000000 -0.809017 76 | vn 0.688190 0.525732 0.500001 77 | v 0.688189 0.525736 0.499997 78 | vn -0.262868 0.525733 0.809015 79 | v -0.262869 0.525738 0.809012 80 | vn -0.850650 0.525732 0.000000 81 | v -0.850648 0.525736 0.000000 82 | vn -0.262868 0.525733 -0.809015 83 | v -0.262869 0.525738 -0.809012 84 | vn 0.688190 0.525732 -0.500001 85 | v 0.688189 0.525736 -0.499997 86 | vn 0.525729 0.850652 0.000000 87 | v 0.525730 0.850652 0.000000 88 | vn 0.162459 0.850653 0.499997 89 | v 0.162456 0.850654 0.499995 90 | vn -0.425324 0.850652 0.309015 91 | v -0.425323 0.850654 0.309011 92 | vn -0.425324 0.850652 -0.309015 93 | v -0.425323 0.850654 -0.309011 94 | vn 0.162459 0.850653 -0.499997 95 | v 0.162456 0.850654 -0.499995 96 | # 42 vertices, 0 vertices normals 97 | 98 | f 1//1 13//13 15//15 99 | f 2//2 13//13 17//17 100 | f 1//1 15//15 19//19 101 | f 1//1 19//19 21//21 102 | f 1//1 21//21 16//16 103 | f 2//2 17//17 24//24 104 | f 3//3 14//14 26//26 105 | f 4//4 18//18 28//28 106 | f 5//5 20//20 30//30 107 | f 6//6 22//22 32//32 108 | f 2//2 24//24 25//25 109 | f 3//3 26//26 27//27 110 | f 4//4 28//28 29//29 111 | f 5//5 30//30 31//31 112 | f 6//6 32//32 23//23 113 | f 7//7 33//33 39//39 114 | f 8//8 34//34 40//40 115 | f 9//9 35//35 41//41 116 | f 10//10 36//36 42//42 117 | f 11//11 37//37 38//38 118 | f 15//15 14//14 3//3 119 | f 15//15 13//13 14//14 120 | f 13//13 2//2 14//14 121 | f 17//17 16//16 6//6 122 | f 17//17 13//13 16//16 123 | f 13//13 1//1 16//16 124 | f 19//19 18//18 4//4 125 | f 19//19 15//15 18//18 126 | f 15//15 3//3 18//18 127 | f 21//21 20//20 5//5 128 | f 21//21 19//19 20//20 129 | f 19//19 4//4 20//20 130 | f 16//16 22//22 6//6 131 | f 16//16 21//21 22//22 132 | f 21//21 5//5 22//22 133 | f 24//24 23//23 11//11 134 | f 24//24 17//17 23//23 135 | f 17//17 6//6 23//23 136 | f 26//26 25//25 7//7 137 | f 26//26 14//14 25//25 138 | f 14//14 2//2 25//25 139 | f 28//28 27//27 8//8 140 | f 28//28 18//18 27//27 141 | f 18//18 3//3 27//27 142 | f 30//30 29//29 9//9 143 | f 30//30 20//20 29//29 144 | f 20//20 4//4 29//29 145 | f 32//32 31//31 10//10 146 | f 32//32 22//22 31//31 147 | f 22//22 5//5 31//31 148 | f 25//25 33//33 7//7 149 | f 25//25 24//24 33//33 150 | f 24//24 11//11 33//33 151 | f 27//27 34//34 8//8 152 | f 27//27 26//26 34//34 153 | f 26//26 7//7 34//34 154 | f 29//29 35//35 9//9 155 | f 29//29 28//28 35//35 156 | f 28//28 8//8 35//35 157 | f 31//31 36//36 10//10 158 | f 31//31 30//30 36//36 159 | f 30//30 9//9 36//36 160 | f 23//23 37//37 11//11 161 | f 23//23 32//32 37//37 162 | f 32//32 10//10 37//37 163 | f 39//39 38//38 12//12 164 | f 39//39 33//33 38//38 165 | f 33//33 11//11 38//38 166 | f 40//40 39//39 12//12 167 | f 40//40 34//34 39//39 168 | f 34//34 7//7 39//39 169 | f 41//41 40//40 12//12 170 | f 41//41 35//35 40//40 171 | f 35//35 8//8 40//40 172 | f 42//42 41//41 12//12 173 | f 42//42 36//36 41//41 174 | f 36//36 9//9 41//41 175 | f 38//38 42//42 12//12 176 | f 38//38 37//37 42//42 177 | f 37//37 10//10 42//42 178 | # 80 faces, 0 coords texture 179 | 180 | # End of File -------------------------------------------------------------------------------- /data/tet.obj: -------------------------------------------------------------------------------- 1 | #### 2 | # 3 | # OBJ File Generated by Meshlab 4 | # 5 | #### 6 | # Object tet.obj 7 | # 8 | # Vertices: 4 9 | # Faces: 4 10 | # 11 | #### 12 | v 0.000000 0.000000 0.000000 13 | v 2.000000 0.000000 0.000000 14 | v 1.000000 1.732050 0.000000 15 | v 1.000000 0.577350 1.633000 16 | # 4 vertices, 0 vertices normals 17 | 18 | f 1 2 4 19 | f 2 3 4 20 | f 1 4 3 21 | f 2 1 3 22 | # 4 faces, 0 coords texture 23 | 24 | # End of File -------------------------------------------------------------------------------- /data/triangle.obj: -------------------------------------------------------------------------------- 1 | v 0.000000 0.000000 0.000000 2 | v 1.000000 0.000000 0.000000 3 | v 0.000000 1.000000 0.000000 4 | vt 0.000000 0.000000 0.000000 5 | vt 1.000000 0.000000 0.000000 6 | vt 0.000000 1.000000 0.000000 7 | f 1/1 2/2 3/3 8 | -------------------------------------------------------------------------------- /images/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sywe1/geodesic-computation/06c42eff1c4dcc206517fb1e73ac1861e208e3c2/images/graph.png -------------------------------------------------------------------------------- /images/numeric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sywe1/geodesic-computation/06c42eff1c4dcc206517fb1e73ac1861e208e3c2/images/numeric.png -------------------------------------------------------------------------------- /src/FMM.cpp: -------------------------------------------------------------------------------- 1 | #include "FMM.h" 2 | #include 3 | void FMM::clean_up() 4 | { 5 | for (auto const& vertex : mesh.vertices()) 6 | { 7 | vfixed[vertex] = false; 8 | vclose[vertex] = false; 9 | vfar[vertex] = true; 10 | Distance[vertex] = -1; 11 | Display[vertex] = 0; 12 | MaxGeodesic = -999; 13 | MinGeodesic = 999; 14 | } 15 | Queue.clear(); 16 | } 17 | 18 | float FMM::compute_distance_dijkstra(Vertex vi, Vertex vj, Vertex vk) 19 | { 20 | assert(!vfixed[vk]); 21 | assert(vfixed[vi]); 22 | assert(vfixed[vj]); 23 | 24 | float d1 = Distance[vi]; 25 | float d2 = Distance[vj]; 26 | 27 | assert(d1 != -1); 28 | assert(d2 != -1); 29 | 30 | float e1 = (mesh.position(vk) - mesh.position(vi)).norm(); 31 | float e2 = (mesh.position(vk) - mesh.position(vj)).norm(); 32 | 33 | d1 += e1; 34 | d2 += e2; 35 | 36 | return d1 < d2 ? d1 : d2; 37 | } 38 | 39 | //not used, another method 40 | float FMM::compute_distance2(Vertex vi, Vertex vj, Vertex vk) 41 | { 42 | //not used, another method 43 | assert(!vfixed[vk]); 44 | assert(vfixed[vi]); 45 | assert(vfixed[vj]); 46 | 47 | float d1 = Distance[vi]; 48 | float d2 = Distance[vj]; 49 | 50 | assert(d1 != -1); 51 | assert(d2 != -1); 52 | 53 | 54 | Vec3 pi = mesh.position(vi); 55 | Vec3 pj = mesh.position(vj); 56 | Vec3 pk = mesh.position(vk); 57 | 58 | //transform the cordinates 59 | Vec3 x_tri = (pj - pi).normalized(); 60 | Vec3 z_tri = (x_tri.cross(pk - pi)).normalized(); 61 | Vec3 y_tri = z_tri.cross(x_tri).normalized(); 62 | 63 | Mat3x3 A; 64 | A.col(0) = x_tri; 65 | A.col(1) = y_tri; 66 | A.col(2) = z_tri; 67 | 68 | Mat3x3 T; 69 | T = ((A.transpose()).inverse())*O; 70 | T.transposeInPlace(); 71 | 72 | pj = T*(pj-pi); 73 | pk = T*(pk-pi); 74 | pi = Vec3(0, 0, 0); 75 | 76 | pj(1) =pj(2)= 0; 77 | pk(2) = 0; 78 | 79 | 80 | float pjx = pj(0); 81 | 82 | float vsx = (d1*d1 - d2*d2 + pjx*pjx) / (2 * pjx); 83 | 84 | float vsy = -1*sqrt(std::abs(d1*d1 - vsx*vsx)); 85 | 86 | Vec3 vs(vsx, vsy, 0); 87 | 88 | Vec3 e1 = pj - pi; 89 | Vec3 e2 = pk - pj; 90 | Vec3 e3 = pk - vs; 91 | 92 | 93 | 94 | float d=-1.0; 95 | float sign1 = e3.cross(e1)(2); 96 | float sign2 = e3.cross(e2)(2); 97 | 98 | 99 | if (sign1 < 0 && sign2>0) 100 | d = e3.norm(); 101 | if (sign1 >= 0) 102 | d = e1.norm() + d1; 103 | if (sign2 <= 0) 104 | d = e2.norm() + d2; 105 | 106 | return d; 107 | } 108 | 109 | 110 | double FMM::compute_distance(Vertex vi, Vertex vj, Vertex vk) 111 | { 112 | assert(!vfixed[vk]); 113 | assert(vfixed[vi]); 114 | assert(vfixed[vj]); 115 | double a, b, u, costheta,A,B,C,di,dj,dk,t,CD; 116 | Vec3 e1, e2; 117 | e1 = mesh.position(vi) - mesh.position(vk); 118 | e2 = mesh.position(vj) - mesh.position(vk); 119 | costheta = (e1.normalized()).dot(e2.normalized()); 120 | di = Distance[vi]; 121 | dj = Distance[vj]; 122 | if (di < dj) 123 | { 124 | u = dj - di; 125 | a = e2.norm(); 126 | b = e1.norm(); 127 | } 128 | else 129 | { 130 | u = di - dj; 131 | a = e1.norm(); 132 | b = e2.norm(); 133 | } 134 | A = a*a + b*b - 2 * a*b*costheta; 135 | B = 2 * b*u*(a*costheta - b); 136 | C = b*b*(u*u - a*a*(1 - costheta*costheta)); 137 | 138 | float root = B*B - 4 * A*C; 139 | 140 | if (root < 0) 141 | root = 0; 142 | t = (-B + sqrt(root)) / (2 * A); 143 | 144 | 145 | CD = b*(t - u) / t; 146 | 147 | 148 | if (t > u&&(CDa*costheta)) { 149 | double T = di < dj ? di : dj; 150 | dk = T + t; 151 | } 152 | else 153 | { 154 | double d1 = e1.norm() + di; 155 | double d2 = e2.norm() + dj; 156 | dk = d1 < d2 ? d1 : d2; 157 | } 158 | 159 | 160 | return dk; 161 | 162 | } 163 | 164 | void FMM::compute_geodesic(const Vertex& start_vertex) 165 | { 166 | 167 | Vertex vstart = start_vertex; 168 | vfixed[vstart] = true; 169 | vfar[vstart] = false; 170 | Distance[vstart] = 0; 171 | 172 | for (auto const& v : mesh.vertices(vstart)) 173 | { 174 | vclose[v] = true; 175 | vfar[v] = false; 176 | float dk= (mesh.position(v) - mesh.position(vstart)).norm(); 177 | bool inserted=Queue.insert_or_update(v, dk); 178 | if (inserted) 179 | Distance[v] = dk; 180 | } 181 | 182 | while (!Queue.is_empty()) 183 | { 184 | Vertex v = Queue.pop(); 185 | vfixed[v] = true; 186 | vclose[v] = false; 187 | 188 | Halfedge e2,e3; 189 | Vertex vj, vk,vm; 190 | for (auto const& e1 : mesh.halfedges(v)) 191 | { 192 | vj = mesh.to_vertex(e1); 193 | if (vfixed[vj]) 194 | { 195 | e2 = mesh.next_halfedge(e1); 196 | e3 = mesh.next_halfedge(mesh.opposite_halfedge(e1)); 197 | vk = mesh.to_vertex(e2); 198 | vm = mesh.to_vertex(e3); 199 | //only compute the non-fixed points. 200 | if (!vfixed[vk]) 201 | { 202 | vclose[vk] = true; 203 | vfar[vk] = false; 204 | float dk = compute_distance(v, vj, vk); 205 | bool inserted = Queue.insert_or_update(vk, dk); 206 | //if inserted, means the value is smaller than previous that computed in another path, so update it in the Distance. 207 | if (inserted) 208 | Distance[vk] = dk; 209 | } 210 | if (!vfixed[vm]) 211 | { 212 | vclose[vm] = true; 213 | vfar[vm] = false; 214 | float dm = compute_distance(v, vj, vm); 215 | bool inserted = Queue.insert_or_update(vm, dm); 216 | if (inserted) 217 | Distance[vm] = dm; 218 | } 219 | } 220 | } 221 | } 222 | //find out the Max G, Min G and the Max geodesic distance span along edge. 223 | for (auto const& vertex : mesh.vertices()) 224 | { 225 | float d1 = Distance[vertex]; 226 | Display[vertex] = d1; 227 | float d2 = 0.0; 228 | for (auto const& vj : mesh.vertices(vertex)) 229 | { 230 | float d = std::abs(Distance[vertex] - Distance[vj]); 231 | if (d > d2) 232 | d2 = d; 233 | } 234 | 235 | if (d1 > MaxGeodesic) 236 | MaxGeodesic = d1; 237 | if (d2 > MaxEdgeSpan) 238 | MaxEdgeSpan = d2; 239 | if (d1 < MinGeodesic) 240 | MinGeodesic = d1; 241 | } 242 | 243 | } 244 | 245 | 246 | std::vector> FMM::draw_isolines() 247 | { 248 | //make sure this is no more than one line cross the triangle (step>MaxEdgeSpan) 249 | int n_iso = MaxGeodesic / MaxEdgeSpan; 250 | float step = MaxGeodesic / float(n_iso); 251 | SurfaceMesh::Vertex v1,v2; 252 | std::vector> Lines; 253 | for (auto const& face : mesh.faces()) 254 | { 255 | std::vector points; 256 | for (auto const& edge : mesh.halfedges(face)) 257 | { 258 | v1 = mesh.from_vertex(edge); 259 | v2 = mesh.to_vertex(edge); 260 | int n1 = Distance[v1] / step; 261 | int n2 = Distance[v2] / step; 262 | if (n1 != n2) 263 | { 264 | int n = n1 < n2 ? n1 : n2; 265 | ++n; 266 | float lamda = (n*step - Distance[v1]) / (Distance[v2] - Distance[v1]); 267 | Vec3 p = (1 - lamda)*mesh.position(v1) + lamda*mesh.position(v2); 268 | points.push_back(p); 269 | } 270 | } 271 | assert(points.size() <= 2); 272 | if (points.size() == 1) 273 | { 274 | Lines.push_back(std::pair(points[0], points[0])); 275 | } 276 | if (points.size() == 2) 277 | { 278 | Lines.push_back(std::pair(points[0], points[1])); 279 | } 280 | } 281 | 282 | return Lines; 283 | 284 | } 285 | 286 | 287 | std::vector> FMM::draw_path(const Vertex& start, const Vertex& end) 288 | { 289 | Vertex v_local = end, v_next = end; 290 | std::vector> Lines; 291 | Lines.push_back(std::pair(mesh.position(v_local), mesh.position(v_next))); 292 | while (v_next != start) 293 | { 294 | Scalar step = -1; 295 | for (auto const& e : mesh.halfedges(v_local)) 296 | { 297 | Vertex v = mesh.to_vertex(e); 298 | float d = Distance[v_local] - Distance[v]; 299 | if (d > step) 300 | { 301 | v_next = v; 302 | step = d; 303 | } 304 | } 305 | Lines.push_back(std::pair(mesh.position(v_local), mesh.position(v_next))); 306 | v_local = v_next; 307 | } 308 | return Lines; 309 | } 310 | 311 | 312 | -------------------------------------------------------------------------------- /src/FMM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include"FMMPriorityQueue.h" 4 | 5 | using namespace OpenGP; 6 | 7 | class FMM 8 | { 9 | typedef SurfaceMesh::Face Face; 10 | typedef SurfaceMesh::Vertex Vertex; 11 | typedef SurfaceMesh::Halfedge Halfedge; 12 | private:SurfaceMesh& mesh; 13 | FMMPriorityQueue Queue; 14 | SurfaceMesh::Vertex_property vfixed; 15 | SurfaceMesh::Vertex_property vclose; 16 | SurfaceMesh::Vertex_property vfar; 17 | SurfaceMesh::Vertex_property Distance; 18 | SurfaceMesh::Vertex_property Display; 19 | const Mat3x3 O = Mat3x3::Identity(3,3); 20 | public: float MaxGeodesic; 21 | float MinGeodesic; 22 | float MaxEdgeSpan; 23 | public: 24 | FMM(SurfaceMesh& mesh) :mesh(mesh), Queue(mesh) 25 | { 26 | vfixed=mesh.add_vertex_property("FMM:v:fixed"); 27 | vclose=mesh.add_vertex_property("FMM:v:close"); 28 | vfar = mesh.add_vertex_property("FMM:v:far"); 29 | Distance=mesh.add_vertex_property("FMM:Geodesic"); 30 | Display=mesh.vertex_property("v:quality"); 31 | MaxGeodesic = 0.0; 32 | MaxEdgeSpan = 0.0; 33 | MinGeodesic = 999; 34 | } 35 | 36 | ~FMM() 37 | { 38 | mesh.remove_vertex_property(vfixed); 39 | mesh.remove_vertex_property(vclose); 40 | mesh.remove_vertex_property(Distance); 41 | mesh.remove_vertex_property(vfar); 42 | } 43 | 44 | void clean_up(); 45 | 46 | void compute_geodesic(const Vertex& start_vertex); 47 | 48 | std::vector> draw_isolines(); 49 | 50 | std::vector> draw_path(const Vertex& start, const Vertex& end); 51 | 52 | float Geodesic(const Vertex& v) { return Distance[v]; }; 53 | 54 | private:float compute_distance2(Vertex vx, Vertex vy, Vertex vz); 55 | 56 | float compute_distance_dijkstra(Vertex vx, Vertex vy, Vertex vz); 57 | 58 | double compute_distance(Vertex vi, Vertex vj, Vertex vk); 59 | }; -------------------------------------------------------------------------------- /src/FMMPriorityQueue.cpp: -------------------------------------------------------------------------------- 1 | #include "FMMPriorityQueue.h" 2 | 3 | FMMPriorityQueue::FMMPriorityQueue(SurfaceMesh& mesh):mesh(mesh),queue(Dist) 4 | { 5 | Dist = mesh.add_vertex_property("v:update distances compare"); 6 | } 7 | 8 | FMMPriorityQueue::~FMMPriorityQueue() 9 | { 10 | mesh.remove_vertex_property(Dist); 11 | } 12 | 13 | bool FMMPriorityQueue::insert_or_update(Vertex v, float d) 14 | { 15 | //if f is not added then insert 16 | if (Dist[v] == -1) 17 | { 18 | Dist[v] = d; 19 | queue.insert(v); 20 | return true; 21 | } 22 | 23 | //if previous distance is minimal then return 24 | float prev_d = Dist[v]; 25 | if (prev_d <= d) 26 | return false; 27 | 28 | //else update the distance 29 | queue.erase(v); 30 | Dist[v] = d; 31 | queue.insert(v); 32 | return true; 33 | } 34 | 35 | FMMPriorityQueue::Vertex FMMPriorityQueue::pop() 36 | { 37 | Vertex v = *queue.begin(); 38 | queue.erase(queue.begin()); 39 | return v; 40 | } 41 | 42 | void FMMPriorityQueue::clear() 43 | { 44 | for (auto const& vertex : mesh.vertices()) 45 | Dist[vertex] = -1; 46 | 47 | queue.clear(); 48 | } -------------------------------------------------------------------------------- /src/FMMPriorityQueue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | using namespace OpenGP; 6 | 7 | class VertexCompare 8 | { 9 | typedef SurfaceMesh::Vertex Vertex; 10 | typedef SurfaceMesh::Vertex_property PerVertexFloat; 11 | private:const PerVertexFloat& vprio; 12 | public: 13 | VertexCompare(PerVertexFloat& vprio_) :vprio(vprio_) {}; 14 | bool operator()(Vertex v1, Vertex v2) 15 | { 16 | float d1 = vprio[v1]; 17 | float d2 = vprio[v2]; 18 | return ( (d1 == d2) ? (v1.idx() < v2.idx()) : (d1 < d2)); 19 | } 20 | }; 21 | 22 | class FMMPriorityQueue 23 | { 24 | typedef SurfaceMesh::Vertex Vertex; 25 | typedef std::set VertexQueue; 26 | private: 27 | SurfaceMesh& mesh; 28 | VertexQueue queue; 29 | SurfaceMesh::Vertex_property Dist; 30 | public: 31 | FMMPriorityQueue(SurfaceMesh& mesh); 32 | ~FMMPriorityQueue(); 33 | 34 | bool insert_or_update(Vertex v, float d); 35 | 36 | Vertex pop(); 37 | 38 | void clear(); 39 | 40 | bool is_empty() 41 | { 42 | return queue.empty(); 43 | } 44 | 45 | int size() 46 | { 47 | return queue.size(); 48 | } 49 | }; -------------------------------------------------------------------------------- /src/GeodesicInHeat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using namespace OpenGP; 7 | 8 | class GeodesicInHeat 9 | { 10 | typedef SurfaceMesh::Vertex Vertex; 11 | typedef SurfaceMesh::Face Face; 12 | typedef SurfaceMesh::Halfedge Halfedge; 13 | typedef Eigen::Triplet Triplet; 14 | typedef Eigen::SparseMatrix Sparse; 15 | private:SurfaceMesh& mesh; 16 | Eigen::SparseMatrix Laplacian; 17 | Eigen::SparseMatrix LaplacianDirichlet; 18 | Eigen::SparseMatrix Grad_x; 19 | Eigen::SparseMatrix Grad_y; 20 | Eigen::SparseMatrix Grad_z; 21 | Eigen::SparseMatrix Div_x; 22 | Eigen::SparseMatrix Div_y; 23 | Eigen::SparseMatrix Div_z; 24 | Eigen::SparseMatrix AreaMatrix; 25 | Eigen::SparseMatrix InvertAreaMatrix; 26 | SurfaceMesh::Vertex_property HeatDisplay; 27 | SurfaceMesh::Vertex_property HeatDistance; 28 | bool initialized = false; 29 | float avglength; 30 | float factor = 100; 31 | public: float MaxGeodesic; 32 | float MinGeodesic; 33 | float MaxEdgeSpan; 34 | bool Dirichlet = false; //display Dirichlet? 35 | bool Neumann = false; //display Neumann? 36 | 37 | public:GeodesicInHeat(SurfaceMesh& mesh) ; 38 | 39 | //initialization construct operator; 40 | void initialization(); 41 | 42 | void clean_up(); 43 | 44 | void compute_distance(const Vertex& start_vertex); 45 | 46 | std::vector> draw_isolines(); 47 | 48 | std::vector> draw_path(const Vertex& start,const Vertex& end); 49 | 50 | void set_factor(float m) { 51 | factor = m; 52 | std::cout<< "M: " < 4 | #include"SurfaceMeshRenderLines.h" 5 | #include "FMM.h" 6 | #include "GeodesicInHeat.h" 7 | #include 8 | 9 | using namespace OpenGP; 10 | class Method 11 | { 12 | private:SurfaceMesh& mesh; 13 | FMM FastMarching; 14 | GeodesicInHeat Heat; 15 | int selectedmethod=0; 16 | SurfaceMesh::Vertex start_v; 17 | SurfaceMesh::Vertex end_v; 18 | bool start = false; 19 | bool end = false; 20 | bool Heat_computed = false; 21 | bool FMM_computed = false; 22 | public: float Color_Max=1; 23 | float Color_Min=0; 24 | Segmentslines Iso; 25 | Segmentslines Path; 26 | 27 | public:Method(SurfaceMesh& mesh) :mesh(mesh),FastMarching(mesh),Heat(mesh) { 28 | }; 29 | 30 | void selectmethod(int index) 31 | { 32 | std::vector> Lines; 33 | selectedmethod = index; 34 | if ((selectedmethod == 1)&&start&&end) 35 | { 36 | //display FMM result 37 | FastMarching.clean_up(); 38 | FastMarching.compute_geodesic(start_v); 39 | //set the color map range 40 | Color_Max = FastMarching.MaxGeodesic; 41 | Color_Min = FastMarching.MinGeodesic; 42 | Lines = removelines(); 43 | //clear lines 44 | Path.update(Lines); 45 | Iso.update(Lines); 46 | FMM_computed = true; 47 | mLogger() << "FMM Distance: " << FastMarching.Geodesic(end_v); 48 | } 49 | if ((selectedmethod == 2)&&start&&end) 50 | { 51 | //display Heat result 52 | Lines = removelines(); 53 | Path.update(Lines); 54 | Iso.update(Lines); 55 | Heat.clean_up(); 56 | //clear lines 57 | Heat.compute_distance(start_v); 58 | Color_Max = Heat.MaxGeodesic; 59 | Color_Min = Heat.MinGeodesic; 60 | mLogger() << "Heat Distance: " << Heat.Geodesic(end_v); 61 | Heat_computed = true; 62 | } 63 | } 64 | 65 | void setHeatParameter(float m) 66 | { 67 | Heat.set_factor(m); 68 | } 69 | 70 | void MeanBoudary() 71 | { 72 | Heat.Dirichlet = false; 73 | Heat.Neumann = false; 74 | } 75 | 76 | void DirichletBoundary() 77 | { 78 | Heat.Dirichlet = true; 79 | Heat.Neumann = false; 80 | } 81 | 82 | void NeumannBounday() 83 | { 84 | Heat.Neumann = true; 85 | Heat.Dirichlet = false; 86 | } 87 | 88 | void selectvertex(Vec3 p) 89 | { 90 | SurfaceMesh::Vertex v; 91 | if (!start || !end) 92 | { 93 | float d = 999,d0; 94 | for (auto const& vertex : mesh.vertices()) 95 | { 96 | d0 = (mesh.position(vertex) - p).norm(); 97 | if (d0 < d) 98 | { 99 | v = vertex; 100 | d = d0; 101 | } 102 | } 103 | } 104 | if (!start) 105 | { 106 | start = true; 107 | start_v = v; 108 | mLogger() << "Starting point selected (" << v.idx() << ")"; 109 | return; 110 | } 111 | if (!end) 112 | { 113 | end = true; 114 | end_v = v; 115 | mLogger() << "Ending point selected (" << v.idx() << ")"; 116 | return; 117 | } 118 | } 119 | 120 | 121 | void removecolor() 122 | { 123 | //remove all color, lines, selected points 124 | if (selectedmethod == 0) 125 | return; 126 | if (selectedmethod == 1) 127 | FastMarching.clean_up(); 128 | if (selectedmethod == 2) 129 | Heat.clean_up(); 130 | selectedmethod = 0; 131 | start = false; 132 | end = false; 133 | Heat_computed = false; 134 | FMM_computed = false; 135 | std::vector> Lines=removelines(); 136 | Iso.update(Lines); 137 | Path.update(Lines); 138 | } 139 | 140 | 141 | std::vector> removelines() 142 | { 143 | return std::vector>(); 144 | } 145 | 146 | 147 | void drawpath() 148 | { 149 | std::vector> Lines; 150 | if (start&&end) 151 | { 152 | assert(start_v.is_valid()); 153 | assert(end_v.is_valid()); 154 | if (selectedmethod == 1) 155 | { 156 | Lines=FastMarching.draw_path(start_v,end_v); 157 | Path.update(Lines); 158 | } 159 | if (selectedmethod == 2) 160 | { 161 | Lines=Heat.draw_path(start_v,end_v); 162 | Path.update(Lines); 163 | } 164 | } 165 | else 166 | { 167 | if (!start) 168 | mLogger() << "Please select start and end point"; 169 | if (!end) 170 | mLogger() << "Please select end point"; 171 | } 172 | } 173 | 174 | void drawisolines() 175 | { 176 | std::vector> Lines; 177 | if (start) 178 | { 179 | if (selectedmethod == 1) 180 | { 181 | Lines = FastMarching.draw_isolines(); 182 | Iso.update(Lines); 183 | } 184 | if (selectedmethod == 2) 185 | { 186 | Lines = Heat.draw_isolines(); 187 | Iso.update(Lines); 188 | } 189 | } 190 | else 191 | { 192 | mLogger() << "Please select start point"; 193 | } 194 | } 195 | 196 | void error_compare() 197 | { 198 | if (start&&Heat_computed&&FMM_computed) 199 | { 200 | float Max_Heat=-999, Max_FMM=-999, Mean_Heat=0, Mean_FMM=0, G_Heat, G_FMM, Euclidean, Er_Heat, Er_FMM; 201 | int n = 0; 202 | for (auto const& vertex : mesh.vertices()) 203 | { 204 | if (vertex != start_v) { 205 | Euclidean = (mesh.position(vertex) - mesh.position(start_v)).norm(); 206 | G_Heat = Heat.Geodesic(vertex); 207 | G_FMM = FastMarching.Geodesic(vertex); 208 | Er_Heat = std::abs(G_Heat - Euclidean) / Euclidean; 209 | Er_FMM = std::abs(G_FMM - Euclidean) / Euclidean; 210 | if (Er_Heat > Max_Heat) 211 | Max_Heat = Er_Heat; 212 | if (Er_FMM > Max_FMM) 213 | Max_FMM = Er_FMM; 214 | Mean_Heat += Er_Heat; 215 | Mean_FMM += Er_FMM; 216 | } 217 | ++n; 218 | } 219 | 220 | Mean_FMM /= float(n); 221 | Mean_Heat /= float(n); 222 | mLogger() << "Test on Plane:" << n / 1000 << "k"; 223 | mLogger() < Max_Heat) 249 | Max_Heat = Er_Heat; 250 | if (Er_FMM > Max_FMM) 251 | Max_FMM = Er_FMM; 252 | Mean_Heat += Er_Heat; 253 | Mean_FMM += Er_FMM; 254 | } 255 | ++n; 256 | } 257 | Mean_FMM /= float(n); 258 | Mean_Heat /= float(n); 259 | mLogger() << "Test on Sphere (Radius 1,Center Origin):" << n / 1000 << "k "; 260 | mLogger() << std::setprecision(2) << std::setiosflags(std::ios::fixed | std::ios::showpoint) 261 | << "Heat Method----Max Error: " << Max_Heat * 100 << "% Mean Error:" << Mean_Heat * 100 << "%"; 262 | mLogger() << std::setprecision(2) << std::setiosflags(std::ios::fixed | std::ios::showpoint) 263 | << "FMM Method----Max Error: " << Max_FMM * 100 << "% Mean Error:" << Mean_FMM * 100 << "%"; 264 | } 265 | } 266 | 267 | 268 | }; -------------------------------------------------------------------------------- /src/SurfaceMeshRenderGeodesic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace OpenGP { 8 | 9 | class SurfaceMeshRenderGeodesic : public SceneObject { 10 | private: 11 | SurfaceMesh& mesh; 12 | VertexArrayObject vao; 13 | ArrayBuffer v_buffer; 14 | ArrayBuffer n_buffer; 15 | ArrayBuffer q_buffer; 16 | ElementArrayBuffer i_buffer; 17 | GLuint _tex; ///< Colormap Texture ID 18 | 19 | public: 20 | SurfaceMeshRenderGeodesic(SurfaceMesh& mesh) : mesh(mesh) {} 21 | 22 | /// @{ color quality mapping 23 | private: 24 | bool _use_colormap = false; 25 | Scalar _colormap_min = 0.0f; 26 | Scalar _colormap_max = 1.0f; 27 | public: 28 | const GLchar* SurfaceMeshRenderGeodesic_vshader = R"GLSL( 29 | #version 330 core 30 | uniform mat4 M; 31 | uniform mat4 MV; 32 | uniform mat4 MVP; 33 | 34 | ///--- Colormap 1D texture 35 | uniform sampler1D colormap; 36 | uniform int use_colormap; ///< 0 37 | uniform float colormap_min; ///< 0.0; 38 | uniform float colormap_max; ///< 1.0; 39 | 40 | ///--- Attributes 41 | in vec3 vposition; ///< per-vertex position 42 | in vec3 vnormal; ///< per-vertex normal 43 | in float vquality; ///< per-vertex quality 44 | 45 | ///--- Outputs 46 | out vec3 fnormal; ///< per-fragment normal 47 | out vec3 fcolor; ///< per-fragment color 48 | 49 | void main(){ 50 | gl_Position = MVP * vec4(vposition, 1.0); 51 | fnormal = normalize( inverse(transpose(mat3(MV))) * vnormal ); 52 | if(use_colormap==0) 53 | fcolor = vec3(1,0,0); 54 | else{ 55 | float vquality_normalized = (vquality - colormap_min) / (colormap_max - colormap_min); 56 | fcolor = texture(colormap, vquality_normalized).rgb; 57 | } 58 | } 59 | )GLSL"; 60 | 61 | const char* SurfaceMeshRenderGeodesic_fshader = R"GLSL( 62 | #version 330 core 63 | // uniform vec3 LDIR; ///< TODO: fix me 64 | in vec3 fnormal; ///< normal camera coords 65 | in vec3 fcolor; 66 | out vec4 FragColor; 67 | 68 | void main(){ 69 | vec3 LDIR = vec3(0,0,1); 70 | vec3 ldir = normalize(LDIR); 71 | float albedo = max( dot( normalize(fnormal), ldir ), 0 ); 72 | vec3 basecolor = fcolor; 73 | FragColor = vec4(basecolor*albedo, 1); 74 | // FragColor = vec4(fnormal,1); ///< normal map 75 | } 76 | )GLSL"; 77 | 78 | 79 | 80 | void init() { 81 | ///--- Shaders 82 | program.add_vshader_from_source(SurfaceMeshRenderGeodesic_vshader); 83 | program.add_fshader_from_source(SurfaceMeshRenderGeodesic_fshader); 84 | program.link(); 85 | 86 | ///--- Vertex positions 87 | auto vpoints = mesh.get_vertex_property("v:point"); CHECK(vpoints); 88 | v_buffer.upload_raw(vpoints.data(), mesh.n_vertices()); 89 | 90 | ///--- Vertex normals 91 | auto vnormals = mesh.get_vertex_property("v:normal"); CHECK(vnormals); 92 | n_buffer.upload_raw(vnormals.data(), mesh.n_vertices()); 93 | 94 | ///--- Vertex quality (Optional) 95 | auto vqualitys = mesh.get_vertex_property("v:quality"); 96 | if (vqualitys) q_buffer.upload_raw(vqualitys.data(), mesh.n_vertices()); 97 | 98 | ///--- Creates index/element buffer data 99 | CHECK(mesh.n_faces() > 0); 100 | std::vector triangles; 101 | for (auto f : mesh.faces()) 102 | for (auto v : mesh.vertices(f)) 103 | triangles.push_back(v.idx()); 104 | i_buffer.upload(triangles); 105 | 106 | ///--- Attributes 107 | program.bind(); 108 | vao.bind(); 109 | ///--- Defaulted attributes 110 | program.set_attribute("vquality", 1.0f); 111 | 112 | ///--- Attributes 113 | program.set_attribute("vposition", v_buffer); 114 | program.set_attribute("vnormal", n_buffer); 115 | if (vqualitys) program.set_attribute("vquality", q_buffer); 116 | 117 | ///--- Colormap texture setup 118 | { 119 | // TODO: wrap this within the Game Engine 120 | const int sz = 3; 121 | GLfloat _tex_data[3 * sz] = {/*white*/ 0.0, 1.0, 1.0, 122 | /*green*/ 0.0, 1.0, 0.0, 123 | /*blue*/ 0.0, 0.0, 1.0 }; 124 | glGenTextures(2, &_tex); 125 | glBindTexture(GL_TEXTURE_1D, _tex); 126 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, sz, 0, GL_RGB, GL_FLOAT, _tex_data); 127 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 128 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, 3); 129 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 130 | program.bind(); ///< note set_attribute unbound them 131 | GLint tex_id = glGetUniformLocation(program.programId(), "colormap"); 132 | glActiveTexture(GL_TEXTURE0 + 0); 133 | glUniform1i(tex_id, 0); 134 | program.release(); 135 | } 136 | vao.release(); 137 | program.release(); 138 | } 139 | 140 | void init_data() 141 | { 142 | auto vquality = mesh.get_vertex_property("v:quality"); 143 | std::vector v_q; 144 | for (auto const& v : mesh.vertices()) 145 | { 146 | v_q.push_back(vquality[v]); 147 | } 148 | q_buffer.upload(v_q); 149 | } 150 | 151 | void display() { 152 | program.bind(); 153 | vao.bind(); 154 | ///--- Bind textures 155 | glActiveTexture(GL_TEXTURE0 + 0); 156 | glBindTexture(GL_TEXTURE_1D, _tex); 157 | 158 | ///--- Upload settings 159 | program.set_uniform("use_colormap", (int)_use_colormap); 160 | program.set_uniform("colormap_min", (float)_colormap_min); 161 | program.set_uniform("colormap_max", (float)_colormap_max); 162 | 163 | ///--- Draw data 164 | glDrawElements(GL_TRIANGLES, i_buffer.size(), GL_UNSIGNED_INT, ZERO_BUFFER_OFFSET); 165 | vao.release(); 166 | program.release(); 167 | } 168 | 169 | Box3 bounding_box() { 170 | return OpenGP::bounding_box(mesh); 171 | } 172 | 173 | void colormap_enabled(bool enabled) { 174 | _use_colormap = enabled; 175 | } 176 | 177 | void colormap_set_range(Scalar min, Scalar max) { 178 | _colormap_min = min; 179 | _colormap_max = max; 180 | } 181 | 182 | }; 183 | } 184 | 185 | -------------------------------------------------------------------------------- /src/SurfaceMeshRenderLines.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | //============================================================================= 7 | namespace OpenGP { 8 | //============================================================================= 9 | 10 | class Segmentslines : public SceneObject { 11 | public: 12 | typedef std::vector> Segments; 13 | protected: 14 | VertexArrayObject _vao; ///< OpenGL object storing data/attributes 15 | MatMxN _data; ///< reference to data to be rendered 16 | ArrayBuffer _buffer_vpos; ///< per-vertex position 17 | ArrayBuffer _buffer_vcolor; ///< per-vertex color (optional) 18 | /// @{ constructors 19 | public: 20 | Segmentslines() { } 21 | Segmentslines(const Segments& segments) { load(segments); } 22 | Segmentslines(const MatMxN& P1, const MatMxN& P2) { load(P1, P2); } 23 | Segmentslines(const std::vector& segments) { load(segments); } 24 | public: 25 | HEADERONLY_INLINE void load(const MatMxN& P1, const MatMxN& P2); 26 | HEADERONLY_INLINE void load(const Segments& segments); 27 | HEADERONLY_INLINE void load(const std::vector& segments); 28 | HEADERONLY_INLINE void set_color(const Vec3& v) { this->color = v; } 29 | /// @} 30 | 31 | public: 32 | HEADERONLY_INLINE void init(); 33 | HEADERONLY_INLINE void display(); 34 | HEADERONLY_INLINE Box3 bounding_box(); 35 | HEADERONLY_INLINE void update(Segments& segments); 36 | 37 | protected: 38 | const GLchar* vshader = R"GLSL( 39 | #version 330 40 | uniform mat4 M; 41 | uniform mat4 MV; 42 | uniform mat4 MVP; 43 | in vec3 vpoint; 44 | in vec3 vcolor; 45 | out vec3 fcolor; 46 | void main() { 47 | gl_Position = MVP * vec4(vpoint, 1.0); 48 | fcolor = vcolor; 49 | } 50 | )GLSL"; 51 | 52 | const char* fshader = R"GLSL( 53 | #version 330 54 | in vec3 fcolor; 55 | out vec4 color; 56 | void main(){ color = vec4(fcolor,1); } 57 | )GLSL"; 58 | }; 59 | 60 | //============================================================================= 61 | 62 | void Segmentslines::load(const MatMxN &P1, const MatMxN &P2) { 63 | CHECK(P1.cols() == P2.cols()); 64 | CHECK((P1.rows() == 3) && (P2.rows() == 3)); 65 | _data.resize(3, 2 * P1.cols()); 66 | for (int i = 0; i& segments) 81 | { 82 | CHECK(segments.size() % 2 == 0); 83 | _data.resize(3, segments.size()); 84 | for (uint i = 0; i < segments.size(); i++) 85 | { 86 | _data.col(i) = segments[i]; 87 | } 88 | } 89 | 90 | void Segmentslines::init() { 91 | ///--- Shader 92 | program.add_vshader_from_source(vshader); 93 | program.add_fshader_from_source(fshader); 94 | program.link(); 95 | 96 | ///--- Data 97 | _buffer_vpos.upload_raw(_data.data(), _data.cols()); 98 | color = Vec3(0, 0, 0); 99 | ///--- Attributes 100 | program.bind(); 101 | _vao.bind(); 102 | program.set_attribute("vpoint", _buffer_vpos); 103 | program.set_attribute("vcolor", color); ///< default use object color 104 | _vao.release(); 105 | program.release(); 106 | } 107 | 108 | void Segmentslines::update(Segments& segments) { 109 | Segmentslines::load(segments); 110 | _buffer_vpos.upload_raw(_data.data(), _data.cols()); 111 | } 112 | 113 | 114 | void Segmentslines::display() { 115 | if (_data.cols() == 0) return; 116 | _vao.bind(); 117 | program.bind(); 118 | glDrawArrays(GL_LINES, 0, _data.cols()); 119 | 120 | 121 | glEnable(GL_LINE_SMOOTH); 122 | glLineWidth(5); 123 | 124 | 125 | program.release(); 126 | _vao.release(); 127 | } 128 | 129 | Box3 Segmentslines::bounding_box() { 130 | Box3 box; 131 | Vec3 _min = _data.rowwise().minCoeff(); 132 | Vec3 _max = _data.rowwise().maxCoeff(); 133 | return Box3(_min, _max); 134 | } 135 | 136 | //============================================================================= 137 | } // namespace OpenGP 138 | //============================================================================= 139 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include"SurfaceMeshRenderGeodesic.h" 6 | #include "GeodesicInHeat.h" 7 | #include "FMM.h" 8 | #include "Method.h" 9 | #include 10 | using namespace OpenGP; 11 | 12 | struct CustomizeWindow : public GlfwWindow 13 | { 14 | SurfaceMesh mesh; 15 | Method UI; 16 | SurfaceMeshRenderGeodesic renderer= SurfaceMeshRenderGeodesic(mesh); 17 | bool mouse_cliked = false; 18 | bool start = false; 19 | bool end = false; 20 | bool input = false; 21 | int W; 22 | int H; 23 | CustomizeWindow(char** argv,int W,int H) :UI(mesh),GlfwWindow("Final Project", W, H),W(W),H(H) 24 | { 25 | 26 | mesh.read(argv[1]); 27 | 28 | mesh.update_face_normals(); 29 | mesh.update_vertex_normals(); 30 | 31 | this->scene.add(renderer); 32 | this->scene.add(UI.Iso); 33 | this->scene.add(UI.Path); 34 | mLogger() << "Final Project-----Shengyang Wei\n\n\nMouse Left: Drag\n\nMouse Right: Select Point"; 35 | mLogger()<<"Key 1: FMM Method\nKey 2: Heat Method(Mean)\n\nKey 3: Heat Method(Neumann)\n\nKey 4: Heat Method(Dirichlet)\n\nKey F: Set Heat Time Factor\n\nKey I : Isolines\n\nKey P : ShowPath\n\nKey R : Remove"; 36 | } 37 | 38 | bool key_callback(int key, int scancode, int action, int mods) override 39 | { 40 | GlfwWindow::key_callback(key, scancode, action, mods); 41 | 42 | if (action == GLFW_PRESS) 43 | { 44 | switch (key) 45 | { 46 | case GLFW_KEY_1: 47 | if (start) { 48 | UI.selectmethod(1); 49 | renderer.colormap_enabled(true); 50 | renderer.colormap_set_range(UI.Color_Min, UI.Color_Max); 51 | } 52 | else 53 | std::cout << "Please select starting point first\n"; 54 | break; 55 | 56 | case GLFW_KEY_2: 57 | if (start) { 58 | UI.MeanBoudary(); 59 | UI.selectmethod(2); 60 | renderer.colormap_enabled(true); 61 | renderer.colormap_set_range(UI.Color_Min, UI.Color_Max); 62 | } 63 | else 64 | std::cout << "Please select starting point first\n"; 65 | break; 66 | 67 | case GLFW_KEY_3: 68 | if (start) { 69 | UI.NeumannBounday(); 70 | UI.selectmethod(2); 71 | renderer.colormap_enabled(true); 72 | renderer.colormap_set_range(UI.Color_Min, UI.Color_Max); 73 | } 74 | else 75 | std::cout << "Please select starting point first\n"; 76 | break; 77 | 78 | case GLFW_KEY_4: 79 | if (start) { 80 | UI.DirichletBoundary(); 81 | UI.selectmethod(2); 82 | renderer.colormap_enabled(true); 83 | renderer.colormap_set_range(UI.Color_Min, UI.Color_Max); 84 | } 85 | else 86 | std::cout << "Please select starting point first\n"; 87 | break; 88 | 89 | case GLFW_KEY_R: 90 | UI.removecolor(); 91 | renderer.colormap_enabled(true); 92 | renderer.colormap_set_range(0,1); 93 | start = false; 94 | end = false; 95 | break; 96 | 97 | case GLFW_KEY_P: 98 | UI.drawpath(); 99 | break; 100 | 101 | case GLFW_KEY_T: 102 | UI.error_compare(); 103 | break; 104 | 105 | case GLFW_KEY_S: 106 | UI.error_compare_sphere(); 107 | break; 108 | 109 | case GLFW_KEY_I: 110 | UI.drawisolines(); 111 | break; 112 | 113 | case GLFW_KEY_F: 114 | input = true; 115 | break; 116 | 117 | default: 118 | break; 119 | } 120 | } 121 | else 122 | { 123 | if (input) 124 | { 125 | //after input the value, please press 2 or 3 or 4 to display the new result 126 | mLogger() << "Please input the parameter M:"; 127 | float M; 128 | input = false; 129 | if (std::cin >> M&&M > 0) 130 | UI.setHeatParameter(M); 131 | else 132 | mLogger() << "Invalid parameter"; 133 | input = false; 134 | } 135 | } 136 | 137 | renderer.init_data(); 138 | 139 | return false; 140 | 141 | } 142 | 143 | bool mouse_press_callback(int button, int action, int mods) override { 144 | using namespace OpenGP; 145 | 146 | if ((button == GLFW_MOUSE_BUTTON_LEFT) && (action == GLFW_PRESS)) { 147 | double x_window, y_window; 148 | getFramebufferCursorPos(&x_window, &y_window); 149 | Vec3 pos_window(x_window, y_window, 0.0f); 150 | Vec3 pos_clip = window_to_clip(pos_window); 151 | scene.trackball_camera.begin_rotate(pos_clip); 152 | return true; 153 | } 154 | 155 | if ((button == GLFW_MOUSE_BUTTON_LEFT) && (action == GLFW_RELEASE)) { 156 | scene.trackball_camera.finish_rotate(); 157 | return true; 158 | } 159 | 160 | if (button == GLFW_MOUSE_BUTTON_RIGHT) { 161 | if (action == GLFW_RELEASE && (!start || !end)) { 162 | double Xpos, Ypos; 163 | glfwGetCursorPos(_window, &Xpos, &Ypos); 164 | if (!start) { 165 | UI.selectvertex(unproject_mouse(Xpos, Ypos)); 166 | start = true; 167 | return true; 168 | } 169 | if (!end) { 170 | UI.selectvertex(unproject_mouse(Xpos, Ypos)); 171 | end = true; 172 | return true; 173 | } 174 | } 175 | } 176 | 177 | return false; 178 | } 179 | 180 | 181 | OpenGP::Vec3 window_to_clip(const Vec3& pos_window) { 182 | Vec3 retval; 183 | retval(0) = 2.0f * static_cast(pos_window(0)) / _width - 1.0f; 184 | retval(1) = 1.0f - 2.0f * static_cast(pos_window(1)) / _height; 185 | retval(2) = 2.0f * pos_window(2) - 1.0f; 186 | return retval; 187 | } 188 | 189 | 190 | 191 | 192 | 193 | OpenGP::Vec3 unproject_mouse(OpenGP::Scalar xPos, OpenGP::Scalar yPos) 194 | { 195 | typedef Eigen::Matrix Vec4; 196 | float zPos; 197 | glReadBuffer(GL_FRONT); 198 | int Scale = _height / H; 199 | xPos = Scale*xPos; 200 | yPos = Scale*yPos; 201 | glReadPixels(int(xPos), _height - int(yPos), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &zPos); 202 | float Objectx, Objecty, Objectz, Objectw; 203 | Objectx = 2.0f*xPos / _width - 1.0f; 204 | Objecty = 1.0f - 2.0f*yPos / _height; 205 | Objectz = 2.0f*zPos - 1.0f; 206 | Objectw = 1.0f; 207 | Vec4 near(Objectx, Objecty, Objectz, Objectw); 208 | Mat4x4 U = (scene.trackball_camera.projection_matrix() * scene.trackball_camera.view_matrix()).inverse(); 209 | Vec4 h_point = (U*near); 210 | Vec3 point(h_point[0]/h_point[3], h_point[1]/ h_point[3], h_point[2]/ h_point[3]); 211 | return point; 212 | } 213 | 214 | 215 | 216 | bool mouse_move_callback(double x_window, double y_window) override { 217 | x_window *= scale_factor_retina(); 218 | y_window *= scale_factor_retina(); 219 | 220 | bool left_down = (glfwGetMouseButton(_window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); 221 | Vec3 pos_window(x_window, y_window, 0.0f); 222 | Vec3 pos_clip = window_to_clip(pos_window); 223 | 224 | bool managed = false; 225 | 226 | // Rotate 227 | if (left_down) { 228 | scene.trackball_camera.rotate(pos_clip); 229 | managed = true; 230 | } 231 | 232 | return managed; 233 | } 234 | 235 | bool scroll_callback(double xOffset, double yOffset) override { 236 | scene.trackball_camera.scale((float)yOffset); 237 | return true; 238 | } 239 | }; 240 | 241 | 242 | 243 | int main(int argc, char** argv){ 244 | 245 | CustomizeWindow window(argv,500,500); 246 | window.run(); 247 | 248 | return 0; 249 | } 250 | --------------------------------------------------------------------------------