{lyn9135} Adding a default prefab during scene processing (#6851)

* {lyn8865} Adding DataTypes::ScriptProcessorFallbackLogic

- Give the user an option how to handle fallback logic for script rules

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* the new code found an error in a Python script... it seems to work!

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* fixing up the regression test

Signed-off-by: Jackson <23512001+jackalbe@users.noreply.github.com>

* dump version number

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* Adding first pass of logic plus test file

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* Applying scene graph transform to entity transform
Excluding other mesh names in the mesh groups


Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* test: parent top stack


Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* cleaned up the code and parent tops FBX file

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* adding tests for the proc prefab constructor logic

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* Linux/Clang fixes

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* assigning matrix data to pass the tests in Linux (and others...)

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* Addressed some concerns based on PR feedback

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* encapsulating the logic into readable sections

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* added comment to watch folder to asset path info

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>
monroegm-disable-blank-issue-2
Allen Jackson 4 years ago committed by GitHub
parent f3e6e57cc4
commit c8cf613cf6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3789abdf439a6d70438fd4bb1e06881ae6686a4699209c6bc371d22d161e5347
size 26476

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c987d7d79685fda83efcffb7e1afbcd356c37fc68ec5c663a89b02d4df10caea
size 46412

@ -18,13 +18,57 @@
#include <AzToolsFramework/Asset/AssetSystemComponent.h>
#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/ITransform.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/MockIGraphObject.h>
#include <SceneAPI/SceneCore/SceneCoreStandaloneAllocator.h>
#include <SceneAPI/SceneData/GraphData/MeshData.h>
#include <SceneAPI/SceneData/SceneDataStandaloneAllocator.h>
#include <PrefabGroup/PrefabBehaviorTests.inl>
// a mock AZ::Render::EditorMeshComponent
namespace AZ::Render
{
struct EditorMeshComponent
: public AZ::Component
{
AZ_COMPONENT(EditorMeshComponent, "{DCE68F6E-2E16-4CB4-A834-B6C2F900A7E9}");
void Activate() override {}
void Deactivate() override {}
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AZ::Render::EditorMeshComponent>();
}
if (BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<EditorMeshComponent>("AZ::Render::EditorMeshComponent");
}
}
};
struct EditorMeshComponentHelper
: public AZ::ComponentDescriptorHelper<EditorMeshComponent>
{
AZ_CLASS_ALLOCATOR(EditorMeshComponentHelper, AZ::SystemAllocator, 0);
void Reflect(AZ::ReflectContext* reflection) const override
{
AZ::Render::EditorMeshComponent::Reflect(reflection);
}
};
}
namespace UnitTest
{
class PrefabBehaviorTests
@ -39,10 +83,12 @@ namespace UnitTest
AZ::AllocatorInstance<AZ::SystemAllocator>().Create();
}
AZ::SceneAPI::SceneCoreStandaloneAllocator::Initialize(AZ::Environment::GetInstance());
AZ::SceneAPI::SceneDataStandaloneAllocator::Initialize(AZ::Environment::GetInstance());
}
static void TearDownTestCase()
{
AZ::SceneAPI::SceneDataStandaloneAllocator::TearDown();
AZ::SceneAPI::SceneCoreStandaloneAllocator::TearDown();
AZ::AllocatorInstance<AZ::SystemAllocator>().Destroy();
}
@ -62,10 +108,16 @@ namespace UnitTest
return PrefabBehaviorTests::OnGetSourceInfoBySourcePath(path, info);
});
m_assetSystemRequestMock.BusConnect();
m_componentDescriptorHelperEditorMeshComponent = AZStd::make_unique<AZ::Render::EditorMeshComponentHelper>();
m_componentDescriptorHelperEditorMeshComponent->Reflect(m_app.GetSerializeContext());
m_componentDescriptorHelperEditorMeshComponent->Reflect(m_app.GetBehaviorContext());
}
void TearDown() override
{
m_componentDescriptorHelperEditorMeshComponent.reset();
m_assetSystemRequestMock.BusDisconnect();
m_prefabGroupBehavior->Deactivate();
@ -86,6 +138,72 @@ namespace UnitTest
return true;
}
AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene> CreateMockScene()
{
using namespace AZ::SceneAPI;
auto scene = AZStd::make_shared<Containers::Scene>("mock_scene");
scene->SetManifestFilename("ManifestFilename");
scene->SetSource("Source", AZ::Uuid::CreateRandom());
scene->SetWatchFolder("WatchFolder");
/*---------------------------------------\
Root
|
1
|
2
/ \
------3m 7
/ / / \
6 5 4t 8m-------
\ \ \
9t 10 11
\---------------------------------------*/
// Build up the graph
auto root = scene->GetGraph().GetRoot();
scene->GetGraph().SetContent(root, AZStd::make_shared<DataTypes::MockIGraphObject>(0));
auto index1 = scene->GetGraph().AddChild(root, "1", AZStd::make_shared<DataTypes::MockIGraphObject>(1));
auto index2 = scene->GetGraph().AddChild(index1, "2", AZStd::make_shared<DataTypes::MockIGraphObject>(2));
auto index3 = scene->GetGraph().AddChild(index2, "3", AZStd::make_shared<AZ::SceneData::GraphData::MeshData>());
auto index4 = scene->GetGraph().AddChild(index3, "4", AZStd::make_shared<MockTransform>());
auto index5 = scene->GetGraph().AddChild(index3, "5", AZStd::make_shared<DataTypes::MockIGraphObject>(5));
auto index6 = scene->GetGraph().AddChild(index3, "6", AZStd::make_shared<DataTypes::MockIGraphObject>(6));
auto index7 = scene->GetGraph().AddChild(index2, "7", AZStd::make_shared<DataTypes::MockIGraphObject>(7));
auto index8 = scene->GetGraph().AddChild(index7, "8", AZStd::make_shared<AZ::SceneData::GraphData::MeshData>());
auto index9 = scene->GetGraph().AddChild(index8, "9", AZStd::make_shared<MockTransform>());
auto index10 = scene->GetGraph().AddChild(index8, "10", AZStd::make_shared<DataTypes::MockIGraphObject>(10));
auto index11 = scene->GetGraph().AddChild(index8, "11", AZStd::make_shared<DataTypes::MockIGraphObject>(11));
scene->GetGraph().MakeEndPoint(index4);
scene->GetGraph().MakeEndPoint(index5);
scene->GetGraph().MakeEndPoint(index6);
scene->GetGraph().MakeEndPoint(index9);
scene->GetGraph().MakeEndPoint(index10);
scene->GetGraph().MakeEndPoint(index11);
return scene;
}
// mock classes and structures
struct MockTransform
: public AZ::SceneAPI::DataTypes::ITransform
{
AZ::Matrix3x4 m_matrix = AZ::Matrix3x4::CreateIdentity();
AZ::Matrix3x4& GetMatrix() override
{
return m_matrix;
}
const AZ::Matrix3x4& GetMatrix() const
{
return m_matrix;
}
};
struct TestPreExportEventContext
{
TestPreExportEventContext()
@ -110,6 +228,7 @@ namespace UnitTest
AZStd::unique_ptr<AZ::SceneAPI::Behaviors::PrefabGroupBehavior> m_prefabGroupBehavior;
testing::NiceMock<UnitTests::MockAssetSystemRequest> m_assetSystemRequestMock;
AZStd::unique_ptr<AZ::Render::EditorMeshComponentHelper> m_componentDescriptorHelperEditorMeshComponent;
};
TEST_F(PrefabBehaviorTests, PrefabBehavior_EmptyContextIgnored_Works)
@ -164,4 +283,72 @@ namespace UnitTest
AZ_Warning("testing", false, "The product asset (%s) is missing", pathStr.c_str());
}
}
TEST_F(PrefabBehaviorTests, PrefabBehavior_UpdateManifestWithEmptyScene_DoesNotFail)
{
using namespace AZ::SceneAPI;
using namespace AZ::SceneAPI::Events;
Containers::Scene scene("empty_scene");
AssetImportRequest::ManifestAction action = AssetImportRequest::ManifestAction::ConstructDefault;
AssetImportRequest::RequestingApplication requester = {};
Behaviors::PrefabGroupBehavior prefabGroupBehavior;
ProcessingResult result = ProcessingResult::Failure;
AssetImportRequestBus::BroadcastResult(result, &AssetImportRequestBus::Events::UpdateManifest, scene, action, requester);
EXPECT_NE(result, ProcessingResult::Failure);
}
TEST_F(PrefabBehaviorTests, PrefabBehavior_UpdateManifestWithEmptyScene_Ignored)
{
using namespace AZ::SceneAPI;
using namespace AZ::SceneAPI::Events;
Containers::Scene scene("empty_scene");
AssetImportRequest::ManifestAction action = AssetImportRequest::ManifestAction::Update;
AssetImportRequest::RequestingApplication requester = {};
Behaviors::PrefabGroupBehavior prefabGroupBehavior;
ProcessingResult result = ProcessingResult::Failure;
AssetImportRequestBus::BroadcastResult(result, &AssetImportRequestBus::Events::UpdateManifest, scene, action, requester);
EXPECT_EQ(result, ProcessingResult::Ignored);
}
TEST_F(PrefabBehaviorTests, PrefabBehavior_UpdateManifestMockScene_CreatesPrefab)
{
using namespace AZ::SceneAPI;
using namespace AZ::SceneAPI::Events;
auto scene = CreateMockScene();
AssetImportRequest::ManifestAction action = AssetImportRequest::ManifestAction::ConstructDefault;
AssetImportRequest::RequestingApplication requester = {};
Behaviors::PrefabGroupBehavior prefabGroupBehavior;
ProcessingResult result = ProcessingResult::Failure;
AssetImportRequestBus::BroadcastResult(result, &AssetImportRequestBus::Events::UpdateManifest, *scene, action, requester);
EXPECT_EQ(result, ProcessingResult::Success);
EXPECT_EQ(scene->GetManifest().GetEntryCount(), 3);
EXPECT_TRUE(azrtti_istypeof<AZ::SceneAPI::DataTypes::IMeshGroup>(scene->GetManifest().GetValue(0).get()));
EXPECT_TRUE(azrtti_istypeof<AZ::SceneAPI::DataTypes::IMeshGroup>(scene->GetManifest().GetValue(1).get()));
EXPECT_TRUE(azrtti_istypeof<AZ::SceneAPI::DataTypes::IPrefabGroup>(scene->GetManifest().GetValue(2).get()));
}
TEST_F(PrefabBehaviorTests, PrefabBehavior_UpdateManifest_ToggleWorks)
{
using namespace AZ::SceneAPI;
using namespace AZ::SceneAPI::Events;
ASSERT_TRUE(AZ::SettingsRegistry::Get());
AZ::SettingsRegistry::Get()->Set("/O3DE/Preferences/Prefabs/CreateDefaults", false);
auto scene = CreateMockScene();
AssetImportRequest::ManifestAction action = AssetImportRequest::ManifestAction::ConstructDefault;
AssetImportRequest::RequestingApplication requester = {};
Behaviors::PrefabGroupBehavior prefabGroupBehavior;
ProcessingResult result = ProcessingResult::Failure;
AssetImportRequestBus::BroadcastResult(result, &AssetImportRequestBus::Events::UpdateManifest, *scene, action, requester);
EXPECT_EQ(result, ProcessingResult::Ignored);
EXPECT_EQ(scene->GetManifest().GetEntryCount(), 0);
}
}

@ -10,6 +10,7 @@
#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>
@ -20,16 +21,42 @@
#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/Instance/InstanceToTemplateInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemScriptingBus.h>
#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>
#include <SceneAPI/SceneData/Groups/MeshGroup.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/ITransform.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphUpwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.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
{
@ -37,8 +64,11 @@ 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;
@ -51,10 +81,12 @@ namespace AZ::SceneAPI::Behaviors
{
BindToCall(&ExportEventHandler::PrepareForExport);
AZ::SceneAPI::SceneCore::ExportingComponent::Activate();
Events::AssetImportRequestBus::Handler::BusConnect();
}
~ExportEventHandler()
{
Events::AssetImportRequestBus::Handler::BusDisconnect();
AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
}
@ -62,8 +94,338 @@ namespace AZ::SceneAPI::Behaviors
{
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>;
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.begin() == view.end())
{
return {};
}
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();
auto currentContent = graph.GetNodeContent(currentIndex);
if (currentContent)
{
if (azrtti_istypeof<AZ::SceneAPI::DataTypes::ITransform>(currentContent.get()))
{
auto parentIndex = graph.GetNodeParent(currentIndex);
if (parentIndex.IsValid() == false)
{
continue;
}
auto parentContent = graph.GetNodeContent(parentIndex);
if (parentContent && azrtti_istypeof<AZ::SceneAPI::DataTypes::IMeshData>(parentContent.get()))
{
// map the node parent to the ITransform
MeshTransformPair pair{ parentIndex, currentIndex };
meshTransformMap.emplace(MeshTransformEntry{ graph.GetNodeParent(parentIndex), AZStd::move(pair) });
}
}
}
}
return meshTransformMap;
}
using ManifestUpdates = AZStd::vector<AZStd::shared_ptr<DataTypes::IManifestObject>>;
using NodeEntityMap = AZStd::unordered_map<Containers::SceneGraph::NodeIndex, AZ::EntityId>;
NodeEntityMap CreateMeshGroups(
ManifestUpdates& manifestUpdates,
const MeshTransformMap& meshTransformMap,
const Containers::Scene& scene,
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 meshNodeFullName;
meshNodeFullName = relativeSourcePath;
meshNodeFullName.append("_");
meshNodeFullName.append(meshNodeName.GetName());
auto meshGroup = AZStd::make_shared<AZ::SceneAPI::SceneData::MeshGroup>();
meshGroup->SetName(meshNodeFullName.c_str());
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);
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
auto meshAssetJson = AZStd::string::format(
R"JSON(
{"Controller": {"Configuration": {"ModelAsset": { "assetHint": "%s.azmodel"}}}}
)JSON", meshNodeFullName.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;
}
using EntityIdList = AZStd::vector<AZ::EntityId>;
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));
entityTransform->SetLocalTM(AZ::Transform::CreateFromMatrix3x4(nodeTransform->GetMatrix()));
}
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();
// create prefab group for entire stack
AzToolsFramework::Prefab::TemplateId prefabTemplateId;
AzToolsFramework::Prefab::PrefabSystemScriptingBus::BroadcastResult(
prefabTemplateId,
&AzToolsFramework::Prefab::PrefabSystemScriptingBus::Events::CreatePrefabTemplate,
entities,
filenameOnly);
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(relativeSourcePath);
prefabGroup->SetPrefabDom(AZStd::move(prefabDom));
prefabGroup->SetId(DataTypes::Utilities::CreateStableUuid(
scene,
azrtti_typeid<AZ::SceneAPI::SceneData::PrefabGroup>(),
relativeSourcePath));
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,
[[maybe_unused]] RequestingApplication requester)
{
if (action != Events::AssetImportRequest::ConstructDefault)
{
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();
AZ::StringFunc::Replace(relativeSourcePath, ".", "_");
// 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(), "");
AZStd::string filenameOnly{ relativeSourcePath };
AZ::StringFunc::Path::GetFileName(filenameOnly.c_str(), filenameOnly);
AZ::StringFunc::Path::ReplaceExtension(filenameOnly, "prefab");
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
//

Loading…
Cancel
Save