www.pudn.com > terrain.rar > OpenGLView.cpp
// Copyright 1996, Microsoft Systems Journal. ///////////////////////////////////////////////////////////////////////////// // // COpenGLView.cpp : implementation of the COpenGLView class // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" //#include "resource.h" // main symbols #include "COpenGLView.h" #include// for MM timers (you'll need WINMM.LIB) #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // add support for OpenGL 1.1 if we're using an old header // These are new PIXELFORMATDESCRIPTOR flags for OpenGL 1.1 #ifndef PFD_GENERIC_ACCELERATED #define PFD_GENERIC_ACCELERATED 0x00001000 #endif #ifndef PFD_DEPTH_DONTCARE #define PFD_DEPTH_DONTCARE 0x20000000 #endif #define INSTALLABLE_DRIVER_TYPE_MASK (PFD_GENERIC_ACCELERATED|PFD_GENERIC_FORMAT) ///////////////////////////////////////////////////////////////////////////// const char* const COpenGLView::_ErrorStrings[]= { {"No Error"}, // 0 {"Unable to get a DC"}, // 1 {"ChoosePixelFormat failed"}, // 2 {"SelectPixelFormat failed"}, // 3 {"wglCreateContext failed"}, // 4 {"wglMakeCurrent failed"}, // 5 {"wglDeleteContext failed"}, // 6 {"SwapBuffers failed"}, // 7 }; ///////////////////////////////////////////////////////////////////////////// // COpenGLView IMPLEMENT_DYNCREATE(COpenGLView, CView) BEGIN_MESSAGE_MAP(COpenGLView, CView) //{{AFX_MSG_MAP(COpenGLView) ON_WM_CREATE() ON_WM_DESTROY() ON_WM_ERASEBKGND() ON_WM_SIZE() // If you don't have an ID_ANIMATE, you can either add one // to your menu (Add an Animate command) or comment out the // references //#if defined ID_ANIMATE // ON_COMMAND(ID_ANIMATE, OnAnimate) // ON_UPDATE_COMMAND_UI(ID_ANIMATE, OnUpdateAnimate) //#else // #pragma message( "No Animation Accelerator Interface Defined in COpenGLView" ) //#endif ON_WM_KEYDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COpenGLView construction/destruction COpenGLView::COpenGLView() : m_PixelFormat(0),m_hRC(0), m_pDC(0), m_ErrorString(_ErrorStrings[0]) { // TODO: add construction code here } ///////////////////////////////////////////////////////////////////////////// COpenGLView::~COpenGLView() { } ///////////////////////////////////////////////////////////////////////////// BOOL COpenGLView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Add your specialized code here and/or call the base class // An OpenGL window must be created with the following flags and must not // include CS_PARENTDC for the class style. Refer to SetPixelFormat // documentation in the "Comments" section for further information. cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; return CView::PreCreateWindow(cs); } ///////////////////////////////////////////////////////////////////////////// // COpenGLView drawing void COpenGLView::OnDraw(CDC* pDC) { CDocument* pDoc = GetDocument(); ASSERT_VALID(pDoc); // Clear out the color & depth buffers ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); PreRenderScene(); ::glPushMatrix(); RenderStockScene(); ::glPopMatrix(); ::glPushMatrix(); RenderScene(); ::glPopMatrix(); // Tell OpenGL to flush its pipeline ::glFinish(); // Now Swap the buffers if ( FALSE == ::SwapBuffers( m_pDC->GetSafeHdc() ) ) { SetError(7); } PostRenderScene(); } ////////////////////////////////////////////////////////////////////////////// // PostRenderScene // perform post display processing // // The default PostRenderScene places the framerate in the // view's title. Replace this with your own title if you like. void COpenGLView::PostRenderScene( void ) { } ///////////////////////////////////////////////////////////////////////////// // COpenGLView diagnostics #ifdef _DEBUG ///////////////////////////////////////////////////////////////////////////// void COpenGLView::AssertValid() const { CView::AssertValid(); } ///////////////////////////////////////////////////////////////////////////// void COpenGLView::Dump(CDumpContext& dc) const { CView::Dump(dc); } ///////////////////////////////////////////////////////////////////////////// CDocument* COpenGLView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDocument))); return (CDocument*)m_pDocument; } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// int COpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; InitializeOpenGL(); return 0; } ///////////////////////////////////////////////////////////////////////////// void COpenGLView::OnDestroy() { CView::OnDestroy(); // TODO: Add your message handler code here // NOTE:wglDeleteContext makes the RC non-current, so this step is unnecessary // (but you can do it if it makes you feel secure) // if ( FALSE == ::wglMakeCurrent( 0, 0 ) ) // { // SetError(2); // return FALSE; // } if ( FALSE == ::wglDeleteContext( m_hRC ) ) { SetError(6); } // For Color-Index mode, you should reset the palette to the original here if ( m_pDC ) { delete m_pDC; } } ///////////////////////////////////////////////////////////////////////////// // OnEraseBkgnd // OpenGL has its own routine to erase the background. Here we tell MFC // not to do it, that we'll take care of it. If we didn't the scene might // flash. BOOL COpenGLView::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default // return CView::OnEraseBkgnd(pDC); return TRUE; // tell Windows not to erase the background } ///////////////////////////////////////////////////////////////////////////// // OnSize // We need to set up the viewport when the size changes, and this is the best // place for it, as long as you don't need to render more than one scene, in which // case you'd have to do it just before each scene gets rendered. // We also set up the viewing volumn here since we're using perspective mode. For // Orthographic you could do it anywhere since you don't need the aspect ratio. // Finally we also set up the default veiwing transform. For an animated scene you'd // have to do it just before the scene was rendered. void COpenGLView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); // TODO: Add your message handler code here GLdouble aspect_ratio; // width/height ratio if ( 0 >= cx || 0 >= cy ) { return; } SetupViewport( cx, cy ); // compute the aspect ratio // this will keep all dimension scales equal aspect_ratio = (GLdouble)cx/(GLdouble)cy; // select the projection matrix and clear it ::glMatrixMode(GL_PROJECTION); ::glLoadIdentity(); // select the viewing volumn SetupViewingFrustum( aspect_ratio ); // switch back to the modelview matrix and clear it ::glMatrixMode(GL_MODELVIEW); ::glLoadIdentity(); // now perform any viewing transformations SetupViewingTransform(); } ////////////////////////////////////////////////////////////////////////////// // SetError-error string manipulation ///////////////////////////////////////////////////////////////////////////// void COpenGLView::SetError( int e ) { // if there was no previous error, // then save this one if ( _ErrorStrings[0] == m_ErrorString ) { m_ErrorString = _ErrorStrings[e]; } } ////////////////////////////////////////////////////////////////////////////// // InitializeOpenGL // - just that. This is set up for RGB mode, though I've indicated // where you would add code for color-index mode. BOOL COpenGLView::InitializeOpenGL() { // Can we put this in the constructor? m_pDC = new CClientDC(this); if ( NULL == m_pDC ) // failure to get DC { SetError(1); return FALSE; } if (!SetupPixelFormat()) { return FALSE; } // For Color-Index mode, you'd probably create your palette here, right // after you select the pixel format if ( 0 == (m_hRC = ::wglCreateContext( m_pDC->GetSafeHdc() ) ) ) { SetError(4); return FALSE; } if ( FALSE == ::wglMakeCurrent( m_pDC->GetSafeHdc(), m_hRC ) ) { SetError(5); return FALSE; } // specify black as clear color ::glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // specify the back of the buffer as clear depth ::glClearDepth(1.0f); // enable depth testing ::glEnable(GL_DEPTH_TEST); return TRUE; } ///////////////////////////////////////////////////////////////////////////// BOOL COpenGLView::SetupPixelFormat() { static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | // support OpenGL PFD_DOUBLEBUFFER, // double buffered PFD_TYPE_RGBA, // RGBA type 24, // 24-bit color depth 0, 0, 0, 0, 0, 0, // color bits ignored 0, // no alpha buffer 0, // shift bit ignored 0, // no accumulation buffer 0, 0, 0, 0, // accum bits ignored 16, // 16-bit z-buffer 0, // no stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; if ( 0 == (m_PixelFormat = ::ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd)) ) { SetError(2); return FALSE; } if ( FALSE == ::SetPixelFormat(m_pDC->GetSafeHdc(), m_PixelFormat, &pfd) ) { SetError(3); return FALSE; } return TRUE; } ///////////////////////////////////////////////////////////////////////////// // SetupViewport BOOL COpenGLView::SetupViewport( int cx, int cy ) { // select the full client area ::glViewport(0, 0, cx, cy); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // SetupViewingFrustum BOOL COpenGLView::SetupViewingFrustum( GLdouble aspect_ratio ) { // select a default viewing volumn ::gluPerspective(40.0f, aspect_ratio, .1f, 20.0f); // here's an ortho view // glOrtho( -2.0f, 2.0f, -2.0f, 2.0f, -.10f, 20.0f ); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // SetupViewingTransform // This is a static, default viewing transform BOOL COpenGLView::SetupViewingTransform() { // select a default viewing transformation // of a 20 degree rotation about the X axis // then a -5 unit transformation along Z // (It's as good as any I guess...) ::glTranslatef( 0.0f, 0.0f, -5.0f ); ::glRotatef( 20.0f, 1.0f, 0.0f, 0.0f ); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // RenderScene // This is the default scene for the COpenGLView class. // draw a red wire sphere inside a light blue cube BOOL COpenGLView::RenderScene() { // rotate the wire sphere so it's vertically // oriented ::glRotatef( 90.0f, 1.0f, 0.0f, 0.0f ); ::glColor3f( 1.0f, 0.0f, 0.0f ); //::auxWireSphere( .5 ); ::glColor3f( 0.5f, 0.5f, 1.0f ); //::auxWireCube( 1.0 ); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // RenderStockScene void COpenGLView::RenderStockScene() { // define all vertices X Y Z GLfloat v0[3], v1[3], v2[3], v3[3], delta; int color = 0; delta = 0.5f; // define the two colors GLfloat color1[3] = { 0.9f, 0.9f, 0.9f }; GLfloat color2[3] = { 0.05f, 0.05f, 0.05f }; v0[1] = v1[1] = v2[1] = v3[1] = 0.0f; ::glBegin( GL_QUADS ); for ( int x = -5 ; x <= 5 ; x++ ) { for ( int z = -5 ; z <= 5 ; z++ ) { ::glColor3fv( (color++)%2 ? color1 : color2 ); v0[0] = 0.0f+delta*z; v0[2] = 0.0f+delta*x; v1[0] = v0[0]+delta; v1[2] = v0[2]; v2[0] = v0[0]+delta; v2[2] = v0[2]+delta; v3[0] = v0[0]; v3[2] = v0[2]+delta; ::glVertex3fv( v0 ); ::glVertex3fv( v1 ); ::glVertex3fv( v2 ); ::glVertex3fv( v3 ); } } ::glEnd(); } ///////////////////////////////////////////////////////////////////////////// // Draw3DAxes // Draws lines along the current 3 axes from "start" units to "finish", with // "ticks" tickmarks spaced out along it. void COpenGLView::Draw3DAxes( float start, float finish, int ticks ) { // make sure that start < finish if ( start > finish ) { float temp = start; start = finish; finish = start; } // if ticks < 0 and delta is larger than 1, place the ticks // on each scales unit length if ( 0 > ticks ) { float delta = finish-start; ticks = delta > 1.0 ? (int)delta : 0; } // draw the tickmarked axes Draw3DAxesLine( start, finish, 0, ticks ); Draw3DAxesLine( start, finish, 1, ticks ); Draw3DAxesLine( start, finish, 2, ticks ); } ///////////////////////////////////////////////////////////////////////////// // Draw3DAxesLine // This routine draws a colored line along a specified axis. // axis_id = 0 for the x, 1 for the y, and anything else for the z // start and finish are the starting and ending location, start < finish. // ticks is the number of ticks to place along the axis. // If you are using lighting/materials, you might want to wrapper this routine // so that it's called with lighting disabled, or else the axis lines will be effected // by lighting claculations - which generally means hard to see. void COpenGLView::Draw3DAxesLine( float start, float finish, int axis_id, int ticks ) { float *px, *py, *pz, zero = 0.0f; float tickx, ticky, tickz; float *pdx, *pdy, *pdz, tinytick, delta = (finish-start)/(ticks<1?1:ticks); GLfloat negativeColor[3] = { 1.0f, 0.0f, 0.0f }; GLfloat positiveColor[3] = { 0.0f, 1.0f, 0.0f }; pdx = pdy = pdz = px = py = pz = &zero; tickx = ticky = tickz = 0.0f; tinytick = 0.05f; // select which of the 3 axes is going to vary if ( 0 == axis_id ) // X axis { pdx = δ ticky = tinytick; px = &start; } else if ( 1 == axis_id ) // Y axis { pdy = δ tickx = tinytick; py = &start; } else // default Z axis { pdz = δ ticky = tinytick; pz = &start; } // turn off the lighting effects // since we don't want the axes lines effected by the // lighting. You might need to call ::glDisable(GL_LIGHTING) // before this routine ::glBegin(GL_LINES); // now draw the two lines that make up the axis ::glColor3fv( negativeColor ); // negative color ::glVertex3f( *px, *py, *pz ); ::glVertex3f( 0.0f, 0.0f, 0.0f ); ::glColor3fv( positiveColor ); // positive color ::glVertex3f( 0.0f, 0.0f, 0.0f ); ::glVertex3f( *px+*pdx*ticks, *py+*pdy*ticks, *pz+*pdz*ticks ); // now draw the tick marks for (int i = 0; i < ticks ; i++ ) { if ( i < ticks/2 ) { ::glColor3fv( negativeColor ); } else { ::glColor3fv( positiveColor ); } ::glVertex3f( *px-tickx, *py-ticky, *pz-tickz ); ::glVertex3f( *px+tickx, *py+ticky, *pz+tickz ); *px += *pdx; *py += *pdy; *pz += *pdz; } ::glEnd(); // don't forget to turn lighting effects back on // via glEnable(GL_LIGHTING) } ///////////////////////////////////////////////////////////////////////////// // OnKeyDown // This routine runs the keyboard interface. // Most of the keys accepted are from the numeric keypad // HOME: Resets viewpoint to initial values (animation keeps going) // SHIFT-HOME: reset viewpoint & clear movement vectors // 5: Clear movement vectors // Up/Down Arrows: change velocity in Z (if shifted, change rotation) // Left/Right Arrows: change velocity in X (if shifted, change rotation) // Plus/Minus: change velocity in Y (if shifted, change rotation) // ESC: Exit animation void COpenGLView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch ( nChar ) { case VK_ESCAPE: // close down the app GetParent()->PostMessage(WM_CLOSE); return; default: return; } // Probably don't ever need this //CView::OnKeyDown(nChar, nRepCnt, nFlags); }