diff --git a/include/Rendering/OpenGL/Rendering/Model.h b/include/Rendering/OpenGL/Rendering/Model.h index 67d1ddb..89b6230 100644 --- a/include/Rendering/OpenGL/Rendering/Model.h +++ b/include/Rendering/OpenGL/Rendering/Model.h @@ -19,28 +19,12 @@ #ifndef Model_h__ #define Model_h__ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "Core/ResourceManager.h" -#include "Rendering/Texture.h" -#include "Rendering/Skeleton.h" +#include "Rendering/RawModel.h" namespace dd { -class Model : public Resource +class Model : public RawModel { friend class ResourceManager; @@ -50,54 +34,12 @@ private: public: ~Model(); - struct Vertex - { - glm::vec3 Position; - glm::vec3 Normal; - glm::vec3 Tangent; - glm::vec3 BiTangent; - glm::vec2 TextureCoords; - glm::vec4 DiffuseVertexColor; - glm::vec4 SpecularVertexColor; - glm::vec4 BoneIndices1; - glm::vec4 BoneIndices2; - glm::vec4 BoneWeights1; - glm::vec4 BoneWeights2; - }; - - struct MaterialGroup - { - float Shininess; - std::shared_ptr Texture; - std::shared_ptr NormalMap; - std::shared_ptr SpecularMap; - unsigned int StartIndex; - unsigned int EndIndex; - }; - GLuint VAO; GLuint ElementBuffer; - std::vector TextureGroups; - - std::vector> texture; - glm::mat4 GetMatrix(); - std::vector Vertices; - std::vector m_Vertices; - std::vector m_Indices; - Skeleton* m_Skeleton = nullptr; glm::mat4 m_Matrix; private: - std::vector BoneIndices; - std::vector BoneWeights; - std::vector Normals; - std::vector DiffuseVertexColor; - std::vector SpecularVertexColor; - std::vector TangentNormals; - std::vector BiTangentNormals; - std::vector TextureCoords; - GLuint VertexBuffer; GLuint DiffuseVertexColorBuffer; GLuint SpecularVertexColorBuffer; @@ -105,8 +47,6 @@ private: GLuint TangentNormalsBuffer; GLuint BiTangentNormalsBuffer; GLuint TextureCoordBuffer; - - void CreateSkeleton(std::vector> &boneInfo, std::map &boneNameMapping, aiNode* node, int parentID); }; } diff --git a/include/Rendering/RawModel.h b/include/Rendering/RawModel.h new file mode 100644 index 0000000..837be36 --- /dev/null +++ b/include/Rendering/RawModel.h @@ -0,0 +1,99 @@ +/* + This file is part of Daydream Engine. + Copyright 2014 Adam Byléhn, Tobias Dahl, Simon Holmberg, Viktor Ljung + + Daydream Engine is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Daydream Engine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Daydream Engine. If not, see . +*/ + +#ifndef RawModel_h__ +#define RawModel_h__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Core/ResourceManager.h" +#include "Rendering/Texture.h" +#include "Rendering/Skeleton.h" + +namespace dd +{ + +class RawModel : public Resource +{ + friend class ResourceManager; + +protected: + RawModel(std::string fileName); + +public: + ~RawModel(); + + struct Vertex + { + glm::vec3 Position; + glm::vec3 Normal; + glm::vec3 Tangent; + glm::vec3 BiTangent; + glm::vec2 TextureCoords; + glm::vec4 DiffuseVertexColor; + glm::vec4 SpecularVertexColor; + glm::vec4 BoneIndices1; + glm::vec4 BoneIndices2; + glm::vec4 BoneWeights1; + glm::vec4 BoneWeights2; + }; + + struct MaterialGroup + { + float Shininess; + std::shared_ptr Texture; + std::shared_ptr NormalMap; + std::shared_ptr SpecularMap; + unsigned int StartIndex; + unsigned int EndIndex; + }; + + std::vector TextureGroups; + + std::vector m_Vertices; + std::vector m_Indices; + Skeleton* m_Skeleton = nullptr; + +private: + std::vector BoneIndices; + std::vector BoneWeights; + std::vector Normals; + std::vector DiffuseVertexColor; + std::vector SpecularVertexColor; + std::vector TangentNormals; + std::vector BiTangentNormals; + std::vector TextureCoords; + + void CreateSkeleton(std::vector> &boneInfo, std::map &boneNameMapping, aiNode* node, int parentID); +}; + +} + +#endif // Model_h__ \ No newline at end of file diff --git a/src/game/Rendering/OpenGL/Model.cpp b/src/game/Rendering/OpenGL/Model.cpp index 3c682bb..23e07fc 100644 --- a/src/game/Rendering/OpenGL/Model.cpp +++ b/src/game/Rendering/OpenGL/Model.cpp @@ -20,15 +20,8 @@ #include "Rendering/Model.h" dd::Model::Model(std::string fileName) + : RawModel(fileName) { - Assimp::Importer importer; - const aiScene* scene = importer.ReadFile(fileName, aiProcess_CalcTangentSpace | aiProcess_Triangulate); - - if (scene == nullptr) { - LOG_ERROR("Failed to load model \"%s\"", fileName.c_str()); - LOG_ERROR("Assimp error: %s", importer.GetErrorString()); - return; - } auto m = scene->mRootNode->mTransformation; m_Matrix = glm::mat4( @@ -39,271 +32,6 @@ dd::Model::Model(std::string fileName) ); m_Matrix = glm::transpose(m_Matrix); - auto meshes = scene->mMeshes; - - // Pre-count vertices - int numVertices = 0; - int numIndices = 0; - for (int i = 0; i < scene->mNumMeshes; ++i) { - numVertices += meshes[i]->mNumVertices; - - // Faces - for (int j = 0; j < meshes[i]->mNumFaces; ++j) { - auto face = meshes[i]->mFaces[j]; - numIndices += face.mNumIndices; - } - } - LOG_DEBUG("Vertex count %i", numVertices); - LOG_DEBUG("Index count %i", numIndices); - - LOG_DEBUG("Model has %i embedded textures", scene->mNumTextures); - - std::vector> boneInfo; - std::map boneNameMapping; - - for (int i = 0; i < scene->mNumMeshes; ++i) { - auto mesh = meshes[i]; - auto material = scene->mMaterials[mesh->mMaterialIndex]; - unsigned int indexOffset = m_Vertices.size(); - - // Vertices, normals and texture coordinates - for (int vertexIndex = 0; vertexIndex < mesh->mNumVertices; ++vertexIndex) { - Vertex desc; - - // Position - auto position = mesh->mVertices[vertexIndex]; - desc.Position = glm::vec3(position.x, position.y, position.z); - - // Normal - auto normal = mesh->mNormals[vertexIndex]; - desc.Normal = glm::vec3(normal.x, normal.y, normal.z); - - //if (mesh->HasTangentsAndBitangents()) { - // // Tangent - // auto tangent = mesh->mTangents[vertexIndex]; - // desc.Tangent = glm::vec3(tangent.x, tangent.y, tangent.z); - - // // Bi-tangent - // auto bitangent = mesh->mBitangents[vertexIndex]; - // desc.BiTangent = glm::vec3(bitangent.x, bitangent.y, bitangent.z); - //} - - // UV - if (mesh->HasTextureCoords(0)) { - auto uv = mesh->mTextureCoords[0][vertexIndex]; - desc.TextureCoords = glm::vec2(uv.x, uv.y); - } - - // Material diffuse color - aiColor4D diffuse; - material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse); - desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, diffuse.a); - // Material specular color - aiColor4D specular; - material->Get(AI_MATKEY_COLOR_SPECULAR, specular); - desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, specular.a); - - m_Vertices.push_back(desc); - } - - // Faces - for (int j = 0; j < mesh->mNumFaces; ++j) { - auto face = mesh->mFaces[j]; - for (int k = 0; k < face.mNumIndices; ++k) { - unsigned int index = face.mIndices[k]; - m_Indices.push_back(indexOffset + index); - } - } - - // Calculate normal mapping tangents - for (int i = 0; i < m_Indices.size(); i += 3) { - Vertex& v0 = m_Vertices[m_Indices[i]]; - Vertex& v1 = m_Vertices[m_Indices[i + 1]]; - Vertex& v2 = m_Vertices[m_Indices[i + 2]]; - - glm::vec3 edge1 = v1.Position - v0.Position; - glm::vec3 edge2 = v2.Position - v0.Position; - - float deltaU1 = v1.TextureCoords.x - v0.TextureCoords.x; - float deltaV1 = v1.TextureCoords.y - v0.TextureCoords.y; - float deltaU2 = v2.TextureCoords.x - v0.TextureCoords.x; - float deltaV2 = v2.TextureCoords.y - v0.TextureCoords.y; - - float f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1); - - glm::vec3 tangent; - tangent.x = f * (deltaV2 * edge1.x - deltaV1 * edge2.x); - tangent.y = f * (deltaV2 * edge1.y - deltaV1 * edge2.y); - tangent.z = f * (deltaV2 * edge1.z - deltaV1 * edge2.z); - - v0.Tangent += tangent; - v1.Tangent += tangent; - v2.Tangent += tangent; - } - for (auto& vertex : m_Vertices) { - vertex.Tangent = glm::normalize(vertex.Tangent); - vertex.BiTangent = glm::normalize(glm::cross(vertex.Tangent, glm::normalize(vertex.Normal))); - } - - // Material info - MaterialGroup matGroup; - matGroup.StartIndex = indexOffset; - matGroup.EndIndex = m_Indices.size() - 1; - // Material shininess - material->Get(AI_MATKEY_SHININESS, matGroup.Shininess); - LOG_DEBUG("Shininess: %f", matGroup.Shininess); - // Diffuse texture - LOG_DEBUG("%i diffuse textures found", material->GetTextureCount(aiTextureType_DIFFUSE)); - if (material->GetTextureCount(aiTextureType_DIFFUSE)) { - aiString path; - aiTextureMapping mapping; - material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping); - std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); - LOG_DEBUG("Diffuse texture: %s", absolutePath.c_str()); - matGroup.Texture = std::shared_ptr(ResourceManager::Load(absolutePath)); - } - // Normal map - LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT)); - if (material->GetTextureCount(aiTextureType_HEIGHT)) { - aiString path; - aiTextureMapping mapping; - material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping); - std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); - LOG_DEBUG("Normal map: %s", absolutePath.c_str()); - matGroup.NormalMap = std::shared_ptr(ResourceManager::Load(absolutePath)); - } - // Specular map - LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR)); - if (material->GetTextureCount(aiTextureType_SPECULAR)) { - aiString path; - aiTextureMapping mapping; - material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping); - std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); - LOG_DEBUG("Specular map: %s", absolutePath.c_str()); - matGroup.SpecularMap = std::shared_ptr(ResourceManager::Load(absolutePath)); - } - TextureGroups.push_back(matGroup); - - // Bones - std::map>> vertexWeights; - for (int j = 0; j < mesh->mNumBones; ++j) { - auto bone = mesh->mBones[j]; - std::string boneName = bone->mName.C_Str(); - - auto mat = bone->mOffsetMatrix; - glm::mat4 glmMat(mat.a1, mat.b1, mat.c1, mat.d1, - mat.a2, mat.b2, mat.c2, mat.d2, - mat.a3, mat.b3, mat.c3, mat.d3, - mat.a4, mat.b4, mat.c4, mat.d4); - - int boneIndex; - if (boneNameMapping.find(boneName) != boneNameMapping.end()) { - boneIndex = boneNameMapping[boneName]; - } else { - boneIndex = boneInfo.size(); - boneInfo.push_back(std::make_tuple(boneName, glmMat)); - boneNameMapping[boneName] = boneIndex; - } - - for (int k = 0; k < bone->mNumWeights; ++k) { - auto weight = bone->mWeights[k]; - unsigned int offsetVertexId = weight.mVertexId + indexOffset; - vertexWeights[offsetVertexId].push_back(std::make_tuple(boneIndex, weight.mWeight)); - } - } - for (auto &pair : vertexWeights) { - auto weights = pair.second; - Vertex& desc = m_Vertices[pair.first]; - - const int maxWeights = 8; - if (weights.size() > maxWeights) { - LOG_WARNING("Vertex weights (%i) greater than max weights per vertex (%i)", weights.size(), maxWeights); - } - for (int weightIndex = 0; weightIndex < weights.size() && weightIndex < maxWeights && weightIndex < 4; ++weightIndex) { - std::tie(desc.BoneIndices1[weightIndex], desc.BoneWeights1[weightIndex]) = weights[weightIndex]; - } - for (int weightIndex = 4; weightIndex < weights.size() && weightIndex < maxWeights && weightIndex < 8; ++weightIndex) { - std::tie(desc.BoneIndices2[weightIndex - 4], desc.BoneWeights2[weightIndex - 4]) = weights[weightIndex]; - } - } - - //break; - } - - // Traverse the node tree and build a skeleton - if (!boneInfo.empty()) { - m_Skeleton = new Skeleton(); - CreateSkeleton(boneInfo, boneNameMapping, scene->mRootNode, -1); - int numBones = m_Skeleton->Bones.size(); - LOG_DEBUG("Bone count: %i", numBones); - if (numBones > 0) { - m_Skeleton->PrintSkeleton(); - } - } - - // Animations - LOG_DEBUG("Animation count: %i", scene->mNumAnimations); - for (int i = 0; i < scene->mNumAnimations; ++i) { - auto animation = scene->mAnimations[i]; - std::string animationName = animation->mName.C_Str(); - LOG_DEBUG("Animation: %s", animationName.c_str()); - LOG_DEBUG("Duration: %f", animation->mDuration); - LOG_DEBUG("Ticks per second: %f", animation->mTicksPerSecond); - - Skeleton::Animation skelAnim; - skelAnim.Name = animationName; - skelAnim.Duration = animation->mDuration / animation->mTicksPerSecond; - - std::map frameTimes; - std::map> frameBoneProperties; - // For each animation channel (bone) - for (int channelIndex = 0; channelIndex < animation->mNumChannels; ++channelIndex) { - auto channel = animation->mChannels[channelIndex]; - std::string boneName = channel->mNodeName.C_Str(); - int boneID = m_Skeleton->GetBoneID(boneName); - if (boneID == -1) { - LOG_ERROR("Animation referenced a bone that doesn't exist: %s", boneName.c_str()); - continue; - } - - // If you don't have the same amount of keyframes for every transformation type you're dumb. - if (channel->mNumPositionKeys != channel->mNumRotationKeys || channel->mNumPositionKeys != channel->mNumScalingKeys) { - LOG_ERROR("Hey, animation! You're dumb!", animationName.c_str()); - continue; - } - - for (int keyframe = 0; keyframe < channel->mNumPositionKeys; ++keyframe) { - auto posKey = channel->mPositionKeys[keyframe]; - auto rotKey = channel->mRotationKeys[keyframe]; - auto scaleKey = channel->mScalingKeys[keyframe]; - - frameTimes[keyframe] = posKey.mTime; - - auto &property = frameBoneProperties[keyframe][boneID]; - property.ID = keyframe; - property.Position = glm::vec3(posKey.mValue.x, posKey.mValue.y, posKey.mValue.z); - property.Rotation = glm::quat(rotKey.mValue.w, rotKey.mValue.x, rotKey.mValue.y, rotKey.mValue.z); - property.Scale = glm::vec3(scaleKey.mValue.x, scaleKey.mValue.y, scaleKey.mValue.z); - } - } - - // Create keyframes from bone properties - for (auto &kv : frameBoneProperties) { - int keyframe = kv.first; - Skeleton::Animation::Keyframe animationFrame; - animationFrame.Index = keyframe; - animationFrame.Time = frameTimes[keyframe] / animation->mTicksPerSecond; - for (auto &kv2 : kv.second) { - int boneID = kv2.first; - auto &property = kv2.second; - animationFrame.BoneProperties[boneID] = property; - } - skelAnim.Keyframes.push_back(animationFrame); - } - - m_Skeleton->Animations[animationName] = skelAnim; - } - // Generate GL buffers GLuint buffer; glGenBuffers(1, &buffer); @@ -359,28 +87,5 @@ dd::Model::Model(std::string fileName) dd::Model::~Model() { - if (m_Skeleton) { - delete m_Skeleton; - } -} -void dd::Model::CreateSkeleton(std::vector> &boneInfo, std::map &boneNameMapping, aiNode* node, int parentID) -{ - std::string nodeName = node->mName.C_Str(); - - // Find the bone by name in the bone info list - if (boneNameMapping.find(nodeName) == boneNameMapping.end()) { - LOG_DEBUG("Node \"%s\" was not a bone", nodeName.c_str()); - } else { - glm::mat4 offsetMatrix; - int ID = boneNameMapping[nodeName]; - std::tie(std::ignore, offsetMatrix) = boneInfo[ID]; - m_Skeleton->CreateBone(ID, parentID, nodeName, offsetMatrix); - parentID = ID; - } - - for (int childIndex = 0; childIndex < node->mNumChildren; ++childIndex) { - aiNode* child = node->mChildren[childIndex]; - CreateSkeleton(boneInfo, boneNameMapping, child, parentID); - } -} +} \ No newline at end of file diff --git a/src/game/Rendering/RawModel.cpp b/src/game/Rendering/RawModel.cpp new file mode 100644 index 0000000..f639f55 --- /dev/null +++ b/src/game/Rendering/RawModel.cpp @@ -0,0 +1,325 @@ +/* + This file is part of Daydream Engine. + Copyright 2014 Adam Byléhn, Tobias Dahl, Simon Holmberg, Viktor Ljung + + Daydream Engine is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Daydream Engine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Daydream Engine. If not, see . +*/ + +#include "PrecompiledHeader.h" +#include "Rendering/RawModel.h" + +dd::RawModel::RawModel(std::string fileName) +{ + Assimp::Importer importer; + const aiScene* scene = importer.ReadFile(fileName, aiProcess_CalcTangentSpace | aiProcess_Triangulate); + + if (scene == nullptr) { + LOG_ERROR("Failed to load model \"%s\"", fileName.c_str()); + LOG_ERROR("Assimp error: %s", importer.GetErrorString()); + return; + } + + auto meshes = scene->mMeshes; + + // Pre-count vertices + int numVertices = 0; + int numIndices = 0; + for (int i = 0; i < scene->mNumMeshes; ++i) { + numVertices += meshes[i]->mNumVertices; + + // Faces + for (int j = 0; j < meshes[i]->mNumFaces; ++j) { + auto face = meshes[i]->mFaces[j]; + numIndices += face.mNumIndices; + } + } + LOG_DEBUG("Vertex count %i", numVertices); + LOG_DEBUG("Index count %i", numIndices); + + LOG_DEBUG("Model has %i embedded textures", scene->mNumTextures); + + std::vector> boneInfo; + std::map boneNameMapping; + + for (int i = 0; i < scene->mNumMeshes; ++i) { + auto mesh = meshes[i]; + auto material = scene->mMaterials[mesh->mMaterialIndex]; + unsigned int indexOffset = m_Vertices.size(); + + // Vertices, normals and texture coordinates + for (int vertexIndex = 0; vertexIndex < mesh->mNumVertices; ++vertexIndex) { + Vertex desc; + + // Position + auto position = mesh->mVertices[vertexIndex]; + desc.Position = glm::vec3(position.x, position.y, position.z); + + // Normal + auto normal = mesh->mNormals[vertexIndex]; + desc.Normal = glm::vec3(normal.x, normal.y, normal.z); + + //if (mesh->HasTangentsAndBitangents()) { + // // Tangent + // auto tangent = mesh->mTangents[vertexIndex]; + // desc.Tangent = glm::vec3(tangent.x, tangent.y, tangent.z); + + // // Bi-tangent + // auto bitangent = mesh->mBitangents[vertexIndex]; + // desc.BiTangent = glm::vec3(bitangent.x, bitangent.y, bitangent.z); + //} + + // UV + if (mesh->HasTextureCoords(0)) { + auto uv = mesh->mTextureCoords[0][vertexIndex]; + desc.TextureCoords = glm::vec2(uv.x, uv.y); + } + + // Material diffuse color + aiColor4D diffuse; + material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse); + desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, diffuse.a); + // Material specular color + aiColor4D specular; + material->Get(AI_MATKEY_COLOR_SPECULAR, specular); + desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, specular.a); + + m_Vertices.push_back(desc); + } + + // Faces + for (int j = 0; j < mesh->mNumFaces; ++j) { + auto face = mesh->mFaces[j]; + for (int k = 0; k < face.mNumIndices; ++k) { + unsigned int index = face.mIndices[k]; + m_Indices.push_back(indexOffset + index); + } + } + + // Calculate normal mapping tangents + for (int i = 0; i < m_Indices.size(); i += 3) { + Vertex& v0 = m_Vertices[m_Indices[i]]; + Vertex& v1 = m_Vertices[m_Indices[i + 1]]; + Vertex& v2 = m_Vertices[m_Indices[i + 2]]; + + glm::vec3 edge1 = v1.Position - v0.Position; + glm::vec3 edge2 = v2.Position - v0.Position; + + float deltaU1 = v1.TextureCoords.x - v0.TextureCoords.x; + float deltaV1 = v1.TextureCoords.y - v0.TextureCoords.y; + float deltaU2 = v2.TextureCoords.x - v0.TextureCoords.x; + float deltaV2 = v2.TextureCoords.y - v0.TextureCoords.y; + + float f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1); + + glm::vec3 tangent; + tangent.x = f * (deltaV2 * edge1.x - deltaV1 * edge2.x); + tangent.y = f * (deltaV2 * edge1.y - deltaV1 * edge2.y); + tangent.z = f * (deltaV2 * edge1.z - deltaV1 * edge2.z); + + v0.Tangent += tangent; + v1.Tangent += tangent; + v2.Tangent += tangent; + } + for (auto& vertex : m_Vertices) { + vertex.Tangent = glm::normalize(vertex.Tangent); + vertex.BiTangent = glm::normalize(glm::cross(vertex.Tangent, glm::normalize(vertex.Normal))); + } + + // Material info + MaterialGroup matGroup; + matGroup.StartIndex = indexOffset; + matGroup.EndIndex = m_Indices.size() - 1; + // Material shininess + material->Get(AI_MATKEY_SHININESS, matGroup.Shininess); + LOG_DEBUG("Shininess: %f", matGroup.Shininess); + // Diffuse texture + LOG_DEBUG("%i diffuse textures found", material->GetTextureCount(aiTextureType_DIFFUSE)); + if (material->GetTextureCount(aiTextureType_DIFFUSE)) { + aiString path; + aiTextureMapping mapping; + material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping); + std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); + LOG_DEBUG("Diffuse texture: %s", absolutePath.c_str()); + matGroup.Texture = std::shared_ptr(ResourceManager::Load(absolutePath)); + } + // Normal map + LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT)); + if (material->GetTextureCount(aiTextureType_HEIGHT)) { + aiString path; + aiTextureMapping mapping; + material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping); + std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); + LOG_DEBUG("Normal map: %s", absolutePath.c_str()); + matGroup.NormalMap = std::shared_ptr(ResourceManager::Load(absolutePath)); + } + // Specular map + LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR)); + if (material->GetTextureCount(aiTextureType_SPECULAR)) { + aiString path; + aiTextureMapping mapping; + material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping); + std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); + LOG_DEBUG("Specular map: %s", absolutePath.c_str()); + matGroup.SpecularMap = std::shared_ptr(ResourceManager::Load(absolutePath)); + } + TextureGroups.push_back(matGroup); + + // Bones + std::map>> vertexWeights; + for (int j = 0; j < mesh->mNumBones; ++j) { + auto bone = mesh->mBones[j]; + std::string boneName = bone->mName.C_Str(); + + auto mat = bone->mOffsetMatrix; + glm::mat4 glmMat(mat.a1, mat.b1, mat.c1, mat.d1, + mat.a2, mat.b2, mat.c2, mat.d2, + mat.a3, mat.b3, mat.c3, mat.d3, + mat.a4, mat.b4, mat.c4, mat.d4); + + int boneIndex; + if (boneNameMapping.find(boneName) != boneNameMapping.end()) { + boneIndex = boneNameMapping[boneName]; + } else { + boneIndex = boneInfo.size(); + boneInfo.push_back(std::make_tuple(boneName, glmMat)); + boneNameMapping[boneName] = boneIndex; + } + + for (int k = 0; k < bone->mNumWeights; ++k) { + auto weight = bone->mWeights[k]; + unsigned int offsetVertexId = weight.mVertexId + indexOffset; + vertexWeights[offsetVertexId].push_back(std::make_tuple(boneIndex, weight.mWeight)); + } + } + for (auto &pair : vertexWeights) { + auto weights = pair.second; + Vertex& desc = m_Vertices[pair.first]; + + const int maxWeights = 8; + if (weights.size() > maxWeights) { + LOG_WARNING("Vertex weights (%i) greater than max weights per vertex (%i)", weights.size(), maxWeights); + } + for (int weightIndex = 0; weightIndex < weights.size() && weightIndex < maxWeights && weightIndex < 4; ++weightIndex) { + std::tie(desc.BoneIndices1[weightIndex], desc.BoneWeights1[weightIndex]) = weights[weightIndex]; + } + for (int weightIndex = 4; weightIndex < weights.size() && weightIndex < maxWeights && weightIndex < 8; ++weightIndex) { + std::tie(desc.BoneIndices2[weightIndex - 4], desc.BoneWeights2[weightIndex - 4]) = weights[weightIndex]; + } + } + + //break; + } + + // Traverse the node tree and build a skeleton + if (!boneInfo.empty()) { + m_Skeleton = new Skeleton(); + CreateSkeleton(boneInfo, boneNameMapping, scene->mRootNode, -1); + int numBones = m_Skeleton->Bones.size(); + LOG_DEBUG("Bone count: %i", numBones); + if (numBones > 0) { + m_Skeleton->PrintSkeleton(); + } + } + + // Animations + LOG_DEBUG("Animation count: %i", scene->mNumAnimations); + for (int i = 0; i < scene->mNumAnimations; ++i) { + auto animation = scene->mAnimations[i]; + std::string animationName = animation->mName.C_Str(); + LOG_DEBUG("Animation: %s", animationName.c_str()); + LOG_DEBUG("Duration: %f", animation->mDuration); + LOG_DEBUG("Ticks per second: %f", animation->mTicksPerSecond); + + Skeleton::Animation skelAnim; + skelAnim.Name = animationName; + skelAnim.Duration = animation->mDuration / animation->mTicksPerSecond; + + std::map frameTimes; + std::map> frameBoneProperties; + // For each animation channel (bone) + for (int channelIndex = 0; channelIndex < animation->mNumChannels; ++channelIndex) { + auto channel = animation->mChannels[channelIndex]; + std::string boneName = channel->mNodeName.C_Str(); + int boneID = m_Skeleton->GetBoneID(boneName); + if (boneID == -1) { + LOG_ERROR("Animation referenced a bone that doesn't exist: %s", boneName.c_str()); + continue; + } + + // If you don't have the same amount of keyframes for every transformation type you're dumb. + if (channel->mNumPositionKeys != channel->mNumRotationKeys || channel->mNumPositionKeys != channel->mNumScalingKeys) { + LOG_ERROR("Hey, animation! You're dumb!", animationName.c_str()); + continue; + } + + for (int keyframe = 0; keyframe < channel->mNumPositionKeys; ++keyframe) { + auto posKey = channel->mPositionKeys[keyframe]; + auto rotKey = channel->mRotationKeys[keyframe]; + auto scaleKey = channel->mScalingKeys[keyframe]; + + frameTimes[keyframe] = posKey.mTime; + + auto &property = frameBoneProperties[keyframe][boneID]; + property.ID = keyframe; + property.Position = glm::vec3(posKey.mValue.x, posKey.mValue.y, posKey.mValue.z); + property.Rotation = glm::quat(rotKey.mValue.w, rotKey.mValue.x, rotKey.mValue.y, rotKey.mValue.z); + property.Scale = glm::vec3(scaleKey.mValue.x, scaleKey.mValue.y, scaleKey.mValue.z); + } + } + + // Create keyframes from bone properties + for (auto &kv : frameBoneProperties) { + int keyframe = kv.first; + Skeleton::Animation::Keyframe animationFrame; + animationFrame.Index = keyframe; + animationFrame.Time = frameTimes[keyframe] / animation->mTicksPerSecond; + for (auto &kv2 : kv.second) { + int boneID = kv2.first; + auto &property = kv2.second; + animationFrame.BoneProperties[boneID] = property; + } + skelAnim.Keyframes.push_back(animationFrame); + } + + m_Skeleton->Animations[animationName] = skelAnim; + } +} + +dd::RawModel::~RawModel() +{ + if (m_Skeleton) { + delete m_Skeleton; + } +} + +void dd::RawModel::CreateSkeleton(std::vector> &boneInfo, std::map &boneNameMapping, aiNode* node, int parentID) +{ + std::string nodeName = node->mName.C_Str(); + + // Find the bone by name in the bone info list + if (boneNameMapping.find(nodeName) == boneNameMapping.end()) { + LOG_DEBUG("Node \"%s\" was not a bone", nodeName.c_str()); + } else { + glm::mat4 offsetMatrix; + int ID = boneNameMapping[nodeName]; + std::tie(std::ignore, offsetMatrix) = boneInfo[ID]; + m_Skeleton->CreateBone(ID, parentID, nodeName, offsetMatrix); + parentID = ID; + } + + for (int childIndex = 0; childIndex < node->mNumChildren; ++childIndex) { + aiNode* child = node->mChildren[childIndex]; + CreateSkeleton(boneInfo, boneNameMapping, child, parentID); + } +}