diff --git a/plugins/Assimp/Plugin.cpp b/plugins/Assimp/Plugin.cpp index 60d70124a..33450275f 100644 --- a/plugins/Assimp/Plugin.cpp +++ b/plugins/Assimp/Plugin.cpp @@ -52,7 +52,7 @@ void ProcessJoints(aiNode* node, Skeleton* skeleton, const std::unordered_setmName.data, node->mName.length); if (joints.count(jointName)) { - Joint* joint = skeleton->GetJoint(jointName); + Joint* joint = skeleton->GetJoint(std::string(jointName)); if (node->mParent) joint->SetParent(skeleton->GetJoint(node->mParent->mName.C_Str())); @@ -116,7 +116,7 @@ std::shared_ptr LoadAnimation(Stream& stream, const AnimationParams& aiPropertyStore* properties = aiCreatePropertyStore(); aiSetImportPropertyInteger(properties, AI_CONFIG_PP_LBW_MAX_WEIGHTS, 4); - aiSetImportPropertyInteger(properties, AI_CONFIG_PP_RVC_FLAGS, ~aiComponent_ANIMATIONS); + //aiSetImportPropertyInteger(properties, AI_CONFIG_PP_RVC_FLAGS, ~aiComponent_ANIMATIONS); const aiScene* scene = aiImportFileExWithProperties(userdata.originalFilePath, postProcess, &fileIO, properties); aiReleasePropertyStore(properties); @@ -261,14 +261,17 @@ std::shared_ptr LoadMesh(Stream& stream, const MeshParams& parameters) bool animatedMesh = false; if (parameters.animated) { - for (unsigned int meshIdx = 0; meshIdx < scene->mNumMeshes; ++meshIdx) + for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex) { - aiMesh* currentMesh = scene->mMeshes[meshIdx]; + aiMesh* currentMesh = scene->mMeshes[meshIndex]; if (currentMesh->HasBones()) // Inline functions can be safely called { animatedMesh = true; - for (unsigned int boneIdx = 0; boneIdx < currentMesh->mNumBones; ++boneIdx) - joints.insert(currentMesh->mBones[boneIdx]->mName.C_Str()); + for (unsigned int boneIndex = 0; boneIndex < currentMesh->mNumBones; ++boneIndex) + { + aiBone* bone = currentMesh->mBones[boneIndex]; + joints.insert(bone->mName.C_Str()); + } } } } @@ -290,13 +293,11 @@ std::shared_ptr LoadMesh(Stream& stream, const MeshParams& parameters) // aiMaterial index in scene => Material index and data in Mesh std::unordered_map> materials; - for (unsigned int meshIdx = 0; meshIdx < scene->mNumMeshes; ++meshIdx) + for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex) { - aiMesh* iMesh = scene->mMeshes[meshIdx]; + aiMesh* iMesh = scene->mMeshes[meshIndex]; if (iMesh->HasBones()) - { - // For now, process only skeletal meshes - } + continue; unsigned int indexCount = iMesh->mNumFaces * 3; unsigned int vertexCount = iMesh->mNumVertices; @@ -309,9 +310,9 @@ std::shared_ptr LoadMesh(Stream& stream, const MeshParams& parameters) IndexMapper indexMapper(*indexBuffer); IndexIterator index = indexMapper.begin(); - for (unsigned int faceIdx = 0; faceIdx < iMesh->mNumFaces; ++faceIdx) + for (unsigned int faceIndex = 0; faceIndex < iMesh->mNumFaces; ++faceIndex) { - aiFace& face = iMesh->mFaces[faceIdx]; + const aiFace& face = iMesh->mFaces[faceIndex]; if (face.mNumIndices != 3) NazaraWarning("Assimp plugin: This face is not a triangle!"); @@ -330,35 +331,85 @@ std::shared_ptr LoadMesh(Stream& stream, const MeshParams& parameters) VertexMapper vertexMapper(*vertexBuffer); - auto posPtr = vertexMapper.GetComponentPtr(VertexComponent::Position); - auto normalPtr = vertexMapper.GetComponentPtr(VertexComponent::Normal); - auto tangentPtr = vertexMapper.GetComponentPtr(VertexComponent::Tangent); + // Vertex positions + if (auto posPtr = vertexMapper.GetComponentPtr(VertexComponent::Position)) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiVector3D position = iMesh->mVertices[vertexIdx]; + *posPtr++ = parameters.matrix * Vector3f(position.x, position.y, position.z); + } + } + + // Vertex normals + if (auto normalPtr = vertexMapper.GetComponentPtr(VertexComponent::Normal)) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiVector3D normal = iMesh->mNormals[vertexIdx]; + *normalPtr++ = normalTangentMatrix.Transform({ normal.x, normal.y, normal.z }, 0.f); + } + } + + // Vertex tangents + bool generateTangents = false; + if (auto tangentPtr = vertexMapper.GetComponentPtr(VertexComponent::Tangent)) + { + if (iMesh->HasTangentsAndBitangents()) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiVector3D tangent = iMesh->mTangents[vertexIdx]; + *tangentPtr++ = normalTangentMatrix.Transform({ tangent.x, tangent.y, tangent.z }, 0.f); + } + } + else + generateTangents = true; + } + + // Vertex UVs + if (auto uvPtr = vertexMapper.GetComponentPtr(VertexComponent::TexCoord)) + { + if (iMesh->HasTextureCoords(0)) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiVector3D uv = iMesh->mTextureCoords[0][vertexIdx]; + *uvPtr++ = parameters.texCoordOffset + Vector2f(uv.x, uv.y) * parameters.texCoordScale; + } + } + else + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + *uvPtr++ = Vector2f::Zero(); + } + } + + // Vertex colors + if (auto colorPtr = vertexMapper.GetComponentPtr(VertexComponent::Color)) + { + if (iMesh->HasVertexColors(0)) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiColor4D color = iMesh->mColors[0][vertexIdx]; + *colorPtr++ = Color(UInt8(color.r * 255.f), UInt8(color.g * 255.f), UInt8(color.b * 255.f), UInt8(color.a * 255.f)); + } + } + else + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + *colorPtr++ = Color::White; + } + } + auto jointIndicesPtr = vertexMapper.GetComponentPtr(VertexComponent::JointIndices); auto jointWeightPtr = vertexMapper.GetComponentPtr(VertexComponent::JointWeights); - auto uvPtr = vertexMapper.GetComponentPtr(VertexComponent::TexCoord); - - for (std::size_t vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) - { - aiVector3D normal = iMesh->mNormals[vertexIndex]; - aiVector3D position = iMesh->mVertices[vertexIndex]; - aiVector3D tangent = iMesh->mTangents[vertexIndex]; - aiVector3D uv = iMesh->mTextureCoords[0][vertexIndex]; - - if (posPtr) - posPtr[vertexIndex] = parameters.matrix * Vector3f(position.x, position.y, position.z); - - if (normalPtr) - normalPtr[vertexIndex] = normalTangentMatrix.Transform({ normal.x, normal.y, normal.z }, 0.f); - - if (tangentPtr) - tangentPtr[vertexIndex] = normalTangentMatrix.Transform({ tangent.x, tangent.y, tangent.z }, 0.f); - - if (uvPtr) - uvPtr[vertexIndex] = parameters.texCoordOffset + Vector2f(uv.x, uv.y) * parameters.texCoordScale; - } if (jointIndicesPtr || jointWeightPtr) { + std::vector weightIndices(iMesh->mNumVertices, 0); + for (unsigned int boneIndex = 0; boneIndex < iMesh->mNumBones; ++boneIndex) { aiBone* bone = iMesh->mBones[boneIndex]; @@ -366,11 +417,13 @@ std::shared_ptr LoadMesh(Stream& stream, const MeshParams& parameters) { aiVertexWeight& vertexWeight = bone->mWeights[weightIndex]; + std::size_t vertexWeightIndex = weightIndices[vertexWeight.mVertexId]++; + if (jointIndicesPtr) - jointIndicesPtr[vertexWeight.mVertexId][weightIndex] = boneIndex; + jointIndicesPtr[vertexWeight.mVertexId][vertexWeightIndex] = boneIndex; if (jointWeightPtr) - jointWeightPtr[vertexWeight.mVertexId][weightIndex] = vertexWeight.mWeight; + jointWeightPtr[vertexWeight.mVertexId][vertexWeightIndex] = vertexWeight.mWeight; } } } @@ -482,13 +535,28 @@ std::shared_ptr LoadMesh(Stream& stream, const MeshParams& parameters) // aiMaterial index in scene => Material index and data in Mesh std::unordered_map> materials; - for (unsigned int meshIdx = 0; meshIdx < scene->mNumMeshes; ++meshIdx) + for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex) { - aiMesh* iMesh = scene->mMeshes[meshIdx]; - if (!iMesh->HasBones()) // Don't process skeletal meshs + aiMesh* iMesh = scene->mMeshes[meshIndex]; + if (iMesh->HasBones()) + continue; // Don't process skeletal meshes + + unsigned int indexCount = iMesh->mNumFaces * 3; + unsigned int vertexCount = iMesh->mNumVertices; + + // Index buffer + bool largeIndices = (vertexCount > std::numeric_limits::max()); + + std::shared_ptr indexBuffer = std::make_shared((largeIndices) ? IndexType::U32 : IndexType::U16, indexCount, parameters.indexBufferFlags, parameters.bufferFactory); + + IndexMapper indexMapper(*indexBuffer); + IndexIterator index = indexMapper.begin(); + + for (unsigned int faceIndex = 0; faceIndex < iMesh->mNumFaces; ++faceIndex) { - unsigned int indexCount = iMesh->mNumFaces * 3; - unsigned int vertexCount = iMesh->mNumVertices; + const aiFace& face = iMesh->mFaces[faceIndex]; + if (face.mNumIndices != 3) + NazaraWarning("Assimp plugin: This face is not a triangle!"); // Index buffer bool largeIndices = (vertexCount > std::numeric_limits::max()); @@ -680,6 +748,178 @@ std::shared_ptr LoadMesh(Stream& stream, const MeshParams& parameters) mesh->AddSubMesh(subMesh); } + indexMapper.Unmap(); + + // Vertex buffer + + // Make sure the normal/tangent matrix won't rescale our vectors + Nz::Matrix4f normalTangentMatrix = parameters.matrix; + if (normalTangentMatrix.HasScale()) + normalTangentMatrix.ApplyScale(1.f / normalTangentMatrix.GetScale()); + + std::shared_ptr vertexBuffer = std::make_shared(parameters.vertexDeclaration, vertexCount, parameters.vertexBufferFlags, parameters.bufferFactory); + + VertexMapper vertexMapper(*vertexBuffer); + + // Vertex positions + if (auto posPtr = vertexMapper.GetComponentPtr(VertexComponent::Position)) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiVector3D position = iMesh->mVertices[vertexIdx]; + *posPtr++ = parameters.matrix * Vector3f(position.x, position.y, position.z); + } + } + + // Vertex normals + if (auto normalPtr = vertexMapper.GetComponentPtr(VertexComponent::Normal)) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiVector3D normal = iMesh->mNormals[vertexIdx]; + *normalPtr++ = normalTangentMatrix.Transform({normal.x, normal.y, normal.z}, 0.f); + } + } + + // Vertex tangents + bool generateTangents = false; + if (auto tangentPtr = vertexMapper.GetComponentPtr(VertexComponent::Tangent)) + { + if (iMesh->HasTangentsAndBitangents()) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiVector3D tangent = iMesh->mTangents[vertexIdx]; + *tangentPtr++ = normalTangentMatrix.Transform({tangent.x, tangent.y, tangent.z}, 0.f); + } + } + else + generateTangents = true; + } + + // Vertex UVs + if (auto uvPtr = vertexMapper.GetComponentPtr(VertexComponent::TexCoord)) + { + if (iMesh->HasTextureCoords(0)) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiVector3D uv = iMesh->mTextureCoords[0][vertexIdx]; + *uvPtr++ = parameters.texCoordOffset + Vector2f(uv.x, uv.y) * parameters.texCoordScale; + } + } + else + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + *uvPtr++ = Vector2f::Zero(); + } + } + + // Vertex colors + if (auto colorPtr = vertexMapper.GetComponentPtr(VertexComponent::Color)) + { + if (iMesh->HasVertexColors(0)) + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + { + aiColor4D color = iMesh->mColors[0][vertexIdx]; + *colorPtr++ = Color(UInt8(color.r * 255.f), UInt8(color.g * 255.f), UInt8(color.b * 255.f), UInt8(color.a * 255.f)); + } + } + else + { + for (unsigned int vertexIdx = 0; vertexIdx < vertexCount; ++vertexIdx) + *colorPtr++ = Color::White; + } + } + + vertexMapper.Unmap(); + + // Submesh + std::shared_ptr subMesh = std::make_shared(vertexBuffer, indexBuffer); + subMesh->GenerateAABB(); + subMesh->SetMaterialIndex(iMesh->mMaterialIndex); + + if (generateTangents) + subMesh->GenerateTangents(); + + auto matIt = materials.find(iMesh->mMaterialIndex); + if (matIt == materials.end()) + { + ParameterList matData; + aiMaterial* aiMat = scene->mMaterials[iMesh->mMaterialIndex]; + + auto ConvertColor = [&] (const char* aiKey, unsigned int aiType, unsigned int aiIndex, const char* colorKey) + { + aiColor4D color; + if (aiGetMaterialColor(aiMat, aiKey, aiType, aiIndex, &color) == aiReturn_SUCCESS) + { + matData.SetParameter(colorKey, Color(static_cast(color.r * 255), static_cast(color.g * 255), static_cast(color.b * 255), static_cast(color.a * 255))); + } + }; + + auto ConvertTexture = [&] (aiTextureType aiType, const char* textureKey, const char* wrapKey = nullptr) + { + aiString path; + aiTextureMapMode mapMode[3]; + if (aiGetMaterialTexture(aiMat, aiType, 0, &path, nullptr, nullptr, nullptr, nullptr, &mapMode[0], nullptr) == aiReturn_SUCCESS) + { + matData.SetParameter(textureKey, (stream.GetDirectory() / std::string_view(path.data, path.length)).generic_u8string()); + + if (wrapKey) + { + SamplerWrap wrap = SamplerWrap::Clamp; + switch (mapMode[0]) + { + case aiTextureMapMode_Clamp: + case aiTextureMapMode_Decal: + wrap = SamplerWrap::Clamp; + break; + + case aiTextureMapMode_Mirror: + wrap = SamplerWrap::MirroredRepeat; + break; + + case aiTextureMapMode_Wrap: + wrap = SamplerWrap::Repeat; + break; + + default: + NazaraWarning("Assimp texture map mode 0x" + NumberToString(mapMode[0], 16) + " not handled"); + break; + } + + matData.SetParameter(wrapKey, static_cast(wrap)); + } + } + }; + + ConvertColor(AI_MATKEY_COLOR_AMBIENT, MaterialData::AmbientColor); + ConvertColor(AI_MATKEY_COLOR_DIFFUSE, MaterialData::DiffuseColor); + ConvertColor(AI_MATKEY_COLOR_SPECULAR, MaterialData::SpecularColor); + + ConvertTexture(aiTextureType_DIFFUSE, MaterialData::DiffuseTexturePath, MaterialData::DiffuseWrap); + ConvertTexture(aiTextureType_EMISSIVE, MaterialData::EmissiveTexturePath); + ConvertTexture(aiTextureType_HEIGHT, MaterialData::HeightTexturePath); + ConvertTexture(aiTextureType_NORMALS, MaterialData::NormalTexturePath); + ConvertTexture(aiTextureType_OPACITY, MaterialData::AlphaTexturePath); + ConvertTexture(aiTextureType_SPECULAR, MaterialData::SpecularTexturePath, MaterialData::SpecularWrap); + + aiString name; + if (aiGetMaterialString(aiMat, AI_MATKEY_NAME, &name) == aiReturn_SUCCESS) + matData.SetParameter(MaterialData::Name, std::string(name.data, name.length)); + + int iValue; + if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue) == aiReturn_SUCCESS) + matData.SetParameter(MaterialData::FaceCulling, !iValue); + + matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(UInt32(materials.size()), std::move(matData)))).first; + } + + subMesh->SetMaterialIndex(matIt->first); + + mesh->AddSubMesh(subMesh); + mesh->SetMaterialCount(std::max(UInt32(materials.size()), 1)); for (const auto& pair : materials)