diff --git a/CMakeLists.txt b/CMakeLists.txt index 071bff5..34f2166 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,5 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) -add_subdirectory(src/dd) add_subdirectory(src/game) add_subdirectory(src/tests) diff --git a/include/Core/Engine.h b/include/Core/Engine.h index af5e0fa..5e958cd 100644 --- a/include/Core/Engine.h +++ b/include/Core/Engine.h @@ -178,4 +178,4 @@ private: double m_LastTime; }; -} \ No newline at end of file +} diff --git a/include/Transform/TransformSystem.h b/include/Transform/TransformSystem.h index 279de8f..433a6cf 100644 --- a/include/Transform/TransformSystem.h +++ b/include/Transform/TransformSystem.h @@ -41,36 +41,12 @@ public: { } //void Update(double dt) override; - void Initialize() override; - void UpdateEntity(double dt, EntityID entity, EntityID parent) override; - Components::Transform AbsoluteTransform(EntityID entity); glm::vec3 AbsolutePosition(EntityID entity); glm::quat AbsoluteOrientation(EntityID entity); glm::vec3 AbsoluteScale(EntityID entity); - // Events - EventRelay m_EMove; - bool OnMove(const Events::Move &event); - EventRelay m_ERotate; - bool OnRotate(const Events::Rotate &event); private: - struct MoveItems - { - EntityID Entity; - glm::vec3 GoalPosition; - float Speed; - }; - std::unordered_map m_MoveItems; - std::multimap m_QueuedMoveItems; - struct RotationItems - { - EntityID Entity; - glm::quat GoalRotation; - double Time; - }; - std::unordered_map m_RotationItems; - std::multimap m_QueuedRotationItems; }; } diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index 499d388..046a306 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -9,7 +9,7 @@ find_package(ZLIB REQUIRED) find_package(PNG REQUIRED) # GLM -set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include/dd) +set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include) include_directories( ${INCLUDE_PATH} ${OPENGL_INCLUDE_DIR} @@ -31,59 +31,55 @@ file(GLOB SOURCE_FILES_Core_Util source_group(Core FILES ${SOURCE_FILES_Core}) source_group(Core\\Util FILES ${SOURCE_FILES_Core_Util}) - file(GLOB SOURCE_FILES_Input "${INCLUDE_PATH}/Input/*.h" "Input/*.cpp" ) source_group(Input FILES ${SOURCE_FILES_Input}) -file(GLOB SOURCE_FILES_Particles - "${INCLUDE_PATH}/Particles/*.h" - "Particles/*.cpp" -) -source_group(Particles FILES ${SOURCE_FILES_Particles}) - file(GLOB SOURCE_FILES_Rendering "${INCLUDE_PATH}/Rendering/*.h" ) source_group(Rendering FILES ${SOURCE_FILES_Rendering}) -file(GLOB SOURCE_FILES_Timer - "${INCLUDE_PATH}/Timer/*.h" - "Timer/*.cpp" -) -source_group(Timer FILES ${SOURCE_FILES_Timer}) - file(GLOB SOURCE_FILES_Transform "${INCLUDE_PATH}/Transform/*.h" "Transform/*.cpp" ) source_group(Transform FILES ${SOURCE_FILES_Transform}) -file(GLOB SOURCE_FILES_Trigger - "${INCLUDE_PATH}/Trigger/*.h" - "Trigger/*.cpp" -) -source_group(Trigger FILES ${SOURCE_FILES_Trigger}) - set(SOURCE_FILES ${SOURCE_FILES_Core} ${SOURCE_FILES_Core_Util} ${SOURCE_FILES_Input} - ${SOURCE_FILES_Particles} ${SOURCE_FILES_Rendering} - ${SOURCE_FILES_Timer} ${SOURCE_FILES_Transform} - ${SOURCE_FILES_Trigger} ) -add_library(daydream ${SOURCE_FILES}) -target_link_libraries(daydream +if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") +endif() + +add_library(game ${SOURCE_FILES}) +target_link_libraries(game ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES} ${GLFW_LIBRARIES} ${Boost_LIBRARIES} ${assimp_LIBRARIES} ${PNG_LIBRARIES} -) \ No newline at end of file +) + +add_executable(breakout + main.cpp +) +target_link_libraries(breakout + game + ${OPENGL_LIBRARIES} + ${GLEW_LIBRARIES} + ${GLFW_LIBRARIES} + ${Boost_LIBRARIES} + ${assimp_LIBRARIES} + ${PNG_LIBRARIES} +) + diff --git a/src/game/Core/Model.cpp b/src/game/Core/Model.cpp new file mode 100755 index 0000000..0d7cea8 --- /dev/null +++ b/src/game/Core/Model.cpp @@ -0,0 +1,377 @@ +/* + 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 "Core/Model.h" + +dd::Model::Model(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; + } + + // Generate GL buffers + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, m_Vertices.size() * sizeof(Vertex), &m_Vertices[0], GL_STATIC_DRAW); + + glGenBuffers(1, &ElementBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Indices.size() * sizeof(unsigned int), &m_Indices[0], GL_STATIC_DRAW); + + glGenVertexArrays(1, &VAO); + glBindVertexArray(VAO); + GLERROR("GLEW: BufferFail4"); + + glBindBuffer(GL_ARRAY_BUFFER, buffer); + std::vector structSizes = { 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4 }; + int stride = 0; + for (int size : structSizes) + stride += size; + stride *= sizeof(GLfloat); + int offset = 0; + { + int element = 0; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * offset)); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + } + GLERROR("GLEW: BufferFail5"); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glEnableVertexAttribArray(4); + glEnableVertexAttribArray(5); + glEnableVertexAttribArray(6); + glEnableVertexAttribArray(7); + glEnableVertexAttribArray(8); + glEnableVertexAttribArray(9); + glEnableVertexAttribArray(10); + GLERROR("GLEW: BufferFail5"); + + //CreateBuffers(); +} + +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); + } +} diff --git a/src/game/Core/OBJ.cpp b/src/game/Core/OBJ.cpp new file mode 100644 index 0000000..fe6a818 --- /dev/null +++ b/src/game/Core/OBJ.cpp @@ -0,0 +1,427 @@ +/* + 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 "Core/OBJ.h" + +bool dd::OBJ::LoadFromFile(std::string filename) +{ + m_Path = boost::filesystem::path(filename); + + // http://paulbourke.net/dataformats/obj/ + std::ifstream file(m_Path.string()); + if (!file.is_open()) + { + LOG_ERROR("Failed to open .obj \"%s\": %s", m_Path.string().c_str(), strerror(errno)); + return false; + } + + //LOG_INFO("Parsing .obj \"%s\"", m_Path.string().c_str()); + + std::string line; + while (std::getline(file, line)) + { + if (line.length() == 0) + continue; + + std::stringstream ss(line); + + std::string prefix; + ss >> prefix; + + // Ignore comments + if (prefix == "#") + continue; + + // Material files + if (prefix == "mtllib") + { + std::string materialFilename; + ss >> materialFilename; + m_MaterialPath = m_Path.branch_path() / materialFilename; + ParseMaterial(); + continue; + } + + // Material statement + if (prefix == "usemtl") + { + std::string material; + ss >> material; + m_CurrentMaterial = &Materials[material]; + continue; + } + + // Vertices + if (prefix == "v") + { + float x, y, z; + ss >> x >> y >> z; + Vertices.push_back(std::make_tuple(x, y, z)); + continue; + } + + // Normals + if (prefix == "vn") + { + float x, y, z; + ss >> x >> y >> z; + Normals.push_back(std::make_tuple(x, y, z)); + } + + // Texture coordinates + if (prefix == "vt") + { + float u, v, w; + ss >> u >> v >> w; + TextureCoords.push_back(std::make_tuple(u, v, w)); + } + + // Face definitions + if (prefix == "f") + { + Face face; + face.Material = m_CurrentMaterial; + + std::string faceDefString; + while (ss >> faceDefString) + { + std::stringstream ss2(faceDefString); + FaceDefinition faceDef = { 0, 0, 0 }; + + ss2 >> faceDef.VertexIndex; + ss2.ignore(); // Ignore first delimiter + if (!ss2) + continue; + + if (ss2.peek() == '/') + { + ss2.ignore(); + ss2 >> faceDef.NormalIndex; + } + else + { + ss2 >> faceDef.TextureCoordIndex; + ss2.ignore(); + ss2 >> faceDef.NormalIndex; + } + face.Definitions.push_back(faceDef); + } + Faces.push_back(face); + } + } + + return true; +} + +void dd::OBJ::ParseMaterial() +{ + // http://paulbourke.net/dataformats/mtl/ + std::ifstream file(m_MaterialPath.string()); + if (!file.is_open()) + { + LOG_ERROR("Failed to open .mtl \"%s\"", m_MaterialPath.string().c_str()); + return; + } + + //LOG_INFO("Parsing .mtl \"%s\"", m_MaterialPath.string().c_str()); + + std::string currentMaterialName; + MaterialInfo* currentMaterial = nullptr; + + unsigned int currentLine = 0; + std::string line; + while (std::getline(file, line)) + { + currentLine++; + if (line.length() == 0) + continue; + + std::stringstream ss(line); + + std::string prefix; + ss >> prefix; + + // Create a new material definition + if (prefix == "newmtl") + { + MaterialInfo mat; + ss >> currentMaterialName; + //LOG_INFO("Parsing material %s", currentMaterialName.c_str()); + Materials[currentMaterialName] = mat; + currentMaterial = &Materials[currentMaterialName]; + continue; + } + + if (!currentMaterial) + continue; + + // Ambient color + if (prefix == "Ka") + { + float r, g, b; + ss >> r >> g >> b; + currentMaterial->AmbientColor = std::make_tuple(r, g, b); + continue; + } + // Diffuse color + if (prefix == "Kd") + { + float r, g, b; + ss >> r >> g >> b; + currentMaterial->DiffuseColor = std::make_tuple(r, g, b); + continue; + } + // Specular color + if (prefix == "Ks") + { + float r, g, b; + ss >> r >> g >> b; + currentMaterial->SpecularColor = std::make_tuple(r, g, b); + continue; + } + // Transmission filter + if (prefix == "Tf") + { + std::stringstream ss2; + ss2 << ss.str(); + + std::string command; + ss2 >> command; + if (command == "xyz") + { + // TODO: "The "Ks xyz" statement specifies the specular reflectivity using CIEXYZ values." + } + else if (command == "spectral") + { + // TODO: "The "Tf spectral" statement specifies the transmission filter using a spectral curve." + } + else + { + float r, g, b; + ss >> r; + // G and B are optional + if (!(ss >> g >> b)) + { + g = r; + b = r; + } + currentMaterial->TransmissionFilter = std::make_tuple(r, g, b); + } + continue; + } + // Optical density + if (prefix == "Ni") + { + ss >> currentMaterial->OpticalDensity; + continue; + } + // Alpha + if (prefix == "d" || prefix == "Tr") + { + ss >> currentMaterial->Alpha; + continue; + } + // Shininess + if (prefix == "Ns") + { + ss >> currentMaterial->Shininess; + continue; + } + // Illumination model + if (prefix == "illum") + { + int illum = 0; + ss >> illum; + currentMaterial->IlluminationModel = illum; + continue; + } + // Diffuse texture + if (prefix == "map_Kd") + { + MaterialInfo::ColorMap colorMap; + + std::string fileName; + std::string arg; + while (ss >> arg) + { + ParseColorMap(currentLine, ss, prefix, arg, colorMap); + } + + // HACK: Should we really have to specify the full path here? + colorMap.FileName = (m_MaterialPath.branch_path() / colorMap.FileName).string(); + currentMaterial->DiffuseTexture = colorMap; + continue; + } + // Specular map + if (prefix == "map_Ks") + { + MaterialInfo::ColorMap colorMap; + + std::string fileName; + std::string arg; + while (ss >> arg) + { + ParseColorMap(currentLine, ss, prefix, arg, colorMap); + } + + // HACK: Should we really have to specify the full path here? + colorMap.FileName = (m_MaterialPath.branch_path() / colorMap.FileName).string(); + currentMaterial->SpecularMap = colorMap; + continue; + } + // Normal map (bump map) + if (prefix == "bump" || prefix == "map_Bump" ) + { + MaterialInfo::BumpMap bumpMap; + + std::string fileName; + std::string arg; + while (ss >> arg) + { + ParseBumpMap(currentLine, ss, prefix, arg, bumpMap); + } + + // HACK: Should we really have to specify the full path here? + bumpMap.FileName = (m_MaterialPath.branch_path() / bumpMap.FileName).string(); + currentMaterial->NormalMap = bumpMap; + continue; + } + } +} + +void dd::OBJ::ParseTextureMap(unsigned int line, std::stringstream &ss, std::string prefix, std::string arg, MaterialInfo::TextureMap &textureMap) +{ + if (arg == "-blendu") + { + std::string val; + ss >> val; + if (val == "off") + textureMap.blendu = false; + else if (val == "on") + textureMap.blendu = true; + else + LOG_ERROR("Unrecognized MTL value \"%s\" to argument \"%s %s\" on line %i", val.c_str(), prefix.c_str(), arg.c_str(), line); + } + else if (arg == "-blendv") + { + std::string val; + ss >> val; + if (val == "off") + textureMap.blendv = false; + else if (val == "on") + textureMap.blendv = true; + else + LOG_ERROR("Unrecognized MTL value \"%s\" to argument \"%s %s\" on line %i", val.c_str(), prefix.c_str(), arg.c_str(), line); + } + else if (arg == "-clamp") + { + std::string val; + ss >> val; + if (val == "off") + textureMap.clamp = false; + else if (val == "on") + textureMap.clamp = true; + else + LOG_ERROR("Unrecognized MTL value \"%s\" to argument \"%s %s\" on line %i", val.c_str(), prefix.c_str(), arg.c_str(), line); + } + else if (arg == "-o") + { + float u, v, w; + if (ss >> u >> v >> w) + { + textureMap.o = std::make_tuple(u, v, w); + } + else + { + LOG_ERROR("Unrecognized MTL value to argument \"%s %s\" on line %i", prefix.c_str(), arg.c_str(), line); + } + } + else if (arg == "-s") + { + float u, v, w; + if (ss >> u >> v >> w) + { + textureMap.s = std::make_tuple(u, v, w); + } + else + { + LOG_ERROR("Unrecognized MTL value to argument \"%s %s\" on line %i", prefix.c_str(), arg.c_str(), line); + } + } + // Assume unrecognized options is part of the file name + else + { + if (!textureMap.FileName.empty()) + textureMap.FileName += " "; + textureMap.FileName += arg; + } +} + +void dd::OBJ::ParseColorMap(unsigned int line, std::stringstream &ss, std::string prefix, std::string arg, MaterialInfo::ColorMap &colorMap) +{ + if (arg == "-cc") + { + if (prefix != "map_Kd" || prefix != "map_Ks") + { + LOG_ERROR("Invalid MTL argument \"%s\" to option \"%s\" on line %i", arg.c_str(), prefix.c_str(), line); + return; + } + + std::string val; + if (ss >> val) + { + if (val == "off") + colorMap.cc = false; + else if (val == "on") + colorMap.cc = true; + else + LOG_ERROR("Unrecognized MTL value \"%s\" to argument \"%s %s\" on line %i", val.c_str(), prefix.c_str(), arg.c_str(), line); + } + else + { + LOG_ERROR("Unrecognized MTL value to argument \"%s %s\" on line %i", prefix.c_str(), arg.c_str(), line); + } + } + else + { + ParseTextureMap(line, ss, prefix, arg, colorMap); + } +} + +void dd::OBJ::ParseBumpMap(unsigned int line, std::stringstream &ss, std::string prefix, std::string arg, MaterialInfo::BumpMap &bumpMap) +{ + if (arg == "-bm") + { + if (prefix != "bump") + { + LOG_ERROR("Invalid MTL argument \"%s\" to option \"%s\" on line %i", arg.c_str(), prefix.c_str(), line); + return; + } + + float val; + if (ss >> val) + bumpMap.bm = val; + else + LOG_ERROR("Unrecognized MTL value to argument \"%s %s\" on line %i", prefix.c_str(), arg.c_str(), line); + } + else + { + ParseTextureMap(line, ss, prefix, arg, bumpMap); + } +} \ No newline at end of file diff --git a/src/game/Core/Skeleton.cpp b/src/game/Core/Skeleton.cpp new file mode 100644 index 0000000..2652ca0 --- /dev/null +++ b/src/game/Core/Skeleton.cpp @@ -0,0 +1,150 @@ +/* + 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 "Core/Skeleton.h" + +int dd::Skeleton::CreateBone(int ID, int parentID, std::string name, glm::mat4 offsetMatrix) +{ + if (m_BonesByName.find(name) != m_BonesByName.end()) { + return m_BonesByName.at(name)->ID; + } else { + Bone* bone; + + if (parentID == -1) { + bone = new Bone(ID, nullptr, name, offsetMatrix); + RootBone = bone; + } else { + Bone* parent = Bones[parentID]; + bone = new Bone(ID, parent, name, offsetMatrix); + parent->Children.push_back(bone); + } + + Bones[ID] = bone; + m_BonesByName[name] = bone; + return ID; + } +} + +dd::Skeleton::~Skeleton() +{ + for (auto &kv : Bones) { + delete kv.second; + } +} + +std::vector dd::Skeleton::GetFrameBones(std::string animationName, double time, bool noRootMotion /*= false*/) +{ + auto& animation = Animations.at(animationName); + + // HACK: Animation wrap-around + while (time < 0) + time += animation.Duration; + while (time > animation.Duration) + time -= animation.Duration; + + int currentKeyframeIndex = GetKeyframe(animation, time); + + Animation::Keyframe& currentFrame = animation.Keyframes[currentKeyframeIndex]; + Animation::Keyframe& nextFrame = animation.Keyframes[currentKeyframeIndex + 1]; + float alpha = (time - currentFrame.Time) / (nextFrame.Time - currentFrame.Time); + + //auto animationFrame = Animations[""].Keyframes[frame]; + std::map frameBones; + AccumulateBoneTransforms(noRootMotion, currentFrame, nextFrame, alpha, frameBones, RootBone, glm::mat4(1)); + + std::vector finalMatrices; + for (auto &kv : frameBones) { + finalMatrices.push_back(kv.second); + } + return finalMatrices; +} + +void dd::Skeleton::AccumulateBoneTransforms(bool noRootMotion, Animation::Keyframe ¤tFrame, Animation::Keyframe &nextFrame, float progress, std::map &boneMatrices, Bone* bone, glm::mat4 parentMatrix) +{ + glm::mat4 boneMatrix; + + if (currentFrame.BoneProperties.find(bone->ID) != currentFrame.BoneProperties.end() || nextFrame.BoneProperties.find(bone->ID) != nextFrame.BoneProperties.end()) { + Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties.at(bone->ID); + Animation::Keyframe::BoneProperty nextBoneProperty = nextFrame.BoneProperties.at(bone->ID); + + glm::vec3 positionInterp = currentBoneProperty.Position * (1.f - progress) + nextBoneProperty.Position * progress; + glm::quat rotationInterp = glm::slerp(currentBoneProperty.Rotation, nextBoneProperty.Rotation, progress); + glm::vec3 scaleInterp = currentBoneProperty.Scale * (1.f - progress) + nextBoneProperty.Scale * progress; + + // Flag for no root motion + if (bone == RootBone && noRootMotion) { + positionInterp.x = 0; + positionInterp.z = 0; + } + + boneMatrix = parentMatrix * (glm::translate(positionInterp) * glm::toMat4(rotationInterp) * glm::scale(scaleInterp)); + boneMatrices[bone->ID] = boneMatrix * bone->OffsetMatrix; + } else { + boneMatrix = parentMatrix * bone->Parent->OffsetMatrix; // * glm::inverse(bone->OffsetMatrix); + boneMatrices[bone->ID] = boneMatrix; // * bone->OffsetMatrix; + } + + for (auto &child : bone->Children) { + std::string name = child->Name; + AccumulateBoneTransforms(noRootMotion, currentFrame, nextFrame, progress, boneMatrices, child, boneMatrix); + } +} + +int dd::Skeleton::GetBoneID(std::string name) +{ + if (m_BonesByName.find(name) == m_BonesByName.end()) { + return -1; + } else { + return m_BonesByName.at(name)->ID; + } +} + +void dd::Skeleton::PrintSkeleton() +{ + PrintSkeleton(RootBone, 0); +} + +void dd::Skeleton::PrintSkeleton(Bone* bone, int depthCount) +{ + std::stringstream ss; + ss << std::string(depthCount, ' '); + ss << bone->ID << ": " << bone->Name; + std::cout << ss.str() << std::endl; + + depthCount++; + + for (auto &child : bone->Children) { + PrintSkeleton(child, depthCount); + } +} + +int dd::Skeleton::GetKeyframe(Animation& animation, double time) +{ + if (time < 0) + time = 0; + if (time > animation.Duration) + return animation.Keyframes.size() - 1; + + for (int keyframe = 0; keyframe < animation.Keyframes.size(); ++keyframe) { + if (animation.Keyframes[keyframe].Time > time) + return keyframe - 1; + } + + return 0; +} diff --git a/src/game/Transform/TransformSystem.cpp b/src/game/Transform/TransformSystem.cpp index bdb5595..c015df4 100644 --- a/src/game/Transform/TransformSystem.cpp +++ b/src/game/Transform/TransformSystem.cpp @@ -20,10 +20,6 @@ #include "Transform/TransformSystem.h" #include "Core/World.h" -void dd::Systems::TransformSystem::Initialize() -{ -} - glm::vec3 dd::Systems::TransformSystem::AbsolutePosition(EntityID entity) { glm::vec3 absPosition; diff --git a/src/game/main.cpp b/src/game/main.cpp new file mode 100755 index 0000000..686f7f4 --- /dev/null +++ b/src/game/main.cpp @@ -0,0 +1,12 @@ +#include "PrecompiledHeader.h" +#include "Core/Engine.h" + +int main(int argc, char* argv[]) +{ + dd::Engine engine(argc, argv); + LOG_INFO("------------ Engine initialized ------------"); + while (engine.Running()) + engine.Tick(); + + return 0; +} \ No newline at end of file diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 9dc55ef..918c679 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -3,7 +3,7 @@ project(tests) find_package(Boost REQUIRED COMPONENTS chrono thread unit_test_framework) include_directories( - ${CMAKE_SOURCE_DIR}/include/dd + ${CMAKE_SOURCE_DIR}/include ${Boost_INCLUDE_DIRS} ) @@ -19,6 +19,6 @@ endif() add_executable(tests ${SOURCE_FILES}) target_link_libraries(tests - daydream + game ${Boost_LIBRARIES} ) diff --git a/tools/deploy.bat b/tools/deploy.bat index 02fda57..dbbe6ed 100644 --- a/tools/deploy.bat +++ b/tools/deploy.bat @@ -8,7 +8,7 @@ MKLINK "%DeployLocation%\Models\" "assets\Models" /J MKLINK "%DeployLocation%\Textures\" "assets\Textures\" /J MKLINK "%DeployLocation%\Sounds\" "assets\Sounds\" /J :: Shaders -MKLINK "%DeployLocation%\Shaders\" "src\dd\Core\Shaders\" /J +MKLINK "%DeployLocation%\Shaders\" "src\game\Core\Shaders\" /J :: Platform specific binaries IF "%~1"=="" GOTO :EOF ECHO Deploying %1 binaries to %DeployLocation% @@ -17,4 +17,4 @@ COPY "deps\bin\%1\x64\*.dll" "%DeployLocation%" ::COPY "libs\assimp-3.1.1\LICENSE" "%ConfigPath%\Assimp License.txt" ::COPY "libs\glew-1.11.0\LICENSE.txt" "%ConfigPath%\GLEW License.txt" ::COPY "libs\glm-0.9.5.4\copying.txt" "%ConfigPath%\GLM License.txt" -::COPY "libs\assimp-3.1.1\LICENSE" "%ConfigPath%\Assimp License.txt" \ No newline at end of file +::COPY "libs\assimp-3.1.1\LICENSE" "%ConfigPath%\Assimp License.txt" diff --git a/tools/deploy.sh b/tools/deploy.sh old mode 100644 new mode 100755 index b2281d8..b40bfaf --- a/tools/deploy.sh +++ b/tools/deploy.sh @@ -9,9 +9,9 @@ ln -srf assets/Models ${DeployLocation} ln -srf assets/Textures ${DeployLocation} ln -srf assets/Sounds ${DeployLocation} # Shaders -ln -srf src/dd/Core/Shaders ${DeployLocation} +ln -srf src/game/Core/Shaders ${DeployLocation} # Platform specific binaries if [[ $# -eq 1 ]]; then echo "Deploying {$1} binaries to ${DeployLocation}" ln -srf deps/bin/$1/x64/*.dll ${DeployLocation} -fi \ No newline at end of file +fi