How to make different Projection View in the split windows

Dear all OpenGL experts:

I have a need to write a Graphics
application under Window2000 with MFC.
My problem is that I need split the graphics
window into 4 different sub windows (panes)
and let each panes shows different projection
model and views (for examples, one Perspective
projection and three Ortho2D with top, left and
right view). I struggled for almost one month,
and with some other people’s help, it did show
4 panes, but they are all with same Perspective
mode. Could anyone tell me how to achieve my
goal? I put some of the codes here, it is
lenghy, but hope to get some help from any of
your guys. Thanks.

In MainFrm.h, I have

protected:
CSplitterWnd m_wndSplitter;

and in MainFrm.cpp, I have

CView *p0, *p1, *p2, p3; //Globl Variable
:
:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext
pContext)
{
if (!m_wndSplitter.CreateStatic(this, 2, 2)) return FALSE;

int nSizeX = (lpcs->cx)/2;
int nSizeY = (lpcs->cy)/2;

if ( !(m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 1, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext)) )
{
m_wndSplitter.DestroyWindow();
return FALSE;
}

//Save the global variable – let call them as Pain ID
p0 = (CView *) m_wndSplitter.GetPane(0,0);
p1 = (CView *) m_wndSplitter.GetPane(0,1);
p2 = (CView *) m_wndSplitter.GetPane(1,0);
p3 = (CView *) m_wndSplitter.GetPane(1,1);

SetActiveView((CView*)m_wndSplitter.GetPane(1,1));
return TRUE;
}
CView * CMainFrame::ReturnActiveView()
{
return CFrameWnd::GetActiveView();
}

And in the View.cpp, I list the code segments as following:

void CGLSplitWndView::OnLButtonDown(UINT nFlags, CPoint point)
{
/*
if (p0 ==reinterpret_cast<CMainFrame*>(::AfxGetMainWnd())->ReturnActiveView())
AfxMessageBox(“Left Upper Pane (0,0) is selected”);
if (p1 ==reinterpret_cast<CMainFrame*>(::AfxGetMainWnd())->ReturnActiveView())
AfxMessageBox(“Right Upper Pane (0,1) is selected”);
if (p2 ==reinterpret_cast<CMainFrame*>(::AfxGetMainWnd())->ReturnActiveView())
AfxMessageBox(“Left Lower Pane (1,0) is selected”);
if (p3 ==reinterpret_cast<CMainFrame*>(::AfxGetMainWnd())->ReturnActiveView())
AfxMessageBox(“Right Lower Pane (1,1) is selected”);
*/
m_LButtonDown = TRUE;
m_LDownPos = point;
CView::OnLButtonDown(nFlags, point);
}

void CGLSplitWndView::OnLButtonUp(UINT nFlags, CPoint point)
{
m_LButtonDown = FALSE;
CView::OnLButtonUp(nFlags, point);
}

void CGLSplitWndView::OnMouseMove(UINT nFlags, CPoint point)
{
SetFocus();
CClientDC dc(this);
fastMakeCurrent(dc.GetSafeHdc(), m_hGLContext);

if (m_LButtonDown)
{
	CGLSplitWndDoc* pDoc = GetDocument();
	CSize rotate = m_LDownPos - point;
	m_LDownPos = point;
	m_yRotate -= rotate.cx/2;
	m_xRotate -= rotate.cy/2;
	GetDocument()-&gt;UpdateAllViews(NULL/*this*/);
}

CView::OnMouseMove(nFlags, point);

}

void CGLSplitWndView::OnPaint()
{
CPaintDC dc(this); // device context for painting
wglMakeCurrent(dc.GetSafeHdc(), m_hGLContext);

CGLSplitWndDoc* pDoc = GetDocument();
RenderScene();//Call the one in View ...

SwapBuffers(dc.m_ps.hdc);

}

void CGLSplitWndView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);

GLsizei width, height;
GLdouble aspect;


width = cx;
height = cy;

if (cy==0)
	aspect = (GLdouble)width;
else
	aspect = (GLdouble)width/(GLdouble)height;

CClientDC dc(this);
wglMakeCurrent(dc.GetSafeHdc(), m_hGLContext);

glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

//???How to specify each panes has its own projection model?? ???
gluPerspective(45, aspect, dClipNear, dClipFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glDrawBuffer(GL_BACK);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);

}

void CGLSplitWndView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
Invalidate(false);
}
void CGLSplitWndView::RenderScene()
{
glClearColor(1.0f,1.0f,1.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glPushMatrix();

	SetModelView();

	double dZRatio = dClipFar/dClipNear;

	//Dwaw the Background Grid
	double dRange = min(5, dClipFar);
	double dStep = dRange/20;

	glDisable(GL_LIGHTING);	
	glDisable(GL_DEPTH_TEST);
	glColor3f(0.9f, 0.9f, 0.3f);
		
	glBegin(GL_LINES);

	for (double dC = -dRange; dC &lt;= dRange; dC += dStep)
	{
		glVertex3d(dC, 0, -dRange);
		glVertex3d(dC, 0,  dRange);
		glVertex3d(-dRange, 0, dC);
		glVertex3d( dRange, 0, dC);
	}
	glEnd();
	glEnable(GL_LIGHTING);	
	glEnable(GL_DEPTH_TEST);

	GetDocument()-&gt;RenderScene();
glPopMatrix();      

}
void CGLSplitWndView::SetModelView()
{
glTranslated(0,0,-8);
glRotated(m_xRotate, 1.0, 0.0, 0.0);
glRotated(m_yRotate, 0.0, 1.0, 0.0);
}

As your guys can see – in the OnLButtonDown routine,
if I took away those commends, then when I pick any of
the panes, it pops the right MessageBox. So I assume
that even there are 4 panes (sub windows), but they
use the same codes. NOW, MY PROBLEM IS::::::::::::

In the OnSize, there is one statement “gluPerspective”,
how can I do (what to change) so that the first pane
(ie, p0 = (CView *) m_wndSplitter.GetPane(0,0)
is ortho2d with top view (rotate x 90 degree) and
the second pane
(ie, p1 = (CView *) m_wndSplitter.GetPane(0,1)
is perspective view, and the third pane
(ie, p2 = (CView *) m_wndSplitter.GetPane(1,0)
is ortho2d with left view (rotate y 90 degree)
and the last pane
(ie p3 = (CView *) m_wndSplitter.GetPane(1,1)
is ortho2d with right view (rotate y -90 degree)???

Since your guys are experts, let me ask one more
question: How to add the displaylist to the code?
Does the displaylist handle differently in the split
window?

Thanks for all your helps,

Robert Yu tyyu@ksts.seed.net.tw

When you create the 4 panes, use a different viewer for each (they can all be derived from a single base viewer – the only difference would be the render function that would use the appropriate camera).

You could also set a variable in the view class (if you didn’t want to use multiple view classes) that would tell you which type of viewer you are and call the appropriate camera routine during OnSize.

Originally posted by mmeyer:
[b]When you create the 4 panes, use a different viewer for each (they can all be derived from a single base viewer – the only difference would be the render function that would use the appropriate camera).

You could also set a variable in the view class (if you didn’t want to use multiple view classes) that would tell you which type of viewer you are and call the appropriate camera routine during OnSize.[/b]

Thanks, but can you please describe more detail? What do you mean by creating the panes with different view class? What do you mean by using the multiple view classes? Can you please show some examples? Thanks.

if you want to have diferents views you can create a class to initialize them. Use the constructor for indicating the projection desired: perspective or ortho.
for example:

  1. declare your class:

class TView
{
private:
//declare here the pixelformat descriptor
bool PERSPECTIVE;//to control the projection mode.
void InitializetheView();// your func to initialize the projection when the window is created.

public:
// declare here the functions you will use to control the initialization and resize the view.
__fastcall TView(bool PerspectiveMode)// constructor with the projection param.
}

2.give the definitions

__fastcall TView::TView(bool PerspectiveMode)
{
// initialize the constructor
PERSPECTIVE=PerspectiveMode;
}
.
.
.
void TView::InitializetheView()
{
// call the setting for controling the Projection Matrix;
if (PERSPECTIVE)
gluPerspective(/* params*/);
else
gluOrtho2D(/params/);
}
note that you can use glOrtho() for the ortho projection.

When you declare a instance of the class in your module, use a global one and destroy it when you close the windows.

I hope you will understand my bad english…

To use different viewing classes, simply change the class in the RUNTIME_CLASS() macro in the following code, for each of the 4 panes:

if ( !(m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 1, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext)) )

You could change it to:
if ( !(m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CGLSplitWndViewPersp), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CGLSplitWndViewOrthoTop), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CGLSplitWndViewOrthoLeft), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 1, RUNTIME_CLASS(CGLSplitWndViewOrthoRight), CSize(nSizeX, nSizeY), pContext)) )

Originally posted by mmeyer:
[b]To use different viewing classes, simply change the class in the RUNTIME_CLASS() macro in the following code, for each of the 4 panes:

if ( !(m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 1, RUNTIME_CLASS(CGLSplitWndView), CSize(nSizeX, nSizeY), pContext)) )

You could change it to:
if ( !(m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CGLSplitWndViewPersp), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CGLSplitWndViewOrthoTop), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CGLSplitWndViewOrthoLeft), CSize(nSizeX, nSizeY), pContext) &&
m_wndSplitter.CreateView(1, 1, RUNTIME_CLASS(CGLSplitWndViewOrthoRight), CSize(nSizeX, nSizeY), pContext)) )

[/b]

May I know how you define the CGLSplitWndViewPersp, CGLSplitWndViewOrthoLeft, CGLSplitWndViewOrthoRight and CGLSplitWndViewOrthoTop?

May I know how you define the CGLSplitWndViewPersp, CGLSplitWndViewOrthoLeft, CGLSplitWndViewOrthoRight and CGLSplitWndViewOrthoTop???

Any way you want. The easiest way is to define them all the same as your previous CGLSplitWndView with different OnSize functions (using gluPerspective and glOrtho appropriately).

Is it possible to have your email address?
I wonder whether I can zip the source code and you add several lines to my codes? I guess it will be more effiecent than I just guess what you say.

Thanks.

Robert Yu tyyu@ksts.seed.net.tw

hello,
just for information, there is another way to draw multiple views in a window with the gl functions. Use glScissor(), that function allows you to specify the coordinate of a “subwindow” in wich the drawing commands will take effect. You can call it 2,3,4 times…
example:

in your display func():

glEnable(GL_SCISSOR_TEST) // do enable the Scissor function
glViewport(0,0,width,height); // specify the coordinate for your projection
glScissor(0,0,width,height);
glMatrixMode(GL_PROJECTION)
glLoadIdentity();
gluperspective(fovy,aspect,near,far); // specify the projecion you want (perspective or ortho)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// modify the view point: front, back, left, right, top, bottom, perpective.
// calls to the drawings func (All the drawings called between the glEnable and glDisable(GL_SCISSOR_TEST) appear in the window specified by glScissor();

glDisable(GL_SCISSOR_TEST); //do disable the Scissor func
// calls to glclear color
// swapBuffers…