www.pudn.com > c++25.rar > OpenGLWnd.cpp


// OpenGLWnd.cpp : implementation file 
// 
 
#include "stdafx.h" 
#include "OpenGLTest.h" 
#include "OpenGLWnd.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
 
#define MAX_LISTS 20 
// used to identify a MCD video driver (partial OGL acceleration) 
#define INSTALLABLE_DRIVER_TYPE_MASK  (PFD_GENERIC_ACCELERATED|PFD_GENERIC_FORMAT) 
 
///////////////////////////////////////////////////////////////////////////// 
// Global Functions/variables 
// These functions are used by CGLTesselator class 
 
struct GarbListItem{ 
	GLdouble *pvert; 
	GarbListItem* next;}; 
 
GarbListItem* m_garbagelist=NULL; 
 
void AddGarbage(GLdouble * ptr) 
{ 
	ASSERT(ptr!=NULL); 
// allocate mem for new list item 
	GarbListItem* temp=new GarbListItem; 
// store pointer 
	temp->pvert=ptr; 
// add at head of list 
	temp->next=m_garbagelist; 
	m_garbagelist=temp; 
} 
 
void DeleteGarbage() 
{ 
	if(m_garbagelist!=NULL) 
	{ 
		GarbListItem* punt=m_garbagelist; 
		GarbListItem* temp=m_garbagelist; 
// scan the list 
		while(punt!=NULL) 
		{ 
// delete vertex 
			delete[] punt->pvert; 
			punt=punt->next; 
// delete list item 
			delete temp; 
			temp=punt; 
		}; 
		m_garbagelist=NULL; 
	}; 
} 
 
void CALLBACK BeginCallback(GLenum type) 
{ 
// issue corresponding GL call 
	glBegin(type); 
} 
 
void CALLBACK ErrorCallback(GLenum errorCode) 
{ 
	const GLubyte *estring; 
	CString mexstr; 
// get the error descritption from OGL 
	estring = gluErrorString(errorCode); 
// prepare and show a message box 
	mexstr.Format("Tessellation/Quadric Error: %s\n", estring); 
	AfxMessageBox(mexstr,MB_OK | MB_ICONEXCLAMATION); 
// replicate mex to debug trace 
	TRACE("Tessellation Error: %s\n", estring); 
} 
 
void CALLBACK EndCallback() 
{ 
// issue corresponding GL call 
	glEnd(); 
} 
 
void CALLBACK VertexCallback(GLvoid *vertex) 
{ 
// issue corresponding GL call (double is used to get max precision) 
	glVertex3dv( (const double *)vertex ); 
} 
 
void CALLBACK CombineCallback(GLdouble coords[3], GLdouble *data[4], GLfloat weight[4], GLdouble **dataOut ) 
{ 
// allocate memory for a new vertex 
	GLdouble *vertex; 
	vertex = new GLdouble[3]; 
// store reported vertex 
	vertex[0] = coords[0]; 
	vertex[1] = coords[1]; 
	vertex[2] = coords[2]; 
// return vertex to OGL 
	*dataOut = vertex; 
// add vertex pointer to garbage collection routines 
	AddGarbage(vertex); 
} 
 
 
// these are used to construct an equilibrated 256 color palette 
static unsigned char _threeto8[8] =  
{ 
	0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377 
}; 
 
static unsigned char _twoto8[4] =  
{ 
	0, 0x55, 0xaa, 0xff 
}; 
 
static unsigned char _oneto8[2] =  
{ 
	0, 255 
}; 
 
static int defaultOverride[13] =  
{ 
	0, 3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91 
}; 
 
// Windows Default Palette 
static PALETTEENTRY defaultPalEntry[20] =  
{ 
	{ 0,   0,   0,    0 }, 
	{ 0x80,0,   0,    0 }, 
	{ 0,   0x80,0,    0 }, 
	{ 0x80,0x80,0,    0 }, 
	{ 0,   0,   0x80, 0 }, 
	{ 0x80,0,   0x80, 0 }, 
	{ 0,   0x80,0x80, 0 }, 
	{ 0xC0,0xC0,0xC0, 0 }, 
 
	{ 192, 220, 192,  0 }, 
	{ 166, 202, 240,  0 }, 
	{ 255, 251, 240,  0 }, 
	{ 160, 160, 164,  0 }, 
 
	{ 0x80,0x80,0x80, 0 }, 
	{ 0xFF,0,   0,    0 }, 
	{ 0,   0xFF,0,    0 }, 
	{ 0xFF,0xFF,0,    0 }, 
	{ 0,   0,   0xFF, 0 }, 
	{ 0xFF,0,   0xFF, 0 }, 
	{ 0,   0xFF,0xFF, 0 }, 
	{ 0xFF,0xFF,0xFF, 0 } 
}; 
 
 
 
///////////////////////////////////////////////////////////////////////////// 
// COpenGLWnd 
 
COpenGLWnd::COpenGLWnd() : 
	m_dAspectRatio(1.0), 
	m_bInsideDispList(FALSE), m_bExternDispListCall(FALSE), 
	m_bExternGLCall(FALSE) 
{ 
// define a default cursor 
	m_hMouseCursor=AfxGetApp()->LoadStandardCursor(IDC_SIZEALL); 
// set the disp list vector to all zeros 
	for (int c=0;cGetSafeHdc()==wglGetCurrentDC() )) 
		{ 
// ...if not specify the target DeviceContext of the subsequent OGL calls 
			wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); 
// set a warning for EndDispList 
			m_bExternDispListCall=TRUE; 
		}; 
// create a handle to the disp list (actually an integer) 
		m_DispListVector[c]=glGenLists(1); 
// set a semaphore 
		m_bInsideDispList=TRUE; 
// start the disp list: all subsequent OGL calls will be redirected to the list 
		glNewList(m_DispListVector[c],GL_COMPILE); 
	};	 
} 
 
void COpenGLWnd::EndStockListDef() 
{ 
// close the disp list 
	glEndList(); 
// unset the semaphore 
	m_bInsideDispList=FALSE; 
// if beginDispList set the warn free the target DeviceContext 
	if(m_bExternDispListCall) wglMakeCurrent(NULL,NULL);	 
} 
 
void COpenGLWnd::DrawStockDispLists() 
{ 
// check if we are already inside a drawing session 
	if(m_hRC==wglGetCurrentContext() && m_pCDC->GetSafeHdc()==wglGetCurrentDC() ) 
	{ 
// draw directly all display lists 
		for (int c=0;cGetSafeHdc(), m_hRC); 
// draw all display lists 
		for (int c=0;cGetSafeHdc()==wglGetCurrentDC() ) 
	{ 
// delete active display lists 
		for (int c=0;cGetSafeHdc(), m_hRC); 
// delete active display lists 
		for (int c=0;cGetSafeHdc(), ::GetPixelFormat(m_pCDC->GetSafeHdc()),sizeof(PIXELFORMATDESCRIPTOR), &pfd );  
 
// specify the target DeviceContext of the subsequent OGL calls 
	wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); 
 
	switch(type) 
	{ 
// Derive driver information 
	case ACCELERATION: if( 0==(INSTALLABLE_DRIVER_TYPE_MASK & pfd.dwFlags) ) str="Fully Accelerated (ICD)"; // fully in hardware (fastest) 
						else if (INSTALLABLE_DRIVER_TYPE_MASK==(INSTALLABLE_DRIVER_TYPE_MASK & pfd.dwFlags) ) str="Partially Accelerated (MCD)"; // partially in hardware (pretty fast, maybe..) 
							 else str="Not Accelerated (Software)";	// software 
						break; 
// get the company name responsible for this implementation 
	case VENDOR:str=(char*)::glGetString(GL_VENDOR); 
				if ( ::glGetError()!=GL_NO_ERROR) str.Format("Not Available");// failed! 
				break; 
// get the renderer name; this is specific of an hardware configuration 
	case RENDERER:str=(char*)::glGetString(GL_RENDERER); 
					if ( ::glGetError()!=GL_NO_ERROR) str.Format("Not Available");// failed! 
					break; 
// get the version 
	case VERSION:str=(char*)::glGetString(GL_VERSION); 
				if ( ::glGetError()!=GL_NO_ERROR) str.Format("Not Available");// failed! 
				break; 
// return a space separated list of extensions 
	case EXTENSIONS: str=(char*)::glGetString(GL_EXTENSIONS); 
				if ( ::glGetError()!=GL_NO_ERROR) str.Format("Not Available");// failed! 
				break; 
	}; 
 
// free the target DeviceContext (window) and return the result 
	wglMakeCurrent(NULL,NULL); 
	return str;	 
} 
 
void COpenGLWnd::SetMouseCursor(HCURSOR mcursor) 
{ 
// set the specified cursor (only if it is a valid one) 
	if(mcursor!=NULL) m_hMouseCursor=mcursor;	 
} 
 
void COpenGLWnd::BeginGLCommands() 
{ 
// check if we are inside a drawing session or not.... 
	if(!( m_hRC==wglGetCurrentContext() && m_pCDC->GetSafeHdc()==wglGetCurrentDC() )) 
	{ 
// ...if not specify the target DeviceContext of the subsequent OGL calls 
		wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); 
// set a warning for EndGLCommands 
		m_bExternGLCall=TRUE; 
	};	 
} 
 
void COpenGLWnd::EndGLCommands() 
{ 
// if BeginGLCommands set the warn free the target DeviceContext 
	if(m_bExternGLCall) wglMakeCurrent(NULL,NULL); 
	 
} 
 
void COpenGLWnd::OnCreateGL() 
{ 
// perform hidden line/surface removal (enabling Z-Buffer) 
	glEnable(GL_DEPTH_TEST); 
 
// set background color to black 
	glClearColor(0.f,0.f,0.f,1.0f ); 
 
// set clear Z-Buffer value 
	glClearDepth(1.0f);	 
} 
 
void COpenGLWnd::OnSizeGL(int cx, int cy) 
{ 
// set correspondence between window and OGL viewport 
		glViewport(0,0,cx,cy); 
 
// update the camera 
 		glPushMatrix(); 
			glMatrixMode(GL_PROJECTION); 
				glLoadIdentity(); 
				gluPerspective(40.0,m_dAspectRatio,0.1f, 10.0f); 
				glTranslatef(0.0f,0.0f,-4.f); 
			glMatrixMode(GL_MODELVIEW); 
		glPopMatrix();	 
} 
 
void COpenGLWnd::OnDrawGL() 
{ 
// draw carthesian axes 
	glBegin(GL_LINES); 
		// red x axis 
		glColor3f(1.f,0.f,0.f); 
		glVertex3f(0.0f,0.0f,0.0f); 
		glVertex3f(1.0f,0.0f,0.0f); 
		glVertex3f(1.0f,0.0f,0.0f); 
		glVertex3f(0.9f,0.1f,0.0f); 
		glVertex3f(1.0f,0.0f,0.0f); 
		glVertex3f(0.9f,-0.1f,0.0f); 
		// green y axis 
		glColor3f(0.f,1.f,0.f); 
		glVertex3f(0.0f,0.0f,0.0f); 
		glVertex3f(0.0f,1.0f,0.0f); 
		glVertex3f(0.0f,1.0f,0.0f); 
		glVertex3f(0.1f,0.9f,0.0f); 
		glVertex3f(0.0f,1.0f,0.0f); 
		glVertex3f(-0.1f,0.9f,0.0f); 
		// blue z axis 
		glColor3f(0.f,0.f,1.f); 
		glVertex3f(0.0f,0.0f,0.0f); 
		glVertex3f(0.0f,0.0f,1.0f); 
		glVertex3f(0.0f,0.0f,1.0f); 
		glVertex3f(0.0f,0.1f,0.9f); 
		glVertex3f(0.0f,0.0f,1.0f); 
		glVertex3f(0.0f,-0.1f,0.9f); 
	glEnd(); 
	 
} 
 
void COpenGLWnd::VideoMode(ColorsNumber &c,ZAccuracy &z,BOOL &dbuf) 
{ 
// set default videomode 
	c=MILLIONS; 
	z=NORMAL; 
	dbuf=TRUE;	 
} 
 
unsigned char COpenGLWnd::ComponentFromIndex(int i, UINT nbits, UINT shift) 
{ 
   unsigned char val; 
 
    val = (unsigned char) (i >> shift); 
    switch (nbits)  
	{ 
 
    case 1: 
        val &= 0x1; 
        return _oneto8[val]; 
    case 2: 
        val &= 0x3; 
        return _twoto8[val]; 
    case 3: 
        val &= 0x7; 
        return _threeto8[val]; 
 
    default: 
        return 0; 
    }	 
} 
 
void COpenGLWnd::CreateRGBPalette() 
{ 
    PIXELFORMATDESCRIPTOR pfd; 
    LOGPALETTE *pPal; 
    int n, i; 
 
// get the initially choosen video mode 
	n = ::GetPixelFormat(m_pCDC->GetSafeHdc()); 
    ::DescribePixelFormat(m_pCDC->GetSafeHdc(), n, sizeof(pfd), &pfd); 
 
// if is an indexed one... 
    if (pfd.dwFlags & PFD_NEED_PALETTE) 
    { 
// ... construct an equilibrated palette (3 red bits, 3 green bits, 2 blue bits) 
// NOTE: this code is integrally taken from MFC example Cube 
		n = 1 << pfd.cColorBits; 
        pPal = (PLOGPALETTE) new char[sizeof(LOGPALETTE) + n * sizeof(PALETTEENTRY)]; 
 
        ASSERT(pPal != NULL); 
 
        pPal->palVersion = 0x300; 
        pPal->palNumEntries = n; 
        for (i=0; ipalPalEntry[i].peRed=ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift); 
            pPal->palPalEntry[i].peGreen=ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift); 
            pPal->palPalEntry[i].peBlue=ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift); 
            pPal->palPalEntry[i].peFlags=0; 
        } 
 
// fix up the palette to include the default Windows palette 
        if ((pfd.cColorBits == 8)                           && 
            (pfd.cRedBits   == 3) && (pfd.cRedShift   == 0) && 
            (pfd.cGreenBits == 3) && (pfd.cGreenShift == 3) && 
            (pfd.cBlueBits  == 2) && (pfd.cBlueShift  == 6) 
           ) 
        { 
			for (i = 1 ; i <= 12 ; i++) 
                pPal->palPalEntry[defaultOverride[i]] = defaultPalEntry[i]; 
        } 
 
        m_CurrentPalette.CreatePalette(pPal); 
        delete pPal; 
 
// set the palette 
        m_pOldPalette=m_pCDC->SelectPalette(&m_CurrentPalette, FALSE); 
        m_pCDC->RealizePalette(); 
    }	 
} 
 
BOOL COpenGLWnd::SetupPixelFormat() 
{ 
// define default desired video mode (pixel format) 
	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,                             // 32-bit z-buffer 
        0,                              // no stencil buffer 
        0,                              // no auxiliary buffer 
        PFD_MAIN_PLANE,                 // main layer 
        0,                              // reserved 
        0, 0, 0                         // layer masks ignored 
    }; 
// let the user change some parameters if he wants 
	BOOL bDoublBuf; 
	ColorsNumber cnum; 
	ZAccuracy zdepth; 
	VideoMode(cnum,zdepth,bDoublBuf); 
//set the changes 
	if(bDoublBuf) pfd.dwFlags=PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |PFD_DOUBLEBUFFER; 
	else pfd.dwFlags=PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; 
	switch(cnum) 
	{ 
	case INDEXED: pfd.cColorBits=8; 
	case THOUSANDS: pfd.cColorBits=16; 
	case MILLIONS: pfd.cColorBits=24; 
	case MILLIONS_WITH_TRANSPARENCY: pfd.cColorBits=32; 
	}; 
	switch(zdepth) 
	{ 
	case NORMAL: pfd.cDepthBits=16; 
	case ACCURATE: pfd.cDepthBits=32; 
	}; 
 
// ask the system for such video mode 
    ASSERT(m_pCDC != NULL); 
    int pixelformat; 
	if ( (pixelformat = ChoosePixelFormat(m_pCDC->GetSafeHdc(), &pfd)) == 0 ) 
    { 
        AfxMessageBox("ChoosePixelFormat failed"); 
        return FALSE; 
    } 
 
// try to set this video mode     
	if (SetPixelFormat(m_pCDC->GetSafeHdc(), pixelformat, &pfd) == FALSE) 
    { 
// the requested video mode is not available so get a default one 
        pixelformat = 1;	 
		if (DescribePixelFormat(m_pCDC->GetSafeHdc(), pixelformat, sizeof(PIXELFORMATDESCRIPTOR), &pfd)==0) 
		{ 
// neither the requested nor the default are available: fail 
			AfxMessageBox("SetPixelFormat failed (no OpenGL compatible video mode)"); 
			return FALSE; 
		} 
    } 
 
    return TRUE;	 
} 
 
int COpenGLWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)  
{ 
	if (CWndEx::OnCreate(lpCreateStruct) == -1) 
		return -1; 
	 
	// TODO: Add your specialized creation code here 
// OpenGL rendering context creation 
	PIXELFORMATDESCRIPTOR pfd; 
    int         n; 
 
// initialize the private member 
	m_pCDC= new CClientDC(this); 
 
// choose the requested video mode 
    if (!SetupPixelFormat()) return 0; 
	 
// ask the system if the video mode is supported 
    n=::GetPixelFormat(m_pCDC->GetSafeHdc()); 
    ::DescribePixelFormat(m_pCDC->GetSafeHdc(),n,sizeof(pfd),&pfd); 
 
// create a palette if the requested video mode has 256 colors (indexed mode) 
    CreateRGBPalette(); 
 
// link the Win Device Context with the OGL Rendering Context 
    m_hRC = wglCreateContext(m_pCDC->GetSafeHdc()); 
 
// specify the target DeviceContext (window) of the subsequent OGL calls 
    wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); 
 
// performs default setting of rendering mode,etc.. 
	OnCreateGL(); 
 
// free the target DeviceContext (window) 
    wglMakeCurrent(NULL,NULL); 
	 
	return 0; 
} 
 
void COpenGLWnd::OnDestroy()  
{ 
	CWndEx::OnDestroy(); 
	 
	// TODO: Add your message handler code here 
// specify the target DeviceContext (window) of the subsequent OGL calls 
    wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); 
 
// remove all display lists 
	for (int c=0;cSelectPalette(&palDefault, FALSE); 
 
// destroy Win Device Context 
	if(m_pCDC) delete m_pCDC; 
	 
} 
 
void COpenGLWnd::OnSize(UINT nType, int cx, int cy)  
{ 
	CWndEx::OnSize(nType, cx, cy); 
	 
	// TODO: Add your message handler code here 
// when called with a nonzero window: 
	if ( 0 < cx && 0 < cy ) 
	{ 
// update the rect and the aspect ratio 
		m_ClientRect.right = cx; 
		m_ClientRect.bottom = cy; 
		m_dAspectRatio=double(cx)/double(cy); 
 
// specify the target DeviceContext of the subsequent OGL calls 
		wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); 
 
// call the virtual sizing procedure (to be overridden by user) 
		OnSizeGL(cx,cy); 
 
// free the target DeviceContext (window) 
		wglMakeCurrent(NULL,NULL); 
 
// force redraw 
		Invalidate(TRUE); 
	};	 
} 
 
BOOL COpenGLWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)  
{ 
	// TODO: Add your message handler code here and/or call default 
	ASSERT(m_hMouseCursor!=NULL); 
	::SetCursor(m_hMouseCursor); 
	return TRUE;	 
//	return CWndEx::OnSetCursor(pWnd, nHitTest, message); 
} 
 
 
 
void COpenGLWnd::OnPaint()  
{ 
	CPaintDC dc(this); // device context for painting 
	 
	// TODO: Add your message handler code here 
	 
	// Do not call CWndEx::OnPaint() for painting messages 
 
	CDC* pDC = &dc; 
 
// prepare a semaphore 
	static BOOL 	bBusy = FALSE; 
// use the semaphore to enter this critic section 
	if(bBusy) return; 
	bBusy = TRUE; 
 
// specify the target DeviceContext of the subsequent OGL calls 
	wglMakeCurrent(m_pCDC->GetSafeHdc(), m_hRC); 
 
// clear background 
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
 
// call the virtual drawing procedure (to be overridden by user) 
	OnDrawGL(); 
 
// execute OGL commands (flush the OGL graphical pipeline) 
	glFinish(); 
 
// if double buffering is used it's time to swap the buffers 
	SwapBuffers(m_pCDC->GetSafeHdc()); 
	 
// turn the semaphore "green" 
	bBusy = FALSE; 
 
// free the target DeviceContext (window) 
    wglMakeCurrent(NULL,NULL); 
} 
 
BOOL COpenGLWnd::OnEraseBkgnd(CDC* pDC)  
{ 
	// TODO: Add your message handler code here and/or call default 
	 
//	return CWndEx::OnEraseBkgnd(pDC); 
	return TRUE; 
} 
 
 
COpenGLWnd::CGLDispList::CGLDispList(): 
	m_glListId(0), m_bIsolated(FALSE) 
{ 
} 
 
COpenGLWnd::CGLDispList::~CGLDispList() 
{ 
// remove display list 
	glDeleteLists(m_glListId,1);  
} 
 
////////////////////////////////////////////////////////////////////// 
// Member functions 
 
void COpenGLWnd::CGLDispList::Draw() 
{ 
// if the list is not empty... 
	if(m_glListId) 
	{ 
		if(m_bIsolated) 
		{ 
// save current transformation matrix 
			glPushMatrix(); 
// save current OGL internal state (lighting, shading, and such) 
			glPushAttrib(GL_ALL_ATTRIB_BITS); 
		}; 
// draw the list 
		glCallList(m_glListId); 
		if(m_bIsolated) 
		{ 
// restore transformation matrix 
			glPopMatrix(); 
// restore OGL internal state 
			glPopAttrib(); 
		}; 
	}; 
} 
 
void COpenGLWnd::CGLDispList::StartDef(BOOL bImmediateExec) 
{ 
// set the context for GL calls (if needed) 
//	BeginGLCommands(); 
// check if another list is under construction 
	int cur; 
	glGetIntegerv(GL_LIST_INDEX,&cur); 
	if(cur != 0) {TRACE("Error: Nested display list definition!");ASSERT(FALSE);}; 
// if the list is empty firstly allocate one 
	if(!m_glListId) m_glListId=glGenLists(1); 
 
// start or replace a list definition 
	if (bImmediateExec) glNewList(m_glListId,GL_COMPILE_AND_EXECUTE); 
	else  glNewList(m_glListId,GL_COMPILE); 
} 
 
void COpenGLWnd::CGLDispList::EndDef() 
{ 
// check the coupling with a preceding call to StartDef() 
	int cur; 
	glGetIntegerv(GL_LIST_INDEX,&cur); 
	if(cur != m_glListId) {TRACE("CGLDispList:Missing StartDef() before EndDef()\n");return;}; 
// close list definition 
	glEndList(); 
// free the context (if needed) 
//	EndGLCommands(); 
} 
 
////////////////////////////////////////////////////////////////////// 
// 
// Implementation of COpenGLWnd::CGLTesselator class. 
// 
/*** DESCRIPTION 
 
  This is actually a helper class which wraps the 
  use of tessellation objects in OGL (see guide). 
  It must be used inside an GLEnabledView cause 
  a tesselator object must refer to a Rendering Context. 
 
****************************************/ 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
 
COpenGLWnd::CGLTesselator::CGLTesselator() 
{ 
// create tessellation object 
	m_tessObj=gluNewTess(); 
// set callback functions 
	gluTessCallback(m_tessObj,GLU_TESS_BEGIN,(void (CALLBACK*)())&BeginCallback);  
	gluTessCallback(m_tessObj,GLU_TESS_VERTEX,(void (CALLBACK*)())&VertexCallback);  
	gluTessCallback(m_tessObj,GLU_TESS_END,(void (CALLBACK*)())&EndCallback); 
	gluTessCallback(m_tessObj,GLU_TESS_COMBINE,(void (CALLBACK*)())&CombineCallback); 
	gluTessCallback(m_tessObj,GLU_TESS_ERROR,(void (CALLBACK*)())&ErrorCallback); 
} 
 
COpenGLWnd::CGLTesselator::~CGLTesselator() 
{ 
// remove tessellation object 
	gluDeleteTess(m_tessObj);	 
} 
 
////////////////////////////////////////////////////////////////////// 
// Member functions 
 
void COpenGLWnd::CGLTesselator::SetWindingRule(GLdouble which) 
{ 
// issue the equivalent GL call 
	gluTessProperty(m_tessObj,GLU_TESS_WINDING_RULE,which);  
} 
 
GLdouble COpenGLWnd::CGLTesselator::GetWindingRule() 
{ 
//retrieve attribute 
	GLdouble temp; 
	gluTessProperty(m_tessObj,GLU_TESS_WINDING_RULE,temp); 
// return value 
	return temp; 
} 
 
void COpenGLWnd::CGLTesselator::SetFilling(BOOL bFill) 
{ 
// issue the equivalent GL calls 
	if(bFill) gluTessProperty(m_tessObj,GLU_TESS_BOUNDARY_ONLY,GL_FALSE); 
	else gluTessProperty(m_tessObj,GLU_TESS_BOUNDARY_ONLY,GL_TRUE); 
} 
 
BOOL COpenGLWnd::CGLTesselator::GetFilling() 
{ 
//retrieve attribute 
	GLdouble temp; 
	gluTessProperty(m_tessObj,GLU_TESS_BOUNDARY_ONLY,temp); 
// convert to a boolean 
	return (temp==GL_TRUE); 
} 
 
void COpenGLWnd::CGLTesselator::StartDef() 
{ 
// start a polygon definition 
	gluTessBeginPolygon(m_tessObj,NULL); 
// start a contour definition 
	gluTessBeginContour(m_tessObj); 
} 
 
void COpenGLWnd::CGLTesselator::EndDef() 
{ 
// end contour and polygon definition 
	gluTessEndContour(m_tessObj); 
	gluTessEndPolygon(m_tessObj); 
// free new vertices created by tessellation 
	::DeleteGarbage(); 
} 
 
void COpenGLWnd::CGLTesselator::ContourSeparator() 
{ 
// insert a contour separation 
	gluTessEndContour(m_tessObj); 
	gluTessBeginContour(m_tessObj); 
} 
 
void COpenGLWnd::CGLTesselator::AddVertex(GLdouble vertData[3]) 
{ 
// IMPORTANT: the 3rd parameter must be given otherwise an access 
// violation will occur, moreover every vertex must have it's own memory 
// location till the closing of the polygon (that is you can't pass all 
// the vertices trough the same variable in a for loop). 
	gluTessVertex(m_tessObj,vertData,vertData);  
} 
 
void COpenGLWnd::CGLTesselator::AddVertexArray(GLdouble arr[][3], int size) 
{ 
	ASSERT(arr!=NULL); 
// pass the vertices to the tessellation object 
	for(int ct=0;ct