/************************************************************************ File: TrainView.cpp Author: Michael Gleicher, gleicher@cs.wisc.edu Modifier Yu-Chi Lai, yu-chi@cs.wisc.edu Comment: The TrainView is the window that actually shows the train. Its a GL display canvas (Fl_Gl_Window). It is held within a TrainWindow that is the outer window with all the widgets. The TrainView needs to be aware of the window - since it might need to check the widgets to see how to draw Note: we need to have pointers to this, but maybe not know about it (beware circular references) Platform: Visio Studio.Net 2003/2005 *************************************************************************/ #include #include // we will need OpenGL, and OpenGL needs windows.h #include //#include "GL/gl.h" #include #include #include "GL/glu.h" #include "TrainView.H" #include "TrainWindow.H" #include "Utilities/3DUtils.H" #include using namespace std; #ifdef EXAMPLE_SOLUTION # include "TrainExample/TrainExample.H" #endif //************************************************************************ // // * Constructor to set up the GL window //======================================================================== TrainView:: TrainView(int x, int y, int w, int h, const char* l) : Fl_Gl_Window(x,y,w,h,l) //======================================================================== { mode( FL_RGB|FL_ALPHA|FL_DOUBLE | FL_STENCIL ); resetArcball(); } //************************************************************************ // // * Reset the camera to look at the world //======================================================================== void TrainView:: resetArcball() //======================================================================== { // Set up the camera to look at the world // these parameters might seem magical, and they kindof are // a little trial and error goes a long way arcball.setup(this, 40, 250, .2f, .4f, 0); } //************************************************************************ // // * FlTk Event handler for the window //######################################################################## // TODO: // if you want to make the train respond to other events // (like key presses), you might want to hack this. //######################################################################## //======================================================================== int TrainView::handle(int event) { // see if the ArcBall will handle the event - if it does, // then we're done // note: the arcball only gets the event if we're in world view if (tw->worldCam->value()) if (arcball.handle(event)) return 1; // remember what button was used static int last_push; switch(event) { // Mouse button being pushed event case FL_PUSH: last_push = Fl::event_button(); // if the left button be pushed is left mouse button if (last_push == FL_LEFT_MOUSE ) { doPick(); damage(1); return 1; }; break; // Mouse button release event case FL_RELEASE: // button release damage(1); last_push = 0; return 1; // Mouse button drag event case FL_DRAG: // Compute the new control point position if ((last_push == FL_LEFT_MOUSE) && (selectedCube >= 0)) { ControlPoint* cp = &m_pTrack->points[selectedCube]; double r1x, r1y, r1z, r2x, r2y, r2z; getMouseLine(r1x, r1y, r1z, r2x, r2y, r2z); double rx, ry, rz; mousePoleGo(r1x, r1y, r1z, r2x, r2y, r2z, static_cast(cp->pos.x), static_cast(cp->pos.y), static_cast(cp->pos.z), rx, ry, rz, (Fl::event_state() & FL_CTRL) != 0); cp->pos.x = (float) rx; cp->pos.y = (float) ry; cp->pos.z = (float) rz; damage(1); } break; // in order to get keyboard events, we need to accept focus case FL_FOCUS: return 1; // every time the mouse enters this window, aggressively take focus case FL_ENTER: focus(this); break; case FL_KEYBOARD: int k = Fl::event_key(); int ks = Fl::event_state(); if (k == 'p') { // Print out the selected control point information if (selectedCube >= 0) printf("Selected(%d) (%g %g %g) (%g %g %g)\n", selectedCube, m_pTrack->points[selectedCube].pos.x, m_pTrack->points[selectedCube].pos.y, m_pTrack->points[selectedCube].pos.z, m_pTrack->points[selectedCube].orient.x, m_pTrack->points[selectedCube].orient.y, m_pTrack->points[selectedCube].orient.z); else printf("Nothing Selected\n"); return 1; }; break; } return Fl_Gl_Window::handle(event); } //************************************************************************ // // * this is the code that actually draws the window // it puts a lot of the work into other routines to simplify things //======================================================================== void TrainView::draw() { //********************************************************************* // // * Set up basic opengl informaiton // //********************************************************************** //initialized glad if (gladLoadGL()) { //initiailize VAO, VBO, Shader... } else throw std::runtime_error("Could not initialize GLAD!"); // Set up the view port glViewport(0,0,w(),h()); // clear the window, be sure to clear the Z-Buffer too glClearColor(0,0,.3f,0); // background should be blue // we need to clear out the stencil buffer since we'll use // it for shadows glClearStencil(0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glEnable(GL_DEPTH); // Blayne prefers GL_DIFFUSE glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); // prepare for projection glMatrixMode(GL_PROJECTION); glLoadIdentity(); setProjection(); // put the code to set up matrices here //###################################################################### // TODO: // you might want to set the lighting up differently. if you do, // we need to set up the lights AFTER setting up the projection //###################################################################### // enable the lighting glEnable(GL_COLOR_MATERIAL); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); // top view only needs one light if (tw->topCam->value()) { glDisable(GL_LIGHT1); glDisable(GL_LIGHT2); } else { glEnable(GL_LIGHT1); glEnable(GL_LIGHT2); } //********************************************************************* // // * set the light parameters // //********************************************************************** GLfloat lightPosition1[] = {0,1,1,0}; // {50, 200.0, 50, 1.0}; GLfloat lightPosition2[] = {1, 0, 0, 0}; GLfloat lightPosition3[] = {0, -1, 0, 0}; GLfloat yellowLight[] = {0.5f, 0.5f, .1f, 1.0}; GLfloat whiteLight[] = {1.0f, 1.0f, 1.0f, 1.0}; GLfloat blueLight[] = {.1f,.1f,.3f,1.0}; GLfloat grayLight[] = {.3f, .3f, .3f, 1.0}; glLightfv(GL_LIGHT0, GL_POSITION, lightPosition1); glLightfv(GL_LIGHT0, GL_DIFFUSE, whiteLight); glLightfv(GL_LIGHT0, GL_AMBIENT, grayLight); glLightfv(GL_LIGHT1, GL_POSITION, lightPosition2); glLightfv(GL_LIGHT1, GL_DIFFUSE, yellowLight); glLightfv(GL_LIGHT2, GL_POSITION, lightPosition3); glLightfv(GL_LIGHT2, GL_DIFFUSE, blueLight); //********************************************************************* // now draw the ground plane //********************************************************************* // set to opengl fixed pipeline(use opengl 1.x draw function) glUseProgram(0); setupFloor(); glDisable(GL_LIGHTING); drawFloor(200,10); //********************************************************************* // now draw the object and we need to do it twice // once for real, and then once for shadows //********************************************************************* glEnable(GL_LIGHTING); setupObjects(); drawStuff(); // this time drawing is for shadows (except for top view) if (!tw->topCam->value()) { setupShadows(); drawStuff(true); unsetupShadows(); } } //************************************************************************ // // * This sets up both the Projection and the ModelView matrices // HOWEVER: it doesn't clear the projection first (the caller handles // that) - its important for picking //======================================================================== void TrainView:: setProjection() //======================================================================== { // Compute the aspect ratio (we'll need it) float aspect = static_cast(w()) / static_cast(h()); // Check whether we use the world camp if (tw->worldCam->value()) arcball.setProjection(false); // Or we use the top cam else if (tw->topCam->value()) { float wi, he; if (aspect >= 1) { wi = 110; he = wi / aspect; } else { he = 110; wi = he * aspect; } // Set up the top camera drop mode to be orthogonal and set // up proper projection matrix glMatrixMode(GL_PROJECTION); glOrtho(-wi, wi, -he, he, 200, -200); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(-90,1,0,0); } // Or do the train view or other view here //#################################################################### // TODO: // put code for train view projection here! //#################################################################### else { #ifdef EXAMPLE_SOLUTION trainCamView(this,aspect); #endif } } //************************************************************************ // // * this draws all of the stuff in the world // // NOTE: if you're drawing shadows, DO NOT set colors (otherwise, you get // colored shadows). this gets called twice per draw // -- once for the objects, once for the shadows //######################################################################## // TODO: // if you have other objects in the world, make sure to draw them //######################################################################## //======================================================================== void TrainView::drawStuff(bool doingShadows) { // Draw the control points // don't draw the control points if you're driving // (otherwise you get sea-sick as you drive through them) if (!tw->trainCam->value()) { for (size_t i = 0; i < m_pTrack->points.size(); ++i) { if (!doingShadows) { if (((int)i) != selectedCube) glColor3ub(240, 60, 60); else glColor3ub(240, 240, 30); } m_pTrack->points[i].draw(); } } // draw the track //#################################################################### // TODO: // call your own track drawing code //#################################################################### drawTrack(this, doingShadows); // draw the train //#################################################################### // TODO: // call your own train drawing code //#################################################################### //if (!tw->trainCam->value()) //drawTrain(this, doingShadows); } // //************************************************************************ // // * this tries to see which control point is under the mouse // (for when the mouse is clicked) // it uses OpenGL picking - which is always a trick //######################################################################## // TODO: // if you want to pick things other than control points, or you // changed how control points are drawn, you might need to change this //######################################################################## //======================================================================== void TrainView::drawTrack(TrainView* TrainV, bool doingShadows) { if (TrainV->tw->splineBrowser->value() == 1) { float DIVIDE_LINE = 20; for (size_t i = 0; i < m_pTrack->points.size(); i++) { // pos Pnt3f cp_pos_p1 = m_pTrack->points[i].pos; Pnt3f cp_pos_p2 = m_pTrack->points[(i + 1) % m_pTrack->points.size()].pos; // orient Pnt3f cp_orient_p1 = m_pTrack->points[i].orient; Pnt3f cp_orient_p2 = m_pTrack->points[(i + 1) % m_pTrack->points.size()].orient; float percent = 1.0f / DIVIDE_LINE; float t = 0; Pnt3f qt = (1 - t) * cp_pos_p1 + t * cp_pos_p2; for (size_t j = 0; j < DIVIDE_LINE; j++) { Pnt3f qt0 = qt; t += percent; qt = (1 - t) * cp_pos_p1 + t * cp_pos_p2; Pnt3f qt1 = qt; Pnt3f orient_t = (1 - t) * cp_orient_p1 + t * cp_orient_p2; orient_t.normalize(); Pnt3f cross_t = (qt1 - qt0) * orient_t; cross_t.normalize(); cross_t = cross_t * 2.5f; if (!doingShadows) { glColor3ub(77, 19, 0); } glBegin(GL_LINES); glVertex3f(qt0.x + cross_t.x, qt0.y + cross_t.y, qt0.z + cross_t.z); glVertex3f(qt1.x + cross_t.x, qt1.y + cross_t.y, qt1.z + cross_t.z); glVertex3f(qt0.x - cross_t.x, qt0.y - cross_t.y, qt0.z - cross_t.z); glVertex3f(qt1.x - cross_t.x, qt1.y - cross_t.y, qt1.z - cross_t.z); glEnd(); glLineWidth(1); if (j % 2 == 0) { if (!doingShadows) { glColor3ub(153, 102, 51); } glBegin(GL_QUADS); glVertex3f(qt0.x + cross_t.x, qt0.y + cross_t.y, qt0.z + cross_t.z); glVertex3f(qt1.x + cross_t.x, qt1.y + cross_t.y, qt1.z + cross_t.z); glVertex3f(qt1.x - cross_t.x, qt1.y + cross_t.y, qt1.z - cross_t.z); glVertex3f(qt0.x - cross_t.x, qt0.y + cross_t.y, qt0.z - cross_t.z); glEnd(); } } } } else if (TrainV->tw->splineBrowser->value() == 2) { float DIVIDE_LINE = 20; for (size_t i = 0; i < m_pTrack->points.size(); i++) { // pos Pnt3f cp_pos_p1 = m_pTrack->points[(i - 1 + m_pTrack->points.size()) % m_pTrack->points.size()].pos; Pnt3f cp_pos_p2 = m_pTrack->points[(i + m_pTrack->points.size()) % m_pTrack->points.size()].pos; Pnt3f cp_pos_p3 = m_pTrack->points[(i + 1 + m_pTrack->points.size()) % m_pTrack->points.size()].pos; Pnt3f cp_pos_p4 = m_pTrack->points[(i + 2 + m_pTrack->points.size()) % m_pTrack->points.size()].pos; // orient Pnt3f cp_orient_p1 = m_pTrack->points[(i - 1 + m_pTrack->points.size()) % m_pTrack->points.size()].orient; Pnt3f cp_orient_p2 = m_pTrack->points[(i + m_pTrack->points.size()) % m_pTrack->points.size()].orient; Pnt3f cp_orient_p3 = m_pTrack->points[(i + 1 + m_pTrack->points.size()) % m_pTrack->points.size()].orient; Pnt3f cp_orient_p4 = m_pTrack->points[(i + 2 + m_pTrack->points.size()) % m_pTrack->points.size()].orient; glm::mat4x4 P = { {cp_pos_p1.x,cp_pos_p1.y,cp_pos_p1.z,1}, {cp_pos_p2.x,cp_pos_p2.y,cp_pos_p2.z,1}, {cp_pos_p3.x,cp_pos_p3.y,cp_pos_p3.z,1}, {cp_pos_p4.x,cp_pos_p4.y,cp_pos_p4.z,1} }; glm::mat4x4 O = { {cp_orient_p1.x,cp_orient_p1.y,cp_orient_p1.z,1}, {cp_orient_p2.x,cp_orient_p2.y,cp_orient_p2.z,1}, {cp_orient_p3.x,cp_orient_p3.y,cp_orient_p3.z,1}, {cp_orient_p4.x,cp_orient_p4.y,cp_orient_p4.z,1} }; /*glm::mat4x4 M = { {-1.0f,3.0f,-3.0f,1.0f}, {3.0f,-6.0f,0.0f,4.0f}, {-3.0f,3.0f,3.0f,1.0f}, {1.0f,0.0f,0.0f,0.0f} };*/ glm::mat4x4 M = { {-1.0f,3.0f,-3.0f,1.0f}, {2.0f,-5.0f,4.0f,-1.0f}, {-1.0f,0.0f,1.0f,0.0f}, {0.0f,2.0f,0.0f,0.0f} }; M /= 2; float percent = 1.0f / DIVIDE_LINE; float t = 0; for (size_t j = 0; j < DIVIDE_LINE; j++) { glm::vec4 T0 = { pow(t,3),pow(t,2),t,1 }; glm::vec4 qtemp = P * M * T0; glm::vec4 ot0 = O * M * T0; Pnt3f qt0(qtemp[0], qtemp[1], qtemp[2]); t += percent; glm::vec4 T1 = { pow(t,3),pow(t,2),t,1 }; qtemp = P * M * T1; Pnt3f qt1(qtemp[0], qtemp[1], qtemp[2]); Pnt3f orient_t(ot0[0], ot0[1], ot0[2]); orient_t.normalize(); Pnt3f cross_t = (qt1 - qt0) * orient_t; cross_t.normalize(); cross_t = cross_t * 2.5f; if (!doingShadows) { glColor3ub(77, 19, 0); } glBegin(GL_LINES); glVertex3f(qt0.x + cross_t.x, qt0.y + cross_t.y, qt0.z + cross_t.z); glVertex3f(qt1.x + cross_t.x, qt1.y + cross_t.y, qt1.z + cross_t.z); glVertex3f(qt0.x - cross_t.x, qt0.y - cross_t.y, qt0.z - cross_t.z); glVertex3f(qt1.x - cross_t.x, qt1.y - cross_t.y, qt1.z - cross_t.z); glEnd(); glLineWidth(1); if (j % 2 == 0) { if (!doingShadows) { glColor3ub(153, 102, 51); } glBegin(GL_QUADS); glVertex3f(qt0.x + cross_t.x, qt0.y + cross_t.y, qt0.z + cross_t.z); glVertex3f(qt1.x + cross_t.x, qt1.y + cross_t.y, qt1.z + cross_t.z); glVertex3f(qt1.x - cross_t.x, qt1.y + cross_t.y, qt1.z - cross_t.z); glVertex3f(qt0.x - cross_t.x, qt0.y + cross_t.y, qt0.z - cross_t.z); glEnd(); } } } } else if (TrainV->tw->splineBrowser->value() == 3) { float DIVIDE_LINE = 20; for (size_t i = 0; i < m_pTrack->points.size(); i++) { // pos Pnt3f cp_pos_p1 = m_pTrack->points[(i - 1 + m_pTrack->points.size()) % m_pTrack->points.size()].pos; Pnt3f cp_pos_p2 = m_pTrack->points[(i + m_pTrack->points.size()) % m_pTrack->points.size()].pos; Pnt3f cp_pos_p3 = m_pTrack->points[(i +1 + m_pTrack->points.size()) % m_pTrack->points.size()].pos; Pnt3f cp_pos_p4 = m_pTrack->points[(i +2 + m_pTrack->points.size()) % m_pTrack->points.size()].pos; // orient Pnt3f cp_orient_p1 = m_pTrack->points[(i -1 + m_pTrack->points.size()) % m_pTrack->points.size()].orient; Pnt3f cp_orient_p2 = m_pTrack->points[(i + m_pTrack->points.size()) % m_pTrack->points.size()].orient; Pnt3f cp_orient_p3 = m_pTrack->points[(i + 1 + m_pTrack->points.size()) % m_pTrack->points.size()].orient; Pnt3f cp_orient_p4 = m_pTrack->points[(i + 2 + m_pTrack->points.size()) % m_pTrack->points.size()].orient; glm::mat4x4 P = { {cp_pos_p1.x,cp_pos_p1.y,cp_pos_p1.z,1}, {cp_pos_p2.x,cp_pos_p2.y,cp_pos_p2.z,1}, {cp_pos_p3.x,cp_pos_p3.y,cp_pos_p3.z,1}, {cp_pos_p4.x,cp_pos_p4.y,cp_pos_p4.z,1} }; glm::mat4x4 O = { {cp_orient_p1.x,cp_orient_p1.y,cp_orient_p1.z,1}, {cp_orient_p2.x,cp_orient_p2.y,cp_orient_p2.z,1}, {cp_orient_p3.x,cp_orient_p3.y,cp_orient_p3.z,1}, {cp_orient_p4.x,cp_orient_p4.y,cp_orient_p4.z,1} }; glm::mat4x4 M = { {-1.0f,3.0f,-3.0f,1.0f}, {3.0f,-6.0f,3.0f,0.0f}, {-3.0f,0.0f,3.0f,0.0f}, {1.0f,4.0f,1.0f,0.0f} }; M /= 6; float percent = 1.0f / DIVIDE_LINE; float t = 0; for (size_t j = 0; j < DIVIDE_LINE; j++) { glm::vec4 T0 = { pow(t,3),pow(t,2),t,1 }; glm::vec4 qtemp = P * M * T0; glm::vec4 ot0 = O * M * T0; Pnt3f qt0(qtemp[0], qtemp[1], qtemp[2]); t += percent; glm::vec4 T1 = { pow(t,3),pow(t,2),t,1 }; qtemp = P * M * T1; Pnt3f qt1(qtemp[0] ,qtemp[1],qtemp[2]); Pnt3f orient_t(ot0[0], ot0[1], ot0[2]); orient_t.normalize(); Pnt3f cross_t = (qt1 - qt0) * orient_t; cross_t.normalize(); cross_t = cross_t * 2.5f; if (!doingShadows) { glColor3ub(77, 19, 0); } glBegin(GL_LINES); glVertex3f(qt0.x + cross_t.x, qt0.y + cross_t.y, qt0.z + cross_t.z); glVertex3f(qt1.x + cross_t.x, qt1.y + cross_t.y, qt1.z + cross_t.z); glVertex3f(qt0.x - cross_t.x, qt0.y - cross_t.y, qt0.z - cross_t.z); glVertex3f(qt1.x - cross_t.x, qt1.y - cross_t.y, qt1.z - cross_t.z); glEnd(); glLineWidth(1); if (j % 2 == 0) { if (!doingShadows) { glColor3ub(153, 102, 51); } glBegin(GL_QUADS); glVertex3f(qt0.x + cross_t.x, qt0.y + cross_t.y, qt0.z + cross_t.z); glVertex3f(qt1.x + cross_t.x, qt1.y + cross_t.y, qt1.z + cross_t.z); glVertex3f(qt1.x - cross_t.x, qt1.y + cross_t.y, qt1.z - cross_t.z); glVertex3f(qt0.x - cross_t.x, qt0.y + cross_t.y, qt0.z - cross_t.z); glEnd(); } } } } } //void TrainView::drawTrain(TrainView* train, bool doingShadows) { //} void TrainView:: doPick() //======================================================================== { // since we'll need to do some GL stuff so we make this window as // active window make_current(); // where is the mouse? int mx = Fl::event_x(); int my = Fl::event_y(); // get the viewport - most reliable way to turn mouse coords into GL coords int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); // Set up the pick matrix on the stack - remember, FlTk is // upside down! glMatrixMode(GL_PROJECTION); glLoadIdentity (); gluPickMatrix((double)mx, (double)(viewport[3]-my), 5, 5, viewport); // now set up the projection setProjection(); // now draw the objects - but really only see what we hit GLuint buf[100]; glSelectBuffer(100,buf); glRenderMode(GL_SELECT); glInitNames(); glPushName(0); // draw the cubes, loading the names as we go for(size_t i=0; ipoints.size(); ++i) { glLoadName((GLuint) (i+1)); m_pTrack->points[i].draw(); } // go back to drawing mode, and see how picking did int hits = glRenderMode(GL_RENDER); if (hits) { // warning; this just grabs the first object hit - if there // are multiple objects, you really want to pick the closest // one - see the OpenGL manual // remember: we load names that are one more than the index selectedCube = buf[3]-1; } else // nothing hit, nothing selected selectedCube = -1; printf("Selected Cube %d\n",selectedCube); }