Asset Loading

来源:互联网 发布:小米网络音响app连不上 编辑:程序博客网 时间:2024/03/28 18:04

Asset Loading

有了Model,Mesh以及ModelMaterial类的声明之后,就可以讨论如何把assets文件加载到这些类中。这正是要使用Open Asset Import Library的地方。

Loading Models

在我们自己编写的模型系统中,Model类是顶层接口,与些类似,在Open Asset Import Library中,aiScene类是用于数据导入的顶层接口。通过调用Importer::ReadFile()函数可以构造一个aiScene对象,aiScene对象的成员变量提供了模型的meshes和model materials接口。所有assets文件的加载都在Model的构造函数中处理,并分别使用Mesh和ModelMaterial类加载模型的meshes和model materials。列表15.4列出了Model类的实现代码。

列表15.4 The Model.cpp File

#include "Model.h"#include "Game.h"#include "GameException.h"#include "Mesh.h"#include "ModelMaterial.h"#include <assimp/Importer.hpp>#include <assimp/scene.h>#include <assimp/postprocess.h>namespace Library{    Model::Model(Game& game, const std::string& filename, bool flipUVs)        : mGame(game), mMeshes(), mMaterials()    {        Assimp::Importer importer;        UINT flags = aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_FlipWindingOrder;        if (flipUVs)        {            flags |= aiProcess_FlipUVs;        }        const aiScene* scene = importer.ReadFile(filename, flags);        if (scene == nullptr)        {            throw GameException(importer.GetErrorString());        }        if (scene->HasMaterials())        {            for (UINT i = 0; i < scene->mNumMaterials; i++)            {                mMaterials.push_back(new ModelMaterial(*this, scene->mMaterials[i]));            }        }        if (scene->HasMeshes())        {            for (UINT i = 0; i < scene->mNumMeshes; i++)            {                ModelMaterial* material = (mMaterials.size() > i ? mMaterials.at(i) : nullptr);                Mesh* mesh = new Mesh(*this, *(scene->mMeshes[i]));                mMeshes.push_back(mesh);            }        }    }    Model::~Model()    {        for (Mesh* mesh : mMeshes)        {            delete mesh;        }        for (ModelMaterial* material : mMaterials)        {            delete material;        }    }    Game& Model::GetGame()    {        return mGame;    }    bool Model::HasMeshes() const    {        return (mMeshes.size() > 0);    }    bool Model::HasMaterials() const    {        return (mMaterials.size() > 0);    }    const std::vector<Mesh*>& Model::Meshes() const    {        return mMeshes;    }    const std::vector<ModelMaterial*>& Model::Materials() const    {        return mMaterials;    }}

如果要开发一个编译期的content管线功能,需要把Model类的构造函数中加载assets文件所得到的数据保存为一个中间文件中。然后就可以增加一个构造函数用于反系列化这些中间文件,并在Library工程中去掉对Open Asset Import Library的调用。另外,纹理的垂直坐标翻转操作也应该在编译期的content管线中执行,并从运行时加载模型的代码中移除。

Loading Meshes

加载一个mesh需要读取真实的vertices,indices,normals等数据,因此Mesh类比Model类包含更多的代码。但是,加载的方式是一样的。在Open Asset Import Library中,aiMesh结构体的成员变量提供了所有需要示数据,只需要简单地把这些数据转移到Mesh类中。列表15.5中列出了Mesh类的部分实现代码,没有列出的部分都是只有一行代码的函数,用于访问私有成员变量。在本书的配套网站上可以找到完整的实现代码。

列表15.5 The Mesh.cpp File (Abbreviated)

#include "Mesh.h"#include "Model.h"#include "Game.h"#include "GameException.h"#include <assimp/scene.h>namespace Library{Mesh::Mesh(Model& model, aiMesh& mesh): mModel(model), mMaterial(nullptr), mName(mesh.mName.C_Str()), mVertices(), mNormals(), mTangents(), mBiNormals(), mTextureCoordinates(), mVertexColors(), mFaceCount(0), mIndices(){mMaterial = mModel.Materials().at(mesh.mMaterialIndex);// VerticesmVertices.reserve(mesh.mNumVertices);for (UINT i = 0; i < mesh.mNumVertices; i++){mVertices.push_back(XMFLOAT3(reinterpret_cast<const float*>(&mesh.mVertices[i])));}// Normalsif (mesh.HasNormals()){mNormals.reserve(mesh.mNumVertices);for (UINT i = 0; i < mesh.mNumVertices; i++){mNormals.push_back(XMFLOAT3(reinterpret_cast<const float*>(&mesh.mNormals[i])));}}// Tangents and Binormalsif (mesh.HasTangentsAndBitangents()){mTangents.reserve(mesh.mNumVertices);mBiNormals.reserve(mesh.mNumVertices);for (UINT i = 0; i < mesh.mNumVertices; i++){mTangents.push_back(XMFLOAT3(reinterpret_cast<const float*>(&mesh.mTangents[i])));mBiNormals.push_back(XMFLOAT3(reinterpret_cast<const float*>(&mesh.mBitangents[i])));}}// Texture CoordinatesUINT uvChannelCount = mesh.GetNumUVChannels();for (UINT i = 0; i < uvChannelCount; i++){std::vector<XMFLOAT3>* textureCoordinates = new std::vector<XMFLOAT3>();textureCoordinates->reserve(mesh.mNumVertices);mTextureCoordinates.push_back(textureCoordinates);aiVector3D* aiTextureCoordinates = mesh.mTextureCoords[i];for (UINT j = 0; j < mesh.mNumVertices; j++){textureCoordinates->push_back(XMFLOAT3(reinterpret_cast<const float*>(&aiTextureCoordinates[j])));}}// Vertex ColorsUINT colorChannelCount = mesh.GetNumColorChannels();for (UINT i = 0; i < colorChannelCount; i++){std::vector<XMFLOAT4>* vertexColors = new std::vector<XMFLOAT4>();vertexColors->reserve(mesh.mNumVertices);mVertexColors.push_back(vertexColors);aiColor4D* aiVertexColors = mesh.mColors[i];for (UINT j = 0; j < mesh.mNumVertices; j++){vertexColors->push_back(XMFLOAT4(reinterpret_cast<const float*>(&aiVertexColors[j])));}}// Faces (note: could pre-reserve if we limit primitive types)if (mesh.HasFaces()){mFaceCount = mesh.mNumFaces;for (UINT i = 0; i < mFaceCount; i++){aiFace* face = &mesh.mFaces[i];for (UINT j = 0; j < face->mNumIndices; j++){mIndices.push_back(face->mIndices[j]);}}}}Mesh::~Mesh(){for (std::vector<XMFLOAT3>* textureCoordinates : mTextureCoordinates){delete textureCoordinates;}for (std::vector<XMFLOAT4>* vertexColors : mVertexColors){delete vertexColors;}}void Mesh::CreateIndexBuffer(ID3D11Buffer** indexBuffer){assert(indexBuffer != nullptr);D3D11_BUFFER_DESC indexBufferDesc;ZeroMemory(&indexBufferDesc, sizeof(indexBufferDesc));indexBufferDesc.ByteWidth = sizeof(UINT) * mIndices.size();indexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE;indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;D3D11_SUBRESOURCE_DATA indexSubResourceData;ZeroMemory(&indexSubResourceData, sizeof(indexSubResourceData));indexSubResourceData.pSysMem = &mIndices[0];if (FAILED(mModel.GetGame().Direct3DDevice()->CreateBuffer(&indexBufferDesc, &indexSubResourceData, indexBuffer))){throw GameException("ID3D11Device::CreateBuffer() failed.");}}}

完全理解Mesh构造函数中的具体语法可能需要仔细的阅读代码,但是大致的执行过程是非常简单的。只需要遍历aiMesh结构体中的变量,并把变量所表示的数据赋值给Mesh类的成员变量中即可。打开Open Asset Import Library的网站,查看aiMesh类的具体信息。

在Mesh类中有两个成员变量Mesh::mTextureCoordinates和Mesh::mVertexColors,这是一个XMFLOAT3数组类型的数组。表示在一个mesh中具有多组坐标和颜色数据,用于多个独立的纹理或者渲染techniques。另外还有一个ModelMaterial指针类型的Mesh::mMaterial成员变量,指向Model类中存储的ModelMaterial对象的其中之一。
最后简单看一下Mesh::CrateIndexBuffer()函数,该函数的实现看起来非常熟悉,与在cube示例中创建一个index buffer的代码完全相同。把这段代码添加到Mesh类中是为了代码复用。

Loading Model Materials

在列表15.6中列出了ModelMaterial类的实现代码,这是在创建一个示例程序使用模型系统之前的最后一部分代码。在ModelMaterial的构造函数中,通过使用Open Asset Import Library的aiMaterial结构体对象获取材质数据。在该实现代码中,唯一需要考虑的是Open Asset Import Library中的纹理类型与ModelMaterial中自定义的TextureType枚举值一一对应。同样地,这么做也是为了避免在应用程序的其他地方调用Open Asset Import Library。在ModelMatrial::InitializeTextureTypeMappings()函数中初始化了静态成员变量sTextureTypeMappings。

列表15.6 The ModelMaterial.cpp File

#include "ModelMaterial.h"#include "GameException.h"#include "Utility.h"#include <assimp/scene.h>namespace Library{    std::map<TextureType, UINT> ModelMaterial::sTextureTypeMappings;    ModelMaterial::ModelMaterial(Model& model)        : mModel(model), mTextures()    {        InitializeTextureTypeMappings();    }    ModelMaterial::ModelMaterial(Model& model, aiMaterial* material)        : mModel(model), mTextures()    {        InitializeTextureTypeMappings();        aiString name;        material->Get(AI_MATKEY_NAME, name);        mName = name.C_Str();        for (TextureType textureType = (TextureType)0; textureType < TextureTypeEnd; textureType = (TextureType)(textureType + 1))        {            aiTextureType mappedTextureType = (aiTextureType)sTextureTypeMappings[textureType];            UINT textureCount = material->GetTextureCount(mappedTextureType);            if (textureCount > 0)            {                std::vector<std::wstring>* textures = new std::vector<std::wstring>();                mTextures.insert(std::pair<TextureType, std::vector<std::wstring>*>(textureType, textures));                textures->reserve(textureCount);                for (UINT textureIndex = 0; textureIndex < textureCount; textureIndex++)                {                    aiString path;                    if (material->GetTexture(mappedTextureType, textureIndex, &path) == AI_SUCCESS)                    {                        std::wstring wPath;                        Utility::ToWideString(path.C_Str(), wPath);                        textures->push_back(wPath);                    }                }            }        }    }    ModelMaterial::~ModelMaterial()    {        for (std::pair<TextureType, std::vector<std::wstring>*> textures : mTextures)        {            DeleteObject(textures.second);        }    }    Model& ModelMaterial::GetModel()    {        return mModel;    }    const std::string& ModelMaterial::Name() const    {        return mName;    }    const std::map<TextureType, std::vector<std::wstring>*> ModelMaterial::Textures() const    {        return mTextures;    }    void ModelMaterial::InitializeTextureTypeMappings()    {        if (sTextureTypeMappings.size() != TextureTypeEnd)        {            sTextureTypeMappings[TextureTypeDifffuse] = aiTextureType_DIFFUSE;            sTextureTypeMappings[TextureTypeSpecularMap] = aiTextureType_SPECULAR;            sTextureTypeMappings[TextureTypeAmbient] = aiTextureType_AMBIENT;            sTextureTypeMappings[TextureTypeHeightmap] = aiTextureType_HEIGHT;            sTextureTypeMappings[TextureTypeNormalMap] = aiTextureType_NORMALS;            sTextureTypeMappings[TextureTypeSpecularPowerMap] = aiTextureType_SHININESS;            sTextureTypeMappings[TextureTypeDisplacementMap] = aiTextureType_DISPLACEMENT;            sTextureTypeMappings[TextureTypeLightMap] = aiTextureType_LIGHTMAP;        }    }}

在列表15.6中缺少了ModleMatrial类的部分实现代码,具体的是一组用于匹配shader变量的键值对。在当前阶段故意没有实现添加这些代码,是因为还没有介绍一个完整的材质系统。在下章将会开发一个完整的材质系统。
0 0