#include <igl/Viewport.h> #include <igl/Camera.h> #include <igl/matlab_format.h> #include <igl/report_gl_error.h> #include <igl/ReAntTweakBar.h> #include <igl/trackball.h> #include <igl/two_axis_valuator_fixed_up.h> #include <igl/PI.h> #include <igl/EPS.h> #include <igl/get_seconds.h> #include <igl/material_colors.h> #include <igl/draw_mesh.h> #include <igl/readOFF.h> #include <igl/per_face_normals.h> #include <igl/draw_floor.h> #include <Eigen/Core> #include <Eigen/Geometry> #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut.h> #endif #include <vector> #include <stack> #include <iostream> #include <algorithm> enum RotationType { ROTATION_TYPE_IGL_TRACKBALL = 0, ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 1, NUM_ROTATION_TYPES = 2, } rotation_type = ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP; enum CenterType { CENTER_TYPE_ORBIT = 0, CENTER_TYPE_FPS = 1, NUM_CENTER_TYPES = 2, } center_type = CENTER_TYPE_ORBIT; int width,height; #define REBAR_NAME "temp.rbr" igl::ReTwBar rebar; struct State { std::vector<igl::Camera> cameras; std::vector<GLuint> tex_ids; std::vector<GLuint> fbo_ids; std::vector<GLuint> dfbo_ids; State():cameras(4), tex_ids(cameras.size()), fbo_ids(cameras.size()), dfbo_ids(cameras.size()) {} } s; const Eigen::Vector4d back(1,1,1,1); std::stack<State> undo_stack; bool is_rotating = false; igl::Camera down_camera; int down_x,down_y; std::stack<State> redo_stack; Eigen::MatrixXd V,N; Eigen::MatrixXi F; Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0); void push_undo() { undo_stack.push(s); // Clear redo_stack = std::stack<State>(); } void undo() { if(!undo_stack.empty()) { redo_stack.push(s); s = undo_stack.top(); undo_stack.pop(); } } void redo() { if(!redo_stack.empty()) { undo_stack.push(s); s = redo_stack.top(); redo_stack.pop(); } } void print(const igl::Camera & camera) { using namespace std; cout<< "rotation: "<<camera.m_rotation_conj.conjugate().coeffs().transpose()<<endl<< "translation: "<<camera.m_translation.transpose()<<endl<< "eye: "<<camera.eye().transpose()<<endl<< "at: "<<camera.at().transpose()<<endl<< "up: "<<camera.up().transpose()<<endl<< endl; } void init_cameras() { using namespace Eigen; using namespace std; s.cameras[0].look_at( Vector3d(0,0,1), Vector3d(0,0,0), Vector3d(0,1,0)); s.cameras[1].look_at( Vector3d(0,0,-1), Vector3d(0,0,0), Vector3d(0,1,0)); s.cameras[2].look_at( Vector3d(-2,0,0), Vector3d(0,0,0), Vector3d(0,1,0)); s.cameras[3].look_at( Vector3d(3,0,0), Vector3d(0,0,0), Vector3d(0,1,0)); } bool init_render_to_texture( const int width, const int height, GLuint & tex_id, GLuint & fbo_id, GLuint & dfbo_id) { using namespace igl; using namespace std; // Set up a "render-to-texture" frame buffer and texture combo glDeleteTextures(1,&tex_id); glDeleteFramebuffersEXT(1,&fbo_id); glDeleteFramebuffersEXT(1,&dfbo_id); // http://www.opengl.org/wiki/Framebuffer_Object_Examples#Quick_example.2C_render_to_texture_.282D.29 glGenTextures(1, &tex_id); glBindTexture(GL_TEXTURE_2D, tex_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //NULL means reserve texture memory, but texels are undefined glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); glBindTexture(GL_TEXTURE_2D, 0); //------------------------- glGenFramebuffersEXT(1, &fbo_id); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id); //Attach 2D texture to this FBO glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex_id, 0); glGenRenderbuffersEXT(1, &dfbo_id); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, dfbo_id); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height); //------------------------- //Attach depth buffer to FBO glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, dfbo_id); //------------------------- //Does the GPU support current FBO configuration? GLenum status; status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); switch(status) { case GL_FRAMEBUFFER_COMPLETE_EXT: break; default: return false; } glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); return true; } void reshape(int width, int height) { ::width = width; ::height = height; glViewport(0,0,width,height); // Send the new window size to AntTweakBar TwWindowSize(width, height); } // Set up double-sided lights void lights() { using namespace std; using namespace Eigen; glEnable(GL_LIGHTING); glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); glEnable(GL_LIGHT0); float WHITE[4] = {0.8,0.8,0.8,1.}; float GREY[4] = {0.4,0.4,0.4,1.}; float BLACK[4] = {0.,0.,0.,1.}; Vector4f pos = light_pos; glLightfv(GL_LIGHT0,GL_AMBIENT,GREY); glLightfv(GL_LIGHT0,GL_DIFFUSE,WHITE); glLightfv(GL_LIGHT0,GL_SPECULAR,BLACK); glLightfv(GL_LIGHT0,GL_POSITION,pos.data()); } void draw_scene(const igl::Camera & v_camera, const bool render_to_texture, const GLuint & v_tex_id, const GLuint & v_fbo_id, const GLuint & v_dfbo_id) { using namespace igl; using namespace std; using namespace Eigen; if(render_to_texture) { // render to framebuffer glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, v_fbo_id); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, v_dfbo_id); glClearColor(back(0),back(1),back(2),1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); if(v_camera.m_angle > IGL_CAMERA_MIN_ANGLE) { gluPerspective(v_camera.m_angle,v_camera.m_aspect,v_camera.m_near,v_camera.m_far); }else { glOrtho( -0.5*v_camera.m_aspect, 0.5*v_camera.m_aspect, -0.5, 0.5, v_camera.m_near, v_camera.m_far); } //{ // Matrix4d m; // glGetDoublev(GL_PROJECTION_MATRIX,m.data()); // cout<<matlab_format(m,"glu")<<endl; //} glLoadIdentity(); glMultMatrixd(v_camera.projection().data()); //{ // Matrix4d m; // glGetDoublev(GL_PROJECTION_MATRIX,m.data()); // cout<<matlab_format(m,"Camera")<<endl; //} glMatrixMode(GL_MODELVIEW); glPushMatrix(); //glLoadIdentity(); //gluLookAt( // v_camera.eye()(0), v_camera.eye()(1), v_camera.eye()(2), // v_camera.at()(0), v_camera.at()(1), v_camera.at()(2), // v_camera.up()(0), v_camera.up()(1), v_camera.up()(2)); glLoadIdentity(); glMultMatrixd(v_camera.inverse().matrix().data()); for(int c = 0;c<(int)s.cameras.size();c++) { auto & camera = s.cameras[c]; if(&v_camera == &camera) { continue; } // draw camera glPushMatrix(); glMultMatrixd(camera.affine().matrix().data()); // eye glColor4f(0,0,0,1); glPointSize(10.f); glBegin(GL_POINTS); glVertex3f(0,0,0); glEnd(); // frustrum const Vector3d u = camera.unit_plane(); glBegin(GL_LINES); for(int x = -1;x<=1;x+=2) { for(int y = -1;y<=1;y+=2) { glVertex3f(0,0,0); glVertex3f(x*u(0),y*u(1),u(2)); } } glEnd(); const Vector3d n = u*(camera.m_near-FLOAT_EPS); glBegin(GL_QUADS); glVertex3f( n(0),-n(1),n(2)); glVertex3f(-n(0),-n(1),n(2)); glVertex3f(-n(0), n(1),n(2)); glVertex3f( n(0), n(1),n(2)); glEnd(); for(int pass = 0;pass<2;pass++) { switch(pass) { case 1: glColor4f(1,1,1,0.5); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); //glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,s.tex_ids[c]); break; default: case 0: glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); glColor4f(0,0,0,1); break; } glBegin(GL_QUADS); glTexCoord2d(1,0); glVertex3f( 0.5*u(0),-0.5*u(1),0.5*u(2)); glTexCoord2d(0,0); glVertex3f(-0.5*u(0),-0.5*u(1),0.5*u(2)); glTexCoord2d(0,1); glVertex3f(-0.5*u(0), 0.5*u(1),0.5*u(2)); glTexCoord2d(1,1); glVertex3f( 0.5*u(0), 0.5*u(1),0.5*u(2)); glEnd(); switch(pass) { case 1: glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); break; default: break; } } glPopMatrix(); } // Set material properties lights(); glEnable(GL_LIGHTING); glDisable(GL_COLOR_MATERIAL); glMaterialfv(GL_FRONT, GL_AMBIENT, GOLD_AMBIENT); glMaterialfv(GL_FRONT, GL_DIFFUSE, GOLD_DIFFUSE ); glMaterialfv(GL_FRONT, GL_SPECULAR, GOLD_SPECULAR); glMaterialf (GL_FRONT, GL_SHININESS, 128); glMaterialfv(GL_BACK, GL_AMBIENT, SILVER_AMBIENT); glMaterialfv(GL_BACK, GL_DIFFUSE, FAST_GREEN_DIFFUSE ); glMaterialfv(GL_BACK, GL_SPECULAR, SILVER_SPECULAR); glMaterialf (GL_BACK, GL_SHININESS, 128); draw_mesh(V,F,N); glDisable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); //glLineWidth(3.f); //glColor4f(1,0,1,1); //glutWireCube(0.25); //glColor4f(1,0.5,0.5,1); ////glutWireSphere(0.125,20,20); { glPushMatrix(); glTranslated(0,-1,0); draw_floor(); glPopMatrix(); } // Axes for(int d = 0;d<3;d++) { glColor4f(d==0,d==1,d==2,1); glBegin(GL_LINES); glVertex3f(0,0,0); glVertex3f(d==0,d==1,d==2); glEnd(); } glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); report_gl_error(); if(render_to_texture) { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); } } void display() { using namespace igl; using namespace std; using namespace Eigen; glClearColor(back(0),back(1),back(2),0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); // Update aspect ratios (may have changed since undo/redo) { const double aspect = (double)width/(double)height; for(int c = 0;c<(int)s.cameras.size();c++) { auto & camera = s.cameras[c]; auto & tex_id = s.tex_ids[c]; auto & fbo_id = s.fbo_ids[c]; auto & dfbo_id = s.dfbo_ids[c]; if(aspect != camera.m_aspect) { cout<<"Initializing camera #"<<c<<"..."<<endl; camera.m_aspect = aspect; bool ret = init_render_to_texture(width,height,tex_id,fbo_id,dfbo_id); assert(ret); } draw_scene(camera,true,tex_id,fbo_id,dfbo_id); } } { auto & camera = s.cameras[0]; draw_scene(camera,false,0,0,0); } TwDraw(); glutSwapBuffers(); glutPostRedisplay(); } void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y) { using namespace std; using namespace igl; using namespace Eigen; GLint viewport[4]; glGetIntegerv(GL_VIEWPORT,viewport); if(wheel == 0 && TwMouseMotion(mouse_x, viewport[3] - mouse_y)) { static double mouse_scroll_y = 0; const double delta_y = 0.125*direction; mouse_scroll_y += delta_y; TwMouseWheel(mouse_scroll_y); return; } auto & camera = s.cameras[0]; switch(center_type) { case CENTER_TYPE_ORBIT: if(wheel==0) { // factor of zoom change double s = (1.-0.01*direction); //// FOV zoom: just widen angle. This is hardly ever appropriate. //camera.m_angle *= s; //camera.m_angle = min(max(camera.m_angle,1),89); camera.push_away(s); }else { // Dolly zoom: camera.dolly_zoom((double)direction*1.0); } break; default: case CENTER_TYPE_FPS: // Move `eye` and `at` camera.dolly((wheel==0?Vector3d(0,0,1):Vector3d(-1,0,0))*0.1*direction); break; } } void mouse(int glutButton, int glutState, int mouse_x, int mouse_y) { using namespace std; using namespace Eigen; using namespace igl; bool tw_using = TwEventMouseButtonGLUT(glutButton,glutState,mouse_x,mouse_y); switch(glutButton) { case GLUT_RIGHT_BUTTON: case GLUT_LEFT_BUTTON: { switch(glutState) { case 1: // up glutSetCursor(GLUT_CURSOR_INHERIT); is_rotating = false; break; case 0: if(!tw_using) { push_undo(); glutSetCursor(GLUT_CURSOR_CYCLE); // collect information for trackball is_rotating = true; down_camera = s.cameras[0]; down_x = mouse_x; down_y = mouse_y; } break; } break; #ifdef GLUT_WHEEL_DOWN // Scroll down case GLUT_WHEEL_DOWN: { mouse_wheel(0,-1,mouse_x,mouse_y); break; } #endif #ifdef GLUT_WHEEL_UP // Scroll up case GLUT_WHEEL_UP: { mouse_wheel(0,1,mouse_x,mouse_y); break; } #endif #ifdef GLUT_WHEEL_LEFT // Scroll left case GLUT_WHEEL_LEFT: { mouse_wheel(1,-1,mouse_x,mouse_y); break; } #endif #ifdef GLUT_WHEEL_RIGHT // Scroll right case GLUT_WHEEL_RIGHT: { mouse_wheel(1,1,mouse_x,mouse_y); break; } #endif } } } void mouse_drag(int mouse_x, int mouse_y) { using namespace igl; using namespace std; using namespace Eigen; /*bool tw_using =*/ TwMouseMotion(mouse_x,mouse_y); if(is_rotating) { glutSetCursor(GLUT_CURSOR_CYCLE); auto & camera = s.cameras[0]; Quaterniond q; switch(rotation_type) { case ROTATION_TYPE_IGL_TRACKBALL: { // Rotate according to trackball igl::trackball( width, height, 2.0, down_camera.m_rotation_conj, down_x, down_y, mouse_x, mouse_y, q); break; } case ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP: { // Rotate according to two axis valuator with fixed up vector two_axis_valuator_fixed_up( width, height, 2.0, down_camera.m_rotation_conj, down_x, down_y, mouse_x, mouse_y, q); break; } default: break; } switch(center_type) { default: case CENTER_TYPE_ORBIT: camera.orbit(q.conjugate()); break; case CENTER_TYPE_FPS: camera.turn_eye(q.conjugate()); break; } } } void key(unsigned char key, int mouse_x, int mouse_y) { using namespace std; int mod = glutGetModifiers(); switch(key) { // ESC case char(27): rebar.save(REBAR_NAME); // ^C case char(3): exit(0); case 'z': case 'Z': if(mod & GLUT_ACTIVE_COMMAND) { if(mod & GLUT_ACTIVE_SHIFT) { redo(); }else { undo(); } break; } default: if(!TwEventKeyboardGLUT(key,mouse_x,mouse_y)) { cout<<"Unknown key command: "<<key<<" "<<int(key)<<endl; } } } int main(int argc, char * argv[]) { using namespace std; using namespace Eigen; using namespace igl; // print key commands cout<<"[Command+Z] Undo."<<endl; cout<<"[Shift+Command+Z] Redo."<<endl; cout<<"[^C,ESC] Exit."<<endl; if(!readOFF("../shared/cheburashka.off",V,F)) { cerr<<"Failed to read in mesh..."<<endl; return 1; } V.rowwise() -= V.colwise().minCoeff().eval(); per_face_normals(V,F,N); // Init glut glutInit(&argc,argv); if( !TwInit(TW_OPENGL, NULL) ) { // A fatal error occured fprintf(stderr, "AntTweakBar initialization failed: %s\n", TwGetLastError()); return 1; } // Create a tweak bar rebar.TwNewBar("bar"); TwDefine("bar label='camera' size='200 550' text=light alpha='200' color='68 68 68'"); TwType RotationTypeTW = ReTwDefineEnumFromString("RotationType","igl_trackball,two_axis_fixed_up"); rebar.TwAddVarRW("rotation_type", RotationTypeTW,&rotation_type, "keyIncr=] keyDecr=["); TwType CenterTypeTW = ReTwDefineEnumFromString("CenterType","orbit,fps"); rebar.TwAddVarRW("center_type", CenterTypeTW,¢er_type, "keyIncr={ keyDecr=}"); rebar.TwAddVarRW("rotation", TW_TYPE_QUAT4D,s.cameras[0].m_rotation_conj.coeffs().data(),""); rebar.load(REBAR_NAME); init_cameras(); // Init antweakbar glutInitDisplayString( "rgba depth double samples>=8"); // Top right corner glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT)/2.0); glutInitWindowPosition(glutGet(GLUT_SCREEN_WIDTH)/2.0,-1); glutCreateWindow("camera"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(key); glutMouseFunc(mouse); glutPassiveMotionFunc((GLUTmousemotionfun)TwEventMouseMotionGLUT); glutMotionFunc(mouse_drag); glutMainLoop(); return 0; }