Model类包含Mesh对象的vector,构造函数需要一个文件路径来加载模型
class Model
{
public:
Model(char *path)
{
loadModel(path); //加载模型路径
}
void Draw(Shader shader);
private:
vector<Mesh> meshes; //meshVector
string directory;
void loadModel(string path);
void processNode(aiNode *node, const aiScene *scene);
Mesh processMesh(aiMesh *mesh, const aiScene *scene);
vector<Texture> loadMaterialTextures(aiMaterial *mat, aiTextureType type, string typeName);
};
其中draw函数通过for循环所有mesh来画
void Draw(Shader &shader)
{
for(unsigned int i = 0; i < meshes.size(); i++)
meshes[i].Draw(shader);
}
aiProcess_GenNormals:如果模型不包含法向量的话,就为每个顶点创建法线。aiProcess_SplitLargeMeshes:将比较大的网格分割成更小的子网格,如果你的渲染有最大顶点数限制,只能渲染较小的网格,那么它会非常有用。aiProcess_OptimizeMeshes:和上个选项相反,它会将多个小网格拼接为一个大的网格,减少绘制调用从而进行优化。
加载模型函数
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;
}
directory = path.substr(0, path.find_last_of('/'));//找到最后一个下划线
processNode(scene->mRootNode, scene); //进行递归
}
使用processNode函数处理所有网格,对所有子节点重复过程
void processNode(aiNode *node, const aiScene *scene)
{
for(unsigned int i = 0; i < node->mNumMeshes; i++) //循环所有mesh
{
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(processMesh(mesh, scene)); //加入vector
}
for(unsigned int i = 0; i < node->mNumChildren; i++)
{
processNode(node->mChildren[i], scene); //递归所有子节点
}
}
然后将aiMesh对象转化为自己的mesh对象,访问所有属性后保存为自己的对象
分别处理顶点数据,法线和坐标纹理
然后循环所有mesh的face,遍历所有面,并储存face的索引
for(unsigned int i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];//遍历所有face
for(unsigned int j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
同理,再从mMaterialIndex属性中找出mesh的材质,并将信息储存在vector中