opengl | openmesh 读取显示3d模型文件

转自:http://blog.csdn.net/qq_28057541/article/details/51362945

操作

鼠标控制物体旋转移动,滚轮缩放

F1,F2,F3 可以更换显示文件 (file1:cow.obj file2:cactus.ply file3 : Armadillo.off)

F4 更换显示模式 (wire,flat,flatlines)

截图

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

使用命令行显示当前状态

这里写图片描述


准备

openmesh的下载配置

  1. 下载最新的安装包
  2. 安装openmesh
  3. 配置vs

    • 工具-》选项-》项目和解决方案-》VC++目录 配置 包含文件和库文件,分别是openmesh\include和openmesh\lib两个路径(比如:加C:\Program Files (x86)\OpenMesh 2.3\include和C:\Program Files (x86)\OpenMesh 2.3\lib目录)
    • 在所建工程上右键-》属性-》预处理器-》预处理器定义添加_USE_MATH_DEFINE,同时在连接器-》输入-》附加依赖性中添加OpenMeshCored.libOpenMeshToolsd.lib
    • 在我的配置时,还出现了一个问题就是:报错 1>c:\program files (x86)\microsoft visual studio11.0\vc\include\xutility(2176): error C4996: ‘std::_Copy_impl’: Function callwith parameters that may be unsafe - this call relies on the caller to checkthat the passed values are correct. To disable this warning, use-D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ ‘CheckedIterators’ 这个的解决也是在预处理器重添加 _SCL_SECURE_NO_WARNINGS

openmesh使用和3d文件的原理

添加头文件即可:

#include <OpenMesh/Core/IO/MeshIO.hh>  // 读取文件
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh> // 操作文件  
   
  • 1
  • 2

mesh中3个关键元素:Face 面,Edge 边,Vertex 顶点 
我们在绘图中就是遍历mesh文件中的这三种数据绘制的

具体可点击查看此链接


制作

第一步:读取文件

读取文件代码如下:

// 读取文件的函数
void readfile(string file) {
    // 请求顶点法线 vertex normals
    mesh.request_vertex_normals();
    //如果不存在顶点法线,则报错 
    if (!mesh.has_vertex_normals())
    {
        cout << "错误:标准定点属性 “法线”不存在" << endl;
        return;
    }
    // 如果有顶点发现则读取文件 
    OpenMesh::IO::Options opt;
    if (!OpenMesh::IO::read_mesh(mesh, file, opt))
    {
        cout << "无法读取文件:" << file << endl;
        return;
    }
    else cout << "成功读取文件:" << file << endl;
    cout << endl; // 为了ui显示好看一些
                  //如果不存在顶点法线,则计算出
    if (!opt.check(OpenMesh::IO::Options::VertexNormal))
    {
        // 通过面法线计算顶点法线
        mesh.request_face_normals();
        // mesh计算出顶点法线
        mesh.update_normals();
        // 释放面法线
        mesh.release_face_normals();
    }
}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

这个文件读取看起来比较繁琐,但是对比网上其他实现读取的方法,我觉的这样写文件读取更加安全一些

第二步:显示

这里我使用了 显示列表 (Display list)进行显示, 关于显示列表是什么,怎么用,可以阅读以下的博客链接: link

其主要优势就是 可以优化程序的性能

//初始化顶点和面   
void initGL()
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearDepth(2.0);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST); //用来开启深度缓冲区的功能,启动后OPengl就可以跟踪Z轴上的像素,那么它只有在前面没有东西的情况下才会绘制这个像素,在绘制3d时,最好启用,视觉效果会比较真实
                             // ------------------- Lighting  
    glEnable(GL_LIGHTING); // 如果enbale那么就使用当前的光照参数去推导顶点的颜色
    glEnable(GL_LIGHT0); //第一个光源,而GL_LIGHT1表示第二个光源
                         // ------------------- Display List  
    showFaceList = glGenLists(1);
    showWireList = glGenLists(1);
    int temp = mesh.n_edges();

    // 绘制 wire 
    glNewList(showWireList, GL_COMPILE);
    glDisable(GL_LIGHTING);
    glLineWidth(1.0f);
    glColor3f(0.5f, 0.5f, 0.5f);
    glBegin(GL_LINES);
    for (MyMesh::HalfedgeIter he_it = mesh.halfedges_begin(); he_it != mesh.halfedges_end(); ++he_it) {
        //链接这个有向边的起点和终点
        glVertex3fv(mesh.point(mesh.from_vertex_handle(*he_it)).data());
        glVertex3fv(mesh.point(mesh.to_vertex_handle(*he_it)).data());
    }
    glEnd();
    glEnable(GL_LIGHTING);
    glEndList();

    // 绘制flat
    glNewList(showFaceList, GL_COMPILE);
    for (MyMesh::FaceIter f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it) {
        glBegin(GL_TRIANGLES); //三角形模式
        for (MyMesh::FaceVertexIter fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) {
            glNormal3fv(mesh.normal(*fv_it).data());
            glVertex3fv(mesh.point(*fv_it).data());
        }
        glEnd();
    }
    glEndList();
}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

halfeage其实就是有向的边,吧所有的有向边的头尾链接起来就是网格了 
face 更好画,使用绘制三角形的模式直接绘制每一个面就可以了

在以上文件中,我将绘制了面和网格的模式。在显示的时候,我会调用 glcalllist(list) 具体制定显示哪一个显示列表

第三步:良好的交互

在使用meshlab的过程中,可以可以用鼠标移动其中的物体角度,滚轮放缩

// 鼠标交互
void myMouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        mousetate = 1;
        Oldx = x;
        Oldy = y;
    }
    if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
        mousetate = 0;
    //滚轮事件
    if (state == GLUT_UP && button == 3) {
        if (currentfile == 3)
            scale -= 0.002;
        else
            scale -= 0.1;
    }
    if (state == GLUT_UP && button == 4) {
        if (currentfile == 3)
            scale += 0.002;
        else
            scale += 0.1;
    }
    glutPostRedisplay();
}

// 鼠标运动时
void onMouseMove(int x, int y) {
    if (mousetate) {
        //x对应y是因为对应的是法向量
        yRotate += x - Oldx;
        glutPostRedisplay();
        Oldx = x;
        xRotate += y - Oldy;
        glutPostRedisplay();
        Oldy = y;
    }
}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

以上函数在鼠标每次按住移动后,记录了当前位置相对于一开始按下点的移动位置,并转化为物体应该旋转的角度。滚轮事件也通过改变scale的大小来改变物体的缩放比

其他

以上就是要用到的主要技术:你还可以通过设定键盘事件来快速的更换显示文件,和显示模式等

还有一个略坑的东西就是最后一个文件极大,所以在更换文件为 file3 之后,要改变 scale 和 滑轮 滚动的缩放改变 到对应大小


全部代码

#include <iostream>  
#include <OpenMesh/Core/IO/MeshIO.hh>  
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>  
#include "GL\glut.h"
#include <math.h>
#include <Windows.h>
#include <string>

using namespace std;
typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh;

//鼠标交互有关的
int mousetate = 0; //鼠标当前的状态
GLfloat Oldx = 0.0; // 点击之前的位置
GLfloat Oldy = 0.0;
//与实现角度大小相关的参数,只需要两个就可以完成
float xRotate = 0.0f;
float yRotate = 0.0f;
float ty = 0.0f;
float scale = 1;

//文件读取有关的
MyMesh mesh;
const string file_1 = "cow.obj";
const string file_2 = "cactus.ply";
const string file_3 = "Armadillo.off";
int currentfile = 1;

GLuint showFaceList, showWireList;
int showstate = 1;
bool showFace = true;
bool showWire = false;
bool showFlatlines = false;

//初始化顶点和面   
void initGL()
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearDepth(2.0);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST); //用来开启深度缓冲区的功能,启动后OPengl就可以跟踪Z轴上的像素,那么它只有在前面没有东西的情况下才会绘制这个像素,在绘制3d时,最好启用,视觉效果会比较真实
                             // ------------------- Lighting  
    glEnable(GL_LIGHTING); // 如果enbale那么就使用当前的光照参数去推导顶点的颜色
    glEnable(GL_LIGHT0); //第一个光源,而GL_LIGHT1表示第二个光源
                         // ------------------- Display List  
    showFaceList = glGenLists(1);
    showWireList = glGenLists(1);
    int temp = mesh.n_edges();

    // 绘制 wire 
    glNewList(showWireList, GL_COMPILE);
    glDisable(GL_LIGHTING);
    glLineWidth(1.0f);
    glColor3f(0.5f, 0.5f, 0.5f);
    glBegin(GL_LINES);
    for (MyMesh::HalfedgeIter he_it = mesh.halfedges_begin(); he_it != mesh.halfedges_end(); ++he_it) {
        //链接这个有向边的起点和终点
        glVertex3fv(mesh.point(mesh.from_vertex_handle(*he_it)).data());
        glVertex3fv(mesh.point(mesh.to_vertex_handle(*he_it)).data());
    }
    glEnd();
    glEnable(GL_LIGHTING);
    glEndList();

    // 绘制flat
    glNewList(showFaceList, GL_COMPILE);
    for (MyMesh::FaceIter f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it) {
        glBegin(GL_TRIANGLES);
        for (MyMesh::FaceVertexIter fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) {
            glNormal3fv(mesh.normal(*fv_it).data());
            glVertex3fv(mesh.point(*fv_it).data());
        }
        glEnd();
    }
    glEndList();
}

// 当窗体改变大小的时候
void myReshape(GLint w, GLint h)
{
    glViewport(0, 0, static_cast<GLsizei>(w), static_cast<GLsizei>(h));
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w > h)
        glOrtho(-static_cast<GLdouble>(w) / h, static_cast<GLdouble>(w) / h, -1.0, 1.0, -100.0, 100.0);
    else
        glOrtho(-1.0, 1.0, -static_cast<GLdouble>(h) / w, static_cast<GLdouble>(h) / w, -100.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}


// 读取文件的函数
void readfile(string file) {
    // 请求顶点法线 vertex normals
    mesh.request_vertex_normals();
    //如果不存在顶点法线,则报错 
    if (!mesh.has_vertex_normals())
    {
        cout << "错误:标准定点属性 “法线”不存在" << endl;
        return;
    }
    // 如果有顶点发现则读取文件 
    OpenMesh::IO::Options opt;
    if (!OpenMesh::IO::read_mesh(mesh, file, opt))
    {
        cout << "无法读取文件:" << file << endl;
        return;
    }
    else cout << "成功读取文件:" << file << endl;
    cout << endl; // 为了ui显示好看一些
                  //如果不存在顶点法线,则计算出
    if (!opt.check(OpenMesh::IO::Options::VertexNormal))
    {
        // 通过面法线计算顶点法线
        mesh.request_face_normals();
        // mesh计算出顶点法线
        mesh.update_normals();
        // 释放面法线
        mesh.release_face_normals();
    }
}

//  键盘交互 1. 切换文件 2.切换显示
void mySpecial(int key, int x, int y) {
    switch (key) {
    case GLUT_KEY_F1:
        cout << "读取文件:" << file_1 << " 中......" << endl;
        readfile(file_1);
        scale = 1.0;
        currentfile = 1;
        initGL();
        break;
    case GLUT_KEY_F2:
        cout << "读取文件:" << file_2 << " 中......" << endl;
        readfile(file_2);
        scale = 1.2;
        currentfile = 2;
        initGL();
        break;
    case GLUT_KEY_F3:
        cout << "读取文件:" << file_3 << " 中......" << endl;
        readfile(file_3);
        scale = 0.01;
        currentfile = 3;
        initGL();
        break;
    case GLUT_KEY_F4:
        if (showFace == true) {
            showFace = false;
            showWire = true;
            cout << "切换显示模式为:WireFrame" << endl;
        }
        else if (showWire == true)
        {
            showWire = false;
            showFlatlines = true;
            cout << "切换显示模式为:Flatlines" << endl;
        }
        else if (showFlatlines == true) {
            showFlatlines = false;
            showFace = true;
            cout << "切换显示模式为:Flat" << endl;
        }
        break;
    case GLUT_KEY_UP:
        ty += 0.01;
        break;
    case GLUT_KEY_DOWN:
        ty -= 0.01;
        break;
    default:
        break;
    }
    glutPostRedisplay();
}

// 鼠标交互
void myMouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        mousetate = 1;
        Oldx = x;
        Oldy = y;
    }
    if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
        mousetate = 0;
    //滚轮事件
    if (state == GLUT_UP && button == 3) {
        if (currentfile == 3)
            scale -= 0.002;
        else
            scale -= 0.1;
    }
    if (state == GLUT_UP && button == 4) {
        if (currentfile == 3)
            scale += 0.002;
        else
            scale += 0.1;
    }
    glutPostRedisplay();
}

// 鼠标运动时
void onMouseMove(int x, int y) {
    if (mousetate) {
        //x对应y是因为对应的是法向量
        yRotate += x - Oldx;
        glutPostRedisplay();
        Oldx = x;
        xRotate += y - Oldy;
        glutPostRedisplay();
        Oldy = y;
    }
}

void myDisplay()
{
    //要清除之前的深度缓存
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    //与显示相关的函数
    glRotatef(xRotate, 1.0f, 0.0f, 0.0f); // 让物体旋转的函数 第一个参数是角度大小,后面的参数是旋转的法向量
    glRotatef(yRotate, 0.0f, 1.0f, 0.0f);
    glTranslatef(0.0f, 0.0f, ty);
    glScalef(scale, scale, scale); // 缩放

                                   //每次display都要使用glcalllist回调函数显示想显示的顶点列表
    if (showFace)
        glCallList(showFaceList);
    if (showFlatlines) {
        glCallList(showFaceList);
        glCallList(showWireList);
    }
    if (showWire)
        glCallList(showWireList);

    glutSwapBuffers(); //这是Opengl中用于实现双缓存技术的一个重要函数
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); // GLUT_Double 表示使用双缓存而非单缓存
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(800, 500);
    glutCreateWindow("Mesh Viewer");
    //一开始默认读取文件1
    readfile(file_1);
    initGL();
    glutMouseFunc(myMouse);
    glutMotionFunc(onMouseMove); // 鼠标移动的时候的函数
    glutSpecialFunc(&mySpecial);
    glutReshapeFunc(&myReshape);
    glutDisplayFunc(&myDisplay);

    glutMainLoop();
    return 0;
}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
版权声明:本文为博主原创文章,未经博主允许不得转载

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页