Merge branch 'main' into hasareej_LYN-2475_viewportui_switcher

This commit is contained in:
hultonha
2021-04-22 17:51:28 +01:00
3269 changed files with 80003 additions and 576052 deletions
@@ -578,16 +578,6 @@ namespace AzToolsFramework
*/
virtual bool IsEditorInIsolationMode() = 0;
/*!
* Get the engine root path that the current tool is running under.
*/
virtual const char* GetEngineRootPath() const = 0;
/**
* Get the version of the engine the current tools application is running under
*/
virtual const char* GetEngineVersion() const = 0;
/**
* Creates and adds a new entity to the tools application from components which match at least one of the requiredTags
* The tag matching occurs on AZ::Edit::SystemComponentTags attribute from the reflected class data in the serialization context
@@ -601,7 +591,7 @@ namespace AzToolsFramework
virtual ResolveToolPathOutcome ResolveConfigToolsPath(const char* toolApplicationName) const = 0;
/**
* LUMBERYARD INTERNAL USE ONLY.
* Open 3D Engine Internal use only.
*
* Run a specific redo command separate from the undo/redo system.
* In many cases before a modifcation on an entity takes place, it is first packaged into
@@ -825,8 +815,6 @@ namespace AzToolsFramework
/// Hide or show the circular dependency error when saving slices
virtual void SetShowCircularDependencyError(const bool& /*showCircularDependencyError*/) {}
virtual void SetEditTool(const char* /*tool*/) {}
/// Launches the Lua editor and opens the specified (space separated) files.
virtual void LaunchLuaEditor(const char* /*files*/) {}
@@ -92,7 +92,7 @@ namespace AzToolsFramework
namespace Internal
{
static const char* s_engineConfigFileName = "engine.json";
static const char* s_engineConfigEngineVersionKey = "LumberyardVersion";
static const char* s_engineConfigEngineVersionKey = "O3DEVersion";
static const char* s_startupLogWindow = "Startup";
@@ -224,112 +224,6 @@ namespace AzToolsFramework
} // Internal
#define AZ_MAX_ENGINE_VERSION_LEN 64
// Private Implementation class to manage the engine root and version
// Note: We are not using any AzCore classes because the ToolsApplication
// initialization happens early on, before the Allocators get instantiated,
// so we are using Qt privately instead
class ToolsApplication::EngineConfigImpl
{
private:
friend class ToolsApplication;
typedef QMap<QString, QString> EngineJsonMap;
EngineConfigImpl(const char* logWindow, const char* fileName)
: m_logWindow(logWindow)
, m_fileName(fileName)
{
m_engineRoot[0] = '\0';
m_engineVersion[0] = '\0';
}
char m_engineRoot[AZ_MAX_PATH_LEN];
char m_engineVersion[AZ_MAX_ENGINE_VERSION_LEN];
EngineJsonMap m_engineConfigMap;
const char* m_logWindow;
const char* m_fileName;
// Read an engine configuration into a map of key/value pairs
bool ReadEngineConfigIntoMap(QString engineJsonPath, EngineJsonMap& engineJsonMap)
{
QFile engineJsonFile(engineJsonPath);
if (!engineJsonFile.open(QIODevice::ReadOnly | QIODevice::Text))
{
AZ_Warning(m_logWindow, false, "Unable to open file '%s' in the current root directory", engineJsonPath.toUtf8().data());
return false;
}
QByteArray engineJsonData = engineJsonFile.readAll();
engineJsonFile.close();
QJsonDocument engineJsonDoc(QJsonDocument::fromJson(engineJsonData));
if (engineJsonDoc.isNull())
{
AZ_Warning(m_logWindow, false, "Unable to read file '%s' in the current root directory", engineJsonPath.toUtf8().data());
return false;
}
QJsonObject engineJsonRoot = engineJsonDoc.object();
for (const QString& configKey : engineJsonRoot.keys())
{
QJsonValue configValue = engineJsonRoot[configKey];
if (configValue.isString() || configValue.isDouble())
{
// Only map strings and numbers, ignore every other type
engineJsonMap[configKey] = configValue.toString();
}
else
{
AZ_Warning(m_logWindow, false, "Ignoring key '%s' from '%s', unsupported type.", configKey.toUtf8().data(), engineJsonPath.toUtf8().data());
}
}
return true;
}
// Initialize the engine config object based on the current
bool Initialize(const char* currentEngineRoot)
{
// Start with the app root as the engine root (legacy), but check to see if the engine root
// is external to the app root
azstrncpy(m_engineRoot, AZ_ARRAY_SIZE(m_engineRoot), currentEngineRoot, strlen(currentEngineRoot) + 1);
// From the appRoot, check and see if we can read any external engine reference in engine.json
QString engineJsonFileName = QString(m_fileName);
QString engineJsonFilePath = QDir(currentEngineRoot).absoluteFilePath(engineJsonFileName);
// From the appRoot, check and see if we can read any external engine reference in engine.json
if (!QFile::exists(engineJsonFilePath))
{
AZ_Warning(m_logWindow, false, "Unable to find '%s' in the current app root directory.", m_fileName);
return false;
}
if (!ReadEngineConfigIntoMap(engineJsonFilePath, m_engineConfigMap))
{
AZ_Warning(m_logWindow, false, "Defaulting root engine path to '%s'", currentEngineRoot);
return false;
}
// Read in the local engine version value
auto localEngineVersionValue = m_engineConfigMap.find(QString(AzToolsFramework::Internal::s_engineConfigEngineVersionKey));
QString localEngineVersion(localEngineVersionValue.value());
azstrncpy(m_engineVersion, AZ_ARRAY_SIZE(m_engineVersion), localEngineVersion.toUtf8().data(), localEngineVersion.length() + 1);
return true;
}
const char* GetEngineRoot() const
{
return m_engineRoot;
}
const char* GetEngineVersion() const
{
return m_engineVersion;
}
};
ToolsApplication::ToolsApplication(int* argc, char*** argv)
: AzFramework::Application(argc, argv)
, m_selectionBounds(AZ::Aabb())
@@ -339,7 +233,6 @@ namespace AzToolsFramework
, m_isInIsolationMode(false)
{
ToolsApplicationRequests::Bus::Handler::BusConnect();
m_engineConfigImpl.reset(new ToolsApplication::EngineConfigImpl(AzToolsFramework::Internal::s_startupLogWindow, AzToolsFramework::Internal::s_engineConfigFileName));
m_undoCache.RegisterToUndoCacheInterface();
}
@@ -391,7 +284,6 @@ namespace AzToolsFramework
void ToolsApplication::Start(const Descriptor& descriptor, const StartupParameters& startupParameters/* = StartupParameters()*/)
{
Application::Start(descriptor, startupParameters);
InitializeEngineConfig();
m_editorEntityManager.Start();
@@ -399,14 +291,6 @@ namespace AzToolsFramework
AZ_Assert(m_editorEntityAPI, "ToolsApplication - Could not retrieve instance of EditorEntityAPI");
}
void ToolsApplication::InitializeEngineConfig()
{
if (!m_engineConfigImpl->Initialize(GetEngineRoot()))
{
AZ_Warning(AzToolsFramework::Internal::s_startupLogWindow, false, "Defaulting engine root path to '%s'", GetEngineRoot());
}
}
void ToolsApplication::StartCommon(AZ::Entity* systemEntity)
{
Application::StartCommon(systemEntity);
@@ -1832,16 +1716,6 @@ namespace AzToolsFramework
return m_isInIsolationMode;
}
const char* ToolsApplication::GetEngineRootPath() const
{
return m_engineConfigImpl->GetEngineRoot();
}
const char* ToolsApplication::GetEngineVersion() const
{
return m_engineConfigImpl->GetEngineVersion();
}
void ToolsApplication::CreateAndAddEntityFromComponentTags(const AZStd::vector<AZ::Crc32>& requiredTags, const char* entityName)
{
if (!entityName || !entityName[0])
@@ -150,12 +150,10 @@ namespace AzToolsFramework
void EnterEditorIsolationMode() override;
void ExitEditorIsolationMode() override;
bool IsEditorInIsolationMode() override;
const char* GetEngineRootPath() const override;
const char* GetEngineVersion() const override;
void CreateAndAddEntityFromComponentTags(const AZStd::vector<AZ::Crc32>& requiredTags, const char* entityName) override;
/* LUMBERYARD INTERNAL USE ONLY. */
/* Open 3D Engine INTERNAL USE ONLY. */
void RunRedoSeparately(UndoSystem::URSequencePoint* redoCommand) override;
//////////////////////////////////////////////////////////////////////////
@@ -174,7 +172,6 @@ namespace AzToolsFramework
void CreateUndosForDirtyEntities();
void ConsistencyCheckUndoCache();
void InitializeEngineConfig();
AZ::Aabb m_selectionBounds;
EntityIdList m_selectedEntities;
EntityIdList m_highlightedEntities;
@@ -186,9 +183,6 @@ namespace AzToolsFramework
bool m_isInIsolationMode;
EntityIdSet m_isolatedEntityIdSet;
class EngineConfigImpl;
AZStd::unique_ptr<EngineConfigImpl> m_engineConfigImpl;
EditorEntityAPI* m_editorEntityAPI = nullptr;
EditorEntityManager m_editorEntityManager;
@@ -255,10 +255,8 @@ namespace AzToolsFramework::AssetUtils
return configFiles;
}
bool UpdateFilePathToCorrectCase(const QString& root, QString& relativePathFromRoot)
bool UpdateFilePathToCorrectCase(AZStd::string_view rootPath, AZStd::string& relPathFromRoot)
{
AZStd::string rootPath(root.toUtf8().data());
AZStd::string relPathFromRoot(relativePathFromRoot.toUtf8().data());
AZ::StringFunc::Path::Normalize(relPathFromRoot);
AZStd::vector<AZStd::string> tokens;
AZ::StringFunc::Tokenize(relPathFromRoot.c_str(), tokens, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
@@ -321,7 +319,6 @@ namespace AzToolsFramework::AssetUtils
{
relPathFromRoot.clear();
AZ::StringFunc::Join(relPathFromRoot, tokens.begin(), tokens.end(), AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
relativePathFromRoot = relPathFromRoot.c_str();
}
return success;
@@ -52,5 +52,5 @@ namespace AzToolsFramework::AssetUtils
//! which will be normalized and updated to be correct casing.
//! @return if such a file does NOT exist, it returns FALSE, else returns TRUE.
//! @note A very expensive function! Call sparingly.
bool UpdateFilePathToCorrectCase(const QString& root, QString& relativePathFromRoot);
bool UpdateFilePathToCorrectCase(AZStd::string_view root, AZStd::string& relativePathFromRoot);
} //namespace AzToolsFramework::AssetUtils
@@ -23,7 +23,7 @@
#include <AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.h>
AZ_PUSH_DISABLE_WARNING(4251 4244, "-Wunknown-warning-option") // disable warnings spawned by QT
#include "AssetBrowser/AssetPicker/ui_AssetPickerDialog.h"
#include <AzToolsFramework/AssetBrowser/AssetPicker/ui_AssetPickerDialog.h>
#include <QPushButton>
#include <QDialogButtonBox>
#include <QKeyEvent>
@@ -17,7 +17,7 @@
// 4251: class needs to have dll-interface to be used by clients of class
// 4800: forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include "AssetBrowser/Previewer/ui_EmptyPreviewer.h"
#include <AzToolsFramework/AssetBrowser/Previewer/ui_EmptyPreviewer.h>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
@@ -45,4 +45,4 @@ namespace AzToolsFramework
}
}
#include <AssetBrowser/Previewer/moc_EmptyPreviewer.cpp>
#include <AssetBrowser/Previewer/moc_EmptyPreviewer.cpp>
@@ -11,7 +11,7 @@
*/
#include <AzQtComponents/Components/ExtendedLabel.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include <AssetBrowser/Search/ui_FilterByWidget.h>
#include <AzToolsFramework/AssetBrowser/Search/ui_FilterByWidget.h>
AZ_POP_DISABLE_WARNING
#include <AzToolsFramework/AssetBrowser/Search/FilterByWidget.h>
@@ -39,4 +39,4 @@ namespace AzToolsFramework
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Search/moc_FilterByWidget.cpp"
#include "AssetBrowser/Search/moc_FilterByWidget.cpp"
@@ -18,7 +18,7 @@
#include <AzToolsFramework/AssetBrowser/Search/FilterByWidget.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include <AssetBrowser/Search/ui_SearchAssetTypeSelectorWidget.h>
#include <AzToolsFramework/AssetBrowser/Search/ui_SearchAssetTypeSelectorWidget.h>
AZ_POP_DISABLE_WARNING
#include <QPushButton>
@@ -154,4 +154,4 @@ namespace AzToolsFramework
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Search/moc_SearchAssetTypeSelectorWidget.cpp"
#include "AssetBrowser/Search/moc_SearchAssetTypeSelectorWidget.cpp"
@@ -12,7 +12,7 @@
#include "SearchParametersWidget.h"
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include "AssetBrowser/Search/ui_SearchParametersWidget.h"
#include <AzToolsFramework/AssetBrowser/Search/ui_SearchParametersWidget.h>
AZ_POP_DISABLE_WARNING
#include <AzQtComponents/Components/ExtendedLabel.h>
@@ -68,4 +68,4 @@ namespace AzToolsFramework
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Search/moc_SearchParametersWidget.cpp"
#include "AssetBrowser/Search/moc_SearchParametersWidget.cpp"
@@ -170,6 +170,15 @@ namespace AzToolsFramework
return m_filter;
}
QSharedPointer<CompositeFilter> SearchWidget::GetStringFilter() const
{
return m_stringFilter;
}
QSharedPointer<CompositeFilter> SearchWidget::GetTypesFilter() const
{
return m_typesFilter;
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -39,6 +39,10 @@ namespace AzToolsFramework
QSharedPointer<CompositeFilter> GetFilter() const;
QSharedPointer<CompositeFilter> GetStringFilter() const;
QSharedPointer<CompositeFilter> GetTypesFilter() const;
QString GetFilterString() const { return textFilter(); }
void ClearStringFilter() { ClearTextFilter(); }
@@ -54,7 +54,7 @@ namespace AzToolsFramework
//! Set unique asset browser name, used to persist tree expansion states
void SetName(const QString& name);
// LUMBERYARD_DEPRECATED
// O3DE_DEPRECATED
void LoadState(const QString& name);
void SaveState() const;
@@ -140,7 +140,7 @@ namespace AzToolsFramework
else
{
QPixmap pixmap = thumbnail->GetPixmap(size);
painter->drawPixmap(point.x(), point.y(), size.width(), size.height(), pixmap);
painter->drawPixmap(point, pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
return m_iconSize;
}
@@ -19,9 +19,9 @@
#include <AssetEditor/AssetEditorBus.h>
#include <AssetEditor/AssetEditorHeader.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include <AssetEditor/ui_AssetEditorToolbar.h>
#include <AzToolsFramework/AssetEditor/ui_AssetEditorToolbar.h>
AZ_POP_DISABLE_WARNING
#include <AssetEditor/ui_AssetEditorStatusBar.h>
#include <AzToolsFramework/AssetEditor/ui_AssetEditorStatusBar.h>
#include <AssetBrowser/AssetSelectionModel.h>
@@ -256,6 +256,8 @@ namespace AzToolsFramework
m_userSettings = AZ::UserSettings::CreateFind<AssetEditorWidgetUserSettings>(k_assetEditorWidgetSettings, AZ::UserSettings::CT_LOCAL);
UpdateRecentFileListState();
QObject::connect(m_recentFileMenu, &QMenu::aboutToShow, this, &AssetEditorWidget::PopulateRecentMenu);
}
@@ -952,7 +954,8 @@ namespace AzToolsFramework
void AssetEditorWidget::AddRecentPath(const AZStd::string& recentPath)
{
m_userSettings->AddRecentPath(recentPath);
m_userSettings->AddRecentPath(recentPath);
UpdateRecentFileListState();
}
void AssetEditorWidget::PopulateRecentMenu()
@@ -989,6 +992,21 @@ namespace AzToolsFramework
m_saveAsAssetAction->setEnabled(true);
}
void AssetEditorWidget::UpdateRecentFileListState()
{
if (m_recentFileMenu)
{
if (!m_userSettings || m_userSettings->m_recentPaths.empty())
{
m_recentFileMenu->setEnabled(false);
}
else
{
m_recentFileMenu->setEnabled(true);
}
}
}
} // namespace AssetEditor
} // namespace AzToolsFramework
@@ -122,6 +122,8 @@ namespace AzToolsFramework
void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override;
void OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& assetInfo) override;
void UpdateRecentFileListState();
private:
void DirtyAsset();
@@ -191,10 +191,17 @@ namespace AzToolsFramework
{
if (componentClass->m_editData->m_name == componentTypeNames[i])
{
// Although it is rare, it can happen that two (or more) components can have the same name.
// We should only count the first occurrence, so that none of the names in componentTypeNames
// get skipped, but whichever component type that is encountered last will be the one
// that is captured in order to preserve the pre-existing behavior.
if (foundTypeIds[i].IsNull())
{
++counter;
}
foundTypeIds[i] = componentClass->m_typeId;
++counter;
//Although it is rare, it can happen that two components can have the same name.
//We will capture only the first occurrence.
return true;
}
}
@@ -130,7 +130,7 @@ namespace AzToolsFramework
int written = uuid.ToString(buffer, aznumeric_caster(bufferSize), false);
if (written > 0)
{
if (bufferSize - written > 0)
if (bufferSize > written)
{
buffer[written - 1] = '\n';
buffer[written] = 0;
@@ -453,6 +453,7 @@ namespace AzToolsFramework
{
loadedSuccessfully = static_cast<PrefabEditorEntityOwnershipService*>(m_entityOwnershipService.get())->LoadFromStream(
stream, AZStd::string_view(levelPakFile.toUtf8(), levelPakFile.size()) );
}
LoadFromStreamComplete(loadedSuccessfully);
@@ -490,6 +491,14 @@ namespace AzToolsFramework
EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnStartPlayInEditorBegin);
//cache the current selected entities.
ToolsApplicationRequests::Bus::BroadcastResult(m_selectedBeforeStartingGame, &ToolsApplicationRequests::GetSelectedEntities);
//deselect entities if selected when entering game mode before deactivating the entities in StartPlayInEditor(...)
if (!m_selectedBeforeStartingGame.empty())
{
ToolsApplicationRequests::Bus::Broadcast(&ToolsApplicationRequests::MarkEntitiesDeselected, m_selectedBeforeStartingGame);
}
if (m_isLegacySliceService)
{
SliceEditorEntityOwnershipService* editorEntityOwnershipService =
@@ -506,8 +515,6 @@ namespace AzToolsFramework
m_isRunningGame = true;
ToolsApplicationRequests::Bus::BroadcastResult(m_selectedBeforeStartingGame, &ToolsApplicationRequests::GetSelectedEntities);
EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnStartPlayInEditor);
}
@@ -136,6 +136,48 @@ namespace AzToolsFramework
return entity->GetName();
}
EntityList EntityIdListToEntityList(const EntityIdList& inputEntityIds)
{
EntityList entities;
entities.reserve(inputEntityIds.size());
for (AZ::EntityId entityId : inputEntityIds)
{
if (!entityId.IsValid())
{
continue;
}
if (auto entity = GetEntityById(entityId))
{
entities.emplace_back(entity);
}
}
return entities;
}
EntityList EntityIdSetToEntityList(const EntityIdSet& inputEntityIds)
{
EntityList entities;
entities.reserve(inputEntityIds.size());
for (AZ::EntityId entityId : inputEntityIds)
{
if (!entityId.IsValid())
{
continue;
}
if (auto entity = GetEntityById(entityId))
{
entities.emplace_back(entity);
}
}
return entities;
}
void GetAllComponentsForEntity(const AZ::Entity* entity, AZ::Entity::ComponentArrayType& componentsOnEntity)
{
if (entity)
@@ -1068,6 +1110,45 @@ namespace AzToolsFramework
return !allEntityClonesContainer.m_entities.empty();
}
EntityIdSet GetCulledEntityHierarchy(const EntityIdList& entities)
{
EntityIdSet culledEntities;
for (const AZ::EntityId& entityId : entities)
{
bool selectionIncludesTransformHeritage = false;
AZ::EntityId parentEntityId = entityId;
do
{
AZ::EntityId nextParentId;
AZ::TransformBus::EventResult(
/*result*/ nextParentId,
/*address*/ parentEntityId,
&AZ::TransformBus::Events::GetParentId);
parentEntityId = nextParentId;
if (!parentEntityId.IsValid())
{
break;
}
for (const AZ::EntityId& parentCheck : entities)
{
if (parentCheck == parentEntityId)
{
selectionIncludesTransformHeritage = true;
break;
}
}
} while (parentEntityId.IsValid() && !selectionIncludesTransformHeritage);
if (!selectionIncludesTransformHeritage)
{
culledEntities.insert(entityId);
}
}
return culledEntities;
}
namespace Internal
{
void CloneSliceEntitiesAndChildren(
@@ -47,6 +47,9 @@ namespace AzToolsFramework
AZStd::string GetEntityName(const AZ::EntityId& entityId, const AZStd::string_view& nameOverride = {});
EntityList EntityIdListToEntityList(const EntityIdList& inputEntityIds);
EntityList EntityIdSetToEntityList(const EntityIdSet& inputEntityIds);
template <typename... ComponentTypes>
struct AddComponents
{
@@ -202,4 +205,8 @@ namespace AzToolsFramework
/// Wrap EBus SetSelectedEntities call.
void SelectEntities(const AzToolsFramework::EntityIdList& entities);
/// Return a set of entities, culling any that have an ancestor in the list.
/// e.g. This is useful for getting a concise set of entities that need to be duplicated.
EntityIdSet GetCulledEntityHierarchy(const EntityIdList& entities);
}; // namespace AzToolsFramework
@@ -707,7 +707,7 @@ namespace AzToolsFramework
{
QMessageBox::warning(AzToolsFramework::GetActiveWindow(),
QStringLiteral("Can't instantiate the selected slice"),
QString("The slice may contain UI elements that can't be instantiated in the main Lumberyard editor. "
QString("The slice may contain UI elements that can't be instantiated in the main Open 3D Engine editor. "
"Use the UI Editor to instantiate this slice or select another one."),
QMessageBox::Ok);
}
@@ -11,8 +11,10 @@
*/
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Script/ScriptSystemBus.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h>
@@ -21,6 +23,7 @@
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/PrefabLoader.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/PrefabUndoHelpers.h>
namespace AzToolsFramework
{
@@ -91,23 +94,48 @@ namespace AzToolsFramework
m_prefabSystemComponent->RemoveTemplate(templateId);
}
m_rootInstance->Reset();
m_rootInstance->SetContainerEntityName("Level");
AzFramework::EntityOwnershipServiceNotificationBus::Event(
m_entityContextId, &AzFramework::EntityOwnershipServiceNotificationBus::Events::OnEntityOwnershipServiceReset);
}
void PrefabEditorEntityOwnershipService::AddEntity(AZ::Entity* entity)
{
AZ_Assert(IsInitialized(), "Tried to add an entity without initializing the Entity Ownership Service");
ScopedUndoBatch undoBatch("Undo adding entity");
Prefab::PrefabDom instanceDomBeforeUpdate;
Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, instanceDomBeforeUpdate);
m_rootInstance->AddEntity(*entity);
HandleEntitiesAdded({ entity });
AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, m_rootInstance->m_containerEntity->GetId());
Prefab::PrefabUndoHelpers::UpdatePrefabInstance(
*m_rootInstance, "Undo adding entity", instanceDomBeforeUpdate, undoBatch.GetUndoBatch());
}
void PrefabEditorEntityOwnershipService::AddEntities(const EntityList& entities)
{
AZ_Assert(IsInitialized(), "Tried to add entities without initializing the Entity Ownership Service");
ScopedUndoBatch undoBatch("Undo adding entities");
Prefab::PrefabDom instanceDomBeforeUpdate;
Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, instanceDomBeforeUpdate);
for (AZ::Entity* entity : entities)
{
m_rootInstance->AddEntity(*entity);
}
HandleEntitiesAdded(entities);
for (AZ::Entity* entity : entities)
{
AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, m_rootInstance->m_containerEntity->GetId());
}
Prefab::PrefabUndoHelpers::UpdatePrefabInstance(
*m_rootInstance, "Undo adding entities", instanceDomBeforeUpdate, undoBatch.GetUndoBatch());
}
bool PrefabEditorEntityOwnershipService::DestroyEntity(AZ::Entity* entity)
@@ -171,7 +199,9 @@ namespace AzToolsFramework
m_rootInstance->SetTemplateId(templateId);
m_rootInstance->SetTemplateSourcePath(m_loaderInterface->GetRelativePathToProject(filename));
m_rootInstance->SetContainerEntityName("Level");
m_prefabSystemComponent->PropagateTemplateChanges(templateId);
return true;
}
@@ -251,18 +281,29 @@ namespace AzToolsFramework
AZStd::unique_ptr<Prefab::Instance> createdPrefabInstance =
m_prefabSystemComponent->CreatePrefab(entities, AZStd::move(nestedPrefabInstances), filePath);
if (!instanceToParentUnder)
{
instanceToParentUnder = *m_rootInstance;
}
if (createdPrefabInstance)
{
if (!instanceToParentUnder)
{
instanceToParentUnder = *m_rootInstance;
}
Prefab::Instance& addedInstance = instanceToParentUnder->get().AddInstance(AZStd::move(createdPrefabInstance));
HandleEntitiesAdded({addedInstance.m_containerEntity.get()});
AZ::Entity* containerEntity = addedInstance.m_containerEntity.get();
containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent());
HandleEntitiesAdded({containerEntity});
HandleEntitiesAdded(entities);
// Update the template of the instance since we modified the entities of the instance by calling HandleEntitiesAdded.
Prefab::PrefabDom serializedInstance;
if (Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(addedInstance, serializedInstance))
{
m_prefabSystemComponent->UpdatePrefabTemplate(addedInstance.GetTemplateId(), serializedInstance);
}
return addedInstance;
}
HandleEntitiesAdded(entities);
return AZStd::nullopt;
}
@@ -295,6 +336,9 @@ namespace AzToolsFramework
void PrefabEditorEntityOwnershipService::StartPlayInEditor()
{
// This is a workaround until the replacement for GameEntityContext is done
AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnPreGameEntitiesStarted);
if (m_rootInstance && !m_playInEditorData.m_isEnabled)
{
// Construct the runtime entities and products
@@ -339,11 +383,16 @@ namespace AzToolsFramework
m_playInEditorData.m_assets.emplace_back(product.ReleaseAsset().release(), AZ::Data::AssetLoadBehavior::Default);
}
if (rootSpawnableIndex != NoRootSpawnable)
{
m_playInEditorData.m_entities.Reset(m_playInEditorData.m_assets[rootSpawnableIndex]);
m_playInEditorData.m_entities.SpawnAllEntities();
}
// This is a workaround until the replacement for GameEntityContext is done
AzFramework::GameEntityContextEventBus::Broadcast(
&AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesStarted);
}
else
{
@@ -402,6 +451,9 @@ namespace AzToolsFramework
AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::GarbageCollect);
});
m_playInEditorData.m_entities.Clear();
// This is a workaround until the replacement for GameEntityContext is done
AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesReset);
}
m_playInEditorData.m_isEnabled = false;
@@ -186,7 +186,7 @@ namespace AzToolsFramework
PlayInEditorData m_playInEditorData;
//////////////////////////////////////////////////////////////////////////
// PrefabSystemComponentInterface interface implementation
// PrefabEditorEntityOwnershipInterface implementation
Prefab::InstanceOptionalReference CreatePrefab(
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Prefab::Instance>>&& nestedPrefabInstances,
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override;
@@ -89,7 +89,7 @@ namespace AzToolsFramework
const AzFramework::CameraState& cameraState,
const ViewportInteraction::MouseInteraction& mouseInteraction);
// LUMBERYARD_DEPRECATED(LY-117150)
// O3DE_DEPRECATED(LY-117150)
/// Check if the modifier key state has changed - if so we may need to refresh
/// certain manipulator bounds.
AZ_DEPRECATED(
@@ -56,16 +56,6 @@ namespace AzToolsFramework
void EditorPrefabComponent::Activate()
{
PrefabPublicInterface* prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
if (prefabPublicInterface && prefabPublicInterface->IsLevelInstanceContainerEntity(GetEntityId()))
{
EntityOutlinerWidgetInterface* entityOutlinerWidgetInterface = AZ::Interface<EntityOutlinerWidgetInterface>::Get();
if (entityOutlinerWidgetInterface)
{
entityOutlinerWidgetInterface->SetRootEntity(GetEntityId());
}
}
PrefabInstanceContainerNotificationBus::Broadcast(
&PrefabInstanceContainerNotifications::OnPrefabComponentActivate, GetEntityId());
}
@@ -123,7 +123,11 @@ namespace AzToolsFramework
void Instance::SetTemplateSourcePath(AZ::IO::PathView sourcePath)
{
m_templateSourcePath = sourcePath;
m_containerEntity->SetName(sourcePath.Filename().Native());
}
void Instance::SetContainerEntityName(AZStd::string_view containerName)
{
m_containerEntity->SetName(containerName);
}
bool Instance::AddEntity(AZ::Entity& entity)
@@ -154,7 +158,7 @@ namespace AzToolsFramework
if (instanceToTemplateEntityIdIterator != m_instanceToTemplateEntityIdMap.end())
{
entityAliasToRemove = instanceToTemplateEntityIdIterator->second;
bool isEntityRemoved = m_instanceEntityMapper->UnregisterEntity(entityId) &&
[[maybe_unused]] bool isEntityRemoved = m_instanceEntityMapper->UnregisterEntity(entityId) &&
m_templateToInstanceEntityIdMap.erase(entityAliasToRemove) && m_instanceToTemplateEntityIdMap.erase(entityId);
AZ_Assert(isEntityRemoved,
"Prefab - Failed to remove entity with id %s with a Prefab Instance derived from source asset %s "
@@ -563,7 +567,7 @@ namespace AzToolsFramework
AZ::EntityId Instance::GetContainerEntityId() const
{
return m_containerEntity->GetId();
return m_containerEntity ? m_containerEntity->GetId() : AZ::EntityId();
}
bool Instance::HasContainerEntity() const
@@ -80,6 +80,7 @@ namespace AzToolsFramework
const AZ::IO::Path& GetTemplateSourcePath() const;
void SetTemplateSourcePath(AZ::IO::PathView sourcePath);
void SetContainerEntityName(AZStd::string_view containerName);
bool AddEntity(AZ::Entity& entity);
bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias);
@@ -31,7 +31,7 @@ namespace AzToolsFramework
class InstanceEntityScrubber
{
public:
AZ_RTTI(InstanceEntityScrubber, "{0BC12562-C240-48AD-89C6-EDF572C9B485}");
AZ_TYPE_INFO(InstanceEntityScrubber, "{0BC12562-C240-48AD-89C6-EDF572C9B485}");
explicit InstanceEntityScrubber(Instance::EntityList& entities);
@@ -209,7 +209,7 @@ namespace AzToolsFramework
EntityList entitiesInInstance;
entitiesInInstance.reserve(instance->m_entities.size() + 1);
if (instance->m_containerEntity->GetId().IsValid())
if (instance->m_containerEntity && instance->m_containerEntity->GetId().IsValid())
{
entitiesInInstance.emplace_back(instance->m_containerEntity.get());
}
@@ -219,11 +219,10 @@ namespace AzToolsFramework
entitiesInInstance.emplace_back(entity.get());
}
InstanceEntityScrubber** instanceEntityScrubber = jsonDeserializerContext.GetMetadata().Find<InstanceEntityScrubber*>();
if (instanceEntityScrubber && (*instanceEntityScrubber))
InstanceEntityScrubber* instanceEntityScrubber = jsonDeserializerContext.GetMetadata().Find<InstanceEntityScrubber>();
if (instanceEntityScrubber)
{
(*instanceEntityScrubber)->AddEntitiesToScrub(entitiesInInstance);
instanceEntityScrubber->AddEntitiesToScrub(entitiesInInstance);
}
}
@@ -43,14 +43,12 @@ namespace AzToolsFramework
const PrefabDom& modifiedState, const LinkId linkId) = 0;
//! Updates the affected template for a given entityId using the providedPatch
virtual bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId) = 0;
virtual bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId) = 0;
virtual bool PatchEntityInTemplate(PrefabDom& providedPatch, AZ::EntityId entityId) = 0;
virtual void AppendEntityAliasToPatchPaths(PrefabDom& providedPatch, const AZ::EntityId& entityId) = 0;
//! Updates the template links (updating instances) for the given templateId using the providedPatch
virtual void PatchTemplate(PrefabDomValue& providedPatch, const TemplateId& templateId) = 0;
virtual bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId) = 0;
virtual void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) = 0;
@@ -107,7 +107,7 @@ namespace AzToolsFramework
return result.GetProcessing() != AZ::JsonSerializationResult::Processing::Halted;
}
bool InstanceToTemplatePropagator::PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId)
bool InstanceToTemplatePropagator::PatchEntityInTemplate(PrefabDom& providedPatch, AZ::EntityId entityId)
{
InstanceOptionalReference instanceOptionalReference = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
@@ -119,54 +119,10 @@ namespace AzToolsFramework
return false;
}
//get template space associated with instance
Instance& instance = instanceOptionalReference->get();
TemplateId templateId = instance.GetTemplateId();
//alias entity goes by in template -> get via owning instance map
AZStd::optional<EntityAlias> entityAlias = instance.GetEntityAlias(entityId);
if (!entityAlias)
{
AZ_Error("Prefab", false, "Failed to find an entity alias for the provided entity");
return false;
}
PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(templateId);
return PatchEntityInTemplate(providedPatch, entityAlias.value(), templateId);
}
bool InstanceToTemplatePropagator::PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId)
{
PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(templateId);
//query into the template dom for the alias
PrefabDomValueReference entityList = PrefabDomUtils::FindPrefabDomValue(templateDomReference, PrefabDomUtils::EntitiesName);
if (!entityList)
{
AZ_Error("Prefab", false, "Cannot patch entity in Template with id [%llu] because entity couldn't be found in the template", templateId);
return false;
}
PrefabDomValueReference entity = PrefabDomUtils::FindPrefabDomValue(entityList->get(), entityAlias.c_str());
if (!entity)
{
AZ_Error("Prefab", false, "Failed to aquire entity value reference");
return false;
}
//apply patch to section
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::ApplyPatch(entity->get(),
templateDomReference.GetAllocator(), providedPatch, AZ::JsonMergeApproach::JsonPatch);
AZ_Error("Prefab", result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success, "Patch was not successfully applied")
//trigger propagation
m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId);
return true;
//get template id associated with instance
TemplateId templateId = instanceOptionalReference->get().GetTemplateId();
AppendEntityAliasToPatchPaths(providedPatch, entityId);
return PatchTemplate(providedPatch, templateId);
}
void InstanceToTemplatePropagator::AppendEntityAliasToPatchPaths(PrefabDom& providedPatch, const AZ::EntityId& entityId)
@@ -216,7 +172,7 @@ namespace AzToolsFramework
}
}
void InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, const TemplateId& templateId)
bool InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId)
{
PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(templateId);
@@ -224,14 +180,17 @@ namespace AzToolsFramework
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::ApplyPatch(templateDomReference,
templateDomReference.GetAllocator(), providedPatch, AZ::JsonMergeApproach::JsonPatch);
AZ_Error("Prefab", result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success,
"Patch was not successfully applied");
//trigger propagation
if (result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success)
{
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, true);
m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId);
return true;
}
else
{
AZ_Error("Prefab", false, "Patch was not successfully applied");
return false;
}
}
@@ -288,6 +247,8 @@ namespace AzToolsFramework
AddPatchesToLink(patches, linkToApplyPatches);
linkToApplyPatches.UpdateTarget();
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(linkToApplyPatches.GetTargetTemplateId(), true);
m_prefabSystemComponentInterface->PropagateTemplateChanges(linkToApplyPatches.GetTargetTemplateId());
}
@@ -314,7 +275,6 @@ namespace AzToolsFramework
PrefabDom& linkDom = link.GetLinkDom();
PrefabDomValueReference linkPatchesReference =
PrefabDomUtils::FindPrefabDomValue(linkDom, PrefabDomUtils::PatchesName);
PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(link.GetTargetTemplateId());
// This logic only covers addition of patches. If patches already exists, the given list of patches must be appended to them.
if (!linkPatchesReference.has_value())
@@ -31,21 +31,19 @@ namespace AzToolsFramework
bool GeneratePatch(PrefabDom& generatedPatch, const PrefabDom& initialState, const PrefabDom& modifiedState) override;
bool GeneratePatchForLink(PrefabDom& generatedPatch, const PrefabDom& initialState,
const PrefabDom& modifiedState, LinkId linkId) override;
bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId) override;
bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId) override;
bool PatchEntityInTemplate(PrefabDom& providedPatch, AZ::EntityId entityId) override;
void AppendEntityAliasToPatchPaths(PrefabDom& providedPatch, const AZ::EntityId& entityId) override;
InstanceOptionalReference GetTopMostInstanceInHierarchy(AZ::EntityId entityId);
void PatchTemplate(PrefabDomValue& providedPatch, const AzToolsFramework::Prefab::TemplateId& templateId) override;
bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId) override;
void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) override;
void AddPatchesToLink(PrefabDom& patches, Link& link);
private:
InstanceEntityMapperInterface* m_instanceEntityMapperInterface;
PrefabSystemComponentInterface* m_prefabSystemComponentInterface;
@@ -15,10 +15,12 @@
#include <AzCore/Component/TickBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Instance/TemplateInstanceMapperInterface.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerWidgetInterface.h>
@@ -115,14 +117,32 @@ namespace AzToolsFramework
"Could not find Template using Id '%llu'. Unable to update Instance.",
currentTemplateId);
// Remove the instance from update queue if its corresponding template couldn't be found
isUpdateSuccessful = false;
m_instancesUpdateQueue.pop();
continue;
}
}
auto findInstancesResult = m_templateInstanceMapperInterface->FindInstancesOwnedByTemplate(instanceTemplateId)->get();
if (findInstancesResult.find(instanceToUpdate) == findInstancesResult.end())
{
// Since nested instances get reconstructed during propagation, remove any nested instance that no longer
// maps to a template.
isUpdateSuccessful = false;
m_instancesUpdateQueue.pop();
continue;
}
Template& currentTemplate = currentTemplateReference->get();
Instance::EntityList newEntities;
if (!PrefabDomUtils::LoadInstanceFromPrefabDom(
*instanceToUpdate, newEntities, currentTemplate.GetPrefabDom()))
if (PrefabDomUtils::LoadInstanceFromPrefabDom(*instanceToUpdate, newEntities, currentTemplate.GetPrefabDom()))
{
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, newEntities);
}
else
{
AZ_Error(
"Prefab", false,
@@ -132,22 +152,34 @@ namespace AzToolsFramework
isUpdateSuccessful = false;
}
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, newEntities);
m_instancesUpdateQueue.pop();
}
for (auto entityIdIterator = selectedEntityIds.begin(); entityIdIterator != selectedEntityIds.end(); entityIdIterator++)
{
// Since entities get recreated during propagation, we need to check whether the entities correspoding to the list
// of selected entity ids are present or not.
AZ::Entity* entity = GetEntityById(*entityIdIterator);
if (entity == nullptr)
{
selectedEntityIds.erase(entityIdIterator--);
}
}
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds);
// Enable the Outliner
AZ::SystemTickBus::QueueFunction([entityOutlinerWidgetInterface]() {
if (entityOutlinerWidgetInterface)
if (entityOutlinerWidgetInterface)
{
entityOutlinerWidgetInterface->SetUpdatesEnabled(true);
auto prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
if (prefabPublicInterface)
{
entityOutlinerWidgetInterface->SetUpdatesEnabled(true);
AZ::EntityId rootEntityId = prefabPublicInterface->GetLevelInstanceContainerEntityId();
entityOutlinerWidgetInterface->ExpandEntityChildren(rootEntityId);
}
});
}
}
m_updatingTemplateInstancesInQueue = false;
@@ -10,6 +10,7 @@
*
*/
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
@@ -78,6 +79,11 @@ namespace AzToolsFramework
bool LoadInstanceFromPrefabDom(Instance& instance, const PrefabDom& prefabDom, LoadInstanceFlags flags)
{
// When entities are rebuilt they are first destroyed. As a result any assets they were exclusively holding on to will
// be released and reloaded once the entities are built up again. By suspending asset release temporarily the asset reload
// is avoided.
AZ::Data::AssetManager::Instance().SuspendAssetRelease();
InstanceEntityIdMapper entityIdMapper;
entityIdMapper.SetLoadingInstance(instance);
if ((flags & LoadInstanceFlags::AssignRandomEntityId) == LoadInstanceFlags::AssignRandomEntityId)
@@ -91,10 +97,12 @@ namespace AzToolsFramework
// data has strict typing and doesn't look for inheritance both have to be explicitly added so they're found both locations.
settings.m_metadata.Add(static_cast<AZ::JsonEntityIdSerializer::JsonEntityIdMapper*>(&entityIdMapper));
settings.m_metadata.Add(&entityIdMapper);
AZ::JsonSerializationResult::ResultCode result =
AZ::JsonSerialization::Load(instance, prefabDom, settings);
AZ::Data::AssetManager::Instance().ResumeAssetRelease();
if (result.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
{
AZ_Error("Prefab", false,
@@ -110,6 +118,11 @@ namespace AzToolsFramework
bool LoadInstanceFromPrefabDom(
Instance& instance, Instance::EntityList& newlyAddedEntities, const PrefabDom& prefabDom, LoadInstanceFlags flags)
{
// When entities are rebuilt they are first destroyed. As a result any assets they were exclusively holding on to will
// be released and reloaded once the entities are built up again. By suspending asset release temporarily the asset reload
// is avoided.
AZ::Data::AssetManager::Instance().SuspendAssetRelease();
InstanceEntityIdMapper entityIdMapper;
entityIdMapper.SetLoadingInstance(instance);
if ((flags & LoadInstanceFlags::AssignRandomEntityId) == LoadInstanceFlags::AssignRandomEntityId)
@@ -123,16 +136,16 @@ namespace AzToolsFramework
// data has strict typing and doesn't look for inheritance both have to be explicitly added so they're found both locations.
settings.m_metadata.Add(static_cast<AZ::JsonEntityIdSerializer::JsonEntityIdMapper*>(&entityIdMapper));
settings.m_metadata.Add(&entityIdMapper);
settings.m_metadata.Create<InstanceEntityScrubber>(newlyAddedEntities);
InstanceEntityScrubber instanceEntityScrubber(newlyAddedEntities);
settings.m_metadata.Add(&instanceEntityScrubber);
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(instance, prefabDom, settings);
AZ::JsonSerializationResult::ResultCode result =
AZ::JsonSerialization::Load(instance, prefabDom, settings);
AZ::Data::AssetManager::Instance().ResumeAssetRelease();
if (result.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
{
AZ_Error("Prefab", false,
AZ_Error(
"Prefab", false,
"Failed to de-serialize Prefab Instance from Prefab DOM. "
"Unable to proceed.");
@@ -39,7 +39,7 @@ namespace AzToolsFramework
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ_Assert(settingsRegistry, "Settings registry is not set");
bool result =
[[maybe_unused]] bool result =
settingsRegistry->Get(m_projectPathWithOsSeparator.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath);
AZ_Assert(result, "Couldn't retrieve project root path");
m_projectPathWithSlashSeparator = AZ::IO::Path(m_projectPathWithOsSeparator.Native(), '/').MakePreferred();
@@ -182,7 +182,6 @@ namespace AzToolsFramework
for (PrefabDomValue::MemberIterator instanceIterator = instances.MemberBegin(); instanceIterator != instances.MemberEnd();
++instanceIterator)
{
const PrefabDomValue& instance = instanceIterator->value;
if (!LoadNestedInstance(instanceIterator, newTemplateId, progressedFilePathsSet))
{
isLoadedWithErrors = true;
@@ -349,8 +348,7 @@ namespace AzToolsFramework
"Prefab", false,
"PrefabLoader::SaveTemplate - Unable to save Prefab Template with id: %llu. "
"Template with that id is invalid",
templateId
);
templateId);
return AZStd::nullopt;
}
@@ -15,18 +15,20 @@
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Utils/TypeHash.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
#include <AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/PrefabUndo.h>
#include <AzToolsFramework/Prefab/PrefabUndoHelpers.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
namespace AzToolsFramework
@@ -36,12 +38,14 @@ namespace AzToolsFramework
void PrefabPublicHandler::RegisterPrefabPublicHandlerInterface()
{
m_instanceEntityMapperInterface = AZ::Interface<InstanceEntityMapperInterface>::Get();
AZ_Assert(
m_instanceEntityMapperInterface, "PrefabPublicHandler - Could not retrieve instance of InstanceEntityMapperInterface");
AZ_Assert(m_instanceEntityMapperInterface, "PrefabPublicHandler - Could not retrieve instance of InstanceEntityMapperInterface");
m_instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
AZ_Assert(m_instanceToTemplateInterface, "PrefabPublicHandler - Could not retrieve instance of InstanceToTemplateInterface");
m_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
AZ_Assert(m_prefabLoaderInterface, "Could not get PrefabLoaderInterface on PrefabPublicHandler construction.");
m_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
AZ_Assert(m_prefabSystemComponentInterface, "Could not get PrefabSystemComponentInterface on PrefabPublicHandler construction.");
@@ -57,93 +61,161 @@ namespace AzToolsFramework
m_prefabUndoCache.Destroy();
}
PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZStd::string_view filePath)
PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath)
{
EntityList inputEntityList, topLevelEntities;
AZ::EntityId commonRootEntityId;
InstanceOptionalReference commonRootEntityOwningInstance;
PrefabOperationResult findCommonRootOutcome = FindCommonRootOwningInstance(
entityIds, inputEntityList, topLevelEntities, commonRootEntityId, commonRootEntityOwningInstance);
if (!findCommonRootOutcome.IsSuccess())
{
return findCommonRootOutcome;
}
InstanceOptionalReference instanceToCreate;
{
// Initialize Undo Batch object
ScopedUndoBatch undoBatch("Create Prefab");
PrefabDom commonRootInstanceDomBeforeCreate;
m_instanceToTemplateInterface->GenerateDomForInstance(
commonRootInstanceDomBeforeCreate, commonRootEntityOwningInstance->get());
AZStd::vector<AZ::Entity*> entities;
AZStd::vector<AZStd::unique_ptr<Instance>> instances;
// Retrieve all entities affected and identify Instances
if (!RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonRootEntityOwningInstance->get(), entities, instances))
{
return AZ::Failure(
AZStd::string("Could not create a new prefab out of the entities provided - entities do not share a common root."));
}
// When we create a prefab with other prefab instances, we have to remove the existing links between the source and
// target templates of the other instances.
for (auto& nestedInstance : instances)
{
PrefabUndoHelpers::RemoveLink(
nestedInstance->GetTemplateId(), commonRootEntityOwningInstance->get().GetTemplateId(),
nestedInstance->GetInstanceAlias(), nestedInstance->GetLinkId(), undoBatch.GetUndoBatch());
}
auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
if (!prefabEditorEntityOwnershipInterface)
{
return AZ::Failure(AZStd::string("Could not create a new prefab out of the entities provided - internal error "
"(PrefabEditorEntityOwnershipInterface unavailable)."));
}
// Create the Prefab
instanceToCreate = prefabEditorEntityOwnershipInterface->CreatePrefab(
entities, AZStd::move(instances), filePath, commonRootEntityOwningInstance);
if (!instanceToCreate)
{
return AZ::Failure(AZStd::string("Could not create a new prefab out of the entities provided - internal error "
"(A null instance is returned)."));
}
PrefabUndoHelpers::UpdatePrefabInstance(
commonRootEntityOwningInstance->get(), "Update prefab instance", commonRootInstanceDomBeforeCreate, undoBatch.GetUndoBatch());
CreateLink(
topLevelEntities, instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(),
commonRootEntityId);
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
// Change top level entities to be parented to the container entity
// Mark them as dirty so this change is correctly applied to the template
for (AZ::Entity* topLevelEntity : topLevelEntities)
{
m_prefabUndoCache.UpdateCache(topLevelEntity->GetId());
undoBatch.MarkEntityDirty(topLevelEntity->GetId());
AZ::TransformBus::Event(topLevelEntity->GetId(), &AZ::TransformBus::Events::SetParent, containerEntityId);
}
// Select Container Entity
{
auto selectionUndo = aznew SelectionCommand({containerEntityId}, "Select Prefab Container Entity");
selectionUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
}
}
// Save Template to file
m_prefabLoaderInterface->SaveTemplate(instanceToCreate->get().GetTemplateId());
return AZ::Success();
}
PrefabOperationResult PrefabPublicHandler::FindCommonRootOwningInstance(
const AZStd::vector<AZ::EntityId>& entityIds, EntityList& inputEntityList, EntityList& topLevelEntities,
AZ::EntityId& commonRootEntityId, InstanceOptionalReference& commonRootEntityOwningInstance)
{
// Retrieve entityList from entityIds
EntityList inputEntityList;
EntityIdListToEntityList(entityIds, inputEntityList);
inputEntityList = EntityIdListToEntityList(entityIds);
// Find common root and top level entities
bool entitiesHaveCommonRoot = false;
AZ::EntityId commonRootEntityId;
EntityList topLevelEntities;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
entitiesHaveCommonRoot,
&AzToolsFramework::ToolsApplicationRequests::FindCommonRootInactive,
inputEntityList,
commonRootEntityId,
&topLevelEntities
);
entitiesHaveCommonRoot, &AzToolsFramework::ToolsApplicationRequests::FindCommonRootInactive, inputEntityList,
commonRootEntityId, &topLevelEntities);
// Bail if entities don't share a common root
if (!entitiesHaveCommonRoot)
{
return AZ::Failure(AZStd::string("Could not create a new prefab out of the entities provided - entities do not share a common root."));
}
AZ::Entity* commonRootEntity = nullptr;
if (commonRootEntityId.IsValid())
{
commonRootEntity = GetEntityById(commonRootEntityId);
return AZ::Failure(AZStd::string("Failed to create a prefab: Provided entities do not share a common root."));
}
// Retrieve the owning instance of the common root entity, which will be our new instance's parent instance.
InstanceOptionalReference commonRootEntityOwningInstance = GetOwnerInstanceByEntityId(commonRootEntityId);
AZ_Assert(commonRootEntityOwningInstance.has_value(), "Failed to create prefab : "
"Couldn't get a valid owning instance for the common root entity of the enities provided");
AZStd::vector<AZ::Entity*> entities;
AZStd::vector<AZStd::unique_ptr<Instance>> instances;
// Retrieve all entities affected and identify Instances
if (!RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonRootEntityOwningInstance->get(), entities, instances))
commonRootEntityOwningInstance = GetOwnerInstanceByEntityId(commonRootEntityId);
if (!commonRootEntityOwningInstance)
{
return AZ::Failure(AZStd::string("Could not create a new prefab out of the entities provided - entities do not share a common root."));
AZ_Assert(
false,
"Failed to create prefab : Couldn't get a valid owning instance for the common root entity of the enities provided");
return AZ::Failure(AZStd::string(
"Failed to create prefab : Couldn't get a valid owning instance for the common root entity of the enities provided"));
}
return AZ::Success();
}
auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
if (!prefabEditorEntityOwnershipInterface)
{
return AZ::Failure(AZStd::string("Could not create a new prefab out of the entities provided - internal error "
"(PrefabEditorEntityOwnershipInterface unavailable)."));
}
void PrefabPublicHandler::CreateLink(
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId)
{
AZ::EntityId containerEntityId = sourceInstance.GetContainerEntityId();
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
Prefab::PrefabDom containerEntityDomBefore;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity);
InstanceOptionalReference instance = prefabEditorEntityOwnershipInterface->CreatePrefab(
entities, AZStd::move(instances), filePath, commonRootEntityOwningInstance);
if (!instance)
{
return AZ::Failure(AZStd::string("Could not create a new prefab out of the entities provided - internal error "
"(A null instance is returned)."));
}
AZ::EntityId containerEntityId = instance->get().GetContainerEntityId();
AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero());
AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero());
// Set the transform (translation, rotation) of the container entity
GenerateContainerEntityTransform(topLevelEntities, containerEntityTranslation, containerEntityRotation);
// Set container entity to be child of common root
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, commonRootEntityId);
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation);
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation);
// Set container entity to be child of common root
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, commonRootEntityId);
// Assign the EditorPrefabComponent to the instance container
EntityCompositionRequests::AddComponentsOutcome outcome;
EntityCompositionRequestBus::BroadcastResult(
outcome, &EntityCompositionRequests::AddComponentsToEntities, EntityIdList{containerEntityId},
AZ::ComponentTypeList{azrtti_typeid<AzToolsFramework::Prefab::EditorPrefabComponent>()});
// Change top level entities to be parented to the container entity
for (AZ::Entity* topLevelEntity : topLevelEntities)
{
AZ::TransformBus::Event(topLevelEntity->GetId(), &AZ::TransformBus::Events::SetParent, containerEntityId);
}
return AZ::Success();
PrefabDom containerEntityDomAfter;
m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity);
PrefabDom patch;
m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
PrefabUndoHelpers::CreateLink(
sourceInstance.GetTemplateId(), targetTemplateId, patch, sourceInstance.GetInstanceAlias(),
undoBatch);
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
}
PrefabOperationResult PrefabPublicHandler::InstantiatePrefab(AZStd::string_view /*filePath*/, AZ::EntityId /*parent*/, AZ::Vector3 /*position*/)
@@ -173,14 +245,7 @@ namespace AzToolsFramework
AZStd::string("SavePrefab - Path error. Path could be invalid, or the prefab may not be loaded in this level."));
}
auto prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
if (prefabLoaderInterface == nullptr)
{
return AZ::Failure(AZStd::string(
"Could not save prefab - internal error (PrefabLoaderInterface unavailable)."));
}
if (!prefabLoaderInterface->SaveTemplate(templateId))
if (!m_prefabLoaderInterface->SaveTemplate(templateId))
{
return AZ::Failure(AZStd::string("Could not save prefab - internal error (Json write operation failure)."));
}
@@ -247,23 +312,8 @@ namespace AzToolsFramework
ToolsApplicationRequests::Bus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selection);
PrefabDom instanceDomAfterUpdate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfterUpdate, entityOwningInstance);
// Generate the patch comparing the instance before and after the entity addition.
PrefabDom patch;
if (!m_instanceToTemplateInterface->GeneratePatch(patch, instanceDomBeforeUpdate, instanceDomAfterUpdate))
{
return AZ::Failure(AZStd::string::format(
"A valid patch couldn't be created for adding an entity with id '%llu'", static_cast<AZ::u64>(entityId)));
}
// create undo node
PrefabUndoInstance* state = aznew PrefabUndoInstance(AZStd::string::format("%llu", static_cast<AZ::u64>(entityId)));
state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, entityOwningInstance.GetTemplateId());
state->SetParent(undoBatch.GetUndoBatch());
state->Redo();
PrefabUndoHelpers::UpdatePrefabInstance(
entityOwningInstance, "Undo adding entity", instanceDomBeforeUpdate, undoBatch.GetUndoBatch());
return AZ::Success(entityId);
}
@@ -276,13 +326,13 @@ namespace AzToolsFramework
if (instanceOptionalReference.has_value())
{
PrefabDom beforeState;
m_prefabUndoCache.Retrieve(entityId, beforeState);
PrefabDom afterState;
AZ::Entity* entity = GetEntityById(entityId);
if (entity)
{
PrefabDom beforeState;
m_prefabUndoCache.Retrieve(entityId, beforeState);
m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity);
PrefabDom patch;
@@ -301,7 +351,10 @@ namespace AzToolsFramework
// Update the cache
m_prefabUndoCache.Store(entityId, AZStd::move(afterState));
}
else
{
m_prefabUndoCache.PurgeCache(entityId);
}
}
}
@@ -433,8 +486,7 @@ namespace AzToolsFramework
InstanceOptionalReference instance = GetOwnerInstanceByEntityId(entityIds[0]);
// Retrieve entityList from entityIds
EntityList inputEntityList;
EntityIdListToEntityList(entityIds, inputEntityList);
EntityList inputEntityList = EntityIdListToEntityList(entityIds);
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@@ -643,14 +695,17 @@ namespace AzToolsFramework
}
bool PrefabPublicHandler::RetrieveAndSortPrefabEntitiesAndInstances(
const EntityList& inputEntities, const Instance& commonRootEntityOwningInstance,
const EntityList& inputEntities, Instance& commonRootEntityOwningInstance,
EntityList& outEntities, AZStd::vector<AZStd::unique_ptr<Instance>>& outInstances) const
{
AZStd::queue<AZ::Entity*> entityQueue;
for (auto inputEntity : inputEntities)
{
entityQueue.push(inputEntity);
if (inputEntity && !IsLevelInstanceContainerEntity(inputEntity->GetId()))
{
entityQueue.push(inputEntity);
}
}
// Support sets to easily identify if we're processing the same entity multiple times.
@@ -664,17 +719,19 @@ namespace AzToolsFramework
// Get this entity's owning instance.
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entity->GetId());
AZ_Assert(owningInstance.has_value(), "An error occored while retrieving entities and prefab instances : "
"Owning instance of entity with id '%llu' couldn't be found", entity->GetId());
AZ_Assert(
owningInstance.has_value(),
"An error occurred while retrieving entities and prefab instances : "
"Owning instance of entity with id '%llu' couldn't be found",
entity->GetId());
// Check if this entity is owned by the same instance owning the root.
if (&owningInstance->get() == &commonRootEntityOwningInstance)
{
AZStd::unique_ptr<AZ::Entity> detachedEntity = owningInstance->get().DetachEntity(entity->GetId());
// If it's the same instance, we can add this entity to the new instance entities.
int priorEntitiesSize = entities.size();
entities.insert(detachedEntity.release());
entities.insert(entity);
// If the size of entities increased, then it wasn't added before.
// In that case, add the children of this entity to the queue.
@@ -714,20 +771,18 @@ namespace AzToolsFramework
// Store results
outEntities.clear();
outEntities.resize(entities.size());
AZStd::copy(entities.begin(), entities.end(), outEntities.begin());
outEntities.reserve(entities.size());
for (AZ::Entity* entity : entities)
{
outEntities.emplace_back(commonRootEntityOwningInstance.DetachEntity(entity->GetId()).release());
}
outInstances.clear();
outInstances.reserve(instances.size());
for (Instance* instancePtr : instances)
{
auto parentInstance = instancePtr->GetParentInstance();
if (parentInstance.has_value())
{
auto uniquePtr = parentInstance->get().DetachNestedInstance(instancePtr->GetInstanceAlias());
outInstances.push_back(AZStd::move(uniquePtr));
}
outInstances.push_back(AZStd::move(commonRootEntityOwningInstance.DetachNestedInstance(instancePtr->GetInstanceAlias())));
}
return true;
@@ -745,8 +800,20 @@ namespace AzToolsFramework
for (AZ::EntityId entityId : entityIds)
{
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
// If this is the container entity, it actually represents the instance so get its owner
if (owningInstance->get().GetContainerEntityId() == entityId)
if (!owningInstance.has_value())
{
AZ_Assert(
false,
"An error occurred in function EntitiesBelongToSameInstance: "
"Owning instance of entity with id '%llu' couldn't be found",
entityId);
return false;
}
// If this is a container entity, it actually represents a child instance so get its owner.
// The only exception in the level root instance. We leave it as is to streamline operations.
if (owningInstance->get().GetContainerEntityId() == entityId && !IsLevelInstanceContainerEntity(entityId))
{
owningInstance = owningInstance->get().GetParentInstance();
}
@@ -766,18 +833,5 @@ namespace AzToolsFramework
return true;
}
void PrefabPublicHandler::EntityIdListToEntityList(const EntityIdList& inputEntityIds, EntityList& outEntities)
{
outEntities.reserve(inputEntityIds.size());
for (AZ::EntityId entityId : inputEntityIds)
{
if (entityId.IsValid())
{
outEntities.emplace_back(GetEntityById(entityId));
}
}
}
}
}
@@ -27,8 +27,10 @@ namespace AzToolsFramework
namespace Prefab
{
class Instance;
class InstanceEntityMapperInterface;
class InstanceToTemplateInterface;
class PrefabLoaderInterface;
class PrefabSystemComponentInterface;
class PrefabPublicHandler final
@@ -42,7 +44,7 @@ namespace AzToolsFramework
void UnregisterPrefabPublicHandlerInterface();
// PrefabPublicInterface...
PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZStd::string_view filePath) override;
PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath) override;
PrefabOperationResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, AZ::Vector3 position) override;
PrefabOperationResult SavePrefab(AZ::IO::Path filePath) override;
PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) override;
@@ -61,19 +63,46 @@ namespace AzToolsFramework
private:
PrefabOperationResult DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants);
bool RetrieveAndSortPrefabEntitiesAndInstances(const EntityList& inputEntities, const Instance& commonRootEntityOwningInstance,
bool RetrieveAndSortPrefabEntitiesAndInstances(const EntityList& inputEntities, Instance& commonRootEntityOwningInstance,
EntityList& outEntities, AZStd::vector<AZStd::unique_ptr<Instance>>& outInstances) const;
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
/**
* Creates a link between the templates of an instance and its parent.
*
* \param topLevelEntities The list of entities that are immediate children to the container entity of the instance.
* \param sourceInstance The instance that corresponds to the source template of the link.
* \param targetInstance The id of the target template.
* \param undoBatch The undo batch to set as parent for this create link action.
* \param commonRootEntityId The id of the entity that the source instance should be parented under.
*/
void CreateLink(
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId);
/**
* Given a list of entityIds, finds the prefab instance that owns the common root entity of the entityIds.
*
* \param entityIds The list of entity ids.
* \param inputEntityList The list of entities corresponding to the entity ids.
* \param topLevelEntities The list of entities that are immediate children of the common root entity.
* \param commonRootEntityId The entity id of the common root entity of all the entityIds.
* \param commonRootEntityOwningInstance The owning instance of the common root entity.
* \return PrefabOperationResult indicating whether the action was successful or not.
*/
PrefabOperationResult FindCommonRootOwningInstance(
const AZStd::vector<AZ::EntityId>& entityIds, EntityList& inputEntityList, EntityList& topLevelEntities,
AZ::EntityId& commonRootEntityId, InstanceOptionalReference& commonRootEntityOwningInstance);
static Instance* GetParentInstance(Instance* instance);
static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant);
static void GenerateContainerEntityTransform(const EntityList& topLevelEntities, AZ::Vector3& translation, AZ::Quaternion& rotation);
static void EntityIdListToEntityList(const EntityIdList& inputEntityIds, EntityList& outEntities);
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
// Caches entity states for undo/redo purposes
@@ -49,7 +49,7 @@ namespace AzToolsFramework
* @param filePath The path for the new prefab file.
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
*/
virtual PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZStd::string_view filePath) = 0;
virtual PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath) = 0;
/**
* Instantiate a prefab from a prefab file.
@@ -104,7 +104,6 @@ namespace AzToolsFramework
return nullptr;
}
AZStd::unique_ptr<Instance> newInstance = AZStd::make_unique<Instance>(AZStd::move(containerEntity));
for (AZ::Entity* entity : entities)
@@ -122,6 +121,7 @@ namespace AzToolsFramework
}
newInstance->SetTemplateSourcePath(relativeFilePath);
newInstance->SetContainerEntityName(relativeFilePath.Stem().Native());
TemplateId newTemplateId = CreateTemplateFromInstance(*newInstance);
if (newTemplateId == InvalidTemplateId)
@@ -142,7 +142,6 @@ namespace AzToolsFramework
void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId)
{
UpdatePrefabInstances(templateId);
auto templateIdToLinkIdsIterator = m_templateToLinkIdsMap.find(templateId);
if (templateIdToLinkIdsIterator != m_templateToLinkIdsMap.end())
{
@@ -153,15 +152,24 @@ namespace AzToolsFramework
templateIdToLinkIdsIterator->second.end()));
UpdateLinkedInstances(linkIdsToUpdateQueue);
}
else
{
UpdatePrefabInstances(templateId);
}
}
void PrefabSystemComponent::UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom)
{
PrefabDom& templateDomToUpdate = FindTemplateDom(templateId);
if (AZ::JsonSerialization::Compare(templateDomToUpdate, updatedDom) != AZ::JsonSerializerCompareResult::Equal)
auto templateToUpdate = FindTemplate(templateId);
if (templateToUpdate)
{
templateDomToUpdate.CopyFrom(updatedDom, templateDomToUpdate.GetAllocator());
PropagateTemplateChanges(templateId);
PrefabDom& templateDomToUpdate = templateToUpdate->get().GetPrefabDom();
if (AZ::JsonSerialization::Compare(templateDomToUpdate, updatedDom) != AZ::JsonSerializerCompareResult::Equal)
{
templateDomToUpdate.CopyFrom(updatedDom, templateDomToUpdate.GetAllocator());
templateToUpdate->get().MarkAsDirty(true);
PropagateTemplateChanges(templateId);
}
}
}
@@ -498,12 +506,14 @@ namespace AzToolsFramework
return false;
}
Template& sourceTemplate = sourceTemplateReference->get();
Template& targetTemplate = targetTemplateReference->get();
#if defined(AZ_ENABLE_TRACING)
Template& sourceTemplate = sourceTemplateReference->get();
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
const AZStd::string& targetTemplateFilePath = targetTemplate.GetFilePath().Native();
const AZStd::string& sourceTemplateFilePath = sourceTemplate.GetFilePath().Native();
#endif
LinkId newLinkId = CreateUniqueLinkId();
Link newLink(newLinkId);
@@ -613,7 +623,12 @@ namespace AzToolsFramework
instancesValue = memberFound->value;
}
instancesValue->get().AddMember(rapidjson::StringRef(instanceAlias.c_str()), PrefabDomValue(), targetTemplateDom.GetAllocator());
// Only add the instance if it's not there already
if (instancesValue->get().FindMember(rapidjson::StringRef(instanceAlias.c_str())) == instancesValue->get().MemberEnd())
{
instancesValue->get().AddMember(
rapidjson::StringRef(instanceAlias.c_str()), PrefabDomValue(), targetTemplateDom.GetAllocator());
}
Template& sourceTemplate = sourceTemplateRef->get();
@@ -731,10 +746,11 @@ namespace AzToolsFramework
}
Template& sourceTemplate = sourceTemplateReference->get();
#if defined(AZ_ENABLE_TRACING)
Template& targetTemplate = targetTemplateReference->get();
#endif
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
const AZStd::string& targetTemplateFilePath = targetTemplate.GetFilePath().Native();
link.SetSourceTemplateId(sourceTemplateId);
link.SetTargetTemplateId(targetTemplateId);
@@ -35,8 +35,8 @@ namespace AzToolsFramework
}
void PrefabUndoInstance::Capture(
PrefabDom& initialState,
PrefabDom& endState,
const PrefabDom& initialState,
const PrefabDom& endState,
const TemplateId& templateId)
{
m_templateId = templateId;
@@ -79,13 +79,16 @@ namespace AzToolsFramework
//generate undo/redo patches
m_instanceToTemplateInterface->GeneratePatch(m_redoPatch, initialState, endState);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(m_redoPatch, entityId);
m_instanceToTemplateInterface->GeneratePatch(m_undoPatch, endState, initialState);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(m_undoPatch, entityId);
}
void PrefabUndoEntityUpdate::Undo()
{
bool isPatchApplicationSuccessful =
m_instanceToTemplateInterface->PatchEntityInTemplate(m_undoPatch, m_entityAlias, m_templateId);
[[maybe_unused]] bool isPatchApplicationSuccessful =
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId);
AZ_Error(
"Prefab", isPatchApplicationSuccessful,
"Applying the undo patch on the entity with alias '%s' in template with id '%llu' was unsuccessful", m_entityAlias.c_str(),
@@ -94,8 +97,9 @@ namespace AzToolsFramework
void PrefabUndoEntityUpdate::Redo()
{
bool isPatchApplicationSuccessful =
m_instanceToTemplateInterface->PatchEntityInTemplate(m_redoPatch, m_entityAlias, m_templateId);
[[maybe_unused]] bool isPatchApplicationSuccessful =
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId);
AZ_Error(
"Prefab", isPatchApplicationSuccessful,
"Applying the redo patch on the entity with alias '%s' in template with id '%llu' was unsuccessful", m_entityAlias.c_str(),
@@ -120,7 +124,7 @@ namespace AzToolsFramework
const TemplateId& targetId,
const TemplateId& sourceId,
const InstanceAlias& instanceAlias,
const PrefabDomReference linkDom,
PrefabDomReference linkDom,
const LinkId linkId)
{
m_targetId = targetId;
@@ -51,8 +51,8 @@ namespace AzToolsFramework
explicit PrefabUndoInstance(const AZStd::string& undoOperationName);
void Capture(
PrefabDom& initialState,
PrefabDom& endState,
const PrefabDom& initialState,
const PrefabDom& endState,
const TemplateId& templateId);
void Undo() override;
@@ -101,7 +101,7 @@ namespace AzToolsFramework
const TemplateId& targetId,
const TemplateId& sourceId,
const InstanceAlias& instanceAlias,
const PrefabDomReference linkDom = PrefabDomReference(),
PrefabDomReference linkDom = PrefabDomReference(),
const LinkId linkId = InvalidLinkId);
void Undo() override;
@@ -0,0 +1,59 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/PrefabUndo.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
namespace AzToolsFramework
{
namespace Prefab
{
namespace PrefabUndoHelpers
{
void UpdatePrefabInstance(
const Instance& instance, AZStd::string_view undoMessage, const PrefabDom& instanceDomBeforeUpdate,
UndoSystem::URSequencePoint* undoBatch)
{
PrefabDom instanceDomAfterUpdate;
PrefabDomUtils::StoreInstanceInPrefabDom(instance, instanceDomAfterUpdate);
PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage);
state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, instance.GetTemplateId());
state->SetParent(undoBatch);
state->Redo();
}
void CreateLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDomReference patch,
const InstanceAlias& instanceAlias, UndoSystem::URSequencePoint* undoBatch)
{
auto linkAddUndo = aznew PrefabUndoInstanceLink("Create Link");
linkAddUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, patch, InvalidLinkId);
linkAddUndo->SetParent(undoBatch);
linkAddUndo->Redo();
}
void RemoveLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias,
LinkId linkId, UndoSystem::URSequencePoint* undoBatch)
{
auto linkRemoveUndo = aznew PrefabUndoInstanceLink("Remove Link");
PrefabDom emptyLinkDom;
linkRemoveUndo->Capture(
targetTemplateId, sourceTemplateId, instanceAlias, emptyLinkDom, linkId);
linkRemoveUndo->SetParent(undoBatch);
linkRemoveUndo->Redo();
}
}
} // namespace Prefab
} // namespace AzToolsFramework
@@ -0,0 +1,32 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
namespace AzToolsFramework
{
namespace Prefab
{
namespace PrefabUndoHelpers
{
void UpdatePrefabInstance(
const Instance& instance, AZStd::string_view undoMessage, const PrefabDom& instanceDomBeforeUpdate,
UndoSystem::URSequencePoint* undoBatch);
void CreateLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDomReference patch,
const InstanceAlias& instanceAlias, UndoSystem::URSequencePoint* undoBatch);
void RemoveLink(
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias,
LinkId linkId, UndoSystem::URSequencePoint* undoBatch);
}
} // namespace Prefab
} // namespace AzToolsFramework
@@ -41,7 +41,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
}
prefabProcessorContext.ListPrefabs(
[this, &serializeContext, &prefabProcessorContext](AZStd::string_view prefabName, PrefabDom& prefab)
[this, &serializeContext, &prefabProcessorContext]([[maybe_unused]] AZStd::string_view prefabName, PrefabDom& prefab)
{
auto result = RemoveEditorInfo(prefab, serializeContext, prefabProcessorContext);
if (!result)
@@ -163,7 +163,8 @@ namespace AzToolsFramework
QMainWindow* mainWindow = nullptr;
for (QWidget* w : qApp->topLevelWidgets())
{
if (mainWindow = qobject_cast<QMainWindow*>(w))
mainWindow = qobject_cast<QMainWindow*>(w);
if (mainWindow)
{
return mainWindow;
}
@@ -2662,7 +2662,7 @@ namespace AzToolsFramework
QString cleanAssetSafeFolder(QDir::cleanPath(assetSafeFolder.c_str()));
// Compare using clean paths so slash direction does not matter.
// Note that this comparison is case sensitive because some file systems
// Lumberyard supports are case sensitive.
// Open 3D Engine supports are case sensitive.
if (cleanSaveAs.startsWith(cleanAssetSafeFolder))
{
isPathSafeForAssets = true;
@@ -4017,8 +4017,8 @@ namespace AzToolsFramework
// Detach entities action currently acts on entities and all descendants, so include those as part of the selection
AzToolsFramework::EntityIdList selectedDetachEntities(selectedTransformHierarchyEntities.begin(), selectedTransformHierarchyEntities.end());
// A selection in Lumberyard is usually singular, but a selection can have more than one entity.
// No Lumberyard systems support multiple selections, or multiple different groups of selected entities.
// A selection in Open 3D Engine is usually singular, but a selection can have more than one entity.
// No Open 3D Engine systems support multiple selections, or multiple different groups of selected entities.
QString detachEntitiesActionText(QObject::tr("Selection"));
QString detachEntitiesTooltipText;
if (selectedDetachEntities.size() == 1)
@@ -71,7 +71,7 @@ namespace AzToolsFramework
AZ_Assert(!s_perforceConn, "You may only have one Perforce component.\n");
m_shutdownThreadSignal = false;
m_waitingOnTrust = false;
m_autoChangelistDescription = "*Lumberyard Auto";
m_autoChangelistDescription = "*Open 3D Engine Auto";
m_connectionState = SourceControlState::Disabled;
m_validConnection = false;
m_testConnection = false;
@@ -10,12 +10,14 @@
*
*/
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
AZ_PUSH_DISABLE_WARNING(4127 4251 4800 4244, "-Wunknown-warning-option") // 4127: conditional expression is constant
// 4251: 'QTextCodec::ConverterState::flags': class 'QFlags<QTextCodec::ConversionFlag>' needs to have dll-interface to be used by clients of struct 'QTextCodec::ConverterState'
// 4800: 'QTextBoundaryFinderPrivate *const ': forcing value to bool 'true' or 'false' (performance warning)
// 4244: conversion from 'int' to 'qint8', possible loss of data
#include <QtConcurrent/QtConcurrent>
#include <QThreadPool>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
@@ -80,7 +82,11 @@ namespace AzToolsFramework
if (m_state == State::Unloaded)
{
m_state = State::Loading;
QFuture<void> future = QtConcurrent::run([this](){ LoadThread(); });
QThreadPool* threadPool;
ThumbnailContextRequestBus::BroadcastResult(
threadPool,
&ThumbnailContextRequestBus::Handler::GetThreadPool);
QFuture<void> future = QtConcurrent::run(threadPool, [this](){ LoadThread(); });
m_watcher.setFuture(future);
}
}
@@ -28,10 +28,15 @@ namespace AzToolsFramework
: m_missingThumbnail(new MissingThumbnail(thumbnailSize))
, m_loadingThumbnail(new LoadingThumbnail(thumbnailSize))
, m_thumbnailSize(thumbnailSize)
, m_threadPool(this)
{
ThumbnailContextRequestBus::Handler::BusConnect();
}
ThumbnailContext::~ThumbnailContext() = default;
ThumbnailContext::~ThumbnailContext()
{
ThumbnailContextRequestBus::Handler::BusDisconnect();
}
bool ThumbnailContext::IsLoading(SharedThumbnailKey key)
{
@@ -53,6 +58,11 @@ namespace AzToolsFramework
AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast(&AzToolsFramework::AssetBrowser::AssetBrowserViewRequests::Update);
}
QThreadPool* ThumbnailContext::GetThreadPool()
{
return &m_threadPool;
}
SharedThumbnail ThumbnailContext::GetThumbnail(SharedThumbnailKey key)
{
SharedThumbnail thumbnail;
@@ -19,6 +19,7 @@
#include <QObject>
#include <QList>
#include <QThreadPool>
#endif
class QString;
@@ -40,6 +41,7 @@ namespace AzToolsFramework
*/
class ThumbnailContext
: public QObject
, public ThumbnailContextRequestBus::Handler
{
Q_OBJECT
public:
@@ -58,10 +60,13 @@ namespace AzToolsFramework
void UnregisterThumbnailProvider(const char* providerName);
void RedrawThumbnail();
//! Default context used for most thumbnails
static constexpr const char* DefaultContext = "Default";
// ThumbnailContextRequestBus::Handler interface overrides...
QThreadPool* GetThreadPool() override;
private:
struct ProviderCompare {
bool operator() (const SharedThumbnailProvider& lhs, const SharedThumbnailProvider& rhs) const
@@ -79,6 +84,9 @@ namespace AzToolsFramework
SharedThumbnail m_loadingThumbnail;
//! Thumbnail size (width and height in pixels)
int m_thumbnailSize;
//! There is only a limited number of threads on global threadPool, because there can be many thumbnails rendering at once
//! an individual threadPool is needed to avoid deadlocks
QThreadPool m_threadPool;
};
} // namespace Thumbnailer
} // namespace AzToolsFramework
@@ -79,7 +79,12 @@ namespace AzToolsFramework
int realHeight = qMin(aznumeric_cast<int>(originalWidth /aspectRatio), originalHeight);
int realWidth = aznumeric_cast<int>(realHeight * aspectRatio);
int x = (originalWidth - realWidth) / 2;
painter.drawPixmap(QRect(x, 0, realHeight, realWidth), pixmap);
// pixmap needs to be manually scaled to produce smoother result and avoid looking pixelated
// using painter.setRenderHint(QPainter::SmoothPixmapTransform); does not seem to work
// Note: there is a potential issue with pixmap.scaled:
// it is multithreaded (using global threadPool) and blocking until finished.
// A deadlock will happen if global threadPool has no free threads available.
painter.drawPixmap(QPoint(x, 0), pixmap.scaled(realWidth, realHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
QWidget::paintEvent(event);
}
@@ -17,11 +17,23 @@
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
class QPixmap;
class QThreadPool;
namespace AzToolsFramework
{
namespace Thumbnailer
{
//! Interaction with thumbnail context
class ThumbnailContextRequests
: public AZ::EBusTraits
{
public:
//! Get thread pool for drawing thumbnails
virtual QThreadPool* GetThreadPool() = 0;
};
using ThumbnailContextRequestBus = AZ::EBus<ThumbnailContextRequests>;
//! Interaction with thumbnailer
class ThumbnailerRequests
: public AZ::EBusTraits
@@ -24,7 +24,7 @@ namespace AzToolsFramework
{
QString ComponentTypeMimeData::GetMimeType()
{
return "application/x-amazon-lumberyard-editorcomponenttypes";
return "application/x-amazon-o3de-editorcomponenttypes";
}
AZStd::unique_ptr<QMimeData> ComponentTypeMimeData::Create(const ClassDataContainer& container)
@@ -99,7 +99,7 @@ namespace AzToolsFramework
QString ComponentMimeData::GetMimeType()
{
return "application/x-amazon-lumberyard-editorcomponentdata";
return "application/x-amazon-o3de-editorcomponentdata";
}
AZStd::unique_ptr<QMimeData> ComponentMimeData::Create(const ComponentDataContainer& components)
@@ -15,7 +15,7 @@
* Header file for the editor component base class.
* Derive from this class to create a version of a component to use in the
* editor, as opposed to the version of the component that is used during run time.
* To learn more about editor components, see the [Lumberyard Developer Guide]
* To learn more about editor components, see the [Open 3D Engine Developer Guide]
* (http://docs.aws.amazon.com/lumberyard/latest/developerguide/component-entity-system-pg-editor-components.html).
*/
@@ -52,7 +52,7 @@ namespace AzToolsFramework
* To create one or more game components to represent your editor component
* in runtime, use BuildGameEntity().
*
* To learn more about editor components, see the [Lumberyard Developer Guide]
* To learn more about editor components, see the [Open 3D Engine Developer Guide]
* (http://docs.aws.amazon.com/lumberyard/latest/developerguide/component-entity-system-pg-editor-components.html).
*/
class EditorComponentBase
@@ -408,7 +408,7 @@ namespace AzToolsFramework
QString fullLayerPath(layerFolder.filePath(myLayerFileName));
// Lumberyard will read in the layer in whatever format it's in, so there's no need to check what the save format is set to.
// Open 3D Engine will read in the layer in whatever format it's in, so there's no need to check what the save format is set to.
// The save format is also set in this object that is being loaded, so it wouldn't even be available.
m_loadedLayer = AZ::Utils::LoadObjectFromFile<EditorLayer>(fullLayerPath.toUtf8().data());
@@ -1051,7 +1051,6 @@ namespace AzToolsFramework
currentFailure));
// FileIO doesn't support removing directory, so use Qt.
// QDir::IsEmpty isn't available until a newer version fo Qt than Lumberyard is using.
if (layerTempFolder.entryInfoList(
QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden).count() == 0)
{
@@ -1485,7 +1484,7 @@ namespace AzToolsFramework
if (!newLayerEntityId.IsValid())
{
return LayerResult(LayerResultStatus::Error, "Lumberyard was unable to create a layer entity.");
return LayerResult(LayerResultStatus::Error, "Open 3D Engine was unable to create a layer entity.");
}
// If this new layer has a parent, then set its parent.
@@ -58,7 +58,7 @@ namespace AzToolsFramework
AZ::Color m_color = AZ::Color::CreateOne();
// Default to text files, so the save history is easier to understand in source control.
// This attribute only effects writing layers, and is safe to store here instead of on the component.
// When reading files off disk, Lumberyard figures out the correct format automatically.
// When reading files off disk, Open 3D Engine figures out the correct format automatically.
bool m_saveAsBinary = false;
// The layer entity needs to be invisible to all other systems, so they don't show up in the viewport.
@@ -338,7 +338,7 @@ namespace AzToolsFramework
EditorLayer* m_loadedLayer = nullptr;
AZStd::string m_layerFileName;
// Lumberyard's serialization system requires everything in the editor to have a serialized to disk counterpart.
// Open 3D Engine's serialization system requires everything in the editor to have a serialized to disk counterpart.
// Layers have their data split into two categories: Stuff that should save to the layer file, and stuff that should
// save to the layer component in the level. To allow the layer component to edit the data that goes in the layer file,
// a placeholder value is serialized. This is only used at edit time, and is copied and cleared during serialization.
@@ -13,6 +13,7 @@
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/ToString.h>
namespace AzToolsFramework
@@ -44,7 +45,9 @@ namespace AzToolsFramework
->DataElement(
AZ::Edit::UIHandlers::Default, &EditorNonUniformScaleComponent::m_scale, "Non-uniform Scale",
"Non-uniform scale for this entity only (does not propagate through hierarchy)")
->Attribute(AZ::Edit::Attributes::Min, AZ::MinNonUniformScale)
->Attribute(AZ::Edit::Attributes::Min, AZ::MinTransformScale)
->Attribute(AZ::Edit::Attributes::Max, AZ::MaxTransformScale)
->Attribute(AZ::Edit::Attributes::Step, 0.1f)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorNonUniformScaleComponent::OnScaleChanged)
;
}
@@ -106,13 +109,13 @@ namespace AzToolsFramework
void EditorNonUniformScaleComponent::SetScale(const AZ::Vector3& scale)
{
if (scale.GetMinElement() >= AZ::MinNonUniformScale)
if (scale.GetMinElement() >= AZ::MinTransformScale && scale.GetMaxElement() <= AZ::MaxTransformScale)
{
m_scale = scale;
}
else
{
AZ::Vector3 clampedScale = scale.GetMax(AZ::Vector3(AZ::MinNonUniformScale));
AZ::Vector3 clampedScale = scale.GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale));
AZ_Warning("Editor Non-uniform Scale Component", false, "SetScale value was clamped from %s to %s for entity %s",
AZ::ToString(scale).c_str(), AZ::ToString(clampedScale).c_str(), GetEntity()->GetName().c_str());
m_scale = clampedScale;
@@ -1276,7 +1276,6 @@ namespace AzToolsFramework
Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)->
DataElement(TransformScaleHandler, &EditorTransform::m_scale, "Scale", "Local Scale")->
Attribute(AZ::Edit::Attributes::Step, 0.1f)->
Attribute(AZ::Edit::Attributes::Min, 0.01f)->
Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)
;
}
@@ -12,6 +12,7 @@
#include "AzToolsFramework_precompiled.h"
#include <ToolsComponents/TransformScalePropertyHandler.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector3.h>
namespace AzToolsFramework
@@ -36,8 +37,8 @@ namespace AzToolsFramework
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestWrite, newCtrl);
});
newCtrl->setMinimum(0.01f);
newCtrl->setMaximum(std::numeric_limits<float>::max());
newCtrl->setMinimum(AZ::MinTransformScale);
newCtrl->setMaximum(AZ::MaxTransformScale);
return newCtrl;
}
@@ -56,6 +56,16 @@ namespace AzToolsFramework
return QPixmap();
}
bool EditorEntityUiHandlerBase::CanToggleLockVisibility(AZ::EntityId /*entityId*/) const
{
return true;
}
bool EditorEntityUiHandlerBase::CanRename(AZ::EntityId /*entityId*/) const
{
return true;
}
void EditorEntityUiHandlerBase::PaintItemBackground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const
{
}
@@ -45,6 +45,10 @@ namespace AzToolsFramework
virtual QString GenerateItemTooltip(AZ::EntityId entityId) const;
//! Returns the item icon pixmap to display in the Outliner.
virtual QPixmap GenerateItemIcon(AZ::EntityId entityId) const;
//! Returns whether the element's lock and visibility state should be accessible in the Outliner
virtual bool CanToggleLockVisibility(AZ::EntityId entityId) const;
//! Returns whether the element's name should be editable
virtual bool CanRename(AZ::EntityId entityId) const;
//! Paints the background of the item in the Outliner.
virtual void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
@@ -97,8 +97,6 @@ namespace AzToolsFramework
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
layerColor, entityId, &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::GetLayerColor);
const QTreeView* outlinerTreeView(qobject_cast<const QTreeView*>(option.widget));
int indentation = outlinerTreeView->indentation();
bool isFirstColumn = index.column() == EntityOutlinerListModel::ColumnName;
bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value<bool>() && index.model()->hasChildren(index);
@@ -112,7 +112,6 @@ namespace LegacyFramework
m_applicationEntity = NULL;
m_ptrSystemEntity = NULL;
m_applicationModule[0] = 0;
m_appRoot[0] = 0;
}
HMODULE Application::GetMainModule()
@@ -193,6 +192,10 @@ namespace LegacyFramework
// if we're in console mode, listen for CTRL+C
::SetConsoleCtrlHandler(CTRL_BREAK_HandlerRoutine, true);
#endif
m_ptrCommandLineParser = aznew AzFramework::CommandLine();
m_ptrCommandLineParser->Parse(m_desc.m_argc, m_desc.m_argv);
// If we don't have one create a serialize context
if (GetSerializeContext() == nullptr)
{
@@ -490,15 +493,7 @@ namespace LegacyFramework
AZ_Assert(!m_desc.m_enableProjectManager || m_desc.m_enableGUI, "Enabling the project manager in the application settings requires enabling the GUI as well.");
// if we're a GUI APP we need the UI Framework component:
if (m_desc.m_enableGUI)
{
EnsureComponentCreated(AzToolsFramework::Framework::RTTI_Type());
}
else
{
EnsureComponentCreated(AzToolsFramework::Framework::RTTI_Type());
}
EnsureComponentCreated(AzToolsFramework::Framework::RTTI_Type());
}
//=========================================================================
@@ -16,7 +16,7 @@
#include "NewLogTabDialog.h"
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include <UI/Logging/ui_NewLogTabDialog.h>
#include <AzToolsFramework/UI/Logging/ui_NewLogTabDialog.h>
AZ_POP_DISABLE_WARNING
#include <QPushButton>
#include <QLineEdit>
@@ -97,4 +97,4 @@ namespace AzToolsFramework
} // namespace LogPanel
} // namespace AzToolsFramework
#include "UI/Logging/moc_NewLogTabDialog.cpp"
#include "UI/Logging/moc_NewLogTabDialog.cpp"
@@ -351,58 +351,68 @@ namespace AzToolsFramework
QVariant EntityOutlinerListModel::dataForVisibility(const QModelIndex& index, int role) const
{
auto id = GetEntityFromIndex(index);
auto entityId = GetEntityFromIndex(index);
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
switch (role)
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
switch (role)
{
case Qt::CheckStateRole:
{
return IsEntitySetToBeVisible(id) ? Qt::Checked : Qt::Unchecked;
}
{
return IsEntitySetToBeVisible(entityId) ? Qt::Checked : Qt::Unchecked;
}
case Qt::ToolTipRole:
{
return QString("Show/Hide Entity");
}
{
return QString("Show/Hide Entity");
}
case Qt::SizeHintRole:
{
return QSize(20, 20);
{
return QSize(20, 20);
}
}
return dataForAll(index, role);
}
return dataForAll(index, role);
return QVariant();
}
QVariant EntityOutlinerListModel::dataForLock(const QModelIndex& index, int role) const
{
auto id = GetEntityFromIndex(index);
auto entityId = GetEntityFromIndex(index);
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
switch (role)
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
switch (role)
{
case Qt::CheckStateRole:
{
bool isLocked = false;
// Lock state is tracked in 3 places:
// EditorLockComponent, EditorEntityModel, and ComponentEntityObject.
// In addition to that, entities that are in layers can have the layer's lock state override their own.
// Retrieving the lock state from the lock component is ideal for drawing the lock icon in the outliner because
// the outliner needs to show that specific entity's lock state, and not the actual final lock state including the layer behavior.
// The EditorLockComponent only knows about the specific entity's lock state and not the hierarchy.
EditorLockComponentRequestBus::EventResult(
isLocked, id, &EditorLockComponentRequests::GetLocked);
{
bool isLocked = false;
// Lock state is tracked in 3 places:
// EditorLockComponent, EditorEntityModel, and ComponentEntityObject.
// In addition to that, entities that are in layers can have the layer's lock state override their own.
// Retrieving the lock state from the lock component is ideal for drawing the lock icon in the outliner because
// the outliner needs to show that specific entity's lock state, and not the actual final lock state including the layer
// behavior. The EditorLockComponent only knows about the specific entity's lock state and not the hierarchy.
EditorLockComponentRequestBus::EventResult(isLocked, entityId, &EditorLockComponentRequests::GetLocked);
return isLocked ? Qt::Checked : Qt::Unchecked;
}
return isLocked ? Qt::Checked : Qt::Unchecked;
}
case Qt::ToolTipRole:
{
return QString("Lock/Unlock Entity (Locked means the entity cannot be moved in the viewport)");
}
{
return QString("Lock/Unlock Entity (Locked means the entity cannot be moved in the viewport)");
}
case Qt::SizeHintRole:
{
return QSize(20, 20);
{
return QSize(20, 20);
}
}
return dataForAll(index, role);
}
return dataForAll(index, role);
return QVariant();
}
QVariant EntityOutlinerListModel::dataForSortIndex(const QModelIndex& index, int role) const
@@ -429,8 +439,12 @@ namespace AzToolsFramework
if (value.canConvert<Qt::CheckState>())
{
const auto entityId = GetEntityFromIndex(index);
switch (index.column())
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
switch (index.column())
{
case ColumnVisibilityToggle:
ToggleEntityVisibility(entityId);
break;
@@ -438,6 +452,7 @@ namespace AzToolsFramework
case ColumnLockToggle:
ToggleEntityLockState(entityId);
break;
}
}
}
}
@@ -826,7 +841,6 @@ namespace AzToolsFramework
for (const ComponentAssetPair& pair : componentAssetPairs)
{
const AZ::TypeId& componentType = pair.first;
const AZ::Data::AssetId& assetId = pair.second;
componentsToAdd.push_back(componentType);
}
@@ -853,7 +867,6 @@ namespace AzToolsFramework
}
// Assign asset associated with each created component.
const AZ::Entity::ComponentArrayType& componentsAdded = addComponentsOutcome.GetValue()[targetEntityId].m_componentsAdded;
for (const ComponentAssetPair& pair : componentAssetPairs)
{
const AZ::TypeId& componentType = pair.first;
@@ -932,6 +945,12 @@ namespace AzToolsFramework
return false;
}
// Disable reparenting to the root level
if (!newParentId.IsValid())
{
return false;
}
// Ignore entities not owned by the editor context. It is assumed that all entities belong
// to the same context since multiple selection doesn't span across views.
for (const AZ::EntityId& entityId : selectedEntityIds)
@@ -961,39 +980,33 @@ namespace AzToolsFramework
}
}
if (newParentId.IsValid())
bool isLayerEntity = false;
Layers::EditorLayerComponentRequestBus::EventResult(
isLayerEntity,
entityId,
&Layers::EditorLayerComponentRequestBus::Events::HasLayer);
// Layers can only have other layers as parents, or have no parent.
if (isLayerEntity)
{
bool isLayerEntity = false;
bool newParentIsLayer = false;
Layers::EditorLayerComponentRequestBus::EventResult(
isLayerEntity,
entityId,
newParentIsLayer,
newParentId,
&Layers::EditorLayerComponentRequestBus::Events::HasLayer);
// Layers can only have other layers as parents, or have no parent.
if (isLayerEntity)
if (!newParentIsLayer)
{
bool newParentIsLayer = false;
Layers::EditorLayerComponentRequestBus::EventResult(
newParentIsLayer,
newParentId,
&Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (!newParentIsLayer)
{
return false;
}
return false;
}
}
}
//Only check the entity pointer if the entity id is valid because
//we want to allow dragging items to unoccupied parts of the tree to un-parent them
if (newParentId.IsValid())
AZ::Entity* newParentEntity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(newParentEntity, &AZ::ComponentApplicationRequests::FindEntity, newParentId);
if (!newParentEntity)
{
AZ::Entity* newParentEntity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(newParentEntity, &AZ::ComponentApplicationRequests::FindEntity, newParentId);
if (!newParentEntity)
{
return false;
}
return false;
}
//reject dragging on to yourself or your children
@@ -1331,14 +1344,15 @@ namespace AzToolsFramework
emit EnableSelectionUpdates(false);
auto parentIndex = GetIndexFromEntity(parentId);
auto childIndex = GetIndexFromEntity(childId);
beginRemoveRows(parentIndex, childIndex.row(), childIndex.row());
beginResetModel();
}
void EntityOutlinerListModel::OnEntityInfoUpdatedRemoveChildEnd(AZ::EntityId parentId, AZ::EntityId childId)
{
(void)childId;
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
endRemoveRows();
endResetModel();
//must refresh partial lock/visibility of parents
m_isFilterDirty = true;
@@ -1978,7 +1992,8 @@ namespace AzToolsFramework
}
// Retrieve the Entity UI Handler
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
auto firstColumnIndex = index.siblingAtColumn(0);
AZ::EntityId entityId(firstColumnIndex.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
const bool isSelected = (option.state & QStyle::State_Selected);
@@ -2001,8 +2016,11 @@ namespace AzToolsFramework
case EntityOutlinerListModel::ColumnVisibilityToggle:
case EntityOutlinerListModel::ColumnLockToggle:
{
// Paint the Visibility and Lock state checkboxes
PaintCheckboxes(painter, option, index, isHovered);
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
// Paint the Visibility and Lock state checkboxes
PaintCheckboxes(painter, option, index, isHovered);
}
}
break;
case EntityOutlinerListModel::ColumnName:
@@ -30,6 +30,7 @@
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerDisplayOptionsMenu.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.hxx>
@@ -49,7 +50,7 @@
#include <QTimer>
#include <QToolButton>
#include <UI/Outliner/ui_EntityOutlinerWidget.h>
#include <AzToolsFramework/UI/Outliner/ui_EntityOutlinerWidget.h>
namespace
{
@@ -184,6 +185,7 @@ namespace AzToolsFramework
m_gui->m_objectTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_gui->m_objectTree->setAutoScrollMargin(20);
m_gui->m_objectTree->setIndentation(24);
m_gui->m_objectTree->setRootIsDecorated(false);
connect(m_gui->m_objectTree, &QTreeView::customContextMenuRequested, this, &EntityOutlinerWidget::OnOpenTreeContextMenu);
// custom item delegate
@@ -270,6 +272,12 @@ namespace AzToolsFramework
m_listModel->Initialize();
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert(
m_editorEntityUiInterface != nullptr,
"EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize.");
EditorPickModeNotificationBus::Handler::BusConnect(GetEntityContextId());
EntityHighlightMessages::Bus::Handler::BusConnect();
EntityOutlinerModelNotificationBus::Handler::BusConnect();
@@ -561,7 +569,13 @@ namespace AzToolsFramework
if (m_selectedEntityIds.size() == 1)
{
contextMenu->addAction(m_actionToRenameSelection);
auto entityId = m_selectedEntityIds.front();
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanRename(entityId))
{
contextMenu->addAction(m_actionToRenameSelection);
}
}
if (m_selectedEntityIds.size() == 1)
@@ -687,11 +701,17 @@ namespace AzToolsFramework
if (m_selectedEntityIds.size() == 1)
{
const QModelIndex proxyIndex = GetIndexFromEntityId(m_selectedEntityIds.front());
if (proxyIndex.isValid())
auto entityId = m_selectedEntityIds.front();
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanRename(entityId))
{
m_gui->m_objectTree->setCurrentIndex(proxyIndex);
m_gui->m_objectTree->QTreeView::edit(proxyIndex);
const QModelIndex proxyIndex = GetIndexFromEntityId(entityId);
if (proxyIndex.isValid())
{
m_gui->m_objectTree->setCurrentIndex(proxyIndex);
m_gui->m_objectTree->QTreeView::edit(proxyIndex);
}
}
}
}
@@ -804,10 +824,10 @@ namespace AzToolsFramework
addAction(m_actionToDeleteSelectionAndDescendants);
m_actionToRenameSelection = new QAction(tr("Rename"), this);
#ifdef Q_OS_MAC
#if defined(Q_OS_MAC)
// "Alt+Return" translates to Option+Return on macOS
m_actionToRenameSelection->setShortcut(tr("Alt+Return"));
#elseif Q_OS_WIN
#elif defined(Q_OS_WIN)
m_actionToRenameSelection->setShortcut(tr("F2"));
#endif
m_actionToRenameSelection->setShortcutContext(Qt::WidgetWithChildrenShortcut);
@@ -1089,16 +1109,6 @@ namespace AzToolsFramework
setEnabled(true);
SetEntityOutlinerState(m_gui, true);
}
void EntityOutlinerWidget::SetRootEntity(AZ::EntityId rootEntityId)
{
// The proxy model needs a tick to initialize, else it will return an invalid index in mapFromSource.
QTimer::singleShot(0, this, [rootEntityId, this]() {
QModelIndex rootIndex = m_listModel->GetIndexFromEntity(rootEntityId);
QModelIndex proxyIndex = m_proxyModel->mapFromSource(rootIndex);
m_gui->m_objectTree->setRootIndex(proxyIndex);
});
}
void EntityOutlinerWidget::SetUpdatesEnabled(bool enable)
{
@@ -1114,6 +1124,12 @@ namespace AzToolsFramework
}
}
void EntityOutlinerWidget::ExpandEntityChildren(AZ::EntityId entityId)
{
QModelIndex index = GetIndexFromEntityId(entityId);
m_gui->m_objectTree->expand(index);
}
void EntityOutlinerWidget::OnEntityInfoUpdatedAddChildEnd(AZ::EntityId /*parentId*/, AZ::EntityId childId)
{
QueueContentUpdateSort(childId);
@@ -42,6 +42,7 @@ namespace Ui
namespace AzToolsFramework
{
class EditorEntityUiInterface;
class EntityOutlinerListModel;
class EntityOutlinerSortFilterProxyModel;
@@ -106,8 +107,8 @@ namespace AzToolsFramework
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
// EntityOutlinerWidgetInterface
void SetRootEntity(AZ::EntityId rootEntityId) override;
void SetUpdatesEnabled(bool enable) override;
void ExpandEntityChildren(AZ::EntityId entityId) override;
// Build a selection object from the given entities. Entities already in the Widget's selection buffers are ignored.
template <class EntityIdCollection>
@@ -193,6 +194,8 @@ namespace AzToolsFramework
EntityIdSet m_entitiesToSort;
EntityOutliner::DisplaySortMode m_sortMode;
bool m_sortContentQueued;
EditorEntityUiInterface* m_editorEntityUiInterface = nullptr;
};
}
@@ -22,8 +22,8 @@ namespace AzToolsFramework
public:
AZ_RTTI(EntityOutlinerWidgetInterface, "{30C0F252-EC84-4196-BF59-EB9E73B8ADCB}");
virtual void SetRootEntity(AZ::EntityId rootEntityId) = 0;
virtual void SetUpdatesEnabled(bool enable) = 0;
virtual void ExpandEntityChildren(AZ::EntityId entityId) = 0;
};
} // namespace AzToolsFramework
@@ -0,0 +1,98 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
#include <QAbstractItemModel>
#include <QPainter>
#include <QPainterPath>
#include <QTreeView>
namespace AzToolsFramework
{
const QColor LevelRootUiHandler::m_levelRootBorderColor = QColor("#656565");
const QString LevelRootUiHandler::m_levelRootIconPath = QString(":/Level/level.svg");
LevelRootUiHandler::LevelRootUiHandler()
{
m_prefabEditInterface = AZ::Interface<Prefab::PrefabEditInterface>::Get();
if (m_prefabEditInterface == nullptr)
{
AZ_Assert(false, "LevelRootUiHandler - could not get PrefabEditInterface on LevelRootUiHandler construction.");
return;
}
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
if (m_prefabPublicInterface == nullptr)
{
AZ_Assert(false, "LevelRootUiHandler - could not get PrefabPublicInterface on LevelRootUiHandler construction.");
return;
}
}
QPixmap LevelRootUiHandler::GenerateItemIcon(AZ::EntityId /*entityId*/) const
{
return QPixmap(m_levelRootIconPath);
}
QString LevelRootUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const
{
QString infoString;
AZ::IO::Path path = m_prefabPublicInterface->GetOwningInstancePrefabPath(entityId);
if (!path.empty())
{
infoString =
QObject::tr("<span style=\"font-style: italic; font-weight: 400;\">(%1)</span>").arg(path.Filename().Native().data());
}
return infoString;
}
bool LevelRootUiHandler::CanToggleLockVisibility(AZ::EntityId /*entityId*/) const
{
return false;
}
bool LevelRootUiHandler::CanRename(AZ::EntityId /*entityId*/) const
{
return false;
}
void LevelRootUiHandler::PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
{
if (!painter)
{
AZ_Warning("LevelRootUiHandler", false, "LevelRootUiHandler - painter is nullptr, can't draw Prefab outliner background.");
return;
}
QPen borderLinePen(m_levelRootBorderColor, m_levelRootBorderThickness);
QRect rect = option.rect;
rect.setLeft(rect.left() + (m_levelRootBorderThickness / 2));
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setPen(borderLinePen);
// Draw border at the bottom
painter->drawLine(rect.bottomLeft(), rect.bottomRight());
}
}
@@ -0,0 +1,51 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabEditInterface;
class PrefabPublicInterface;
};
class LevelRootUiHandler
: public EditorEntityUiHandlerBase
{
public:
AZ_CLASS_ALLOCATOR(LevelRootUiHandler, AZ::SystemAllocator, 0);
AZ_RTTI(AzToolsFramework::LevelRootUiHandler, "{B1D3B270-CD29-4033-873A-D78E76AB24A4}", EditorEntityUiHandlerBase);
LevelRootUiHandler();
~LevelRootUiHandler() override = default;
// EditorEntityUiHandler...
QPixmap GenerateItemIcon(AZ::EntityId entityId) const override;
QString GenerateItemInfoString(AZ::EntityId entityId) const override;
bool CanToggleLockVisibility(AZ::EntityId entityId) const override;
bool CanRename(AZ::EntityId entityId) const override;
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
private:
Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr;
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
static constexpr int m_levelRootBorderThickness = 1;
static const QColor m_levelRootBorderColor;
static const QString m_levelRootIconPath;
};
} // namespace AzToolsFramework
@@ -24,6 +24,7 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
@@ -39,9 +40,12 @@ namespace AzToolsFramework
{
namespace Prefab
{
EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr;
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr;
PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr;
const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab";
void PrefabUserSettings::Reflect(AZ::ReflectContext* context)
@@ -79,6 +83,13 @@ namespace AzToolsFramework
return;
}
s_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
if (s_prefabLoaderInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get PrefabLoaderInterface on PrefabIntegrationManager construction.");
return;
}
EditorContextMenuBus::Handler::BusConnect();
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
AZ::Interface<PrefabIntegrationInterface>::Register(this);
@@ -130,14 +141,14 @@ namespace AzToolsFramework
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
if (prefabWipFeaturesEnabled)
{
// Create Prefab
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
if (!selectedEntities.empty())
{
bool layerInSelection = false;
@@ -186,10 +197,6 @@ namespace AzToolsFramework
// Edit/Save Prefab
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
if (selectedEntities.size() == 1)
{
AZ::EntityId selectedEntity = selectedEntities[0];
@@ -237,6 +244,14 @@ namespace AzToolsFramework
{
menu->addSeparator();
}
QAction* deleteAction = menu->addAction(QObject::tr("Delete"));
QObject::connect(deleteAction, &QAction::triggered, deleteAction, [this] { ContextMenu_DeleteSelected(); });
if (selectedEntities.size() == 0 ||
(selectedEntities.size() == 1 && s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0])))
{
deleteAction->setDisabled(true);
}
}
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
@@ -316,14 +331,15 @@ namespace AzToolsFramework
GenerateSuggestedFilenameFromEntities(prefabRootEntities, suggestedName);
if (!QueryUserForPrefabSaveLocation(suggestedName, targetDirectory, AZ_CRC("PrefabUserSettings"), activeWindow, prefabName, prefabFilePath))
if (!QueryUserForPrefabSaveLocation(
suggestedName, targetDirectory, AZ_CRC("PrefabUserSettings"), activeWindow, prefabName, prefabFilePath))
{
// User canceled prefab creation, or error prevented continuation.
return;
}
}
auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, prefabFilePath);
auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, s_prefabLoaderInterface->GetRelativePathToProject(prefabFilePath.data()));
if (!createPrefabOutcome.IsSuccess())
{
@@ -369,6 +385,16 @@ namespace AzToolsFramework
}
}
void PrefabIntegrationManager::ContextMenu_DeleteSelected()
{
AzToolsFramework::EntityIdList selectedEntityIds;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntityIds, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
&AzToolsFramework::ToolsApplicationRequests::DeleteEntitiesAndAllDescendants, selectedEntityIds);
}
void PrefabIntegrationManager::GenerateSuggestedFilenameFromEntities(const EntityIdList& entityIds, AZStd::string& outName)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@@ -630,7 +656,7 @@ namespace AzToolsFramework
QString cleanAssetSafeFolder(QDir::cleanPath(assetSafeFolder.c_str()));
// Compare using clean paths so slash direction does not matter.
// Note that this comparison is case sensitive because some file systems
// Lumberyard supports are case sensitive.
// Open 3D Engine supports are case sensitive.
if (cleanSaveAs.startsWith(cleanAssetSafeFolder))
{
isPathSafeForAssets = true;
@@ -972,7 +998,14 @@ namespace AzToolsFramework
void PrefabIntegrationManager::OnPrefabComponentActivate(AZ::EntityId entityId)
{
s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId());
if (s_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId))
{
s_editorEntityUiInterface->RegisterEntity(entityId, m_levelRootUiHandler.GetHandlerId());
}
else
{
s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId());
}
}
void PrefabIntegrationManager::OnPrefabComponentDeactivate(AZ::EntityId entityId)
@@ -19,6 +19,7 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserSourceDropBus.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
@@ -28,6 +29,9 @@ namespace AzToolsFramework
{
namespace Prefab
{
class PrefabLoaderInterface;
//! Structure for saving/retrieving user settings related to prefab workflows.
class PrefabUserSettings
: public AZ::UserSettings
@@ -77,6 +81,9 @@ namespace AzToolsFramework
// Manages the Edit Mode UI for prefabs
PrefabEditManager m_prefabEditManager;
// Used to handle the UI for the level root
LevelRootUiHandler m_levelRootUiHandler;
// Used to handle the UI for prefab entities
PrefabUiHandler m_prefabUiHandler;
@@ -85,6 +92,7 @@ namespace AzToolsFramework
static void ContextMenu_InstantiatePrefab();
static void ContextMenu_EditPrefab(AZ::EntityId containerEntity);
static void ContextMenu_SavePrefab(AZ::EntityId containerEntity);
static void ContextMenu_DeleteSelected();
// Prompt and resolve dialogs
static bool QueryUserForPrefabSaveLocation(
@@ -124,6 +132,7 @@ namespace AzToolsFramework
static EditorEntityUiInterface* s_editorEntityUiInterface;
static PrefabPublicInterface* s_prefabPublicInterface;
static PrefabEditInterface* s_prefabEditInterface;
static PrefabLoaderInterface* s_prefabLoaderInterface;
};
}
}
@@ -100,7 +100,7 @@ AZ_PUSH_DISABLE_WARNING(4244 4251 4800, "-Wunknown-warning-option") // 4244: con
#include <QTimer>
AZ_POP_DISABLE_WARNING
#include <UI/PropertyEditor/ui_EntityPropertyEditor.h>
#include <AzToolsFramework/UI/PropertyEditor/ui_EntityPropertyEditor.h>
// This has to live outside of any namespaces due to issues on Linux with calls to Q_INIT_RESOURCE if they are inside a namespace
void initEntityPropertyEditorResources()
@@ -310,6 +310,9 @@ namespace AzToolsFramework
{
initEntityPropertyEditorResources();
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
AZ_Assert(m_prefabPublicInterface != nullptr, "EntityPropertyEditor requires a PrefabPublicInterface instance on Initialize.");
setObjectName("EntityPropertyEditor");
setAcceptDrops(true);
@@ -405,8 +408,6 @@ namespace AzToolsFramework
CreateActions();
UpdateContents();
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
EditorEntityContextNotificationBus::Handler::BusConnect();
//forced to register global event filter with application for selection
@@ -693,11 +694,38 @@ namespace AzToolsFramework
m_gui->m_entityIcon->repaint();
}
EntityPropertyEditor::InspectorLayout EntityPropertyEditor::GetCurrentInspectorLayout() const
{
if (!m_prefabsAreEnabled)
{
return m_isLevelEntityEditor ? InspectorLayout::LEVEL : InspectorLayout::ENTITY;
}
AZ::EntityId levelContainerEntityId = m_prefabPublicInterface->GetLevelInstanceContainerEntityId();
if (AZStd::find(m_selectedEntityIds.begin(), m_selectedEntityIds.end(), levelContainerEntityId) != m_selectedEntityIds.end())
{
if (m_selectedEntityIds.size() > 1)
{
return InspectorLayout::INVALID;
}
else
{
return InspectorLayout::LEVEL;
}
}
else
{
return InspectorLayout::ENTITY;
}
}
void EntityPropertyEditor::UpdateEntityDisplay()
{
UpdateStatusComboBox();
if (m_isLevelEntityEditor)
InspectorLayout layout = GetCurrentInspectorLayout();
if (layout == InspectorLayout::LEVEL)
{
AZStd::string levelName;
AzToolsFramework::EditorRequestBus::BroadcastResult(levelName, &AzToolsFramework::EditorRequests::GetLevelName);
@@ -737,13 +765,20 @@ namespace AzToolsFramework
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
SelectionEntityTypeInfo result = SelectionEntityTypeInfo::None;
if (m_isLevelEntityEditor)
InspectorLayout layout = GetCurrentInspectorLayout();
if (layout == InspectorLayout::LEVEL)
{
// The Level Inspector should only have a list of selectable components after the
// level entity itself is valid (i.e. "selected").
return selection.empty() ? SelectionEntityTypeInfo::None : SelectionEntityTypeInfo::LevelEntity;
}
if (layout == InspectorLayout::INVALID)
{
return SelectionEntityTypeInfo::Mixed;
}
for (AZ::EntityId selectedEntityId : selection)
{
bool isLayerEntity = false;
@@ -909,16 +944,18 @@ namespace AzToolsFramework
}
}
bool isLevelLayout = GetCurrentInspectorLayout() == InspectorLayout::LEVEL;
m_gui->m_entityDetailsLabel->setText(entityDetailsLabelText);
m_gui->m_entityDetailsLabel->setVisible(entityDetailsVisible);
m_gui->m_entityNameEditor->setVisible(hasEntitiesDisplayed);
m_gui->m_entityNameLabel->setVisible(hasEntitiesDisplayed);
m_gui->m_entityIcon->setVisible(hasEntitiesDisplayed);
m_gui->m_pinButton->setVisible(m_overrideSelectedEntityIds.empty() && hasEntitiesDisplayed && !m_isSystemEntityEditor && !m_isLevelEntityEditor);
m_gui->m_statusLabel->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !m_isLevelEntityEditor);
m_gui->m_statusComboBox->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !m_isLevelEntityEditor);
m_gui->m_entityIdLabel->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !m_isLevelEntityEditor);
m_gui->m_entityIdText->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !m_isLevelEntityEditor);
m_gui->m_pinButton->setVisible(m_overrideSelectedEntityIds.empty() && hasEntitiesDisplayed && !m_isSystemEntityEditor);
m_gui->m_statusLabel->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
m_gui->m_statusComboBox->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
m_gui->m_entityIdLabel->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
m_gui->m_entityIdText->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
bool displayComponentSearchBox = hasEntitiesDisplayed;
if (hasEntitiesDisplayed)
@@ -941,7 +978,7 @@ namespace AzToolsFramework
UpdateEntityDisplay();
}
m_gui->m_darkBox->setVisible(displayComponentSearchBox && !m_isSystemEntityEditor && !m_isLevelEntityEditor);
m_gui->m_darkBox->setVisible(displayComponentSearchBox && !m_isSystemEntityEditor && !isLevelLayout);
m_gui->m_entitySearchBox->setVisible(displayComponentSearchBox);
bool displayAddComponentMenu = CanAddComponentsToSelection(selectionEntityTypeInfo);
@@ -32,7 +32,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponentBus.h>
#include <AzQtComponents/Components/LumberyardStylesheet.h>
#include <AzQtComponents/Components/O3DEStylesheet.h>
#include <QtWidgets/QWidget>
#include <QtGui/QIcon>
@@ -521,6 +521,15 @@ namespace AzToolsFramework
bool m_isSystemEntityEditor;
bool m_isLevelEntityEditor = false;
enum class InspectorLayout
{
ENTITY = 0, // All selected entities are regular entities
LEVEL, // The selected entity is the level prefab container entity
INVALID // Other entities are selected alongside the level prefab container entity
};
InspectorLayout GetCurrentInspectorLayout() const;
// the spacer's job is to make sure that its always at the end of the list of components.
QSpacerItem* m_spacer;
bool m_isAlreadyQueuedRefresh;
@@ -38,6 +38,7 @@ AZ_POP_DISABLE_WARNING
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Asset/SimpleAsset.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
@@ -62,6 +63,7 @@ AZ_POP_DISABLE_WARNING
#include <UI/PropertyEditor/Model/AssetCompleterModel.h>
#include <UI/PropertyEditor/View/AssetCompleterListView.h>
#include <UI/PropertyEditor/ThumbnailDropDown.h>
namespace AzToolsFramework
{
@@ -95,6 +97,12 @@ namespace AzToolsFramework
m_thumbnail->setFixedSize(QSize(24, 24));
m_thumbnail->setVisible(false);
m_thumbnailDropDown = new ThumbnailDropDown(this);
m_thumbnailDropDown->setFixedSize(QSize(40, 24));
m_thumbnailDropDown->setVisible(false);
connect(m_thumbnailDropDown, &ThumbnailDropDown::clicked, this, &PropertyAssetCtrl::OnEditButtonClicked);
m_editButton = new QToolButton(this);
m_editButton->setAutoRaise(true);
m_editButton->setIcon(QIcon(":/stylesheet/img/UI20/open-in-internal-app.svg"));
@@ -104,6 +112,7 @@ namespace AzToolsFramework
connect(m_editButton, &QToolButton::clicked, this, &PropertyAssetCtrl::OnEditButtonClicked);
pLayout->addWidget(m_thumbnail);
pLayout->addWidget(m_thumbnailDropDown);
pLayout->addWidget(m_browseEdit);
pLayout->addWidget(m_editButton);
@@ -1082,8 +1091,9 @@ namespace AzToolsFramework
void PropertyAssetCtrl::UpdateThumbnail()
{
m_thumbnail->setVisible(m_showThumbnail);
m_thumbnailDropDown->setVisible(m_showThumbnailDropDown);
if (m_showThumbnail)
if (m_showThumbnail || m_showThumbnailDropDown)
{
const AZ::Data::AssetId assetID = GetCurrentAssetID();
if (assetID.IsValid())
@@ -1098,13 +1108,21 @@ namespace AzToolsFramework
if (result)
{
SharedThumbnailKey thumbnailKey = MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, assetID);
m_thumbnail->SetThumbnailKey(thumbnailKey, Thumbnailer::ThumbnailContext::DefaultContext);
if (m_showThumbnail)
{
m_thumbnail->SetThumbnailKey(thumbnailKey, Thumbnailer::ThumbnailContext::DefaultContext);
}
if (m_showThumbnailDropDown)
{
m_thumbnailDropDown->SetThumbnailKey(thumbnailKey, Thumbnailer::ThumbnailContext::DefaultContext);
}
return;
}
}
}
m_thumbnail->ClearThumbnail();
m_thumbnailDropDown->ClearThumbnail();
}
void PropertyAssetCtrl::SetClearButtonEnabled(bool enable)
@@ -1138,6 +1156,16 @@ namespace AzToolsFramework
return m_showThumbnail;
}
void PropertyAssetCtrl::SetShowThumbnailDropDown(bool enable)
{
m_showThumbnailDropDown = enable;
}
bool PropertyAssetCtrl::GetShowThumbnailDropDown() const
{
return m_showThumbnailDropDown;
}
const AZ::Uuid& AssetPropertyHandlerDefault::GetHandledType() const
{
return AZ::GetAssetClassId();
@@ -1185,9 +1213,8 @@ namespace AzToolsFramework
if (!QFile::exists(path))
{
const char* engineRoot = nullptr;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath);
QDir engineDir = engineRoot ? QDir(engineRoot) : QDir::current();
AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath();
QDir engineDir = !engineRoot.empty() ? QDir(QString(engineRoot.c_str())) : QDir::current();
path = engineDir.absoluteFilePath(iconPath.c_str());
}
@@ -1248,7 +1275,7 @@ namespace AzToolsFramework
GUI->SetBrowseButtonIcon(QIcon(iconPath.c_str()));
}
}
else if (attrib == AZ_CRC_CE("ShowThumbnail"))
else if (attrib == AZ_CRC_CE("Thumbnail"))
{
bool showThumbnail = false;
if (attrValue->Read<bool>(showThumbnail))
@@ -1256,6 +1283,19 @@ namespace AzToolsFramework
GUI->SetShowThumbnail(showThumbnail);
}
}
else if (attrib == AZ_CRC_CE("ThumbnailWithDropDown"))
{
PropertyAssetCtrl::EditCallbackType* func = azdynamic_cast<PropertyAssetCtrl::EditCallbackType*>(attrValue->GetAttribute());
if (func)
{
GUI->SetShowThumbnailDropDown(true);
GUI->SetEditNotifyCallback(func);
}
else
{
GUI->SetEditNotifyCallback(nullptr);
}
}
}
void AssetPropertyHandlerDefault::WriteGUIValuesIntoProperty(size_t index, PropertyAssetCtrl* GUI, property_t& instance, InstanceDataNode* node)
@@ -45,6 +45,7 @@ namespace AzToolsFramework
{
class AssetCompleterModel;
class AssetCompleterListView;
class ThumbnailDropDown;
namespace Thumbnailer
{
@@ -94,6 +95,7 @@ namespace AzToolsFramework
void OnAssetIDChanged(AZ::Data::AssetId newAssetID);
protected:
ThumbnailDropDown* m_thumbnailDropDown = nullptr;
Thumbnailer::ThumbnailWidget* m_thumbnail = nullptr;
QPushButton* m_errorButton = nullptr;
QToolButton* m_editButton = nullptr;
@@ -156,6 +158,8 @@ namespace AzToolsFramework
bool m_showThumbnail = false;
bool m_showThumbnailDropDown = false;
// ! Default suffix used in the field's placeholder text when a default value is set.
const char* m_DefaultSuffix = " (default)";
@@ -205,6 +209,8 @@ namespace AzToolsFramework
void SetShowThumbnail(bool enable);
bool GetShowThumbnail() const;
void SetShowThumbnailDropDown(bool enable);
bool GetShowThumbnailDropDown() const;
void SetSelectedAssetID(const AZ::Data::AssetId& newID);
void SetCurrentAssetType(const AZ::Data::AssetType& newType);
@@ -89,7 +89,7 @@ namespace AzToolsFramework
//! When set, disables data access for this property editor.
//! This prevents any value refreshes from the inspected values from occurring as well as disabling user input.
void PreventDataAccess(bool shouldPrevent);
// LUMBERYARD_DEPRECATED(LY-120821)
// O3DE_DEPRECATED(LY-120821)
void PreventRefresh(bool shouldPrevent){PreventDataAccess(shouldPrevent);}
void SetAutoResizeLabels(bool autoResizeLabels);
@@ -0,0 +1,101 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzToolsFramework/Debug/TraceContext.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: 'QRawFont::d': class 'QExplicitlySharedDataPointer<QRawFontPrivate>' needs to have dll-interface to be used by clients of class 'QRawFont'
// 4800: 'QTextEngine *const ': forcing value to bool 'true' or 'false' (performance warning)
#include <QLabel>
#include <QHBoxLayout>
#include <QEvent>
#include <QPainter>
#include <UI/UICore/AspectRatioAwarePixmapWidget.hxx>
#include <Thumbnails/ThumbnailWidget.h>
AZ_POP_DISABLE_WARNING
#include "ThumbnailDropDown.h"
namespace AzToolsFramework
{
ThumbnailDropDown::ThumbnailDropDown(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* pLayout = new QHBoxLayout();
pLayout->setContentsMargins(0, 0, 0, 0);
pLayout->setSpacing(0);
m_thumbnail = new Thumbnailer::ThumbnailWidget(this);
m_thumbnail->setFixedSize(QSize(24, 24));
m_dropDownArrow = new AspectRatioAwarePixmapWidget(this);
m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png"));
m_dropDownArrow->setFixedSize(QSize(8, 24));
m_emptyThumbnail = new QLabel(this);
m_emptyThumbnail->setPixmap(QPixmap(":/stylesheet/img/line.png"));
m_emptyThumbnail->setFixedSize(QSize(24, 24));
pLayout->addWidget(m_emptyThumbnail);
pLayout->addWidget(m_thumbnail);
pLayout->addSpacing(4);
pLayout->addWidget(m_dropDownArrow);
pLayout->addSpacing(4);
setLayout(pLayout);
}
void ThumbnailDropDown::SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName)
{
m_emptyThumbnail->setVisible(false);
m_thumbnail->SetThumbnailKey(key, contextName);
}
void ThumbnailDropDown::ClearThumbnail()
{
m_emptyThumbnail->setVisible(true);
m_thumbnail->ClearThumbnail();
}
bool ThumbnailDropDown::event(QEvent* e)
{
if (isEnabled())
{
if (e->type() == QEvent::MouseButtonPress)
{
emit clicked();
return true; //ignore
}
}
return QWidget::event(e);
}
void ThumbnailDropDown::paintEvent(QPaintEvent* e)
{
QPainter p(this);
QRect targetRect(QPoint(), QSize(40, 24));
p.fillRect(targetRect, QColor(17, 17, 17)); // #111111
QWidget::paintEvent(e);
}
void ThumbnailDropDown::enterEvent(QEvent* e)
{
m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0_highlighted.png"));
QWidget::enterEvent(e);
}
void ThumbnailDropDown::leaveEvent(QEvent* e)
{
m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png"));
QWidget::leaveEvent(e);
}
}
#include "UI/PropertyEditor/moc_ThumbnailDropDown.cpp"
@@ -0,0 +1,59 @@
#pragma once
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if !defined(Q_MOC_RUN)
#include <AzCore/PlatformDef.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...'
// 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
#include <QWidget>
AZ_POP_DISABLE_WARNING
#endif
namespace AzToolsFramework
{
class AspectRatioAwarePixmapWidget;
namespace Thumbnailer
{
class ThumbnailWidget;
}
class ThumbnailDropDown : public QWidget
{
Q_OBJECT
public:
explicit ThumbnailDropDown(QWidget* parent = nullptr);
//! Call this to set what thumbnail widget will display
void SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName = "Default");
//! Remove current thumbnail
void ClearThumbnail();
bool event(QEvent* e) override;
Q_SIGNALS:
void clicked();
protected:
void paintEvent(QPaintEvent* e) override;
void enterEvent(QEvent* e) override;
void leaveEvent(QEvent* e) override;
private:
Thumbnailer::ThumbnailWidget* m_thumbnail = nullptr;
QLabel* m_emptyThumbnail = nullptr;
AspectRatioAwarePixmapWidget* m_dropDownArrow = nullptr;
};
}
@@ -11,7 +11,7 @@
*/
#include "AzToolsFramework_precompiled.h"
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include "UI/Slice/ui_NotificationWindow.h"
#include <AzToolsFramework/UI/Slice/ui_NotificationWindow.h>
AZ_POP_DISABLE_WARNING
#include "UI/Slice/Constants.h"
@@ -14,7 +14,7 @@
#include "OverwritePromptDialog.hxx"
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include <UI/UICore/ui_OverwritePromptDialog.h>
#include <AzToolsFramework/UI/UICore/ui_OverwritePromptDialog.h>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
@@ -54,4 +54,4 @@ namespace AzToolsFramework
}
#include "UI/UICore/moc_OverwritePromptDialog.cpp"
#include "UI/UICore/moc_OverwritePromptDialog.cpp"
@@ -18,7 +18,7 @@
#include <AzToolsFramework/UI/UICore/ProgressShield.hxx>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include <UI/UICore/ui_ProgressShield.h>
#include <AzToolsFramework/UI/UICore/ui_ProgressShield.h>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
@@ -118,4 +118,4 @@ namespace AzToolsFramework
} // namespace AzToolsFramework
#include "UI/UICore/moc_ProgressShield.cpp"
#include "UI/UICore/moc_ProgressShield.cpp"
@@ -14,7 +14,7 @@
#include "SaveChangesDialog.hxx"
AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option")
#include <UI/UICore/ui_SaveChangesDialog.h>
#include <AzToolsFramework/UI/UICore/ui_SaveChangesDialog.h>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
@@ -52,4 +52,4 @@ namespace AzToolsFramework
}
}
#include "UI/UICore/moc_SaveChangesDialog.cpp"
#include "UI/UICore/moc_SaveChangesDialog.cpp"
@@ -246,7 +246,7 @@ namespace UnitTest
R"(... client unittest_workspace)" "\r\n"
R"(... status pending)" "\r\n"
R"(... changeType public)" "\r\n"
R"(... desc *Lumberyard Auto)" "\r\n"
R"(... desc *Open 3D Engine Auto)" "\r\n"
"\r\n";
}
else if (m_commandArgs.starts_with("fstat"))
@@ -65,7 +65,7 @@ namespace AzToolsFramework
if (!contextMenu.m_menu->isEmpty())
{
contextMenu.m_menu->popup(QCursor::pos());
contextMenu.m_menu->exec(QCursor::pos());
}
}
}
@@ -21,7 +21,12 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
class QPoint;
class QPoint; // LYN-2315 in-progress, remove this
namespace AzFramework
{
struct ScreenPoint;
}
namespace AzToolsFramework
{
@@ -235,12 +240,12 @@ namespace AzToolsFramework
/// Restores the cursor and ends locking it in place, allowing it to be moved freely.
virtual void EndCursorCapture() = 0;
/// Gets the most recent recorded cursor position in the viewport in screen space coordinates.
virtual QPoint ViewportCursorScreenPosition() = 0;
virtual AzFramework::ScreenPoint ViewportCursorScreenPosition() = 0;
/// Gets the cursor position recorded prior to the most recent cursor position.
/// Note: The cursor may be captured by the viewport, in which case this may not correspond to the last result
/// from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse
/// position delta.
virtual AZStd::optional<QPoint> PreviousViewportCursorScreenPosition() = 0;
virtual AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() = 0;
protected:
~ViewportMouseCursorRequests() = default;
@@ -202,13 +202,19 @@ namespace AzToolsFramework
return mouseInteractionEvent.m_wheelDelta;
}
/// Return Qt QPoint from an Viewport ScreenPoint.
/// Return QPoint from AzFramework::ScreenPoint.
inline QPoint QPointFromScreenPoint(const AzFramework::ScreenPoint& screenPoint)
{
return {screenPoint.m_x, screenPoint.m_y};
}
/// Map from Qt -> Lumberyard buttons.
/// Return AzFramework::ScreenPoint from QPoint.
inline AzFramework::ScreenPoint ScreenPointFromQPoint(const QPoint& qpoint)
{
return AzFramework::ScreenPoint{qpoint.x(), qpoint.y()};
}
/// Map from Qt -> Open 3D Engine buttons.>>>>>>> main
inline AZ::u32 TranslateMouseButtons(const Qt::MouseButtons buttons)
{
AZ::u32 result = 0;
@@ -218,7 +224,7 @@ namespace AzToolsFramework
return result;
}
/// Map from Qt -> Lumberyard modifiers.
/// Map from Qt -> Open 3D Engine modifiers.
inline AZ::u32 TranslateKeyboardModifiers(const Qt::KeyboardModifiers modifiers)
{
AZ::u32 result = 0;
@@ -228,13 +234,13 @@ namespace AzToolsFramework
return result;
}
/// Interface to translate Qt modifiers to Lumberyard modifiers.
/// Interface to translate Qt modifiers to Open 3D Engine modifiers.
inline KeyboardModifiers BuildKeyboardModifiers(const Qt::KeyboardModifiers modifiers)
{
return KeyboardModifiers(TranslateKeyboardModifiers(modifiers));
}
/// Interface to translate Qt buttons to Lumberyard buttons.
/// Interface to translate Qt buttons to Open 3D Engine buttons.
inline MouseButtons BuildMouseButtons(const Qt::MouseButtons buttons)
{
return MouseButtons(TranslateMouseButtons(buttons));
@@ -53,7 +53,7 @@ namespace AzToolsFramework
float, cl_viewportGizmoAxisLabelOffset, 1.15f, nullptr, AZ::ConsoleFunctorFlags::Null,
"The offset of the label for the viewport axis gizmo");
AZ_CVAR(
float, cl_viewportGizmoAxisLabelSize, 2.0f, nullptr, AZ::ConsoleFunctorFlags::Null,
float, cl_viewportGizmoAxisLabelSize, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null,
"The size of each label for the viewport axis gizmo");
AZ_CVAR(
AZ::Vector2, cl_viewportGizmoAxisScreenPosition, AZ::Vector2(0.045f, 0.9f), nullptr,
@@ -1626,7 +1626,7 @@ namespace AzToolsFramework
const AZ::Vector3 uniformScale = AZ::Vector3(action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset()));
const AZ::Vector3 scale = (AZ::Vector3::CreateOne() +
(uniformScale / initialScale)).GetMax(AZ::Vector3(0.01f));
(uniformScale / initialScale)).GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale));
const AZ::Transform scaleTransform = AZ::Transform::CreateScale(scale);
if (action.m_modifiers.Alt())
@@ -3501,19 +3501,22 @@ namespace AzToolsFramework
const auto cameraProjection = AzFramework::CameraProjection(gizmoCameraState);
// screen space offset to move the 2d gizmo around
const AZ::Vector3 screenPosition =
(AZ::Vector2ToVector3(cl_viewportGizmoAxisScreenPosition) - AZ::Vector3(0.5f, 0.5f, 0.0f)) *
AZ::Vector2ToVector3(gizmoCameraState.m_viewportSize);
const AZ::Vector2 screenOffset = AZ::Vector2(cl_viewportGizmoAxisScreenPosition) - AZ::Vector2(0.5f, 0.5f);
// map from a position in world space (relative to the the gizmo camera near the origin) to a position in
// screen space
const auto calculateGizmoAxis =
[&cameraView, &cameraProjection, &gizmoCameraState, &screenPosition]
(const AZ::Vector3& position)
[&cameraView, &cameraProjection, &screenOffset]
(const AZ::Vector3& axis)
{
return AZ::Vector2ToVector3(AzFramework::Vector2FromScreenPoint(
AzFramework::WorldToScreen(
position, cameraView, cameraProjection, gizmoCameraState.m_viewportSize))) + screenPosition;
auto result = AZ::Vector2(
AzFramework::WorldToScreenNDC(
axis,
cameraView,
cameraProjection)
);
result.SetY(1.0f - result.GetY());
return result + screenOffset;
};
// get all important axis positions in screen space
@@ -3523,31 +3526,31 @@ namespace AzToolsFramework
const auto gizmoEndAxisY = calculateGizmoAxis(-AZ::Vector3::CreateAxisY() * lineLength);
const auto gizmoEndAxisZ = calculateGizmoAxis(-AZ::Vector3::CreateAxisZ() * lineLength);
const AZ::Vector3 gizmoAxisX = gizmoEndAxisX - gizmoStart;
const AZ::Vector3 gizmoAxisY = gizmoEndAxisY - gizmoStart;
const AZ::Vector3 gizmoAxisZ = gizmoEndAxisZ - gizmoStart;
const AZ::Vector2 gizmoAxisX = gizmoEndAxisX - gizmoStart;
const AZ::Vector2 gizmoAxisY = gizmoEndAxisY - gizmoStart;
const AZ::Vector2 gizmoAxisZ = gizmoEndAxisZ - gizmoStart;
// draw the axes of the gizmo
debugDisplay.SetLineWidth(cl_viewportGizmoAxisLineWidth);
debugDisplay.SetColor(AZ::Colors::Red);
debugDisplay.DrawLine(gizmoStart, gizmoEndAxisX);
debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisX, 1.0f);
debugDisplay.SetColor(AZ::Colors::Lime);
debugDisplay.DrawLine(gizmoStart, gizmoEndAxisY);
debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisY, 1.0f);
debugDisplay.SetColor(AZ::Colors::Blue);
debugDisplay.DrawLine(gizmoStart, gizmoEndAxisZ);
debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisZ, 1.0f);
debugDisplay.SetLineWidth(1.0f);
const float labelOffset = cl_viewportGizmoAxisLabelOffset;
const auto labelOffsetX = gizmoStart + gizmoAxisX * labelOffset;
const auto labelOffsetY = gizmoStart + gizmoAxisY * labelOffset;
const auto labelOffsetZ = gizmoStart + gizmoAxisZ * labelOffset;
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize;
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize;
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize;
// draw the label of of each axis for the gizmo
const float labelSize = cl_viewportGizmoAxisLabelSize;
debugDisplay.SetColor(AZ::Colors::White);
debugDisplay.Draw2dTextLabel(labelOffsetX.GetX(), labelOffsetX.GetY(), labelSize, "X", true);
debugDisplay.Draw2dTextLabel(labelOffsetY.GetX(), labelOffsetY.GetY(), labelSize, "Y", true);
debugDisplay.Draw2dTextLabel(labelOffsetZ.GetX(), labelOffsetZ.GetY(), labelSize, "Z", true);
debugDisplay.Draw2dTextLabel(labelXScreenPosition.GetX(), labelXScreenPosition.GetY(), labelSize, "X", true);
debugDisplay.Draw2dTextLabel(labelYScreenPosition.GetX(), labelYScreenPosition.GetY(), labelSize, "Y", true);
debugDisplay.Draw2dTextLabel(labelZScreenPosition.GetX(), labelZScreenPosition.GetY(), labelSize, "Z", true);
}
void EditorTransformComponentSelection::DisplayViewportSelection2d(
@@ -417,6 +417,8 @@ set(FILES
UI/PropertyEditor/GrowTextEdit.cpp
UI/PropertyEditor/MultiLineTextEditHandler.h
UI/PropertyEditor/MultiLineTextEditHandler.cpp
UI/PropertyEditor/ThumbnailDropDown.h
UI/PropertyEditor/ThumbnailDropDown.cpp
UI/Slice/SlicePushWidget.cpp
UI/Slice/SlicePushWidget.hxx
UI/Slice/SliceOverridesNotificationWindow.cpp
@@ -649,6 +651,15 @@ set(FILES
Prefab/Instance/TemplateInstanceMapperInterface.h
Prefab/Link/Link.h
Prefab/Link/Link.cpp
Prefab/PrefabPublicHandler.h
Prefab/PrefabPublicHandler.cpp
Prefab/PrefabPublicInterface.h
Prefab/PrefabUndo.h
Prefab/PrefabUndo.cpp
Prefab/PrefabUndoCache.cpp
Prefab/PrefabUndoCache.h
Prefab/PrefabUndoHelpers.cpp
Prefab/PrefabUndoHelpers.h
Prefab/Spawnable/ComponentRequirementsValidator.h
Prefab/Spawnable/ComponentRequirementsValidator.cpp
Prefab/Spawnable/EditorInfoRemover.h
@@ -674,13 +685,6 @@ set(FILES
Prefab/Spawnable/SpawnableUtils.cpp
Prefab/Template/Template.h
Prefab/Template/Template.cpp
Prefab/PrefabUndo.h
Prefab/PrefabUndo.cpp
Prefab/PrefabUndoCache.cpp
Prefab/PrefabUndoCache.h
Prefab/PrefabPublicHandler.h
Prefab/PrefabPublicHandler.cpp
Prefab/PrefabPublicInterface.h
UI/Outliner/EntityOutlinerDisplayOptionsMenu.h
UI/Outliner/EntityOutlinerDisplayOptionsMenu.cpp
UI/Outliner/EntityOutlinerTreeView.hxx
@@ -705,6 +709,8 @@ set(FILES
UI/EditorEntityUi/EditorEntityUiSystemComponent.cpp
UI/Layer/LayerUiHandler.h
UI/Layer/LayerUiHandler.cpp
UI/Prefab/LevelRootUiHandler.h
UI/Prefab/LevelRootUiHandler.cpp
UI/Prefab/PrefabEditInterface.h
UI/Prefab/PrefabEditManager.h
UI/Prefab/PrefabEditManager.cpp