Click here to Skip to main content
15,885,757 members
Articles / Multimedia / OpenGL

Generating Outlines in OpenGL

Rate me:
Please Sign up or sign in to vote.
4.78/5 (12 votes)
7 Oct 20045 min read 198.9K   5K   52  
Using multi-pass techniques to generate outlines in OpenGL.
// CWGL.cpp - implementation of CWGL - 
//			  MS Windows OpenGL rendering interface class.
//
// (c) W.Weyna
//

#include "stdafx.h"
//#include "resource.h"
#include "OGLT.h"
#include "CWGL.h"


LRESULT	CALLBACK CWGL_FullScreen_WndProc(HWND, UINT, WPARAM, LPARAM);	

//******************************************************
//
// CWGL()
//
//******************************************************

CWGL::CWGL()
{
	m_pDC = NULL;
	m_pBitmapDC = NULL;
	m_pWnd = NULL;
	m_hWnd = NULL;
	m_hWndRC = NULL;
	m_hFSWndRC = NULL;
	m_hBmpRC = NULL;
	m_bNewRCCreated = false;
	m_bWeCreatedDC = false;
	m_dwRenderingTime = 1;
	m_dwStartRenderingTime = 0;
	
	m_hPrevDC = NULL;
	m_hPrevRC = NULL;
	
	// Remember current RC (needed for CWGL nesting)
	m_hPrevRC = wglGetCurrentContext(); 
	m_hPrevDC = wglGetCurrentDC();


	//
	//  Create a default font
	//
	memset(&m_defaultLogfont, 0, sizeof m_defaultLogfont);
	m_defaultLogfont.lfHeight = 14;
	m_defaultLogfont.lfWeight = 1;  
	strcpy(m_defaultLogfont.lfFaceName, "Courier New");
	m_defaultLogfont.lfCharSet			= ANSI_CHARSET;
	m_defaultLogfont.lfOutPrecision		= OUT_TT_PRECIS;
	m_defaultLogfont.lfClipPrecision	= CLIP_DEFAULT_PRECIS;
	m_defaultLogfont.lfQuality			= PROOF_QUALITY;
	m_defaultLogfont.lfPitchAndFamily	= FF_SWISS | VARIABLE_PITCH;
	m_defaultFont.CreateFontIndirect(&m_defaultLogfont);
	
	m_pFont = &m_defaultFont;

	//
	// Register FULL SCREEN window class
	//

	m_hFSWnd = NULL;
	m_hViewWnd = NULL;
	m_pView = NULL;
	m_bFullScreen = false;
	m_nMode = 0;
	m_windowRect = CRect(0, 0, 640, 480);

	WNDCLASS	wc;		
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	
	wc.lpfnWndProc		= (WNDPROC)CWGL_FullScreen_WndProc;					
	wc.cbClsExtra		= 0;									
	wc.cbWndExtra		= 0;									
	wc.hInstance		= AfxGetInstanceHandle();
	wc.hIcon			= LoadIcon(AfxGetInstanceHandle(), "IDR_MAINFRAME");		
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);			
	wc.hbrBackground	= NULL;									
	wc.lpszMenuName		= NULL;									
	wc.lpszClassName	= "SIWinFS";							

	RegisterClass(&wc);									
}


//******************************************************
//
// ~CWGL()
//
//******************************************************

CWGL::~CWGL()
{
	TRACE("--- CWGL Destroying all\n");
	wglMakeCurrent(NULL, NULL);

	if(m_hFSWndRC)
		wglDeleteContext(m_hFSWndRC);
	
	if(m_hFSWnd)
	{
		// If we created a full screen window for a view,
		// unsubclass now and reattach view's window. 
		if(m_pView)
		{
			ASSERT(m_hViewWnd);
			m_pView->UnsubclassWindow();
			m_pView->Attach(m_hViewWnd);
		}
		::DestroyWindow(m_hFSWnd);
	}

	if(m_hWndRC)
		wglDeleteContext(m_hWndRC);
	
	if(m_hBmpRC)
		wglDeleteContext(m_hBmpRC);

	// If nested RC's reactivate previous one
	if(m_hPrevRC)
		wglMakeCurrent(m_hPrevDC, m_hPrevRC);

	TRACE("--- CWGL Destroying done\n");
}



bool CWGL::Begin(CDC* pDC /* = NULL */)
{
	// If NULL is passed instead of a valid DC,
	// it is a request to create a DC for last used window.
	// Pass NULL if you want to make last used window RC current again.
	m_bWeCreatedDC = false;
	if(pDC == NULL)
	{
		ASSERT(!m_bFullScreen);
		if(m_pWnd == NULL || !IsWindow(m_hWnd))
		{
			// You must call Begin(windowDC) at least once
			// before Begin(NULL) calls are allowed.
			ASSERT(0);
			return false;
		}
		pDC = new CClientDC(m_pWnd); 
		m_bWeCreatedDC = true;
	}

	if(!m_hPrevRC)
	{
		// Synchronize access to surface
		// ( on Win95 may be remove to increased performance)
		GdiFlush();
	}

	// Make sure that previous RC is inactivated.
	wglMakeCurrent(NULL, NULL);
	
	m_bNewRCCreated = false;

	// m_pDC is for SwapBuffers() to work,
	// and will be NULL if rendered to bitmap.
	m_pDC = NULL;  
	
	if(pDC->IsPrinting())
	{
		// Printing not supported.
		// Render on a DIB section and print the DIB section.
		ASSERT(0);
		return false;	
	}

	//
	// Rendering in a window (in a window's backbuffer)
	//
	if(pDC->GetWindow())
	{
		if(m_bFullScreen)
		{
			if(m_hFSWndRC == NULL)
			{
				if(!SetPixelFormat(pDC))
					return false;
				if((m_hFSWndRC = wglCreateContext(pDC->m_hDC))==NULL)
					return false;
				m_bNewRCCreated = true;
			}

			ASSERT(m_hFSWndRC);
			
			// Associate OpenGL rendering context with this window
			if(!wglMakeCurrent(pDC->m_hDC, m_hFSWndRC))
				return false;	
		}
		else
		{
			if(m_pWnd && m_pWnd != pDC->GetWindow())
			{
				// one CWGL object may be used to render only in one window
				ASSERT(0);	
				return false;
			}

			// If rendering in a window for the first time,
			// set the pixelformat for this window and create RC.
			// The pixelformat may be set only once for the windows life time.
			// The created RC will exist and will be used for subsequent
			// renderings until the window will be destroyed.
			if(m_pWnd == NULL)
			{
				m_pWnd = pDC->GetWindow();
				m_hWnd = m_pWnd->m_hWnd; 
				if(!SetPixelFormat(pDC))
					return false;
				if((m_hWndRC = wglCreateContext(pDC->m_hDC))==NULL)
					return false;
				m_bNewRCCreated = true;
			}

			ASSERT(m_hWndRC);

			// Associate OpenGL rendering context with this window
			if(!wglMakeCurrent(pDC->m_hDC, m_hWndRC))
				return false;	
		}

		m_pDC = pDC; // save pDC so  we can do SwapBuffers() in End()

		if(m_bNewRCCreated)
			CompileFonts();

		m_dwStartRenderingTime = GetTickCount();
		return true;
	}
	
	HGDIOBJ hGDIObject = GetCurrentObject(pDC->m_hDC, OBJ_BITMAP);

	if(hGDIObject == NULL)
	{
		// Dont know how to render on this DC
		ASSERT(0);
		return false;
	}

	//
	// Rendering on a DIB section GDI object or bitmap.
	// For each new DC we must call SetPixelFormat()
	// and we must create new RC.
	//

	// Delete previously used RC. 
	if(m_hBmpRC && pDC != m_pBitmapDC)
	{
		wglDeleteContext(m_hBmpRC);
		m_hBmpRC = NULL;
		m_pBitmapDC = NULL;
	}
	
	// Create new RC for new bitmap DC
	if(m_hBmpRC == NULL)
	{
		// Set the pixelformat for the bitmap selected into pDC.
	
		CBitmap* pBitmap = pDC->GetCurrentBitmap();
		BITMAP bmInfo;
		pBitmap->GetObject(sizeof(BITMAP), &bmInfo);

		ASSERT(bmInfo.bmPlanes == 1);
		ASSERT(bmInfo.bmBitsPixel == 8 || bmInfo.bmBitsPixel == 16 || bmInfo.bmBitsPixel == 24 || bmInfo.bmBitsPixel == 32);		

		if(!SetPixelFormat(pDC, PFD_DRAW_TO_BITMAP, bmInfo.bmBitsPixel))
			return false;

		// Create new RC for the bitmap selected into pDC
		if((m_hBmpRC = wglCreateContext(pDC->m_hDC)) == NULL)
			return false;
		
		m_bNewRCCreated = true;
		m_pBitmapDC = pDC;
	}

	if(!wglMakeCurrent(pDC->m_hDC, m_hBmpRC))
		return false;	

	CompileFonts();

	m_dwStartRenderingTime = GetTickCount();
	return true;
}


void CWGL::End(bool m_bDontSwapBuffers /* = false*/)
{
	// Synchronize access to surface
	// ( on Win95 may be remove to increased performance)
	glFinish();

	// if(m_pDC) means 'if not rendered to bitmap'
	if(m_pDC && !m_bDontSwapBuffers)
	{
		// If rendered to the window's backbuffer, show the image.
		// If rendered directly to the window,
		// this call will have no effect.
		SwapBuffers(m_pDC->m_hDC);
		GdiFlush();  // ( on Win95 may be remove to increased performance)
	}

	// Free currently selected OpenGL RC, 
	// so the other window belonging to
	// this thread may also do the OpenGL rendering.
	wglMakeCurrent(NULL, NULL);
	
	if(m_bWeCreatedDC)
	{
		ASSERT(m_pDC);
		delete m_pDC;
	}


	m_dwRenderingTime = GetTimeDif(m_dwStartRenderingTime);
	if(m_dwRenderingTime == 0)
		m_dwRenderingTime = 10;  
}


bool CWGL::SetPixelFormat(CDC* pDC, DWORD dwFlags /* = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER */, int nBitsPerPixel /* = 24 */)
{
	PIXELFORMATDESCRIPTOR pfd = 
	{
        sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
        1,                              // version number
		PFD_SUPPORT_OPENGL | dwFlags,	
		PFD_TYPE_RGBA,                  // RGBA type
        nBitsPerPixel,                  // 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
        8,                              // require stencil buffer
        0,                              // no auxiliary buffer
        PFD_MAIN_PLANE,                 // main layer
        0,                              // reserved
        0, 0, 0                         // layer masks ignored
    };

    int pixelformat;

    if(!(pixelformat = ChoosePixelFormat(pDC->m_hDC, &pfd)))
	    return false;
	
	if(!::SetPixelFormat(pDC->m_hDC, pixelformat, &pfd))
	    return false;
	
    return true;
}  


DWORD CWGL::GetTimeDif(DWORD dwStartTime)
{
	DWORD dwTickCount = GetTickCount();
	if(dwTickCount < dwStartTime)
		return dwTickCount + 0xFFFFFFFF - dwStartTime;
	return dwTickCount - dwStartTime;
}



void CWGL::CompileFonts()
{
	HDC hDC = wglGetCurrentDC();
	if(hDC == NULL)
	{
		// CompileFonts() called without RC active
		ASSERT(0);
		return;
	}
	SelectObject(hDC, m_pFont->m_hObject); 
	wglUseFontBitmaps(hDC, 0, 255, WGL_FONTS_LISTBASE);  
}



void CWGL::TextOut(const CString& str)
{
	glListBase(WGL_FONTS_LISTBASE); 
	glCallLists(str.GetLength(), GL_UNSIGNED_BYTE, LPCTSTR(str)); 
	glListBase(0);
}


bool CWGL::FullScreen(int nMode /* = -1*/, CView* pView /* = NULL */) 
{
	if(m_bFullScreen)
	{
		//
		// Restore windowed mode
		//
		ASSERT(m_hFSWnd);

		m_bFullScreen = false;

		if(m_hFSWndRC)
		{
			// In CWGL, for full screen mode,
			// a window is always created when going
			// into the full screen mode and 
			// destroyed when going back to windowed mode.
			// So, RC has to be also created and destroyed.
			wglDeleteContext(m_hFSWndRC);
			m_hFSWndRC = NULL;
		}
		
		// If we created a full screen window for a view,
		// unsubclass now and reattach view's window. 
		if(m_pView)
		{
			ASSERT(m_hViewWnd);
			m_pView->UnsubclassWindow();
			m_pView->Attach(m_hViewWnd);
		}

		::DestroyWindow(m_hFSWnd);
		m_hFSWnd = NULL;

		if(m_nMode != -1)
			ChangeDisplaySettings(NULL, NULL);
		
		return true;
	}

	m_bFullScreen = false;
	m_hFSWnd = NULL;
	
	m_nMode = nMode;

	switch(m_nMode)
	{
	case -1:
		{
        // use current display settings
		CClientDC dc(NULL);
		m_windowRect.right = dc.GetDeviceCaps(HORZRES);
		m_windowRect.bottom = dc.GetDeviceCaps(VERTRES);
		}
		break;
		//
		// use some fixed display settings
		//
	case 0:
		m_windowRect = CRect(0, 0, 640, 480);
		break;
	case 1:
		m_windowRect = CRect(0, 0, 800, 600);
		break;
	case 2:
		m_windowRect = CRect(0, 0, 1024, 768);
		break;
	case 3:
		m_windowRect = CRect(0, 0, 1152, 864);
		break;
	default:
		m_windowRect = CRect(0, 0, 1280, 1024);
		break;
	}

	//
	// Try to change to full screen display mode.
	//
	
	if(m_nMode != -1)
	{
		DEVMODE dmScreenSettings;
		ZeroMemory(&dmScreenSettings, sizeof dmScreenSettings);	
		dmScreenSettings.dmSize = sizeof dmScreenSettings;
		dmScreenSettings.dmPelsWidth    = m_windowRect.Width();				// Selected Screen Width
		dmScreenSettings.dmPelsHeight	= m_windowRect.Height();				// Selected Screen Height
		dmScreenSettings.dmBitsPerPel	= 32;
		dmScreenSettings.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
		
		// CDS_FULLSCREEN option means - 'the mode will be temporary'. 
		if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
		{
			dmScreenSettings.dmBitsPerPel	= 24;
			if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
			{
				dmScreenSettings.dmBitsPerPel = 16;
				if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
					return false;
			}
		}
	}
	
	//
	// Create the full screen window 
	//
	if (!(m_hFSWnd = CreateWindowEx(NULL,
								"SIWinFS",							
								"SIWinFS",							
								WS_POPUP |							
								WS_CLIPSIBLINGS |					
								WS_CLIPCHILDREN |
								WS_VISIBLE,					
								0, 0,								
								m_windowRect.Width(),
								m_windowRect.Height(),
								NULL,								
								NULL,								
								AfxGetInstanceHandle(),
								NULL)))								
	{
		return false;
	}
	
	m_hViewWnd = NULL;
	m_pView = NULL;

	// If we create a full screen window for a CView view,
	// subclass the window.
	if(pView)
	{
		m_pView = pView;
		m_hViewWnd = m_pView->m_hWnd;
		m_pView->SubclassWindow(m_hFSWnd);
	}
		
	m_bFullScreen = true;

	return true;
}


//*********************************************************************
// 
// CWGL_FullScreen_WndProc()
// 
//*********************************************************************

LRESULT CALLBACK CWGL_FullScreen_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg)	
	{
	case WM_ACTIVATE:		
		return 0;	
	
	case WM_SYSCOMMAND:	
		switch (wParam)
		{
		case SC_SCREENSAVE:
		case SC_MONITORPOWER:
			return 0;			
		}
		break;

	case WM_CLOSE:				
		PostQuitMessage(0);		
		return 0;	
	}

	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}


//////////////////////////////////////////////////////////////////////

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
I started programming on 8 bit machines as a teenager, writing my first compiled programming language before I was 16. I went on to study Engineering and Computer Science at Oxford University, getting a first and the University Prize for the best results in Computer Science. Since then I have worked in a variety of roles, involving systems management and development management on a wide variety of platforms. Now I manage a software development company producing CAD software for Windows using C++.

My 3 favourite reference books are: Design Patterns, Gamma et al; The C++ Standard Library, Josuttis; and Computer Graphics, Foley et al.

Outside computers, I am also the drummer in a band, The Unbelievers and we have just released our first album. I am a pretty good juggler and close up magician, and in my more insane past, I have cycled from Spain to Eastern Turkey, and cycled across the Namib desert.

Comments and Discussions