/* * 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 #include namespace AZ::SceneAPI::Behaviors { // // ExportEventHandler // struct PrefabGroupBehavior::ExportEventHandler final : public AZ::SceneAPI::SceneCore::ExportingComponent { using PreExportEventContextFunction = AZStd::function; PreExportEventContextFunction m_preExportEventContextFunction; AZ::Prefab::PrefabGroupAssetHandler m_prefabGroupAssetHandler; ExportEventHandler() = delete; ExportEventHandler(PreExportEventContextFunction function) : m_preExportEventContextFunction(AZStd::move(function)) { BindToCall(&ExportEventHandler::PrepareForExport); AZ::SceneAPI::SceneCore::ExportingComponent::Activate(); } ~ExportEventHandler() { AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate(); } Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context) { return m_preExportEventContextFunction(context); } }; // // PrefabGroupBehavior // void PrefabGroupBehavior::Activate() { m_exportEventHandler = AZStd::make_shared([this](auto& context) { return this->OnPrepareForExport(context); }); } void PrefabGroupBehavior::Deactivate() { m_exportEventHandler.reset(); } AZStd::unique_ptr PrefabGroupBehavior::CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const { using namespace AzToolsFramework::Prefab; auto* prefabLoaderInterface = AZ::Interface::Get(); if (!prefabLoaderInterface) { AZ_Error("prefab", false, "Could not get PrefabLoaderInterface"); return {}; } // write to a UTF-8 string buffer auto prefabDomRef = prefabGroup->GetPrefabDomRef(); if (!prefabDomRef) { AZ_Error("prefab", false, "PrefabGroup(%s) missing PrefabDom", prefabGroup->GetName().c_str()); return {}; } const AzToolsFramework::Prefab::PrefabDom& prefabDom = prefabDomRef.value(); rapidjson::StringBuffer sb; rapidjson::Writer> writer(sb); if (prefabDom.Accept(writer) == false) { AZ_Error("prefab", false, "Could not write PrefabGroup(%s) to JSON", prefabGroup->GetName().c_str()); return {}; } // validate the PrefabDom will make a valid Prefab template instance auto templateId = prefabLoaderInterface->LoadTemplateFromString(sb.GetString(), prefabGroup->GetName().c_str()); if (templateId == InvalidTemplateId) { AZ_Error("prefab", false, "PrefabGroup(%s) Could not write load template", prefabGroup->GetName().c_str()); return {}; } auto* prefabSystemComponentInterface = AZ::Interface::Get(); if (!prefabSystemComponentInterface) { AZ_Error("prefab", false, "Could not get PrefabSystemComponentInterface"); return {}; } const rapidjson::Document& generatedInstanceDom = prefabSystemComponentInterface->FindTemplateDom(templateId); auto proceduralPrefab = AZStd::make_unique(rapidjson::kObjectType); proceduralPrefab->CopyFrom(generatedInstanceDom, proceduralPrefab->GetAllocator(), true); return proceduralPrefab; } bool PrefabGroupBehavior::WriteOutProductAsset( Events::PreExportEventContext& context, const SceneData::PrefabGroup* prefabGroup, const rapidjson::Document& doc) const { // Retrieve source asset info so we can get a string with the relative path to the asset bool assetInfoResult; Data::AssetInfo info; AZStd::string watchFolder; AzToolsFramework::AssetSystemRequestBus::BroadcastResult( assetInfoResult, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, context.GetScene().GetSourceFilename().c_str(), info, watchFolder); AZ::IO::FixedMaxPath assetPath(info.m_relativePath); assetPath.ReplaceFilename(prefabGroup->GetName().c_str()); AZStd::string filePath = AZ::SceneAPI::Utilities::FileUtilities::CreateOutputFileName( assetPath.c_str(), context.GetOutputDirectory(), AZ::Prefab::PrefabGroupAssetHandler::s_Extension); AZ::IO::FileIOStream fileStream(filePath.c_str(), AZ::IO::OpenMode::ModeWrite); if (fileStream.IsOpen() == false) { AZ_Error("prefab", false, "File path(%s) could not open for write", filePath.c_str()); return false; } // write to a UTF-8 string buffer rapidjson::StringBuffer sb; rapidjson::Writer> writer(sb); if (doc.Accept(writer) == false) { AZ_Error("prefab", false, "PrefabGroup(%s) Could not buffer JSON", prefabGroup->GetName().c_str()); return false; } const auto bytesWritten = fileStream.Write(sb.GetSize(), sb.GetString()); if (bytesWritten > 1) { AZ::u32 subId = AZ::Crc32(assetPath.c_str()); context.GetProductList().AddProduct( filePath, context.GetScene().GetSourceGuid(), azrtti_typeid(), {}, AZStd::make_optional(subId)); return true; } return false; } Events::ProcessingResult PrefabGroupBehavior::OnPrepareForExport(Events::PreExportEventContext& context) const { AZStd::vector prefabGroupCollection; const Containers::SceneManifest& manifest = context.GetScene().GetManifest(); for (size_t i = 0; i < manifest.GetEntryCount(); ++i) { const auto* group = azrtti_cast(manifest.GetValue(i).get()); if (group) { prefabGroupCollection.push_back(group); } } if (prefabGroupCollection.empty()) { return AZ::SceneAPI::Events::ProcessingResult::Ignored; } for (const auto* prefabGroup : prefabGroupCollection) { auto result = CreateProductAssetData(prefabGroup); if (!result) { return Events::ProcessingResult::Failure; } if (WriteOutProductAsset(context, prefabGroup, *result.get()) == false) { return Events::ProcessingResult::Failure; } } return Events::ProcessingResult::Success; } void PrefabGroupBehavior::Reflect(ReflectContext* context) { AZ::SceneAPI::SceneData::PrefabGroup::Reflect(context); Prefab::ProceduralPrefabAsset::Reflect(context); SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class()->Version(1); } } }