规则多面体生成算法,算法本身很。开始想百度一份的,结果没百度到。贴出来,希望以后有用得到的同学可在直接拿去用。
算法过程
- 根据经纬线数目求出多面体表面所有点的坐标;
- 连接南北极附近的三角形面;
- 连接中间的四边形(或两个三角形);
算法实现
下面是该算法的C++实现.
Convex* SphereGenerator::generate(int longitudes, int latitudes, Float radius) { m_radius = radius; m_longitudes = longitudes; m_latitudes = latitudes; return generate(); } Convex* SphereGenerator::generate() { Convex* pConvex = new Convex(); assert(m_latitudes >= 1); assert(m_longitudes >= 3); Point northPole(0, m_radius, 0); Point southPole(0, -m_radius, 0); int iNorth = pConvex->addVertex(northPole); int iSouth = pConvex->addVertex(southPole); double lonDelta = 2*M_PI / m_longitudes; double latDelta = M_PI / (m_latitudes+1); std::vector< std::vector<int> > vertIndices; vertIndices.resize(m_latitudes); // 计算所有顶点 for (int lat=0; lat<m_latitudes; ++lat) { vertIndices[lat] = std::vector<int>(size_t(m_longitudes)); Float y = m_radius * glm::sin(M_PI/2 - (lat+1)*latDelta); Float r = m_radius * glm::cos(M_PI/2 - (lat+1)*latDelta); // important!! for (int i=0; i<m_longitudes; ++i) { Point pt( r * glm::cos(i * lonDelta), y, -r * glm::sin(i * lonDelta) ); vertIndices[lat][i] = pConvex->addVertex(pt); } } // 连接南北两极附近三角形面 for (int i=0; i<m_longitudes; ++i) { int next = i+1 < m_longitudes ? i+1 : 0; Triangle triN(vertIndices[0][i], vertIndices[0][next], iNorth); pConvex->addTriangle(triN); Triangle triS(vertIndices[m_latitudes-1][i], vertIndices[m_latitudes-1][next], iSouth); pConvex->addTriangle(triS); } // 连接中间的三角形面 if (m_latitudes >= 2) { for (int lat=0; lat<m_latitudes-1; ++lat) { int nextLat = lat+1; for (int i=0; i<m_longitudes; ++i) { int nextI = i+1 < m_longitudes ? i+1 : 0; int A = vertIndices[lat][i]; int B = vertIndices[nextLat][i]; int C = vertIndices[nextLat][nextI]; int D = vertIndices[lat][nextI]; pConvex->addTriangle(Triangle(A, B, D)); pConvex->addTriangle(Triangle(B, C, D)); } } } return pConvex; }
演示程序
使用GLUT写的一个演示程序:
上键——增加纬线,
下键——减少纬线,
左键——减少经线,
右键——增加纬线。
#include <windows.h> // windows API 实现的OpenGL #include <gl/gl.h> #include <gl/glut.h> #include <stdio.h> #include "GlutApp.h" #include "SphereGenerator.h" class DemoSphereGen : public GlutApp { Convex* pConvex; SphereGenerator* generator; float angle; int depth; void onKey(unsigned char key, int x, int y) { switch(key) { case ' ': int lon = generator->getLongitude(); int lat = generator->getLatitude(); static char buf[128]; sprintf(buf, "sphere%d-%d.obj", lon, lat); printf("serialized to %s!\n", buf); pConvex->serialize(buf); break; } } virtual void onSpecialKey(int key, int x, int y) { int lon = generator->getLongitude(); int lat = generator->getLatitude(); bool flag=false; switch(key) { case GLUT_KEY_UP: lat++; flag = true; break; case GLUT_KEY_DOWN: lat--; if (lat < 1) { lat = 1; } flag = true; break; case GLUT_KEY_LEFT: lon--; if (lon < 3) { lon = 3; } flag = true; break; case GLUT_KEY_RIGHT: lon++; flag = true; break; default: break; } if (flag) { Convex* pOld = pConvex; pConvex = generator->generate(lon, lat); printf("longitudes: %d, latitudes:%d\n", lon, lat); printf("vertices: %d, triangles: %d\n", pConvex->getNumVertices(), pConvex->getNumTriangles()); delete pOld; } } virtual void onTimer() { //static int count = 0; //printf("Alarm %d!\n", count++); angle += 1.0; glutPostRedisplay(); } virtual void onInit() { angle = 0; depth = 1; printf("OnInit\n"); generator = new SphereGenerator(3, 1); pConvex = generator->generate(); printf("vertices: %d, triangles: %d\n", pConvex->getNumVertices(), pConvex->getNumTriangles()); //pConvex->serialize("convex1.obj"); glClearColor(0.0, 0.0, 0.0, 0.0); //glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glPolygonMode(GL_FRONT, GL_LINE); // glPolygonMode(GL_FRONT, GL_FILL); //glPolygonMode(GL_BACK, GL_LINE); // 背面显示线条 glCullFace(GL_BACK); // 剔除背面 } void onResize(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-2.0 * w / h, 2.0 * w / h, -2.0, 2.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // 不能少 } virtual void onDisplay() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0, -0.5, 0); glRotated(angle, 0, 1, 0); // 旋转 //glRotated(angle, 1, 1, 1); pConvex->render(); //glutPostRedisplay(); } }; int main(int argc, char **argv) { GlutApp* app = new DemoSphereGen(); app->initGlut(argc, argv); app->setTitle("test Regular convex generator\n"); app->setWindowsSize(600, 600); app->setTimer(1000, 100); app->run(); return 0; }
上图:
5经线3纬线
9经线5纬线
15经线9纬线
其余源码
GlutApp.h
#ifndef GLUT_APP_H #define GLUT_APP_H class GlutApp { public: typedef void (*MenuFuncPtr)(void); struct MenuEntry { int id; const char* str; MenuFuncPtr fun; }; // 当前 App 实例指针,指向子类实例 static GlutApp* s_pCurrentApp; // 右键菜单 项数最大值 static const int MAX_MENU = 32; // ctor GlutApp(); // getter and setters: static void initGlut(int argc, char** argv) { s_argc = argc; s_argv = argv; } void setDisplayMode(unsigned int mode) { m_displayMode = mode; } void setWindowsSize(int w, int h) { m_winWidth = w; m_winHeight = h; } int getWindowWidth() { return m_winWidth; } int getWindowHeight() { return m_winHeight; } void setWindowsPos(int x, int y) { m_winPosX = x; m_winPosY = y; } void setTitle(char *title) { m_title = title; } void run(); void addRightMenu(const char *str, MenuFuncPtr fun); // 初始化 virtual void onInit(){} ////////////////////////////////////////////////////////////////////////// // GLUT delegate callbacks: // 空闲函数 virtual void onIdle(){} // 图形显示(OpenGL绘图指令) virtual void onDisplay() = 0; // 子类必须重写;不能实例化该类 // 窗口大小改变 virtual void onResize(int w, int h){} ////////////////////////////////////////////////////////////////////////// // 键盘事件响应 方法: // 一般按键(可打印字符,ESC) virtual void onKey(unsigned char key, int x, int y){} // 一般按键 按下 virtual void onKeyDown(unsigned char key, int x, int y) {} // 特殊按键(除一般按键外按键) virtual void onSpecialKey(int key, int x, int y){} // 特殊按键按下 virtual void onSpecialKeyDown(int key, int x, int y){} ////////////////////////////////////////////////////////////////////////// // 鼠标事件响应 方法: // 鼠标按键 //! @param button: The button parameter is one of GLUT LEFT BUTTON, GLUT MIDDLE BUTTON, or GLUT RIGHT BUTTON. //! @param state: The state parameter is either GLUT UP or GLUT DOWN indicating // whether the callback was due to a release or press respectively. virtual void onMousePress(int button, int state, int x, int y){} // 鼠标移动 virtual void onMouseMove(int x, int y){} // 鼠标拖动 virtual void onMousePressMove(int x,int y){} ////////////////////////////////////////////////////////////////////////// // 定时器相关 方法: virtual void onTimer() {} void setTimer(int delay, int period = 0); protected: void registerMenus(); // actual GLUT callback functions: static void KeyboardCallback(unsigned char key, int x, int y); static void KeyboardUpCallback(unsigned char key, int x, int y); static void SpecialKeyboardCallback(int key, int x, int y); static void SpecialKeyboardUpCallback(int key, int x, int y); static void ReshapeCallback(int w, int h); static void IdleCallback(); static void MouseFuncCallback(int button, int state, int x, int y); static void MotionFuncCallback(int x,int y); static void MousePassiveCallback(int x, int y); static void DisplayCallback(); static void MenuCallback(int menuId); static void TimerCallback(int period); private: unsigned int m_displayMode; // for glutInit static int s_argc; static char** s_argv; char *m_title; // for glutSetWindowSize int m_winWidth; int m_winHeight; // for windows position int m_winPosX; int m_winPosY; // for menus: int m_menuCount; MenuEntry m_menuEntry[MAX_MENU]; // for timer: int m_delay; int m_period; }; #endif // GLUT_APP_H
GlutApp.cpp
#include <gl/glut.h> #include <assert.h> #include <stdio.h> #include "GlutApp.h" int GlutApp::s_argc = 0; char** GlutApp::s_argv = 0; GlutApp* GlutApp::s_pCurrentApp = 0; int g_iLastWindow = 0; void GlutApp::run() { GlutApp* lastApp = GlutApp::s_pCurrentApp; GlutApp::s_pCurrentApp = this; GlutApp* app = GlutApp::s_pCurrentApp; assert(app); int screenW = glutGet(GLUT_SCREEN_WIDTH); int screenH = glutGet(GLUT_SCREEN_HEIGHT); if (!app->m_winWidth) { app->m_winWidth = screenW / 2; app->m_winHeight = screenH / 2; } if (!app->m_winPosX) { app->m_winPosX = (screenW - app->m_winWidth) / 2; app->m_winPosY = (screenH - app->m_winHeight) / 2; } if (!lastApp) // first time calling Glut::run(). { // glutInit that should only be called exactly once in a GLUT program. glutInit(&this->s_argc, this->s_argv); glutInitDisplayMode(this->m_displayMode); glutInitWindowPosition(app->m_winPosX, app->m_winPosY); glutInitWindowSize(app->m_winWidth, app->m_winHeight); glutCreateWindow(app->m_title); g_iLastWindow = glutGetWindow(); printf("create window: %d\n", g_iLastWindow); // debug [6/2/2014 xu] } else { glutDestroyWindow(g_iLastWindow); glutInitDisplayMode(this->m_displayMode); glutInitWindowPosition(app->m_winPosX, app->m_winPosY); glutInitWindowSize(app->m_winWidth, app->m_winHeight); glutCreateWindow(app->m_title); g_iLastWindow = glutGetWindow(); printf("create window: %d\n", g_iLastWindow); // debug [6/2/2014 xu] } app->onInit(); // register keyboard callbacks glutKeyboardFunc(GlutApp::KeyboardCallback); glutKeyboardUpFunc(GlutApp::KeyboardUpCallback); glutSpecialFunc(GlutApp::SpecialKeyboardCallback); glutSpecialUpFunc(GlutApp::SpecialKeyboardUpCallback); // register mouse callbacks glutMouseFunc(GlutApp::MouseFuncCallback); glutMotionFunc(GlutApp::MotionFuncCallback); glutPassiveMotionFunc(GlutApp::MousePassiveCallback); // register menus: registerMenus(); // regitser windows resize callback glutReshapeFunc(GlutApp::ReshapeCallback); // register render callback glutDisplayFunc(GlutApp::DisplayCallback); // register timer callbacks: if (app->m_delay) { glutTimerFunc(app->m_delay, GlutApp::TimerCallback, app->m_period); } // register idle callback glutIdleFunc(GlutApp::IdleCallback); GlutApp::IdleCallback(); glutMainLoop(); } GlutApp::GlutApp() { m_displayMode = GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL; m_menuCount = 0; m_delay = 0; m_period = 0; m_winPosX = 0; m_winPosY = 0; m_winWidth = 0; m_winHeight = 0; } void GlutApp::KeyboardCallback( unsigned char key, int x, int y ) { GlutApp::s_pCurrentApp->onKey(key,x,y); } void GlutApp::KeyboardUpCallback( unsigned char key, int x, int y ) { GlutApp::s_pCurrentApp->onKeyDown(key,x,y); } void GlutApp::SpecialKeyboardCallback( int key, int x, int y ) { GlutApp::s_pCurrentApp->onSpecialKey(key,x,y); } void GlutApp::SpecialKeyboardUpCallback( int key, int x, int y ) { GlutApp::s_pCurrentApp->onSpecialKeyDown(key,x,y); } void GlutApp::ReshapeCallback( int w, int h ) { GlutApp::s_pCurrentApp->setWindowsSize(w, h); GlutApp::s_pCurrentApp->onResize(w,h); } void GlutApp::IdleCallback() { GlutApp::s_pCurrentApp->onIdle(); } void GlutApp::MouseFuncCallback( int button, int state, int x, int y ) { GlutApp::s_pCurrentApp->onMousePress(button,state,x,y); } void GlutApp::MotionFuncCallback( int x,int y ) { GlutApp::s_pCurrentApp->onMousePressMove(x,y); } void GlutApp::MousePassiveCallback( int x, int y ) { GlutApp::s_pCurrentApp->onMouseMove(x, y); } void GlutApp::DisplayCallback( void ) { GlutApp::s_pCurrentApp->onDisplay(); } void GlutApp::addRightMenu( const char *str, MenuFuncPtr fun ) { m_menuEntry[m_menuCount].id = m_menuCount; m_menuEntry[m_menuCount].str = str; m_menuEntry[m_menuCount].fun = fun; m_menuCount++; } void GlutApp::registerMenus() { if (m_menuCount > 0) { glutCreateMenu(GlutApp::MenuCallback); for (int i=0; i<m_menuCount; ++i) { glutAddMenuEntry(m_menuEntry[i].str, m_menuEntry[i].id); } glutAttachMenu(GLUT_RIGHT_BUTTON); } } void GlutApp::MenuCallback( int menuId ) { for (int i=0; i<GlutApp::s_pCurrentApp->m_menuCount; ++i) { if (menuId == GlutApp::s_pCurrentApp->m_menuEntry[i].id) { GlutApp::s_pCurrentApp->m_menuEntry[i].fun(); } } } void GlutApp::setTimer( int delay, int period ) { this->m_delay = delay; this->m_period = period; } void GlutApp::TimerCallback( int period ) { // printf("Timer Alarm!\n"); GlutApp::s_pCurrentApp->onTimer(); if (period) { glutTimerFunc(period, GlutApp::TimerCallback, period); } }
ShpereGennerator.h
#ifndef SPHERE_GENERATOR_H #define SPHERE_GENERATOR_H #include "Convex.h" class SphereGenerator { public: typedef Convex::Float Float; typedef Convex::Point Point; typedef Convex::Triangle Triangle; SphereGenerator(void); SphereGenerator(int longitudes, int latitudes, Float radius=1.0f); Convex* generate(int longitudes, int latitudes, Float radius=1.0f); Convex* generate(); int getLongitude() { return m_longitudes; } int getLatitude() { return m_latitudes; } private: Float m_radius; int m_longitudes; // 经线数 int m_latitudes; // 纬线数 }; #endif
SphereGenerator.cpp
#include "SphereGenerator.h" #include <vector> #include <math.h> #include <glm/glm.hpp> #define M_PI 3.14159265358979323846 SphereGenerator::SphereGenerator(void) { m_radius = 1.0; m_longitudes = 3; m_latitudes = 1; } SphereGenerator::SphereGenerator(int longitudes, int latitudes, Float radius) { m_radius = radius; m_longitudes = longitudes; m_latitudes = latitudes; } Convex* SphereGenerator::generate(int longitudes, int latitudes, Float radius) { m_radius = radius; m_longitudes = longitudes; m_latitudes = latitudes; return generate(); } Convex* SphereGenerator::generate() { Convex* pConvex = new Convex(); assert(m_latitudes >= 1); assert(m_longitudes >= 3); Point northPole(0, m_radius, 0); Point southPole(0, -m_radius, 0); int iNorth = pConvex->addVertex(northPole); int iSouth = pConvex->addVertex(southPole); double lonDelta = 2*M_PI / m_longitudes; double latDelta = M_PI / (m_latitudes+1); std::vector< std::vector<int> > vertIndices; vertIndices.resize(m_latitudes); // 计算所有顶点 for (int lat=0; lat<m_latitudes; ++lat) { vertIndices[lat] = std::vector<int>(size_t(m_longitudes)); Float y = m_radius * glm::sin(M_PI/2 - (lat+1)*latDelta); Float r = m_radius * glm::cos(M_PI/2 - (lat+1)*latDelta); // important!! for (int i=0; i<m_longitudes; ++i) { Point pt( r * glm::cos(i * lonDelta), y, -r * glm::sin(i * lonDelta) ); vertIndices[lat][i] = pConvex->addVertex(pt); } } // 连接南北两极附近三角形面 for (int i=0; i<m_longitudes; ++i) { int next = i+1 < m_longitudes ? i+1 : 0; Triangle triN(vertIndices[0][i], vertIndices[0][next], iNorth); pConvex->addTriangle(triN); Triangle triS(vertIndices[m_latitudes-1][i], vertIndices[m_latitudes-1][next], iSouth); pConvex->addTriangle(triS); } // 连接中间的三角形面 if (m_latitudes >= 2) { for (int lat=0; lat<m_latitudes-1; ++lat) { int nextLat = lat+1; for (int i=0; i<m_longitudes; ++i) { int nextI = i+1 < m_longitudes ? i+1 : 0; int A = vertIndices[lat][i]; int B = vertIndices[nextLat][i]; int C = vertIndices[nextLat][nextI]; int D = vertIndices[lat][nextI]; pConvex->addTriangle(Triangle(A, B, D)); pConvex->addTriangle(Triangle(B, C, D)); } } } return pConvex; }
Convex.h
#ifndef CONVEX_H #define CONVEX_H #include <vector> #include <glm/glm.hpp> class Convex { public: typedef float Float; typedef glm::vec3 Point; typedef glm::uvec3 Triangle; // ctor and dtor Convex(void); ~Convex(void); // int addVertex(const Point& vert); int addTriangle(const Triangle& tria); void clear(); // getters: const Point& getPos() const { return m_position; } int getNumVertices() const { return m_numVertices; } int getNumTriangles() const { return m_numTriangles; } const Triangle& getTriangle(int triIdx) const { return m_triangles[triIdx]; } Point getVertex(int vIndex) const { return m_vertices[vIndex]; } void setPos(const Point& pos) { m_position = pos; } void setVertex(const Point& vert, int vIndex) { m_vertices[vIndex] = vert; } // show void render(); // to obj file void serialize(const char* filename); private: int m_numVertices; int m_numTriangles; Point m_position; std::vector<Point> m_vertices; std::vector<Triangle> m_triangles; }; #endif // CONVEX_H
Convex.cpp
#include "Convex.h" #include <windows.h> #include <gl/GL.h> #include <gl/glut.h> #include <fstream> Convex::Convex(void) { m_numVertices = 0; m_numTriangles = 0; } Convex::~Convex(void) { } int Convex::addVertex( const Point& vert ) { m_vertices.push_back(vert); return m_numVertices++; } int Convex::addTriangle( const Triangle& tria ) { m_triangles.push_back(tria); return m_numTriangles++; } void Convex::clear() { m_vertices.clear(); m_triangles.clear(); m_numVertices = 0; m_numTriangles = 0; } void Convex::render() { //glMatrixMode(GL_MODELVIEW); //glLoadIdentity(); glTranslatef(m_position.x, m_position.y, m_position.z); // glPolygonMode(GL_FRONT, GL_LINE); // 更改多边形绘制形 #if 0 glBegin(GL_TRIANGLES); // 开始绘图 for (int i=0; i<m_numTriangles; ++i) { int idx0 = m_triangles[i][0]; int idx1 = m_triangles[i][1]; int idx2 = m_triangles[i][2]; glColor3fv((float*)&m_vertices[idx0]); glVertex3fv((float*)&m_vertices[idx0]); glColor3fv((float*)&m_vertices[idx1]); glVertex3fv((float*)&m_vertices[idx1]); glColor3fv((float*)&m_vertices[idx2]); glVertex3fv((float*)&m_vertices[idx2]); } glEnd(); // 结束绘图 #else glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, &m_vertices[0]); glEnableClientState(GL_COLOR_ARRAY); glColorPointer(3, GL_FLOAT, 0, &m_vertices[0]); #if 1 glBegin(GL_TRIANGLES); for (int i=0; i<m_numTriangles; ++i) { glArrayElement(m_triangles[i][0]); glArrayElement(m_triangles[i][1]); glArrayElement(m_triangles[i][2]); } glEnd(); // glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, &m_triangles[0]); #endif glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); #endif // glPolygonMode(GL_FRONT, GL_FILL); glFlush(); glutSwapBuffers(); } void Convex::serialize( const char* filename ) { int vtNum = this->m_vertices.size(); int faceN = this->m_triangles.size(); #if 1 FILE* fout = NULL; fout = fopen(filename, "w"); if(fout == NULL) { fprintf(stderr, "file %s open failed!\n", filename); return ; } fprintf(fout, "# serialized Convex data file.\n" "# It is simplest .obj file.\n" ); fprintf(fout, "# number of vertices: %d\n", vtNum); fprintf(fout, "# number of triangles: %d\n\n", faceN); fprintf(fout, "# vertices:\n"); for (int i=0; i<vtNum; ++i) { fprintf(fout, "v %g %g %g\n", m_vertices[i][0], m_vertices[i][1], m_vertices[i][2]); } fprintf(fout, "\n# faces:\n"); for (int i=0; i<faceN; ++i) { fprintf(fout, "f %d %d %d\n", m_triangles[i][0]+1, m_triangles[i][1]+1, m_triangles[i][2]+1); } fclose(fout); #endif }
时间: 2024-10-07 01:23:42