您的位置:首页 > 编程语言 > Qt开发

NeHe教程Qt实现——lesson10

2012-03-17 12:34 253 查看
NeHe 系列教程之十:在3D空间中漫游

英文教程地址:lesson10

本课演示了从外部文件中加载数据构建3D模型的实例,代码基于第一课

首先是3D模型的数据结构定义:

namespace {
bool fp;			// F pressed?

const float piover180 = 0.0174532925f;
float heading;
float xpos;
float zpos;

GLfloat	yrot;                   // Y Rotation
GLfloat walkbias = 0;
GLfloat walkbiasangle = 0;
GLfloat lookupdown = 0.0f;

GLuint filter;			// Which filter to use
GLuint texture[3];		// Storage for 3 textures

typedef struct tagVERTEX
{
float x, y, z;
float u, v;
} VERTEX;

typedef struct tagTRIANGLE
{
VERTEX vertex[3];
} TRIANGLE;

typedef struct tagSECTOR
{
int numtriangles;
TRIANGLE* triangle;
} SECTOR;

SECTOR sector1;         // Our model goes here:


接着是读取外部数据文件,构建3D模型:

const char* readstr(QFile &f)
{
QByteArray line = f.readLine();

while (line.at(0) == '/' || line.at(0) == '\n') {
line = f.readLine();
}
return line.constData();
}

void SetupWorld()
{
float x, y, z, u, v;
int numtriangles;

const char *oneline;

QFile filein(":/World.txt");
if (!filein.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug("failed to open file");
} else {
qDebug("open file successfully");
}

oneline = readstr(filein);
sscanf(oneline, "NUMPOLLIES %d\n", &numtriangles);

sector1.triangle = new TRIANGLE[numtriangles];
sector1.numtriangles = numtriangles;
for (int loop = 0; loop < numtriangles; loop++)
{
for (int vert = 0; vert < 3; vert++)
{
oneline = readstr(filein);
sscanf(oneline, "%f %f %f %f %f", &x, &y, &z, &u, &v);
sector1.triangle[loop].vertex[vert].x = x;
sector1.triangle[loop].vertex[vert].y = y;
sector1.triangle[loop].vertex[vert].z = z;
sector1.triangle[loop].vertex[vert].u = u;
sector1.triangle[loop].vertex[vert].v = v;
}
}
filein.close();
return;
}
}


采用了三种不同的纹理过滤方式, 加载纹理代码如下:

void MyGLWidget::loadTextures()
{
QImage image;
if (image.load(":/Mud.bmp")) {
image =  convertToGLFormat(image);
glGenTextures(3, texture);
// Create Nearest Filtered Texture
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.bits());

// Create Linear Filtered Texture
glBindTexture(GL_TEXTURE_2D, texture[1]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.bits());

// Create MipMapped Texture
glBindTexture(GL_TEXTURE_2D, texture[2]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, image.width(), image.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.bits());
}
}
在初始化代码中,调用构建3D模型函数,即:

void MyGLWidget::initializeGL()
{
...

SetupWorld();

}


然后是绘制代码,将3D场景显示出来:

void MyGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     // Clear the screen and the depth buffer
glLoadIdentity();					// Reset the view

GLfloat x_m, y_m, z_m, u_m, v_m;
GLfloat xtrans = -xpos;
GLfloat ztrans = -zpos;
GLfloat ytrans = -walkbias-0.25f;
GLfloat sceneroty = 360.0f - yrot;

int numtriangles;

glRotatef(lookupdown,1.0f,0,0);
glRotatef(sceneroty,0,1.0f,0);

glTranslatef(xtrans, ytrans, ztrans);
glBindTexture(GL_TEXTURE_2D, texture[filter]);

numtriangles = sector1.numtriangles;

// Process each triangle
for (int loop_m = 0; loop_m < numtriangles; loop_m++)
{
glBegin(GL_TRIANGLES);
glNormal3f( 0.0f, 0.0f, 1.0f);
x_m = sector1.triangle[loop_m].vertex[0].x;
y_m = sector1.triangle[loop_m].vertex[0].y;
z_m = sector1.triangle[loop_m].vertex[0].z;
u_m = sector1.triangle[loop_m].vertex[0].u;
v_m = sector1.triangle[loop_m].vertex[0].v;
glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m);

x_m = sector1.triangle[loop_m].vertex[1].x;
y_m = sector1.triangle[loop_m].vertex[1].y;
z_m = sector1.triangle[loop_m].vertex[1].z;
u_m = sector1.triangle[loop_m].vertex[1].u;
v_m = sector1.triangle[loop_m].vertex[1].v;
glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m);

x_m = sector1.triangle[loop_m].vertex[2].x;
y_m = sector1.triangle[loop_m].vertex[2].y;
z_m = sector1.triangle[loop_m].vertex[2].z;
u_m = sector1.triangle[loop_m].vertex[2].u;
v_m = sector1.triangle[loop_m].vertex[2].v;
glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m);
glEnd();
}
}


最后是键盘控制处理:

void MyGLWidget::keyReleaseEvent(QKeyEvent *e)
{
switch (e->key()) {
case Qt::Key_I:
fp = false;
break;
default:
QGLWidget::keyReleaseEvent(e);
}
}

void MyGLWidget::keyPressEvent(QKeyEvent *e)
{
switch (e->key()) {
case Qt::Key_I:
fp = true;
filter += 1;
if (filter > 2)
filter = 0;
break;
case Qt::Key_F:
fullscreen = !fullscreen;
if (fullscreen) {
showFullScreen();
} else {
resize(640, 480);
showNormal();
}
break;
case Qt::Key_Right:
yrot -= 1.5f;
break;
case Qt::Key_Left:
yrot += 1.5f;
break;
case Qt::Key_Up:
xpos -= (float)sin(heading*piover180) * 0.05f;          // Move On The X-Plane Based On Player Direction
zpos -= (float)cos(heading*piover180) * 0.05f;          // Move On The Z-Plane Based On Player Direction
if (walkbiasangle >= 359.0f)                 // Is walkbiasangle>=359?
{
walkbiasangle = 0.0f;                   // Make walkbiasangle Equal 0
}
else                                // Otherwise
{
walkbiasangle+= 10;                    // If walkbiasangle < 359 Increase It By 10
}
walkbias = (float)sin(walkbiasangle * piover180)/20.0f;     // Causes The Player To Bounce
break;
case Qt::Key_Down:
xpos += (float)sin(heading*piover180) * 0.05f;          // Move On The X-Plane Based On Player Direction
zpos += (float)cos(heading*piover180) * 0.05f;          // Move On The Z-Plane Based On Player Direction
if (walkbiasangle <= 1.0f)                   // Is walkbiasangle<=1?
{
walkbiasangle = 359.0f;                 // Make walkbiasangle Equal 359
}
else                                // Otherwise
{
walkbiasangle-= 10;                 // If walkbiasangle > 1 Decrease It By 10
}
walkbias = (float)sin(walkbiasangle * piover180)/20.0f;     // Causes The Player To Bounce
break;
case Qt::Key_Escape:
QMessageBox::StandardButton reply;
reply = QMessageBox::question(NULL, "NeHe",
"Do you want to exit?",
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes);
if (reply == QMessageBox::Yes) {
qApp->quit();
}
break;
default:
QGLWidget::keyPressEvent(e);
break;
}
}


最后运行效果如下所示:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: