OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

时间:2022-12-19 07:57:56

原文作者:aircraft

原文链接:https://www.cnblogs.com/DOMLX/p/11627508.html

前言:

  因为接下来的项目需求是要读取多个3D模型,并且移动拼接,那么我就先把基本的小demo给写好当做前期测试。

  在我之前网上的博客都只有读取移动旋转单个3d模型的, 导致我根本查不到有关的资料,只能自己写了。

  前人栽树,后人乘凉。

  技术就是要分享出来,大家共同进步,我们走过的坑没有必要让后来人再走一次。

有了前面两篇的分部实现,掌握这次的就不难了:

OpenGl读取导入3D模型并且添加鼠标移动旋转显示

OpenGl 实现鼠标分别移动多个物体图形 ----------移动一个物体另外一个物体不动--读取多个3d模型操作的前期踏脚石

当然还是要回顾一下整个实现过程,和一些需求准备。

一.读取3D模型

  在3d图形处理中,一个模型(model)通常由一个或者多个Mesh(网格)组成,一个Mesh是可绘制的独立实体。例如复杂的人物模型,可以分别划分为头部,四肢,服饰,武器等各个部分来建模,这些Mesh组合在一起最终形成人物模型。

Mesh由顶点、边、面Faces组成的,它包含绘制所需的数据,例如顶点位置、纹理坐标、法向量,材质属性等内容,它是OpenGL用来绘制的最小实体。Mesh的概念示意如下图所示(来自:What is a mesh in OpenGL?):

OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

Mesh

Mesh可以包含多个Face,一个Face是Mesh中一个可绘制的基本图元,例如三角形,多边形,点。要想模型更加逼真,一般需要增加更多图元使Mesh更加精细,当然这也会受到硬件处理能力的限制,例如PC游戏的处理能力要强于移动设备。由于多边形都可以划分为三角形,而三角形是图形处理器中都支持的基本图元,因此使用得较多的就是三角形网格来建模。例如下面的图(来自:What is a mesh in OpenGL?)表达了使用越来越复杂的Mesh建模一只兔子的过程:

Mesh2
OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

随着增加三角形个数,兔子模型变得越来越真实。

  读取3d模型有很多种方法,但是最常用的无非就是调用别人写好的库,比如(openmesh),其次呢就是自己读取解析3d模型文件里面的一个个坐标数据,什么v  vf  vn之类的。

在上面的链接中,我们使用的是openmesh库来导入3d模型,并且添加光照,鼠标控制之类的。

如图:

OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

  那么现在就是讲一下第二种方法,就是直接解析读取3d模型文件,提取里面我们所需的数据。下面是一个obj的模型文件,我们可以使用记事本打开看看里面是什么:

# Blender3D v249 OBJ File: untitled.blend
# www.blender3d.org
mtllib cube.mtl
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.748573 0.750412
vt 0.749279 0.501284
vt 0.999110 0.501077
vt 0.999455 0.750380
vt 0.250471 0.500702
vt 0.249682 0.749677
vt 0.001085 0.750380
vt 0.001517 0.499994
vt 0.499422 0.500239
vt 0.500149 0.750166
vt 0.748355 0.998230
vt 0.500193 0.998728
vt 0.498993 0.250415
vt 0.748953 0.250920
vn 0.000000 0.000000 -1.000000
vn -1.000000 -0.000000 -0.000000
vn -0.000000 -0.000000 1.000000
vn -0.000001 0.000000 1.000000
vn 1.000000 -0.000000 0.000000
vn 1.000000 0.000000 0.000001
vn 0.000000 1.000000 -0.000000
vn -0.000000 -1.000000 0.000000
usemtl Material_ray.png
s off
f // // //
f // // //
f // // //
f // // //
f // // //
f // // //
f // // //
f // // //
f // // //
f // // //
f // // //
f // // //

对这个文本格式做一个简要说明:

  1. 以#开始的行为注释行
  2. usemtl和mtllib表示的材质相关数据,解析材质数据稍微繁琐,本节我们只是为了说明加载模型的原理,不做讨论。
  3. o 引入一个新的object
  4. v 表示顶点位置
  5. vt 表示顶点纹理坐标
  6. vn 表示顶点法向量
  7. f 表示一个面,面使用1/2/8这样格式,表示顶点位置/纹理坐标/法向量的索引,这里索引的是前面用v,vt,vn定义的数据 注意这里Obj的索引是从1开始的,而不是0

那么我们只要拿到这些数据,按照opengl的绘制的规则,不就可以把他们都绘制出来了吗?

提取数据代码:

struct POINT3 {
double X;
double Y;
double Z;
};
struct WenLi {
double TU;
double TV;
};
struct FaXiangLiang {
double NX;
double NY;
double NZ;
};
struct Mian {
int V[];
int T[];
int N[];
};
class PIC
{
public:
vector<POINT3> V;//V:代表顶点。格式为V X Y Z,V后面的X Y Z表示三个顶点坐标。浮点型
vector<WenLi> VT;//表示纹理坐标。格式为VT TU TV。浮点型
vector<FaXiangLiang> VN;//VN:法向量。每个三角形的三个顶点都要指定一个法向量。格式为VN NX NY NZ。浮点型
vector<Mian> F;//F:面。面后面跟着的整型值分别是属于这个面的顶点、纹理坐标、法向量的索引。
//面的格式为:f Vertex1/Texture1/Normal1 Vertex2/Texture2/Normal2 Vertex3/Texture3/Normal3
};
//GLfloat m_fAngle = 0.0f;; // Rotation angle of the cube
PIC m_pic1;
PIC m_pic2; void ReadPIC2()
{
ifstream ifs("cow.obj");//cube bunny Eight
string s;
Mian *f;
POINT3 *v;
FaXiangLiang *vn;
WenLi *vt;
while (getline(ifs, s))
{
if (s.length() < )continue;
if (s[] == 'v') {
if (s[] == 't') {//vt 0.581151 0.979929 纹理
istringstream in(s);
vt = new WenLi();
string head;
in >> head >> vt->TU >> vt->TV;
m_pic2.VT.push_back(*vt);
}
else if (s[] == 'n') {//vn 0.637005 -0.0421857 0.769705 法向量
istringstream in(s);
vn = new FaXiangLiang();
string head;
in >> head >> vn->NX >> vn->NY >> vn->NZ;
m_pic2.VN.push_back(*vn);
}
else {//v -53.0413 158.84 -135.806 点
istringstream in(s);
v = new POINT3();
string head;
in >> head >> v->X >> v->Y >> v->Z;
m_pic2.V.push_back(*v);
}
}
else if (s[] == 'f') {//f 2443//2656 2442//2656 2444//2656 面
for (int k = s.size() - ; k >= ; k--) {
if (s[k] == '/')s[k] = ' ';
}
istringstream in(s);
f = new Mian();
string head;
in >> head;
int i = ;
while (i < )
{
if (m_pic2.V.size() != )
{
in >> f->V[i];
f->V[i] -= ;
}
if (m_pic2.VT.size() != )
{
in >> f->T[i];
f->T[i] -= ;
}
if (m_pic2.VN.size() != )
{
in >> f->N[i];
f->N[i] -= ;
}
i++;
}
m_pic2.F.push_back(*f);
}
}
}

那么接下来直接将其绘制出来即可:

for (int i = ; i < m_pic1.F.size(); i++)
{
glBegin(GL_TRIANGLES); // 绘制三角形
if (m_pic1.VT.size() != )glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[]].TU, m_pic1.VT[m_pic1.F[i].T[]].TV); //纹理
if (m_pic1.VN.size() != )glNormal3f(m_pic1.VN[m_pic1.F[i].N[]].NX, m_pic1.VN[m_pic1.F[i].N[]].NY, m_pic1.VN[m_pic1.F[i].N[]].NZ);//法向量
glVertex3f(m_pic1.V[m_pic1.F[i].V[]].X / YU, m_pic1.V[m_pic1.F[i].V[]].Y / YU, m_pic1.V[m_pic1.F[i].V[]].Z / YU); // 上顶点 if (m_pic1.VT.size() != )glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[]].TU, m_pic1.VT[m_pic1.F[i].T[]].TV); //纹理
if (m_pic1.VN.size() != )glNormal3f(m_pic1.VN[m_pic1.F[i].N[]].NX, m_pic1.VN[m_pic1.F[i].N[]].NY, m_pic1.VN[m_pic1.F[i].N[]].NZ);//法向量
glVertex3f(m_pic1.V[m_pic1.F[i].V[]].X / YU, m_pic1.V[m_pic1.F[i].V[]].Y / YU, m_pic1.V[m_pic1.F[i].V[]].Z / YU); // 左下 if (m_pic1.VT.size() != )glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[]].TU, m_pic1.VT[m_pic1.F[i].T[]].TV); //纹理
if (m_pic1.VN.size() != )glNormal3f(m_pic1.VN[m_pic1.F[i].N[]].NX, m_pic1.VN[m_pic1.F[i].N[]].NY, m_pic1.VN[m_pic1.F[i].N[]].NZ);//法向量
glVertex3f(m_pic1.V[m_pic1.F[i].V[]].X / YU, m_pic1.V[m_pic1.F[i].V[]].Y / YU, m_pic1.V[m_pic1.F[i].V[]].Z / YU); // 右下
glEnd(); // 三角形绘制结束
}

那么读取多个3d模型,就是把函数和绘制多调用几次不就行了吗?

二.鼠标控制相关

  1.鼠标控制函数准备

  我们需要对鼠标信息的获取,那么必然需要一个鼠标事件的响应函数来控制,很好opengl已经有内部的鼠标控制函数了,我们直接拿来使用就行了。

glutMouseFunc( (void*)Func(int button, int state, int x, int y) );
glutMouseFunc这个是调用鼠标函数的入口,func是我们给鼠标处理函数的命名, 三个参数分别是鼠标响应的事件类型,比如左键点击,右键点击之类,x,y则是当前鼠标在窗口的位置坐标。 下面这个是处理鼠标移动时候的调用函数
glutMotionFunc(&func(int x,inty)); // 鼠标移动的时候的函数 x,y当前鼠标坐标 反正调用起来非常的简单只要自己写好一个鼠标点击类事件处理函数和一个鼠标移动事件处理函数,然后传入进去就行了,调用函数放在main函数里。反正后面代码有。
// 鼠标运动时
void onMouseMove(int x, int y) {
//当鼠标状态为按下时进入后续判断
if (mousetate) {
if (choose != ) {
//x对应y是因为对应的是法向量
if (choose == ) {
movX1 = (x - x1) / * ;
glutPostRedisplay();
movY1 = -((y - Y1) / * );
glutPostRedisplay();
std::cout << " 移动 x1 = " << x << " y1 = " << y << std::endl;
}
if (choose == ) {
movX2 = (x - x2) / * ;
glutPostRedisplay();
movY2 = -((y - y2) / * );
glutPostRedisplay();
std::cout << " 移动 x2 = " << x << " y2 = " << y << std::endl;
}
if (choose == ) {
//x对应y是因为对应的是法向量
yRotate1 += x - Oldx1;
glutPostRedisplay();//标记当前窗口需要重新绘制
Oldx1 = x;
xRotate1 += y - Oldy1;
glutPostRedisplay();
Oldy1 = y;
std::cout << "xRotate = " << xRotate1 << "yRotate = " << yRotate1 << std::endl;
}
if (choose == ) {
//x对应y是因为对应的是法向量
yRotate2 += x - Oldx2;
glutPostRedisplay();//标记当前窗口需要重新绘制
Oldx2 = x;
xRotate2 += y - Oldy2;
glutPostRedisplay();
Oldy2 = y;
std::cout << "xRotate = " << xRotate1 << "yRotate = " << yRotate1 << std::endl;
}
}
else std::cout << "not choose" << std::endl;
}
}

2.一些鼠标的响应事件

if(state == GLUT_DOWN) //相当于“如果某个鼠标键被按下”
if(state == GLUT_UP) //相当于“如果某个鼠标键被放开”
if(button == GLUT_LEFT_BUTTON) //相当于“如果鼠标左键被按下或者被放开”
if(button == GLUT_RIGHT_BUTTON) //相当于“如果鼠标右键被按下或被放开”
if(button == GLUT_MIDDLE_BUTTON) //相当于“如果鼠标中键被按下或者被放开”

还有鼠标的滚轮事件

GLUT_WHEEL_UP

GLUT_WHEEL_DOWN

这两个可能有时候会遇到自己gult库没有定义,那么就是版本比较老的缘故,不想麻烦下新版本或者下了新版本还是没有解决的话就直接像这样定义在文件头部:

#define  GLUT_WHEEL_UP 3           //定义滚轮操作
#define  GLUT_WHEEL_DOWN 4

三.实现过程

  我们将读取的两个3d模型绘制,并且分开显示(光照还没有搞好QAQ。。。。)

OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

绘制代码:

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);
glTranslatef(tx, ty, 0); //移动
glScalef(scale, scale, scale); // 缩放 x,y,z分别乘以scale
*/
//每次display都要使用glcalllist回调函数显示想显示的顶点列表
//glScalef(scale, scale, scale);
glScalef(0.001, 0.001, 0.001); //glTranslatef(400, 0, 0);
//glPushMatrix();
//glTranslatef(-6, 0, 0);
glPushMatrix();
glColor3f(, , ); //glRotatef(50,1, 0, 0);
glTranslatef(movX1, movY1, );
glRotatef(xRotate1, 1.0f, 0.0f, 0.0f);
glRotatef(yRotate1, 0.0f, 1.0f, 0.0f);
//用键盘的insert按键控制显示的模式 网格,面,网格面 分别显示
for (int i = ; i < m_pic1.F.size(); i++)
{
glBegin(GL_TRIANGLES); // 绘制三角形
if (m_pic1.VT.size() != )glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[]].TU, m_pic1.VT[m_pic1.F[i].T[]].TV); //纹理
if (m_pic1.VN.size() != )glNormal3f(m_pic1.VN[m_pic1.F[i].N[]].NX, m_pic1.VN[m_pic1.F[i].N[]].NY, m_pic1.VN[m_pic1.F[i].N[]].NZ);//法向量
glVertex3f(m_pic1.V[m_pic1.F[i].V[]].X / YU, m_pic1.V[m_pic1.F[i].V[]].Y / YU, m_pic1.V[m_pic1.F[i].V[]].Z / YU); // 上顶点 if (m_pic1.VT.size() != )glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[]].TU, m_pic1.VT[m_pic1.F[i].T[]].TV); //纹理
if (m_pic1.VN.size() != )glNormal3f(m_pic1.VN[m_pic1.F[i].N[]].NX, m_pic1.VN[m_pic1.F[i].N[]].NY, m_pic1.VN[m_pic1.F[i].N[]].NZ);//法向量
glVertex3f(m_pic1.V[m_pic1.F[i].V[]].X / YU, m_pic1.V[m_pic1.F[i].V[]].Y / YU, m_pic1.V[m_pic1.F[i].V[]].Z / YU); // 左下 if (m_pic1.VT.size() != )glTexCoord2f(m_pic1.VT[m_pic1.F[i].T[]].TU, m_pic1.VT[m_pic1.F[i].T[]].TV); //纹理
if (m_pic1.VN.size() != )glNormal3f(m_pic1.VN[m_pic1.F[i].N[]].NX, m_pic1.VN[m_pic1.F[i].N[]].NY, m_pic1.VN[m_pic1.F[i].N[]].NZ);//法向量
glVertex3f(m_pic1.V[m_pic1.F[i].V[]].X / YU, m_pic1.V[m_pic1.F[i].V[]].Y / YU, m_pic1.V[m_pic1.F[i].V[]].Z / YU); // 右下
glEnd(); // 三角形绘制结束
}
glPopMatrix(); glPushMatrix();
glColor3f(, , );
glTranslatef(movX2, movY2, );
glRotatef(xRotate2, 1.0f, 0.0f, 0.0f);
glRotatef(yRotate2, 0.0f, 1.0f, 0.0f); for (int i = ; i < m_pic2.F.size(); i++)
{
glBegin(GL_TRIANGLES); // 绘制三角形
if (m_pic2.VT.size() != )glTexCoord2f(m_pic2.VT[m_pic2.F[i].T[]].TU, m_pic2.VT[m_pic2.F[i].T[]].TV); //纹理
if (m_pic2.VN.size() != )glNormal3f(m_pic2.VN[m_pic2.F[i].N[]].NX, m_pic2.VN[m_pic2.F[i].N[]].NY, m_pic2.VN[m_pic2.F[i].N[]].NZ);//法向量
glVertex3f(m_pic2.V[m_pic2.F[i].V[]].X / YU, m_pic2.V[m_pic2.F[i].V[]].Y / YU, m_pic2.V[m_pic2.F[i].V[]].Z / YU); // 上顶点 if (m_pic2.VT.size() != )glTexCoord2f(m_pic2.VT[m_pic2.F[i].T[]].TU, m_pic2.VT[m_pic2.F[i].T[]].TV); //纹理
if (m_pic2.VN.size() != )glNormal3f(m_pic2.VN[m_pic2.F[i].N[]].NX, m_pic2.VN[m_pic2.F[i].N[]].NY, m_pic2.VN[m_pic2.F[i].N[]].NZ);//法向量
glVertex3f(m_pic2.V[m_pic2.F[i].V[]].X / YU, m_pic2.V[m_pic2.F[i].V[]].Y / YU, m_pic2.V[m_pic2.F[i].V[]].Z / YU); // 左下 if (m_pic2.VT.size() != )glTexCoord2f(m_pic2.VT[m_pic2.F[i].T[]].TU, m_pic2.VT[m_pic2.F[i].T[]].TV); //纹理
if (m_pic2.VN.size() != )glNormal3f(m_pic2.VN[m_pic2.F[i].N[]].NX, m_pic2.VN[m_pic2.F[i].N[]].NY, m_pic2.VN[m_pic2.F[i].N[]].NZ);//法向量
glVertex3f(m_pic2.V[m_pic2.F[i].V[]].X / YU, m_pic2.V[m_pic2.F[i].V[]].Y / YU, m_pic2.V[m_pic2.F[i].V[]].Z / YU); // 右下
glEnd(); // 三角形绘制结束
}
glPopMatrix();
glutSwapBuffers(); //这是Opengl中用于实现双缓存技术的一个重要函数
}

  其次我们鼠标要点击选取一个物体,当我们鼠标按住移动时,物体跟随我们的鼠标移动。按住鼠标点击选取的范围可以是这个物体中心为定点坐标,以边长为d的一个矩形区域,当鼠标点击在这个区域时,我们则判定选取了这个物体。

  当两个物体重叠时,我们优先选取画出的第一个物体进行移动。

  那么问题就来了,选取了物体后,如何实现物体跟随我们鼠标移动呢?

  非常简单,水平方向上,只要在鼠标移动时将移动后的坐标减去移动前的坐标然后除以实际移动的差值(自己误差测试) ,就得到了移动的法向量。movX1 = (x - x1)  / 2 * 5;

  垂直方向上,同理可得movY1 = -((y - Y1) / 2 * 5);  为什么这里多个负号,是因为向下移动是负数,向上是正数。

  然后将移动后改变的移动法向量,让程序调用窗口重新绘制一次即可。如果出现闪烁问题,可以使用双缓冲。

 最后实现右键按住移动鼠标,被选中的物体会进行旋转。

  这个我们就比较简单了,我们只需要将移动后的坐标减去移动前的坐标这个差值作为一个法向量,然后传给旋转绘制的变量,后面调用重绘即可实现。

鼠标部分控制代码:

// 鼠标交互
void myMouse(int button, int state, int x, int y)
{ //鼠标左键按下或者松开
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
mousetate = ;
if (!choose) {
if (x<(x1 + chooseWidth) && x>(x1 - chooseWidth)) {
if (y<(Y1 + chooseHeight) && y>(Y1 - chooseHeight)) {
x1 = ;
Y1 = ;
movX1 = (x - x1) * / ;
movY1 = -((y - Y1) * / );
choose = ;
std::cout << "选择图形一 x = " << x << " y = " << y << std::endl;
}
}
else if (x<(x2 + chooseWidth) && x>(x2 - chooseWidth)) {
if (y<(y2 + chooseHeight) && y>(y2 - chooseHeight)) {
x2 = ;
y2 = ;
movX2 = (x - x2) * / ;
movY2 = -((y - y2) * / );
choose = ;
}
} }
std::cout << "x = " << x << " y = " << y << std::endl;
std::cout << "x1 = " << x1 << " y1 = " << Y1 << std::endl;
std::cout << "x2 = " << x2 << " y2 = " << y2 << std::endl;
}
if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
if (choose == ) {
x1 += (movX1 * / );
Y1 += -(movY1 * / );
}
if (choose == ) {
x2 += (movX2 * / );
y2 += -(movY2 * / );
}
mousetate = ;
choose = ;
std::cout << "x = " << x1 << " y = " << Y1 << std::endl;
} //右键按下控制旋转
if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
mousetate = ;
if (!choose) {
if (x<(x1 + chooseWidth) && x>(x1 - chooseWidth)) {
if (y<(Y1 + chooseHeight) && y>(Y1 - chooseHeight)) {
Oldx1 = x;
Oldy1 = y;
choose = ;
std::cout << "右击选择图形一 x = " << x << " y = " << y << std::endl;
}
}
else if (x<(x2 + chooseWidth) && x>(x2 - chooseWidth)) {
if (y<(y2 + chooseHeight) && y>(y2 - chooseHeight)) {
Oldx2 = x;
Oldy2 = y;
choose = ;
}
} }
}
if (button == GLUT_RIGHT_BUTTON && state == GLUT_UP) {
mousetate = ;
choose = ;
} //滚轮事件
//scale 增加就是放大 减小就是缩小
//currentfile 对不同的模型用不用的scale
if (state == GLUT_UP && button == GLUT_WHEEL_UP) {
//cout << "hello" << endl;
//滑轮向上滑,则scale减小
if (currentfile == )
scale += 0.0005;
if (currentfile == )
scale += 0.001;
if (currentfile == ) {
scale += 0.1;
}
else
scale += 0.001;
}
if (state == GLUT_UP && button == GLUT_WHEEL_DOWN) {
//cout << "good" << endl;
//滑轮向下滑,则scale增加
if (currentfile == )
scale -= 0.0005;
if (currentfile == )
scale -= 0.001;
if (currentfile == ) {
scale -= 0.1;
}
else
scale -= 0.001;
}
glutPostRedisplay();//促使主程序尽快的重绘窗口
}
// 鼠标运动时
void onMouseMove2(int x, int y) {
//当鼠标状态为按下时进入后续判断
std::cout << " 移动 x = " << x << " y = " << y << std::endl;
}
// 鼠标运动时
void onMouseMove(int x, int y) {
//当鼠标状态为按下时进入后续判断
if (mousetate) {
if (choose != ) {
//x对应y是因为对应的是法向量
if (choose == ) {
movX1 = (x - x1) / * ;
glutPostRedisplay();
movY1 = -((y - Y1) / * );
glutPostRedisplay();
std::cout << " 移动 x1 = " << x << " y1 = " << y << std::endl;
}
if (choose == ) {
movX2 = (x - x2) / * ;
glutPostRedisplay();
movY2 = -((y - y2) / * );
glutPostRedisplay();
std::cout << " 移动 x2 = " << x << " y2 = " << y << std::endl;
}
if (choose == ) {
//x对应y是因为对应的是法向量
yRotate1 += x - Oldx1;
glutPostRedisplay();//标记当前窗口需要重新绘制
Oldx1 = x;
xRotate1 += y - Oldy1;
glutPostRedisplay();
Oldy1 = y;
std::cout << "xRotate = " << xRotate1 << "yRotate = " << yRotate1 << std::endl;
}
if (choose == ) {
//x对应y是因为对应的是法向量
yRotate2 += x - Oldx2;
glutPostRedisplay();//标记当前窗口需要重新绘制
Oldx2 = x;
xRotate2 += y - Oldy2;
glutPostRedisplay();
Oldy2 = y;
std::cout << "xRotate = " << xRotate1 << "yRotate = " << yRotate1 << std::endl;
}
}
else std::cout << "not choose" << std::endl;
}
}

这些基本就是这个博客讲的所有内容了,让我们最后看看成果。

移动旋转前:

OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

移动旋转后:

OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

若有兴趣交流分享技术,可关注本人公众号,里面会不定期的分享各种编程教程,和共享源码,诸如研究分享关于c/c++,python,前端,后端,opencv,halcon,opengl,机器学习深度学习之类有关于基础编程,图像处理和机器视觉开发的知识OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转

OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转的更多相关文章

  1. OpenGl读取导入3D模型并且添加鼠标移动旋转显示

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11543828.html 最近实习要用到opengl库就是跟opencv 有点像的那个,然后下了 ...

  2. OpenGl 实现鼠标分别移动多个物体 ----------移动一个物体另外一个物体不动--读取多个3d模型操作的前期踏脚石

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11620088.html 前言: 因为接下来的项目需求是要读取多个3D模型,并且移动拼接,那么我 ...

  3. OpenGL编程(七)3D模型的深度(z轴)检测

    下图是我们要修改后的效果图: 一.深度检测 1.模型Z轴显示有问题: 上一次试验中,如果认真留意,会发现一个问题.当控制锥体在左右或上下旋转时,你会发现锥体看起来是在+-180度之间来回摆动,而不是3 ...

  4. Cesium学习笔记(九):导入3D模型(obj转gltf)

    在用cesium的过程中难免需要导入别人做好的3D模型,这时候就需要将这些模型转成gltf格式了 当然,官方也给了我们一个网页版的转换器,但是毕竟是网页版的,效率极其低下,文件还不能太大,所以我们就需 ...

  5. Altium制作DC002的PCB封装和3D模型

    Altium制作DC002的PCB封装和3D模型 常用的电源连接器(Dc Power Jack Connector)DC002.DC005等等型号的3D模型在网上很难找到合适的,我们可以选择CUI 公 ...

  6. Altium Designer 3D模型的下载与添加

    先 先晒几个图:是不是很逼真啊..  ---------------------------------------教程---------------------------------------- ...

  7. bullet物理引擎与OpenGL结合 导入3D模型进行碰撞检测 以及画三角网格的坑

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11681069.html 一.初始化世界以及模型 /// 冲突配置包含内存的默认设置,冲突设置. ...

  8. c&num; winform用sharpGL(OpenGl)解析读取3D模型obj

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11783026.html 自己写了个简单的类读取解析obj模型,使用导入类,然后new个对象,在 ...

  9. opengl导入obj模型

    在经过查阅各种资料以及各种bug之后,终于成功的实现了导入基本的obj模型. 首相介绍一下什么是obj模型 一.什么是OBJ模型 obj文件实际上是一个文本文档,主要有以下数据,一般可以通过blend ...

随机推荐

  1. linux 项目部署问题

    1.ubuntu 安装 pillow 报错 缺少依赖包 pip install Pillow ValueError: zlib is required unless explicitly disabl ...

  2. VirtualBox5中安装的CentOS6&period;7安装增强工具

    1.安装编译内核的相关组件 yum install kernel-devel gcc 2.安装VirtualBox增强功能 sh ./VBoxLinuxAdditions.run 3.重启系统 reb ...

  3. 关于ThinkRock中的Topics

    thinkrock是一款非常优秀的思想管理软件 主题是用来分类思想的,从而将思想具体化 比如:个人,书籍,小孩等等 在其中红色以及灰色是不推荐使用的,因为有别的意思.

  4. MAVEN&colon;&colon;&colon;&colon;&colon;&colon; maven-dependency-plugin &lpar;goals &quot&semi;copy-dependencies&quot&semi;&comma; &quot&semi;unpack&quot&semi;&rpar; is not supported

    zhuan:http://elan1986.iteye.com/blog/1537967 <!--add by wangquanjun 20140529-->            &lt ...

  5. Java 线程第三版 第八章 Thread与Collection Class 读书笔记

        JDK1.2引入最有争议性的改变是将集合类默觉得不是Thread安全性的. 一.Collection Class的概述 1. 具有Threadsafe 的Collection Class: j ...

  6. GOPS2017全球运维大会深圳站 出席嘉宾盘点!

    去年,GOPS全球运维大会在深圳出发,当时门票提前几周收盘,2017年,承载着运维人的期望,GOPS全球运维大会再次来到了深圳.第六届GOPS2017全球运维大会深圳站(本次)将于2017年4月21日 ...

  7. (删)Java线程同步实现一:synchronzied和wait&lpar;&rpar;&sol;notify&lpar;&rpar;

    上面文章(2.Java多线程总结系列:Java的线程控制实现)讲到了如何对线程进行控制,其中有一个是线程同步问题.下面我们先来看一个例子: 1.一个典型的Java线程安全例子 package com. ...

  8. PHP就业前景好不好一看便知,转行选择需谨慎!

    随着互联网行业迎来新一波的热潮,更多的年轻人选择软件行业发展.由于互联网本身快速发展.不断创新的特点,决定了只有以快开发速度和低成本,才能赢得胜利,才能始终保持网站的领先性和吸引更多的网民. 互联网的 ...

  9. Docker----起步&lpar;2&rpar;----在Ubuntu上安装最新版的Docker CE

    之前写了一篇文章关于Docker安装的博客,最近再次使用的时候,在docker的官方文档上发现最新版本的Docker(V18.03)的安装方式和之前有点区别,有一些命令发现不能用了.(真的是一天不学习 ...

  10. 浅谈AngularJS中的指令和指令间的相互通信

    说到AngularJS,我们首先想到的大概也就是双向数据绑定和指令系统了,这两者也是AngularJS中最为吸引人的地方.双向数据绑定呢,感觉没什么好说的,那么今天我们就来简单的讨论下AngularJ ...