Click here to Skip to main content
15,879,239 members
Articles / Desktop Programming / MFC

Writing a Platform and GUI Toolkit Independent OpenGL Class

Rate me:
Please Sign up or sign in to vote.
4.92/5 (33 votes)
1 Nov 2010CPOL13 min read 96.5K   7.5K   89  
Article showing how to write OS and GUI toolkit portable OpenGL view class
// GLView.cpp : Defines the exported functions for the DLL application.
//

#include "GLView.h"
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;

// This is the constructor of a class that has been exported.
// see GLView.h for the class definition
CGLView::CGLView()
{
#ifdef WIN32
	m_hWnd = NULL;
	m_hDC = NULL;
	m_hGLRC = NULL;
#else
	m_pXDisplay = NULL;
	m_hGLContext = NULL;
	m_hVisual = NULL;
#endif

	nRange = 100;
	m_bAntiAlias = true;

	ambiLight[0] = 0.3f;
	ambiLight[1] = 0.3f;
	ambiLight[2] = 0.3f;
	ambiLight[3] = 1.0f;

	diffLight[0] = 0.7f;
	diffLight[1] = 0.7f;
	diffLight[2] = 0.7f;
	diffLight[3] = 1.0f;

	specLight[0] = 1.0f;
	specLight[1] = 1.0f;
	specLight[2] = 1.0f;
	specLight[3] = 1.0f;

	// gradient background color
	bgTopR = 77;
	bgTopG  =77;
	bgTopB = 77;
	bgBotR = 236;
	bgBotG = 233;
	bgBotB = 216;


	m_bGradientBg = true;

	return;
}

#ifdef WIN32
void CGLView::SetWindow(HWND ihWnd)
{
	m_hWnd = ihWnd;
}
#else
void CGLView::SetWindow(Display* pDisp, const Window& wnd)
{
	m_pXDisplay = pDisp;
	m_iXWindow = wnd;
}
#endif

void CGLView::Resize(unsigned short iWidth, unsigned short iHeight)
{
	GLdouble modelMatrix[16];
	GLdouble projMatrix[16];
	GLint viewport[4];

	winH = (GLdouble)iHeight;
	winW = (GLdouble)iWidth;

	// setup viewport, projection etc.:
	/* Prevent a divide by zero*/
	if(iHeight == 0)
		iHeight = 1;

#ifdef WIN32
	wglMakeCurrent(m_hDC, m_hGLRC);
#else
	glXMakeCurrent(m_pXDisplay, m_iXWindow, m_hGLContext);
#endif

	glViewport (0, 0, iWidth, iHeight);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	if (iWidth <= iHeight)
		glOrtho (-nRange, nRange, -nRange*iHeight/iWidth, nRange*iHeight/iWidth, -nRange*10000, nRange*10000);
	else
		glOrtho (-nRange*iWidth/iHeight, nRange*iWidth/iHeight, -nRange, nRange, -nRange*10000, nRange*10000);
	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity ();

	/* store limits for gradient background */
	glGetDoublev (GL_PROJECTION_MATRIX, projMatrix);
	glGetDoublev (GL_MODELVIEW_MATRIX, modelMatrix);
	glGetIntegerv (GL_VIEWPORT, viewport);
	gluUnProject (0, 0, 1, modelMatrix, projMatrix, viewport,
		&win_xmin, &win_ymin, &winz);
	gluUnProject (iWidth, iHeight, 1, modelMatrix, projMatrix, viewport,
		&win_xmax, &win_ymax, &winz);

#ifdef WIN32
	wglMakeCurrent(NULL,NULL);
#else
	glXMakeCurrent(m_pXDisplay, None, NULL);
#endif
}

void CGLView::Refresh( void)
{
#ifdef WIN32
	::InvalidateRect(m_hWnd, NULL, FALSE);
#else
	// setup event data
	XWindowAttributes winattr;
	Status st = XGetWindowAttributes(m_pXDisplay, m_iXWindow, &winattr);
	XExposeEvent ev = { Expose, 0, 1, m_pXDisplay, m_iXWindow, 0, 0, winattr.width, winattr.height, 0 };
	// send event to display connection
	XSendEvent(m_pXDisplay, m_iXWindow, False, ExposureMask, (XEvent *) &ev);
	XFlush(m_pXDisplay);
#endif
}

void CGLView::RenderScene(void)
{
#ifdef WIN32
	wglMakeCurrent(m_hDC, m_hGLRC);
#else
	glXMakeCurrent(m_pXDisplay, m_iXWindow, m_hGLContext);
#endif

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	DrawGradientBackground();	
	DoAntialiasing();
	Setup3DEnvironment();	

	glPushMatrix();
	{
		//Draw stuff here
		glRotatef(-45.0f, 1.0f, 0.0f, 0.0f);
		glRotatef(-45.0f, 0.0f, 0.0f, 1.0f);

		glColor4ub(125, 255, 255, 255);
		drawTorus(30, 20, 50, 25, false);
	}
	glPopMatrix();

#ifdef WIN32
	SwapBuffers(m_hDC);
	wglMakeCurrent(NULL, NULL);
#else
	glXSwapBuffers(m_pXDisplay, m_iXWindow);
	glXMakeCurrent(m_pXDisplay, None, NULL);
#endif
}

bool CGLView::SetupGLContext(bool iSetPixelFormat)
{
#ifdef WIN32
	if(m_hWnd == NULL)
	{
		return false;
	}

	m_hDC = ::GetDC(m_hWnd);

	if(iSetPixelFormat)
	{
		SetPixelFormat(m_hDC);
	}	

	if (m_hGLRC = ::wglCreateContext(m_hDC))
	{ 
		// try to make it the thread's current rendering context 
		if(false == wglMakeCurrent(m_hDC, m_hGLRC))
		{
			MessageBox(m_hWnd, _T("Failed wglMakeCurrent"), _T("Error!"), MB_ICONERROR);
			return false;
		}
		else
		{
			glClearColor(0.0, 0.0, 0.0, 1.0);
			InitDisplayLists();
		}
	}
	return true;
#else
	int attrListSgl[] =
	{ GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4,
	GLX_DEPTH_SIZE, 16, None };

	int attrListDbl[] =
	{ GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4,
	GLX_BLUE_SIZE, 4, GLX_DEPTH_SIZE, 16, None };

	m_iXScreen = DefaultScreen(m_pXDisplay);

	m_hVisual = glXChooseVisual(m_pXDisplay, m_iXScreen, attrListDbl);
	if (NULL == m_hVisual)
	{
		m_hVisual = glXChooseVisual(m_pXDisplay, m_iXScreen, attrListSgl);
		cout << "Singlebuffered : true" << endl;
		if(NULL == m_hVisual)
		{
			cerr << "Could not get suitable XVisualInfo\n" << endl;
			return false;
		}
	}
	else
	{
		cout << "Doublebuffered : true\n" << endl;
	}

	// Create the rendering context
	m_hGLContext = glXCreateContext(m_pXDisplay, m_hVisual, 0, GL_TRUE);

	// Create a colormap
	m_ColMap = XCreateColormap(m_pXDisplay, RootWindow(m_pXDisplay, m_hVisual->screen),
		m_hVisual->visual, AllocNone);

	// Make the rendering context current, perform initialization, then
	// deselect it
	if(False == glXMakeCurrent(m_pXDisplay, m_iXWindow, m_hGLContext))
	{
		return false;
	}
	else
	{
		glClearColor(0.0, 0.0, 0.0, 1.0);
		InitDisplayLists();
	}
	glXMakeCurrent(m_pXDisplay, None, NULL);
	return true;
#endif
}

#ifdef WIN32
int CGLView::SetPixelFormat(HDC hdc)
{
	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 
		32,                               // 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 
	}; 

	int  iPixelFormat; 

	// get the device context's best, available pixel format match 
	if((iPixelFormat = ChoosePixelFormat(hdc, &pfd)) == 0)
	{
		MessageBox(m_hWnd, _T("ChoosePixelFormat Failed"), NULL, MB_ICONERROR);
		return 0;
	}

	// make that match the device context's current pixel format 
	if(::SetPixelFormat(hdc, iPixelFormat, &pfd) == FALSE)
	{
		MessageBox(m_hWnd, _T("SetPixelFormat Failed"), NULL, MB_ICONERROR);
		return 0;
	}

	return 1;
}
#endif

void CGLView::Setup3DEnvironment()
{
	// Enable Depth Testing
	glEnable(GL_DEPTH_TEST);
	// Enable lighting
	glEnable(GL_LIGHTING);
	
	GLfloat position[] = {0.0f, 0.0f, 3.0f, 0.0f};
	GLfloat lmodel_ambient[] = {0.2f, 0.2f, 0.2f, 1.0f};
	GLfloat local_view[] = {0.0f};

	// Setup and enable light 0
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambiLight);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffLight);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specLight);
	glLightfv(GL_LIGHT0, GL_POSITION, position);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
	glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view);
	glEnable(GL_LIGHT0);

	glShadeModel(GL_SMOOTH);
}
void CGLView::DoAntialiasing()
{
	if(m_bAntiAlias)
	{
		glEnable (GL_POINT_SMOOTH);
		glEnable (GL_LINE_SMOOTH);
		glEnable (GL_BLEND);
		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glHint (GL_POINT_SMOOTH_HINT, GL_NICEST);
		glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
		glLineWidth(1.5125f);
		glDepthFunc(GL_ALWAYS);
		glEnable(GL_DEPTH_TEST);
	}
	else
	{
		glDisable (GL_LINE_SMOOTH);
		glLineWidth (1.2125f);
		glDepthFunc(GL_LESS);
		glDisable (GL_BLEND);
	}
}

void CGLView::DrawGradientBackground()
{
	/*gradient background */
	glPushAttrib(GL_ENABLE_BIT);
	glDisable (GL_DEPTH_TEST);
	glDisable (GL_LIGHTING);
	glDisable (GL_TEXTURE_2D);
	glShadeModel (GL_SMOOTH);
	glPolygonMode(GL_FRONT_AND_BACK ,GL_FILL);
	glBegin (GL_POLYGON);
	//glColor3ub (51, 51, 101);
	glColor3ub (bgTopR, bgTopG, bgTopB);
	glVertex3d (win_xmin, win_ymax, winz);
	glVertex3d (win_xmax, win_ymax, winz);
	//glColor3ub (171, 172, 193);
	if(m_bGradientBg)
	{
		glColor3ub (bgBotR, bgBotG, bgBotB);
	}
	glVertex3d (win_xmax, win_ymin, winz);
	glVertex3d (win_xmin, win_ymin, winz);
	glEnd ();
	glPopAttrib();
	/*end gradient background */
}

void CGLView::InitDisplayLists()
{
}

#include <cmath>
//helper function to draw torus removes 'glut' dependency
void GLVIEW_API drawTorus(int numMajor, int numMinor, float majorRadius, float minorRadius, bool bShaded)
{
	static double PI = 3.1415926535897932384626433832795;
	double majorStep = 2.0 * PI / numMajor;
	double minorStep = 2.0 * PI / numMinor;
	int i, j;

	GLfloat opacity = 255;

	GLfloat ambiMat[4] = { 0.2f, 0.5f, 0.7f, opacity };

	GLfloat diffMat[4] = { 0.7f, 0.7f, 0.7f, opacity };

	GLfloat specMat[4] = { 1.0f, 1.0f, 1.0f, opacity };

	GLfloat specRef[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

	GLint shine;

	shine = 128;

	opacity = 255;


	// Enable Material
	glEnable(GL_COLOR_MATERIAL);

	glEnable(GL_AUTO_NORMAL);
	glEnable(GL_NORMALIZE);
	glDepthFunc(GL_LESS);

	//Setup Material
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambiMat);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffMat);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specMat);


	// All materials hereafter have full specular reflectivity
	// with a high shine
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,specRef);
	glMateriali(GL_FRONT_AND_BACK,GL_SHININESS, shine);

	if(!bShaded)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	}


	for (i = 0; i < numMajor; ++i) 
	{
		double a0 = i * majorStep;
		double a1 = a0 + majorStep;
		GLdouble x0 = cos(a0);
		GLdouble y0 = sin(a0);
		GLdouble x1 = cos(a1);
		GLdouble y1 = sin(a1);

		glBegin(GL_TRIANGLE_STRIP);
		for (j = 0; j <= numMinor; ++j) {
			double b = j * minorStep;
			GLdouble c = cos(b);
			GLdouble r = minorRadius * c + majorRadius;
			GLdouble z = minorRadius * sin(b);

			glNormal3d(x0 * c, y0 * c, z / minorRadius);
			glTexCoord2d(i / (GLdouble) numMajor, j / (GLdouble) numMinor);
			glVertex3d(x0 * r, y0 * r, z);

			glNormal3d(x1 * c, y1 * c, z / minorRadius);
			glTexCoord2d((i + 1) / (GLdouble) numMajor, j / (GLdouble) numMinor);
			glVertex3d(x1 * r, y1 * r, z);
		}
		glEnd();
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Product Manager Mahindra & Mahindra
India India
Sharjith is a Mechanical Engineer with strong passion for Automobiles, Aircrafts and Software development.

Comments and Discussions