OpenGL加载模型-模型

一、简介
在本篇文章中,我们将以前面两篇为基础,着手启用Assimp,并开始创建实际的加载和转换代码。本次的目标是创建另一个类,这个类可以表达模型(Model)的全部。更确切的说,一个模型包含多个网格(Mesh),一个网格可能带有多个对象。一个别墅,包含一个木制阳台,一个尖顶或许也有一个游泳池,它仍然被加载为一个单一模型。我们通过Assimp加载模型,把它们转变为多个Mesh对象,这些对象是是上一节教程里创建的。

二、过程介绍
Model类的结构:
class Model {
public: /* 成员函数 /
Model(GLchar
path) {
this->loadModel(path);
}
void Draw(Shader shader);
private: /* 模型数据 */
vector meshes;
string directory;

/* 私有成员函数 /
void loadModel(string path);
void processNode(aiNode
node, const aiScene* scene);
Mesh processMesh(aiMesh* mesh, const aiScene* scene);
vector loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName);
};
Model类包含一个Mesh对象的向量,我们需要在构造函数中给出文件的位置。之后,在构造其中,它通过loadModel函数加载文件。私有方法都被设计为处理一部分的Assimp导入的常规动作,我们会简单讲讲它们。同样,我们储存文件路径的目录,这样稍后加载纹理的时候会用到。
函数Draw没有什么特别之处,基本上是循环每个网格,调用各自的Draw函数。
void Draw(Shader shader) {
for(GLuint i = 0; i < this->meshes.size(); i++)
this->meshes[i].Draw(shader);
}
为了导入一个模型,并把它转换为我们自己的数据结构,第一件需要做的事是包含合适的Assimp头文件。
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
我们将要调用的第一个函数是loadModel,它被构造函数直接调用。在loadModel函数里面,我们使用Assimp加载模型到Assimp中被称为scene对象的数据结构。

完整的loadModel函数在这里列出:
void loadModel(string path) {
Assimp::Importer import;
const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
if(!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
cout << “ERROR::ASSIMP::” << import.GetErrorString() << endl; return;
}
this->directory = path.substr(0, path.find_last_of(’/’));
this->processNode(scene->mRootNode, scene);
}
在我们加载了模型之后,我们检验是否场景和场景的根节点为空,查看这些标记中的一个来看看返回的数据是否完整。如果发生了任何一个错误,我们通过导入器(impoter)的GetErrorString函数返回错误报告。我们同样重新获取文件的目录路径。
如果没什么错误发生,我们希望处理所有的场景节点,所以我们传递第一个节点(根节点)到递归函数processNode。因为每个节点(可能)包含多个子节点,我们希望先处理父节点再处理子节点,以此类推。这符合递归结构,所以我们定义一个递归函数。递归函数就是一个做一些什么处理之后,用不同的参数调用它自身的函数,此种循环不会停止,直到一个特定条件发生。在我们的例子里,特定条件是所有的节点都被处理。
也许你记得,Assimp的结构,每个节点包含一个网格集合的索引,每个索引指向一个在场景对象中特定的网格位置。我们希望获取这些网格索引,获取每个网格,处理每个网格,然后对其他的节点的子节点做同样的处理。processNode函数的内容如下:
void processNode(aiNode* node, const aiScene* scene) {
// 添加当前节点中的所有Mesh
for(GLuint i = 0; i < node->mNumMeshes; i++) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
this->meshes.push_back(this->processMesh(mesh, scene));
} // 递归处理该节点的子孙节点
for(GLuint i = 0; i < node->mNumChildren; i++) {
this->processNode(node->mChildren[i], scene);
}
}
我们首先利用场景的mMeshes数组来检查每个节点的网格索引以获取相应的网格。被返回的网格被传递给processMesh函数,它返回一个网格对象,我们可以把它储存在meshes的list或vector(STL里的两种实现链表的数据结构)中。
一旦所有的网格都被处理,我们遍历所有子节点,同样调用processNode函数。一旦一个节点不再拥有任何子节点,函数就会停止执行。

三、总结
在本篇文章中,我们对OpenGl模型架构进行了大量的阐述,并在前面两篇内容的基础上进行了代码剖析,希望大家能有所收获。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值