Click here to Skip to main content
15,887,435 members
Articles / Desktop Programming / WPF

Creating OpenGL Windows in WPF

Rate me:
Please Sign up or sign in to vote.
4.86/5 (23 votes)
10 Mar 2009CPOL9 min read 161.3K   7.8K   63  
A guide to creating OpenGL applications with Windows Presentation Foundation
#pragma once

// Exclude rarely used parts of the windows headers
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#include "Helper.h"
#include "OpenGL.h"

// To use these, we must add some references...
//	o PresentationFramework (for HwndHost)
//		* PresentationCore
//		* WindowsBase
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Interop;
using namespace System::Windows::Input;
using namespace System::Windows::Media;
using namespace System::Windows::Forms; // We derive from UserControl this time, so this ref is necessary
using namespace System::Runtime::InteropServices;

namespace WPFOpenGLLib 
{
	public ref class OpenGLUserControl : public UserControl
	{
	private:
		HDC						m_hDC;
		HWND					m_hWnd;
		HGLRC					m_hRC;

		float					m_fRotationAngle;
		
		System::ComponentModel::Container^ components;
	
	public:
		virtual ~OpenGLUserControl()
		{
			if(NULL != m_hRC)
			{
				wglDeleteContext(m_hRC);
				m_hRC = NULL;
			}

			if(NULL != m_hWnd && NULL != m_hDC)
			{
				ReleaseDC(m_hWnd, m_hDC);
				m_hDC = NULL;
			}

			// Don't destroy the HWND... we didn't allocate it!

			if (components) 
			{
				delete components;
			}
		}

		OpenGLUserControl() : components(nullptr),
			m_hDC(NULL),
			m_hWnd(NULL),
			m_hRC(NULL),
			m_fRotationAngle(0.0f)
		{
			InitializeComponent();
			this->components = gcnew System::ComponentModel::Container();

			//Attach load and size change event handlers
			this->Load += gcnew System::EventHandler(this, &OpenGLUserControl::InitializeOpenGL);
			this->SizeChanged += gcnew EventHandler(this, &OpenGLUserControl::ResizeOpenGL);
		}

	protected:
		void InitializeComponent()
		{
			this->Name = "OpenGLUserControl";
		}

		virtual void OnPaintBackground( PaintEventArgs^ e ) override
		{ 
			UNREF(e);
			// not doing anything here avoids flicker
		}

		virtual void OnPaint( System::Windows::Forms::PaintEventArgs^ e ) override
		{
			UNREF(e);

			if(m_hDC == NULL || m_hRC == NULL)
				return;

			wglMakeCurrent(m_hDC, m_hRC);

			glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glLoadIdentity();

			glTranslatef (0.0f, 0.0f, -2.6f);

			glRotatef(m_fRotationAngle, 0.0f, 1.0f, 0.0f);

			glBegin (GL_TRIANGLES);
				glColor3f (1.0f, 0.0f, 0.0f);	glVertex3f(-1.0f,-1.0f, 0.0f);
				glColor3f (0.0f, 1.0f, 0.0f);	glVertex3f( 0.0f, 1.0f, 0.0f);
				glColor3f (0.0f, 0.0f, 1.0f);	glVertex3f( 1.0f,-1.0f, 0.0f);
			glEnd ();

			glFlush ();
			SwapBuffers(m_hDC); // NOTE: This is no longer wglSwapBuffers

			m_fRotationAngle += 1.0f;
		}

		/// <summary>
		///	Event handler called when the form is loaded.  It retrieves the controls
		///	window handle and device context and creates the rendering context.
		/// </summary>
		virtual void InitializeOpenGL( Object^ sender, EventArgs^ e)
		{
			UNREF(e);
			UNREF(sender);

			// Get the HWND from the base object
			m_hWnd	= (HWND) this->Handle.ToPointer();

			if(m_hWnd)
			{
				m_hDC = GetDC(m_hWnd);
				if(!m_hDC)
				{
					Helper::ErrorExit(L"BuildWindowCore");
				}

				//
				// Create PixelFormatDescriptor and choose pixel format
				//
				uint PixelFormat;

				BYTE iAlphaBits = 0;
				BYTE iColorBits = 32;
				BYTE iDepthBits = 16;
				BYTE iAccumBits = 0;
				BYTE iStencilBits = 0;

				static PIXELFORMATDESCRIPTOR pfd = 
				{
					sizeof(PIXELFORMATDESCRIPTOR),	//size
					1,								//version
					PFD_DRAW_TO_WINDOW|				//flags
					PFD_SUPPORT_OPENGL|
					PFD_DOUBLEBUFFER,
					PFD_TYPE_RGBA,					//pixeltype
					iColorBits,
					0, 0, 0, 0, 0, 0,				//color bits ignored
					iAlphaBits,						
					0,								//alpha shift ignored
					iAccumBits,						//accum. buffer
					0, 0, 0, 0,						//accum bits ignored
					iDepthBits,						//depth buffer
					iStencilBits,					//stencil buffer
					0,								//aux buffer
					PFD_MAIN_PLANE,					//layer type
					0,								//reserved
					0, 0, 0							//masks ignored
				};
				
				PixelFormat = ChoosePixelFormat(m_hDC, &pfd);
				if(!PixelFormat)
				{
					Helper::ErrorExit(L"BuildWindowCore");
				}

				if(!SetPixelFormat(m_hDC, PixelFormat, &pfd))
				{
					Helper::ErrorExit(L"BuildWindowCore");
				}

				m_hRC = wglCreateContext(m_hDC);
				if(!m_hRC)
				{
					Helper::ErrorExit(L"BuildWindowCore");
				}

				if(!wglMakeCurrent(m_hDC, m_hRC))
				{
					Helper::ErrorExit(L"BuildWindowCore");
				}

				//
				// DPI scaling is performed automatically!
				// TODO: Check with non-XP-Style DPI scaling!

				//ShowWindow(m_hWnd, SW_SHOW);
				//SetFocus(m_hWnd);

				glEnable(GL_DEPTH_TEST);
				glDisable(GL_TEXTURE_2D);
				return;
			}
		}

		/// <summary>
		/// Event handler called when the control is resized.
		/// </summary>
		void ResizeOpenGL(Object^ sender, EventArgs^ e)
		{
			UNREF(e);
			UNREF(sender);

			if(m_hDC == NULL || m_hRC == NULL)
				return;

			if(Width == 0 || Height == 0)
				return;

			wglMakeCurrent(m_hDC, m_hRC);
			glViewport( 0, 0, Width, Height );

			glMatrixMode ( GL_PROJECTION );
			glLoadIdentity();
			// gluPerspective( 67.5,((double)(Width) / (double)(Height)), 1.0, 500.0);
			glOrtho(-1.0, 1.0, -1.0, 1.0, 1.0, 100.0);
			glMatrixMode ( GL_MODELVIEW );
			glLoadIdentity();
		}
	};
};

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
Software Developer emphess
Germany Germany
Chris lives in Munich, Germany and tries to study Physics.

Alongside, he works as a self-employed software consultant on projects from various fields.

Unfortunately, he loved software ever since he started with C++, and his addiction has become a lot worse since the introduction of .NET 2.0.

In his spare time, he tries to go sailing, drink some nice single malt whisky and, in very rare cases, he also sleeps.

You can reach him at ChrisNOSPAM at emphess.net or via his blog emphess.net.

Comments and Discussions