Assimp里的一些知识(2)

上一节的Mesh类主要的目标是将加载进来的模型相关数据做处理,然后绘制出来。那么在Mesh类之前,我们还需要一个Model类去加载这些模型,将其转化为数据并对另外的一些数据做一些处理。



话不多说,直接上code:

class Model
{
    public:
        /*  函数   */
        Model(char *path)
        {
            loadModel(path);
        }
        void Draw(Shader shader);
    private:
        /*  模型数据  */
        vector<Mesh> meshes;
        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函数没什么好讲的啦,遍历所有的网格(处理过的),并且调用网格各自的Draw函数:

void Draw(Shader shader)
{
    for(unsigned int i = 0; i < meshes.size(); i++)
        meshes[i].Draw(shader);
}

接下来导入(加载)模型:

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);
}

库函数的意思这里就不详细讲了,直接查文档来得快。说一下功能:加载模型函数,首先ReadFile函数从给定的文件路径中读取模型,并且做一些后期处理(第二个参数枚举类)。接着检测场景以及根节点不为0,并且检查了一个标记(mFlags)来看返回的模型是不是完整的,如果不是就打印错误信息并直接退出loadModel函数。directory变量获取文件路径的目录路径,之后用processNode函数处理各个节点。

processNode函数:

void processNode(aiNode *node, const aiScene *scene)
{
    // 处理节点所有的网格(如果有的话)
    for(unsigned int i = 0; i < node->mNumMeshes; i++)
    {
        aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
        meshes.push_back(processMesh(mesh, scene));
    }
    // 接下来对它的子节点重复这一过程
    for(unsigned int i = 0; i < node->mNumChildren; i++)
    {
        processNode(node->mChildren[i], scene);
    }
}

processNode函数这里,首先用一个for循环处理第一个节点中mMeshes索引,找到scene中对应的mesh(网格),经过processMesh函数处理后将其添加入Mesh类vector模板的变量。之后由于节点符合递归的条件,所以我们直接使用递归函数对后续的子节点进行同样的操作。

processMesh()函数:

Mesh processMesh(aiMesh *mesh, const aiScene *scene)
{
    vector<Vertex> vertices;
    vector<unsigned int> indices;
    vector<Texture> textures;

    for(unsigned int i = 0; i < mesh->mNumVertices; i++)
    {
        Vertex vertex;
        // 处理顶点位置、法线和纹理坐标
        glm::vec3 vector;
        vector.x = mesh->mVertices[i].x;
        vector.y = mesh->mVertices[i].y;
        vector.z = mesh->mVertices[i].z;
        vertex.Position = vector;

        vector.x = mesh->mNormals[i].x;
        vector.y = mesh->mNormals[i].y;
        vector.z = mesh->mNormals[i].z;
        vertex.Normal = vector;

        if(mesh->mTextureCoords[0]) // 网格是否有纹理坐标?
        {
            glm::vec2 vec;
            vec.x = mesh->mTextureCoords[0][i].x;
            vec.y = mesh->mTextureCoords[0][i].y;
            vertex.TexCoords = vec;
        }
    else
        vertex.TexCoords = glm::vec2(0.0f, 0.0f);

       vertices.push_back(vertex);
}
    // 处理索引
    for(unsigned int i = 0; i < mesh->mNumFaces; i++)
    {
        aiFace face = mesh->mFaces[i];
        for(unsigned int j = 0; j < face.mNumIndices; j++)
        indices.push_back(face.mIndices[j]);
    }
    // 处理材质
    if(mesh->mMaterialIndex >= 0)
    {
        aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
        vector<Texture> diffuseMaps = loadMaterialTextures(material,
                                            aiTextureType_DIFFUSE, "texture_diffuse");
        textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
        vector<Texture> specularMaps = loadMaterialTextures(material,
                                        aiTextureType_SPECULAR, "texture_specular");
        textures.insert(textures.end(), specularMaps.begin(),            specularMaps.end());
}

    return Mesh(vertices, indices, textures);
}    

这个函数有点长,不过难度并不高。首先处理顶点结构体中的数据(顶点位置,法线,以及纹理坐标)。然后处理索引,最后处理材质。之后返回值是什么呢?是调用Mesh类的构造函数之后的返回值。而我们知道Mesh类构造函数中有一个setupMesh()函数处理这些数据,也就是说会将这些数据处理之后再返回。之后这个返回值就追加(push_back)到了上面的meshes。再然后我们都知道了,meshes调用了Draw函数绘制图形。

由下至上地完成了这个加载模型,绘制模型的过程。

这个过程让我感受到了面对对象编程的魅力(XD)。

还忘了处理材质那里出现的函数:

vector<Texture> loadMaterialTextures(aiMaterial *mat, aiTextureType type, string typeName)
{
    vector<Texture> textures;
    for(unsigned int i = 0; i < mat->GetTextureCount(type); i++)
    {
        aiString str;
        mat->GetTexture(type, i, &str);
        Texture texture;
        texture.id = TextureFromFile(str.C_Str(), directory);
        texture.type = typeName;
        texture.path = str;
        textures.push_back(texture);
    }
    return textures;
}

loadMaterialTextures函数遍历了给定纹理类型的所有纹理位置,获取了纹理的文件位置,并加载并和生成了纹理,将信息储存在了一个Vertex结构体中。

这里其实出现了一个问题,就是如果有重复的纹理怎么办?我们上面并没有针对处理重复纹理的代码,这样子会大大降低速度,因此,我们做出如下优化:

vector<Texture> loadMaterialTextures(aiMaterial *mat, aiTextureType type, string typeName)
{
    vector<Texture> textures;
    for(unsigned int i = 0; i < mat->GetTextureCount(type); i++)
    {
        aiString str;
        mat->GetTexture(type, i, &str);
        bool skip = false;
        for(unsigned int j = 0; j < textures_loaded.size(); j++)
        {
            if(std::strcmp(textures_loaded[j].path.data(), str.C_Str()) == 0)
            {
                textures.push_back(textures_loaded[j]);
                skip = true;
                break;
            }
        }
        if(!skip)
        {   // 如果纹理还没有被加载,则加载它
            Texture texture;
            texture.id = TextureFromFile(str.C_Str(), directory);
            texture.type = typeName;
            texture.path = str.C_Str();
            textures.push_back(texture);
            textures_loaded.push_back(texture); // 添加到已加载的纹理中
        }
    }
    return textures;
}

同时,还需要将texture结构体加入path变量

struct Texture {
    unsigned int id;
    string type;
    aiString path;  // 我们储存纹理的路径用于与其它纹理进行比较
};

vector<Texture> textures_loaded;

最后在主函数里面调用Model类就完事儿啦

ohhhhhh

原文地址:https://www.cnblogs.com/zhlabcd/p/11727712.html

时间: 2024-11-01 11:01:28

Assimp里的一些知识(2)的相关文章

Assimp里的一些知识(1)

OpenGL 学习到模型加载的时候,介绍了一个模型导入库(Open Asset Import Library,Assimp)的用法.初学的时候觉得稍微有些复杂,故借由这篇blog来简单地捋一下其中的细节. 首先,当我们使用Assimp导入模型的时候,它通常会将整个模型加载到一个场景(Scene)对象,这个对象包含了导入模型的所有数据.具体结构如下图所示(这个图结构十分重要,需要充分理解): Scene 场景.Scene场景有3个成员,分别是mRootNode(场景根节点),mMeshes(场景中

codeforces#253 D - Andrey and Problem里的数学知识

这道题是这样的,给主人公一堆事件的成功概率,他只想恰好成功一件. 于是,问题来了,他要选择哪些事件去做,才能使他的想法实现的概率最大. 我的第一个想法是枚举,枚举的话我想到用dfs,可是觉得太麻烦. 于是想是不是有什么规律,于是推导了一下,推了一个出来,写成代码提交之后发现是错的. 最后就没办法了,剩下的时间不够写dfs,于是就放弃了. 今天看thnkndblv的代码,代码很短,于是就想肯定是有什么数学规律,于是看了一下, 果然如此. 是这样的,还是枚举,当然是有技巧的,看我娓娓道来. 枚举的话

烂在肚子里的救命知识!

救命 一.半身不遂(不管脑出血还是栓塞)出现口眼歪钭,马上取缝衣针将双耳垂最下点刺破,各挤出一滴血就能好转,且愈后不留任何后遗症. 二.心脏病刚刚猝死病人发现后,马上脱掉袜子,用缝衣针先分别刺破十个脚趾尖,然后各挤出一滴血,不等挤完十个脚趾尖,病人即清醒过来. 三.不管哮喘还是急性喉炎等,发现病人出不来气,憋得脸红脖子粗,赶紧用缝衣针刺破鼻尖,挤出两滴黑血即愈. 四.抽羊角风(癜痫)后,取出缝衣针刺破人中穴挤出一滴血马上即愈,(人中穴在鼻唇沟的中间).           以上四法均无任何危险,

PMBOK指南里十大知识领域指的什么?

PMBOK十大知识领域是:整合管理.范围管理.时间管理.成本管理.质量管理.人力资源管理.沟通管理.风险管理.采购管理.干系人管理 各用一句话概括项目管理知识体系十大知识领域: 1.整合管理:其作用犹如项链中的那根线: 2.范围管理:做且只做该做的事: 3.时间管理:让一切按既定的项目进度进行: 4.成本管理:算准钱和花好钱: 5.质量管理:目的是满足需求: 6.人力资源管理:让团队成员高效率地一起合作: 7.沟通管理:在合适的时间让合适的人通过合适的方式把合适的信息传达给合适的人: 8.风险管

比特币里的计算机知识

一.人民币.支付宝和比特币有什么区别 (1)人民币 大家都很清楚,人民币就是中国人民银行发行的货币.这些货币流通到市场中,每个人有了钱以后可以去购买各种商品和服务,人民币就会从一个人手里转到另外一个人手里.钱流通到市场中后,具体某一个编号的纸币在某个特定的时候在谁身上是不确定的,国家也不关心.纸币在实际中是很难复制的,复制成本高.风险也很大. (2)支付宝 支付宝,当然包括微信,里面的余额说白了就是一个数据,存储在支付宝和微信公司的服务器上.同人民币不同,数据的复制.修改是不需要成本的.这些电子

世界是数字的重点读书笔记(计算机科普知识)

<世界是数字的>是世界顶尖计算机科学家Brian W.Kernighan写的一本计算机科普类读物,简明扼要但又深入全面地解释了计算机和通信系统背后的秘密,适合计算机初学者和非计算机专业的人读.这真的是一本好书,借Google常务董事长的话: 对计算机.互联网及其背后的奥秘充满好奇的人们,这绝对是一本不容错过的好书. 对于一个计算机已经学了N年的专业人士来说,这本书也许简单了点,不过我还是认真过了一遍,发现也有一定的收货,因为一个人很难掌握本领域里的所有知识,或多或少会有一些欠缺,总会有一些你以

知乎:有哪些让你相见恨晚的 PPT 制作技术或知识?

邵云蛟,倒鸡汤小能手! 谢其伟.影舞狂魔.郭郭郭 等人赞同 作为一个资深的PPT发烧友,让我带你们见识一些足以让你相见恨晚的知识,希望给各位增加点人前装逼的资本! 未经允许,请勿转载!如需转载,请联系我本人,微信号:syj18520239046 另外,本文在知乎和我的公众号旁门左道(ID:pangmenzd)同时发布. -----------------------------------废话就那么多,接下来让我们正式进入主题: 本文分为3大部分:自带功能篇,辅助工具篇和独家经验篇. 先来说第一

科幻小说《霜与火》 by 雷&#183;布雷德伯里

一半夜里,西穆降生了.他躺在洞穴里冰冷的石块上号哭. 他的血液流经全身,每分钟脉搏达一千跳.他不断地长大. 他的母亲用发烫的手把吃的送进他的嘴里,生命的噩梦开始了.他几乎一生下来就露出警惕的眼光,接着也不知道为什么缘故,眼光里充满了惊吓害怕的神色.吃的东西噎住了他的喉咙,他呛着又号哭起来.他漫无目的地环顾四周. 周围是一重浓雾.雾慢慢散开了.洞穴显现了轮廓.一个男人的高大身影出现在他眼前,这人疯疯癫病的,神情狂乱,十分可怕.一张垂死的脸.由于风吹雨打,显得十分苍老,好象在火中烘干了的土坯.这人蹲

JavaScript基础知识3

JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里 JavaScript基础知识3