├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── main.cpp ├── misc ├── fragment_shader ├── fragment_shader_plane ├── fragment_shader_wire ├── interface.png ├── vertex_shader ├── vertex_shader_contour └── vertex_shader_plane ├── mouseBrain.off ├── utils ├── Quaternion.cpp ├── Quaternion.h ├── Shader.cpp ├── Shader.h ├── WriteBMP.cpp └── WriteBMP.h └── version.txt /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | nanogui/* 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nanogui"] 2 | path = nanogui 3 | url = https://github.com/wjakob/nanogui 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8.12) 2 | 3 | project(mesh_slicer) 4 | 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 6 | 7 | # Disable building extras we won't need (pure C++ project) 8 | set(NANOGUI_BUILD_EXAMPLE OFF CACHE BOOL " " FORCE) 9 | set(NANOGUI_BUILD_PYTHON OFF CACHE BOOL " " FORCE) 10 | set(NANOGUI_INSTALL OFF CACHE BOOL " " FORCE) 11 | 12 | # Add the configurations from nanogui 13 | add_subdirectory(./nanogui) 14 | 15 | # Various preprocessor definitions have been generated by NanoGUI 16 | add_definitions(${NANOGUI_EXTRA_DEFS}) 17 | 18 | # On top of adding the path to nanogui/include, you may need extras 19 | include_directories(${NANOGUI_EXTRA_INCS}) 20 | include_directories(./nanogui/include) 21 | 22 | 23 | # Compile a target using NanoGUI 24 | add_executable(mesh_slicer main.cpp ./utils/Shader.cpp ./utils/WriteBMP.cpp ./utils/Quaternion.cpp) 25 | 26 | target_link_libraries(mesh_slicer nanogui GLEW ${NANOGUI_EXTRA_LIBS}) 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mesh Slicer 2 | ### Journal of Neuroscience Methods 2018 [[Paper]](https://arxiv.org/abs/1712.09684) [[Project Page]](https://www.ics.uci.edu/~agarwal/mouseBrain/index.html) 3 | ![](./misc/interface.png) 4 | 5 | A simple user interface with which one can slice any mesh (with single or multiple components) in arbitarary directions and get either a rasterized image of the resultant contour or a mesh file (PLY) containing the vertices and edges of the contour. 6 | 7 | ## Getting Started 8 | 9 | The below instructions will help you install the mesh slicer on your local linux machine and give details on the user interface. 10 | 11 | ### Prerequisites 12 | 13 | On Ubuntu/Debian, make sure you have installed the following packages 14 | 15 | ``` 16 | sudo apt-get install cmake xorg-dev libglu1-mesa-dev python-dev libglew-dev 17 | ``` 18 | 19 | Also install [libigl](https://github.com/libigl/libigl) and make sure its in your path. Its a header only library used to import/export mesh files 20 | 21 | 22 | ### Compiling 23 | 24 | Clone the repository and all the dependencies 25 | 26 | ``` 27 | git clone --recursive-submodules https://github.com/nitinagarwal/mesh_slicer.git 28 | mkdir build 29 | cd build 30 | cmake .. 31 | make 32 | ``` 33 | 34 | After successful installation, you can import any meshfile with [OFF](http://segeval.cs.princeton.edu/public/off_format.html) format. For example: 35 | 36 | ``` 37 | ./mesh_slicer ../mouseBrain.off ./output_directory 38 | ``` 39 | 40 | where: 41 | * the first argument is the mesh file 42 | * the second argument is the output directory for contour mesh or image file(s). 43 | 44 | ## User Interface 45 | 46 | 1. The orientation of the cutting plane is given by its normal vector in x, y and z coordinates. 47 | 2. The position of the cutting plane is given by the position of its center in x, y, z coordinates. 48 | 3. The user can specify either the number of slices they want or the inter-slice distance (same units as the input mesh). 49 | 4. To assist the user in visualizing what output files would be generated, they can slice the mesh in the UI to see 50 | an example section. After which they can reload the mesh to slice at a different orientation. 51 | 5. The output after slicing could either be a [.PLY](http://paulbourke.net/dataformats/ply/) file with vertices of the contour or rasterized section images in bmp format. 52 | 6. If the vertices of the input mesh do not have color, by default they will be assinged an RGB color of [255,0,255] 53 | 54 | To know more about mesh slicer press `H` inside the UI. 55 | 56 | ## Citation 57 | If you use the code/data, please cite the following paper: 58 | 59 | ``` 60 | @article{agarwal2017mouse, 61 | author = {Nitin Agarwal, Xiangmin Xu, Gopi Meenakshisundaram}, 62 | title = {Geometry Processing of Conventionally Produced Mouse Brain Slice Images}, 63 | journal = {Journal of Neuroscience Methods}, 64 | year = {2018} 65 | } 66 | ``` 67 | 68 | ## License 69 | 70 | Feel free to use the code for your own research or project. However keep in mind that this is a research code, thus 71 | it is NOT clean and might have unncessary variables and wierd inter-dependencies. 72 | 73 | 74 | ## Contact 75 | 76 | Mesh Slicer was created by [Nitin Agarwal](http://www.ics.uci.edu/~agarwal/) to help neuroscience researchers to 77 | generate 2D atlas images by slicing a Virtual 3D Mouse Brain Atlas Model in any arbitarary direction. 78 | 79 | 80 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #if defined(NANOGUI_GLAD) 2 | #if defined(NANOGUI_SHARED) && !defined(GLAD_GLAPI_EXPORT) 3 | #define GLAD_GLAPI_EXPORT #endif 4 | #include 5 | #else 6 | #if defined(__APPLE__) 7 | #define GLFW_INCLUDE_GLCOREARB 8 | #else 9 | #define GL_GLEXT_PROTOTYPES 10 | #endif 11 | #endif 12 | #endif 13 | 14 | #define GLEW_STATIC 15 | #include /* always before GLFW */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | #include "./utils/Shader.h" 30 | #include "./utils/WriteBMP.h" 31 | #include "./utils/Quaternion.h" 32 | using std::cout; 33 | using std::endl; 34 | using namespace nanogui; 35 | 36 | /* mesh input */ 37 | Eigen::MatrixXd inputV1; 38 | Eigen::MatrixXi inputF1; 39 | Eigen::MatrixXd inputC1; 40 | const char* mesh_pathname; 41 | std::string storage_pathname; 42 | 43 | Screen *screen = NULL; 44 | GLFWwindow* window = NULL; 45 | 46 | // settings 47 | const unsigned int SCR_WIDTH = 1500; 48 | const unsigned int SCR_HEIGHT = 1000; 49 | 50 | /* camera */ 51 | glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f); 52 | glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f); 53 | glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); 54 | 55 | bool firstMouse = true; 56 | float yaw = -90.0f; // yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction vector pointing to the right so we initially rotate a bit to the left. 57 | float pitch = 0.0f; 58 | float lastX = 1500.0f / 2.0; 59 | float lastY = 1000.0 / 2.0; 60 | float fov = 45.0f; 61 | 62 | // timing 63 | float deltaTime = 0.0f; // time between current frame and last frame 64 | float lastFrame = 0.0f; 65 | 66 | //Default Values of Variables 67 | double normal_x = 1.0; 68 | double normal_y = 0.0; 69 | double normal_z = 0.0; 70 | Eigen::Vector3d old_normal; 71 | Eigen::Vector3d new_normal; 72 | Eigen::Vector3d rot_axis; 73 | GLfloat rot_angle = 0.0f; 74 | int num_slices = 0; 75 | double dis_slices = 100.0; 76 | double plane_xcord = 0; // default transations 77 | double plane_ycord = 0; 78 | double plane_zcord = 0; 79 | double plane_offset = 0; 80 | bool contour = false; 81 | bool image = false; 82 | bool color= false; 83 | 84 | /* Cutting Plane coorinate */ 85 | double max_value,min_value; 86 | double plane[] = { // default plane 87 | -0.01f, -1.0f, 1.0f, 88 | -0.01f, -1.0f, -1.0f, 89 | -0.01f, 1.0f, 1.0f, 90 | -0.01f, 1.0f, -1.0f 91 | }; 92 | double temp_plane[12]; // creating a copy for modification 93 | unsigned int planeind[] = { 94 | 0, 1, 2, 95 | 1, 3, 2 96 | }; 97 | 98 | //Default drawing bool values 99 | bool draw_mesh = true; // start with true; 100 | bool draw_wire = false; 101 | bool draw_plane = false; 102 | bool draw_contour = false; 103 | bool allow_cursor_movement = false; 104 | 105 | /* for input mesh */ 106 | struct Vertex { 107 | glm::vec3 Position; // Position 108 | glm::vec3 vertexColor; // Vertex color 109 | }; 110 | 111 | /* contour */ 112 | std::vector outputVertices; 113 | std::vector outputEdges; 114 | std::vector outputColor; 115 | std::vector slices; 116 | 117 | unsigned int VBO[3], VAO[3], EBO[2]; // buffers 118 | 119 | // function declarations 120 | void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); 121 | void glfw_cursor_callback(GLFWwindow* window, double xpos, double ypos); 122 | void glfw_drop_callback(GLFWwindow* window, int count, const char **filenames); 123 | void glfw_char_callback(GLFWwindow* window, unsigned int codepoint); 124 | void glfw_scroll_callback(GLFWwindow* window, double xoffset, double yoffset); 125 | void glfw_mouse_callback(GLFWwindow* window, int button, int action, int modifiers); 126 | void framebuffer_size_callback(GLFWwindow* window, int width, int height); 127 | bool point_onPlane(Eigen::Vector3d &vec1,Eigen::Vector3d &nomPL, Eigen::Vector3d &ptPL); 128 | bool plane_triangleIntersect(Eigen::Vector3d &vec1, Eigen::Vector3d &vec2, Eigen::Vector3d &vec3, Eigen::Vector3d &nomPL, Eigen::Vector3d &ptPL); 129 | std::vector plane_edgeIntersect(Eigen::Vector3d &vec1, Eigen::Vector3d &vec2, Eigen::Vector3d &vec3, Eigen::Vector3d &nomPL, Eigen::Vector3d &ptPL); 130 | void compute_intersection(Eigen::MatrixXd &mesh_ver, Eigen::MatrixXi &mesh_fac, Eigen::MatrixXd &mesh_col, Eigen::Vector3d &normal_PL, Eigen::Vector3d &point_PL); 131 | void writePLY(std::vector VERTICES, std::vector COLOR, std::vector EDGES,std::string directory); 132 | std::vector rayBB_intersection(Eigen::Vector3d &min_BB, Eigen::Vector3d &max_BB, Eigen::Vector3d &ray_direction); 133 | void updatePlane(); 134 | void updatePoints(std::vector &outputVertices, Eigen::Vector4d &size); 135 | void updateBuffer(); 136 | 137 | 138 | int main(int argc, char ** argv){ 139 | 140 | if(argc != 3){ 141 | cout << "Not enough arguments" << endl; 142 | cout << "Usage: mesh_slicer [mesh_file_name.off] [storage_directory_location]" << endl; 143 | return 1; 144 | }else{ 145 | 146 | mesh_pathname = argv[1]; 147 | storage_pathname = argv[2]; 148 | } 149 | 150 | /* ------------------------------Mesh Data to Load------------------------------------------------------ */ 151 | igl::readOFF(mesh_pathname,inputV1, inputF1, inputC1); 152 | 153 | cout << " INPUT =====================================" << endl; 154 | cout << " mesh_dir = " << mesh_pathname << endl; 155 | cout << " storage_dir = " << storage_pathname << endl; 156 | cout << " #Vertices in input_mesh = " << inputV1.rows() << endl; 157 | cout << " #Faces in input_mesh = " << inputF1.rows() << endl; 158 | 159 | if (inputC1.rows() > 0){ 160 | cout << " Vertices of input_mesh have color " << endl; 161 | color = true; 162 | }else{ 163 | cout << " Vertices of input_mesh have NO color " << endl; 164 | } 165 | cout << " =====================================" << endl; 166 | 167 | std::vector vertices; 168 | std::vector indices; 169 | 170 | min_value = inputV1.minCoeff(); // compute the overall min as we dont want to uniformaly scale the mesh down 171 | max_value = inputV1.maxCoeff(); 172 | 173 | /* Walk through each of the mesh's vertices */ 174 | for(GLuint i = 0; i < inputV1.rows(); i++) 175 | { 176 | Vertex vertex; 177 | glm::vec3 vec; 178 | 179 | // Positions --- Normalize each vertex 180 | vec.x = 2 * (( inputV1(i,0) - min_value)/(max_value-min_value)) -1; 181 | vec.y = 2 * (( inputV1(i,1) - min_value)/(max_value-min_value)) -1; 182 | vec.z = 2 * (( inputV1(i,2) - min_value)/(max_value-min_value)) -1; 183 | vertex.Position = vec; 184 | 185 | if( color == true ){ 186 | // VertexColor 187 | vec.x = inputC1(i,0); 188 | vec.y = inputC1(i,1); 189 | vec.z = inputC1(i,2); 190 | vertex.vertexColor = vec; 191 | }else{ 192 | 193 | vertex.vertexColor = glm::vec3(1.0f, 0.0f, 1.0f ); // magenta color mesh 194 | } 195 | vertices.push_back(vertex); 196 | } 197 | // Walk through each of the mesh's faces 198 | for(GLuint i = 0; i < inputF1.rows(); i++) 199 | { 200 | indices.push_back(inputF1(i,0)); 201 | indices.push_back(inputF1(i,1)); 202 | indices.push_back(inputF1(i,2)); 203 | } 204 | // initialize temp_plane 205 | for (int i = 0; i < 12; ++i) { 206 | temp_plane[i]=plane[i]; 207 | } 208 | 209 | /* initialize GLFW context */ 210 | glfwInit(); 211 | 212 | glfwSetTime(0); 213 | 214 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 215 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 216 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); /* Needed to work on mac */ 217 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 218 | 219 | glfwWindowHint(GLFW_SAMPLES, 0); 220 | glfwWindowHint(GLFW_RED_BITS, 8); 221 | glfwWindowHint(GLFW_GREEN_BITS, 8); 222 | glfwWindowHint(GLFW_BLUE_BITS, 8); 223 | glfwWindowHint(GLFW_ALPHA_BITS, 8); 224 | glfwWindowHint(GLFW_STENCIL_BITS, 8); 225 | glfwWindowHint(GLFW_DEPTH_BITS, 24); 226 | glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); 227 | 228 | // Create a GLFWwindow object 229 | window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Mesh Slicer", NULL, NULL); 230 | if (window == NULL) { 231 | std::cout << "Failed to create GLFW window" << std::endl; 232 | glfwTerminate(); 233 | return -1; 234 | } 235 | glfwMakeContextCurrent(window); 236 | 237 | /* intializing GLEW */ 238 | glewExperimental = GL_TRUE; 239 | if (glewInit() != GLEW_OK) 240 | { 241 | std::cout << "Failed to initialize GLEW" << std::endl; 242 | return -1; 243 | } 244 | 245 | #if defined(NANOGUI_GLAD) 246 | if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) 247 | throw std::runtime_error("Could not initialize GLAD!"); 248 | glGetError(); // pull and ignore unhandled errors like GL_INVALID_ENUM 249 | #endif 250 | 251 | /* capture mouse */ 252 | /* glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); */ 253 | 254 | /* ------------------Nano Gui Setting---------------------------------------------------------------- */ 255 | // Create a nanogui screen and pass the glfw pointer to initialize 256 | screen = new Screen(); 257 | screen->initialize(window, true); 258 | 259 | // Create nanogui gui 260 | FormHelper *gui = new FormHelper(screen); 261 | gui->setFixedSize(Eigen::Vector2i(60,20)); 262 | ref nanoguiWindow = gui->addWindow(Eigen::Vector2i(1,1), "Mesh Slicer"); 263 | 264 | gui->addGroup("Input (Plane Settings)"); 265 | gui->addVariable("Normal Vector (x-dir)", normal_x)->setSpinnable(true); 266 | gui->addVariable("Normal Vector (y-dir)", normal_y)->setSpinnable(true); 267 | gui->addVariable("Normal Vector (z-dir)", normal_z)->setSpinnable(true); 268 | 269 | gui->addVariable("Position (xcord)", plane_xcord)->setSpinnable(true); 270 | gui->addVariable("Position (ycord)", plane_ycord)->setSpinnable(true); 271 | gui->addVariable("Position (zcord)", plane_zcord)->setSpinnable(true); 272 | /* gui->addVariable("Offset", plane_offset)->setSpinnable(true); */ 273 | gui->addVariable("# of Slices", num_slices)->setTooltip("Number of slices. Either specify the number of slices or the interslice distance."); 274 | gui->addVariable("Inter-Slice Distance", dis_slices)->setTooltip("Interslice Distance. Either specify the interslice distance or the number of slics."); 275 | 276 | 277 | gui->addGroup("Output"); 278 | gui->addVariable("Contour", [&](bool c_temp){ 279 | contour = c_temp; 280 | },[&](){ 281 | return false; 282 | })->setTooltip("Output is a .PLY file with a list of vertices and edges of the intersecting contours."); 283 | 284 | gui->addVariable("Image", [&](bool c_img){ 285 | image = c_img; 286 | },[&](){ 287 | return false; 288 | })->setTooltip("Output is a rasterized image of the intersecting contours"); 289 | 290 | gui->addButton("Generate", []() { 291 | 292 | double intersectDistance, inter_sliceDist; 293 | int number_slices; 294 | 295 | if(contour && image){ 296 | cout << "ERROR:------Choose Only One Output--------" << endl; 297 | }else if(!contour && !image){ 298 | cout << "ERROR:------Choose At Least One Output--------" << endl; 299 | }else if(num_slices ==0 && dis_slices ==0){ 300 | cout << "ERROR:------BOTH # of slices and distance between slices can not be ZERO--------" << endl; 301 | }else if(num_slices !=0 && dis_slices != 0){ 302 | cout << "ERROR:------BOTH # of slices and distance between slices can not be NONZERO--------" << endl; 303 | }else if(num_slices == 1){ 304 | cout << "ERROR:------# of slices should be greater than 1--------" << endl; 305 | }else{ 306 | 307 | /* check which two pts does the ray (pt with new normal) intersect with the BB */ 308 | /* can be done using the min and max of the Bounding box only */ 309 | Eigen::Vector3d BB_min, BB_max; 310 | BB_min = inputV1.colwise().minCoeff(); 311 | BB_max = inputV1.colwise().maxCoeff(); 312 | 313 | BB_min = BB_min.array() + 5; //slightly shrinking the bounding box to allow intersection 314 | BB_max = BB_max.array() - 5; 315 | std::vector intersectPts; 316 | Eigen::Vector3d new_point; 317 | 318 | intersectPts = rayBB_intersection(BB_min, BB_max, new_normal); 319 | 320 | intersectDistance = sqrt( pow(intersectPts.at(0)(0)-intersectPts.at(1)(0),2) + pow(intersectPts.at(0)(1)-intersectPts.at(1)(1),2) + pow(intersectPts.at(0)(2)-intersectPts.at(1)(2),2) ); // distance 321 | 322 | if(num_slices ==0){ 323 | inter_sliceDist = dis_slices/4; // the ratio 100um -> 25um 324 | number_slices = intersectDistance / inter_sliceDist; 325 | }else{ 326 | number_slices = num_slices; 327 | inter_sliceDist = intersectDistance / (number_slices-1); 328 | } 329 | 330 | if(contour){ 331 | cout << "Generating contours----" << endl; 332 | }else{ 333 | cout << "Generating images-----" << endl; 334 | } 335 | 336 | for(int i=0; i0){ // there might be no vertices at all from the intersection 342 | 343 | cout << "contour # " << i+1 << endl; 344 | if(contour){ 345 | 346 | writePLY(outputVertices, outputColor, outputEdges, storage_pathname + "/" + std::to_string(i+1)+".ply"); 347 | 348 | }else{ 349 | 350 | Eigen::Vector4d sz; 351 | updatePoints(outputVertices,sz); // aligning the vertices to z axis for writing to bmp 352 | WriteBMP(ceil(sz(0))+50, ceil(sz(1))+50, outputVertices, outputColor, outputEdges, storage_pathname + "/" + std::to_string(i+1)+".bmp"); 353 | } 354 | 355 | } 356 | 357 | outputVertices.clear(); // clearing the old values 358 | outputColor.clear(); 359 | outputEdges.clear(); 360 | } 361 | cout << "Done----" << endl; 362 | } 363 | }); 364 | 365 | gui->addGroup("GUI"); 366 | gui->addButton("Slice the Mesh", []() { 367 | outputVertices.clear(); // clearing the old values 368 | outputColor.clear(); 369 | outputEdges.clear(); 370 | slices.clear(); 371 | 372 | updatePlane(); // update the location of the plane 373 | 374 | Eigen::Vector3d PT; 375 | PT << temp_plane[0], temp_plane[1], temp_plane[2]; // take any point on the plane 376 | PT = ((PT.array()+1)*(max_value-min_value))/2 + min_value; // renormalize 377 | 378 | // slice the mesh 379 | compute_intersection(inputV1, inputF1, inputC1, new_normal,PT); 380 | 381 | /* Walk through each of the contour's vertices */ 382 | for(GLuint i = 0; i < outputVertices.size(); i++) 383 | { 384 | Vertex v1; 385 | glm::vec3 v2; 386 | // Positions --- Normalize each vertex 387 | v2.x = 2 * (( outputVertices.at(i)(0) - min_value)/(max_value-min_value)) -1; 388 | v2.y = 2 * (( outputVertices.at(i)(1) - min_value)/(max_value-min_value)) -1; 389 | v2.z = 2 * (( outputVertices.at(i)(2) - min_value)/(max_value-min_value)) -1; 390 | v1.Position = v2; 391 | 392 | if( color == true ){ 393 | // VertexColor 394 | v2.x = outputColor.at(i)(0); 395 | v2.y = outputColor.at(i)(1); 396 | v2.z = outputColor.at(i)(2); 397 | v1.vertexColor = v2; 398 | }else{ 399 | v1.vertexColor = glm::vec3(1.0f, 0.0f, 1.0f ); // magenta color mesh 400 | } 401 | slices.push_back(v1); 402 | } 403 | 404 | updateBuffer(); 405 | 406 | //draw contour 407 | draw_contour = true; 408 | draw_wire = false; 409 | draw_mesh = false; 410 | draw_plane = false; 411 | cout << "Slice pressed." << endl; 412 | }); 413 | 414 | gui->addButton("Reload Mesh", []() { 415 | draw_mesh = true; 416 | draw_wire = true; 417 | draw_plane = false; 418 | draw_contour = false; 419 | /* rot_axis = old_normal; // resetting the plane orientation */ 420 | cout << "Mesh Reloaded" << endl; 421 | }); 422 | 423 | screen->setVisible(true); 424 | screen->performLayout(); 425 | /* nanoguiWindow->center(); */ 426 | 427 | /* ------------------Call back Functions---------------------------------------------------------------- */ 428 | 429 | glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 430 | 431 | glfwSetCursorPosCallback(window, glfw_cursor_callback); 432 | glfwSetScrollCallback(window, glfw_scroll_callback); 433 | glfwSetMouseButtonCallback(window, glfw_mouse_callback); 434 | glfwSetKeyCallback(window, glfw_key_callback); 435 | glfwSetCharCallback(window, glfw_char_callback); 436 | glfwSetDropCallback(window, glfw_drop_callback); 437 | 438 | /* ------------------OPENGL shader configurations ------------------------------------------------------ */ 439 | 440 | /* Build and compile the shader program */ 441 | Shader ourProgram("../misc/vertex_shader","../misc/fragment_shader"); 442 | Shader Wireframe("../misc/vertex_shader","../misc/fragment_shader_wire"); 443 | Shader PlaneShader("../misc/vertex_shader_plane","../misc/fragment_shader_plane"); 444 | Shader ContourShader("../misc/vertex_shader_contour","../misc/fragment_shader"); 445 | 446 | /* ------------------------------Init VAO, VBO, EBO----------------------------------------------------- */ 447 | 448 | glGenVertexArrays(3, VAO); 449 | glGenBuffers(3, VBO); 450 | glGenBuffers(2, EBO); 451 | 452 | glBindVertexArray(VAO[0]); 453 | 454 | glBindBuffer(GL_ARRAY_BUFFER, VBO[0]); 455 | glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex) , &vertices[0], GL_STATIC_DRAW); 456 | 457 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[0]); 458 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW); 459 | 460 | /* position attribute */ 461 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0); 462 | glEnableVertexAttribArray(0); 463 | 464 | /* color attribute */ 465 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex,vertexColor)); 466 | glEnableVertexAttribArray(1); 467 | 468 | glBindBuffer(GL_ARRAY_BUFFER, 0); 469 | 470 | /* Unbinding VAO so that other VAO can be initialized */ 471 | glBindVertexArray(0); 472 | 473 | /* ----------------second object - plane---------------------------------------------------- */ 474 | 475 | glBindVertexArray(VAO[1]); 476 | 477 | glBindBuffer(GL_ARRAY_BUFFER, VBO[1]); 478 | glBufferData(GL_ARRAY_BUFFER, sizeof(plane), plane, GL_STATIC_DRAW); 479 | 480 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[1]); 481 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(planeind), planeind, GL_STATIC_DRAW); 482 | 483 | /* position attribute */ 484 | glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, 3 * sizeof(double), (GLvoid*)0); 485 | glEnableVertexAttribArray(0); 486 | 487 | glBindBuffer(GL_ARRAY_BUFFER, 0); 488 | 489 | glBindVertexArray(0); 490 | 491 | old_normal << normal_x, normal_y, normal_z; 492 | rot_axis = old_normal; 493 | 494 | /* ------------------------------Game Loop-------------------------------------------------------------- */ 495 | while (!glfwWindowShouldClose(window)) { 496 | 497 | /* Getting the time for camera movement */ 498 | float currentFrame = glfwGetTime(); 499 | deltaTime = currentFrame - lastFrame; 500 | lastFrame = currentFrame; 501 | 502 | glEnable(GL_DEPTH_TEST); 503 | glEnable(GL_PROGRAM_POINT_SIZE); // can change the pt size 504 | 505 | /* event handling */ 506 | glfwPollEvents(); 507 | 508 | glClearColor(0.2f, 0.25f, 0.3f, 1.0f); 509 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 510 | 511 | /* computing the angle of rotation for plane */ 512 | new_normal<< normal_x, normal_y, normal_z; 513 | new_normal.normalize(); 514 | old_normal.normalize(); 515 | if(old_normal.dot(new_normal)<=1 && old_normal.dot(new_normal)>=-1 && old_normal != new_normal){ 516 | rot_angle = acos(old_normal.dot(new_normal)); 517 | rot_axis = old_normal.cross(new_normal); 518 | }else if(old_normal==new_normal){ 519 | rot_angle = 0; 520 | rot_axis = old_normal; 521 | } 522 | 523 | /* Camera View and Model Transformation */ 524 | glm::mat4 view; 525 | view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); 526 | 527 | glm::mat4 model(1.0f); // not changing the model 528 | model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); 529 | 530 | glm::mat4 projection; 531 | projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 10.0f); 532 | 533 | if(draw_contour){ 534 | // draw the sliced mesh. i.e contour 535 | ContourShader.Use(); /* glUseProgram(shaderProgram); */ 536 | ContourShader.setMat4("view", view); 537 | ContourShader.setMat4("model", model); 538 | ContourShader.setMat4("projection", projection); 539 | 540 | glBindVertexArray(VAO[2]); 541 | glDrawArrays(GL_POINTS,0, slices.size()); 542 | glBindVertexArray(0); 543 | } 544 | 545 | if(draw_plane){ 546 | /* plane overlay */ 547 | PlaneShader.Use(); 548 | glm::mat4 model_plane; 549 | model_plane = glm::translate(model_plane, glm::vec3(plane_xcord,plane_ycord,plane_zcord)); 550 | model_plane = glm::rotate(model_plane, rot_angle , glm::vec3(rot_axis(0),rot_axis(1),rot_axis(2))); 551 | PlaneShader.setMat4("model", model_plane); 552 | PlaneShader.setMat4("projection", projection); 553 | PlaneShader.setMat4("view", view); 554 | 555 | glBindVertexArray(VAO[1]); 556 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 557 | glBindVertexArray(0); // unbinding 558 | } 559 | 560 | if(draw_mesh){ 561 | /* drawing the mesh */ 562 | ourProgram.Use(); /* glUseProgram(shaderProgram); */ 563 | ourProgram.setMat4("view", view); 564 | ourProgram.setMat4("model", model); 565 | ourProgram.setMat4("projection", projection); 566 | 567 | glBindVertexArray(VAO[0]); 568 | glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); 569 | glBindVertexArray(0); 570 | } 571 | 572 | if(draw_wire){ 573 | /* overlay wireframe */ 574 | Wireframe.Use(); 575 | Wireframe.setMat4("view", view); 576 | Wireframe.setMat4("model", model); 577 | Wireframe.setMat4("projection", projection); 578 | 579 | glBindVertexArray(VAO[0]); 580 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 581 | glEnable(GL_POLYGON_OFFSET_LINE); 582 | glPolygonOffset(-1,-1); 583 | glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); 584 | glDisable(GL_POLYGON_OFFSET_LINE); 585 | glBindVertexArray(0); 586 | } 587 | 588 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 589 | // Draw nanogui 590 | screen->drawContents(); 591 | screen->drawWidgets(); 592 | 593 | glfwSwapBuffers(window); 594 | } 595 | // Terminate GLFW, clearing any resources allocated by GLFW. 596 | glfwTerminate(); 597 | return 0; 598 | } 599 | 600 | 601 | void updateBuffer(){ 602 | 603 | glBindVertexArray(VAO[2]); 604 | 605 | glBindBuffer(GL_ARRAY_BUFFER, VBO[2]); 606 | glBufferData(GL_ARRAY_BUFFER, slices.size() * sizeof(Vertex) , &slices[0], GL_DYNAMIC_DRAW); 607 | 608 | /* position attribute */ 609 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0); 610 | glEnableVertexAttribArray(0); 611 | 612 | /* color attribute */ 613 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex,vertexColor)); 614 | glEnableVertexAttribArray(1); 615 | 616 | glBindBuffer(GL_ARRAY_BUFFER, 0); 617 | 618 | glBindVertexArray(0); 619 | } 620 | 621 | void updatePlane(){ 622 | 623 | Eigen::Matrix pt_plane; 624 | Eigen::Matrix PL; 625 | PL << plane[0], plane[1], plane[2], 1, 626 | plane[3], plane[4], plane[5], 1, 627 | plane[6], plane[7], plane[8], 1, 628 | plane[9], plane[10], plane[11], 1; 629 | 630 | Eigen::Matrix Tran; 631 | Eigen::Matrix Tran2; 632 | Eigen::Matrix R; 633 | 634 | Tran.setIdentity(4,4); 635 | R.setIdentity(4,4); 636 | Tran2.setIdentity(4,4); 637 | 638 | Quaternion quat_plane(rot_axis, rot_angle); 639 | R = quat_plane.Quat_to_Rotmatrix(); 640 | 641 | double xcenter = (PL(0,0) + PL(3,0))/2; 642 | double ycenter = (PL(0,1) + PL(3,1))/2; 643 | double zcenter = (PL(0,2) + PL(3,2))/2; 644 | 645 | Tran.col(3) << xcenter, ycenter, zcenter, 1; 646 | Tran2.col(3) << plane_xcord, plane_ycord, plane_zcord; 647 | 648 | pt_plane = Tran2 * Tran * R * Tran.inverse() * PL.transpose(); 649 | pt_plane.transposeInPlace(); 650 | 651 | /* update the plane */ 652 | temp_plane[0] = pt_plane(0,0); 653 | temp_plane[1] =pt_plane(0,1); 654 | temp_plane[2] = pt_plane(0,2); 655 | 656 | temp_plane[3] =pt_plane(1,0); 657 | temp_plane[4] =pt_plane(1,1); 658 | temp_plane[5]= pt_plane(1,2); 659 | 660 | temp_plane[6]= pt_plane(2,0); 661 | temp_plane[7]= pt_plane(2,1); 662 | temp_plane[8]= pt_plane(2,2); 663 | 664 | temp_plane[9] = pt_plane(3,0); 665 | temp_plane[10] = pt_plane(3,1); 666 | temp_plane[11] = pt_plane(3,2); 667 | } 668 | 669 | 670 | void compute_intersection(Eigen::MatrixXd &mesh_ver, Eigen::MatrixXi &mesh_fac, Eigen::MatrixXd &mesh_col, Eigen::Vector3d &normal_PL, Eigen::Vector3d &point_PL){ 671 | 672 | std::vector vertices_temp; 673 | 674 | int j=0; // counter for vertices and color 675 | int k=0; // counter for the edges 676 | 677 | /* check whether triangles intersect the plane or not */ 678 | for (int i = 0; i < mesh_fac.rows(); i++) { 679 | 680 | Eigen::Vector3d vec1 = mesh_ver.row(mesh_fac(i,0)); // vertices of triangle faces 681 | Eigen::Vector3d vec2 = mesh_ver.row(mesh_fac(i,1)); 682 | Eigen::Vector3d vec3 = mesh_ver.row(mesh_fac(i,2)); 683 | 684 | if( plane_triangleIntersect(vec1,vec2,vec3,normal_PL,point_PL) ){ 685 | /* there is an intersection */ 686 | 687 | vertices_temp = plane_edgeIntersect(vec1, vec2, vec3, normal_PL, point_PL); 688 | /* vertices_temp.rows() ==2 or 3 cant be anything else*/ 689 | 690 | if(vertices_temp.size() ==3){ 691 | 692 | outputVertices.push_back(vertices_temp.at(0)); 693 | outputVertices.push_back(vertices_temp.at(1)); 694 | outputVertices.push_back(vertices_temp.at(2)); 695 | 696 | Eigen::Vector2d t(j, j+1); 697 | outputEdges.push_back(t); 698 | t << j+1,j+2; 699 | outputEdges.push_back(t); 700 | t << j+2,j; 701 | outputEdges.push_back(t); 702 | 703 | outputColor.push_back(mesh_col.row(mesh_fac(i,0))); 704 | outputColor.push_back(mesh_col.row(mesh_fac(i,0))); 705 | outputColor.push_back(mesh_col.row(mesh_fac(i,0))); 706 | 707 | j = j+3; 708 | k = k+3; 709 | }else if( vertices_temp.size() == 2 ){ 710 | 711 | outputVertices.push_back(vertices_temp.at(0)); 712 | outputVertices.push_back(vertices_temp.at(1)); 713 | 714 | Eigen::Vector2d t(j, j+1); 715 | outputEdges.push_back(t); 716 | 717 | outputColor.push_back(mesh_col.row(mesh_fac(i,0))); 718 | outputColor.push_back(mesh_col.row(mesh_fac(i,0))); 719 | 720 | j = j+2; 721 | k = k+1; 722 | } 723 | } 724 | } 725 | } 726 | 727 | std::vector plane_edgeIntersect(Eigen::Vector3d &vec1, Eigen::Vector3d &vec2, Eigen::Vector3d &vec3, Eigen::Vector3d &nomPL, Eigen::Vector3d &ptPL){ 728 | 729 | /* compute the intersection of line segments (traiangle edges) with plane */ 730 | std::vector intersects; 731 | 732 | /* all three pts lie on plane */ 733 | if (point_onPlane(vec1,nomPL,ptPL) && point_onPlane(vec2,nomPL,ptPL) && point_onPlane(vec3,nomPL,ptPL) ){ 734 | 735 | intersects.push_back(vec1); 736 | intersects.push_back(vec2); 737 | intersects.push_back(vec3); 738 | return intersects; 739 | } 740 | 741 | Eigen::Matrix vertices; /* new matrix for looping those the edges */ 742 | vertices.row(0) = vec1; 743 | vertices.row(1) = vec2; 744 | vertices.row(2) = vec3; 745 | vertices.row(3) = vec1; 746 | 747 | for (int i = 0; i < 3; i++) { 748 | 749 | Eigen::Vector3d v1 = vertices.row(i); 750 | Eigen::Vector3d v2 = vertices.row(i+1); 751 | 752 | if(nomPL.dot(v2-v1) == 0){ 753 | /* edge can be on plane or parallel. checking if edge on plane(both points lie on plane)*/ 754 | if( point_onPlane(v2,nomPL,ptPL) && point_onPlane(v1,nomPL,ptPL) ){ 755 | intersects.push_back(vec1); 756 | intersects.push_back(vec2); 757 | return intersects; 758 | } 759 | }else{ 760 | 761 | double r =(nomPL.dot(ptPL-v1)) / (nomPL.dot(v2-v1)); 762 | if( r >= 0 & r <=1 ){ 763 | 764 | Eigen::Vector3d pt = v1 + r*(v2-v1); 765 | intersects.push_back(pt); 766 | } 767 | } 768 | } 769 | /* checking if the vertices are duplicate. could be if the triangle is only touching one vertex */ 770 | if (intersects.size() == 2 && intersects[0] == intersects[1] ){ 771 | intersects.clear(); 772 | } 773 | return intersects; 774 | } 775 | 776 | bool plane_triangleIntersect(Eigen::Vector3d &vec1, Eigen::Vector3d &vec2, Eigen::Vector3d &vec3, Eigen::Vector3d &nomPL, Eigen::Vector3d &ptPL){ 777 | 778 | /* check whether the triangle cross the plane or not */ 779 | double a = (vec1 - ptPL).dot(nomPL); 780 | double b = (vec2 - ptPL).dot(nomPL); 781 | double c = (vec3 - ptPL).dot(nomPL); 782 | 783 | if (a > 0 && b > 0 && c > 0){ // /* checking if any two scalars are diff than the other */ 784 | return false; 785 | }else if(a < 0 && b < 0 && c < 0){ 786 | return false; 787 | }else{ 788 | return true; // vertices can be on the plane as well 789 | } 790 | } 791 | 792 | bool point_onPlane(Eigen::Vector3d &vec1,Eigen::Vector3d &nomPL, Eigen::Vector3d &ptPL){ 793 | /* checking if point lie on plane. satisfy plane equ */ 794 | if ( (vec1 - ptPL).dot(nomPL) == 0 ){ 795 | return true; 796 | }else{ 797 | return false; 798 | } 799 | } 800 | 801 | void updatePoints(std::vector &outputVertices, Eigen::Vector4d &size){ 802 | /* rotating the points to make them align to z axis */ 803 | 804 | double diff_deg = acos(Eigen::RowVector3d(0,0,1).dot(new_normal)); // align to z axis 805 | Eigen::RowVector3d axis = new_normal.cross(Eigen::RowVector3d(0,0,1)); 806 | 807 | Eigen::MatrixXd inputV,outputV; 808 | Eigen::Matrix T; 809 | Eigen::Matrix R; 810 | T.setIdentity(); 811 | 812 | inputV.resize(outputVertices.size(),4); 813 | outputV.resize(outputVertices.size(),4); 814 | 815 | for(int i=0; i rayBB_intersection(Eigen::Vector3d &min_BB, Eigen::Vector3d &max_BB, Eigen::Vector3d &ray_direction){ 851 | /* copmuting the intersection of the new normal with the BB */ 852 | 853 | std::vector BB_intersects; 854 | Eigen::Vector3d temp; 855 | 856 | if (abs(ray_direction.dot(Eigen::Vector3d(1,0,0))) == 1){ // direction parallel to x axis 857 | 858 | BB_intersects.push_back(min_BB); 859 | 860 | temp << max_BB(0),min_BB(1), min_BB(2); 861 | BB_intersects.push_back(temp); 862 | 863 | }else if(abs(ray_direction.dot(Eigen::Vector3d(0,1,0))) == 1){ // direction parallel to y axis 864 | 865 | BB_intersects.push_back(min_BB); 866 | 867 | temp << min_BB(0),max_BB(1), min_BB(2); 868 | BB_intersects.push_back(temp); 869 | 870 | }else if(abs(ray_direction.dot(Eigen::Vector3d(0,0,1))) == 1){ // direction parallel to z axis 871 | 872 | BB_intersects.push_back(min_BB); 873 | 874 | temp << min_BB(0),min_BB(1), max_BB(2); 875 | BB_intersects.push_back(temp); 876 | }else{ 877 | 878 | double r; 879 | double r1 = 1000000.0; 880 | Eigen::Vector3d ray_pt1; 881 | ray_pt1 = min_BB + ray_direction; // new pt in same direction 882 | 883 | if( (Eigen::Vector3d(1,0,0).dot(ray_pt1-min_BB)) != 0 ){ 884 | 885 | r =(Eigen::Vector3d(1,0,0).dot(max_BB-min_BB)) / (Eigen::Vector3d(1,0,0).dot(ray_pt1-min_BB)); 886 | if( r1 > r ){ 887 | r1 = r; 888 | } 889 | } 890 | 891 | if( (Eigen::Vector3d(0,1,0).dot(ray_pt1-min_BB)) != 0 ){ 892 | 893 | r =(Eigen::Vector3d(0,1,0).dot(max_BB-min_BB)) / (Eigen::Vector3d(0,1,0).dot(ray_pt1-min_BB)); 894 | 895 | if( r1 > r ){ 896 | r1 = r; 897 | } 898 | } 899 | 900 | if( (Eigen::Vector3d(0,0,1).dot(ray_pt1-min_BB)) != 0 ){ 901 | 902 | r =(Eigen::Vector3d(0,0,1).dot(max_BB-min_BB)) / (Eigen::Vector3d(0,0,1).dot(ray_pt1-min_BB)); 903 | 904 | if( r1 > r ){ 905 | r1 = r; 906 | } 907 | } 908 | BB_intersects.push_back(min_BB); 909 | BB_intersects.push_back(min_BB + r1*ray_direction); 910 | } 911 | return BB_intersects; 912 | } 913 | 914 | 915 | void writePLY(std::vector VERTICES, std::vector COLOR, std::vector EDGES,std::string directory){ 916 | 917 | FILE * PLYfile = fopen(directory.c_str(),"w"); 918 | 919 | fprintf(PLYfile, "ply\n"); 920 | fprintf(PLYfile, "format ascii 1.0\n"); 921 | fprintf(PLYfile, "element vertex %d\n", (int)VERTICES.size()); 922 | fprintf(PLYfile, "property float x\n"); 923 | fprintf(PLYfile, "property float y\n"); 924 | fprintf(PLYfile, "property float z\n"); 925 | fprintf(PLYfile, "property uchar red\n"); 926 | fprintf(PLYfile, "property uchar green\n"); 927 | fprintf(PLYfile, "property uchar blue\n"); 928 | fprintf(PLYfile, "element faces %d\n", (int)EDGES.size()); 929 | fprintf(PLYfile, "property list uchar int vertex_indices\n"); 930 | fprintf(PLYfile, "end_header\n"); 931 | 932 | for(int i=0; ikeyCallbackEvent(key, scancode, action, mode); 950 | 951 | if (key == GLFW_KEY_W){ 952 | cameraPos += cameraSpeed * cameraFront; 953 | } 954 | if (key == GLFW_KEY_S) 955 | cameraPos -= cameraSpeed * cameraFront; 956 | if (key == GLFW_KEY_A) 957 | cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; 958 | if (key == GLFW_KEY_D) 959 | cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; 960 | } 961 | 962 | void glfw_cursor_callback(GLFWwindow* window, double xpos, double ypos) 963 | { 964 | screen->cursorPosCallbackEvent(xpos, ypos); 965 | 966 | if(allow_cursor_movement){ 967 | if (firstMouse) 968 | { 969 | lastX = xpos; 970 | lastY = ypos; 971 | firstMouse = false; 972 | } 973 | 974 | float xoffset = xpos - lastX; 975 | float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top 976 | lastX = xpos; 977 | lastY = ypos; 978 | 979 | float sensitivity = 0.1f; // change this value to your liking 980 | xoffset *= sensitivity; 981 | yoffset *= sensitivity; 982 | 983 | yaw += xoffset; 984 | pitch += yoffset; 985 | 986 | // make sure that when pitch is out of bounds, screen doesn't get flipped 987 | if (pitch > 89.0f) 988 | pitch = 89.0f; 989 | if (pitch < -89.0f) 990 | pitch = -89.0f; 991 | 992 | glm::vec3 front; 993 | front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); 994 | front.y = sin(glm::radians(pitch)); 995 | front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); 996 | cameraFront = glm::normalize(front); 997 | } 998 | } 999 | 1000 | void glfw_scroll_callback(GLFWwindow* window, double xoffset, double yoffset) 1001 | { 1002 | /* scroll will only work when inside the window */ 1003 | if( screen->scrollCallbackEvent(xoffset, yoffset) == false){ 1004 | 1005 | if (fov >= 1.0f && fov <= 45.0f) 1006 | fov -= yoffset; 1007 | if (fov <= 1.0f) 1008 | fov = 1.0f; 1009 | if (fov >= 45.0f) 1010 | fov = 45.0f; 1011 | } 1012 | } 1013 | 1014 | void glfw_mouse_callback(GLFWwindow* window, int button, int action, int modifiers) 1015 | { 1016 | screen->mouseButtonCallbackEvent(button, action, modifiers); 1017 | 1018 | if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS){ 1019 | allow_cursor_movement = false; 1020 | } 1021 | if(button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS){ 1022 | allow_cursor_movement = true; 1023 | } 1024 | } 1025 | 1026 | // glfw: whenever the window size changed (by OS or user resize) this callback function executes 1027 | void framebuffer_size_callback(GLFWwindow* window, int width, int height) 1028 | { 1029 | // make sure the viewport matches the new window dimensions; note that width and 1030 | // height will be significantly larger than specified on retina displays. 1031 | glViewport(0, 0, width, height); 1032 | } 1033 | 1034 | void glfw_drop_callback(GLFWwindow* window, int count, const char **filenames) 1035 | { 1036 | screen->dropCallbackEvent(count, filenames); 1037 | } 1038 | 1039 | void glfw_char_callback(GLFWwindow* window, unsigned int codepoint) 1040 | { 1041 | switch(codepoint){ 1042 | 1043 | case 'P': 1044 | case 'p': 1045 | { 1046 | draw_plane = !draw_plane; 1047 | break; 1048 | } 1049 | case 'M': 1050 | case 'm': 1051 | { 1052 | draw_wire = !draw_wire; 1053 | break; 1054 | } 1055 | case 'H': 1056 | case 'h': 1057 | { 1058 | cout << " ======================= HELP MENU ===========================" << endl; 1059 | cout << " M/m : Overlay Mesh " << endl; 1060 | cout << " P/p : Show the Slicing Plane " << endl; 1061 | cout << " Esc : Quit Application " << endl; 1062 | cout << " W/S/A/D : Move front, back, left & right " << endl; 1063 | cout << " Mouse_Right_Click : Allow Camera Movement " << endl; 1064 | cout << " Mouse_Left_Click : Stop Camera Movement " << endl; 1065 | cout << " ======================= HELP MENU ===========================" << endl; 1066 | } 1067 | default: 1068 | break; 1069 | } 1070 | screen->charCallbackEvent(codepoint); 1071 | } 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | -------------------------------------------------------------------------------- /misc/fragment_shader: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | in vec3 ourColor; 3 | 4 | out vec4 color; 5 | 6 | void main() 7 | { 8 | color = vec4(ourColor, 1.0f); 9 | } 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /misc/fragment_shader_plane: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | in vec3 ourColor; 3 | 4 | out vec4 color; 5 | 6 | void main() 7 | { 8 | color = vec4(ourColor, 1.0f); 9 | } 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /misc/fragment_shader_wire: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | in vec3 ourColor; 3 | 4 | out vec4 color; 5 | 6 | void main() 7 | { 8 | color = vec4(0.0f, 0.0f, 0.0f, 1.0f); 9 | 10 | } 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /misc/interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitinagarwal/mesh_slicer/66441e4210934161408335b3fe63db9962e987f2/misc/interface.png -------------------------------------------------------------------------------- /misc/vertex_shader: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec3 position; 3 | layout (location = 1) in vec3 color; 4 | 5 | out vec3 ourColor; 6 | 7 | uniform mat4 model; 8 | uniform mat4 projection; 9 | uniform mat4 view; 10 | 11 | void main() 12 | { 13 | gl_Position = projection * view * model * vec4(position.x,position.y, position.z, 1.0f); 14 | ourColor = color; 15 | } 16 | -------------------------------------------------------------------------------- /misc/vertex_shader_contour: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec3 position; 3 | layout (location = 1) in vec3 color; 4 | 5 | out vec3 ourColor; 6 | 7 | uniform mat4 model; 8 | uniform mat4 projection; 9 | uniform mat4 view; 10 | 11 | void main() 12 | { 13 | gl_Position = projection * view * model * vec4(position.x, position.y, position.z, 1.0f); 14 | gl_PointSize = 2; 15 | ourColor = color; 16 | } 17 | -------------------------------------------------------------------------------- /misc/vertex_shader_plane: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | layout (location = 0) in vec3 position; 3 | 4 | out vec3 ourColor; 5 | 6 | uniform mat4 model; 7 | uniform mat4 view; 8 | uniform mat4 projection; 9 | 10 | void main() 11 | { 12 | gl_Position = projection * view * model * vec4(position, 1.0f); 13 | ourColor = vec3(0.5, 0.5f, 0.5f); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /utils/Quaternion.cpp: -------------------------------------------------------------------------------- 1 | #include "Quaternion.h" 2 | 3 | Quaternion::Quaternion(Eigen::RowVector3d axis, double angle){ 4 | 5 | // angle is in radians 6 | quat(0) = axis(0) * sin(angle/2); 7 | quat(1) = axis(1) * sin(angle/2); 8 | quat(2) = axis(2) * sin(angle/2); 9 | quat(3) = cos(angle/2); 10 | 11 | quat.normalize(); 12 | } 13 | 14 | Eigen::Matrix4d Quaternion::Quat_to_Rotmatrix(){ 15 | 16 | double xx2 = 2.0f * quat[0] * quat[0]; 17 | double yy2 = 2.0f * quat[1] * quat[1]; 18 | double zz2 = 2.0f * quat[2] * quat[2]; 19 | double xy2 = 2.0f * quat[0] * quat[1]; 20 | double xz2 = 2.0f * quat[0] * quat[2]; 21 | double yz2 = 2.0f * quat[1] * quat[2]; 22 | double wx2 = 2.0f * quat[3] * quat[0]; 23 | double wy2 = 2.0f * quat[3] * quat[1]; 24 | double wz2 = 2.0f * quat[3] * quat[2]; 25 | 26 | R_matrix(0,0) = 1.0 - yy2 - zz2; 27 | R_matrix(0,1) = xy2 - wz2; 28 | R_matrix(0,2) = xz2 + wy2; 29 | R_matrix(0,3) = 0; 30 | R_matrix(1,0) = xy2 + wz2; 31 | R_matrix(1,1) = 1.0 - xx2 - zz2; 32 | R_matrix(1,2) = yz2 - wx2; 33 | R_matrix(1,3) = 0; 34 | R_matrix(2,0) = xz2 -wy2; 35 | R_matrix(2,1) = yz2 + wx2; 36 | R_matrix(2,2) = 1.0 - xx2 - yy2; 37 | R_matrix(2,3) = 0; 38 | R_matrix.row(3) << 0, 0, 0, 1; 39 | 40 | /* mat[0*4+0] = - yy2 - zz2 + 1.0f; */ 41 | /* mat[0*4+1] = xy2 + wz2; */ 42 | /* mat[0*4+2] = xz2 - wy2; */ 43 | /* mat[0*4+3] = 0; */ 44 | /* mat[1*4+0] = xy2 - wz2; */ 45 | /* mat[1*4+1] = - xx2 - zz2 + 1.0f; */ 46 | /* mat[1*4+2] = yz2 + wx2; */ 47 | /* mat[1*4+3] = 0; */ 48 | /* mat[2*4+0] = xz2 + wy2; */ 49 | /* mat[2*4+1] = yz2 - wx2; */ 50 | /* mat[2*4+2] = - xx2 - yy2 + 1.0f; */ 51 | /* mat[2*4+3] = 0; */ 52 | /* mat[3*4+0] = mat[3*4+1] = mat[3*4+2] = 0; */ 53 | /* mat[3*4+3] = 1; */ 54 | 55 | return R_matrix; 56 | } 57 | -------------------------------------------------------------------------------- /utils/Quaternion.h: -------------------------------------------------------------------------------- 1 | #ifndef QUATERNION_H 2 | #define QUATERNION_H 3 | 4 | #include 5 | 6 | 7 | class Quaternion 8 | { 9 | private: 10 | 11 | // quat = a_i + b_j + c_k + w 12 | Eigen::Vector4d quat; 13 | Eigen::Matrix4d R_matrix; 14 | 15 | public: 16 | // convert an axis-angle representation into a quat 17 | Quaternion(Eigen::RowVector3d axis, double angle); // constructor to create a quat 18 | 19 | // compute the 4x4 rotation matrix for a given quat 20 | Eigen::Matrix4d Quat_to_Rotmatrix(); 21 | 22 | }; 23 | 24 | #endif /* QUATERNION_H */ 25 | -------------------------------------------------------------------------------- /utils/Shader.cpp: -------------------------------------------------------------------------------- 1 | #include "Shader.h" 2 | 3 | /* 1. Retrieve the vertex/fragment source code from filePath */ 4 | Shader::Shader(const GLchar* vertexPath, const GLchar* fragmentPath) 5 | { 6 | 7 | /* ensures ifstream objects can throw exceptions: */ 8 | vShaderFile.exceptions (std::ifstream::badbit); 9 | fShaderFile.exceptions (std::ifstream::badbit); 10 | 11 | try 12 | { 13 | /* Open files */ 14 | vShaderFile.open(vertexPath); 15 | fShaderFile.open(fragmentPath); 16 | 17 | /* Read file's buffer contents into streams */ 18 | vShaderStream << vShaderFile.rdbuf(); 19 | fShaderStream << fShaderFile.rdbuf(); 20 | 21 | /* close file handlers */ 22 | vShaderFile.close(); 23 | fShaderFile.close(); 24 | 25 | /* Convert stream into string */ 26 | vertexCode = vShaderStream.str(); 27 | fragmentCode = fShaderStream.str(); 28 | } 29 | catch (std::ifstream::failure e) 30 | { 31 | std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; 32 | } 33 | 34 | /* Convert string into c_str */ 35 | vShaderCode = vertexCode.c_str(); 36 | fShaderCode = fragmentCode.c_str(); 37 | 38 | /* ------------------OPENGL shader configurations ------------------------------------------------------ */ 39 | /* ----------------------------------------------------------------------------------------------------- */ 40 | 41 | /* Vertex Shader */ 42 | vertex = glCreateShader(GL_VERTEX_SHADER); 43 | glShaderSource(vertex, 1, &vShaderCode, NULL); 44 | glCompileShader(vertex); 45 | /* Print compile errors if any */ 46 | checkCompileErrors(vertex, "VERTEX"); 47 | 48 | /* Fragment Shader */ 49 | fragment = glCreateShader(GL_FRAGMENT_SHADER); 50 | glShaderSource(fragment, 1, &fShaderCode, NULL); 51 | glCompileShader(fragment); 52 | /* Print compile errors if any */ 53 | checkCompileErrors(fragment, "FRAGMENT"); 54 | 55 | /* Shader Program & Linking*/ 56 | this->Program = glCreateProgram(); 57 | glAttachShader(this->Program, vertex); 58 | glAttachShader(this->Program, fragment); 59 | glLinkProgram(this->Program); 60 | /* Print linking errors if any */ 61 | checkCompileErrors(this->Program, "PROGRAM"); 62 | 63 | /* Delete the shaders as they're linked into our program now and no longer necessery */ 64 | glDeleteShader(vertex); 65 | glDeleteShader(fragment); 66 | 67 | } 68 | 69 | /* Uses the current shader */ 70 | void Shader::Use() 71 | { 72 | glUseProgram(this->Program); 73 | } 74 | 75 | // utility uniform functions 76 | void Shader::setBool(const std::string &name, bool value) const 77 | { 78 | glUniform1i(glGetUniformLocation(this->Program, name.c_str()), (int)value); 79 | } 80 | // ------------------------------------------------------------------------ 81 | void Shader::setInt(const std::string &name, int value) const 82 | { 83 | glUniform1i(glGetUniformLocation(this->Program, name.c_str()), value); 84 | } 85 | // ------------------------------------------------------------------------ 86 | void Shader::setFloat(const std::string &name, float value) const 87 | { 88 | glUniform1f(glGetUniformLocation(this->Program, name.c_str()), value); 89 | } 90 | // ------------------------------------------------------------------------ 91 | void Shader::setVec2(const std::string &name, const glm::vec2 &value) const 92 | { 93 | glUniform2fv(glGetUniformLocation(this->Program, name.c_str()), 1, &value[0]); 94 | } 95 | void Shader::setVec2(const std::string &name, float x, float y) const 96 | { 97 | glUniform2f(glGetUniformLocation(this->Program, name.c_str()), x, y); 98 | } 99 | // ------------------------------------------------------------------------ 100 | void Shader::setVec3(const std::string &name, const glm::vec3 &value) const 101 | { 102 | glUniform3fv(glGetUniformLocation(this->Program, name.c_str()), 1, &value[0]); 103 | } 104 | void Shader::setVec3(const std::string &name, float x, float y, float z) const 105 | { 106 | glUniform3f(glGetUniformLocation(this->Program, name.c_str()), x, y, z); 107 | } 108 | // ------------------------------------------------------------------------ 109 | void Shader::setVec4(const std::string &name, const glm::vec4 &value) const 110 | { 111 | glUniform4fv(glGetUniformLocation(this->Program, name.c_str()), 1, &value[0]); 112 | } 113 | void Shader::setVec4(const std::string &name, float x, float y, float z, float w) 114 | { 115 | glUniform4f(glGetUniformLocation(this->Program, name.c_str()), x, y, z, w); 116 | } 117 | // ------------------------------------------------------------------------ 118 | void Shader::setMat2(const std::string &name, const glm::mat2 &mat) const 119 | { 120 | glUniformMatrix2fv(glGetUniformLocation(this->Program, name.c_str()), 1, GL_FALSE, &mat[0][0]); 121 | } 122 | // ------------------------------------------------------------------------ 123 | void Shader::setMat3(const std::string &name, const glm::mat3 &mat) const 124 | { 125 | glUniformMatrix3fv(glGetUniformLocation(this->Program, name.c_str()), 1, GL_FALSE, &mat[0][0]); 126 | } 127 | // ------------------------------------------------------------------------ 128 | void Shader::setMat4(const std::string &name, const glm::mat4 &mat) const 129 | { 130 | glUniformMatrix4fv(glGetUniformLocation(this->Program, name.c_str()), 1, GL_FALSE, &mat[0][0]); 131 | } 132 | 133 | /* check compile and linking error */ 134 | void Shader::checkCompileErrors(GLuint shader, std::string type) 135 | { 136 | GLint success; 137 | GLchar infoLog[1024]; 138 | if(type != "PROGRAM") 139 | { 140 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 141 | if(!success) 142 | { 143 | glGetShaderInfoLog(shader, 1024, NULL, infoLog); 144 | std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; 145 | } 146 | } 147 | else 148 | { 149 | glGetProgramiv(shader, GL_LINK_STATUS, &success); 150 | if(!success) 151 | { 152 | glGetProgramInfoLog(shader, 1024, NULL, infoLog); 153 | std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; 154 | } 155 | } 156 | } 157 | 158 | 159 | -------------------------------------------------------------------------------- /utils/Shader.h: -------------------------------------------------------------------------------- 1 | /* This is a shader class which reads both vertex and fragment shader txtfile */ 2 | #ifndef SHADER_H 3 | #define SHADER_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include // include glew to get all the required OpenGL headers 10 | #include 11 | 12 | class Shader 13 | { 14 | 15 | private: 16 | std::string vertexCode; 17 | std::string fragmentCode; 18 | std::ifstream vShaderFile; 19 | std::ifstream fShaderFile; 20 | 21 | /* Defining stringstream */ 22 | std::stringstream vShaderStream, fShaderStream; 23 | 24 | /* Defining c_str */ 25 | const GLchar* vShaderCode ; 26 | const GLchar* fShaderCode ; 27 | 28 | /* Defining vertex and fragment shaders */ 29 | GLuint vertex, fragment; 30 | 31 | /* to check compile and linking errors */ 32 | void checkCompileErrors(GLuint shader, std::string type); 33 | 34 | public: 35 | /* The program ID */ 36 | GLuint Program; 37 | 38 | /* Constructor reads and builds the shader */ 39 | Shader(const GLchar* vertexPath, const GLchar* fragmentPath); 40 | 41 | /* Use the program */ 42 | void Use(); 43 | 44 | 45 | // utility uniform functions 46 | void setBool(const std::string &name, bool value) const; 47 | // ------------------------------------------------------------------------ 48 | void setInt(const std::string &name, int value) const; 49 | // ------------------------------------------------------------------------ 50 | void setFloat(const std::string &name, float value) const; 51 | // ------------------------------------------------------------------------ 52 | void setVec2(const std::string &name, const glm::vec2 &value) const; 53 | void setVec2(const std::string &name, float x, float y) const; 54 | // ------------------------------------------------------------------------ 55 | void setVec3(const std::string &name, const glm::vec3 &value) const; 56 | void setVec3(const std::string &name, float x, float y, float z) const; 57 | // ------------------------------------------------------------------------ 58 | void setVec4(const std::string &name, const glm::vec4 &value) const; 59 | void setVec4(const std::string &name, float x, float y, float z, float w) ; 60 | // ------------------------------------------------------------------------ 61 | void setMat2(const std::string &name, const glm::mat2 &mat) const; 62 | // ------------------------------------------------------------------------ 63 | void setMat3(const std::string &name, const glm::mat3 &mat) const; 64 | // ------------------------------------------------------------------------ 65 | void setMat4(const std::string &name, const glm::mat4 &mat) const; 66 | 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /utils/WriteBMP.cpp: -------------------------------------------------------------------------------- 1 | #include "WriteBMP.h" 2 | 3 | WriteBMP::WriteBMP(int width, int height, std::vector vertices, std::vector color, std::vector edges, std::string filename){ 4 | 5 | /* assuming the input vertices are already aligned and have constant z value */ 6 | 7 | padSize = (4- (( width*3 ) %4 )); // have to make sure the width is multiple of 4 8 | if(padSize==4){ 9 | padSize=0; 10 | } 11 | 12 | imageSize = (width * 3 + padSize) * height; // size of the image in bytes 13 | totalSize = imageSize + sizeof(file) + sizeof(info); // total size in bytes 14 | 15 | file[ 2] = (unsigned char)( totalSize ); 16 | file[ 3] = (unsigned char)( totalSize>> 8); 17 | file[ 4] = (unsigned char)( totalSize>>16); 18 | file[ 5] = (unsigned char)( totalSize>>24); 19 | 20 | info[ 4] = (unsigned char)( width ); 21 | info[ 5] = (unsigned char)( width>> 8); 22 | info[ 6] = (unsigned char)( width>>16); 23 | info[ 7] = (unsigned char)( width>>24); 24 | 25 | info[ 8] = (unsigned char)( height ); 26 | info[ 9] = (unsigned char)( height>> 8); 27 | info[10] = (unsigned char)( height>>16); 28 | info[11] = (unsigned char)( height>>24); 29 | 30 | info[20] = (unsigned char)( imageSize ); 31 | info[21] = (unsigned char)( imageSize>> 8); 32 | info[22] = (unsigned char)( imageSize>>16); 33 | info[23] = (unsigned char)( imageSize>>24); 34 | 35 | double z_value = vertices.at(0)(2); // constant z value 36 | 37 | /* Interpolating the edges so that contours are closed */ 38 | for(int i=0;i::iterator it1; 74 | 75 | for ( int y=0; y=0; y-- ) 76 | { 77 | for ( int x=0; x 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* using namespace std; */ 12 | 13 | class WriteBMP 14 | { 15 | private: 16 | unsigned char file[14] = { 17 | 'B','M', // bftype - specifies its bmp file 18 | 0,0,0,0, // bfsize - size of the whole bmp in bytes 19 | 0,0, // bfreserved1 - app data (must be zero) 20 | 0,0, // bfreserved2 - app data (must be zero) 21 | 40+14,0,0,0 // bfoffbits - distance to the begining of the image data in bytes (data offset) 22 | }; 23 | 24 | unsigned char info[40] = { 25 | 40,0,0,0, // bisize - info hd size 26 | 0,0,0,0, // biwidth - image width in pixels 27 | 0,0,0,0, // biheight - image heigth in pixels 28 | 1,0, // biplanes - number color planes 29 | 24,0, // bibitcount - bits per pixel 30 | 0,0,0,0, // bicompression - compression is none 31 | 0,0,0,0, // bisizeimage - image size in bytes 32 | 0x13,0x0B,0,0, // bixpelspermeter - horz resoluition in pixel / m 33 | 0x13,0x0B,0,0, // biypelspermeter - vert resolutions (0x03C3 = 96 dpi, 0x0B13 = 72 dpi) 34 | 0,0,0,0, // #colors in pallete 35 | 0,0,0,0, // #important colors 36 | }; 37 | 38 | unsigned char pad[3] = {0,0,0}; 39 | unsigned char pixel[3]; 40 | 41 | int padSize; 42 | int imageSize; 43 | int totalSize; 44 | int row; 45 | std::ofstream myfile; 46 | 47 | long red, green, blue; 48 | std::vector v1; 49 | std::vector v2; 50 | std::vector v3; 51 | 52 | Eigen::RowVector2d pt1, pt2, vec, newpt; 53 | Eigen::RowVector3d temp; 54 | 55 | public: 56 | WriteBMP(int width, int height, std::vector vertices, std::vector color, std::vector edges, std::string filename); 57 | 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | # This file contains the current version number of mesh_slicer 2 | # World.Major.Minor 3 | # Minor indicates a small change 4 | # Major indicates a large change or large number of changes (upload to website) 5 | # World indicates a substantial change or release 6 | 1.0.1 7 | --------------------------------------------------------------------------------