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:
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; } }
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。