操作方法:按鼠標左鍵,繞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();
}
是個聰明的女同學幫我找出的,真正體會到了獨學而無友,則孤陋寡聞這句話了。