LoggingGroupBehavior.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Serialization/SerializeContext.h>
  9. #include <AzCore/std/smart_ptr/make_shared.h>
  10. #include <SceneAPI/SceneCore/Containers/Scene.h>
  11. #include <SceneAPI/SceneCore/Containers/SceneManifest.h>
  12. #include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
  13. #include <SceneAPI/SceneCore/Utilities/Reporting.h>
  14. #include <Behaviors/LoggingGroupBehavior.h>
  15. #include <Groups/LoggingGroup.h>
  16. namespace SceneLoggingExample
  17. {
  18. // Reflection is a basic requirement for components. For behaviors, you can often keep the Reflect()
  19. // function simple because the SceneAPI just needs to be able to find the component. For more details
  20. // on the Reflect() function, see LoggingGroup.cpp.
  21. void LoggingGroupBehavior::Reflect(AZ::ReflectContext* context)
  22. {
  23. // The data and UI elements used in the SceneAPI are not components, but they need to be reflected
  24. // for serialization and the Scene Settings to work. This can done at any point in the gem, but the
  25. // behavior that controls the data is a good place for this. Because the LoggingGroupBehavior controls
  26. // the LoggingGroup, we will register it here.
  27. LoggingGroup::Reflect(context);
  28. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  29. if (serializeContext)
  30. {
  31. serializeContext->Class<LoggingGroupBehavior, AZ::SceneAPI::SceneCore::BehaviorComponent>()->Version(1);
  32. }
  33. }
  34. // Later in this example, messages that deal with manifest changes and loading files will be used
  35. // to create the various ways that the behavior controls settings. Before any events can be sent
  36. // to the behavior, it first needs to be connected to the EBuses that it monitors.
  37. void LoggingGroupBehavior::Activate()
  38. {
  39. AZ::SceneAPI::Events::ManifestMetaInfoBus::Handler::BusConnect();
  40. AZ::SceneAPI::Events::AssetImportRequestBus::Handler::BusConnect();
  41. }
  42. // Disconnect from the EBuses when this behavior is no longer active.
  43. void LoggingGroupBehavior::Deactivate()
  44. {
  45. AZ::SceneAPI::Events::ManifestMetaInfoBus::Handler::BusDisconnect();
  46. AZ::SceneAPI::Events::AssetImportRequestBus::Handler::BusDisconnect();
  47. }
  48. // This behavior will control the logging for the UI, so let's begin by registering the LoggingGroup with the UI under a new
  49. // "Logging" tab and ignore the position of the tab for now. This will add a new tab to the Scene Settings window. The tab
  50. // will have a single button to add a LoggingGroup. If additional groups are registered under the same tab name, the button
  51. // will be changed to a drop-down button and allow the registered groups to be added.
  52. //
  53. // The scene is passed as one of the arguments so that the manifest and/or the graph can be inspected to determine if a group
  54. // should be added. For example, if the graph doesn't contain any meshes, the mesh group can be left out. This helps prevent
  55. // users from adding groups that have no effect.
  56. void LoggingGroupBehavior::GetCategoryAssignments(CategoryRegistrationList& categories, [[maybe_unused]] const AZ::SceneAPI::Containers::Scene& scene)
  57. {
  58. categories.emplace_back("Logging", LoggingGroup::TYPEINFO_Uuid(), s_loggingPreferredTabOrder);
  59. }
  60. // When a scene is loaded for the first time (for example, from an .fbx file), there won't be a manifest (.assetinfo file).
  61. // If the scene was loaded previously, there might be a manifest that requires updates because it contains values that no
  62. // longer match the graph. This EBus call gives a one-time opportunity right after loading has completed to update the manifest
  63. // or to add data to a new one.
  64. //
  65. // In this example, let's add a LoggingGroup to a new manifest only. Don't forget to remove the manifest (.assetinfo file)
  66. // for your test scene file. Otherwise, the following code won't trigger.
  67. AZ::SceneAPI::Events::ProcessingResult LoggingGroupBehavior::UpdateManifest(AZ::SceneAPI::Containers::Scene& scene,
  68. ManifestAction action, [[maybe_unused]] RequestingApplication requester)
  69. {
  70. if (action == ManifestAction::ConstructDefault)
  71. {
  72. AZStd::shared_ptr<LoggingGroup> group = AZStd::make_shared<LoggingGroup>();
  73. // This might not be the only behavior that wants to make modifications to the new group. An example is a material
  74. // behavior that wants to add a material rule when a mesh group is created. By calling the EBus below, other behaviors
  75. // get a chance to change or add their own values. Listening to this EBus is also a good place to add any settings to
  76. // the new group instead of doing it here. This is because this EBus is also called when tools such as the UI create
  77. // a new group, which keeps initialization in one place.
  78. AZ::SceneAPI::Events::ManifestMetaInfoBus::Broadcast(
  79. &AZ::SceneAPI::Events::ManifestMetaInfoBus::Events::InitializeObject, scene, *group);
  80. if (scene.GetManifest().AddEntry(AZStd::move(group)))
  81. {
  82. // Let the SceneAPI know that a LoggingGroup has been successfully added.
  83. return AZ::SceneAPI::Events::ProcessingResult::Success;
  84. }
  85. else
  86. {
  87. // It wasn't possible to add the new logging group, so let the SceneAPI know that
  88. // a problem was encountered. Don't forget to also tell the user what is going on,
  89. // because this will cause the loading to fail.
  90. AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Unable to add a new logging group.");
  91. return AZ::SceneAPI::Events::ProcessingResult::Failure;
  92. }
  93. }
  94. // In any other situation, there's no plan to do anything so tell the SceneAPI to ignore this behavior.
  95. return AZ::SceneAPI::Events::ProcessingResult::Ignored;
  96. }
  97. // When a new manifest object is created, the caller can choose to allow other behaviors to change or add their own data, such
  98. // as rules to a group. The EBus call in the above function shows a typical use case. Using InitializeObject() provides a more
  99. // powerful alternative to default values. It allows domain logic to be spread to appropriate behaviors, but also allows general
  100. // awareness of the manifest and graph to select default values that are more appropriate to the user.
  101. //
  102. // For this example, let's use the passed-in manifest to look for the last LoggingGroup in the manifest and use the log setting that
  103. // is its opposite. When viewing this in the Scene Settings window, "Log processing events" will be off when adding a new logging group.
  104. // The one directly above it is on, and vice versa.
  105. void LoggingGroupBehavior::InitializeObject(const AZ::SceneAPI::Containers::Scene& scene, AZ::SceneAPI::DataTypes::IManifestObject& target)
  106. {
  107. // If the item being added isn't a LoggingGroup, ignore it.
  108. if (!target.RTTI_IsTypeOf(LoggingGroup::TYPEINFO_Uuid()))
  109. {
  110. return;
  111. }
  112. LoggingGroup* newGroup = azrtti_cast<LoggingGroup*>(&target);
  113. AZ_Assert(newGroup, "Manifest object has been identified as LoggingGroup, but failed to cast to it.");
  114. // First create a view that only contains instances that exactly match LoggingGroups. Use MakeDerivedFilterView() to do
  115. // the same for any instances that implement a specific interface and/or base class. For more details on using iterators
  116. // to get data from the manifest and graph, see ExportTrackingProcessor.cpp.
  117. auto values = scene.GetManifest().GetValueStorage();
  118. auto view = AZ::SceneAPI::Containers::MakeExactFilterView<LoggingGroup>(values);
  119. // Find the last LoggingGroup in the manifest.
  120. auto last = view.begin();
  121. while (AZStd::next(last) != view.end())
  122. {
  123. ++last;
  124. }
  125. // Only take the values if there's actually another LoggingGroup in the manifest.
  126. if (last != view.end())
  127. {
  128. newGroup->ShouldLogProcessingEvents(!last->DoesLogProcessingEvents());
  129. }
  130. // Let's also set a default name for this group. Groups often match one-to-one with the file that they output.
  131. // For example, a Mesh Group will produce a .cgf file with the same name. If the name is used as a file name,
  132. // it is important to check whether it's a valid path name and isn't duplicating another name.
  133. const size_t size = AZStd::distance(view.begin(), view.end());
  134. newGroup->SetName(AZStd::string::format("Logger_%zu", size));
  135. }
  136. } // namespace SceneLoggingExample