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

2D Drawing with an OpenGL Control

Rate me:
Please Sign up or sign in to vote.
4.87/5 (30 votes)
8 Feb 2010CPOL2 min read 210K   7.7K   130  
An article explaning how to create a user control for 2D shape drawing with OpenGL
#pragma once

#include <windows.h>
#include <GL/gl.h>

using namespace System;
using namespace System::Drawing;
using namespace System::ComponentModel;
using namespace System::Windows::Forms;


namespace GLView {

	// Forward class declarations
	ref class GLGraphics;
	ref class GLPerformanceTimer;

	/// <summary>
	/// Represents a 2D drawing canvas utilizing OpenGL.
	/// </summary>
	[ToolboxBitmap(GLView::GLCanvas2D::typeid)]
	public ref class GLCanvas2D : public System::Windows::Forms::UserControl
	{
	private:
		value class GLPointFloat
		{
		    float X;
			float Y;
		};

		value class GLGlyphMetrics
		{
		    float gmfBlackBoxX;
			float gmfBlackBoxY;
			GLPointFloat gmfptGlyphOrigin;
			float gmfCellIncX;
			float gmfCellIncY;
		};

	// Constructor/destructor
	public:
		GLCanvas2D();

	protected:
		~GLCanvas2D();

	// Enums
	public:
		enum class SelectMode
		{
			None,
			PointPick,
			Line,
			Circle,
			Rectangle,
			ShadedRectangle
		};

		ref class GLGraphics;

	// Member variables
	private:
		Drawing::Color mBackColor;
		HDC mhDC;
		HGLRC mhGLRC;
		bool mIsAccelerated;
		PointF mCameraPosition;
	    bool mPanning;
		bool mSelecting;
	    Point mLastMouse;
		float mZoomFactor;
		Drawing::Point mSelPt1, mSelPt2;
		Drawing::RectangleF mLimits;
		bool mShowGrid;
		bool mShowAxes;
		float mGridSpacing;
		Drawing::Color mMinorGridColor;
		Drawing::Color mMajorGridColor;
		Drawing::Color mAxisColor;
		bool mDynamicGrid;
		GLPerformanceTimer ^ mTimer;
		unsigned long mRenderTime;
		unsigned long mElapsedTime;
		int mFrameCount;
		int mFPS;
		bool mAntiAlias;
		GLuint base, rasterbase;

	public:
		/// <summary>
		/// Determines whether hardware acceleration is enabled.
		/// </summary>
		[Category("Behavior"), Browsable(false)] 
		property bool IsAccelerated
		{
			virtual bool get(void) { return mIsAccelerated; }
		}
		/// <summary>
		/// Gets or sets the mouse selection mode.
		/// </summary>
		[Category("Behavior"), Browsable(true)] 
		property SelectMode SelectionMode;
		/// <summary>
		/// Determines whether zooming and panning with the mouse is allowed.
		/// </summary>
		[Category("Behavior"), Browsable(true)] 
		property bool AllowZoomAndPan;
		/// <summary>
		/// Determines whether grid lines are visible.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property bool ShowGrid
		{
			virtual bool get(void) { return mShowGrid; }
			virtual void set(bool value) { mShowGrid = value; Invalidate(); }
		}
		/// <summary>
		/// Determines whether axes are visible.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property bool ShowAxes
		{
			virtual bool get(void) { return mShowAxes; }
			virtual void set(bool value) { mShowAxes = value; Invalidate(); }
		}
		/// <summary>
		/// Determines whether grid spacing is dynamically determined.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property bool DynamicGrid
		{
			virtual bool get(void) { return mDynamicGrid; }
			virtual void set(bool value) { mDynamicGrid = value; Invalidate(); }
		}
		/// <summary>
		/// Gets or sets the grid spacing.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property float GridSpacing
		{
			virtual float get(void) { return mGridSpacing; }
			virtual void set(float value) { mGridSpacing = value; Invalidate(); }
		}
		/// <summary>
		/// Gets or sets the color of minor gridlines.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property Drawing::Color MinorGridColor
		{
			virtual Drawing::Color get(void) { return mMinorGridColor; }
			virtual void set(Drawing::Color value) { mMinorGridColor = value; Invalidate(); }
		}
		/// <summary>
		/// Gets or sets the color of major gridlines.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property Drawing::Color MajorGridColor
		{
			virtual Drawing::Color get(void) { return mMajorGridColor; }
			virtual void set(Drawing::Color value) { mMajorGridColor = value; Invalidate(); }
		}
		/// <summary>
		/// Gets or sets the color of axes.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property Drawing::Color AxisColor
		{
			virtual Drawing::Color get(void) { return mAxisColor; }
			virtual void set(Drawing::Color value) { mAxisColor = value; Invalidate(); }
		}
		/// <summary>
		/// Determines whether lines are anti-aliased.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property bool AntiAlias
		{
			virtual bool get(void) { return mAntiAlias; }
			virtual void set(bool value) 
			{ 
				if (value)
					glEnable(GL_LINE_SMOOTH);
				else
					glDisable(GL_LINE_SMOOTH);
				mAntiAlias = value; 
				Invalidate(); 
			}
		}
		/// <summary>
		/// Gets or sets the color of selection lines.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property Drawing::Color SelectionColor;
		/// <summary>
		/// Gets the limits of all drawing objects on the canvas.
		/// </summary>
		[Category("Behavior"), Browsable(false)] 
		property Drawing::RectangleF Limits
		{
			virtual Drawing::RectangleF get(void) { return mLimits; }
		}
		/// <summary>
		/// Gets the last render time in milliseconds.
		/// </summary>
		[Category("Performance"), Browsable(false)] 
		property unsigned long RenderTime
		{
			virtual unsigned long get(void) { return mRenderTime; }
		}
		/// <summary>
		/// Gets the count of frames per second.
		/// </summary>
		[Category("Performance"), Browsable(false)] 
		property int FPS
		{
			virtual int get(void) { return mFPS; }
		}
		/// <summary>
		/// Returns the display list used to draw vector text.
		/// </summary>
		[Category("Appearance"), Browsable(false)] 
		property GLuint VectorListBase
		{
			virtual GLuint get(void) { return base; }
		}
		/// <summary>
		/// Returns the display list used to draw raster text.
		/// </summary>
		[Category("Appearance"), Browsable(false)] 
		property GLuint RasterListBase
		{
			virtual GLuint get(void) { return rasterbase; }
		}
		/// <summary>
		/// Gets or sets the font used to display text in the control.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		property System::Drawing::Font ^ Font
		{
			virtual void set(System::Drawing::Font ^ value) override
			{
				Control::Font::set(value);

				// Save previous context and make our context current
				HDC mhOldDC = wglGetCurrentDC();
				HGLRC mhOldGLRC = wglGetCurrentContext();
				wglMakeCurrent(mhDC, mhGLRC);

				// Delete old display lists
				glDeleteLists(base, 256);
				glDeleteLists(rasterbase, 256);
				
				// Create the font display lists
				SelectObject(mhDC, (HGDIOBJ)value->ToHfont());
				base = glGenLists(256);
				rasterbase = glGenLists(256);
				
				wglUseFontOutlines(mhDC, 0, 256, base, 0.0f, 0.0f, WGL_FONT_POLYGONS, NULL);
				wglUseFontBitmaps(mhDC, 0, 256, rasterbase);
				
				// Restore previous context
				wglMakeCurrent(mhOldDC, mhOldGLRC);

				Invalidate(); 
			}
		}

	// Public methods.
	public:
		/// <summary>
		/// Converts the given point from world coordinates to screen coordinates.
		/// </summary>
		Drawing::Point WorldToScreen(float x, float y)
		{ 
			return Drawing::Point((int)((x - mCameraPosition.X) / mZoomFactor) + ClientRectangle.Width / 2,
								  -(int)((y - mCameraPosition.Y) / mZoomFactor) + ClientRectangle.Height / 2);
		}
		/// <summary>
		/// Converts the given point from world coordinates to screen coordinates.
		/// </summary>
		Drawing::Point WorldToScreen(Drawing::PointF pt) { return WorldToScreen(pt.X, pt.Y); }
		/// <summary>
		/// Converts the given vector from world coordinates to screen coordinates.
		/// </summary>
		Drawing::Size WorldToScreen(Drawing::SizeF pt)
		{
			Drawing::Point pt1 = WorldToScreen(0.0f, 0.0f);
			Drawing::Point pt2 = WorldToScreen(pt.Width, pt.Height);
			return Drawing::Size(pt2.X - pt1.X, pt2.Y - pt1.Y);
		}
		/// <summary>
		/// Converts the given point from screen coordinates to world coordinates.
		/// </summary>
		Drawing::PointF ScreenToWorld(int x, int y)
		{ 
			return Drawing::PointF((float)(x - ClientRectangle.Width / 2) * mZoomFactor + mCameraPosition.X, 
								  -(float)(y - ClientRectangle.Height / 2) * mZoomFactor + mCameraPosition.Y);
		}
		/// <summary>
		/// Converts the given point from screen coordinates to world coordinates.
		/// </summary>
		Drawing::PointF ScreenToWorld(Drawing::Point pt) { return ScreenToWorld(pt.X, pt.Y); }
		/// <summary>
		/// Converts the given vector from screen coordinates to world coordinates.
		/// </summary>
		Drawing::SizeF ScreenToWorld(Drawing::Size pt)
		{
			Drawing::PointF pt1 = ScreenToWorld(0, 0);
			Drawing::PointF pt2 = ScreenToWorld(pt.Width, pt.Height);
			return Drawing::SizeF(pt2.X - pt1.X, pt2.Y - pt1.Y);
		}
		/// <summary>
		/// Returns the coordinates of the viewport in world coordinates.
		/// </summary>
		Drawing::RectangleF GetViewPort()
		{ 
			Drawing::PointF bl = ScreenToWorld(ClientRectangle.Left, ClientRectangle.Bottom);
			Drawing::PointF tr = ScreenToWorld(ClientRectangle.Right, ClientRectangle.Top);
			return Drawing::RectangleF(bl.X, bl.Y, tr.X - bl.X, tr.Y - bl.Y);
		}
		/// <summary>
		/// Sets the viewport to the given model coordinates.
		/// </summary>
		System::Void SetView(float x1, float y1, float x2, float y2)
		{ 
			float h = Math::Abs(y1 - y2);
			float w = Math::Abs(x1 - x2);
			mCameraPosition = PointF((x1 + x2) / 2, (y1 + y2) / 2);
			if ((ClientRectangle.Height != 0) && (ClientRectangle.Width != 0))
				mZoomFactor = Math::Max(h / (float)(ClientRectangle.Height), w / (float)(ClientRectangle.Width));
			else
				mZoomFactor = 1;
		}

	private:
		System::Void ControlResize(System::Object^  sender, System::EventArgs^  e);
		System::Void ControlMouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e);
		System::Void ControlMouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e);
		System::Void ControlMouseUp(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e);
		System::Void ControlMouseWheel(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e);
		System::Void ControlMouseDoubleClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e);

	protected:
		virtual void OnPaint(System::Windows::Forms::PaintEventArgs^  e) override sealed;

	// Events
	public:
		delegate void RenderHandler(System::Object ^ sender, GLView::GLGraphics ^ Graphics);
		delegate void MousePickHandler(System::Object ^ sender, Drawing::Point pt);
		delegate void MouseSelectHandler(System::Object ^ sender, Drawing::Point pt1, Drawing::Point pt2);
		/// <summary>
		/// Occurs when the control is redrawn.
		/// </summary>
		[Category("Appearance"), Browsable(true)] 
		event RenderHandler ^ Render;
		/// <summary>
		/// Occurs when the user picks a point with the mouse.
		/// </summary>
		[Category("Mouse"), Browsable(true)] 
		event MousePickHandler ^ MousePick;
		/// <summary>
		/// Occurs when the user selects a region with the mouse.
		/// </summary>
		[Category("Mouse"), Browsable(true)] 
		event MouseSelectHandler ^ MouseSelect;

	};

}

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
Turkey Turkey
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions