版权声明:本文为博主原创文章,未经博主允许不得转载。 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去自己实现,其实也很简单~