I have a skeletal model and it looks right when it is loaded. When it is transformed it is really distorted. To me it seems to be stretched in the Y-axis It doesn't have much detail because I haven't got to that yet. I just have values inputted when it comes to the rotation. These are aiQuaternion types set to 0.0 that can go to 2PI. I am using Assimp to load the model and its types for the transformation and there might be problems with conversion.
I can show before transformed and after.
this is the code:
//Model.h
#pragma once
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include "Mesh.h"
#include <map>
#include <unordered_map>
#include <string>
using namespace std;
using namespace DirectX;
struct Bone {
int id = 0; // position of the bone in final upload array
std::string name = "";
XMMATRIX offset = XMMatrixIdentity();
std::vector<Bone> children = {};
};
class Model
{
public:
bool Initialize(const std::string & filePath, ID3D11Device * device, ID3D11DeviceContext * deviceContext, ConstantBuffer<CB_VS_vertexshader> & cb_vs_vertexshader, bool skel);
void Draw(const XMMATRIX & worldMatrix, const XMMATRIX & viewProjectionMatrix);
void Draw(const XMMATRIX& worldMatrix, const XMMATRIX& viewProjectionMatrix, float locx, float locz);
void boneTransform(ID3D11Device* device);
bool makeGrass(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader);
bool makeMount(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader);
bool makeWindow(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader);
std::vector<Vertex> getVert(int num);
std::vector<DWORD> getIndu(int num);
float boneRots[500][4];
int numbones = 0;
string bonenames[500];
Bone skeleton;
private:
std::vector<Mesh> meshes;
XMMATRIX convertMat(aiMatrix4x4 m);
bool LoadModel(const std::string & filePath, bool hasSkel);
void ProcessNode(aiNode * node, const aiScene * scene, const XMMATRIX & parentTransformMatrix);
Mesh ProcessMesh(aiNode* node, aiMesh * mesh, const aiScene * scene, const XMMATRIX & transformMatrix);
void readNodeHierarchy(const aiNode* p_node, const aiMatrix4x4 parent_transform);
void updateMeshes(ID3D11Device* device);
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);
bool first;
ID3D11Device * device = nullptr;
ID3D11DeviceContext * deviceContext = nullptr;
ConstantBuffer<CB_VS_vertexshader> * cb_vs_vertexshader = nullptr;
std::string directory = "";
bool hasSkeleton;
aiMatrix4x4 m_global_inverse_transform;
std::vector<Mesh> transformed;
std::map<std::string, unsigned int> m_bone_mapping;
std::vector<BoneMatrix> m_bone_matrices;
std::vector<XMMATRIX> glmTransforms;
unsigned int m_num_bones = 0;
std::vector<MeshInfo> m_mesh_infos;
int mesh_index = 0;
std::vector<MeshBones> forMesh;
std::vector<unsigned int> texnum;
//std::unordered_map<std::string, std::pair<int, XMMATRIX>> boneInfo = {};
};
//Model.cpp
#include "Model.h"
#include <random>
unsigned int seed(165981);
mt19937 gen(seed);
uniform_real_distribution<double> dist(0, 1);
aiNode* root1;
bool Model::Initialize(const std::string & filePath, ID3D11Device * device, ID3D11DeviceContext * deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader, bool skel)
{
this->device = device;
this->deviceContext = deviceContext;
this->cb_vs_vertexshader = &cb_vs_vertexshader;
try
{
if (!this->LoadModel(filePath, skel))
return false;
}
catch (COMException & exception)
{
ErrorLogger::Log(exception);
return false;
}
return true;
}
void Model::Draw(const XMMATRIX & worldMatrix, const XMMATRIX & viewProjectionMatrix)
{
this->deviceContext->VSSetConstantBuffers(0, 1, this->cb_vs_vertexshader->GetAddressOf());
if (hasSkeleton) {
this->cb_vs_vertexshader->data.mat = viewProjectionMatrix; //Calculate World-View-Projection Matrix
this->cb_vs_vertexshader->data.mat = XMMatrixTranspose(this->cb_vs_vertexshader->data.mat);
this->cb_vs_vertexshader->ApplyChanges();
for (int i = 0; i < transformed.size(); i++) transformed[i].Draw();
}else{
for (int i = 0; i < meshes.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();
meshes[i].Draw();
}
}
}
void Model::Draw(const XMMATRIX& worldMatrix, const XMMATRIX& viewProjectionMatrix, float locx, float locz) {
if (hasSkeleton) {
int non = 0;
}
this->deviceContext->VSSetConstantBuffers(0, 1, this->cb_vs_vertexshader->GetAddressOf());
char ord[] = {0, 0, 0};
float val[] = { 0, 0, 0 };
char last = 0;
for (char i = 0; i < 3; i++) {
float relx = meshes.at(i).locx - locx;
float relz = meshes.at(i).locz - locz;
float dist = sqrt(relx * relx + relz * relz);
for (char j = 0; j < 3; j++) {
if (dist > val[j]) {
last = j;
break;
}
}
if (last < 2) {
for (char j = 1; j >= last; j--) {
ord[j + 1] = ord[j];
val[j + 1] = val[j];
}
}
ord[last] = i;
val[last] = dist;
}
for (int i = 0; i < 3; i++)
{
int cho = (int)ord[i];
//Update Constant buffer with WVP Matrix
this->cb_vs_vertexshader->data.mat = meshes[cho].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();
meshes[cho].Draw();
}
}
std::vector<Vertex> Model::getVert(int num) {
std::vector<Vertex> ret;
if (num < meshes.size()) ret = meshes.at(num).getVerts();
return ret;
}
std::vector<DWORD> Model::getIndu(int num) {
std::vector<DWORD> ret;
if (num < meshes.size()) ret = meshes.at(num).getIndus();
return ret;
}
bool Model::LoadModel(const std::string & filePath, bool hasSkel)
{
first = true;
hasSkeleton = hasSkel;
this->directory = StringHelper::GetDirectoryFromPath(filePath);
Assimp::Importer importer;
const aiScene* pScene = importer.ReadFile(filePath,
aiProcess_Triangulate |
aiProcess_ConvertToLeftHanded | aiProcess_OptimizeMeshes);
if (pScene == nullptr)
return false;
root1 = pScene->mRootNode;
m_global_inverse_transform = pScene->mRootNode->mTransformation;
m_global_inverse_transform.Inverse();
this->ProcessNode(pScene->mRootNode, pScene, DirectX::XMMatrixIdentity());
return true;
}
void Model::ProcessNode(aiNode * node, const aiScene * scene, const XMMATRIX & parentTransformMatrix)
{
XMMATRIX nodeTransformMatrix = XMMatrixTranspose(XMMATRIX(&node->mTransformation.a1)) * parentTransformMatrix;
for (UINT i = 0; i < node->mNumMeshes; i++)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
Mesh mdup = this->ProcessMesh(node, mesh, scene, nodeTransformMatrix);
meshes.push_back(mdup);
if (hasSkeleton) transformed.push_back(mdup);
}
//int num = node->mNumChildren;
for (UINT i = 0; i < node->mNumChildren; i++)
{
this->ProcessNode(node->mChildren[i], scene, nodeTransformMatrix);
}
}
Mesh Model::ProcessMesh(aiNode* node, aiMesh* mesh, const aiScene* scene, const XMMATRIX& transformMatrix)
{
// Data to fill
std::vector<Vertex> vertices;
std::vector<DWORD> indices;
std::vector<VertexBoneData> bones_id_weights;
VertexBoneData emp;
//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);
bones_id_weights.push_back(emp);
}
//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]);
}
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());
if (hasSkeleton) {
//std::unordered_map<std::string, std::pair<int, XMMATRIX>> boneInfo = {};
std::vector<unsigned int> boneCounts;
boneCounts.resize(vertices.size(), 0);
int nBoneCount = mesh->mNumBones;
for (unsigned int i = 0; i < mesh->mNumBones; i++)
{
unsigned int bone_index = 0;
std::string bone_name(mesh->mBones[i]->mName.data);
if (m_bone_mapping.find(bone_name) == m_bone_mapping.end())
{
bonenames[numbones] = bone_name;
bone_index = m_num_bones;
m_num_bones++;
BoneMatrix bi;
m_bone_matrices.push_back(bi);
m_bone_matrices[bone_index].offset_matrix = mesh->mBones[i]->mOffsetMatrix;
m_bone_mapping[bone_name] = bone_index;
for (int j = 0; j < 4; j++) {
boneRots[numbones][j] = 0;
}
XMMATRIX pusher1;
glmTransforms.push_back(pusher1);
numbones++;
}
else
{
bone_index = m_bone_mapping[bone_name];
}
for (unsigned int j = 0; j < mesh->mBones[i]->mNumWeights; j++)
{
unsigned int vertex_id = mesh->mBones[i]->mWeights[j].mVertexId;
float weight = mesh->mBones[i]->mWeights[j].mWeight;
if (vertex_id >= bones_id_weights.size()) {
int non = 0;
}
bones_id_weights[vertex_id].ids.push_back(bone_index);
bones_id_weights[vertex_id].weights.push_back(weight);
}
}
MeshBones tmp;
tmp.boneInfl = bones_id_weights;
tmp.mesh = mesh_index;
forMesh.push_back(tmp);
mesh_index++;
}
return Mesh(this->device, this->deviceContext, vertices, indices, textures, transformMatrix);
}
TextureStorageType Model::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> Model::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 Model::GetTextureIndex(aiString * pStr)
{
assert(pStr->length >= 2);
return atoi(&pStr->C_Str()[1]);
}
void Model::readNodeHierarchy(const aiNode* p_node, const aiMatrix4x4 parent_transform)
{
std::string aname = p_node->mName.data;
aiMatrix4x4 global_transform;
std::string node_name(p_node->mName.data);
aiMatrix4x4 node_transform = p_node->mTransformation;
int sizes = 0;
for (int i = 0; i < meshes.size(); i++) sizes += meshes.at(i).getSize();
if (m_bone_mapping.find(node_name) != m_bone_mapping.end())
{
int bi = m_bone_mapping[node_name];
//aiVector3D scaling_vector = node_anim->mScalingKeys[2].mValue;
aiVector3D scaling_vector = aiVector3D(1, 1, 1);
aiMatrix4x4 scaling_matr;
aiMatrix4x4::Scaling(scaling_vector, scaling_matr);
//rotation
//aiQuaternion rotate_quat = node_anim->mRotationKeys[2].mValue;
aiQuaternion rotate_quat = aiQuaternion(boneRots[bi][0], boneRots[bi][1], boneRots[bi][2], boneRots[bi][3]);;
aiMatrix4x4 rotate_matr = aiMatrix4x4(rotate_quat.GetMatrix());
//translation
//aiVector3D translate_vector = node_anim->mPositionKeys[2].mValue;
aiVector3D translate_vector = aiVector3D(0, 0, 0);
aiMatrix4x4 translate_matr;
aiMatrix4x4::Translation(translate_vector, translate_matr);
node_transform = translate_matr * rotate_matr * scaling_matr;
aiMatrix4x4 global_transform = parent_transform * node_transform;
unsigned int bone_ind1 = m_bone_mapping[node_name];
m_bone_matrices[bone_ind1].final_world_transform = m_global_inverse_transform * global_transform * m_bone_matrices[bone_ind1].offset_matrix;
glmTransforms[bone_ind1] = XMMATRIX(&m_bone_matrices[bone_ind1].final_world_transform.a1);
//XMMATRIX(&pScene->mRootNode->mTransformation.a1)
}
int num = p_node->mNumChildren;
if (num > -1) {
for (unsigned int i = 0; i < p_node->mNumChildren; i++)
{
readNodeHierarchy(p_node->mChildren[i], global_transform);
}
}
}
void Model::boneTransform(ID3D11Device* device)
{
std::vector<aiMatrix4x4> transforms;
aiMatrix4x4 identity_matrix;
readNodeHierarchy(root1, identity_matrix);
glmTransforms.resize(m_num_bones);
for (unsigned int i = 0; i < m_num_bones; i++)
{
glmTransforms[i] = convertMat(m_bone_matrices[i].final_world_transform);
}
updateMeshes(device);
}
void Model::updateMeshes(ID3D11Device* device) {
for (int i = 0; i < forMesh.size(); i++) {
int curm = forMesh.at(i).mesh;
for (int j = 0; j < forMesh.at(i).boneInfl.size();j++) {
XMFLOAT3 opos = meshes.at(curm).getVertex(j);
XMMATRIX bend = XMMatrixSet(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (forMesh.at(curm).boneInfl.at(j).ids.size() > 0) {
for (int k = 0; k < forMesh.at(curm).boneInfl.at(j).ids.size(); k++) {
int matnum = forMesh.at(curm).boneInfl.at(j).ids.at(k);
float matwei = forMesh.at(curm).boneInfl.at(j).weights.at(k);
bend += glmTransforms.at(matnum) * matwei;
}
XMVECTOR invec = XMLoadFloat3(&opos);
XMVECTOR vec = XMVector3Transform(invec, bend);
XMFLOAT3 otpos;
XMStoreFloat3(&otpos, vec);
transformed.at(curm).setVertex(otpos, j);
}else{
}
}
transformed.at(curm).reinit(device);
}
}
bool Model::makeGrass(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader) {
if (!Initialize("Data\\Objects\\MyStuff\\grassblade.obj", device, deviceContext, cb_vs_vertexshader, false)) {
return false;
}
std::vector<Vertex> grassVerts;
std::vector<DWORD> grassIndu;
grassVerts = getVert(0);
grassIndu = getIndu(0);
std::vector<Vertex> outVerts;
std::vector<DWORD> outIndu;
int blanum = 0;
for (float x = -120; x < 120; x += .6) {
for (float z = -120; z < 120; z += .6) {
if (!(x > -25 && x < 25 && z > -25 && z < 25)) {
XMFLOAT3 grasPos(x, 0, z);
float col = dist(gen);
XMFLOAT3 grasScale(.5, .5, .5);
float myAngle = dist(gen) * 360;
float revAngle = dist(gen) * 90 - 45;
XMMATRIX WVP;
XMMATRIX translation, rotationX, rotationY, rotationZ, scale;
translation = XMMatrixTranslation(grasPos.x, 0, grasPos.z);
rotationX = XMMatrixRotationX(revAngle * 0.01745329251994329576923690768489);
rotationY = XMMatrixRotationY(myAngle * 0.01745329251994329576923690768489);
scale = XMMatrixScaling(50, 50, 50);
WVP = rotationX * rotationY * translation * scale;
int vertsiz = grassVerts.size();
for (int i = 0; i < vertsiz; i++) {
XMFLOAT3 inpos = grassVerts.at(i).pos;
XMVECTOR invec = XMLoadFloat3(&inpos);
XMVECTOR vec = XMVector3Transform(invec, WVP);
XMFLOAT3 otpos;
Vertex tmp;
XMStoreFloat3(&otpos, vec);
tmp.pos = otpos;
tmp.texCoord.x = col;
tmp.texCoord.y = 0;
outVerts.push_back(tmp);
}
int indsiz = grassIndu.size();
for (int i = 0; i < indsiz; i++) {
int strt = blanum * indsiz;
DWORD theind = strt + grassIndu.at(i);
outIndu.push_back(theind);
}
blanum++;
}
}
}
hasSkeleton = true;
vector<Texture> materialTextures;
materialTextures.push_back(Texture(this->device, Colors::UnhandledTextureColor, aiTextureType::aiTextureType_DIFFUSE));
transformed.push_back(Mesh(this->device, this->deviceContext, outVerts, outIndu, materialTextures, XMMatrixIdentity()));
return true;
}
bool Model::makeMount(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader) {
if (!Initialize("Data\\Objects\\MyStuff\\mountains1.fbx", device, deviceContext, cb_vs_vertexshader, false)) {
return false;
}
meshes.at(0).clearTextures();
meshes.at(0).ismore = true;
meshes.at(0).loadTexture(device, "Data/Textures2/variation.png");
meshes.at(0).loadTexture(device, "Data/Textures2/cliff-rockface1_albedo.png");
meshes.at(0).loadTexture(device, "Data/Textures/seamless_grass.jpg");
return true;
}
bool Model::makeWindow(ID3D11Device* device, ID3D11DeviceContext* deviceContext, ConstantBuffer<CB_VS_vertexshader>& cb_vs_vertexshader) {
if (!Initialize("Data\\Objects\\MyStuff\\window.fbx", device, deviceContext, cb_vs_vertexshader, false)) {
return false;
}
for (int i = 0; i < 3; i++) {
meshes.at(i).clearTextures();
meshes.at(i).ismore = true;
meshes.at(i).loadTexture(device, "Data/Textures2/liquid_pattern.jpg");
meshes.at(i).loadTexture(device, "Data/Textures2/liquid_alpha.png");
}
return true;
}
XMMATRIX Model::convertMat(aiMatrix4x4 m) {
return XMMATRIX
(m.a1, m.a2, m.a3, m.a4,
m.b1, m.b2, m.b3, m.b4,
m.c1, m.c2, m.c3, m.c4,
m.d1, m.d2, m.d3, m.d4);
}