Qt下学习OpenGL之OBJ模型

我这里的OBJ格式不是c++代码产生的中间文件,而是那个g什么wave公司的OBJ格式,格式很简单,作用就是拿来存储3D模型的一些基本信息。以前在VS2005下能很轻松读取,这次换QT了,幸好QT公司对客户很用心和负责,在其QtLab下发现了不错的类和代码。加以运用,成功导入OBJ~

   切入正题。
   首先这个类会需要引用该文件point3d.h,其内容如下:

#ifndef POINT3D_H
#define POINT3D_H

#include “math.h”

#include

struct Point3d
{
float x, y, z;

Point3d()
: x(0)
, y(0)
, z(0)
{
}

Point3d(float x_, float y_, float z_)
: x(x_)
, y(y_)
, z(z_)
{
}

Point3d operator+(const Point3d &p) const
{
return Point3d(*this) += p;
}

Point3d operator-(const Point3d &p) const
{
return Point3d(*this) -= p;
}

Point3d operator*(float f) const
{
return Point3d(*this) *= f;
}
Point3d &operator+=(const Point3d &p)
{
x += p.x;
y += p.y;
z += p.z;
return *this;
}

Point3d &operator-=(const Point3d &p)
{
x -= p.x;
y -= p.y;
z -= p.z;
return *this;
}

bool operator==(const Point3d &p)
{
if((x==p.x)&&(y==p.y)&&(z==p.z))
return true;
return false;
}

Point3d &operator*=(float f)
{
x *= f;
y *= f;
z *= f;
return *this;
}

Point3d normalize() const
{
float r = 1. / sqrt(x * x + y * y + z * z);
return Point3d(x * r, y * r, z * r);
}
float &operator[](unsigned int index) {
Q_ASSERT(index < 3); return (&x)[index]; } const float &operator[](unsigned int index) const { Q_ASSERT(index < 3); return (&x)[index]; } }; inline float dot(const Point3d &a, const Point3d &b) { return a.x * b.x + a.y * b.y + a.z * b.z; } inline Point3d cross(const Point3d &a, const Point3d &b) { return Point3d(a.y * b.z – a.z * b.y, a.z * b.x – a.x * b.z, a.x * b.y – a.y * b.x); } #endif 然后是Model.h的内容: #ifndef MODEL_H #define MODEL_H #include
#include

#include

#include “point3d.h”

class Model
{
public:
Model() {}
Model(const QString &filePath);

//画出模型
void render(bool wireframe = false, bool normals = false) const;

QString fileName() const { return m_fileName; }
int faces() const { return m_pointIndices.size() / 3; }
int edges() const { return m_edgeIndices.size() / 2; }
int points() const { return m_points.size(); }

private:
QString m_fileName;//文件名
QVector m_points;//顶点信息
QVector m_normals;//向量信息
QVector m_edgeIndices;//边索引
QVector m_pointIndices;//点索引
};

#endif

最后是Model.cpp的内容:

#include “model.h”

#include
#include
#include

#include

Model::Model(const QString &filePath)
: m_fileName(QFileInfo(filePath).fileName())
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly))
return;

Point3d boundsMin( 1e9, 1e9, 1e9);
Point3d boundsMax(-1e9,-1e9,-1e9);

QTextStream in(&file);
while (!in.atEnd()) {
QString input = in.readLine();
if (input.isEmpty() || input[0] == ‘#’)
continue;

QTextStream ts(&input);
QString id;
ts >> id;
if (id == “v”) {
Point3d p;
for (int i = 0; i < 3; ++i) { ts >> p[i];
boundsMin[i] = qMin(boundsMin[i], p[i]);
boundsMax[i] = qMax(boundsMax[i], p[i]);
}
m_points << p; } else if (id == “f” || id == “fo”) { QVarLengthArray p;

while (!ts.atEnd()) {
QString vertex;
ts >> vertex;
const int vertexIndex = vertex.split(‘/’).value(0).toInt();
if (vertexIndex)
p.append(vertexIndex > 0 ? vertexIndex – 1 : m_points.size() + vertexIndex);
}

for (int i = 0; i < p.size(); ++i) { const int edgeA = p[i]; const int edgeB = p[(i + 1) % p.size()]; if (edgeA < edgeB) m_edgeIndices << edgeA << edgeB; } for (int i = 0; i < 3; ++i) m_pointIndices << p[i]; if (p.size() == 4) for (int i = 0; i < 3; ++i) m_pointIndices << p[(i + 2) % 4]; } } const Point3d bounds = boundsMax – boundsMin; const qreal scale = 1 / qMax(bounds.x, qMax(bounds.y, bounds.z)); for (int i = 0; i < m_points.size(); ++i) m_points[i] = (m_points[i] – (boundsMin + bounds * 0.5)) * scale; m_normals.resize(m_points.size()); for (int i = 0; i < m_pointIndices.size(); i += 3) { const Point3d a = m_points.at(m_pointIndices.at(i)); const Point3d b = m_points.at(m_pointIndices.at(i+1)); const Point3d c = m_points.at(m_pointIndices.at(i+2)); const Point3d normal = cross(b – a, c – a).normalize(); for (int j = 0; j < 3; ++j) m_normals[m_pointIndices.at(i + j)] += normal; } for (int i = 0; i < m_normals.size(); ++i) m_normals[i] = m_normals[i].normalize(); } void Model::render(bool wireframe, bool normals) const { glEnable(GL_DEPTH_TEST); glEnableClientState(GL_VERTEX_ARRAY); if (wireframe) { glVertexPointer(3, GL_FLOAT, 0, (float *)m_points.data()); glDrawElements(GL_LINES, m_edgeIndices.size(), GL_UNSIGNED_INT, m_edgeIndices.data()); } else { glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); glShadeModel(GL_SMOOTH); glEnableClientState(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, 0, (float *)m_points.data()); glNormalPointer(GL_FLOAT, 0, (float *)m_normals.data()); glDrawElements(GL_TRIANGLES, m_pointIndices.size(), GL_UNSIGNED_INT, m_pointIndices.data()); glDisableClientState(GL_NORMAL_ARRAY); glDisable(GL_COLOR_MATERIAL); glDisable(GL_LIGHT0); glDisable(GL_LIGHTING); } if (normals) { QVector normals;
for (int i = 0; i < m_normals.size(); ++i) normals << m_points.at(i) << (m_points.at(i) + m_normals.at(i) * 0.02f); glVertexPointer(3, GL_FLOAT, 0, (float *)normals.data()); glDrawArrays(GL_LINES, 0, normals.size()); } glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_DEPTH_TEST); } 在需要调用外部OBJ模型的地方,声明一个该类的对象,在构造函数里输入模型的uri,然后调用render函数就ok了。

关于Zeno Chen

本人涉及的领域较多,杂而不精 程序设计语言: Perl, Java, PHP, Python; 数据库系统: MySQL,Oracle; 偶尔做做电路板的开发,主攻STM32单片机
此条目发表在C/C++分类目录。将固定链接加入收藏夹。