/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace AZ { namespace SceneAPI { namespace SceneBuilder { const char* AssImpBitangentStreamImporter::m_defaultNodeName = "Bitangent"; AssImpBitangentStreamImporter::AssImpBitangentStreamImporter() { BindToCall(&AssImpBitangentStreamImporter::ImportBitangentStreams); } void AssImpBitangentStreamImporter::Reflect(ReflectContext* context) { SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class()->Version(3); // LYN-3250 } } Events::ProcessingResult AssImpBitangentStreamImporter::ImportBitangentStreams(AssImpSceneNodeAppendedContext& context) { AZ_TraceContext("Importer", m_defaultNodeName); if (!context.m_sourceNode.ContainsMesh()) { return Events::ProcessingResult::Ignored; } const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); const auto meshHasTangentsAndBitangents = [&scene](const unsigned int meshIndex) { return scene->mMeshes[meshIndex]->HasTangentsAndBitangents(); }; // If there are no bitangents on any meshes, there's nothing to import in this function. const bool anyMeshHasTangentsAndBitangents = AZStd::any_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents); if (!anyMeshHasTangentsAndBitangents) { return Events::ProcessingResult::Ignored; } // AssImp nodes with multiple meshes on them occur when AssImp split a mesh on material. // This logic recombines those meshes to minimize the changes needed to replace FBX SDK with AssImp, FBX SDK did not separate meshes, // and the engine has code to do this later. const bool allMeshesHaveTangentsAndBitangents = AZStd::all_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents); if (!allMeshesHaveTangentsAndBitangents) { AZ_Error( Utilities::ErrorWindow, false, "Node with name %s has meshes with and without bitangents. " "Placeholder incorrect bitangents will be generated to allow the data to process, " "but the source art needs to be fixed to correct this. Either apply bitangents to all meshes on this node, " "or remove all bitangents from all meshes on this node.", currentNode->mName.C_Str()); } const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene); AZStd::shared_ptr bitangentStream = AZStd::make_shared(); // AssImp only has one bitangentStream per mesh. bitangentStream->SetBitangentSetIndex(0); bitangentStream->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene); bitangentStream->ReserveContainerSpace(vertexCount); for (unsigned int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex) { const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]]; for (unsigned int v = 0; v < mesh->mNumVertices; ++v) { if (!mesh->HasTangentsAndBitangents()) { // This node has mixed meshes with and without bitangents. // An error was already thrown above. Output stub bitangents so // the mesh can still be output in some form, even if the data isn't correct. // The bitangent count needs to match the vertex count on the associated mesh node. bitangentStream->AppendBitangent(Vector3::CreateAxisY()); } else { const Vector3 bitangent( AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mBitangents[v])); bitangentStream->AppendBitangent(bitangent); } } } Containers::SceneGraph::NodeIndex newIndex = context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, m_defaultNodeName); Events::ProcessingResult bitangentResults; AssImpSceneAttributeDataPopulatedContext dataPopulated(context, bitangentStream, newIndex, m_defaultNodeName); bitangentResults = Events::Process(dataPopulated); if (bitangentResults != Events::ProcessingResult::Failure) { bitangentResults = AddAttributeDataNodeWithContexts(dataPopulated); } return bitangentResults; } } // namespace SceneBuilder } // namespace SceneAPI } // namespace AZ