Merge branch 'stabilization/2106' of https://github.com/o3de/o3de into cgalvan/FixLCGraphNotOpeningOnEdit
This commit is contained in:
@@ -403,7 +403,12 @@ namespace AZ
|
||||
{
|
||||
if (!parentValue->EraseMember(tokens[path.GetTokenCount() - 1].name))
|
||||
{
|
||||
return settings.m_reporting(R"(The "remove" operation failed to remove member from object.)",
|
||||
rapidjson::StringBuffer pathString;
|
||||
path.Stringify(pathString);
|
||||
return settings.m_reporting(
|
||||
AZStd::string::format(
|
||||
R"(The "remove" operation failed to remove member '%s' from object at path '%s'.)",
|
||||
tokens[path.GetTokenCount() - 1].name, pathString.GetString()),
|
||||
ResultCode(Tasks::Merge, Outcomes::Invalid), element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,28 @@ namespace AzPhysics
|
||||
const CollisionGroup CollisionGroup::All_NoTouchBend = CollisionGroup::All.GetMask() & ~CollisionLayer::TouchBend.GetMask();
|
||||
#endif
|
||||
|
||||
void CollisionGroupScriptConstructor(CollisionGroup* thisPtr, AZ::ScriptDataContext& scriptDataContext)
|
||||
{
|
||||
if (int numArgs = scriptDataContext.GetNumArguments();
|
||||
numArgs != 1)
|
||||
{
|
||||
scriptDataContext.GetScriptContext()->Error(AZ::ScriptContext::ErrorType::Error, true,
|
||||
"CollisionGroup() accepts only 1 argument, not %d", numArgs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scriptDataContext.IsString(0))
|
||||
{
|
||||
scriptDataContext.GetScriptContext()->Error(AZ::ScriptContext::ErrorType::Error, true,
|
||||
"Argument to CollisionGroup() should be string");
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string groupName;
|
||||
scriptDataContext.ReadArg(0, groupName);
|
||||
*thisPtr = CollisionGroup(groupName);
|
||||
}
|
||||
|
||||
void CollisionGroup::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
@@ -50,7 +72,9 @@ namespace AzPhysics
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Module, "physics")
|
||||
->Attribute(AZ::Script::Attributes::Category, "AzPhysics")
|
||||
->Constructor<const AZStd::string>()
|
||||
->Constructor<const AZStd::string&>()
|
||||
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
|
||||
->Attribute(AZ::Script::Attributes::ConstructorOverride, &CollisionGroupScriptConstructor)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,19 @@ namespace AzPhysics
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* behaviorContext = azdynamic_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<SceneQueryRequest>("SceneQueryRequest")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Module, "physics")
|
||||
->Attribute(AZ::Script::Attributes::Category, "PhysX")
|
||||
->Property("Collision", BehaviorValueProperty(&SceneQueryRequest::m_collisionGroup))
|
||||
// Until enum class support for behavior context is done, expose this as an int
|
||||
->Property("QueryType", [](const SceneQueryRequest& self) { return static_cast<int>(self.m_queryType); },
|
||||
[](SceneQueryRequest& self, int newQueryType) { self.m_queryType = SceneQuery::QueryType(newQueryType); })
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ void RayCastRequest::Reflect(AZ::ReflectContext* context)
|
||||
@@ -123,10 +136,6 @@ namespace AzPhysics
|
||||
->Property("Distance", BehaviorValueProperty(&RayCastRequest::m_distance))
|
||||
->Property("Start", BehaviorValueProperty(&RayCastRequest::m_start))
|
||||
->Property("Direction", BehaviorValueProperty(&RayCastRequest::m_direction))
|
||||
->Property("Collision", BehaviorValueProperty(&RayCastRequest::m_collisionGroup))
|
||||
// Until enum class support for behavior context is done, expose this as an int
|
||||
->Property("QueryType", [](const RayCastRequest& self) { return static_cast<int>(self.m_queryType); },
|
||||
[](RayCastRequest& self, int newQueryType) { self.m_queryType = SceneQuery::QueryType(newQueryType); })
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,21 +73,22 @@ namespace Physics
|
||||
{
|
||||
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<Physics::CharacterRequestBus>("CharacterControllerRequestBus", "Character Controller")
|
||||
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
|
||||
behaviorContext->EBus<CharacterRequestBus>("CharacterControllerRequestBus")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Module, "physics")
|
||||
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
|
||||
->Event("GetBasePosition", &Physics::CharacterRequests::GetBasePosition, "Get Base Position")
|
||||
->Event("SetBasePosition", &Physics::CharacterRequests::SetBasePosition, "Set Base Position")
|
||||
->Event("GetCenterPosition", &Physics::CharacterRequests::GetCenterPosition, "Get Center Position")
|
||||
->Event("GetStepHeight", &Physics::CharacterRequests::GetStepHeight, "Get Step Height")
|
||||
->Event("SetStepHeight", &Physics::CharacterRequests::SetStepHeight, "Set Step Height")
|
||||
->Event("GetUpDirection", &Physics::CharacterRequests::GetUpDirection, "Get Up Direction")
|
||||
->Event("GetSlopeLimitDegrees", &Physics::CharacterRequests::GetSlopeLimitDegrees, "Get Slope Limit (Degrees)")
|
||||
->Event("SetSlopeLimitDegrees", &Physics::CharacterRequests::SetSlopeLimitDegrees, "Set Slope Limit (Degrees)")
|
||||
->Event("GetMaximumSpeed", &Physics::CharacterRequests::GetMaximumSpeed, "Get Maximum Speed")
|
||||
->Event("SetMaximumSpeed", &Physics::CharacterRequests::SetMaximumSpeed, "Set Maximum Speed")
|
||||
->Event("GetVelocity", &Physics::CharacterRequests::GetVelocity, "Get Velocity")
|
||||
->Event("AddVelocity", &Physics::CharacterRequests::AddVelocity, "Add Velocity")
|
||||
->Event("GetBasePosition", &CharacterRequests::GetBasePosition, "Get Base Position")
|
||||
->Event("SetBasePosition", &CharacterRequests::SetBasePosition, "Set Base Position")
|
||||
->Event("GetCenterPosition", &CharacterRequests::GetCenterPosition, "Get Center Position")
|
||||
->Event("GetStepHeight", &CharacterRequests::GetStepHeight, "Get Step Height")
|
||||
->Event("SetStepHeight", &CharacterRequests::SetStepHeight, "Set Step Height")
|
||||
->Event("GetUpDirection", &CharacterRequests::GetUpDirection, "Get Up Direction")
|
||||
->Event("GetSlopeLimitDegrees", &CharacterRequests::GetSlopeLimitDegrees, "Get Slope Limit (Degrees)")
|
||||
->Event("SetSlopeLimitDegrees", &CharacterRequests::SetSlopeLimitDegrees, "Set Slope Limit (Degrees)")
|
||||
->Event("GetMaximumSpeed", &CharacterRequests::GetMaximumSpeed, "Get Maximum Speed")
|
||||
->Event("SetMaximumSpeed", &CharacterRequests::SetMaximumSpeed, "Set Maximum Speed")
|
||||
->Event("GetVelocity", &CharacterRequests::GetVelocity, "Get Velocity")
|
||||
->Event("AddVelocity", &CharacterRequests::AddVelocity, "Add Velocity")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,9 +280,11 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy the entities *before* clearing the lookup maps so that any lookups triggered during an entity's destructor
|
||||
// are still valid.
|
||||
m_entities.clear();
|
||||
m_instanceToTemplateEntityIdMap.clear();
|
||||
m_templateToInstanceEntityIdMap.clear();
|
||||
m_entities.clear();
|
||||
}
|
||||
|
||||
bool Instance::RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias)
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ namespace AzToolsFramework
|
||||
"Prefab - EntityIdMapper: Entity with Id %s has no registered owning instance",
|
||||
entityId.ToString().c_str());
|
||||
|
||||
return AZStd::string::format("Entity_%s", entityId.ToString().c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
Instance* owningInstance = &(owningInstanceReference->get());
|
||||
|
||||
@@ -70,7 +70,15 @@ namespace AzToolsFramework
|
||||
"Failed to find an owning instance for the entity with id %llu.", static_cast<AZ::u64>(entityId));
|
||||
Instance& instance = instanceReference->get();
|
||||
m_templateId = instance.GetTemplateId();
|
||||
m_entityAlias = (instance.GetEntityAlias(entityId)).value();
|
||||
auto aliasReference = instance.GetEntityAlias(entityId);
|
||||
if (!aliasReference.has_value())
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", aliasReference.has_value(), "Failed to find the entity alias for entity %s.", entityId.ToString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
m_entityAlias = aliasReference.value();
|
||||
|
||||
//generate undo/redo patches
|
||||
m_instanceToTemplateInterface->GeneratePatch(m_redoPatch, initialState, endState);
|
||||
|
||||
+22
-5
@@ -488,6 +488,13 @@ namespace AzToolsFramework
|
||||
return worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, pivot));
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::SetAllViewportUiVisible(const bool visible)
|
||||
{
|
||||
SetViewportUiClusterVisible(m_transformModeClusterId, visible);
|
||||
SetViewportUiClusterVisible(m_spaceCluster.m_spaceClusterId, visible);
|
||||
m_viewportUiVisible = visible;
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::UpdateSpaceCluster(const ReferenceFrame referenceFrame)
|
||||
{
|
||||
auto buttonIdFromFrameFn = [this](const ReferenceFrame referenceFrame)
|
||||
@@ -1009,6 +1016,7 @@ namespace AzToolsFramework
|
||||
ToolsApplicationNotificationBus::Handler::BusConnect();
|
||||
Camera::EditorCameraNotificationBus::Handler::BusConnect();
|
||||
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(entityContextId);
|
||||
EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
EditorEntityVisibilityNotificationBus::Router::BusRouterConnect();
|
||||
EditorEntityLockComponentNotificationBus::Router::BusRouterConnect();
|
||||
EditorManipulatorCommandUndoRedoRequestBus::Handler::BusConnect(entityContextId);
|
||||
@@ -1037,6 +1045,7 @@ namespace AzToolsFramework
|
||||
EditorManipulatorCommandUndoRedoRequestBus::Handler::BusDisconnect();
|
||||
EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect();
|
||||
EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect();
|
||||
EditorEntityContextNotificationBus::Handler::BusDisconnect();
|
||||
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
|
||||
Camera::EditorCameraNotificationBus::Handler::BusDisconnect();
|
||||
ToolsApplicationNotificationBus::Handler::BusDisconnect();
|
||||
@@ -2387,9 +2396,7 @@ namespace AzToolsFramework
|
||||
/*ID_VIEWPORTUI_VISIBLE=*/50040, "Toggle Viewport UI", "Hide/Show Viewport UI",
|
||||
[this]()
|
||||
{
|
||||
SetViewportUiClusterVisible(m_transformModeClusterId, !m_viewportUiVisible);
|
||||
SetViewportUiClusterVisible(m_spaceCluster.m_spaceClusterId, !m_viewportUiVisible);
|
||||
m_viewportUiVisible = !m_viewportUiVisible;
|
||||
SetAllViewportUiVisible(!m_viewportUiVisible);
|
||||
});
|
||||
|
||||
EditorMenuRequestBus::Broadcast(&EditorMenuRequests::RestoreEditMenuToDefault);
|
||||
@@ -3560,7 +3567,7 @@ namespace AzToolsFramework
|
||||
|
||||
void EditorTransformComponentSelection::EnteredComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
|
||||
{
|
||||
SetViewportUiClusterVisible(m_transformModeClusterId, false);
|
||||
SetAllViewportUiVisible(false);
|
||||
|
||||
EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect();
|
||||
EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect();
|
||||
@@ -3569,7 +3576,7 @@ namespace AzToolsFramework
|
||||
|
||||
void EditorTransformComponentSelection::LeftComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
|
||||
{
|
||||
SetViewportUiClusterVisible(m_transformModeClusterId, true);
|
||||
SetAllViewportUiVisible(true);
|
||||
|
||||
ToolsApplicationNotificationBus::Handler::BusConnect();
|
||||
EditorEntityVisibilityNotificationBus::Router::BusRouterConnect();
|
||||
@@ -3625,6 +3632,16 @@ namespace AzToolsFramework
|
||||
ETCS::SetEntityLocalRotation(entityId, localRotation, m_transformChangedInternally);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::OnStartPlayInEditor()
|
||||
{
|
||||
SetAllViewportUiVisible(false);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::OnStopPlayInEditor()
|
||||
{
|
||||
SetAllViewportUiVisible(true);
|
||||
}
|
||||
|
||||
namespace ETCS
|
||||
{
|
||||
// little raii wrapper to switch a value from true to false and back
|
||||
|
||||
+9
-1
@@ -128,6 +128,7 @@ namespace AzToolsFramework
|
||||
, private ToolsApplicationNotificationBus::Handler
|
||||
, private Camera::EditorCameraNotificationBus::Handler
|
||||
, private ComponentModeFramework::EditorComponentModeNotificationBus::Handler
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
, private EditorEntityVisibilityNotificationBus::Router
|
||||
, private EditorEntityLockComponentNotificationBus::Router
|
||||
, private EditorManipulatorCommandUndoRedoRequestBus::Handler
|
||||
@@ -264,6 +265,10 @@ namespace AzToolsFramework
|
||||
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
|
||||
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
|
||||
|
||||
// EditorEntityContextNotificationBus overrides ...
|
||||
void OnStartPlayInEditor() override;
|
||||
void OnStopPlayInEditor() override;
|
||||
|
||||
// Helpers to safely interact with the TransformBus (requests).
|
||||
void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation);
|
||||
void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation);
|
||||
@@ -271,9 +276,12 @@ namespace AzToolsFramework
|
||||
void SetEntityLocalScale(AZ::EntityId entityId, float localScale);
|
||||
void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation);
|
||||
|
||||
// Responsible for keeping the space cluster in sync with the current reference frame.
|
||||
//! Responsible for keeping the space cluster in sync with the current reference frame.
|
||||
void UpdateSpaceCluster(ReferenceFrame referenceFrame);
|
||||
|
||||
//! Hides/Shows all viewportUi toolbars.
|
||||
void SetAllViewportUiVisible(bool visible);
|
||||
|
||||
AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any).
|
||||
AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display.
|
||||
AZ::EntityId m_editorCameraComponentEntityId; //!< The EditorCameraComponent EntityId if it is set.
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <NewProjectSettingsScreen.h>
|
||||
#include <ScreenHeaderWidget.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
@@ -46,6 +47,40 @@ namespace O3DE::ProjectManager
|
||||
m_stack->addWidget(m_gemCatalogScreen);
|
||||
vLayout->addWidget(m_stack);
|
||||
|
||||
// When there are multiple project templates present, we re-gather the gems when changing the selected the project template.
|
||||
connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex)
|
||||
{
|
||||
const GemModel* gemModel = m_gemCatalogScreen->GetGemModel();
|
||||
const QVector<QModelIndex> toBeAdded = gemModel->GatherGemsToBeAdded();
|
||||
const QVector<QModelIndex> toBeRemoved = gemModel->GatherGemsToBeRemoved();
|
||||
if (!toBeAdded.isEmpty() || !toBeRemoved.isEmpty())
|
||||
{
|
||||
// In case the user enabled or disabled any gem and the current selection does not match the default from the
|
||||
// // project template anymore, we need to ask the user if they want to proceed as their modifications will be lost.
|
||||
const QString title = tr("Modifications will be lost");
|
||||
const QString text = tr("You selected a new project template after modifying the enabled gems.\n\n"
|
||||
"All modifications will be lost and the default from the new project template will be used.\n\n"
|
||||
"Do you want to proceed?");
|
||||
if (QMessageBox::warning(this, title, text, QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
||||
{
|
||||
// The users wants to proceed. Reinitialize based on the newly selected project template.
|
||||
ReinitGemCatalogForSelectedTemplate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Roll-back to the previously selected project template and
|
||||
// block signals so that we don't end up in this same callback again.
|
||||
m_newProjectSettingsScreen->SelectProjectTemplate(oldIndex, /*blockSignals=*/true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// In case the user did not enable or disable any gem and the currently enabled gems matches the previously selected
|
||||
// ones from the project template, we can just reinitialize based on the newly selected project template.
|
||||
ReinitGemCatalogForSelectedTemplate();
|
||||
}
|
||||
});
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox();
|
||||
buttons->setObjectName("footer");
|
||||
vLayout->addWidget(buttons);
|
||||
@@ -81,10 +116,8 @@ namespace O3DE::ProjectManager
|
||||
currentScreen->NotifyCurrentScreen();
|
||||
}
|
||||
|
||||
// Gather the gems from the project template. When we will have multiple project templates, we need to re-gather them
|
||||
// on changing the template and let the user know that any further changes on top of the template will be lost.
|
||||
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
|
||||
m_gemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template", /*isNewProject=*/true);
|
||||
// Gather the enabled gems from the default project template when starting the create new project workflow.
|
||||
ReinitGemCatalogForSelectedTemplate();
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::HandleBackButton()
|
||||
@@ -223,4 +256,9 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::ReinitGemCatalogForSelectedTemplate()
|
||||
{
|
||||
const QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
|
||||
m_gemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template", /*isNewProject=*/true);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace O3DE::ProjectManager
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
void OnChangeScreenRequest(ProjectManagerScreen screen);
|
||||
void HandleSecondaryButton();
|
||||
void ReinitGemCatalogForSelectedTemplate();
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
private:
|
||||
|
||||
@@ -21,4 +21,9 @@ namespace O3DE::ProjectManager
|
||||
connect(browseButton, &QPushButton::pressed, this, &FormBrowseEditWidget::HandleBrowseButton);
|
||||
m_frameLayout->addWidget(browseButton);
|
||||
}
|
||||
|
||||
FormBrowseEditWidget::FormBrowseEditWidget(const QString& labelText, QWidget* parent)
|
||||
: FormBrowseEditWidget(labelText, "", parent)
|
||||
{
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
public:
|
||||
explicit FormBrowseEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
|
||||
explicit FormBrowseEditWidget(const QString& labelText = "", QWidget* parent = nullptr);
|
||||
~FormBrowseEditWidget() = default;
|
||||
|
||||
protected slots:
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
FormFolderBrowseEditWidget::FormFolderBrowseEditWidget(const QString& labelText, const QString& valueText, QWidget* parent)
|
||||
: FormBrowseEditWidget(labelText, valueText, parent)
|
||||
: FormBrowseEditWidget(labelText, parent)
|
||||
{
|
||||
setText(valueText);
|
||||
}
|
||||
|
||||
void FormFolderBrowseEditWidget::HandleBrowseButton()
|
||||
@@ -30,8 +31,14 @@ namespace O3DE::ProjectManager
|
||||
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
|
||||
if (!directory.isEmpty())
|
||||
{
|
||||
m_lineEdit->setText(directory);
|
||||
setText(directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FormFolderBrowseEditWidget::setText(const QString& text)
|
||||
{
|
||||
QString path = QDir::toNativeSeparators(text);
|
||||
FormBrowseEditWidget::setText(path);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace O3DE::ProjectManager
|
||||
explicit FormFolderBrowseEditWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
|
||||
~FormFolderBrowseEditWidget() = default;
|
||||
|
||||
void setText(const QString& text) override;
|
||||
|
||||
protected:
|
||||
void HandleBrowseButton() override;
|
||||
};
|
||||
|
||||
@@ -123,4 +123,14 @@ namespace O3DE::ProjectManager
|
||||
child->style()->polish(child);
|
||||
}
|
||||
}
|
||||
|
||||
void FormLineEditWidget::setText(const QString& text)
|
||||
{
|
||||
m_lineEdit->setText(text);
|
||||
}
|
||||
|
||||
void FormLineEditWidget::mousePressEvent([[maybe_unused]] QMouseEvent* event)
|
||||
{
|
||||
m_lineEdit->setFocus();
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -15,6 +15,7 @@ QT_FORWARD_DECLARE_CLASS(QLineEdit)
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
QT_FORWARD_DECLARE_CLASS(QFrame)
|
||||
QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
|
||||
QT_FORWARD_DECLARE_CLASS(QMouseEvent)
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
@@ -39,6 +40,8 @@ namespace O3DE::ProjectManager
|
||||
//! Returns a pointer to the underlying LineEdit.
|
||||
QLineEdit* lineEdit() const;
|
||||
|
||||
virtual void setText(const QString& text);
|
||||
|
||||
protected:
|
||||
QLabel* m_errorLabel = nullptr;
|
||||
QFrame* m_frame = nullptr;
|
||||
@@ -51,6 +54,8 @@ namespace O3DE::ProjectManager
|
||||
void onFocusOut();
|
||||
|
||||
private:
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
void refreshStyle();
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace O3DE::ProjectManager
|
||||
void ReinitForProject(const QString& projectPath, bool isNewProject);
|
||||
bool EnableDisableGemsForProject(const QString& projectPath);
|
||||
|
||||
GemModel* GetGemModel() const { return m_gemModel; }
|
||||
|
||||
private:
|
||||
void FillModel(const QString& projectPath, bool isNewProject);
|
||||
|
||||
|
||||
@@ -81,8 +81,14 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (button && button->property(k_templateIndexProperty).isValid())
|
||||
{
|
||||
int projectIndex = button->property(k_templateIndexProperty).toInt();
|
||||
UpdateTemplateDetails(m_templates.at(projectIndex));
|
||||
int projectTemplateIndex = button->property(k_templateIndexProperty).toInt();
|
||||
if (m_selectedTemplateIndex != projectTemplateIndex)
|
||||
{
|
||||
const int oldIndex = m_selectedTemplateIndex;
|
||||
m_selectedTemplateIndex = projectTemplateIndex;
|
||||
UpdateTemplateDetails(m_templates.at(m_selectedTemplateIndex));
|
||||
emit OnTemplateSelectionChanged(/*oldIndex=*/oldIndex, /*newIndex=*/m_selectedTemplateIndex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -115,7 +121,8 @@ namespace O3DE::ProjectManager
|
||||
flowLayout->addWidget(templateButton);
|
||||
}
|
||||
|
||||
m_projectTemplateButtonGroup->buttons().first()->setChecked(true);
|
||||
// Select the first project template (default selection).
|
||||
SelectProjectTemplate(0, /*blockSignals=*/true);
|
||||
}
|
||||
containerLayout->addWidget(templatesScrollArea);
|
||||
}
|
||||
@@ -159,8 +166,9 @@ namespace O3DE::ProjectManager
|
||||
|
||||
QString NewProjectSettingsScreen::GetProjectTemplatePath()
|
||||
{
|
||||
const int templateIndex = m_projectTemplateButtonGroup->checkedButton()->property(k_templateIndexProperty).toInt();
|
||||
return m_templates.at(templateIndex).m_path;
|
||||
AZ_Assert(m_selectedTemplateIndex == m_projectTemplateButtonGroup->checkedButton()->property(k_templateIndexProperty).toInt(),
|
||||
"Selected template index not in sync with the currently checked project template button.");
|
||||
return m_templates.at(m_selectedTemplateIndex).m_path;
|
||||
}
|
||||
|
||||
QFrame* NewProjectSettingsScreen::CreateTemplateDetails(int margin)
|
||||
@@ -216,4 +224,27 @@ namespace O3DE::ProjectManager
|
||||
m_templateSummary->setText(templateInfo.m_summary);
|
||||
m_templateIncludedGems->Update(templateInfo.m_includedGems);
|
||||
}
|
||||
|
||||
void NewProjectSettingsScreen::SelectProjectTemplate(int index, bool blockSignals)
|
||||
{
|
||||
const QList<QAbstractButton*> buttons = m_projectTemplateButtonGroup->buttons();
|
||||
if (index >= buttons.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (blockSignals)
|
||||
{
|
||||
m_projectTemplateButtonGroup->blockSignals(true);
|
||||
}
|
||||
|
||||
QAbstractButton* button = buttons.at(index);
|
||||
button->setChecked(true);
|
||||
m_selectedTemplateIndex = button->property(k_templateIndexProperty).toInt();
|
||||
|
||||
if (blockSignals)
|
||||
{
|
||||
m_projectTemplateButtonGroup->blockSignals(false);
|
||||
}
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace O3DE::ProjectManager
|
||||
class NewProjectSettingsScreen
|
||||
: public ProjectSettingsScreen
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NewProjectSettingsScreen(QWidget* parent = nullptr);
|
||||
~NewProjectSettingsScreen() = default;
|
||||
@@ -31,6 +33,11 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void NotifyCurrentScreen() override;
|
||||
|
||||
void SelectProjectTemplate(int index, bool blockSignals = false);
|
||||
|
||||
signals:
|
||||
void OnTemplateSelectionChanged(int oldIndex, int newIndex);
|
||||
|
||||
private:
|
||||
QString GetDefaultProjectPath();
|
||||
QFrame* CreateTemplateDetails(int margin);
|
||||
@@ -41,6 +48,7 @@ namespace O3DE::ProjectManager
|
||||
QLabel* m_templateSummary;
|
||||
TagContainerWidget* m_templateIncludedGems;
|
||||
QVector<ProjectTemplateInfo> m_templates;
|
||||
int m_selectedTemplateIndex = -1;
|
||||
|
||||
inline constexpr static int s_spacerSize = 20;
|
||||
inline constexpr static int s_templateDetailsContentMargin = 20;
|
||||
|
||||
@@ -92,8 +92,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
QLabel* introLabel = new QLabel(this);
|
||||
introLabel->setObjectName("introLabel");
|
||||
introLabel->setText(tr("Welcome to O3DE! Start something new by creating a project. Not sure what to create? \nExplore what's "
|
||||
"available by downloading our sample project."));
|
||||
introLabel->setText(tr("Welcome to O3DE! Start something new by creating a project."));
|
||||
layout->addWidget(introLabel);
|
||||
|
||||
QHBoxLayout* buttonLayout = new QHBoxLayout();
|
||||
|
||||
@@ -36,63 +36,49 @@ namespace AZ
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpBoneImporter, SceneCore::LoadingComponent>()->Version(1);
|
||||
serializeContext->Class<AssImpBoneImporter, SceneCore::LoadingComponent>()->Version(2);
|
||||
}
|
||||
}
|
||||
|
||||
void EnumBonesInNode(
|
||||
const aiScene* scene, const aiNode* node, AZStd::unordered_map<AZStd::string, const aiNode*>& mainBoneList,
|
||||
AZStd::unordered_map<AZStd::string, const aiBone*>& boneLookup)
|
||||
void MakeBoneMap(const aiScene* scene, AZStd::unordered_map<AZStd::string, const aiBone*>& boneLookup)
|
||||
{
|
||||
/* From AssImp Documentation
|
||||
a) Create a map or a similar container to store which nodes are necessary for the skeleton. Pre-initialise it for all nodes with a "no".
|
||||
b) For each bone in the mesh:
|
||||
b1) Find the corresponding node in the scene's hierarchy by comparing their names.
|
||||
b2) Mark this node as "yes" in the necessityMap.
|
||||
b3) Mark all of its parents the same way until you 1) find the mesh's node or 2) the parent of the mesh's node.
|
||||
c) Recursively iterate over the node hierarchy
|
||||
c1) If the node is marked as necessary, copy it into the skeleton and check its children
|
||||
c2) If the node is marked as not necessary, skip it and do not iterate over its children.
|
||||
*/
|
||||
AZStd::queue<const aiNode*> queue;
|
||||
AZStd::unordered_set<AZStd::string> nodesWithNoMesh;
|
||||
|
||||
for (unsigned meshIndex = 0; meshIndex < node->mNumMeshes; ++meshIndex)
|
||||
queue.push(scene->mRootNode);
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[node->mMeshes[meshIndex]];
|
||||
const aiNode* currentNode = queue.front();
|
||||
queue.pop();
|
||||
|
||||
for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
|
||||
if (currentNode->mNumMeshes == 0)
|
||||
{
|
||||
nodesWithNoMesh.emplace(currentNode->mName.C_Str());
|
||||
}
|
||||
|
||||
for (int childIndex = 0; childIndex < currentNode->mNumChildren; ++childIndex)
|
||||
{
|
||||
queue.push(currentNode->mChildren[childIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[meshIndex];
|
||||
|
||||
for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
|
||||
{
|
||||
const aiBone* bone = mesh->mBones[boneIndex];
|
||||
|
||||
const aiNode* boneNode = scene->mRootNode->FindNode(bone->mName);
|
||||
const aiNode* boneParent = boneNode->mParent;
|
||||
|
||||
mainBoneList[bone->mName.C_Str()] = boneNode;
|
||||
boneLookup[bone->mName.C_Str()] = bone;
|
||||
|
||||
while (boneParent && boneParent != node && boneParent != node->mParent && boneParent != scene->mRootNode)
|
||||
if (nodesWithNoMesh.contains(bone->mName.C_Str()))
|
||||
{
|
||||
mainBoneList[boneParent->mName.C_Str()] = boneParent;
|
||||
|
||||
boneParent = boneParent->mParent;
|
||||
boneLookup.emplace(bone->mName.C_Str(), bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EnumChildren(
|
||||
const aiScene* scene, const aiNode* node, AZStd::unordered_map<AZStd::string, const aiNode*>& mainBoneList,
|
||||
AZStd::unordered_map<AZStd::string, const aiBone*>& boneLookup)
|
||||
{
|
||||
EnumBonesInNode(scene, node, mainBoneList, boneLookup);
|
||||
|
||||
for (unsigned childIndex = 0; childIndex < node->mNumChildren; ++childIndex)
|
||||
{
|
||||
const aiNode* child = node->mChildren[childIndex];
|
||||
|
||||
EnumChildren(scene, child, mainBoneList, boneLookup);
|
||||
}
|
||||
}
|
||||
|
||||
aiMatrix4x4 CalculateWorldTransform(const aiNode* currentNode)
|
||||
{
|
||||
aiMatrix4x4 transform = {};
|
||||
@@ -122,14 +108,10 @@ namespace AZ
|
||||
bool isBone = false;
|
||||
|
||||
{
|
||||
AZStd::unordered_map<AZStd::string, const aiNode*> mainBoneList;
|
||||
AZStd::unordered_map<AZStd::string, const aiBone*> boneLookup;
|
||||
EnumChildren(scene, scene->mRootNode, mainBoneList, boneLookup);
|
||||
MakeBoneMap(scene, boneLookup);
|
||||
|
||||
if (mainBoneList.find(currentNode->mName.C_Str()) != mainBoneList.end())
|
||||
{
|
||||
isBone = true;
|
||||
}
|
||||
isBone = boneLookup.contains(currentNode->mName.C_Str());
|
||||
|
||||
// If we have an animation, the bones will be listed in there
|
||||
if (!isBone)
|
||||
|
||||
@@ -42,9 +42,29 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void GetAllBones(
|
||||
const aiScene* scene, AZStd::unordered_multimap<AZStd::string, const aiBone*>& boneLookup)
|
||||
void GetAllBones(const aiScene* scene, AZStd::unordered_multimap<AZStd::string, const aiBone*>& boneLookup)
|
||||
{
|
||||
AZStd::queue<const aiNode*> queue;
|
||||
AZStd::unordered_set<AZStd::string> nodesWithNoMesh;
|
||||
|
||||
queue.push(scene->mRootNode);
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
const aiNode* currentNode = queue.front();
|
||||
queue.pop();
|
||||
|
||||
if (currentNode->mNumMeshes == 0)
|
||||
{
|
||||
nodesWithNoMesh.emplace(currentNode->mName.C_Str());
|
||||
}
|
||||
|
||||
for (int childIndex = 0; childIndex < currentNode->mNumChildren; ++childIndex)
|
||||
{
|
||||
queue.push(currentNode->mChildren[childIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[meshIndex];
|
||||
@@ -53,7 +73,10 @@ namespace AZ
|
||||
{
|
||||
const aiBone* bone = mesh->mBones[boneIndex];
|
||||
|
||||
boneLookup.emplace(bone->mName.C_Str(), bone);
|
||||
if (nodesWithNoMesh.contains(bone->mName.C_Str()))
|
||||
{
|
||||
boneLookup.emplace(bone->mName.C_Str(), bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,13 +208,6 @@ namespace AZ
|
||||
bool SliceConverter::ConvertSliceToPrefab(
|
||||
AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity)
|
||||
{
|
||||
/* Given a root slice entity, we convert it to a prefab by doing the following:
|
||||
* - Locate the SliceComponent
|
||||
* - Take all the entities directly located on the slice, and put them into a prefab
|
||||
* - Fix up any top-level entities to have the prefab container entity as their parent
|
||||
* - If there are any nested slice instances, convert the nested slices to prefabs, then convert the instances.
|
||||
*/
|
||||
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
// Find the slice from the root entity.
|
||||
@@ -233,23 +226,47 @@ namespace AZ
|
||||
sliceComponent->RemoveAllEntities(deleteEntities, removeEmptyInstances);
|
||||
AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size());
|
||||
|
||||
// Create the Prefab with the entities from the slice.
|
||||
// Create an empty Prefab as the start of our conversion.
|
||||
// The entities are added in a separate step so that we can give them deterministic entity aliases that match their entity Ids
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sourceInstance(
|
||||
prefabSystemComponentInterface->CreatePrefab({}, {}, outputPath));
|
||||
|
||||
// Add entities into our prefab.
|
||||
// In slice->prefab conversions, there's a chicken-and-egg problem that occurs with entity references, so we're initially
|
||||
// going to add empty dummy entities with the right IDs and aliases.
|
||||
// The problem is that we can have entities in this root list that have references to nested slice instance entities that we
|
||||
// haven't created yet, and we will have nested slice entities that need to reference these entities as parents.
|
||||
// If we create these entities as fully-formed first, they will fail to serialize correctly when adding each nested instance,
|
||||
// due to the references not pointing to valid entities yet. And if we *wait* to create these and build the nested instances
|
||||
// first, they'll fail to serialize correctly due to referencing these as parents.
|
||||
// So our solution is that we'll initially create these entities as empty placeholders with no references, *then* we'll build
|
||||
// up the nested instances, *then* we'll finish building these entities out.
|
||||
|
||||
// prefabPlaceholderEntities will hold onto pointers to the entities we're building up in the prefab. The prefab will own
|
||||
// the lifetime of them, but we'll use the references here for convenient access.
|
||||
AZStd::vector<AZ::Entity*> prefabPlaceholderEntities;
|
||||
// entityAliases will hold onto the alias we want to use for each of those entities. We'll need to use the same alias when
|
||||
// we replace the entities at the end.
|
||||
AZStd::vector<AZStd::string> entityAliases;
|
||||
for (auto& entity : sliceEntities)
|
||||
{
|
||||
sourceInstance->AddEntity(*entity, AZStd::string::format("Entity_%s", entity->GetId().ToString().c_str()));
|
||||
auto id = entity->GetId();
|
||||
prefabPlaceholderEntities.emplace_back(aznew AZ::Entity(id));
|
||||
entityAliases.emplace_back(AZStd::string::format("Entity_%s", id.ToString().c_str()));
|
||||
sourceInstance->AddEntity(*(prefabPlaceholderEntities.back()), entityAliases.back());
|
||||
|
||||
// Save off a mapping of the original slice entity IDs to the new prefab template entity aliases.
|
||||
// We'll need this mapping for fixing up all the entity references in this slice as well as any nested instances.
|
||||
auto result = m_aliasIdMapper.emplace(id, SliceEntityMappingInfo(sourceInstance->GetTemplateId(), entityAliases.back()));
|
||||
if (!result.second)
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " Duplicate entity alias -> entity id entries found, conversion may not be successful.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch events here, because prefab creation might trigger asset loads in rare circumstances.
|
||||
AZ::Data::AssetManager::Instance().DispatchEvents();
|
||||
|
||||
// Fix up the container entity to have the proper components and fix up the slice entities to have the proper hierarchy
|
||||
// with the container as the top-most parent.
|
||||
AzToolsFramework::Prefab::EntityOptionalReference container = sourceInstance->GetContainerEntity();
|
||||
FixPrefabEntities(container->get(), sliceEntities);
|
||||
|
||||
// Keep track of the template Id we created, we're going to remove it at the end of slice file conversion to make sure
|
||||
// the data doesn't stick around between file conversions.
|
||||
auto templateId = sourceInstance->GetTemplateId();
|
||||
@@ -260,26 +277,8 @@ namespace AZ
|
||||
}
|
||||
m_createdTemplateIds.emplace(templateId);
|
||||
|
||||
// Save off a mapping of the original slice entity IDs to the new prefab template entity aliases.
|
||||
// When converting nested slices, this mapping will be needed to fix up the parent entity hierarchy correctly.
|
||||
auto entityAliases = sourceInstance->GetEntityAliases();
|
||||
for (auto& alias : entityAliases)
|
||||
{
|
||||
auto id = sourceInstance->GetEntityId(alias);
|
||||
auto result = m_aliasIdMapper.emplace(id, SliceEntityMappingInfo(templateId, alias));
|
||||
if (!result.second)
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " Duplicate entity alias -> entity id entries found, conversion may not be successful.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Save off a mapping of the slice's metadata entity ID as well, even though we never converted the entity itself.
|
||||
// This will help us better detect entity ID mapping errors for nested slice instances.
|
||||
AZ::Entity* metadataEntity = sliceComponent->GetMetadataEntity();
|
||||
constexpr bool isMetadataEntity = true;
|
||||
m_aliasIdMapper.emplace(metadataEntity->GetId(), SliceEntityMappingInfo(templateId, "MetadataEntity", isMetadataEntity));
|
||||
|
||||
// Update the prefab template with the fixed-up data in our prefab instance.
|
||||
// Save off the the first version of this prefab template with our empty placeholder entities.
|
||||
// As it saves off, the entities will all change IDs during serialization / propagation, but the aliases will remain the same.
|
||||
AzToolsFramework::Prefab::PrefabDom prefabDom;
|
||||
bool storeResult = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*sourceInstance, prefabDom);
|
||||
if (storeResult == false)
|
||||
@@ -288,10 +287,20 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
prefabSystemComponentInterface->UpdatePrefabTemplate(templateId, prefabDom);
|
||||
AZ::Interface<AzToolsFramework::Prefab::InstanceUpdateExecutorInterface>::Get()->UpdateTemplateInstancesInQueue();
|
||||
|
||||
// Dispatch events here, because prefab serialization might trigger asset loads in rare circumstances.
|
||||
AZ::Data::AssetManager::Instance().DispatchEvents();
|
||||
|
||||
// Save off a mapping of the slice's metadata entity ID as well, even though we never converted the entity itself.
|
||||
// This will help us better detect entity ID mapping errors for nested slice instances.
|
||||
AZ::Entity* metadataEntity = sliceComponent->GetMetadataEntity();
|
||||
constexpr bool isMetadataEntity = true;
|
||||
m_aliasIdMapper.emplace(metadataEntity->GetId(), SliceEntityMappingInfo(templateId, "MetadataEntity", isMetadataEntity));
|
||||
|
||||
// Also save off a mapping of the prefab's container entity ID.
|
||||
m_aliasIdMapper.emplace(sourceInstance->GetContainerEntityId(), SliceEntityMappingInfo(templateId, "ContainerEntity"));
|
||||
|
||||
// If this slice has nested slices, we need to loop through those, convert them to prefabs as well, and
|
||||
// set up the new nesting relationships correctly.
|
||||
const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices();
|
||||
@@ -305,6 +314,51 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
// *After* converting the nested slices, remove our placeholder entities and replace them with the correct ones.
|
||||
// The placeholder entity IDs will have changed from what we originally created, so we need to make sure our replacement
|
||||
// entities have the same IDs and aliases as the placeholders so that any instance references that have already been fixed
|
||||
// up continue to reference the correct entities here.
|
||||
for (size_t curEntityIdx = 0; curEntityIdx < sliceEntities.size(); curEntityIdx++)
|
||||
{
|
||||
auto& sliceEntity = sliceEntities[curEntityIdx];
|
||||
auto& prefabEntity = prefabPlaceholderEntities[curEntityIdx];
|
||||
sliceEntity->SetId(prefabEntity->GetId());
|
||||
}
|
||||
// Remove and delete our placeholder entities.
|
||||
// (By using an empty callback on DetachEntities, the unique_ptr will auto-delete the placeholder entities)
|
||||
sourceInstance->DetachEntities([](AZStd::unique_ptr<AZ::Entity>){});
|
||||
prefabPlaceholderEntities.clear();
|
||||
for (size_t curEntityIdx = 0; curEntityIdx < sliceEntities.size(); curEntityIdx++)
|
||||
{
|
||||
sourceInstance->AddEntity(*(sliceEntities[curEntityIdx]), entityAliases[curEntityIdx]);
|
||||
}
|
||||
|
||||
// Fix up the container entity to have the proper components and fix up the slice entities to have the proper hierarchy
|
||||
// with the container as the top-most parent.
|
||||
AzToolsFramework::Prefab::EntityOptionalReference container = sourceInstance->GetContainerEntity();
|
||||
FixPrefabEntities(container->get(), sliceEntities);
|
||||
|
||||
// Also save off a mapping of the prefab's container entity ID.
|
||||
m_aliasIdMapper.emplace(sourceInstance->GetContainerEntityId(), SliceEntityMappingInfo(templateId, "ContainerEntity"));
|
||||
|
||||
// Remap all of the entity references that exist in these top-level slice entities.
|
||||
SliceComponent::InstantiatedContainer instantiatedEntities(false);
|
||||
instantiatedEntities.m_entities = sliceEntities;
|
||||
RemapIdReferences(m_aliasIdMapper, sourceInstance.get(), sourceInstance.get(), &instantiatedEntities, serializeContext);
|
||||
|
||||
// Finally, store the completed slice->prefab conversion back into the template.
|
||||
storeResult = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*sourceInstance, prefabDom);
|
||||
if (storeResult == false)
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " Failed to convert prefab instance data to a PrefabDom.\n");
|
||||
return false;
|
||||
}
|
||||
prefabSystemComponentInterface->UpdatePrefabTemplate(templateId, prefabDom);
|
||||
AZ::Interface<AzToolsFramework::Prefab::InstanceUpdateExecutorInterface>::Get()->UpdateTemplateInstancesInQueue();
|
||||
|
||||
// Dispatch events here, because prefab serialization might trigger asset loads in rare circumstances.
|
||||
AZ::Data::AssetManager::Instance().DispatchEvents();
|
||||
|
||||
if (isDryRun)
|
||||
{
|
||||
PrintPrefab(templateId);
|
||||
@@ -417,7 +471,7 @@ namespace AZ
|
||||
|
||||
auto instances = slice.GetInstances();
|
||||
AZ_Printf(
|
||||
"Convert-Slice", " Attaching %zu instances of nested slice '%s'.\n", instances.size(),
|
||||
"Convert-Slice", "Attaching %zu instances of nested slice '%s'.\n", instances.size(),
|
||||
nestedPrefabPath.Native().c_str());
|
||||
|
||||
// Before processing any further, save off all the known entity IDs from all the instances and how they map back to
|
||||
@@ -435,14 +489,20 @@ namespace AZ
|
||||
}
|
||||
|
||||
// Now that we have all the entity ID mappings, convert all the instances.
|
||||
size_t curInstance = 0;
|
||||
for (auto& instance : instances)
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " Converting instance %zu.\n", curInstance++);
|
||||
bool instanceConvertResult = ConvertSliceInstance(instance, sliceAsset, nestedTemplate, sourceInstance);
|
||||
if (!instanceConvertResult)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Printf(
|
||||
"Convert-Slice", "Finished attaching %zu instances of nested slice '%s'.\n", instances.size(),
|
||||
nestedPrefabPath.Native().c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -507,6 +567,10 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save off a mapping of the new nested Instance's container ID
|
||||
m_aliasIdMapper.emplace(nestedInstance->GetContainerEntityId(),
|
||||
SliceEntityMappingInfo(nestedInstance->GetTemplateId(), "ContainerEntity"));
|
||||
|
||||
// Get the DOM for the unmodified nested instance. This will be used later below for generating the correct patch
|
||||
// to the top-level template DOM.
|
||||
AzToolsFramework::Prefab::PrefabDom unmodifiedNestedInstanceDom;
|
||||
@@ -595,7 +659,7 @@ namespace AZ
|
||||
auto parentId = transformComponent->GetParentId();
|
||||
if (parentId.IsValid())
|
||||
{
|
||||
// Look to see if the parent ID exists in the same instance (i.e. an entity in the nested slice is a
|
||||
// Look to see if the parent ID exists in a different instance (i.e. an entity in the nested slice is a
|
||||
// child of an entity in the containing slice). If this case exists, we need to adjust the parents so that
|
||||
// the child entity connects to the prefab container, and the *container* is the child of the entity in the
|
||||
// containing slice. (i.e. go from A->B to A->container->B)
|
||||
@@ -607,6 +671,7 @@ namespace AZ
|
||||
{
|
||||
if (topLevelInstance->GetTemplateId() == parentMappingInfo.m_templateId)
|
||||
{
|
||||
// This entity has a parent from the topLevelInstance, so get its parent ID.
|
||||
parentId = topLevelInstance->GetEntityId(parentMappingInfo.m_entityAlias);
|
||||
}
|
||||
else
|
||||
@@ -630,15 +695,23 @@ namespace AZ
|
||||
}
|
||||
|
||||
// Set the container's parent to this entity's parent, and set this entity's parent to the container
|
||||
// auto newParentId = topLevelInstance->GetEntityId(parentMappingInfo.m_entityAlias);
|
||||
SetParentEntity(containerEntity->get(), parentId, false);
|
||||
onlySetIfInvalid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the parent ID is valid and exists inside the same slice instance (i.e. template IDs are equal)
|
||||
// then it's just a nested entity hierarchy inside the slice and we don't need to adjust anything.
|
||||
// "onlySetIfInvalid" will still be true, which means we won't change the parent ID below.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the parent ID is set to something valid, but we can't find it in our ID mapper, something went wrong.
|
||||
// We'll assert, but don't change the container entity's parent below.
|
||||
AZ_Assert(false, "Could not find parent entity id: %s", parentId.ToString().c_str());
|
||||
}
|
||||
|
||||
// If the parent ID is valid, but NOT in the top-level instance, then it's just a nested hierarchy inside
|
||||
// the slice and we don't need to adjust anything. "onlySetIfInvalid" will still be true, which means we
|
||||
// won't change the parent ID below.
|
||||
}
|
||||
|
||||
SetParentEntity(*entity, containerEntityId, onlySetIfInvalid);
|
||||
@@ -846,9 +919,10 @@ namespace AZ
|
||||
{
|
||||
auto entityEntry = idMapper.find(sourceId);
|
||||
|
||||
// Since we've already remapped transform hierarchies to include container entities, it's possible that our entity
|
||||
// reference is pointing to a container, which means it won't be in our slice mapping table. In that case, just
|
||||
// return it as-is.
|
||||
// The id mapping table should include all of our known slice entities, slice metadata entities, and prefab
|
||||
// container entities. If we can't find the entity reference, it should either be because it's actually invalid
|
||||
// in the source data or because it's a transform parent id that we've already remapped prior to this point.
|
||||
// Either way, just keep it as-is and return it.
|
||||
if (entityEntry == idMapper.end())
|
||||
{
|
||||
return sourceId;
|
||||
@@ -876,6 +950,7 @@ namespace AZ
|
||||
else
|
||||
{
|
||||
AZ_Error("Convert-Slice", false, " Couldn't find source ID %s", sourceId.ToString().c_str());
|
||||
newId = sourceId;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user