Weiler-Atherton 算法 3D





5.00/5 (10投票s)
Weiler-Atherton 算法 3D 代码演示实现。
引言
2D 中多边形裁剪的 Weiler-Atherton 算法可以简要描述如下:
- 找到第一个多边形中在第二个多边形内部的所有点和线段
- 找到第二个多边形中在第一个多边形内部的所有点和线段
- 将线段链的起点与终点连接起来,作为裁剪结果多边形
3D 中对象裁剪的 Weiler-Atherton 算法与 2D 算法类似,可以简要描述如下:
- 找到第一个 3D 对象中在第二个对象内部的所有点和多边形
- 找到第二个 3D 对象中在第一个对象内部的所有点和多边形
- 将具有公共边的多边形叠加,作为裁剪结果 3D 对象
这个想法被证明是有效的。本文提供了在 MFC 中裁剪任意 3D 半二次对象的完整代码。
背景
演示项目 Weiler3D
是使用标准 MFC Appwizard 创建的。3D 半二次对象的性能与我在之前的 CodeProject 文章 "在 OpenGL MFC 中创建自己的二次曲面" 中的 class CPlaneObject
相同。3D 半二次对象是使用上述文章的技术随机创建的。
在开始构建提供的项目之前,强烈建议您查看随附的 演示文稿,以了解预期的输出。
演示说明
可执行文件 Weiler3D.exe 是使用 MSVS-2015 pro 并使用 MSVS-2010 的工具构建的。因此,Weiler3D.exe 甚至对 Windows-XP 也有效(与我之前的 CodeProject 文章不同,不需要特殊的 *.dll 文件,因为只使用了 RAM 内存)。
为了演示 Weiler3D 项目的实现,安排了一些菜单和一些特殊的加速键
- 菜单 文件->播放 - 播放/停止对象旋转(也按 Ctrl+P)
- 菜单 编辑->重置场景 - 创建两个具有随机旋转速度和恒定速度的对象(也按 空格键)
- 菜单 编辑->开始裁剪 - 如果对象相交 - 停止对象旋转和移动并开始裁剪(也按 Enter 键)
- 菜单 编辑->下一个场景 - 下一个性能场景(如果播放停止;也按 右箭头 键)
- 菜单 帮助->帮助 - 显示 帮助对话框(也按 F1 键)
- 按住鼠标左键移动 - 围绕垂直和水平轴旋转场景
- 按住鼠标右键 - 恢复默认场景位置
帮助对话框是非模态的,因此您可以直接使用菜单和加速命令,或者点击帮助对话框中的确定按钮(或双击对应的项目)
构建说明
解决方案配置必须设置为 Release,平台必须设置为 x86。
提供的项目是使用 MSVS-2015 pro 并使用 MSVS-2010 的工具开发的。因此,EXE 文件甚至对 Windows-XP 也有效。如果您不需要在 Windows-XP 中运行应用程序,只需将工具更改为 MSVS-2015 即可。
默认编码属性为 UNICODE;尽管 MBCS 编码也可用,只需更改属性即可。
即使您是第一次使用 MSVS,只需选择菜单 调试->不调试启动,程序 Weiler3D.exe 就会开始构建和运行。
项目源代码存储
Weiler3Dproj
路径中的标准源代码是使用标准 MFC 应用程序向导创建的
- Weiler3D.cpp - 定义应用程序的标准类行为
- MainFrm.cpp -
CMainFrame class
的标准实现 - CChildView.cpp -
CWnd class
的标准实现;作者使用标准 MFC 应用程序向导过程创建的消息处理过程 - DlgHelp.cpp -
CDialogEx class
的非模态实现;作者使用标准 MFC 应用程序向导过程创建的消息处理过程 - Weiler3D.rc 和 resource.h - 作者使用标准 资源向导过程创建的菜单、对话框、加速器资源
Weiler3DProj\GlobUse 路径中的特殊源代码是由作者基于标准图形和几何例程过程开发的
- Vector_2D.cpp, Poligon_2D.cpp - 2D 对象处理
- Material.cpp - 对象的颜色性能
- GLObject.cpp, BoxObject.cpp, Vector_3D.cpp, Plane.cpp, PlaneArea.cpp, PlaneObject.cpp - 3D 对象处理
代码解释
所有以下 菜单
和 加速器
命令都是用标准 MFC AppWizard 技术完成的。
1. 初始化应用程序。
CChildView class
变量在 CChildView.h 中声明
BOOL m_bPlay; //Flag of the scene to play
CDlgHelp * m_pDlgHelp;//reference to non-modal Help Dialog
int mouse_x0; //mouse x pos fixed with L button down
int mouse_y0; //mouse y pos fixed with L button down
float m_AngleY; //angle scene Y potation
float m_AngleX; //angle scene X potation
float m_AngleZ; //angle scene Z potation
float m_z; //camera z pos
全局变量在 CChildView.cpp 中声明
CChildView * m_pView = NULL; //reference this CChildView window
CObList m_polygonList; // object list of polygons to draw
并在 CLObject.cpp 中
CGLObject * m_pRedObj = NULL; //Reference to red object
CGLObject * m_pBlueObj = NULL; //Reference to blue object
CGLObject * m_pRedCut = NULL; //Reference to red object clipped inside blue
CGLObject * m_pBlueCut = NULL; //Reference to blue object clipped inside red
Vector_3D vRed(0); //Vector of red object start clipping
Vector_3D vBlue(0); //Vector of blue object start clipping
变量的初始化发生在 CChildView class
创建期间
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
m_pView = this; //reference to this CChidView window
srand((unsigned)time(NULL)); //set random seed
Init(); // initialize OpenGL borrowed from Standard Cube Sample
SetTimer(ID_TIMER_PLAY, 50, NULL); //start timer for 20 scenes per second
m_pDlgHelp = new CDlgHelp; //non-modal Help Dialog initialization
return 0;
}
2. 绘制场景程序
绘制过程将由虚过程 OnPaint
调用
void CChildView::OnPaint()
{
CPaintDC dc(this); // device context for painting
DrawMyScene();
}
BOOL CChildView::DrawMyScene(void)
{
static BOOL bBusy = FALSE; //Draw Scene busy flag
if (bBusy) //If Drawing going on return
return FALSE;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Indicate the buffers to be cleared
glLoadIdentity(); //Reset matrix
glColor3f(1.0f, 1.0f, 1.0f);
glTranslatef(0.0f, 0.0f, -m_z); //move object far-near
glRotatef(m_AngleX, 1.0f, 0.0f, 0.0f); //rotate object
glRotatef(m_AngleY, 0.0f, 1.0f, 0.0f); //around the axe
glRotatef(m_AngleZ, 0.0f, 0.0f, 1.0f); //specified
for (POSITION pos = m_glObjList.GetHeadPosition(); pos != NULL;) //for all 3D objects
{
CGLObject * pGL = (CGLObject *)m_glObjList.GetNext(pos); //get recurrent object
pGL->DrawGLObjectScene(); //Draw recurrent object
}//for(POSITION pos = m_glObjList.GetHeadPosition(); pos != NULL;)
glFinish(); //Blocks until all
//OpenGL execution is complete
SwapBuffers(wglGetCurrentDC()); //Exchanges the front and back buffers
bBusy = FALSE;
return TRUE;
}
3. 随机 3D 对象创建
使用 菜单 编辑->重置场景 命令(也按 空格键),删除所有先前的对象,并创建两个新的、具有随机旋转速度且相互移动的 3D 对象,如下图所示
3D 对象是使用我之前的 CodeProject 文章 "在 OpenGL MFC 中创建自己的二次曲面" 的技术随机创建的
void ResetScene(void)
{
while (m_glObjList.GetCount()) //while any object exists
{
CGLObject * pGl = (CGLObject *)m_glObjList.GetTail(); //get the last object
m_glObjList.RemoveTail(); //remove the last object
pGl->DeleteContents(); //clear the last object
}
CreateRandomObj(); //create random object
CGLObject * pGl = (CGLObject *)m_glObjList.GetTail(); //get the object created
pGl->cntrPt.y -= 3; //move object left
pGl->m_NameGL = _T("RED_UNIT"); //assign unit name
pGl->m_pPlaneObject->SetColor(RGB(255, 0, 0)); //assign unit color
pGl->vel.y = 0.05f; //set moving right
CreateRandomObj(); //create random object
CGLObject * pGlt = (CGLObject *)m_glObjList.GetTail(); //get the object created
pGlt->cntrPt.y += 3; //move object left
pGlt->m_NameGL = _T("BLUE_UNIT"); //assign unit name
pGlt->m_pPlaneObject->SetColor(RGB(0, 0, 255)); //assign unit color
pGlt->vel.y = -0.05f; //set moving right
m_pRedObj = pGl; m_pBlueObj = pGlt; //assign references to objects created
}
4. 3D 对象裁剪
使用 菜单 编辑->开始裁剪 命令(也按 Enter 键),对象旋转和移动停止,并开始裁剪过程
void CChildView::OnVkReturn()
{
if (m_glObjList.GetCount() != 2) //if not two objects do nothing
return;
m_pBlueObj->m_bBlend = TRUE; //set Blue object semi-transparent in demo purpose
BOOL bRemPlay = m_bPlay; //remember play flag status
m_bPlay = FALSE; //stop play
DrawMyScene(); //Draw intersected scene
Sleep(500); //wait half second to observe
CPlaneObject * pljRed = m_pRedObj->CreateTruePlaneObject(); //fix red object as it is
CPlaneObject * pljBlue = m_pBlueObj->CreateTruePlaneObject(); //fix blue object as it is
CPlaneObject plObj;
//find all the points and polygons of the red object inside the blue one:
//find all the points and polygons of the blue object inside the red one:
plObj.IntersectPlaneObjectDemo(pljBlue, pljRed);
if (m_pRedCut == NULL || m_pBlueCut == NULL) //if no intersection
{
m_bPlay = bRemPlay; //restore play flag
return; //do nothing
}
m_pRedCut->m_NameGL = m_pRedObj->m_NameGL; //assign the name red truncated object
m_pBlueCut->m_NameGL = m_pBlueObj->m_NameGL; //assign the name blue truncated object
m_pRedCut->vel = m_pRedObj->vel; //the velocity and
m_pRedCut->rot = m_pRedObj->rot; //rotation rate
m_pBlueCut->vel = m_pBlueObj->vel; //of the truncated objects
m_pBlueCut->rot = m_pBlueObj->rot; //the same as origin
vRed = m_pRedCut->cntrPt; //remember vector of red object
vBlue = m_pBlueCut->cntrPt; //remember vector of blue object
m_pRedCut->cntrPt += m_pRedCut->vel; //start moving
m_pBlueCut->cntrPt += m_pBlueCut->vel; //truncated objects
m_pRedObj->vel = m_pBlueObj->vel = 0; //original objects stopped
m_pRedObj->rot = m_pBlueObj->rot = 0;
m_bPlay = bRemPlay; //restore play flag
pljRed->DeleteContents(); //clear temporary plane objects
pljBlue->DeleteContents();
delete pljRed;
delete pljBlue;
pljRed = pljBlue = NULL;
}
在下图中,出于演示目的,蓝色对象被设置为半透明
半秒后,截断的对象开始向旁边移动并旋转,以达到演示目的,如本文标题图片所示。
接下来,截断的对象返回到裁剪位置,结果是上图中的叠加对象。
使用提供的项目开发您自己的应用程序。
您可以获取所有这些项目,将其重命名为我以前的 CodeProject 文章“一键从现有代码创建 MFC 项目”中的项目,并根据需要组合和改进代码。
或者,您可以从本项目中选取 GlobUse 目录,并通过菜单 项目->现有项 将其中包含的特殊过程包含在您的任何图形项目中。
如果引用了我的代码,将不胜感激。
关注点
我相信这个演示和代码应该对从事 3D 对象裁剪的软件人员有所帮助。
我确信 Weiler-Atherton 算法在需要时也应该适用于 4D 和 5D。
该项目是在 MFC 平台中开发的。然而,在 MFC 中开发的一切都可以转换为 Win32,反之亦然。
历史
我以前所有的CodeProject文章都是本文的前身。
我相信本文是我的下一篇即将发表文章的先驱。
工作将继续进行...