You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp

642 lines
27 KiB
C++

/*
* 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 <PrefabGroup/PrefabGroupBehavior.h>
#include <PrefabGroup/PrefabGroup.h>
#include <PrefabGroup/ProceduralAssetHandler.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/error/en.h>
#include <AzCore/JSON/error/error.h>
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/JSON/stringbuffer.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
#include <AzToolsFramework/Entity/EntityUtilityComponent.h>
#include <AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemScriptingBus.h>
#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphUpwardsIterator.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/ITransform.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneData/Groups/MeshGroup.h>
#include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>
#include <SceneAPI/SceneData/Rules/LodRule.h>
namespace AZStd
{
template<> struct hash<AZ::SceneAPI::Containers::SceneGraph::NodeIndex>
{
inline size_t operator()(const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex) const
{
size_t hashValue{ 0 };
hash_combine(hashValue, nodeIndex.AsNumber());
return hashValue;
}
};
}
namespace AZ::SceneAPI::Behaviors
{
//
// ExportEventHandler
//
static constexpr const char s_PrefabGroupBehaviorCreateDefaultKey[] = "/O3DE/Preferences/Prefabs/CreateDefaults";
struct PrefabGroupBehavior::ExportEventHandler final
: public AZ::SceneAPI::SceneCore::ExportingComponent
, public Events::AssetImportRequestBus::Handler
{
using PreExportEventContextFunction = AZStd::function<Events::ProcessingResult(Events::PreExportEventContext&)>;
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();
Events::AssetImportRequestBus::Handler::BusConnect();
}
~ExportEventHandler()
{
Events::AssetImportRequestBus::Handler::BusDisconnect();
AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
}
Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context)
{
return m_preExportEventContextFunction(context);
}
using MeshTransformPair = AZStd::pair<Containers::SceneGraph::NodeIndex, Containers::SceneGraph::NodeIndex>;
using MeshTransformEntry = AZStd::pair<Containers::SceneGraph::NodeIndex, MeshTransformPair>;
using MeshTransformMap = AZStd::unordered_map<Containers::SceneGraph::NodeIndex, MeshTransformPair>;
using MeshIndexContainer = AZStd::unordered_set<Containers::SceneGraph::NodeIndex>;
using ManifestUpdates = AZStd::vector<AZStd::shared_ptr<DataTypes::IManifestObject>>;
using NodeEntityMap = AZStd::unordered_map<Containers::SceneGraph::NodeIndex, AZ::EntityId>;
using EntityIdList = AZStd::vector<AZ::EntityId>;
MeshTransformMap CalculateMeshTransformMap(const Containers::Scene& scene)
{
auto graph = scene.GetGraph();
const auto view = Containers::Views::MakeSceneGraphDownwardsView<Containers::Views::BreadthFirst>(
graph,
graph.GetRoot(),
graph.GetContentStorage().cbegin(),
true);
if (view.empty())
{
return {};
}
MeshIndexContainer meshIndexContainer;
MeshTransformMap meshTransformMap;
for (auto it = view.begin(); it != view.end(); ++it)
{
Containers::SceneGraph::NodeIndex currentIndex = graph.ConvertToNodeIndex(it.GetHierarchyIterator());
AZStd::string currentNodeName = graph.GetNodeName(currentIndex).GetPath();
const auto currentContent = graph.GetNodeContent(currentIndex);
if (currentContent)
{
if (azrtti_istypeof<AZ::SceneAPI::DataTypes::ITransform>(currentContent.get()))
{
const auto parentIndex = graph.GetNodeParent(currentIndex);
if (parentIndex.IsValid() == false)
{
continue;
}
const auto parentContent = graph.GetNodeContent(parentIndex);
if (parentContent && azrtti_istypeof<AZ::SceneAPI::DataTypes::IMeshData>(parentContent.get()))
{
// map the node parent to the ITransform
meshIndexContainer.erase(parentIndex);
MeshTransformPair pair{ parentIndex, currentIndex };
meshTransformMap.emplace(MeshTransformEntry{ graph.GetNodeParent(parentIndex), AZStd::move(pair) });
}
}
else if (azrtti_istypeof<AZ::SceneAPI::DataTypes::IMeshData>(currentContent.get()))
{
meshIndexContainer.insert(currentIndex);
}
}
}
// all mesh data nodes left in the meshIndexContainer do not have a matching TransformData node
// since the nodes have an identity transform, so map the MeshData index with an Invalid mesh index to
// indicate the transform should not be set to a default value
for( const auto meshIndex : meshIndexContainer)
{
MeshTransformPair pair{ meshIndex, Containers::SceneGraph::NodeIndex{} };
meshTransformMap.emplace(MeshTransformEntry{ graph.GetNodeParent(meshIndex), AZStd::move(pair) });
}
return meshTransformMap;
}
NodeEntityMap CreateMeshGroups(
ManifestUpdates& manifestUpdates,
const MeshTransformMap& meshTransformMap,
const Containers::Scene& scene,
const AZStd::string& relativeSourcePath)
{
NodeEntityMap nodeEntityMap;
const auto& graph = scene.GetGraph();
for (const auto& entry : meshTransformMap)
{
const auto thisNodeIndex = entry.first;
const auto meshNodeIndex = entry.second.first;
const auto meshNodeName = graph.GetNodeName(meshNodeIndex);
AZStd::string meshNodePath{ meshNodeName.GetPath() };
AZStd::string meshGroupName = "default_";
meshGroupName += scene.GetName();
meshGroupName += meshNodePath;
AZ::StringFunc::Replace(meshGroupName, ".", "_");
auto meshGroup = AZStd::make_shared<AZ::SceneAPI::SceneData::MeshGroup>();
meshGroup->SetName(meshGroupName);
meshGroup->GetSceneNodeSelectionList().AddSelectedNode(AZStd::move(meshNodePath));
for (const auto& meshGoupNamePair : meshTransformMap)
{
if (meshGoupNamePair.first != thisNodeIndex)
{
const auto nodeName = graph.GetNodeName(meshGoupNamePair.second.first);
meshGroup->GetSceneNodeSelectionList().RemoveSelectedNode(nodeName.GetPath());
}
}
meshGroup->OverrideId(DataTypes::Utilities::CreateStableUuid(
scene,
azrtti_typeid<AZ::SceneAPI::SceneData::MeshGroup>(),
meshNodeName.GetPath()));
// this clears out the mesh coordinates each mesh group will be rotated and translated
// using the attached scene graph node
auto coordinateSystemRule = AZStd::make_shared<AZ::SceneAPI::SceneData::CoordinateSystemRule>();
coordinateSystemRule->SetUseAdvancedData(true);
coordinateSystemRule->SetRotation(AZ::Quaternion::CreateIdentity());
coordinateSystemRule->SetTranslation(AZ::Vector3::CreateZero());
coordinateSystemRule->SetScale(1.0f);
meshGroup->GetRuleContainer().AddRule(coordinateSystemRule);
// create an empty LOD rule in order to skip the LOD buffer creation
meshGroup->GetRuleContainer().AddRule(AZStd::make_shared<AZ::SceneAPI::SceneData::LodRule>());
manifestUpdates.emplace_back(meshGroup);
// create an entity for each MeshGroup
AZ::EntityId entityId;
AzToolsFramework::EntityUtilityBus::BroadcastResult(
entityId,
&AzToolsFramework::EntityUtilityBus::Events::CreateEditorReadyEntity,
meshNodeName.GetName());
if (entityId.IsValid() == false)
{
return {};
}
// Since the mesh component lives in a gem, then create it by name
AzFramework::BehaviorComponentId editorMeshComponent;
AzToolsFramework::EntityUtilityBus::BroadcastResult(
editorMeshComponent,
&AzToolsFramework::EntityUtilityBus::Events::GetOrAddComponentByTypeName,
entityId,
"{DCE68F6E-2E16-4CB4-A834-B6C2F900A7E9} AZ::Render::EditorMeshComponent");
if (editorMeshComponent.IsValid() == false)
{
AZ_Warning("prefab", false, "Could not add the EditorMeshComponent component; project needs Atom enabled.");
return {};
}
// assign mesh asset id hint using JSON
AZStd::string modelAssetPath;
modelAssetPath = relativeSourcePath;
AZ::StringFunc::Path::ReplaceFullName(modelAssetPath, meshGroupName.c_str());
AZ::StringFunc::Replace(modelAssetPath, "\\", "/"); // asset paths use forward slashes
auto meshAssetJson = AZStd::string::format(
R"JSON(
{"Controller": {"Configuration": {"ModelAsset": { "assetHint": "%s.azmodel"}}}}
)JSON", modelAssetPath.c_str());
bool result = false;
AzToolsFramework::EntityUtilityBus::BroadcastResult(
result,
&AzToolsFramework::EntityUtilityBus::Events::UpdateComponentForEntity,
entityId,
editorMeshComponent,
meshAssetJson);
if (result == false)
{
AZ_Error("prefab", false, "UpdateComponentForEntity failed for EditorMeshComponent component");
return {};
}
nodeEntityMap.insert({ thisNodeIndex, entityId });
}
return nodeEntityMap;
}
EntityIdList FixUpEntityParenting(
const NodeEntityMap& nodeEntityMap,
const Containers::SceneGraph& graph,
const MeshTransformMap& meshTransformMap)
{
EntityIdList entities;
entities.reserve(nodeEntityMap.size());
for (const auto& nodeEntity : nodeEntityMap)
{
entities.emplace_back(nodeEntity.second);
// find matching parent EntityId (if any)
AZ::EntityId parentEntityId;
const auto thisNodeIndex = nodeEntity.first;
auto parentNodeIndex = graph.GetNodeParent(thisNodeIndex);
while (parentNodeIndex.IsValid())
{
auto parentNodeIterator = meshTransformMap.find(parentNodeIndex);
if (meshTransformMap.end() != parentNodeIterator)
{
auto parentEntiyIterator = nodeEntityMap.find(parentNodeIterator->first);
if (nodeEntityMap.end() != parentEntiyIterator)
{
parentEntityId = parentEntiyIterator->second;
break;
}
}
else if (graph.HasNodeParent(parentNodeIndex))
{
parentNodeIndex = graph.GetNodeParent(parentNodeIndex);
}
else
{
parentNodeIndex = {};
}
}
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(nodeEntity.second);
auto* entityTransform = entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
if (!entityTransform)
{
return {};
}
// parent entities
if (parentEntityId.IsValid())
{
entityTransform->SetParent(parentEntityId);
}
auto thisNodeIterator = meshTransformMap.find(thisNodeIndex);
AZ_Assert(thisNodeIterator != meshTransformMap.end(), "This node index missing.");
auto thisTransformIndex = thisNodeIterator->second.second;
// get node matrix data to set the entity's local transform
const auto nodeTransform = azrtti_cast<const DataTypes::ITransform*>(graph.GetNodeContent(thisTransformIndex));
if (nodeTransform)
{
entityTransform->SetLocalTM(AZ::Transform::CreateFromMatrix3x4(nodeTransform->GetMatrix()));
}
else
{
entityTransform->SetLocalTM(AZ::Transform::CreateUniformScale(1.0f));
}
}
return entities;
}
bool CreatePrefabGroup(
ManifestUpdates& manifestUpdates,
Containers::Scene& scene,
const EntityIdList& entities,
const AZStd::string& filenameOnly,
const AZStd::string& relativeSourcePath)
{
AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get()->RemoveAllTemplates();
AZStd::string prefabTemplateName { relativeSourcePath };
AZ::StringFunc::Path::ReplaceFullName(prefabTemplateName, filenameOnly.c_str());
AZ::StringFunc::Replace(prefabTemplateName, "\\", "/"); // the source folder uses forward slash
// create prefab group for entire stack
AzToolsFramework::Prefab::TemplateId prefabTemplateId;
AzToolsFramework::Prefab::PrefabSystemScriptingBus::BroadcastResult(
prefabTemplateId,
&AzToolsFramework::Prefab::PrefabSystemScriptingBus::Events::CreatePrefabTemplate,
entities,
prefabTemplateName);
if (prefabTemplateId == AzToolsFramework::Prefab::InvalidTemplateId)
{
AZ_Error("prefab", false, "Could not create a prefab template for entites.");
return false;
}
// Convert the prefab to a JSON string
AZ::Outcome<AZStd::string, void> outcome;
AzToolsFramework::Prefab::PrefabLoaderScriptingBus::BroadcastResult(
outcome,
&AzToolsFramework::Prefab::PrefabLoaderScriptingBus::Events::SaveTemplateToString,
prefabTemplateId);
if (outcome.IsSuccess() == false)
{
AZ_Error("prefab", false, "Could not create JSON string for template; maybe NaN values in the template?");
return false;
}
AzToolsFramework::Prefab::PrefabDom prefabDom;
prefabDom.Parse(outcome.GetValue().c_str());
auto prefabGroup = AZStd::make_shared<AZ::SceneAPI::SceneData::PrefabGroup>();
prefabGroup->SetName(prefabTemplateName);
prefabGroup->SetPrefabDom(AZStd::move(prefabDom));
prefabGroup->SetId(DataTypes::Utilities::CreateStableUuid(
scene,
azrtti_typeid<AZ::SceneAPI::SceneData::PrefabGroup>(),
prefabTemplateName));
manifestUpdates.emplace_back(prefabGroup);
// update manifest if there where no errors
for (auto update : manifestUpdates)
{
scene.GetManifest().AddEntry(update);
}
return true;
}
// AssetImportRequest
Events::ProcessingResult UpdateManifest(Containers::Scene& scene, ManifestAction action, RequestingApplication requester) override;
};
Events::ProcessingResult PrefabGroupBehavior::ExportEventHandler::UpdateManifest(
Containers::Scene& scene,
ManifestAction action,
RequestingApplication requester)
{
if (action == Events::AssetImportRequest::Update)
{
// ignore constructing a default procedural prefab if some tool or script is attempting
// to update the scene manifest
return Events::ProcessingResult::Ignored;
}
else if (action == Events::AssetImportRequest::ConstructDefault && requester == RequestingApplication::Editor)
{
// ignore constructing a default procedurla prefab if the Editor's "Edit Settings..." is being used
// the user is trying to assign the source scene asset their own mesh groups
return Events::ProcessingResult::Ignored;
}
// this toggle makes constructing default mesh groups and a prefab optional
if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry)
{
bool createDefaultPrefab = true;
settingsRegistry->Get(createDefaultPrefab, s_PrefabGroupBehaviorCreateDefaultKey);
if (createDefaultPrefab == false)
{
return Events::ProcessingResult::Ignored;
}
}
auto meshTransformMap = CalculateMeshTransformMap(scene);
if (meshTransformMap.empty())
{
return Events::ProcessingResult::Ignored;
}
// compute the filenames of the scene file
AZStd::string relativeSourcePath = scene.GetSourceFilename();
// the watch folder and forward slash is used to in the asset hint path of the file
AZStd::string watchFolder = scene.GetWatchFolder() + "/";
AZ::StringFunc::Replace(relativeSourcePath, watchFolder.c_str(), "");
AZ::StringFunc::Replace(relativeSourcePath, ".", "_");
AZStd::string filenameOnly{ relativeSourcePath };
AZ::StringFunc::Path::GetFileName(filenameOnly.c_str(), filenameOnly);
AZ::StringFunc::Path::ReplaceExtension(filenameOnly, "procprefab");
ManifestUpdates manifestUpdates;
auto nodeEntityMap = CreateMeshGroups(manifestUpdates, meshTransformMap, scene, relativeSourcePath);
if(nodeEntityMap.empty())
{
return Events::ProcessingResult::Ignored;
}
auto entities = FixUpEntityParenting(nodeEntityMap, scene.GetGraph(), meshTransformMap);
if(entities.empty())
{
return Events::ProcessingResult::Ignored;
}
bool success = CreatePrefabGroup(manifestUpdates, scene, entities, filenameOnly, relativeSourcePath);
return success ? Events::ProcessingResult::Success : Events::ProcessingResult::Ignored;
}
//
// PrefabGroupBehavior
//
void PrefabGroupBehavior::Activate()
{
m_exportEventHandler = AZStd::make_shared<ExportEventHandler>([this](auto& context)
{
return this->OnPrepareForExport(context);
});
}
void PrefabGroupBehavior::Deactivate()
{
m_exportEventHandler.reset();
}
AZStd::unique_ptr<rapidjson::Document> PrefabGroupBehavior::CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup, const AZ::IO::Path& relativePath) const
{
using namespace AzToolsFramework::Prefab;
auto* prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::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<rapidjson::StringBuffer, rapidjson::UTF8<>> writer(sb);
if (prefabDom.Accept(writer) == false)
{
AZ_Error("prefab", false, "Could not write PrefabGroup(%s) to JSON", prefabGroup->GetName().c_str());
return {};
}
// The originPath we pass to LoadTemplateFromString must be the relative path of the file
AZ::IO::Path templateName(prefabGroup->GetName());
templateName.ReplaceExtension(AZ::Prefab::PrefabGroupAssetHandler::s_Extension);
if (!AZ::StringFunc::StartsWith(templateName.c_str(), relativePath.c_str()))
{
templateName = relativePath / templateName;
}
auto templateId = prefabLoaderInterface->LoadTemplateFromString(sb.GetString(), templateName.Native().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<PrefabSystemComponentInterface>::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::Document>(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
{
AZStd::string filePath = AZ::SceneAPI::Utilities::FileUtilities::CreateOutputFileName(
prefabGroup->GetName().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<rapidjson::StringBuffer, rapidjson::UTF8<>> 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(prefabGroup->GetName().c_str());
context.GetProductList().AddProduct(
filePath,
context.GetScene().GetSourceGuid(),
azrtti_typeid<Prefab::ProceduralPrefabAsset>(),
{},
AZStd::make_optional(subId));
return true;
}
return false;
}
Events::ProcessingResult PrefabGroupBehavior::OnPrepareForExport(Events::PreExportEventContext& context) const
{
AZStd::vector<const SceneData::PrefabGroup*> prefabGroupCollection;
const Containers::SceneManifest& manifest = context.GetScene().GetManifest();
for (size_t i = 0; i < manifest.GetEntryCount(); ++i)
{
const auto* group = azrtti_cast<const SceneData::PrefabGroup*>(manifest.GetValue(i).get());
if (group)
{
prefabGroupCollection.push_back(group);
}
}
if (prefabGroupCollection.empty())
{
return AZ::SceneAPI::Events::ProcessingResult::Ignored;
}
// Get the relative path of the source and then take just the path portion of it (no file name)
AZ::IO::Path relativePath = context.GetScene().GetSourceFilename();
relativePath = relativePath.LexicallyRelative(AZStd::string_view(context.GetScene().GetWatchFolder()));
AZStd::string relativeSourcePath { AZStd::move(relativePath.ParentPath().Native()) };
AZ::StringFunc::Replace(relativeSourcePath, "\\", "/"); // the source paths use forward slashes
for (const auto* prefabGroup : prefabGroupCollection)
{
auto result = CreateProductAssetData(prefabGroup, relativeSourcePath);
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<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<PrefabGroupBehavior, BehaviorComponent>()->Version(1);
}
}
}