目 录
第1章 绪论 1
1.1 研究背景 1
1.2 内存网格简化算法 1
1.2.1 顶点聚类 1
1.2.2 区域合并 2
1.2.3 迭代式消除 4
1.2.4 随机重采样 5
1.3 三维模型简化算法 6
1.3.1 分片简化 6
1.3.2 使用外部数据结构 7
1.3.3 网格批处理 9
1.3.4 流式简化 10
1.3.5 小结 11
1.4 自适应等值面生成算法 11
1.5 论文的主要内容及章节安排 12
第2章 基于点分片的三维模型简化 13
2.1 边收缩操作对边界拓扑结构的自动保持 13
2.2 算法概述 14
2.3 LRU缓存系统 19
2.4 分片文件格式设计 21
2.5 单个分片的简化 22
2.6 分片合并 23
2.7 执行结果 24
2.8 本章小结 27
第3章 三维模型简化 31
3.1 算法概述 31
3.2 等值面生成过程中终结信息的判断 31
3.3 生成边界的延伸与模型的简化 34
3.4 顶点拓扑关系的重建与网格数据结构的设计 35
3.5 执行结果 36
3.6 本章小结 38
第4章 基于八叉树单形分割的并行等值面生成 41
4.1 算法概述 41
4.2 自适应八叉树的四面体分割 42
4.3 对偶点的求取 43
4.4 八叉树的建立 45
4.5 最小边的查找 47
4.6 四面体与等值面生成 49
4.7 执行结果 51
4.8 本章小结 52
第5章 总结与展望 53
5.1 总结 53
5.2 展望 53
参考文献 54
致 谢 57
声 明 58
第2章 基于点分片的三维模型简化
使用分片进行三维模型简化具有简化质量好、操作较为简单等优势。但现存网格分片算法有一些劣势:
(1). 大部分算法由于分片简化的过程中不能对边界进行简化,因此必须对合并后的网格再进行一次处理。
(2). 有的方法在合并网格的过程中需要将所有简化后分片的顶点都载入内存,对简化后网格的大小做出了限制。
相对于原分片简化算法的缺点,本章节算法的主要特点是:
(1). 由于使用了对点而非对三角形进行分割的方法,简化过程不需要对分片的边界进行保持,分片合并后不会产生严重的分片边界与内部顶点的密度不统一,因此不需要在分片合并后再对网格进行处理。
(2). 由于算法在对分片进行合并的时候只需要保持输出网格的一部分信息,因此算法对输出网格的大小没有限制。算法可以输出一个大于内存装载能力的网格。
两种分片方法的对比如图2.1所示。其中(a)为原始网格;(b)©为对三角形进行分片的简化过程,由于边界不能被简化,导致了密度不统一;(d)(e)为对点进行分片的简化过程,由于所有点均可以被简化,因此没有产生密度不统一的情况。
2.1 边收缩操作对边界拓扑结构的自动保持
在简化分片的时候,我们选择了边收缩作为简化操作。边收缩操作最早在Hoppe的论文[19]中被提出。边收缩与边分裂是一对互逆的操作(如图2.2),它将一条边的两个端点合并为一个新的顶点,并消除邻接这条边的三角形。
如图2.1-(a)、2.1-(b)所示,在使用对点进行分片的三维模型简化算法中,使用了边收缩的内存简化不会产生拓扑结构的不一致性。这是因为,边收缩对分片的拓扑结构改变可以保持在分片之内,而不影响其他分片。这保证了分片之间简化的独立性,使得边界被简化变得可能。
如图2.3所示,AB为一条处在边界上的可被收缩的边,CD为另一边界上的可收缩的边。在收缩操作中,A与B收缩为点A’,C与D收缩为点C’。两次不同分片中的收缩操作所影响的不属于本分片的三角形仅仅为处在分片边界上的三角形(图中的亮蓝色三角形区域)。在后续的处理中,算法将移除这些退化的边界三角形。需要注意的是,在边被收缩的时候,需要检查是否有代表顶点跨越了边界(如图2.4所示)。这个问题可以通过判断收缩后的点是否落在了本分片的包围盒之内来解决。如果出现了越界的情况,则可以使用两个端点的中点作为代表顶点。
第2章 基于点分片的三维模型简化
使用分片进行三维模型简化具有简化质量好、操作较为简单等优势。但现存网格分片算法有一些劣势:
(1). 大部分算法由于分片简化的过程中不能对边界进行简化,因此必须对合并后的网格再进行一次处理。
(2). 有的方法在合并网格的过程中需要将所有简化后分片的顶点都载入内存,对简化后网格的大小做出了限制。
相对于原分片简化算法的缺点,本章节算法的主要特点是:
(1). 由于使用了对点而非对三角形进行分割的方法,简化过程不需要对分片的边界进行保持,分片合并后不会产生严重的分片边界与内部顶点的密度不统一,因此不需要在分片合并后再对网格进行处理。
(2). 由于算法在对分片进行合并的时候只需要保持输出网格的一部分信息,因此算法对输出网格的大小没有限制。算法可以输出一个大于内存装载能力的网格。
两种分片方法的对比如图2.1所示。其中(a)为原始网格;(b)©为对三角形进行分片的简化过程,由于边界不能被简化,导致了密度不统一;(d)(e)为对点进行分片的简化过程,由于所有点均可以被简化,因此没有产生密度不统一的情况。
2.1 边收缩操作对边界拓扑结构的自动保持
在简化分片的时候,我们选择了边收缩作为简化操作。边收缩操作最早在Hoppe的论文[19]中被提出。边收缩与边分裂是一对互逆的操作(如图2.2),它将一条边的两个端点合并为一个新的顶点,并消除邻接这条边的三角形。
如图2.1-(a)、2.1-(b)所示,在使用对点进行分片的三维模型简化算法中,使用了边收缩的内存简化不会产生拓扑结构的不一致性。这是因为,边收缩对分片的拓扑结构改变可以保持在分片之内,而不影响其他分片。这保证了分片之间简化的独立性,使得边界被简化变得可能。
如图2.3所示,AB为一条处在边界上的可被收缩的边,CD为另一边界上的可收缩的边。在收缩操作中,A与B收缩为点A’,C与D收缩为点C’。两次不同分片中的收缩操作所影响的不属于本分片的三角形仅仅为处在分片边界上的三角形(图中的亮蓝色三角形区域)。在后续的处理中,算法将移除这些退化的边界三角形。需要注意的是,在边被收缩的时候,需要检查是否有代表顶点跨越了边界(如图2.4所示)。这个问题可以通过判断收缩后的点是否落在了本分片的包围盒之内来解决。如果出现了越界的情况,则可以使用两个端点的中点作为代表顶点。
(a) (b)
© (d)
图2.1 (a)、(b):对三角形进行分片的简化过程。©、(d):对点进行分片的简化过程。图中,不同颜色(紫,绿,灰)的三角形、边和顶点代表不同分片中的元素。(a)、(b)中白色边和顶点代表对三角形分割的分片边界,边界附近黑色的边代表不能被收缩的边。©、(d)中的白色三角形代表对点分割的分片边界,边界上黑色的边代表不能被收缩的边。
并不是所有简化操作都具有这种分片边界简化的独立性。以顶点移除为例,在图2.5中,不同分片中的边界顶点A与B被执行了顶点移除操作,对他们的操作影响到了分片之外的顶点。图2.5中对B的移除实际上是对B和A做了一次边收缩,对A的移除则是与B做了一次边收缩并在由绿色标记的边上做了一次边交换(边收缩和边交换操作可以产生任意拓扑结构的简化网格[37])。在论文[12]中,作者在三角化的时候试图优化产生的三角形的质量,这使得三角化的方法具有不确定性。
事实上,所有顶点聚类操作都具有对拓扑结构的自动保持特性。边收缩操作可以转化为顶点聚类操作。
2.2 算法概述
本章算法的流程如图2.6所示。算法首先对网格中的顶点做一次扫描,获取网格的包围盒,并将顶点的信息写入二进制顶点文件。二进制顶点文件用来在后续的对三角形进行分片的过程中,根据顶点索引来取得三维坐标。使用二进制文件是因为,这样可以根据顶点的索引来确定数据在文件中的位置。算法随后对网格进行分割。被分割的网格会被写入分片文件中,每个分片将进行一次内存简化。最后,算法合并所有分片并生成最终的网格。
(a) (b)
© (d)
图2.1 (a)、(b):对三角形进行分片的简化过程。©、(d):对点进行分片的简化过程。图中,不同颜色(紫,绿,灰)的三角形、边和顶点代表不同分片中的元素。(a)、(b)中白色边和顶点代表对三角形分割的分片边界,边界附近黑色的边代表不能被收缩的边。©、(d)中的白色三角形代表对点分割的分片边界,边界上黑色的边代表不能被收缩的边。
并不是所有简化操作都具有这种分片边界简化的独立性。以顶点移除为例,在图2.5中,不同分片中的边界顶点A与B被执行了顶点移除操作,对他们的操作影响到了分片之外的顶点。图2.5中对B的移除实际上是对B和A做了一次边收缩,对A的移除则是与B做了一次边收缩并在由绿色标记的边上做了一次边交换(边收缩和边交换操作可以产生任意拓扑结构的简化网格[37])。在论文[12]中,作者在三角化的时候试图优化产生的三角形的质量,这使得三角化的方法具有不确定性。
事实上,所有顶点聚类操作都具有对拓扑结构的自动保持特性。边收缩操作可以转化为顶点聚类操作。
2.2 算法概述
本章算法的流程如图2.6所示。算法首先对网格中的顶点做一次扫描,获取网格的包围盒,并将顶点的信息写入二进制顶点文件。二进制顶点文件用来在后续的对三角形进行分片的过程中,根据顶点索引来取得三维坐标。使用二进制文件是因为,这样可以根据顶点的索引来确定数据在文件中的位置。算法随后对网格进行分割。被分割的网格会被写入分片文件中,每个分片将进行一次内存简化。最后,算法合并所有分片并生成最终的网格。
//#define WIN32_LEAN_AND_MEAN // we can't use this because we use the OPENFILENAME struct.
#include <windows.h> // Header File For Windows
#include <sys/stat.h>
#include <cstdio>
#if defined (_MSC_VER) && (_MSC_VER >= 1020)
#pragma warning(disable:4710) // function not inlined
#pragma warning(disable:4702) // unreachable code
#pragma warning(disable:4514) // unreferenced inline function has been removed
#pragma warning(disable:4786) // disable "identifier was truncated to '255' characters in the browser information" warning in Visual C++ 6*
#endif
#include "resource.h"
#include "mesh.h"
#include "pmesh.h"
#include "glmodelwin.h"
// Menu positions
const int SIMPLICATION_MENU = 1;
const int UPDATE_MENU = 2;
const int FILL_MENU = 3;
const int SHADING_MENU = 4;
// App. Instance
HINSTANCE g_hInstance = NULL;
// Triangle model
Mesh* g_pMesh = NULL;
// Progressive Mesh
PMesh* g_pProgMesh = NULL;
// Edge Collapse Options
PMesh::EdgeCost g_edgemethod = PMesh::QUADRICTRI;
// OpenGL Window
glModelWindow* g_pWindow = NULL;
// file name
char g_filename[256] = {'\0'};
LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT WINAPI aboutDlgProc( HWND, UINT, WPARAM, LPARAM);
// allow user to chose which mesh to load.
void loadMesh()
{
static char szFilter[]= "Ply files (*.ply)\0*.ply\0";
OPENFILENAME ofn;
char pszFileLocn[256] = {'\0'};
// Set up OPENFILENAME struct to use commond dialog box for open
ZeroMemory(&ofn, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(ofn); // size of struct
ofn.hwndOwner = NULL; // window that owns Dlg
ofn.lpstrFilter = szFilter; // Filter text
ofn.lpstrFile = pszFileLocn; // File name string
ofn.nMaxFile = sizeof(pszFileLocn); // size of file name
ofn.Flags = OFN_HIDEREADONLY; // don't display "Read Only"
ofn.lpstrDefExt = "ply"; // extension name
ofn.lpstrTitle = "Open Mesh File"; // title of dlg box
// call common dlg control for file open
if (!GetOpenFileName(&ofn)) {
return ;
}
// see if file exists
struct stat fileStat;
if (stat(ofn.lpstrFile, &fileStat))
{
char errormsg[1024];
sprintf(errormsg, "%s not found.", ofn.lpstrFile);
MessageBox(NULL,errormsg,"File Not Found Error",MB_OK | MB_ICONINFORMATION);
return ;
}
if (!g_pProgMesh) // 1st time through
{
// load "plus sign" cursor
SetClassLong(g_pWindow->getHWnd(), GCL_HCURSOR, (LONG) LoadCursor(NULL, IDC_CROSS));
}
delete g_pMesh;
g_pMesh = NULL; // not necessary, but a nice CYA habit
delete g_pProgMesh;
g_pProgMesh = NULL;
SetWindowText(g_pWindow->getHWnd(), "Jeff Somers Mesh Simplification Viewer - (loading....)");
g_pMesh = new Mesh(ofn.lpstrFile);
strcpy(g_filename, ofn.lpstrFile);
if (g_pMesh) g_pMesh->Normalize();// center mesh around the origin & shrink to fit
g_pProgMesh = new PMesh(g_pMesh, g_edgemethod );
// reset the position of the mesh
g_pWindow->resetOrientation();
g_pWindow->displayWindowTitle();
}
// User has selected a new mesh simplification algorithm
void changeSimplificationAlgorithm(const char* name, const PMesh::EdgeCost &ec)
{
if (ec != g_edgemethod)
{
char temp[1024];
strcpy(temp, "Jeff Somers Mesh Simplification Viewer - ");
strcat(temp, name);
SetWindowText(g_pWindow->getHWnd(), temp);
g_edgemethod = ec;
if (0 != strlen(g_filename))
{
strcat(temp, " (loading....)");
SetWindowText(g_pWindow->getHWnd(), temp);
if (g_pMesh == NULL)
{
g_pMesh = new Mesh(g_filename);
if (g_pMesh) g_pMesh->Normalize();
}
delete g_pProgMesh;
g_pProgMesh = new PMesh(g_pMesh, g_edgemethod);
g_pWindow->displayWindowTitle();
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
}
}
// User selected a menu item
int handleMenuCommands(WPARAM wParam, LPARAM lParam)
{
const int REDUCE_TRI_PERCENT = 5; // when page up/page down, inc/dec # tris by this percent
// (percent applies to # of tris in *original* mesh.)
const int NUM_PAGEUPDN_INTERVALS = 100/REDUCE_TRI_PERCENT;
switch (LOWORD(wParam))
{
case IDM_FILE_EXIT:
{
SendMessage (g_pWindow->getHWnd(), WM_CLOSE, wParam, lParam) ;
return 0 ;
}
case IDM_FILE_OPEN:
{
loadMesh();
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
return 0;
}
case IDM_UPDATE_ADDONETRI: // split vertex
{
if (g_pProgMesh)
{
bool ret = g_pProgMesh->splitVertex();
if (!ret) MessageBeep(0);
g_pWindow->displayWindowTitle();
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
}
case IDM_UPDATE_REMOVEONETRI: // collapse vertex
{
if (g_pProgMesh)
{
bool ret = g_pProgMesh->collapseEdge();
if (!ret) MessageBeep(0);
g_pWindow->displayWindowTitle();
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
}
case IDM_UPDATE_INCREASETRI5PERC: // split vertices
{
if (g_pProgMesh)
{
int size = (g_pProgMesh->numEdgeCollapses()) / NUM_PAGEUPDN_INTERVALS;
if (size == 0) size = 1;
bool ret = true;
for (int i = 0; ret && i < size; ++i) {
ret = g_pProgMesh->splitVertex();
}
if (!ret) MessageBeep(0);
g_pWindow->displayWindowTitle();
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
}
case IDM_UPDATE_REDUCETRI5PERC: // collapse vertices
{
if (g_pProgMesh)
{
int size = (g_pProgMesh->numEdgeCollapses()) / NUM_PAGEUPDN_INTERVALS;
if (size == 0) size = 1;
bool ret = true;
for (int i = 0; ret && i < size; ++i) {
ret = g_pProgMesh->collapseEdge();
}
if (!ret) MessageBeep(0);
g_pWindow->displayWindowTitle();
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
}
case IDM_METHOD_QUADRIC:
{
changeSimplificationAlgorithm("Quadric", PMesh::QUADRIC);
return 0;
}
case IDM_METHOD_QUADRICTRI:
{
changeSimplificationAlgorithm("Quadric Weighted by Triangle Area", PMesh::QUADRICTRI );
return 0;
}
case IDM_METHOD_MELAX:
{
changeSimplificationAlgorithm("Melax", PMesh::MELAX);
return 0;
}
case IDM_METHOD_SHORTEST:
{
changeSimplificationAlgorithm("Shortest Edge", PMesh::SHORTEST);
return 0;
}
case IDM_FULLSCREEN_FULLSCREEN_640X480:
{
// Set settings for new display mode
g_pWindow->flipFullScreen(640, 480);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
return 0;
}
case IDM_FULLSCREEN_FULLSCREEN_800X600:
{
// Set settings for new display mode
g_pWindow->flipFullScreen(800, 600);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
return 0;
}
case IDM_FULLSCREEN_FULLSCREEN_1024X768:
{
// Set settings for new display mode
g_pWindow->flipFullScreen(1024, 768);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
return 0;
}
case IDM_FULLSCREEN_FULLSCREEN_1280X1024:
{
// Set settings for new display mode
g_pWindow->flipFullScreen(1280, 1024);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
return 0;
}
case IDM_FULLSCREEN_FULLSCREEN_1600X1200:
{
// Set settings for new display mode
g_pWindow->flipFullScreen(1600, 1200);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
return 0;
}
case IDM_FILL_FILLED:
{
if (!g_pWindow->isFillTriMode())
{
g_pWindow->setFillTriMode(true);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
}
case IDM_FILL_WIREFRAME:
{
if (g_pWindow->isFillTriMode())
{
g_pWindow->setFillTriMode(false);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
}
case IDM_SHADING_FLATSHADING:
{
if (g_pWindow->isSmoothShadingMode())
{
g_pWindow->setSmoothShadingMode(false);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
}
case IDM_SHADING_SMOOTHSHADING:
{
if (!g_pWindow->isSmoothShadingMode())
{
g_pWindow->setSmoothShadingMode(true);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
}
case IDM_HELP_ABOUT:
{
DialogBox( g_hInstance, MAKEINTRESOURCE(IDD_ABOUT_DIALOG), g_pWindow->getHWnd(), (DLGPROC)
aboutDlgProc );
return 0;
}
default:
{
break;
}
}
return 0;
}
// The popup menu is coming down. Find out if menu items
// are currently set or reset, and gray out the inappropriate items.
void handleInitMenuPopup(WPARAM wParam, int menu)
{
if (menu == SIMPLICATION_MENU)
{
switch(g_edgemethod)
{
case PMesh::QUADRICTRI:
{
CheckMenuItem ((HMENU)wParam,IDM_METHOD_QUADRICTRI, MF_CHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_QUADRIC, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_MELAX, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_SHORTEST, MF_UNCHECKED) ;
break;
}
case PMesh::QUADRIC:
{
CheckMenuItem ((HMENU)wParam,IDM_METHOD_QUADRICTRI, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_QUADRIC, MF_CHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_MELAX, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_SHORTEST, MF_UNCHECKED) ;
break;
}
case PMesh::MELAX:
{
CheckMenuItem ((HMENU)wParam,IDM_METHOD_QUADRICTRI, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_QUADRIC, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_MELAX, MF_CHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_SHORTEST, MF_UNCHECKED) ;
break;
}
case PMesh::SHORTEST:
{
CheckMenuItem ((HMENU)wParam,IDM_METHOD_QUADRICTRI, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_QUADRIC, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_MELAX, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_METHOD_SHORTEST, MF_CHECKED) ;
break;
}
}
}
else if (menu == UPDATE_MENU)
{
if (g_pMesh)
{
EnableMenuItem ((HMENU)wParam,IDM_UPDATE_REDUCETRI5PERC, MF_ENABLED) ;
EnableMenuItem ((HMENU)wParam,IDM_UPDATE_INCREASETRI5PERC, MF_ENABLED) ;
EnableMenuItem ((HMENU)wParam,IDM_UPDATE_REMOVEONETRI, MF_ENABLED) ;
EnableMenuItem ((HMENU)wParam,IDM_UPDATE_ADDONETRI, MF_ENABLED) ;
}
else
{
EnableMenuItem ((HMENU)wParam,IDM_UPDATE_REDUCETRI5PERC, MF_GRAYED) ;
EnableMenuItem ((HMENU)wParam,IDM_UPDATE_INCREASETRI5PERC, MF_GRAYED) ;
EnableMenuItem ((HMENU)wParam,IDM_UPDATE_REMOVEONETRI, MF_GRAYED) ;
EnableMenuItem ((HMENU)wParam,IDM_UPDATE_ADDONETRI, MF_GRAYED) ;
}
}
else if (menu == FILL_MENU)
{
if (g_pWindow->isFillTriMode())
{
CheckMenuItem ((HMENU)wParam,IDM_FILL_WIREFRAME, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_FILL_FILLED, MF_CHECKED) ;
}
else
{
CheckMenuItem ((HMENU)wParam,IDM_FILL_WIREFRAME, MF_CHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_FILL_FILLED, MF_UNCHECKED) ;
}
}
else if (menu == SHADING_MENU)
{
if (g_pWindow->isSmoothShadingMode())
{
CheckMenuItem ((HMENU)wParam,IDM_SHADING_FLATSHADING, MF_UNCHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_SHADING_SMOOTHSHADING, MF_CHECKED) ;
}
else
{
CheckMenuItem ((HMENU)wParam,IDM_SHADING_FLATSHADING, MF_CHECKED) ;
CheckMenuItem ((HMENU)wParam,IDM_SHADING_SMOOTHSHADING, MF_UNCHECKED) ;
}
}
}
// Handles Windows Messages
LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SYSCOMMAND:
{
switch (wParam)
{
case SC_MONITORPOWER: // Prevent screen saver, monitor from going into Power Save mode
case SC_SCREENSAVE:
return 0;
}
break;
}
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
case WM_LBUTTONDOWN:
{
SetCapture(g_pWindow->getHWnd()); // capture mouse outside client area
g_pWindow->setNewXY(LOWORD(lParam), HIWORD(lParam));
return 0;
}
case WM_LBUTTONUP:
{
ReleaseCapture(); // stop capturing mouse outside client area
return 0;
}
case WM_RBUTTONDOWN:
{
SetCapture(g_pWindow->getHWnd()); // capture mouse outside client area
g_pWindow->setNewXY(LOWORD(lParam), HIWORD(lParam));
return 0;
}
case WM_RBUTTONUP:
{
ReleaseCapture(); // stop capturing mouse outside client area
return 0;
}
case WM_MOUSEMOVE:
{
if ((wParam & MK_LBUTTON) || (wParam & MK_RBUTTON)) // if left or right button pressed
{
bool leftButton = (wParam & MK_LBUTTON) ? true : false;
bool rightButton = (wParam & MK_RBUTTON) ? true : false;;
g_pWindow->mouseMotion(LOWORD(lParam), HIWORD(lParam), leftButton, rightButton);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
}
case WM_PAINT:
{
static PAINTSTRUCT ps;
BeginPaint(g_pWindow->getHWnd(), &ps);
g_pWindow->displayMesh();
EndPaint(g_pWindow->getHWnd(), &ps);
return 0;
}
case WM_SIZE:
{
g_pWindow->reSizeScene(LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_COMMAND:
{
handleMenuCommands(wParam, lParam);
return 0;
}
case WM_CHAR:
// handle keyboard input
switch ((int)wParam) {
case VK_ESCAPE: // fall through
case VK_SPACE: // fall through
case VK_RETURN: // fall through
if (g_pWindow->isFullScreen())
{
// exit Full screen mode
g_pWindow->flipFullScreen(0, 0);
InvalidateRect(g_pWindow->getHWnd(), NULL, TRUE);
}
return 0;
default:
break;
}
case WM_INITMENUPOPUP:
{
// Deal with menu initialization
handleInitMenuPopup(wParam, LOWORD(lParam));
return 0;
}
default:
{
break;
}
}
// Pass All Unhandled Messages To DefWindowProc
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
// Handle the "About" Dialog
LRESULT CALLBACK aboutDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM )
{
switch( uMsg ) {
case WM_INITDIALOG:
return true;
case WM_COMMAND:
switch( wParam ) {
case IDOK:
EndDialog( hDlg, TRUE );
return true;
}
break;
}
return false;
}
// Main function for executable
int PASCAL WinMain(HINSTANCE hInst,HINSTANCE ,LPSTR ,int )
{
MSG msg;
HACCEL hAccel; /* Keyboard accelerators */
g_hInstance = hInst;
/* Load keyboard shortcuts */
hAccel = LoadAccelerators(g_hInstance, "MAINACCEL");
bool bFullScreen = false;
int width = 640; // initial width
int height = 480; // initial height
unsigned char depth = 16; // 16 bit color
// Create Window
g_pWindow = new glModelWindow();
if (!g_pWindow || !g_pWindow->createMyWindow(width,height,depth,bFullScreen))
{
return 0;
}
// We don't use PeekMessage here since this is not an interactive
// game. Framerate is not crucial.
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(g_pWindow->getHWnd(), hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Shutdown
g_pWindow->killMyWindow();
delete g_pProgMesh; // this is here to keep Boundschecker happy
delete g_pMesh;
return (msg.wParam);
}