我这里的OBJ格式不是c++代码产生的中间文件,而是那个g什么wave公司的OBJ格式,格式很简单,作用就是拿来存储3D模型的一些基本信 息。以前在VS2005下能很轻松读取,这次换QT了,幸好QT公司对客户很用心和负责,在其QtLab下发现了不错的类和代码。加以运用,成功导入 OBJ~
切入正题。
首先这个类会需要引用该文件point3d.h,其内容如下:
- #ifndef POINT3D_H
- #define POINT3D_H
-
- #include “math.h”
-
- #include <qglobal.h>
-
- 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 <QString>
- #include <QVector>
-
- #include <math.h>
-
- #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<Point3d> m_points;
- QVector<Point3d> m_normals;
- QVector<int> m_edgeIndices;
- QVector<int> m_pointIndices;
- };
-
- #endif
最后是Model.cpp的内容:
- #include “model.h”
-
- #include <QFile>
- #include <QTextStream>
- #include <QVarLengthArray>
-
- #include <QtOpenGL>
-
- 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<int, 4> 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<Point3d> 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了。呵呵,封装得很彻底很强大,祝你成功。
如果文章中有什么错误的地方,请指出也方便以后看到该日志的人能少走弯路。
如果文章中有内容帮助了你,也希望可以不吝啬点时间留下言,作为我的动力,呵呵~。
(民警小刘) |