Qt实现读取显示obj文件——读取数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sx341125/article/details/62423811

前一篇写了关于实现的一些说明Qt实现读取显示obj文件——说明,这一篇说一说数据的读取~


说明

在头文件中我们建立一个OBJ文件的数据模型类;记录一些模型的结构与之后可能会使用的数据结构;(之后会在Github上上传所有的源码,但是数据不会上传~)

class _GLModel
{
public:

    QString path;//obj文件路径
    QString mtllibName;//材质文件名称
    size_t num_Vertices;//节点个数
    size_t num_Normals;//节点向量的个数
    size_t num_Textcoords;//节点纹理坐标个数
    size_t num_Materials;//材质个数
    size_t num_Faces;//面的个数

    QList<Point3> list_Vertices;//节点对象集合
    QList<VertNormals> list_Normals;//节点向量集合
    QList<TextCoords> list_Textcoords;//纹理坐标集合
    QList<Face> list_Faces;//面集合
    QList<Material> list_Materials;//材质集合
    QList<FacetNormal> list_FaceNormal;//面向量集合
    QList<QString> list_ImagePath;//贴图路径集合,全路径

    //GLfloat Center[3];//进行归一化之后的坐标中心

    int textureArray[MAX_TEXTURE];//注册纹理数组
};

读取OBJ数据结构

读取数据使用的是Qt中的类QInfo类,还是挺好用的~就是需要的注意的是每一行的结尾都会有\r\n回车换行符,和中文乱码,需要注意乱码和使用Trimmed()方法处理:

//读取OBJ文件
_GLModel* _glReadOBJ(QString filename)
{
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return NULL;
    }
    QString dirPath = _glGetDir(filename); QStringList list; QString currentMaterialName;

    _GLModel* model;
    model = new _GLModel();
    QString split = ' ';

    model->path = filename;
    model->num_Faces = 0;
    model->num_Materials = 0;
    model->num_Normals = 0;
    model->num_Textcoords = 0;
    model->num_Vertices = 0;

    Point3 *v;
    TextCoords *vt;
    VertNormals *vn;
    Face *f;

    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        QString str(line);
        if (str.length() < 2)//太短~
            continue;
        if (str[0] == 'm')
        {
            QStringList str0 = str.split(' ');
            QString mtlname = str0[1];
            mtlname = mtlname.trimmed();//一定要trimmed处理,否则路径有问题
            dirPath.append(mtlname);
            model->mtllibName = dirPath;
            _glReadMTL(model, model->mtllibName);
        }
        else if (str[0] == 'v'){
            if (str[1] == 't'){//纹理
                list = str.split(split);//无论是否包括Z方向的纹理都先取前两个值
                vt = new TextCoords();
                vt->U = list[_Y].toFloat(); vt->V = list[_Z].toFloat();
                model->num_Textcoords++;
                model->list_Textcoords.push_back(*vt);
            }
            else if (str[1] == 'n'){//法向量
                list = str.split(split);
                vn = new VertNormals();
                vn->_NX = list[_Y].toFloat(); vn->_NY = list[_Z].toFloat(); vn->_NZ = list[_W].toFloat();
                model->num_Normals++;
                model->list_Normals.push_back(*vn);
            }
            else//节点~
            {
                list = str.split(split);
                v = new Point3();
                v->_X = list[_Y].toFloat(); v->_Y = list[_Z].toFloat(); v->_Z = list[_W].toFloat();
                model->num_Vertices++;
                model->list_Vertices.push_back(*v);
            }
        }
        else if (str[0] == 'u')//材质的名称
        {
            list = str.split(split);
            currentMaterialName = list[1];
        }
        else if (str[0] == 'f')//面
        {
            str = str.trimmed();
            list = str.split(split);

            f = new Face();
            f->materialName = currentMaterialName;

            if (list[1].contains('/'))
            {

                for (int i = 1; i < list.length(); i++)
                {
                    QStringList sublist = list[i].split('/');
                    f->list_index_Points.push_back(sublist[_X].toInt() - 1);
                    f->list_index_TextCoords.push_back(sublist[_Y].toInt() - 1);
                    if (list[1].split('/').length() == 3)//只有v和vt
                    {
                        f->list_index_VertNormals.push_back(sublist[_Z].toInt() - 1);
                    }
                }
            }
            else//不包括/,那么只有节点
            {
                for (int i = 1; i < list.length(); i++)
                {
                    f->list_index_Points.push_back(list[i].toInt() - 1);
                }
            }
            model->num_Faces++;
            model->list_Faces.push_back(*f);
        }
    }
    return model;
}

读取纹理文件

在读取OBJ的同时还读取了纹理文件mtl:

//读取mtl文件
void _glReadMTL(_GLModel *model, QString fileName)
{
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly)) {
        qDebug("mtl文件打开失败。");
        return;
    }
    QString dirPath;

    Material *material = NULL; QStringList list;
    int index = -1;//材质索引
    QString split = ' ';
    while (!file.atEnd())
    {
        QByteArray line = file.readLine();
        if (line.length() == 2 && line.at(line.length() - 2) == '\r'&&line.at(line.length() - 1) == '\n')//如果读到了空行且材质指针不为空,那么证明当前的材质已经读完
        {
            if (material&&material->materialName != NULL)
                model->list_Materials.push_back(*material);
        }

        QString str(line);
        if (str[0] == 'n')//名称
        {
            list = str.split(split);
            material = new Material();
            material->_Ka[_X] = 0.0; material->_Ka[_Y] = 0.0; material->_Ka[_Z] = 0.0;
            material->_Kd[_X] = 0.0; material->_Kd[_Y] = 0.0; material->_Kd[_Z] = 0.0;
            material->_Ks[_X] = 0.0; material->_Ks[_Y] = 0.0; material->_Ks[_Z] = 0.0;

            QString str1 = list[1];
            material->materialName = str1.trimmed();
            material->index_Material = ++index;
            model->num_Materials++;
        }
        else if (str[0] == 'm')//贴图路径
        {
            list = str.split(split);
            dirPath = _glGetDir(fileName);//获取文件夹路径
            dirPath.append(list[1].trimmed());
            material->imageName = dirPath;
            model->list_ImagePath.push_back(dirPath);
        }
        else if (str[0] == 'K')
        {
            list = str.split(split);
            if (str[1] == 'a')
            {
                material->_Ka[0] = list[1].toFloat();
                material->_Ka[1] = list[2].toFloat();
                material->_Ka[2] = list[3].toFloat();
            }
            else if (str[1] == 'd')
            {
                material->_Kd[0] = list[1].toFloat();
                material->_Kd[1] = list[2].toFloat();
                material->_Kd[2] = list[3].toFloat();
            }
            else if (str[1] == 's')
            {
                material->_Ks[0] = list[1].toFloat();
                material->_Ks[1] = list[2].toFloat();
                material->_Ks[2] = list[3].toFloat();
            }
        }
    }
}

在实际的绘制obj中我虽然读取了材质参数,但是实际上并没有运用这些参数,主要用到了材质的纹理路径;我的意图主要还是渲染数据并贴上纹理~有兴趣的同学可以参考glm.c去自己实现,其实也很简单~

猜你喜欢

转载自blog.csdn.net/sx341125/article/details/62423811