Getting vertices from a skeletal model with Assimp and DirectX C++

Viewed 18

What I have is some code that I have got from trying to reverse engineer model loading code, because all the instructions that I have seen show how to transform the vertex positions in the shader and adapt the code to that. At first, I didn't know how I would store the information that needs to be loaded in because there are a few ways to do it. I finally just store bone IDs with their weights and pushed a vector for each mesh. The reason why I am writing code like this is that I want vertex positions from my model while I am animating it. This is something that I need for, some physics that I want to apply. I am not sure I got everything right because I am looking at OpenGL and using DirectX and some of the functions, I need I do not know what they are called. Here is my code.

//SkinMesh.h

#pragma once
#include "Vertex.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "ConstantBuffer.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include "Texture.h"
//#include <vector>


struct BoneMatrix
{
    aiMatrix4x4 offset_matrix;
    aiMatrix4x4 final_world_transform;

    //glm::mat4 offset_matrix;
    //glm::mat4 final_world_transform;
};

struct pasvars {
    std::vector<Vertex> vertices;
    std::vector<DWORD> indices;
};
class SkinMesh
{
public:
    SkinMesh(ID3D11Device* device, ID3D11DeviceContext* deviceContext, std::vector<Vertex>& vertices, std::vector<DWORD>& indices, std::vector<Texture>& textures, const DirectX::XMMATRIX& transformMatrix);
    SkinMesh(const SkinMesh& mesh);
    void Draw();
    const DirectX::XMMATRIX& GetTransformMatrix();
    pasvars getVars();
    DirectX::XMFLOAT4 vertPos(UINT vert);
    void setVert(DirectX::XMFLOAT4 pos, UINT vert);
private:
    
    std::vector<Vertex> vertices;
    VertexBuffer<Vertex> vertexbuffer;
    std::vector<DWORD> inducies;
    IndexBuffer indexbuffer;
    ID3D11DeviceContext* deviceContext;
    std::vector<Texture> textures;
    DirectX::XMMATRIX transformMatrix;
}; 

//SkinMesh.cpp

#include "SkinMesh.h"

SkinMesh::SkinMesh(ID3D11Device* device, ID3D11DeviceContext* deviceContext, std::vector<Vertex>& vertices, std::vector<DWORD>& indices, std::vector<Texture>& textures, const DirectX::XMMATRIX& transformMatrix)
{
    this->deviceContext = deviceContext;
    this->textures = textures;
    this->transformMatrix = transformMatrix;
    this->vertices = vertices;
    this->inducies = indices;

    //HRESULT hr = this->vertexbuffer.Initialize(device, vertices.data(), vertices.size());
    //COM_ERROR_IF_FAILED(hr, "Failed to initialize vertex buffer for SkinMesh.");
    //
    //hr = this->indexbuffer.Initialize(device, indices.data(), indices.size());
    //COM_ERROR_IF_FAILED(hr, "Failed to initialize index buffer for SkinMesh.");
}

SkinMesh::SkinMesh(const SkinMesh& SkinMesh)
{
    this->deviceContext = SkinMesh.deviceContext;
    this->inducies = SkinMesh.inducies;
    this->vertices = SkinMesh.vertices;
    this->textures = SkinMesh.textures;
    this->transformMatrix = SkinMesh.transformMatrix;
}

void SkinMesh::Draw()
{
    UINT offset = 0;

    for (int i = 0; i < textures.size(); i++)
    {
        if (textures[i].GetType() == aiTextureType::aiTextureType_DIFFUSE)
        {
            this->deviceContext->PSSetShaderResources(0, 1, textures[i].GetTextureResourceViewAddress());
            break;
        }
    }

    this->deviceContext->IASetVertexBuffers(0, 1, this->vertexbuffer.GetAddressOf(), this->vertexbuffer.StridePtr(), &offset);
    this->deviceContext->IASetIndexBuffer(this->indexbuffer.Get(), DXGI_FORMAT::DXGI_FORMAT_R32_UINT, 0);
    this->deviceContext->DrawIndexed(this->indexbuffer.IndexCount(), 0, 0);
}

const DirectX::XMMATRIX& SkinMesh::GetTransformMatrix()
{
    return this->transformMatrix;
}

pasvars SkinMesh::getVars() {
    pasvars ret;
    ret.indices = this->inducies;
    ret.vertices = this->vertices;
    return ret;
}

DirectX::XMFLOAT4 SkinMesh::vertPos(UINT vert) {
    return DirectX::XMFLOAT4(vertices.at(vert).pos.x, vertices.at(vert).pos.y, vertices.at(vert).pos.z, 1.0f);
}

void SkinMesh::setVert(DirectX::XMFLOAT4 pos, UINT vert) {
    vertices.at(vert).pos.x = pos.x;
    vertices.at(vert).pos.y = pos.y;
    vertices.at(vert).pos.z = pos.z;
}

//SkinModel.h

#pragma once
#include "VertexBoneData.h"
#include <DirectXMath.h>
#include "SkinMesh.h"
#include <string>
#include <map>
using namespace std;
using namespace DirectX;

struct BoneAndWeight {
    vector<UINT> boneIdx;
    vector<float> weights;
};

struct BoneToMesh {
    UINT meshNum;
    vector<UINT> vertIdx;
    vector<float> weights;
    XMMATRIX boneMatrix;
};

struct BoneData {
    vector<BoneAndWeight> access;
    string name;
};

struct BoneMat {
    XMMATRIX final;
    XMMATRIX offset;
};

class SkinModel
{
public:
    bool Initialize(const std::string& filePath, ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader);
    void Draw(const XMMATRIX& worldMatrix, const XMMATRIX& viewProjectionMatrix);
    void Transform();
private:
    void getzero();
    XMMATRIX zeroed;
    void readNodeHierarchy(const aiNode* p_node, const XMMATRIX parent_transform);
    XMMATRIX global_inverse;
    map<string, UINT> m_bone_mapping;
    std::vector<SkinMesh> meshes;
    std::vector<SkinMesh> transformed;
    bool LoadModel(const std::string& filePath);
    void ProcessNode(aiNode* node, const aiScene* scene, const XMMATRIX& parentTransformMatrix);
    SkinMesh ProcessMesh(aiMesh* mesh, const aiScene* scene, const XMMATRIX& transformMatrix);
    TextureStorageType DetermineTextureStorageType(const aiScene* pScene, aiMaterial* pMat, unsigned int index, aiTextureType textureType);
    std::vector<Texture> LoadMaterialTextures(aiMaterial* pMaterial, aiTextureType textureType, const aiScene* pScene);
    int GetTextureIndex(aiString* pStr);
    vector<UINT> meshinfo;
    ID3D11Device* device = nullptr;
    ID3D11DeviceContext* deviceContext = nullptr;
    ConstantBuffer<CB_VS_vertexshader>* cb_vs_vertexshader = nullptr;
    std::string directory = "";
    UINT m_num_bones = 0;
    
    int meshIndex = 0;
    const aiNode* rootNode;
    vector<BoneData> theBones;
    
    vector<VertexBoneData> bones_id_weights;
    vector<XMFLOAT3> boneRotations;
    vector<BoneMat> m_bone_matrices;
    //vector<MeshInfo> m_mesh_infos;
    
};

//SkinModel.cpp

#include "SkinModel.h"

bool SkinModel::Initialize(const std::string& filePath, ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader)
{
    getzero();
    this->device = device;
    this->deviceContext = deviceContext;
    this->cb_vs_vertexshader = &cb_vs_vertexshader;

    try
    {
        if (!this->LoadModel(filePath))
            return false;
    }
    catch (COMException& exception)
    {
        ErrorLogger::Log(exception);
        return false;
    }

    return true;
}

void SkinModel::Draw(const XMMATRIX& worldMatrix, const XMMATRIX& viewProjectionMatrix)
{
    this->deviceContext->VSSetConstantBuffers(0, 1, this->cb_vs_vertexshader->GetAddressOf());

    for (int i = 0; i < transformed.size(); i++)
    {
        //Update Constant buffer with WVP Matrix
        this->cb_vs_vertexshader->data.mat = meshes[i].GetTransformMatrix() * worldMatrix * viewProjectionMatrix; //Calculate World-View-Projection Matrix
        this->cb_vs_vertexshader->data.mat = XMMatrixTranspose(this->cb_vs_vertexshader->data.mat);
        this->cb_vs_vertexshader->ApplyChanges();
        transformed[i].Draw();
    }
}

bool SkinModel::LoadModel(const std::string& filePath)
{
    this->directory = StringHelper::GetDirectoryFromPath(filePath);

    Assimp::Importer importer;

    const aiScene* pScene = importer.ReadFile(filePath,
        aiProcess_Triangulate |
        aiProcess_ConvertToLeftHanded);

    if (pScene == nullptr)
        return false;

    rootNode = pScene->mRootNode;
    global_inverse = XMMatrixTranspose(XMMATRIX(&pScene->mRootNode->mTransformation.a1));
    //global_inverse = XMMatrixInverse(); // I dont know how to get an inverse in DirectX.
    this->ProcessNode(pScene->mRootNode, pScene, DirectX::XMMatrixIdentity());
    transformed.resize(meshes.size());
    return true;
}

void SkinModel::ProcessNode(aiNode* node, const aiScene* scene, const XMMATRIX& parentTransformMatrix)
{
    vector<VertexBoneData> bones_id_weights;

    XMMATRIX nodeTransformMatrix = XMMatrixTranspose(XMMATRIX(&node->mTransformation.a1)) * parentTransformMatrix;

    for (UINT i = 0; i < node->mNumMeshes; i++)
    {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        meshes.push_back(this->ProcessMesh(mesh, scene, nodeTransformMatrix));
    }

    for (UINT i = 0; i < node->mNumChildren; i++)
    {
        this->ProcessNode(node->mChildren[i], scene, nodeTransformMatrix);
    }
}


SkinMesh SkinModel::ProcessMesh(aiMesh* mesh, const aiScene* scene, const XMMATRIX& transformMatrix)
{
    vector<BoneAndWeight> bwstor;
    bwstor.resize(mesh->mNumVertices);
    // Data to fill
    std::vector<Vertex> vertices;
    std::vector<DWORD> indices;
    //Get vertices
    for (UINT i = 0; i < mesh->mNumVertices; i++)
    {
        Vertex vertex;

        vertex.pos.x = mesh->mVertices[i].x;
        vertex.pos.y = mesh->mVertices[i].y;
        vertex.pos.z = mesh->mVertices[i].z;

        if (mesh->mTextureCoords[0])
        {
            vertex.texCoord.x = (float)mesh->mTextureCoords[0][i].x;
            vertex.texCoord.y = (float)mesh->mTextureCoords[0][i].y;
        }

        vertices.push_back(vertex);
    }

    //Get indices
    for (UINT i = 0; i < mesh->mNumFaces; i++)
    {
        aiFace face = mesh->mFaces[i];

        for (UINT j = 0; j < face.mNumIndices; j++)
            indices.push_back(face.mIndices[j]);
    }
    bones_id_weights.resize(mesh->mNumVertices);
    for (UINT i = 0; i < mesh->mNumBones; i++)
    {
        UINT bone_index = 0;
        string bone_name(mesh->mBones[i]->mName.data);
        

        if (m_bone_mapping.find(bone_name) == m_bone_mapping.end()) 
        {
            // Allocate an index for a new bone
            bone_index = m_num_bones;
            m_num_bones++;
            XMMATRIX bi;
            BoneMat pushMat;
            pushMat.offset = bi;
            m_bone_matrices.push_back(pushMat);
            m_bone_matrices[bone_index].offset = XMMatrixTranspose(XMMATRIX(&mesh->mBones[i]->mOffsetMatrix.a1));
            m_bone_mapping[bone_name] = bone_index;
            BoneData abone;
            theBones.push_back(abone);
        }
        else
        {
            bone_index = m_bone_mapping[bone_name];
        }
        BoneToMesh stor;
        stor.meshNum = meshIndex;
        for (UINT j = 0; j < mesh->mBones[i]->mNumWeights; j++)
        {
            UINT vertex_id = mesh->mBones[i]->mWeights[j].mVertexId;
            float weight = mesh->mBones[i]->mWeights[j].mWeight;
            bwstor.at(vertex_id).boneIdx.push_back(bone_index);
            bwstor.at(vertex_id).weights.push_back(weight);
        }
        
    }
    BoneData dat;
    dat.access = bwstor;
    theBones.push_back(dat);
    
    std::vector<Texture> textures;
    aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
    std::vector<Texture> diffuseTextures = LoadMaterialTextures(material, aiTextureType::aiTextureType_DIFFUSE, scene);
    textures.insert(textures.end(), diffuseTextures.begin(), diffuseTextures.end());
    meshIndex++;
    return SkinMesh(this->device, this->deviceContext, vertices, indices, textures, transformMatrix);
}

TextureStorageType SkinModel::DetermineTextureStorageType(const aiScene* pScene, aiMaterial* pMat, unsigned int index, aiTextureType textureType)
{
    if (pMat->GetTextureCount(textureType) == 0)
        return TextureStorageType::None;

    aiString path;
    pMat->GetTexture(textureType, index, &path);
    std::string texturePath = path.C_Str();
    //Check if texture is an embedded indexed texture by seeing if the file path is an index #
    if (texturePath[0] == '*')
    {
        if (pScene->mTextures[0]->mHeight == 0)
        {
            return TextureStorageType::EmbeddedIndexCompressed;
        }
        else
        {
            assert("SUPPORT DOES NOT EXIST YET FOR INDEXED NON COMPRESSED TEXTURES!" && 0);
            return TextureStorageType::EmbeddedIndexNonCompressed;
        }
    }
    //Check if texture is an embedded texture but not indexed (path will be the texture's name instead of #)
    if (auto pTex = pScene->GetEmbeddedTexture(texturePath.c_str()))
    {
        if (pTex->mHeight == 0)
        {
            return TextureStorageType::EmbeddedCompressed;
        }
        else
        {
            assert("SUPPORT DOES NOT EXIST YET FOR EMBEDDED NON COMPRESSED TEXTURES!" && 0);
            return TextureStorageType::EmbeddedNonCompressed;
        }
    }
    //Lastly check if texture is a filepath by checking for period before extension name
    if (texturePath.find('.') != std::string::npos)
    {
        return TextureStorageType::Disk;
    }

    return TextureStorageType::None; // No texture exists
}

std::vector<Texture> SkinModel::LoadMaterialTextures(aiMaterial* pMaterial, aiTextureType textureType, const aiScene* pScene)
{
    std::vector<Texture> materialTextures;
    TextureStorageType storetype = TextureStorageType::Invalid;
    unsigned int textureCount = pMaterial->GetTextureCount(textureType);

    if (textureCount == 0) //If there are no textures
    {
        storetype = TextureStorageType::None;
        aiColor3D aiColor(0.0f, 0.0f, 0.0f);
        switch (textureType)
        {
        case aiTextureType_DIFFUSE:
            pMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, aiColor);
            if (aiColor.IsBlack()) //If color = black, just use grey
            {
                materialTextures.push_back(Texture(this->device, Colors::UnloadedTextureColor, textureType));
                return materialTextures;
            }
            materialTextures.push_back(Texture(this->device, Color(aiColor.r * 255, aiColor.g * 255, aiColor.b * 255), textureType));
            return materialTextures;
        }
    }
    else
    {
        for (UINT i = 0; i < textureCount; i++)
        {
            aiString path;
            pMaterial->GetTexture(textureType, i, &path);
            TextureStorageType storetype = DetermineTextureStorageType(pScene, pMaterial, i, textureType);
            switch (storetype)
            {
            case TextureStorageType::EmbeddedIndexCompressed:
            {
                int index = GetTextureIndex(&path);
                Texture embeddedIndexedTexture(this->device,
                    reinterpret_cast<uint8_t*>(pScene->mTextures[index]->pcData),
                    pScene->mTextures[index]->mWidth,
                    textureType);
                materialTextures.push_back(embeddedIndexedTexture);
                break;
            }
            case TextureStorageType::EmbeddedCompressed:
            {
                const aiTexture* pTexture = pScene->GetEmbeddedTexture(path.C_Str());
                Texture embeddedTexture(this->device,
                    reinterpret_cast<uint8_t*>(pTexture->pcData),
                    pTexture->mWidth,
                    textureType);
                materialTextures.push_back(embeddedTexture);
                break;
            }
            case TextureStorageType::Disk:
            {
                std::string filename = this->directory + '\\' + path.C_Str();
                Texture diskTexture(this->device, filename, textureType);
                materialTextures.push_back(diskTexture);
                break;
            }
            }
        }
    }

    if (materialTextures.size() == 0)
    {
        materialTextures.push_back(Texture(this->device, Colors::UnhandledTextureColor, aiTextureType::aiTextureType_DIFFUSE));
    }
    return materialTextures;

}

int SkinModel::GetTextureIndex(aiString* pStr)
{
    assert(pStr->length >= 2);
    return atoi(&pStr->C_Str()[1]);
}

void SkinModel::Transform() {
    readNodeHierarchy(rootNode, XMMatrixIdentity());
    for (UINT i = 0; i < meshes.size(); i++) {
        for (UINT j = 0; j < theBones.at(i).access.size();j++) {
            XMMATRIX trans = zeroed;
            if (theBones.at(i).access.at(j).boneIdx.size() > 0) {
                for (UINT k = 0; k < theBones.at(i).access.at(j).boneIdx.size(); k++) {
                    UINT curidx = theBones.at(i).access.at(j).boneIdx.at(k);
                    trans += m_bone_matrices.at(curidx).final * theBones.at(i).access.at(j).weights.at(k);
                }
            }else{
                trans = XMMatrixIdentity();
            }
            XMFLOAT4 tomul = meshes.at(i).vertPos(j);
            XMVECTOR amul = XMLoadFloat4(&tomul);
            XMVector4Transform(amul, trans);
            XMFLOAT4 tr;
            //How do I get a vector position from this
            transformed.at(i).setVert(tr, j);
        }
    }
}

void SkinModel::readNodeHierarchy(const aiNode* p_node, const XMMATRIX parent_transform) {
    string node_name(p_node->mName.data);
    if (m_bone_mapping.find(node_name) != m_bone_mapping.end()) // true if node_name exist in bone_mapping
    {
        UINT bonenum = m_bone_mapping[node_name];
        XMMATRIX node_transform = XMMatrixRotationRollPitchYaw(boneRotations.at(bonenum).x, boneRotations.at(bonenum).y, boneRotations.at(bonenum).z);
        const XMMATRIX global_transform = parent_transform * node_transform;


        UINT bone_index = m_bone_mapping[node_name];
        m_bone_matrices[bone_index].final = global_inverse * global_transform * m_bone_matrices[bone_index].offset;


        for (UINT i = 0; i < p_node->mNumChildren; i++)
        {
            readNodeHierarchy(p_node->mChildren[i], global_transform);
        }
    }
}

void SkinModel::getzero() {
    zeroed = XMMatrixSet(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
0 Answers
Related