diff --git a/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py index d5a7acfe91..4aeff06f18 100644 --- a/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py +++ b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py @@ -74,6 +74,7 @@ def update_manifest(scene): source_filename_only = os.path.basename(clean_filename) created_entities = [] + previous_entity_id = azlmbr.entity.InvalidEntityId # Loop every mesh node in the scene for activeMeshIndex in range(len(mesh_name_list)): @@ -102,14 +103,33 @@ def update_manifest(scene): # The MeshGroup we created will be output as a product in the asset's path named mesh_group_name.azmodel # The assetHint will be converted to an AssetId later during prefab loading json_update = json.dumps({ - "Controller": { "Configuration": { "ModelAsset": { - "assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}} - }); + "Controller": { "Configuration": { "ModelAsset": { + "assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}} + }); # Apply the JSON above to the component we created result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, editor_mesh_component, json_update) if not result: - raise RuntimeError("UpdateComponentForEntity failed") + raise RuntimeError("UpdateComponentForEntity failed for Mesh component") + + # Get the transform component + transform_component = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "GetOrAddComponentByTypeName", entity_id, "27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0") + + # Set this entity to be a child of the last entity we created + # This is just an example of how to do parenting and isn't necessarily useful to parent everything like this + if previous_entity_id is not None: + transform_json = json.dumps({ + "Parent Entity" : previous_entity_id.to_json() + }); + + # Apply the JSON update + result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, transform_component, transform_json) + + if not result: + raise RuntimeError("UpdateComponentForEntity failed for Transform component") + + # Update the last entity id for next time + previous_entity_id = entity_id # Keep track of the entity we set up, we'll add them all to the prefab we're creating later created_entities.append(entity_id) @@ -147,6 +167,8 @@ def on_update_manifest(args): except RuntimeError as err: print (f'ERROR - {err}') log_exception_traceback() + except: + log_exception_traceback() global sceneJobHandler sceneJobHandler = None diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp index d34e2cfc92..884c463bff 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp @@ -6,11 +6,14 @@ * */ +#include #include #include #include #include #include +#include +#include namespace AzToolsFramework::Prefab { @@ -61,9 +64,29 @@ namespace AzToolsFramework::Prefab entities.push_back(entity); } } - - auto prefab = m_prefabSystemComponentInterface->CreatePrefab(entities, {}, AZ::IO::PathView(AZStd::string_view(filePath))); + bool result = false; + [[maybe_unused]] AZ::EntityId commonRoot; + EntityList topLevelEntities; + AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(result, &AzToolsFramework::ToolsApplicationRequestBus::Events::FindCommonRootInactive, + entities, commonRoot, &topLevelEntities); + + auto containerEntity = AZStd::make_unique(); + + for (AZ::Entity* entity : topLevelEntities) + { + AzToolsFramework::Components::TransformComponent* transformComponent = + entity->FindComponent(); + + if (transformComponent) + { + transformComponent->SetParent(containerEntity->GetId()); + } + } + + auto prefab = m_prefabSystemComponentInterface->CreatePrefab( + entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)), AZStd::move(containerEntity)); + if (!prefab) { AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str()); diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp index 706ca48156..df246d230d 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp @@ -17,10 +17,16 @@ #include #include +#include +#include #include +#include #include #include +#include +#include +#include namespace EditorPythonBindings { @@ -571,6 +577,37 @@ namespace EditorPythonBindings return false; } + pybind11::object PythonProxyObject::ToJson() + { + rapidjson::Document document; + AZ::JsonSerializerSettings settings; + settings.m_keepDefaults = true; + + auto resultCode = + AZ::JsonSerialization::Store(document, document.GetAllocator(), m_wrappedObject.m_address, nullptr, m_wrappedObject.m_typeId, settings); + + if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) + { + AZ_Error("PythonProxyObject", false, "Failed to serialize to json"); + return pybind11::cast(Py_None); + } + + AZStd::string jsonString; + AZ::Outcome outcome = AZ::JsonSerializationUtils::WriteJsonString(document, jsonString); + + if (!outcome.IsSuccess()) + { + AZ_Error("PythonProxyObject", false, "Failed to write json string: %s", outcome.GetError().c_str()); + return pybind11::cast(Py_None); + } + + jsonString.erase(AZStd::remove(jsonString.begin(), jsonString.end(), '\n'), jsonString.end()); + auto pythonCode = AZStd::string::format( + R"PYTHON(exec("import json") or json.loads("""%s"""))PYTHON", jsonString.c_str()); + + return pybind11::eval(pythonCode.c_str()); + } + bool PythonProxyObject::DoComparisonEvaluation(pybind11::object pythonOther, Comparison comparison) { bool invertLogic = false; @@ -912,6 +949,7 @@ namespace EditorPythonBindings .def("set_property", &PythonProxyObject::SetPropertyValue) .def("get_property", &PythonProxyObject::GetPropertyValue) .def("invoke", &PythonProxyObject::Invoke) + .def("to_json", &PythonProxyObject::ToJson) .def(Operator::s_isEqual, [](PythonProxyObject& self, pybind11::object rhs) { return self.DoEqualityEvaluation(rhs); diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h index 5b611c32c0..a9d2fad5a0 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.h @@ -58,6 +58,8 @@ namespace EditorPythonBindings //! Performs an equality operation to compare this object with another object bool DoEqualityEvaluation(pybind11::object pythonOther); + pybind11::object ToJson(); + //! Perform a comparison of a Python operator enum class Comparison {