操作方法:按鼠标左键,绕x轴旋转值+=12角度
按鼠标中键,绕y轴旋转值+=12角度
按鼠标右键,绕z轴旋转值+=12角度
按键盘x,eye_x-- 按键盘X, eye_x++
按键盘y,eye_y-- 按键盘Y, eye_y++
按键盘z,eye_z-- 按键盘Z ,eye_z++
按a或A,切换使用材质与否
按s或S,切换使用纹理与否
其实一直想做个arcBall操作方式,可惜线性代数学的不好,又缺乏耐心去搞懂如何把多维矩阵的运算映射成通过代码的来运算的方法。不然真的很爽,只要拖拖拽拽,就能任意观察模型了。
// 此代码加载大女孩luweiqi.txt数据文件
#include <stdio.h>
#include "glut.h"
#include <gl/glaux.h>
#pragma comment(lib, "openGL32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")
int countOfTextureFile; // 纹理文件数量
int countOfMaterial; // 材质数量
int countOfVertex; // 顶点数量
int countOfTexturePosition; // 贴图坐标数量
int countOfNormal; // 法线数量
int countOfSubModelNum; // 模型分组数量
char** texture_file_list; // 记住不能string* texture_file_list;
AUX_RGBImageRec **TextureImage;
GLuint* texture; // 存储多个纹理
struct material // 建立结构体,可以避免发杂性,以及可以减少动态生成数组的维数
{
float ambient[4];
float diffuse[4];
float specular[4];
float emission[4];
float shiness;
float indexOfTextureFile; // 这个变量怎么用??????????
};
material* material_list;
struct vertex // 建立结构体,可以避免发杂性,以及可以减少动态生成数组的维数,减少复杂性
{
float x;
float y;
float z;
};
vertex* vertex_list;
struct texture_position // 每个贴图的坐标
{
float x;
float y;
};
texture_position* texture_position_list;
struct normal
{
float x;
float y;
float z;
};
normal* normal_list;
struct scale_factor // 缩放系数
{
float x;
float y;
float z;
};
scale_factor g_scale_factor;
struct triangle
{
int vi1; // 顶点1位置索引指向顶点编号
int ti1; // 顶点1的贴图坐标编号
int ni1; // 顶点1的法线编号
int vi2;
int ti2;
int ni2;
int vi3;
int ti3;
int ni3;
};
struct submodel
{
int countOfTriangle; // the submode 的三角形数量
int indexOfMaterial; // the submode 的材质索引
triangle* triangle_list; // the submode 的所有三角形list
};
submodel* submodel_list;
//int g_iSpinX = 0;
//int g_iSpinY = 0;
//int g_iSpinZ = 0;
//int g_lastMousePosX = 0;
//int g_lastMousePosY = 0;
GLenum g_mode = GL_TRIANGLES; // GL_TRIANGLES 按住t 或T , GL_LINE_LOOP----按key L 或 l , GL_POINT---按key p 或 P
bool g_isMaterial = true;
bool g_isTexture = true;
static GLfloat theta[] = {0.0,0.0,0.0}; // 初始的旋转角度为0
static GLint axis = 2;
static GLdouble viewer[]= {0.0, -15.0, 5.0}; /* initial viewer location */
// ----------------------载入位图图象------------------------
AUX_RGBImageRec *LoadBMP(const CHAR *Filename)
{
FILE *File=NULL; // 文件句柄
if (!Filename) // 确保文件名已提供
{
return NULL; // 如果没提供,返回 NULL
}
File=fopen(Filename,"r"); // 尝试打开文件
if (File) // 文件存在么?
{
fclose(File); // 关闭句柄
return auxDIBImageLoadA(Filename); // 载入位图并返回指针
}
return NULL; // 如果载入失败,返回 NULL
}
// ----------------载入位图(调用上面的代码)并转换成纹理,注意先调用readData后调用此函数------------------
int LoadGLTextures(char** fileNameList,int texturefileNameCount)
{
int Status=FALSE; // 状态指示器
TextureImage=new AUX_RGBImageRec *[texturefileNameCount]; // 创建纹理的存储空间
memset(TextureImage,0,sizeof(void *)*texturefileNameCount); // 将指针设为 NULL
// 载入位图,检查有无错误,如果位图没找到则退出
for (int i=0;i<texturefileNameCount;i++)
{
if ( TextureImage[i]=LoadBMP(fileNameList[i] ) )
{
Status=TRUE; // 将 Status 设为 TRUE
glGenTextures(1, &texture[i]); // 每次创建一个纹理,当然也是可以一次创建texturefileNameCount个,保存到&texture里
}
/* if (TextureImage[i]) // 纹理是否存在
{
if (TextureImage[i]->data) // 纹理图像是否存在
{
free(TextureImage[i]->data); // 释放纹理图像占用的内存
}
free(TextureImage[i]); // 释放图像结构
}*/
}
return Status; // 返回 Status
}
// -----------------从luweiqi.txt数据文件读取数据------------------------
void readData()
{
int i;
FILE *fp;
if((fp=fopen("luweiqi.txt","r"))==NULL)
{
printf("Can't open the file!\n");
exit(0);
}
1.读取所有的纹理文件名字/
fscanf(fp,"%d",&countOfTextureFile);
// texture_file_list=new string[countOfTextureFile];
texture_file_list=new char* [countOfTextureFile] ;
for (i=0;i<countOfTextureFile;i++)
{
texture_file_list[i]=new char[255];
}
texture=new GLuint[countOfTextureFile]; // 2013年12月12日14:25:51
for (i=0;i<countOfTextureFile;i++)
{
fscanf(fp,"%s",texture_file_list[i]); // 这里特别容易出错哦fscanf(fp,"%s",&texture_file_list[i]); 是错的
texture[i]=i; // 2013年12月12日14:25:51
}
/2.读取所有材质数据///
fscanf(fp,"%d",&countOfMaterial);
material_list=new material[countOfMaterial];
for (i=0;i<countOfMaterial;i++)
{
// 读材质的ambient环境光属性数据
fscanf(fp,"%f%f%f%f",&material_list[i].ambient[0],
&material_list[i].ambient[1],
&material_list[i].ambient[2],
&material_list[i].ambient[3]);
// 读取材质的diffuse漫反射属性数据
fscanf(fp,"%f%f%f%f",&material_list[i].diffuse[0],
&material_list[i].diffuse[1],
&material_list[i].diffuse[2],
&material_list[i].diffuse[3]);
// 读取材质的specular镜面反射属性数据
fscanf(fp,"%f%f%f%f",&material_list[i].specular[0],
&material_list[i].specular[1],
&material_list[i].specular[2],
&material_list[i].specular[3]);
// 读取材质的emission辐射属性数据
fscanf(fp,"%f%f%f%f",&material_list[i].emission[0],
&material_list[i].emission[1],
&material_list[i].emission[2],
&material_list[i].emission[3]);
// 读取材质的光斑的大小属性的数据
fscanf(fp,"%f",&material_list[i].shiness);
// 读取材质的indexOfTextureFile所用纹理文件编号
fscanf(fp,"%f",&material_list[i].indexOfTextureFile);
}
/3.读取所有顶点的数据//
fscanf(fp,"%d",&countOfVertex);
vertex_list=new vertex[countOfVertex];
for (i=0;i<countOfVertex;i++)
{
fscanf(fp,"%f%f%f",&vertex_list[i].x,&vertex_list[i].y,&vertex_list[i].z);
}
/4.读取所有贴图坐标///
fscanf(fp,"%d",&countOfTexturePosition);
texture_position_list=new texture_position[countOfTexturePosition];
for (i=0;i<countOfTexturePosition;i++)
{
fscanf(fp,"%f%f",&texture_position_list[i].x,&texture_position_list[i].y);
}
5.读取所有法线向量/ 没用上。。。。。
fscanf(fp,"%d",&countOfNormal);
normal_list=new normal[countOfNormal];
for (i=0;i<countOfNormal;i++)
{
fscanf(fp,"%f%f%f",&normal_list[i].x,&normal_list[i].y,&normal_list[i].z);
}
6.读取所有子模型的所有三角形/
fscanf(fp,"%d",&countOfSubModelNum);
fscanf(fp,"%f%f%f",&g_scale_factor.x,&g_scale_factor.y,&g_scale_factor.z);
submodel_list=new submodel[countOfSubModelNum];
for (i=0;i<countOfSubModelNum;i++)
{
fscanf(fp,"%d",&submodel_list[i].countOfTriangle);
fscanf(fp,"%d",&submodel_list[i].indexOfMaterial);
// 因为都是unsigned 类型,所以可以使用动态产生的多维数组也行,当然,一维的结构体也行。
// 但是如果使用前者的方法的话就忽视了这些同类型的不同的变量的作用了,不利于维护,所以
// 还是选择动态一维结构体数组的方式
submodel_list[i].triangle_list=new triangle[submodel_list[i].countOfTriangle];
for(int j=0;j<submodel_list[i].countOfTriangle;j++)
{
fscanf(fp,"%d%d%d%d%d%d%d%d%d",&submodel_list[i].triangle_list[j].vi1,
&submodel_list[i].triangle_list[j].ti1,
&submodel_list[i].triangle_list[j].ni1,
&submodel_list[i].triangle_list[j].vi2,
&submodel_list[i].triangle_list[j].ti2,
&submodel_list[i].triangle_list[j].ni2,
&submodel_list[i].triangle_list[j].vi3,
&submodel_list[i].triangle_list[j].ti3,
&submodel_list[i].triangle_list[j].ni3);
}
}
fclose(fp);
}
void display(void);
// 初始化
void init (void)
{
// printf operate tip
printf("GL_TRIANGLES 按住t 或T,GL_LINE_LOOP----按key L 或 l,GL_POINT---按key p 或 P\n");// // GL_TRIANGLES 按住t 或T , GL_LINE_LOOP----按key L 或 l , GL_POINT---按key p 或 P
printf("按鼠标左键,绕x轴旋转值+=12角度\n");
printf("按鼠标中键,绕y轴旋转值+=12角度\n");
printf("按鼠标右键,绕z轴旋转值+=12角度\n");
printf("按键盘x,eye_x-- 按键盘X, eye_x++\n");
printf("按键盘y,eye_y-- 按键盘Y, eye_y++\n");
printf("按键盘z,eye_z-- 按键盘Z ,eye_z++\n");
printf("按a 或A 切换是否使用材质\n");
printf("按s 或S 切换是否使用纹理\n");
printf("\n");
glClearColor (0.0, 0.0, 0.0, 0.0); // 清理颜色,为黑色,(也可认为是背景颜色)
glEnable(GL_CULL_FACE); // 启用裁剪
glEnable(GL_TEXTURE_2D);
// glEnable(GL_MULTISAMPLE);
glEnable(GL_DEPTH_TEST);
readData(); // 读入模型数据
LoadGLTextures(texture_file_list,countOfTextureFile);// 载入所有的纹理贴图 2013年12月12日13:47:22
// 这里可以自行打开光照 (和数据文件无关)
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
float fAmbientColor[] = { 1.0f, 1.0f, 1.0f, 1.0f};
glLightfv( GL_LIGHT0, GL_AMBIENT, fAmbientColor );
float fDiffuseColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glLightfv( GL_LIGHT0, GL_DIFFUSE, fDiffuseColor );
float fSpecularColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
glLightfv( GL_LIGHT0, GL_SPECULAR, fSpecularColor );
float fPosition[] = { 1.0f, 1.0f, 1.0f, 0.0f };
glLightfv( GL_LIGHT0, GL_POSITION, fPosition );
GLfloat ambient_lightModel[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, ambient_lightModel );
display();
}
void display(void)
{
int i,j,k;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(viewer[0],viewer[1],viewer[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// 画出3个坐标轴
glBegin(GL_LINES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(10.0f, 0.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 10.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 10.0f);
glEnd();
glRotatef(theta[0], 1.0, 0.0, 0.0);
glRotatef(theta[1], 0.0, 1.0, 0.0);
glRotatef(theta[2], 0.0, 0.0, 1.0);
// glRotatef( -g_iSpinY*0.1, 1.0f, 0.0f, 0.0f );
// glRotatef( -g_iSpinX*0.1, 0.0f, 1.0f, 0.0f );
// glRotatef( -g_iSpinZ*0.1, 0.0f, 0.0f, 1.0f ); // 如果不用复杂的arcBall来操作,似乎无法改变绕z轴旋转。
for (i=0,k=0;i<countOfSubModelNum;i++) // 子模型数量
{
// 1.打开材质
if (g_isMaterial)
{
glMaterialfv(GL_FRONT, GL_AMBIENT, material_list[i].ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, material_list[i].diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, material_list[i].specular);
glMaterialfv(GL_FRONT, GL_EMISSION, material_list[i].emission);
glMaterialf (GL_FRONT, GL_SHININESS, material_list[i].shiness);
}
// 2.打开纹理
if (g_isTexture)
{
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 线形滤波
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 线形滤波
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // 2013年12月13日10:52:55
int index = material_list[i].indexOfTextureFile -1 ;
glBindTexture(GL_TEXTURE_2D, texture[ index ]);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[
index]->sizeX,
TextureImage[ index]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
TextureImage[index]->data);
}
// 3.画出三角形
for (j=0;j<submodel_list[i].countOfTriangle;j++) // 每个子模型的三角形数
{
glBegin(g_mode);
glTexCoord2f(texture_position_list[ submodel_list[i].triangle_list[j].ti1 -1].x,
texture_position_list[submodel_list[i].triangle_list[j].ti1 -1].y);
glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi1 -1] );
glTexCoord2f(texture_position_list[submodel_list[i].triangle_list[j].ti2 -1 ].x,
texture_position_list[submodel_list[i].triangle_list[j].ti2 -1 ].y );
glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi2 -1] );
glTexCoord2f(texture_position_list[submodel_list[i].triangle_list[j].ti3 -1].x,
texture_position_list[submodel_list[i].triangle_list[j].ti3 -1 ].y);
glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi3 -1] );
glEnd();
}
}
glFlush();
glutSwapBuffers();
}
void mouse(int btn, int state, int x, int y) // 鼠标左中右键改变的是各个方向的旋转角度
{
if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN) axis = 0;
if(btn==GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) axis = 1;
if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN) axis = 2;
theta[axis] += 12.0;
if( theta[axis] > 360.0 ) theta[axis] -= 360.0;
/* if( btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN) // GLUT_DOWN 是按下的状态
{
g_iSpinX = x - g_lastMousePosX;
g_iSpinY = y - g_lastMousePosY;
g_lastMousePosX = x;
g_lastMousePosY = y;
}*/
display();
}
void keys(unsigned char key, int x, int y)
{
/* Use x, X, y, Y, z, and Z keys to move viewer */ // 键盘的xyz 改变眼睛的位置 viewer[0] viewer[1] viewer[2] 用在gluLookAt
if(key == 'x') viewer[0]-= 1.0;
if(key == 'X') viewer[0]+= 1.0;
if(key == 'y') viewer[1]-= 1.0;
if(key == 'Y') viewer[1]+= 1.0;
if(key == 'z') viewer[2]-= 1.0;
if(key == 'Z') viewer[2]+= 1.0;
// GLenum mode = GL_TRIANGLES; // GL_TRIANGLES 按住t 或T , GL_LINE_LOOP----按key L 或 l , GL_POINT---按key p 或 P
if (key == 't' || key == 'T')
{
g_mode = GL_TRIANGLES;
}
if (key == 'l' || key == 'L')
{
g_mode = GL_LINE_LOOP;
}
if (key == 'p' || key == 'P')
{
g_mode = GL_POINTS;
}
if (key == 'a'||key == 'A') // 是否用材质
{
g_isMaterial = !g_isMaterial;
}
if (key == 's'||key == 'S') // 是否用纹理
{
g_isTexture = !g_isTexture;
}
display();
}
void myReshape(int w, int h)
{
glViewport(0, 0, w, h);
/* Use a perspective view */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w<=h) glFrustum(-0.2, 0.2, -0.2 * (GLfloat) h/ (GLfloat) w, // 此时h/w >1 -0.2 ~ 0.2
0.2* (GLfloat) h / (GLfloat) w, 2.0, 40.0);
else glFrustum(-0.2, 0.2, -0.2 * (GLfloat) w/ (GLfloat) h, // 此时w/h >1
0.2* (GLfloat) w / (GLfloat) h, 2.0, 20.0);
/* Or we can use gluPerspective */
// gluPerspective(20.0, w/h, -0.1, 0.1);
glMatrixMode(GL_MODELVIEW); // 设置回模型矩阵
}
void
main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutCreateWindow("大女孩");
init();
glutReshapeFunc(myReshape);
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutKeyboardFunc(keys);
glutMainLoop();
}
操作方式
截图1 在填充模式下,打开纹理和材质 按t或T 是三角形就是填充模式
截图2 在画线模式下,打开纹理和材质按l 或L
截图3 在点模式下,打开纹理和贴图 (操作是按p或P)
截图4 在填充模式下,打开材质,关闭纹理(操作时按s进行切换)
同时运行3次exe,来观察
期间有个很有意思的bug,万万没想到的bug,哈哈。
读取文件的函数是我一口气写出来的,照着文件格式写。运行后发现贴纹理时候会贴裂掉,原因怎么也找不到,一直检查读取数据函数都没找到原因。问题出在下面的代码里的每个顶点的索引,纹理的索引需要额外的减掉1.因为数据文件里的索引最小的是1.也就是说生成模型的顶点的索引,纹理的索引是从1开始数的,而我们的读数据的时候却是把第一条数据放进了第0号索引,结果总是差1
所以说以后读数据不能只看文件格式,文件里的数据也要稍微注意下。这就是我犯的错误。
// 3.画出三角形
for (j=0;j<submodel_list[i].countOfTriangle;j++) // 每个子模型的三角形数
{
glBegin(GL_TRIANGLES);
// glBegin(GL_LINE_LOOP);
glTexCoord2f(texture_position_list[ submodel_list[i].triangle_list[j].ti1 -1].x,
texture_position_list[submodel_list[i].triangle_list[j].ti1 -1].y);
glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi1 -1] );
glTexCoord2f(texture_position_list[submodel_list[i].triangle_list[j].ti2 -1 ].x,
texture_position_list[submodel_list[i].triangle_list[j].ti2 -1 ].y );
glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi2 -1] );
glTexCoord2f(texture_position_list[submodel_list[i].triangle_list[j].ti3 -1].x,
texture_position_list[submodel_list[i].triangle_list[j].ti3 -1 ].y);
glVertex3fv((float*)&vertex_list[ submodel_list[i].triangle_list[j].vi3 -1] );
glEnd();
}
是个聪明的女同学帮我找出的,真正体会到了独学而无友,则孤陋寡闻这句话了。