Zooming and Panning an Orthographic Projection

This article assumes that you understand how to set-up OpenGL in a CView derived class.

Zooming and panning an orthographic projection can be done by manipulating the projection matrix. In the following function, SetupOrtho(int cx, int cy), values for the real world origin and maximum coordinate values are used to set up the clip rectangle. First, the viewport is set to the width and height of the views client area. The desired rectangle, in world coordinates, is adjusted based on the diminsions of the client area.

SetupOrtho(int cx, int cy) is called by the OnSize() handler and by any function that wants to change the zoom or pan of the window. The window is invalidated and the views OnDraw(CDC* pDC) member function is called. The OnDraw places the new matrix on the current matrix stack and the scene is rendered on the back buffer. The buffer's are then swapped.

In the following code, xOrg, yOrg, xMax, and yMax are member variables used to store the current clip rectangle


void CGLView::SetupOrtho(int cx, int cy)
{
	if( 0 >= cx || 0 >= cy ) return;

	//Set viewport dimensions
	::glViewport(0, 0, cx, cy);
	
	// OK, now save the dimensions of the window
	m_width = cx;
	m_height = cy;
	
	// Now that the viewport dimensions are set up,
	//  we can set up the projection matrix. 

	// select the viewing volumn
	::glMatrixMode ( GL_PROJECTION );
	::glLoadIdentity ();

	GLdouble dx = xMax - xOrg;
	GLdouble dy = yMax - yOrg;
	double aspect_ratio;
	if(fabs(dx) > 0.001 || fabs(dy) > 0.001)//Controls how far you can zoom in
	{
		if(dx >= dy)
		{	
	
			GLdouble dY = dx * m_height / m_width;
			GLdouble yMax = yOrg  + dY;
			::glOrtho( xOrg, xMax, yOrg, yMax, -zMax, zMax);
		}
		else
		{
			GLdouble dX = dy * m_width / m_height;
			GLdouble xMax = xOrg + dX;
			::glOrtho( xOrg, xMax, yOrg, yMax, -zMax, zMax);
	
		}
	}
	// switch back to the modelview matrix and clear it 
	::glMatrixMode( GL_MODELVIEW ); 
	::glLoadIdentity(); 
} 

Overridden version of SetupOrtho that uses the client rect:


void CGLView::SetupOrtho()
{
	//Get the client rect	
	CRect rect;
	GetClientRect(&rect);

	//Now save the dimensions of the window

	m_width = rect.Width();
	m_height = rect.Height();

	//Use client rect to set up viewport when SetupOrtho is called
	SetupOrtho(rect.Width(), rect.Height());
}

OnSize() calls SetupOrtho(int cx, int cy):


void CGLView::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);
	SetupOrtho(cx, cy);
}

OnDraw renders the scene:


void CGLView::OnDraw(CDC* pDC)
{
	

	HDC hdc;
	HGLRC rc;
	 
	//The following two lines are needed for splinter windows and MDI applications
	CGLView::GetCurrent(hdc, rc);//Store Current rendering context and it's HDC
	MakeActive();//Make this current OpenGL View
	
	//Clear back buffer
	::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	
	//Push new matrix onto stack
	::glPushMatrix();
	
	//Do any drawing on back buffer
	RenderScene();
	
	//Restore stack
	::glPopMatrix();
	
	// Tell OpenGL to flush its pipeline
	::glFinish();
	// Now Swap the buffers
	::SwapBuffers(m_pDC->GetSafeHdc());
	
	CGLView::SetCurrent(hdc, rc);//Reset last rendering context

}

The following functions are called by OnDraw()


BOOL CGLView::MakeActive()
{
	return SetCurrent(m_pDC->GetSafeHdc(), m_hRC);
}


void CGLView::GetCurrent(HDC& hdc, HGLRC& rc) 
{	
	hdc = ::wglGetCurrentDC();
	rc =  ::wglGetCurrentContext(); 
} 

BOOL CGLView::SetCurrent(HDC hdc, HGLRC rc) 
{
	//Set the current rendering context
	if (FALSE == ::wglMakeCurrent(hdc, rc))
		return FALSE;
	return TRUE;
} 

void CGLView::RenderScene()
{
	//draw anything you want in here
}

Example mouse handlers:


void CGLView::OnLButtonDown(UINT nFlags, CPoint point) 
{

	CView::OnLButtonDown(nFlags, point);
	GLdouble modelMatrix[16];
	GLdouble projMatrix[16];
	GLint viewport[4];
	GLdouble objx, objy, objz;
	glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
	glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
	glGetIntegerv(GL_VIEWPORT, viewport);
	CRect rect;
	GetClientRect(&rect);
	int y = rect.Height() - point.y;
	gluUnProject(point.x, y, 0, 
					modelMatrix, projMatrix, viewport, 
					&objx, &objy, &objz); 

	if(!m_FirstPickPoint)
	{
		switch(currTool)
		{
			case ID_ZOOM_WINDOW:
			{
				m_EndPoint.east = m_StartPoint.east = objx;
				m_EndPoint.north = m_StartPoint.north = objy;
				m_EndPoint.elev = m_StartPoint.elev = objz;
				m_FirstPickPoint = TRUE;
				m_StartPosition.x = m_CursorPosition.x = point.x;
				m_StartPosition.y = m_CursorPosition.y = point.y;
				DrawZoomRect();
				break;

			}
			case ID_PAN:
			{
				m_EndPoint.east = m_StartPoint.east = objx;
				m_EndPoint.north = m_StartPoint.north = objy;
				m_EndPoint.elev = m_StartPoint.elev = objz;
				m_FirstPickPoint = TRUE;
				m_StartPosition.x = m_CursorPosition.x = point.x;
				m_StartPosition.y = m_CursorPosition.y = point.y;
				DrawRubberBand();
				break;

			}

		}
	}
	else
	{
		switch(currTool)
		{
			case ID_ZOOM_WINDOW:
			{
				m_PrevZoomLeft.east = xOrg;
				m_PrevZoomLeft.north = yOrg;
				m_PrevZoomLeft.elev = -zMax;
				m_PrevZoomRight.east = xMax;
				m_PrevZoomRight.north = yMax;
				m_PrevZoomRight.elev = zMax;
				
				m_CursorPosition.x = point.x;
				m_CursorPosition.y = point.y;
				
				DrawZoomRect();			
				m_EndPoint.east = objx;
				m_EndPoint.north = objy;
				m_EndPoint.elev = objz;
				xOrg = min(m_StartPoint.east, m_EndPoint.east);
				xMax = max(m_StartPoint.east, m_EndPoint.east);
				yOrg = min(m_StartPoint.north, m_EndPoint.north);
				yMax = max(m_StartPoint.north, m_EndPoint.north);
				currTool = -1;
				m_FirstPickPoint = FALSE;
				SetupOrtho();
				Invalidate();
				break;
			}
			case ID_PAN:
			{
				m_PrevZoomLeft.east = xOrg;
				m_PrevZoomLeft.north = yOrg;
				m_PrevZoomLeft.elev = -zMax;
				m_PrevZoomRight.east = xMax;
				m_PrevZoomRight.north = yMax;
				m_PrevZoomRight.elev = zMax;
				
				m_CursorPosition.x = point.x;
				m_CursorPosition.y = point.y;
				
				DrawRubberBand();			
				m_EndPoint.east = objx;
				m_EndPoint.north = objy;
				m_EndPoint.elev = objz;
				GLdouble DE = m_EndPoint.east - m_StartPoint.east;
				GLdouble DN = m_EndPoint.north - m_StartPoint.north;
				xOrg -= DE;
				xMax -= DE;
				yOrg -= DN;
				yMax -= DN;
				currTool = -1;
				m_FirstPickPoint = FALSE;
				SetupOrtho();
				Invalidate();
				break;
			}
			default:break;
		}
	}

		
}

void CGLView::OnMouseMove(UINT nFlags, CPoint point) 
{

	CView::OnMouseMove(nFlags, point);
	
	if(currTool == ID_ZOOM_WINDOW)
		if(m_FirstPickPoint)
			DrawZoomRect();

	if(currTool == ID_PAN)
		if(m_FirstPickPoint)
			DrawRubberBand();


	m_CursorPosition.x = point.x;
	m_CursorPosition.y = point.y;
	GLdouble modelMatrix[16];
	GLdouble projMatrix[16];
	GLint viewport[4];
	GLdouble objx, objy, objz;

	glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
	glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
	glGetIntegerv(GL_VIEWPORT, viewport);
	
	CRect rect;
	GetClientRect(&rect);
	point.y = rect.Height() - point.y;
	gluUnProject(point.x, point.y, 0, 
					modelMatrix, projMatrix, viewport, 
					&objx, &objy, &objz);
	
	m_EndPoint.east = m_MovePoint.east = objx;
	m_EndPoint.north = m_MovePoint.north = objy;
	m_EndPoint.elev = m_MovePoint.elev = objz;

	if(currTool == ID_ZOOM_WINDOW)
		if(m_FirstPickPoint)
			DrawZoomRect();

	if(currTool == ID_PAN)
		if(m_FirstPickPoint)
			DrawRubberBand();
	
}
Bibliography
Fosner, Ron. OpenGL Programming for Windows 95 and Windows NT. Reading Mass.:Addison-Wesley, 1996.
Download source. 62KB



Comments

  • please tell me (im a beginer) how can i run this or other OpenGL files...

    Posted by Legacy on 11/26/2003 12:00am

    Originally posted by: Venci

    How can i run this or other files...scripts...i dont know how what file type the files must be!...please E-Mail me...
    Bullfroger@abv.bg

    Reply
  • cool, but it doesn't redraw well

    Posted by Legacy on 01/30/2002 12:00am

    Originally posted by: Jorge

    Hi, the code is fantastic but it seems that (in some situations) something is not correct when you resize or maximize the window...

    try to zoom until only a half the rectangle is visible
    and the click maximize button...
    it doesn't redraw a little portion of the screen,
    maybe the formulas of "glOrtho" must be corrected?

    Other: In CGLTestView, you've got "currTool == -1"
    instead of "currTool = -1;"

    bye
    Jorge

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Remember getting your first box of LEGOS as a kid? How fun it was putting the pieces together, collaborating with your friends to create something new? Now, as an IT professional, assembling and maintaining a Lego-like collaboration infrastructure isn't what you signed up for. Piecing together disparate systems of record for email, web meetings and other applications is about as painful as stepping on a pile of Legos. Download the e-book to learn how implementing a collaboration system connects systems of …

  • Companies undertaking an IT project need to find the right balance between cost and functionality. It's important to start by determining whether to build a solution from scratch, buy an out-of-the-box solution, or a combination of both. In reality, most projects will require some system tailoring to meet business requirements. Decision-makers must understand how much software development is enough and craft a detailed implementation plan to ensure the project's success. This white paper examines the different …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date