C++ pogramming

profilewind9j72b
main.cpp

#include "Helpers.h" #ifdef __APPLE__ #define GL_SILENCE_DEPRECATION // GLFW is necessary to handle the OpenGL context #include <GLFW/glfw3.h> #else // GLFW is necessary to handle the OpenGL context #include <GLFW/glfw3.h> #endif // OpenGL Mathematics Library #include <glm/glm.hpp> // glm::vec3 #include <glm/vec3.hpp> // glm::vec3 #include <glm/vec4.hpp> // glm::vec4 #include <glm/mat4x4.hpp> // glm::mat4 #include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective #include <glm/gtx/string_cast.hpp> #include <glm/gtc/type_ptr.hpp> // glm::value_ptr #include <vector> #include <string> #include <iostream> #include <sstream> #include <fstream> #include <cmath> #include <unordered_map> // Timer #include <chrono> using namespace std; using namespace glm; //----------------------------------mesh container----------------------------------------------------- class tri_mesh { public: vector<vec3> Vertex; vector<vec3> Face_index; // each face hase its indices indicating which vertices used vector<vec3> V_normals; vector<vec3> F_normals; mat4 model_mat; mat4 normal_mat; float specularStrength = 0.2f; //obj's factor of specular reflection strength int render_mode = 0; // 0: wireframe, 1: flat shading, 2: phong shading // float phong_exp; float bound_radius; vec3 bound_center; }; class light{ //but light calculation should be in viewspace(camera space) public: vec3 color = {1.0f,1.0f,1.0f}; //white vec3 pos = {1.f,1.f,1.8f}; // in world space not camera space }; class ray { public: // ray: e(origin)+t(scalar) * d(normalized direction) vec3 e; vec3 d; }; class sphere { public: vec3 center; double radius; }; class hit_sphere { public: bool hit = false; double t; //scalar in ray expression sphere hit_obj; }; //--------------------------------------gloabl variables------------------------------------------ VertexBufferObject VBO; VertexBufferObject VBO_vNormal; VertexBufferObject VBO_fNormal; vector<vec3> mesh_V; vector<vec3> mesh_vnormal; vector<vec3> mesh_fnormal; vector<vec3> V_Color; vector<tri_mesh> meshes; //tri_mesh selected_mesh; bool selected = false; int selected_mesh_idx = -1; mat4 selected_model_mat; vec3 selected_bound_center(0.0f,0.0f,0.0f); vec3 selected_pos(0.0f,0.0f,0.0f); string mode =""; light spotlight = light(); float ambientStrength = 0.15f; //environment factor of ambient strength //track ball: #define PI 3.14159265; float theta = 0.f; float phi = 0.f; float last_phi = 0.f; //-------------------------------opengl pipline matrix----------------------------- vec3 camera_pos(0.0f,0.0f,2.0f); // in world space vec3 camera_target(0.0f, 0.0f, 0.0f); vec3 upVector(0.0f, 1.0f, 0.0f); mat4 camera_mat = glm::lookAt( camera_pos, // the position of your camera, in world space camera_target, // where you want to look at, in world space upVector // probably glm::vec3(0,1,0), but (0,-1,0) would make you looking upside-down, which can be great too ); mat4 perspective_proj; mat4 ortho_proj; mat4 proj_mat; int proj_opt = 1; glm::mat4 view_port; float zoom_factor = 1.0f; float view_offset_x = 0.0f; float view_offset_y = 0.0f; //-------------------------------function variable--------------------------------- // string mode; // int n_tri = 0; //number of triangle already built // bool selected = false; //if an trangle is selected // int selected_tri = -1; // int count_click = 0; // bool preview = false; //if need preview // vector<int> delete_idx; // float angle = 0.0; // float scalar = 1.0; // int color_idx = -1; //index for color plan for color mode // int near_tri = -1; // int near_vertex = -1; // vector<glm::vec3> color_plan (10,glm::vec3 {0,0,0}); //-----------------------------mesh function------------------------------------------------------ vector<string> split_str(string line) { //separates string into a vector of substrings by space vector<string> res; istringstream iss(line); string temp; while(iss >> temp)res.push_back(temp); return res; } double l2_distance(glm::vec3 p1, glm::vec3 p2){ double dist = (p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]) + (p1[2] - p2[2]) * (p1[2] - p2[2]); return sqrt(dist); } tri_mesh load_mesh(string path, vec3 position, double scale) { // load mesh object at the file path for .off (Object File Format) file cout << endl << "loading " << path << " ..." << endl; tri_mesh mesh; // mesh.phong_exp = 64; mesh.render_mode = 0; mesh.model_mat = glm::mat4(1); mat4 comb_mat = camera_mat * mesh.model_mat; mesh.normal_mat = glm::transpose(glm::inverse(comb_mat)); ifstream object(path); if (!object) { cout << "Error opening .off file: " << path << endl; } // mesh vertices/faces number int n_vertices; int n_faces; char str[255]; bool comment_finish = false; vector<string> substrings; //read the first line object.getline(str,255); //check if it is .off file if(string(str).substr(0,3) == "OFF") { //cout << "Valid .off file" << endl; // skip comment while (comment_finish == false && object.getline(str,255)) { if(str[0]!='#'){ substrings = split_str(string(str)); n_vertices = stoi(substrings[0]); n_faces = stoi(substrings[1]); comment_finish = true; } } mesh.Vertex.resize(n_vertices); mesh.V_normals.resize(n_vertices); mesh.Face_index.resize(n_faces); mesh.F_normals.resize(n_faces); //load matrix Vertex with vertices for (unsigned i=0 ; i < n_vertices ; i++) { object.getline(str,255); substrings = split_str(string(str)); mesh.Vertex[i] = {(stof(substrings[0]) + position[0]) * scale, (stof(substrings[1]) + position[1]) * scale, (stof(substrings[2]) + position[2]) * scale}; //first move to origin then scale } //load matrix Face_index with vertices index of each face for (unsigned i=0 ; i < n_faces ; i++) { object.getline(str,255); substrings = split_str(string(str)); //calculate face normals un-normalized //which will weight the vertex normal average based on each face's area vec3 v1 = mesh.Vertex[stoi(substrings[1])]; vec3 v2 = mesh.Vertex[stoi(substrings[2])]; vec3 v3 = mesh.Vertex[stoi(substrings[3])]; vec3 face_normal = cross((v2-v1),(v3-v1)); //in .off file, tri. vetices are Clockwise, so we can get normal heading outside mesh.Face_index[i] = {stoi(substrings[1]), stoi(substrings[2]), stoi(substrings[3])}; mesh.F_normals[i] = face_normal; } mesh.bound_center = {0,0,0}; //iterate through matrix V: bound center and vertex normal for(unsigned i=0; i<mesh.Vertex.size(); i++) { for(unsigned j=0; j<3; j++) { mesh.bound_center[j]+=mesh.Vertex[i][j]; } //for each face for(unsigned j=0; j<mesh.Face_index.size(); j++) { //for each face vertex for(unsigned k=0; k<3; k++) { int face_vertex_index = int(mesh.Face_index[j][k]); //if face vertex index == i if(face_vertex_index == i) //find faces related to current vertex { //add face normal to vertex normal mesh.V_normals[i] += mesh.F_normals[j]; } } } //normalize vertex normal (naturally get vertex normal on average of faces normal); mesh.V_normals[i]=normalize(mesh.V_normals[i]); } mesh.bound_center = (1.f/(float)n_vertices) * mesh.bound_center ; mesh.bound_radius = 0; // numeric_limits<float>::infinity(); // used to be 0 for(unsigned i=0; i<mesh.Vertex.size(); i++){ float cand_radius = l2_distance(mesh.Vertex[i],mesh.bound_center); mesh.bound_radius = cand_radius > mesh.bound_radius ? cand_radius : mesh.bound_radius; //used to be max } cout << "mesh.bound_center: " << glm::to_string(mesh.bound_center) << endl; cout << "mesh.bound_radius: " << mesh.bound_radius << endl; cout << "loading .off finished" << endl; return mesh; } else { //otherwise exit with output "Invalid file (not OFF file)" std::cout << "Invalid file (not OFF file)" << std::endl; return mesh; } } void update_mesh_V(tri_mesh new_mesh) { cout << "start update mesh_V..." << endl; int insert_start; if(mesh_V.size() < 3) { //cout << 1 << endl; insert_start = 0; mesh_V.resize(new_mesh.Face_index.size()*3); //cout << 1.5 << endl; mesh_vnormal.resize(new_mesh.Face_index.size()*3); mesh_fnormal.resize(new_mesh.Face_index.size()*3); //cout << 2 << endl; } else { //cout << 3 << endl; insert_start = mesh_V.size(); mesh_V.resize(mesh_V.size()+new_mesh.Face_index.size()*3); mesh_vnormal.resize(mesh_vnormal.size()+new_mesh.Face_index.size()*3); mesh_fnormal.resize(mesh_fnormal.size()+new_mesh.Face_index.size()*3); //cout << 4 << endl; } for(unsigned i=0; i < new_mesh.Face_index.size();i++) { for(unsigned j=0; j<3;j++) { mesh_V[insert_start] = new_mesh.Vertex[new_mesh.Face_index[i][j]]; mesh_vnormal[insert_start] = new_mesh.V_normals[new_mesh.Face_index[i][j]]; //find normal by index of face vertex mesh_fnormal[insert_start] = new_mesh.F_normals[i];//vertex normals equal to face normals insert_start++; } } cout << "updated mesh_V.size: " << mesh_V.size() << ", mesh_vnormal.size(): " << mesh_vnormal.size() << ", mesh_fnormal.size(): " << mesh_fnormal.size() << endl; VBO.update(mesh_V); VBO_vNormal.update(mesh_vnormal); VBO_fNormal.update(mesh_fnormal); cout << "VBO,VBO_v/fNormal updated" << endl; } //-----------------------------------------aux function-------------------------------------------------- void update_camera_matrix(){ camera_mat = glm::lookAt( camera_pos, // the position of your camera, in world space camera_target, // where you want to look at, in world space upVector // probably glm::vec3(0,1,0), but (0,-1,0) would make you looking upside-down, which can be great too ); } glm::vec3 get_world_pos(GLFWwindow *window, mat4 proj) { // Get the position of the mouse in the window double xpos, ypos; glfwGetCursorPos(window, &xpos, &ypos); // Get the size of the window int width, height; glfwGetWindowSize(window, &width, &height); // Convert screen position to world coordinates, considering view control effect in vertex shader! glm::vec4 p_screen(xpos,height-ypos-1.0,0.0,1.0); //cordinates on screen starts at up-lef, (0,height-1), so y is inversed glm::vec4 p_canonical((p_screen.x/(double)width)*2-1,(p_screen.y/(double)height)*2-1,0,1); //convert to (-1,1) NDC glm::vec4 p_world = glm::inverse(camera_mat) * glm::inverse(proj) * glm::inverse(view_port) * p_canonical; double xworld = p_world.x; double yworld = p_world.y; double zworld = p_world.z; return glm::vec3(xworld, yworld,zworld); } hit_sphere ray_hit_sphere(ray r, double t_lower, sphere test_sphere) { hit_sphere test; const double sphere_radius = test_sphere.radius; vec3 sphere_center = test_sphere.center; vec3 e_c = r.e - sphere_center; double a = glm::dot(r.d,r.d); double b = glm::dot(r.d,e_c); double c = glm::dot(e_c,e_c) - sphere_radius * sphere_radius; //discriminant double discriminant = b*b - a*c; if(discriminant>=0) { test.hit = true; test.hit_obj = test_sphere; //if discriminant is greater than or equal to 0 //Calculate sphere intersection point if(discriminant ==0) { test.t = -b/(2*a); } else { test.t = (-b - sqrt(discriminant)) / (2*a); if(test.t < 0) { test.t = (-b + sqrt(discriminant)) / (2*a); } else { double t2 = (-b + sqrt(discriminant)) / (2*a); if (t2>0 && t2<test.t) { test.t = t2; } } } } return test; } void clear_select(){ selected = false; selected_mesh_idx = -1; selected_model_mat = mat4(1); selected_bound_center = {0.0f,0.0f,0.0f}; selected_pos = {0.0f,0.0f,0.0f}; } //--------------------------------------function------------------------------------------------------------ void select(vec3 world_pos){ selected = false; ray click_ray; click_ray.e = world_pos; click_ray.d = {0.0,0.0,-1.0}; //normalized device coordinated at clipping space: far end from screen vec4 d_homogeneous; d_homogeneous = {click_ray.d, 0.0}; // mat4 inv_viewport = glm::inverse(view_port); mat4 inv_proj = glm::inverse(proj_mat); mat4 inv_cam = glm::inverse(camera_mat); d_homogeneous = inv_cam * inv_proj *d_homogeneous; //* inv_viewport vec3 d_world(d_homogeneous[0],d_homogeneous[1],d_homogeneous[2]); click_ray.d = glm::normalize(d_world); //ray direction, head into screen vertically //cout<< click_ray.d <<endl; float min_dist = numeric_limits<float>::infinity(); for(unsigned i=0; i<meshes.size(); i++) { sphere test_sphere; test_sphere.center = meshes[i].bound_center; test_sphere.radius = meshes[i].bound_radius; hit_sphere check = ray_hit_sphere(click_ray,0,test_sphere); if(check.hit) { if(check.t < min_dist) { min_dist = check.t; selected_mesh_idx = i; selected = true; } } } if(selected == false || min_dist == numeric_limits<float>::infinity()) //no object selected { cout <<" nothing selected "<<endl; // selected_mesh_idx = -1; // selected = false; } else { selected_pos = world_pos; selected_model_mat = meshes[selected_mesh_idx].model_mat; //record origin at click selected_bound_center = meshes[selected_mesh_idx].bound_center; cout << endl << "mesh selected! " << "selected_index = " << selected_mesh_idx << endl; } } void delete_mesh(){ int start = 0; for(unsigned i = 0; i < selected_mesh_idx; i++) { start+= meshes[i].Face_index.size()*3; } //cout << "deleting Face_index: " << meshes[selected_mesh_idx].Face_index.size() << endl; for(unsigned i=0; i<meshes[selected_mesh_idx].Face_index.size()*3;i++) { mesh_V.erase(mesh_V.begin()+start); mesh_vnormal.erase(mesh_vnormal.begin()+start); mesh_fnormal.erase(mesh_fnormal.begin()+start); } meshes.erase(meshes.begin()+selected_mesh_idx); // if(mesh_V.size() ==0)mesh_V.push_back({0, 0, 0});//incase update empty array exception if(mesh_V.size()!=0)VBO.update(mesh_V); if(mesh_fnormal.size()!=0)VBO_fNormal.update(mesh_fnormal); if(mesh_vnormal.size()!=0)VBO_vNormal.update(mesh_vnormal); cout << endl << "mesh " << selected_mesh_idx << " deleted, meshes.size(): " << meshes.size() << ", mesh_V.size(): " << mesh_V.size() << ", mesh_vnormal.size(): " << mesh_vnormal.size() << ", mesh_fnormal.size(): " << mesh_fnormal.size() << endl; selected = false; selected_mesh_idx = -1; } void trackball(float radius, float &theta, float &phi){ float r = radius; float pi = 3.1415926; theta = (float)((int)theta % 360); phi = (float)((int)phi % 360); float z = cos((theta / 180.f) * pi)*r; float x = sin((theta / 180.f) * pi)*r; float y = sin((phi / 180.f) * pi)*r; float r_prime = sqrt(r*r - y*y); float ratio = r_prime / r; z *= ratio; x *= ratio; if((last_phi = 90.f && phi > 90 && phi < 270) || (last_phi = -90.f && phi < -90 && phi > -270)){ z *= -1; x *= -1; } last_phi = phi; camera_pos = vec3(x, y, z); cout << "trackball: theta= " << theta << ", phi= " << phi << ", camera_pos= " << glm::to_string(camera_pos) << endl; } //---------------------------------------------------call back functions------------------------------------------------------ void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { glm::vec3 world_pos = get_world_pos(window, ortho_proj); // When left mouse button is pressed if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) { select(world_pos); } if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) //avoid triggers twice!!!!!!!! { if(mode == "translation" && selected ){ clear_select(); mode = ""; cout << "release translation" << endl; } return; } } void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) { } void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { if(action == GLFW_RELEASE) //avoid triggers twice!!!!!!!! { return; } switch (key) { case GLFW_KEY_O: { proj_opt = 1; cout << endl << "orthographic projection" << endl; break; } case GLFW_KEY_P: { proj_opt = 2; cout << endl << "perspective projection" << endl; break; } case GLFW_KEY_1: { tri_mesh cube = load_mesh("../data/cube.off",vec3(0,0,0),1); //radius: 0.866025 center: vec3(0.000000, 0.000000, 0.000000) // cout << "cube.Vertex.size(): " << cube.Vertex.size() << endl; meshes.push_back(cube); cout << "updated meshes.size():" << meshes.size() <<endl; update_mesh_V(cube); //arrange vertex for faces (triangle) break; } case GLFW_KEY_2: { // to contain obj into cube, scale it to a sephere space of radius of 0.5 tri_mesh bumpy_cube = load_mesh("../data/bumpy_cube.off",vec3(-0.003338, -0.000037, -0.000392),0.5/4.4079);//radius: 4.4079 center: vec3(0.003338, 0.000037, 0.000392) // cout << "bumpy_cube.Vertex.size(): " << bumpy_cube.Vertex.size() << endl; meshes.push_back(bumpy_cube); cout << "updated meshes.size():" << meshes.size() <<endl; update_mesh_V(bumpy_cube); break; } case GLFW_KEY_3: { cout << "loading bunny.off..." << endl; // tri_mesh bunny = load_mesh("../data/bunny.off",vec3(0,0,0),0.5/0.116281); tri_mesh bunny = load_mesh("../data/bunny.off",vec3(0.028088, -0.094288, -0.008176),0.5/0.116281); //radius: 0.116281 center: vec3(-0.028088, 0.094288, 0.008176) // cout << "bunny.Vertex.size(): " << bunny.Vertex.size() << endl; meshes.push_back(bunny); cout << "updated meshes.size():" << meshes.size() <<endl; update_mesh_V(bunny); break; } case GLFW_KEY_DELETE: { if(selected==true){ delete_mesh(); clear_select(); } break; } case GLFW_KEY_T: { clear_select(); if(mode == "translation"){ mode = ""; cout <<endl<<"quit translation"<<endl; } else{ mode = "translation"; cout <<endl<<"translating"<<endl; } break; } case GLFW_KEY_B: //"wireframe" { if(selected==true){ meshes[selected_mesh_idx].render_mode = 0; clear_select(); } break; } case GLFW_KEY_N: //"flat shading" { if(selected==true){ meshes[selected_mesh_idx].render_mode = 1; clear_select(); } break; } case GLFW_KEY_M: //"phong shading" { if(selected==true){ meshes[selected_mesh_idx].render_mode = 2; clear_select(); } break; } case GLFW_KEY_U: //scale up { if(selected==true){ //顺�很��,左乘在mdel_mat左边的执行顺��� meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (-1.f * meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; //back to origin first meshes[selected_mesh_idx].model_mat = glm::scale(mat4(1), glm::vec3(1.25, 1.25, 1.25)) * meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].bound_radius *= 1.25; // adjust radius cout << "scale up, cur radius: " << meshes[selected_mesh_idx].bound_radius << endl; clear_select(); } break; } case GLFW_KEY_I: //scale down { if(selected==true){ meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (-1.f * meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; //back to origin first meshes[selected_mesh_idx].model_mat = glm::scale(mat4(1), glm::vec3(0.75, 0.75, 0.75)) * meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].bound_radius *= 0.75; cout << "scale down, cur radius: " << meshes[selected_mesh_idx].bound_radius << endl; clear_select(); } break; } case GLFW_KEY_K: //rotate counter-clockwise 30 degree along z axis { if(selected==true){ meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (-1.f * meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; //back to origin first meshes[selected_mesh_idx].model_mat = glm::rotate(mat4(1), glm::radians(30.f), glm::vec3(0, 0, 1)) * // where vec3(x, y, z) is axis of rotation (e.g. 0 1 0) meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; cout << "rotate counter-clockwise 30 degree along z axis" << endl; clear_select(); } break; } case GLFW_KEY_L: //rotate clockwise 30 degree along z axis { if(selected==true){ meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (-1.f * meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; //back to origin first meshes[selected_mesh_idx].model_mat = glm::rotate(mat4(1), glm::radians(-30.f), glm::vec3(0, 0, 1)) * // where vec3(x, y, z) is axis of rotation (e.g. 0 1 0) meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; cout << "rotate clockwise 30 degree along z axis" << endl; clear_select(); } break; } case GLFW_KEY_F: //rotate counter-clockwise 30 degree along x axis { if(selected==true){ meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (-1.f * meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; //back to origin first meshes[selected_mesh_idx].model_mat = glm::rotate(mat4(1), glm::radians(30.f), glm::vec3(1, 0, 0)) * // where vec3(x, y, z) is axis of rotation (e.g. 0 1 0) meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; cout << "rotate counter-clockwise 30 degree along x axis" << endl; clear_select(); } break; } case GLFW_KEY_G: //rotate clockwise 30 degree along x axis { if(selected==true){ meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (-1.f * meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; //back to origin first meshes[selected_mesh_idx].model_mat = glm::rotate(mat4(1), glm::radians(-30.f), glm::vec3(1, 0, 0)) * // where vec3(x, y, z) is axis of rotation (e.g. 0 1 0) meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; cout << "rotate clockwise 30 degree along x axis" << endl; clear_select(); } break; } case GLFW_KEY_H: //rotate counter-clockwise 30 degree along y axis { if(selected==true){ meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (-1.f * meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; //back to origin first meshes[selected_mesh_idx].model_mat = glm::rotate(mat4(1), glm::radians(30.f), glm::vec3(0, 1, 0)) * // where vec3(x, y, z) is axis of rotation (e.g. 0 1 0) meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; cout << "rotate counter-clockwise 30 degree along y axis" << endl; clear_select(); } break; } case GLFW_KEY_J: //rotate clockwise 30 degree along y axis { if(selected==true){ meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (-1.f * meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; //back to origin first meshes[selected_mesh_idx].model_mat = glm::rotate(mat4(1), glm::radians(-30.f), glm::vec3(0, 1, 0)) * // where vec3(x, y, z) is axis of rotation (e.g. 0 1 0) meshes[selected_mesh_idx].model_mat; meshes[selected_mesh_idx].model_mat = glm::translate(mat4(1), (meshes[selected_mesh_idx].bound_center)) * meshes[selected_mesh_idx].model_mat; cout << "rotate clockwise 30 degree along y axis" << endl; clear_select(); } break; } case GLFW_KEY_R: camera_pos = vec3(0,0,2.0); theta = 0.f; phi = 0.f; last_phi = 0.f; cout << "camera pos reset to "<<glm::to_string(camera_pos) << endl; update_camera_matrix(); break; case GLFW_KEY_W: camera_pos = camera_pos - vec3(0,0,0.1); cout << "current camera pos "<<glm::to_string(camera_pos) << endl; update_camera_matrix(); break; case GLFW_KEY_S: camera_pos = camera_pos + vec3(0.0f,0.0f,0.1f); cout << "current camera pos "<<glm::to_string(camera_pos) << endl; update_camera_matrix(); break; case GLFW_KEY_A: camera_pos = camera_pos - vec3(0.1f,0.0f,0.0f); cout << "current camera pos "<<glm::to_string(camera_pos) << endl; update_camera_matrix(); break; case GLFW_KEY_D: camera_pos = camera_pos + vec3(0.1f,0.0f,0.0f); cout << "current camera pos "<<glm::to_string(camera_pos) << endl; update_camera_matrix(); break; case GLFW_KEY_Q: camera_pos = camera_pos + vec3(0.0f,0.1f,0.0f); cout << "current camera pos "<<glm::to_string(camera_pos) << endl; update_camera_matrix(); break; case GLFW_KEY_E: camera_pos = camera_pos - vec3(0.0f,0.1f,0.0f); cout << "current camera pos "<<glm::to_string(camera_pos) << endl; update_camera_matrix(); break; case GLFW_KEY_Z: trackball(2.f, theta-=10.f, phi); update_camera_matrix(); break; case GLFW_KEY_X: trackball(2.f, theta+=10.f, phi); update_camera_matrix(); break; case GLFW_KEY_C: trackball(2.f, theta, phi+=10.f); update_camera_matrix(); break; case GLFW_KEY_V: trackball(2.f, theta, phi-=10.f); update_camera_matrix(); break; default: break; } // cout << "view offset x: " << view_offset_x << endl; // cout << "view offset y: " << view_offset_y << endl; } int main(void) { //-------------------------------------settings---------------------------------------------- GLFWwindow* window; // Initialize the library if (!glfwInit()) return -1; // Activate supersampling glfwWindowHint(GLFW_SAMPLES, 8); // Ensure that we get at least a 3.2 context glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); // On apple we have to load a core profile with forward compatibility #ifdef __APPLE__ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #endif // Create a windowed mode window and its OpenGL context window = glfwCreateWindow(640, 480, "3D Editor", NULL, NULL); if (!window) { glfwTerminate(); return -1; } // Make the window's context current glfwMakeContextCurrent(window); #ifndef __APPLE__ glewExperimental = true; GLenum err = glewInit(); if(GLEW_OK != err) { /* Problem: glewInit failed, something is seriously wrong. */ fprintf(stderr, "Error: %s\n", glewGetErrorString(err)); } glGetError(); // pull and savely ignonre unhandled errors like GL_INVALID_ENUM fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); #endif int major, minor, rev; major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR); minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR); rev = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION); printf("OpenGL version recieved: %d.%d.%d\n", major, minor, rev); printf("Supported OpenGL is %s\n", (const char*)glGetString(GL_VERSION)); printf("Supported GLSL is %s\n", (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); // Initialize the VAO // A Vertex Array Object (or VAO) is an object that describes how the vertex // attributes are stored in a Vertex Buffer Object (or VBO). This means that // the VAO is not the actual object storing the vertex data, // but the descriptor of the vertex data. VertexArrayObject VAO; VAO.init(); VAO.bind(); // Initialize the VBO with the vertices data // A VBO is a data container that lives in the GPU memory VBO.init(); mesh_V.resize(1); mesh_V[0] = glm::vec3(0, 0, 0); VBO.update(mesh_V); // VBO for normals VBO_vNormal.init(); VBO_fNormal.init(); mesh_vnormal.resize(1); mesh_fnormal.resize(1); mesh_vnormal[0] = glm::vec3(0, 0, 0); mesh_fnormal[0] = glm::vec3(0, 0, 0); VBO_vNormal.update(mesh_vnormal); VBO_fNormal.update(mesh_fnormal); // Initialize the OpenGL Program // A program controls the OpenGL pipeline and it must contains // at least a vertex shader and a fragment shader to be valid Program program; const GLchar *vertex_shader = "#version 150 core\n" "in vec3 position;" "in vec3 vNormal;" "in vec3 fNormal;" "uniform int renderMode;" "uniform vec3 lightPos;" "uniform mat4 modelMatrix;" "uniform mat4 normalMatrix;" "uniform mat4 cameraMatrix;" "uniform mat4 projMatrix;" "uniform mat4 viewport;" "out vec4 normal;" "out vec3 lightDir;" "out vec3 viewDir;" "void main()" "{" " gl_Position = viewport * projMatrix * cameraMatrix * modelMatrix * vec4(position, 1.0);" " vec4 camera_space_Pos = cameraMatrix * modelMatrix * vec4(position, 1.0);" //fragment pos in camera space " vec4 camera_space_LightPos = cameraMatrix * vec4(lightPos, 1.0);" // Transform world-space light position to view-space light position " lightDir = normalize(camera_space_LightPos.xyz - camera_space_Pos.xyz);" // direction to light pos " viewDir = normalize(-camera_space_Pos.xyz);" //dir to viewer // the viewer is always at (0,0,0) in view-space, so viewDir is (0,0,0) - Position => -Position " if(renderMode == 1){" " normal = normalMatrix * vec4(fNormal, 0.0);" " }" " else{" " normal = normalMatrix * vec4(vNormal, 0.0);" " }" "}"; const GLchar *fragment_shader = "#version 150 core\n" "in vec4 normal;" "in vec3 lightDir;" "in vec3 viewDir;" "uniform int renderMode;" "uniform vec3 defaultColor;" // "uniform vec3 indicateColor;" "uniform vec3 frameColor;" // "uniform float indicate = 0.0f;" "uniform vec3 lightColor;" "uniform float ambientStrength;" "uniform float specularStrength;" "out vec4 outColor;" "void main()" "{" " if(renderMode == 0){" " outColor = vec4(frameColor, 1.0f);" " }" " else{" " vec3 ambient = ambientStrength * lightColor;" " vec3 n = normalize(normal.xyz);" " float diffFactor = max(0.0f, dot(n,lightDir));" //weight on diffuse, if dot < 0, it means it is on the surface got no light " vec3 diffuse = diffFactor * lightColor;" " vec3 reflectDir = normalize(reflect(-lightDir, n));" " float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 64);" //32/64/128 is highlight factor, larger -> highlight aggregate " vec3 specular = specularStrength * specFactor * lightColor;"// " float lightIntensity = 1.0f;" " vec3 result = (ambient + diffuse + specular) * lightIntensity * defaultColor;" " outColor = vec4(result, 1.0f);" " }" // " if(indicate > 0) {" // " outColor = vec4(indicateColor, 1.0f);" // " } " "}"; // Compile the two shaders and upload the binary to the GPU // Note that we have to explicitly specify that the output "slot" called outColor // is the one that we want in the fragment buffer (and thus on screen) program.init(vertex_shader,fragment_shader,"outColor"); program.bind(); // The vertex shader wants the position of the vertices as an input. // The following line connects the VBO we defined above with the position "slot" // in the vertex shader program.bindVertexAttribArray("position",VBO); program.bindVertexAttribArray("vNormal", VBO_vNormal); program.bindVertexAttribArray("fNormal", VBO_fNormal); // Save the current time --- it will be used to dynamically change the triangle color auto t_start = std::chrono::high_resolution_clock::now(); // Register the keyboard callback glfwSetKeyCallback(window, key_callback); // Register the mouse callback glfwSetMouseButtonCallback(window, mouse_button_callback); // Update viewport glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); //--------------------------------------------------Keep refreshing-------------------------------------------------- mesh_V.clear(); mesh_fnormal.clear(); mesh_vnormal.clear(); // Enable depth test glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // projection matrix ortho_proj = glm::ortho(-1.0f,1.0f,-1.0f,1.0f,0.1f,100.0f); // (left,right,top,down,z_near?,z_far?)In world coordinates perspective_proj = glm::perspective( glm::radians(65.0f), //45.0f// The vertical Field of View, in radians: the amount of "zoom". Think "camera lens". Usually between 90° (extra wide) and 30° (quite zoomed in) 1.0f / 1.0f, //(float)width / (float)height// Aspect Ratio of perspective projection space obj, set as 1:1 for orrginal obj's weidth and length 0.1f, // Near clipping plane. Keep as big as possible, or you'll get precision issues. 100.0f // Far clipping plane. Keep as little as possible. ); //glm::mat4 proj = glm::perspective(45.0f, (float)width/(float)height, 0.1f, 100.0f); glUniform1f(program.uniform("ambientStrength"), ambientStrength); glUniform3fv(program.uniform("lightPos"),1, glm::value_ptr(spotlight.pos)); glUniform3fv(program.uniform("lightColor"),1, glm::value_ptr(spotlight.color)); // Loop until the user closes the window while (!glfwWindowShouldClose(window)){ // Bind your VAO (not necessary if you have only one) VAO.bind(); // Bind your program program.bind(); int width, height; glfwGetWindowSize(window, &width, &height); //keep updating peojection matrix if(proj_opt == 1)proj_mat = ortho_proj; else if(proj_opt == 2)proj_mat = perspective_proj; //viewport control: use aspect_ratio to solve distortion from world to screen float aspect_ratio = float(height) / float(width); // corresponds to the necessary width scaling // use min(width,height) as base to adjust view if(width>height){ view_port = glm::scale(glm::mat4(1.f), glm::vec3(aspect_ratio * zoom_factor, zoom_factor, 1.0f)); } else{ view_port = glm::scale(glm::mat4(1.f), glm::vec3(zoom_factor, (1 / aspect_ratio) * zoom_factor, 1.0f)); } view_port = glm::translate(view_port, glm::vec3(view_offset_x, view_offset_y, 0.0f)); glUniform3f(program.uniform("defaultColor"), 128.f/255.f,102.f/255.f,255.f/255.f); // glUniform3f(program.uniform("indicateColor"), 0.f/255.f, 0.f/255.f, 255.f/255.f); glUniform3f(program.uniform("frameColor"), 51.f/255.f, 51.f/255.f, 153.f/255.f); //uniforms for transformation matrices glUniformMatrix4fv(program.uniform("viewport"), 1, GL_FALSE, glm::value_ptr(view_port)); glUniformMatrix4fv(program.uniform("projMatrix"),1, GL_FALSE, glm::value_ptr(proj_mat)); glUniformMatrix4fv(program.uniform("cameraMatrix"),1, GL_FALSE, glm::value_ptr(camera_mat)); // Clear the framebuffer glClearColor(80.f/255.f,102.f/255.f,255.f/255.f,1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if(mode == "translation" && selected){ vec3 cur_mouse = get_world_pos(window,ortho_proj); vec3 trans = cur_mouse - selected_pos; glm::mat4 translation; translation = glm::translate(mat4(1), trans); meshes[selected_mesh_idx].model_mat = translation * selected_model_mat; meshes[selected_mesh_idx].bound_center = selected_bound_center + trans; } if(mesh_V.size()>2) { //for each mesh long long start = 0; for(unsigned i = 0; i<meshes.size(); i++) { glUniform1i(program.uniform("renderMode"), meshes[i].render_mode); glUniformMatrix4fv(program.uniform("modelMatrix"),1, GL_FALSE, glm::value_ptr(meshes[i].model_mat)); //light calculation in camera space and use this matrix to avoid invalid transform to normal mat4 comb_mat = camera_mat * meshes[i].model_mat; meshes[i].normal_mat = glm::transpose(glm::inverse(comb_mat)); glUniformMatrix4fv(program.uniform("normalMatrix"),1, GL_FALSE, glm::value_ptr(meshes[i].normal_mat)); glUniform1f(program.uniform("specularStrength"), meshes[i].specularStrength); //if selected, change color if(selected == true && i == selected_mesh_idx ){ glUniform3f(program.uniform("defaultColor"), 255.f/255.f,255.f/255.f,204.f/255.f); glUniform3f(program.uniform("frameColor"), 255.f/255.f,255.f/255.f,204.f/255.f); } for(unsigned j=0; j<meshes[i].Face_index.size(); j++) { if(meshes[i].render_mode==0){ // cout << "mode 00000" << endl; glDrawArrays(GL_LINE_LOOP, start+j*3, 3); } if(meshes[i].render_mode==1){ // cout << "mode 11111" << endl; glDrawArrays(GL_TRIANGLES, start+j*3, 3); //WIREFRAME glUniform1i(program.uniform("renderMode"), 0); glLineWidth(1); glDrawArrays(GL_LINE_LOOP, start + j * 3, 3); glUniform1i(program.uniform("renderMode"), 1); //must put mode back } if(meshes[i].render_mode==2){ // cout << "mode 22222" << endl; glDrawArrays(GL_TRIANGLES, start+j*3, 3); } //draw indicated mesh object // if(selected == true && i == selected_mesh_idx ){ // //cout << endl << "drawing indicating color" << endl; // glUniform1f(program.uniform("indicate"), 1.0f); //use as status indicater in fragment shader // glLineWidth(2); // glDrawArrays(GL_LINE_LOOP, start+j*3, 3); // glUniform1f(program.uniform("indicate"), 0.0f); // glLineWidth(1); // } } //change color back when finish render selected mesh glUniform3f(program.uniform("defaultColor"), 128.f/255.f,102.f/255.f,255.f/255.f); glUniform3f(program.uniform("frameColor"), 51.f/255.f, 51.f/255.f, 153.f/255.f); start+= meshes[i].Face_index.size()*3; } } // Swap front and back buffers glfwSwapBuffers(window); // Poll for and process events glfwPollEvents(); } // Deallocate opengl memory program.free(); VAO.free(); VBO.free(); // Deallocate glfw internals glfwTerminate(); return 0; }