话说,今天重装电脑程序,把VS2010重装以后,运行了几个之前写的小程序测试了一下.不经意间,我发现2年前自己参考着资料写的一个Sweep扫成程序,当时程序能够跑,但是和翼边数据结构的含义有所不同,当时费了好大的劲也没弄明白其中一个指针的处理怎么会是那个样子的,现在时间过去两年了,自己竟然还是没有抽出时间来将其弄明白,真是惭愧.也许我当时坚持查阅资料仔细研究就不会是这个样子了,而且有些东西一旦放下,再抽出整块的时间去理解它是不现实的。现在2年过去了,如果不是重装电脑,自己早忘了有这么一回事情。这些年自己在技术方面没有多少成长不怪其他,一切源于自己没有坚持钻研和探索的精神.所以遇到挫折了不要一味的怪罪环境,最主要的还是自己不够努力.下面把程序的结果贴出来,后面还要仔细再研究一下.
下面是我实现程序的例子,第一幅图是一个线框模型,第二幅图是另一个实体模型。
这个程序是在一门选修课(忘了叫什么名字了,大概叫CAD实体建模之类的)上老师布置的大程作业,当时为了做出来费了好大的力气,现在看来这只不过是个要求通过欧拉操作来实现扫成的小程序,而且算法的思想很简单,只是需要用到实体建模中的名为翼边数据结构(又叫,半边数据结构)的东西,和一个欧拉公式。我看过网络上的一些同样的介绍半边数据结构的东西,他们写的也算比较详细,但是有个很致命的问题,那就是他们只写出了核心代码,没有具体实现的环境。对于一个编程初学者而言,你光给出核心代码,他们是没有能力把这个程序给搭建起来的,因为程序不仅涉及到核心数据结构和算法,通过MFC实现鼠标点击和画图也够一般初学者头疼脑大的了。
为了方便初学者学习,也为了分享自己的一些心得,我下面不仅给出了核心代码,而且给出了整个实现程序的源代码。想学习实体建模的同学能够通过这个程序学到很多东西,对于那些想要交图形学的代码课程作业的同学来说,你也可以直接把这个交上去了吆,因为这个代码我已经测试过了,没有任何问题,可以直接运行。当然,最好你是为了学习需要,而不是为了直接down下来应付老师,而且前提是你要部署好OpenGl的库。具体的下载链接在下面。
如果运行过程中有什么问题,欢迎留言讨论。如果大家觉得此文对您有帮助,麻烦你“顶”一下,予人玫瑰,手有余香~~
运行的环境和平台是:
Windows 7, visual studio 2010
刚装了win7和VS2010。配置OpenGL的方法如下。
glut下载地址:
http://download.csdn.net/detail/daringpig/4375605
glut.h ---> C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include\gl
glut.dll,glut32.dll ---> C:\Windows\SysWOW64 (windows7 64位操作系统)
---> C:\Windows\System32 (windows7 32位操作系统)
glut.lib,glut32.lib ---> C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib
核心数据结构采用半边数据结构实现,具体的实现功能是:
通过绘制一个基本的面,然后通过该面通过指定的向量和距离就可以扫成为一个体。
具体的核心代码如下:
CSolid* CEulerDoc::mvfs(GLdouble *point) { CSolid *newSolid; CFace *newFace; CLoop *newLoop; CVertex *newVert; newSolid = new CSolid; newFace = new CFace; newLoop = new CLoop; newVert = new CVertex; // Set the coordinate of the new vertex newVert->SetCoord(point); // Add the new face to the new solid newSolid->AddFace(newFace); // Add the new vertex to the new solid newSolid->AddVertex(newVert); // Add the new loop to the new face newFace->AddLoop(newLoop); return newSolid; } CHalfEdge* CEulerDoc::mev(GLdouble *point1, GLdouble *point2, CLoop* lp) { CSolid *pSolid = lp->host_f->host_s; CVertex *v1, *newVert; CEdge *newEdge; CHalfEdge *he1, *he2; newVert = new CVertex; newEdge = new CEdge; he1 = new CHalfEdge; he2 = new CHalfEdge; // Find the start vertex of the edge v1 = pSolid->FindVertex(point1); if(!v1) { AfxMessageBox("Can't find the point1!"); return NULL; } // Set the coordinate of the new vertex newVert->SetCoord(point2); // Set v1 as the start vertex of halfedge1 he1->SetVertex(v1); // Set v2 as the start vertex of halfedge2 he2->SetVertex(newVert); // Add two new halfedges to the loop lp->AddHalfEdge(he1, he2); // Set the new edge to new halfedges he1->edge = he2->edge = newEdge; newEdge->he1 = he1; newEdge->he2 = he2; // Add the new edge to the solid pSolid->AddEdge(newEdge); // Add the new vertex to the solid pSolid->AddVertex(newVert); return he1; } CLoop* CEulerDoc::mef(GLdouble *point1, GLdouble *point2, CLoop* lp) { CSolid *pSolid = lp->host_f->host_s; CVertex *v1, *v2; // Find the two given vertex v1 = pSolid->FindVertex(point1); if(!v1) { AfxMessageBox("Can't find the point1!"); return NULL; } v2 = pSolid->FindVertex(point2); if(!v2) { AfxMessageBox("Can't find the point2!"); return NULL; } // two vertex must in the same loop if(!lp->IsVertexIn(v1) && !lp->IsVertexIn(v2)) { AfxMessageBox("该两点不位于同一个loop中"); return NULL; } CFace *newFace; CLoop *newLoop; CEdge *newEdge; CHalfEdge *he1, *he2, *temphe1, *temphe2; newFace = new CFace; newLoop = new CLoop; newEdge = new CEdge; he1 = new CHalfEdge; he2 = new CHalfEdge; // Find two halfedges start with two vertexs temphe1 = lp->FindHostHalfEdge(v1); temphe2 = lp->FindHostHalfEdge(v2); // Change halfedges' host loop to new loop temphe1->prev->next = NULL; newLoop->AddHalfEdge(temphe1, NULL); while(temphe1 != temphe2) temphe1 = temphe1->next; temphe1->prev->next = he2; he2->prev = temphe1->prev; he2->next = newLoop->GetHalfEdgeHead(); newLoop->GetHalfEdgeHead()->prev = he2; temphe1->prev = NULL; // Add halfedge start with vertex one to the old loop % close this loop lp->AddHalfEdge(he1, NULL); he1->next = temphe2; temphe2->prev = he1; // Set two halfedges' start vertex and adjacent he1->SetVertex(v1); he2->SetVertex(v2); he1->adj = he2; he2->adj = he1; // Set the new edge and add to the solid newEdge->he1 = he1; newEdge->he2 = he2; pSolid->AddEdge(newEdge); // Add new face to the solid pSolid->AddFace(newFace); // Add new loop to the new face newFace->AddLoop(newLoop); return newLoop; } CLoop* CEulerDoc::kemr(GLdouble *point1, GLdouble *point2, CLoop* lp) { CSolid *pSolid = lp->host_f->host_s; CVertex *v1, *v2; // Find the two given vertex v1 = pSolid->FindVertex(point1); if(!v1) { AfxMessageBox("Can't find the point1!"); return NULL; } v2 = pSolid->FindVertex(point2); if(!v2) { AfxMessageBox("Can't find the point2!"); return NULL; } // two vertex must in the same loop if(!lp->IsVertexIn(v1) && !lp->IsVertexIn(v2)) return NULL; CLoop *newLoop; CHalfEdge *he1, *he2; CEdge *edge; newLoop = new CLoop; // Find two halfedges start with two vertexs he1 = lp->GetHalfEdgeHead(); while(he1) { if(he1->GetVertex() == v1 && he1->next->GetVertex() == v2) break; he1 = he1->next; } he2 = he1->adj; // Make a newloop and delete two halfedges newLoop->AddHalfEdge(he1->next, NULL); he2->prev->next = he1->next; he1->next->prev = he2->prev; he1->prev->next = he2->next; he2->next->prev = he1->prev; he1->next = he1->prev = he2->next = he2->prev = NULL; delete he1; delete he2; // Find the edge and delete it edge = pSolid->GetEdgeHead(); while(edge) { if(edge->he1 == he1) break; edge = edge->next; } edge->prev->next = edge->next; edge->next->prev = edge->prev; edge->next = edge->prev = NULL; // Add new loop to the face lp->host_f->AddLoop(newLoop); return newLoop; } void CEulerDoc::kfmrh(CLoop* outlp, CLoop* lp) { if(!outlp || !lp) { AfxMessageBox("The loop is NULL!"); return; } CSolid *pSolid = lp->host_f->host_s; // Add the loop to the face outlp->host_f->AddLoop(lp); // Get the face need to be killed CFace* pFace = pSolid->GetFaceEnd(); // Delete it pFace->prev->next = NULL; delete pFace; } void CEulerDoc::sweep(CFace* pFace, GLdouble dx, GLdouble dy, GLdouble dz) { CFace *pEnd; CHalfEdge *pHe, *pHead; CLoop *pLoop, *newLoop, *pOutLoop; GLdouble *pPoint; GLdouble point1[3], point2[3], first[3], last[3]; BOOL bOut = TRUE; // Show that if is the outloop of the top face // Remember the last inside loop's face pEnd = pFace->host_s->GetFaceEnd(); // Start with the second face, because the first face is for buttom pFace = pFace->next; while(pFace) { // Get the first point of the loop newLoop = pLoop = pFace->GetLoopHead(); pHe = pHead = pLoop->GetHalfEdgeHead(); pPoint = pHe->GetVertex()->vcoord; point1[0] = pPoint[0]; point1[1] = pPoint[1]; point1[2] = pPoint[2]; // first[] is used for close the top face // last[] is used for side face first[0] = last[0] = point2[0] = point1[0] + dx; first[1] = last[1] = point2[1] = point1[1] + dy; first[2] = last[2] = point2[2] = point1[2] + dz; // Make the new edge mev(point1, point2, pLoop); // Goto next halfedge pHe = pHe->next; while(pHe->GetVertex() != pHead->GetVertex()) { // Get the point pPoint = pHe->GetVertex()->vcoord; point1[0] = pPoint[0]; point1[1] = pPoint[1]; point1[2] = pPoint[2]; point2[0] = point1[0] + dx; point2[1] = point1[1] + dy; point2[2] = point1[2] + dz; // Make the new edge mev(point1, point2, newLoop); // Make a new side face newLoop = mef(point2, last, newLoop); // Remember the lastest point last[0] = point2[0]; last[1] = point2[1]; last[2] = point2[2]; pHe = pHe->next; } // Close the top face newLoop = mef(first, last, newLoop); if(bOut) { pOutLoop = newLoop; bOut = FALSE; } else kfmrh(pOutLoop, newLoop); if(pFace == pEnd) break; pFace = pFace->next; } } CSolid* CEulerDoc::MakeSolid(GLdouble point[][3], GLint* pointnum, GLint loopnum, GLdouble* vector, GLdouble len) { CSolid *pSolid, *pTemp; CLoop *pLoop, *pHead; int i, j, pos; // Create the solid pSolid = mvfs(point[1]); pHead = pLoop = pSolid->GetFaceHead()->GetLoopHead(); // Create the out loop for(i = 1;i < pointnum[1]-1;i ++) { mev(point[i], point[i+1], pLoop); } mef(point[i], point[1], pLoop); pos = i+1; // Create the inside loops for(i = 2;i < loopnum; i ++) { mev(point[1], point[pos+1], pHead); for(j = 1;j < pointnum[i]-1;j ++) { mev(point[pos+j], point[pos+j+1], pHead); } mef(point[pos+j], point[pos+1], pHead); kemr(point[1], point[pos+1], pHead); pos = pos+j+1; } sweep(pSolid->GetFaceHead(), vector[0] * len, vector[1] * len, vector[2] * len); // Insert the new solid if(!solid) solid = pSolid; else { pTemp = solid; while(pTemp->next) pTemp = pTemp->next; pTemp->next = pSolid; } return pSolid; } void CEulerView::RenderScene(void) { CEulerDoc *pDoc = GetDocument(); CSolid *pSolid; CFace *pFace; CLoop *pLoop; CHalfEdge *pHalfEdge, *pHeHead; GLint i, j, pos = 0; GLfloat draw_ambient[] = { 1.0, 1.0, 1.0}, mat_ambient[] = { 0.5, 0.5, 0.5 }, mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }, shininess[] = { 50.0 }; GLfloat position1[] = { 10.0, 10.0, 10.0, 0.0 }; glLoadIdentity(); glTranslatef(m_xPos, m_yPos, m_zPos); glRotatef(m_xAngle, 1.0f,0.0f,0.0f); glRotatef(m_yAngle, 0.0f,1.0f,0.0f); if(m_bCoord) RenderCoordinate(1); glLightfv(GL_LIGHT0, GL_POSITION, position1); ////////////////////////////////////////////////////////////////////////// // Draw glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, draw_ambient); for(i = 1; i <= m_iLoopNum ;i ++) { pos += m_iLoopPointNum[i-1]; glBegin(GL_LINE_STRIP); for(j = 1;j <= m_iLoopPointNum[i]; j++) glVertex3d(m_dPoint[pos + j][0], m_dPoint[pos + j][1], m_dPoint[pos + j][2]); glEnd(); } ////////////////////////////////////////////////////////////////////////// pSolid = pDoc->solid; if(m_bWire) { glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, draw_ambient); glLineWidth(2.0); glBegin(GL_LINES); while(pSolid) { pFace = pSolid->GetFaceHead(); while(pFace) { pLoop = pFace->GetLoopHead(); while(pLoop) { pHalfEdge = pHeHead = pLoop->GetHalfEdgeHead(); while(pHalfEdge) { glVertex3dv(pHalfEdge->GetVertex()->vcoord); glVertex3dv(pHalfEdge->next->GetVertex()->vcoord); pHalfEdge = pHalfEdge->next; if(pHalfEdge == pHeHead) break; } pLoop = pLoop->next; } pFace = pFace->next; } pSolid = pSolid->next; } glEnd(); } else { glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_ambient); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, shininess); glEnable(GL_NORMALIZE); while(pSolid) { pFace = pSolid->GetFaceHead(); while(pFace) { gluTessBeginPolygon(tessobj, NULL); glNormal3dv(pFace->feq); pLoop = pFace->GetLoopHead(); while(pLoop) { gluTessBeginContour(tessobj); pHalfEdge = pHeHead = pLoop->GetHalfEdgeHead(); while(pHalfEdge) { gluTessVertex(tessobj, pHalfEdge->GetVertex()->vcoord, pHalfEdge->GetVertex()->vcoord); pHalfEdge = pHalfEdge->next; if(pHalfEdge == pHeHead) break; } gluTessEndContour(tessobj); pLoop = pLoop->next; } gluTessEndPolygon(tessobj); pFace = pFace->next; } pSolid = pSolid->next; } } }
OpenGL库和Eular的源代码下载地址为:
http://download.csdn.net/detail/daringpig/4375605
如果运行过程中有什么问题,欢迎留言讨论。
http://download.csdn.net/detail/daringpig/4375599
如果大家觉得此文对您有帮助,麻烦你“顶”一下,谢谢