65.9K
CodeProject 正在变化。 阅读更多。
Home

Weiler-Atherton 算法 3D

2018年3月11日

CPOL

5分钟阅读

viewsIcon

18679

downloadIcon

706

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.rcresource.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文章都是本文的前身。

我相信本文是我的下一篇即将发表文章的先驱。

工作将继续进行...

© . All rights reserved.