www.pudn.com > etree_src.zip > OpenGLWnd.cpp
//****************************************************************************
// OpenGLWnd.cpp : implementation file
//
// Copyright (c) Boris J. Wang (e_boris2002@hotmail.com) 2002 - 2003
// From Institute of Computing Technology, Chinese Academy of Sciences
// Beijing 100871, China
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//****************************************************************************
#include "stdafx.h"
#include "OpenGL.h"
#include "OpenGLWnd.h"
#include "ControlPane.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// COpenGLWnd dialog
int COpenGLWnd::MoveSpeed = 10; //it is timer interval ^_^ not real speed
COpenGLWnd::COpenGLWnd(CWnd* pParent /*=NULL*/)
: CDialog(COpenGLWnd::IDD, pParent),
Help(_T("help.BMP"),GL_Tex_BMP_RGA_RRGB,-1.0),
Toolbar(_T("button.bmp"),GL_Tex_BMP_RGA_RRGB,-1.0)
{
//{{AFX_DATA_INIT(COpenGLWnd)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_pDC=NULL;
///////////////////////////////////////0.000000,-9.099350,-11.970904,0.000000,-1022.000000
//Finial Control
MouseEvent = MS_NONE;
KeyEvent = MouseEvent =0;
OrgX = 0.0f;
OrgY = -11.7872f;
OrgZ = -8.04298f;
RotX = -10.00f;
RotY = 19346.0f;
ShowTitle = false;
GLInitialized = false;
wcp = NULL;
ButtonHoverState = BUTTON_HOVER_NONE;
AutoRotating = FALSE;
}
void COpenGLWnd::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(COpenGLWnd)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(COpenGLWnd, CDialog)
//{{AFX_MSG_MAP(COpenGLWnd)
ON_WM_PAINT()
ON_WM_SIZE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_TIMER()
//}}AFX_MSG_MAP
// ON_WM_KILLFOCUS()
//ON_WM_SETCURSOR()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// COpenGLWnd message handlers
BOOL COpenGLWnd::Create(int cx,int cy, CWnd* pParentWnd,bool FullScreen)
{
bool re;
re=CDialog::Create(IDD, pParentWnd);
if(re)
{
SetWindowPos(NULL,0,0,cx,cy,SWP_NOZORDER|SWP_NOMOVE);
}
return re;
}
void COpenGLWnd::OnCancel()
{
if(IsZoomed())
{
ShowWindow(SW_RESTORE);
((COpenGLApp *)AfxGetApp())->IsZoomed = TRUE;
}
else
((COpenGLApp *)AfxGetApp())->IsZoomed = FALSE;
CRect rc;
GetWindowRect(rc);
((COpenGLApp *)AfxGetApp())->Wnd_h = rc.Height();
((COpenGLApp *)AfxGetApp())->Wnd_w = rc.Width();
wcp->GetWindowRect(rc);
((COpenGLApp *)AfxGetApp())->CtrlPane_X = rc.left;
((COpenGLApp *)AfxGetApp())->CtrlPane_Y = rc.top;
wcp->UpdateData();
((COpenGLApp *)AfxGetApp())->bAutoClose = wcp->bAutoClose;
wcp->DestroyWindow();
DestroyWindow();
AfxGetApp()->m_pMainWnd = NULL;
AfxGetApp()->PostThreadMessage(WM_QUIT,0,0);
};
BOOL COpenGLWnd::DestroyWindow()
{
if(GLInitialized)
{
HGLRC hrc;
hrc = ::wglGetCurrentContext();
::wglMakeCurrent(NULL, NULL);
if (hrc)
{
::wglDeleteContext(hrc);
hrc = NULL;
}
}
if (m_pDC)
{
delete m_pDC;
m_pDC = NULL;
}
return CDialog::DestroyWindow();
}
BOOL COpenGLWnd::OnInitDialog()
{
hHand = ::LoadCursor(NULL,IDC_HAND);
hArrow = ::LoadCursor(NULL,IDC_ARROW);
CDialog::OnInitDialog();
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
GetClientRect(&m_oldRect);
if(!InitGL())
{
CString str;
str.Format(_T("Failed to initialize OpenGL subsystem. \15\12Error Code: 0x%08x\15\12\15\12A hardware 3D accelerator is required.\15\12Or you'd better update your video card driver.\15\12\15\12Please contact e_boris2002@hotmail.com with\15\12the error code. Thank you for the cooperation."),GetLastError());
AfxMessageBox(str,MB_ICONSTOP);
return TRUE;
}
GLInitialized = true;
Tree.Initialize();
ASSERT(wcp);
//// Detect Renderer
{CString str = _T(" Natural Tree Simulation on ");
LPCSTR pStr;
pStr = (LPCSTR)glGetString(GL_RENDERER);
if(pStr)
{
str += pStr;
pStr = (LPCSTR)glGetString(GL_VERSION);
if(pStr)
{
str+=_T(" (");
str+=pStr;
str+=_T(")");
}
}
else
str += "UnknownDevice";
SetWindowText(str);
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void COpenGLWnd::OnPaint()
{
CPaintDC dc(this); // device context for painting
if(GLInitialized)Render();
}
#define FovZoom 0.6f
void COpenGLWnd::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
Width = cx,Height = cy;
if(cy > 0 && GLInitialized)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-FovZoom,FovZoom,-1.0,1.0,1.0,3.0e10);
glViewport(0, 0, cx, cy);
Aspect = (double)cy/(double)cx;
Render();
}
}
//#define BONE_ONLY
bool COpenGLWnd::InitGL()
{
//Setup GL pixel format
m_pDC = new CClientDC(this);
ASSERT(m_pDC);
if(SetPixel())
{
PIXELFORMATDESCRIPTOR pfd;
int n;
HGLRC hrc;
n =::GetPixelFormat(m_pDC->GetSafeHdc());
::DescribePixelFormat(m_pDC->GetSafeHdc(), n, sizeof(pfd), &pfd);
hrc = wglCreateContext(m_pDC->GetSafeHdc());
if(hrc == NULL)return FALSE;
if(!wglMakeCurrent(m_pDC->GetSafeHdc(), hrc))return FALSE;
glClearDepth(5.0f);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//TODO: Initialize some optional GL options
CglTexture::Enable();
glEnable(GL_LIGHTING);
static GLfloat LightAmb[]={1.0f,1.0f,1.0f,0.5f};
static GLfloat LightDif[]={1.0f,1.0f,1.0f,0.8f};
// glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,spotDir);
// glLightfv(GL_LIGHT0,GL_POSITION,spotPos);
glLightfv(GL_LIGHT0,GL_AMBIENT,LightAmb);
glLightfv(GL_LIGHT0,GL_DIFFUSE,LightDif);
static float LightPos[4]={1.0f,0.0f,0.0f,0.0f};
glLightfv(GL_LIGHT0,GL_POSITION,LightPos);
glEnable(GL_LIGHT0);
/////////////////////////////////////////////////////////////
//Must disable this when rendering leaf, for, on ATI Riva128 I740 Video card,there will
//be a box-edge on every leaf. I think this option is collision with
//GL_ALPHA_TEST
//
//glEnable(GL_POLYGON_SMOOTH);
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
#ifdef BONE_ONLY
glPolygonMode(GL_FRONT,GL_LINE);
glPolygonMode(GL_BACK,GL_LINE);
// Antialiasing
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
#endif
#ifdef POINT_ONLY
glPolygonMode(GL_FRONT,GL_POINT);
glPolygonMode(GL_BACK,GL_POINT);
#endif
#ifdef SINLE_SIDE
glPolygonMode(GL_FRONT,GL_POINT);
glPolygonMode(GL_BACK,GL_FILL);
#endif
}
else
{
return false;
}
return TRUE;
}
bool COpenGLWnd::SetPixel()
{
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW| // support window
PFD_SUPPORT_OPENGL| // support OpenGL
PFD_DOUBLEBUFFER, // double buffered,
PFD_TYPE_RGBA, // RGBA type
16, // 24-bit 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
24, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int pixelformat;
if ( (pixelformat = ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd)) == 0 )
{
return FALSE;
}
if (SetPixelFormat(m_pDC->GetSafeHdc(), pixelformat, &pfd) == FALSE)
{
return FALSE;
}
return TRUE;
}
void COpenGLWnd::Render()
{
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
///////////////////////////////////////////////////////
//Move the Camera,set view position
glRotatef(RotX,1,0,0);
glTranslatef(0,OrgY,OrgZ);
glScalef(0.3,0.3,0.3);
///////////////////////////////////////////////////////////
//render the whole world
CTree::TotalVertex = CTree::TotalQuad = CTree::TotalNode = CTree::TotalMemory = 0;
//////////////////////////////////////////////
//1. Render Sky and ground
#ifndef BONE_ONLY
glScalef(Aspect,1.0,1.0);
glPushMatrix();
glRotatef(RotY,0,1,0); //Rotate the camera
Env.RenderSky();
Env.RenderGround();
glPopMatrix();
glScalef(1.0f,1.0f/FovZoom,1.0f);
//////////////////////////////////////////////
//////////////////////////////////////////////
//2. Render shadow of the tree
Tree.DrawShadow(-RotY);
#endif
//////////////////////////////////////////////
//////////////////////////////////////////////
//3. Render the tree
#ifdef BONE_ONLY
CglTexture::Enable(FALSE);
glColor3f(1.0,1.0,1.0);
#endif
glPushMatrix();
glRotatef(RotY,0,1,0);//Rotate the camera
Tree.Render();
glPopMatrix();
//////////////////////////////////////////////////
#ifndef BONE_ONLY
///////////////////////////////////////////////
//4 Render Toolbar and help
glLoadIdentity();
glScaled(FovZoom*2.0,2,1);
#define BUTTON_ALPHA 0.6
#define HOVER_BUTTON_ALPHA 0.85
#define TITLE_ALPHA 0.65
{
CTitlePlane::ABS_POSITION_CTX ctx;
ctx = CTitlePlane::BeginRenderAbsPosition(this);
{
const static RECT TexPart[] =
{
{0,155,256,256}, //Help for buttons
{0,0,255,155}, //Help for Title
{0,0,37,128}, //By Boris
{37,0,256,61}, //Title Tree
{44,61,256,128} //Bottons
};
//By Boris J. Wang
glPushMatrix();
glTranslatef(5,0,0);
Toolbar.SetAbsPosition(TP_ALIGN_TOP,TP_ALIGN_TOP,30,200);
Toolbar.RenderAbsPosition(ctx,TITLE_ALPHA,&TexPart[2]);
glPopMatrix();
//Fractal Tree
glPushMatrix();
glTranslatef(27.5,-4.3,0);
Toolbar.SetAbsPosition(TP_ALIGN_TOP,TP_ALIGN_TOP,220,60);
Toolbar.RenderAbsPosition(ctx,TITLE_ALPHA,&TexPart[3]);
glPopMatrix();
//Buttons
glPushMatrix();
glTranslatef(-7,7,0);
Toolbar.SetAbsPosition(TP_ALIGN_BTM,TP_ALIGN_BTM,200,60);
Toolbar.RenderAbsPosition(ctx,ButtonHoverState?HOVER_BUTTON_ALPHA:BUTTON_ALPHA,&TexPart[4]);
glPopMatrix();
if(ShowTitle)
{
//Help for buttons
glPushMatrix();
glTranslatef(0,47.5,0);
Help.SetAbsPosition(TP_ALIGN_BTM,TP_ALIGN_BTM,256,256-155);
Help.RenderAbsPosition(ctx,TITLE_ALPHA,&TexPart[0]);
glPopMatrix();
//Help for Title
glPushMatrix();
glTranslatef(29,-52,0);
Help.SetAbsPosition(TP_ALIGN_TOP,TP_ALIGN_TOP,300,120);
Help.RenderAbsPosition(ctx,TITLE_ALPHA,&TexPart[1]);
glPopMatrix();
}
}
CTitlePlane::EndRenderAbsPosition();
}
#undef BUTTON_ALPHA
#undef HOVER_BUTTON_ALPHA
#undef TITLE_ALPHA
#endif
////////////////////////////////////////////////
////////////////////////////////////////////////
//5. Flip scene
glFinish();
SwapBuffers(wglGetCurrentDC());
////////////////////////////////////////////////
////////////////////////////////////////////////
//End of rendering
////////////////////////////////////////////////
//Output status information
CString str;
str.Format(_T("[Model] Object: %d Nodes\15\12 Memory: %1.2f KB\15\12[Scene] Elevation: %3.1f Deg.\15\12 Pos.(%2.1f,%2.1f,%2.1f)\15\12[Mesh.] %7d Vertices\15\12 %7d Quadrangles"),
CTree::TotalNode,(float)CTree::TotalMemory/1024.0f,
-RotX,
sin(Deg2Rad(RotY))*OrgZ,-OrgY,cos(Deg2Rad(RotY))*OrgZ,
CTree::TotalVertex + 50,CTree::TotalQuad + 22
);
//str.Format("[模型] 总结点个数: %d个\15\12 存储器分配: %1.2f KB\15\12[场景] 视点: (%2.1f,%2.1f,%2.1f)\15\12 观察仰角: %3.2f度\15\12[渲染] 总顶点个数: %d个\15\12 四边形个数: %d个",
// CTree::TotalNode,(float)CTree::TotalMemory/1024.0f,
// sin(Deg2Rad(RotY))*OrgZ,-OrgY,cos(Deg2Rad(RotY))*OrgZ,
// RotX,
// CTree::TotalVertex + 50,CTree::TotalQuad + 22
// );
//str.Format("%f,%f,%f,%f,%f",OrgX,OrgY,OrgZ,RotX,RotY);
wcp->DisplayStatus(str);
}
void COpenGLWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
if(ButtonHoverState == BUTTON_HOVER_NONE)
{
MouseEvent = MS_LBUT;
MouseX = (point.x - Width>>1)/(float)(Width>>1);
MouseY = (Height>>1 - point.y)/(float)(Height>>1);
SetTimer(MOUSE_TIMER,MoveSpeed,NULL);
SetCapture();
}
if(ButtonHoverState)
SetCursor(hHand);
else
SetCursor(hArrow);
}
void COpenGLWnd::OnLButtonUp(UINT nFlags, CPoint point)
{
if(MouseEvent != MS_NONE)
{
MouseEvent = MS_NONE;
KillTimer(MOUSE_TIMER);
ReleaseCapture();
return;
}
if(ButtonHoverState)
SetCursor(hHand);
else
SetCursor(hArrow);
MSG msg;
msg.message = WM_KEYDOWN;
switch(ButtonHoverState)
{
case BUTTON_HOVER_SPIN:
{
msg.wParam = 's';
break;
}
case BUTTON_HOVER_HELP:
{
msg.wParam = 'h';
break;
}
case BUTTON_HOVER_TREE:
{
msg.wParam = 't';
break;
}
case BUTTON_HOVER_MAIL:
{
msg.wParam = 'm';
break;
}
case BUTTON_HOVER_NONE:
{
return;
}
default:ASSERT(0);
}
PreTranslateMessage(&msg);
}
void COpenGLWnd::OnMouseMove(UINT nFlags, CPoint point)
{
if(MouseEvent)
{
MouseX = (point.x - Width>>1)/(float)(Width>>1);
MouseY = (Height>>1 - point.y)/(float)(Height>>1);
}
int v = Height - point.y;
bool Org_State = ButtonHoverState;
ButtonHoverState = BUTTON_HOVER_NONE;
if(v>32 && v<62)
{
v = Width - point.x;
if(v>18 && v<39)
{
ButtonHoverState = BUTTON_HOVER_HELP;
}
else
if(v>59 && v<80)
{
ButtonHoverState = BUTTON_HOVER_TREE;
}
else
if(v>102 && v<120)
{
ButtonHoverState = BUTTON_HOVER_MAIL;
}
else
if(v>142 && v<161)ButtonHoverState = BUTTON_HOVER_SPIN;
}
if(ButtonHoverState)
SetCursor(hHand);
else
SetCursor(hArrow);
if(Org_State != (bool)ButtonHoverState && !AutoRotating)Render();
}
void COpenGLWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
MouseEvent = MS_RBUT;
MouseX = (point.x - Width>>1)/(float)(Width>>1);
MouseY = (Height>>1 - point.y)/(float)(Height>>1);
SetTimer(MOUSE_TIMER,MoveSpeed,NULL);
SetCapture();
}
void COpenGLWnd::OnRButtonUp(UINT nFlags, CPoint point)
{
MouseEvent = MS_NONE;
KillTimer(MOUSE_TIMER);
ReleaseCapture();
}
BOOL COpenGLWnd::PreTranslateMessage(MSG* pMsg)
{
////Prevent MFC from handling F1 help hotkey
if (pMsg->message == 0x4d)return TRUE;
if(pMsg->message == WM_KEYDOWN)
{
if( pMsg->wParam == 's' || pMsg->wParam == 'S' )
{
if(!AutoRotating)
{
SetTimer(ROTATE_TIMER,50,NULL);
AutoRotating = TRUE;
}
else
{
KillTimer(ROTATE_TIMER);
AutoRotating = FALSE;
}
return true;
}
if( pMsg->wParam == 'h' || pMsg->wParam == 'H' )
{
KeyEvent = pMsg->wParam;
ToggleHelpWnd();
return true;
}
if( pMsg->wParam == 't' || pMsg->wParam == 'T' )
{
wcp->ShowWindow(SW_SHOW);
wcp->SetFocus();
return true;
}
if( pMsg->wParam == 'm' || pMsg->wParam == 'M' )
{
ShellExecute(NULL,_T("open"),_T("mailto:e_boris2002@hotmail.com?subject=Feedback_to_Fractal_Tree_(OpenGL_Version)"),NULL,NULL,SW_SHOW);
return true;
}
if(KeyEvent == pMsg->wParam)return true;
KeyEvent = pMsg->wParam;
if( KeyEvent == VK_LEFT || KeyEvent == VK_UP ||
KeyEvent == VK_RIGHT || KeyEvent == VK_DOWN )
{
OnTimer(KEY_TIMER);
SetTimer(KEY_TIMER,MoveSpeed,NULL);
return true;
}
}
if(pMsg->message == WM_KEYUP)
{
KeyEvent = pMsg->wParam;
if( KeyEvent == VK_LEFT || KeyEvent == VK_UP ||
KeyEvent == VK_RIGHT || KeyEvent == VK_DOWN )
{
KeyEvent = 0;
KillTimer(KEY_TIMER);
return true;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
#define XY_MOVING_SPEED 0.4f
#define XY_ANGLE_SPEED 4.0f
void COpenGLWnd::OnTimer(UINT nIDEvent)
{
if(nIDEvent == MOUSE_TIMER)
{
ASSERT(MouseEvent);
float oy,oz;
oy = sin(Deg2Rad(RotX))*XY_MOVING_SPEED;
oz = cos(Deg2Rad(RotX))*XY_MOVING_SPEED;
if(MouseEvent == MS_LBUT)
{// go forward
OrgY += oy;
if(OrgY >= -1.5 || OrgY <= -60)
{
OrgY -= oy;
}
OrgZ += oz;
if(OrgZ >= -3.2 || OrgZ < -130)
{
OrgZ -= oz;
}
}
else
{// go backward
ASSERT(MouseEvent == MS_RBUT);
OrgY -= oy;
if(OrgY >= -1.5 || OrgY <= -60)
{
OrgY += oy;
}
OrgZ -= oz;
if(OrgZ >= -3.2 || OrgZ < -130)
{
OrgZ += oz;
}
}
Render();
return;
}
if(nIDEvent == KEY_TIMER)
{
ASSERT(KeyEvent);
switch(KeyEvent)
{
case VK_UP:
{
RotX -= XY_ANGLE_SPEED/2;
Render();
break;
}
case VK_DOWN:
{
RotX += XY_ANGLE_SPEED/2;
Render();
break;
}
case VK_LEFT:
{
RotY -= XY_ANGLE_SPEED;
Render();
break;
}
case VK_RIGHT:
{
RotY += XY_ANGLE_SPEED;
Render();
break;
}
default:
ASSERT(0);
}
return;
}
if(nIDEvent == ROTATE_TIMER)
{
RotY += XY_ANGLE_SPEED/8;
Render();
return;
}
}
void COpenGLWnd::ToggleHelpWnd()
{
if(ShowTitle)
{
ShowTitle = false;
Render();
}
else
{
ShowTitle = true;
Render();
}
}
void COpenGLWnd::RebuildTreeModel()
{
Tree.Rebuild(CTree::TreeDepth);
}