/* * 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 #include #include #include #include #include #include #include #include #include namespace AZ { namespace SceneAPI { namespace SceneBuilder { struct QueueNode { std::shared_ptr m_node; Containers::SceneGraph::NodeIndex m_parent; QueueNode() = delete; QueueNode(std::shared_ptr&& node, Containers::SceneGraph::NodeIndex parent) : m_node(std::move(node)) , m_parent(parent) { } }; SceneImporter::SceneImporter() : m_sceneSystem(new SceneSystem()) { m_sceneWrapper = AZStd::make_unique(); BindToCall(&SceneImporter::ImportProcessing); } void SceneImporter::Reflect(ReflectContext* context) { SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class()->Version(2); // SPEC-5776 } } SceneAPI::SceneImportSettings SceneImporter::GetSceneImportSettings(const AZStd::string& sourceAssetPath) const { // Start with a default set of import settings. SceneAPI::SceneImportSettings importSettings; // Try to read in any global settings from the settings registry. if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry) { settingsRegistry->GetObject(importSettings, AZ::SceneAPI::DataTypes::IImportGroup::SceneImportSettingsRegistryKey); } // Try reading in the scene manifest (.assetinfo file), which contains the import settings if they've been // changed from the defaults. Containers::Scene scene; Import::ManifestImportRequestHandler manifestHandler; manifestHandler.LoadAsset( scene, sourceAssetPath, Uuid::CreateNull(), Events::AssetImportRequest::RequestingApplication::AssetProcessor); // Search for the ImportGroup. If it's there, get the new import settings. If not, we'll just use the defaults. size_t count = scene.GetManifest().GetEntryCount(); for (size_t index = 0; index < count; index++) { if (auto* importGroup = azrtti_cast(scene.GetManifest().GetValue(index).get()); importGroup) { importSettings = importGroup->GetImportSettings(); break; } } return importSettings; } Events::ProcessingResult SceneImporter::ImportProcessing(Events::ImportEventContext& context) { SceneAPI::SceneImportSettings importSettings = GetSceneImportSettings(context.GetInputDirectory()); m_sceneWrapper->Clear(); if (!m_sceneWrapper->LoadSceneFromFile(context.GetInputDirectory().c_str(), importSettings)) { return Events::ProcessingResult::Failure; } m_sceneSystem->Set(m_sceneWrapper.get()); if (!azrtti_istypeof(m_sceneWrapper.get())) { return Events::ProcessingResult::Failure; } if (ConvertScene(context.GetScene())) { return Events::ProcessingResult::Success; } else { return Events::ProcessingResult::Failure; } } bool SceneImporter::ConvertScene(Containers::Scene& scene) const { std::shared_ptr sceneRoot = m_sceneWrapper->GetRootNode(); if (!sceneRoot) { return false; } const AssImpSDKWrapper::AssImpSceneWrapper* assImpSceneWrapper = azrtti_cast (m_sceneWrapper.get()); AZStd::pair upAxisAndSign = assImpSceneWrapper->GetUpVectorAndSign(); const aiAABB& aabb = assImpSceneWrapper->GetAABB(); aiVector3t dimension = aabb.mMax - aabb.mMin; Vector3 t{ dimension.x, dimension.y, dimension.z }; scene.SetSceneDimension(t); scene.SetSceneVertices(assImpSceneWrapper->GetVertices()); if (upAxisAndSign.second <= 0) { AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Negative scene orientation is not a currently supported orientation."); return false; } switch (upAxisAndSign.first) { case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::X: scene.SetOriginalSceneOrientation(Containers::Scene::SceneOrientation::XUp); break; case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Y: scene.SetOriginalSceneOrientation(Containers::Scene::SceneOrientation::YUp); break; case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Z: scene.SetOriginalSceneOrientation(Containers::Scene::SceneOrientation::ZUp); break; default: AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Unknown scene orientation, %d.", upAxisAndSign.first); AZ_Assert(false, "Unknown scene orientation, %d.", upAxisAndSign.first); break; } AZStd::queue nodes; nodes.emplace(AZStd::move(sceneRoot), scene.GetGraph().GetRoot()); RenamedNodesMap nodeNameMap; while (!nodes.empty()) { SceneBuilder::QueueNode& node = nodes.front(); AZ_Assert(node.m_node, "Empty asset importer node queued"); if (!nodeNameMap.RegisterNode(node.m_node, scene.GetGraph(), node.m_parent)) { AZ_TracePrintf(Utilities::ErrorWindow, "Failed to register asset importer node in name table."); // Skip this node since it could not be registered nodes.pop(); continue; } AZStd::string nodeName = nodeNameMap.GetNodeName(node.m_node); SanitizeNodeName(nodeName); AZ_TraceContext("SceneAPI Node Name", nodeName); Containers::SceneGraph::NodeIndex newNode = scene.GetGraph().AddChild(node.m_parent, nodeName.c_str()); AZ_Error(Utilities::ErrorWindow, newNode.IsValid(), "Failed to add Asset Importer node to scene graph"); if (!newNode.IsValid()) { continue; } AssImpNodeEncounteredContext sourceNodeEncountered(scene, newNode, *assImpSceneWrapper, *m_sceneSystem, nodeNameMap, *azrtti_cast(node.m_node.get())); Events::ProcessingResultCombiner nodeResult; nodeResult += Events::Process(sourceNodeEncountered); // If no importer created data, we still create an empty node that may eventually contain a transform if (sourceNodeEncountered.m_createdData.empty()) { AZ_Assert(nodeResult.GetResult() != Events::ProcessingResult::Success, "Importers returned success but no data was created"); AZStd::shared_ptr nullData(nullptr); sourceNodeEncountered.m_createdData.emplace_back(nullData); nodeResult += Events::ProcessingResult::Success; } AZ_Assert(nodeResult.GetResult() != Events::ProcessingResult::Ignored, "%i importer(s) created data, but did not return success", sourceNodeEncountered.m_createdData.size()); if (nodeResult.GetResult() == Events::ProcessingResult::Failure) { AZ_TracePrintf(Utilities::ErrorWindow, "One or more importers failed to create data."); } size_t offset = nodeName.length(); for (size_t i = 0; i < sourceNodeEncountered.m_createdData.size(); ++i) { bool saveCreatedDataToNewNode = (sourceNodeEncountered.m_createdData.size() == 1 || sourceNodeEncountered.m_createdData[i]->RTTI_IsTypeOf(DataTypes::IBoneData::TYPEINFO_Uuid())); if (!saveCreatedDataToNewNode) { nodeName += '_'; nodeName += AZStd::to_string(aznumeric_cast(i + 1)); } AssImpSceneDataPopulatedContext dataProcessed(sourceNodeEncountered, sourceNodeEncountered.m_createdData[i], nodeName.c_str()); if (saveCreatedDataToNewNode) { // Create single node since only one piece of graph data was created Events::ProcessingResult result = AddDataNodeWithContexts(dataProcessed); if (result != Events::ProcessingResult::Failure) { newNode = dataProcessed.m_currentGraphPosition; } } else { // Create an empty parent node and place all data under it. The remaining // tree will be built off of this as the logical parent Containers::SceneGraph::NodeIndex subNode = scene.GetGraph().AddChild(newNode, nodeName.c_str()); AZ_Assert(subNode.IsValid(), "Failed to create new scene sub node"); dataProcessed.m_currentGraphPosition = subNode; AddDataNodeWithContexts(dataProcessed); // Remove the temporary extension again. nodeName.erase(offset, nodeName.length() - offset); } } AZ_Assert(nodeResult.GetResult() == Events::ProcessingResult::Success, "No importers successfully added processed scene data."); AZ_Assert(newNode != node.m_parent, "Failed to update current graph position during data processing."); int childCount = node.m_node->GetChildCount(); for (int i = 0; i < childCount; ++i) { const std::shared_ptr nodeWrapper = node.m_node->GetChild(i); auto assImpNodeWrapper = azrtti_cast(nodeWrapper.get()); AZ_Assert(assImpNodeWrapper, "Child node is not the expected AssImpNodeWrapper type"); std::shared_ptr child = std::make_shared(assImpNodeWrapper->GetAssImpNode()); if (child) { nodes.emplace(AZStd::move(child), newNode); } } nodes.pop(); }; Events::ProcessingResult result = Events::Process(scene, *assImpSceneWrapper, *m_sceneSystem, nodeNameMap); if (result == Events::ProcessingResult::Failure) { return false; } return true; } void SceneImporter::SanitizeNodeName(AZStd::string& nodeName) const { // Replace % with something else so it is safe for use in printfs. AZStd::replace(nodeName.begin(), nodeName.end(), '%', '_'); } } // namespace SceneBuilder } // namespace SceneAPI } // namespace AZ