Files
o3de/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
T
Artur K 56904d1799 Cry Legacy: the Lessening (#5961)
* Generic Cleanup

Removals:

* Unused cvars,member variables and defines
* Windows media center edition support routines
* CSystem - removed debug_GetCallStackRaw/GetRootWindowMessageHandler/
UnloadDLL/ShutdownModuleLibraries

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Fix CryMessageBox return values in Windows build

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove named selection group and a few smaller unused functionalities

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove ObjectManager export functionality + 2 deprecated functions

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove object legacy freeze/hide support from ObjectManager

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove ObjectManager duplicate name dection, as well as object renaming

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove ObjectManager serialization and selection callbacks

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* ObjectManager - remove MoveObjects/HitTestObject/EndEditParams and related members/functions

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove a bunch of unused clone related functionality + misc

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* ObjectManager - misc removals

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* ObjectManager - more removals

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove unused object selection/retrieval in ObjectManager and LoadObjects in ObjectLoader

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* CBaseObject - remove unused material layers mask support

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* More CBaseObject cleanups.

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* CBaseObject - remove SubObj functions and IMouseCreateCallback

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove unused procedural floor management, helper scale and tags.

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* CBaseObject - remove more unused methods.

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Removals in multiple places.

CBaseObject - unused `OBJTYPE_DUMMY` flag removed, member `IsSameClass`
CObjectArchive removed unused methods/members
CObjectManager removed unused `Update` method

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* More removals

IDisplayViewport - HitTestLine/GetGridStep/setHitcontext/
GetConstructionPlane
Unused Cry_Matrix44 template specializations.

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* O3DE.exe Project-Centric "Open Editor" fix (#5852)

* The O3DE.exe Open Editor button now attempts to open the Editor in the
build directory of the project being opened.

If their is no Editor within the build directory of the Project, it uses
the Editor.exe in the current O3DE.exe executable directory if it exists

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Engine .gitignore now ignores the build directory if placed in the
AutomatedTesting project

Previously it was just ignoring a `[Bb]uild` directory if it was
directly within the engine root.
This change matches the behavior of the project templates.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Renamed the ProjectUtils GetEditorDirectory function to
GetEditorExecutablePath

Added a platform specific implementation for retrieving the path to the
Editor executable in the GetEditorExectuablePath function.

It first attempts to locate the Editor via checking the project build
directory for an Editor executable before falling back to checking the
binary directory of the currently running O3DE executable.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Correct the MacOS GetEditorExecutablePath to return the Editor path

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Adding missing C++20 std::erase implementations (#5735)

There were already implementations for std::erase_if.
This adds the counterpart AZStd::erase versions

resolves #5734

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* More removals and refactors

`ShowMessage` and `CryMessageBox` return void now
Simplify code in `CSystem::WarningV`
Remove unused `CryGetTicksPerSec`
Remove unused WinBase functionality
Replace `nanosleep` call in WinBase with `std::this_thread::sleep_for`
Remove unused Win32Wrapper routines
Remove unused IFunctorBase.h and IEntityObjectListener.h
Fix VectorAndArray.cpp compilation
Use QMessageBox instead of CryMessageBox in the editor.
Remove empty ArchiveVars platform specific files

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Fix test code.

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove an unused  function

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* CBaseObject and undo description removals.

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* A bunch of removals

* Remove IRenderNode
* Remove editor's KDTree
* Remove StatObjBus, InstanceStatObjEventBus::ReleaseData is redundant (
same functionality in the only available handler is triggered by
`OnCryEditorCloseScene`)
* Remove CExportManager::AddStatObj/AddMeshes/AddMesh,
IExportManager::ExportSingleStatObj
* Remove CIconManager/IIconManager::GetObject
* Remove CBaseObject::IntersectRayMesh
* Remove IIndexedMesh and related structs.
* Unused IUndoObject::GetDescription and all derived implementations.
* Unused CUndoBaseLibrary/CUndoBaseLibraryManager
* Unused Matrix34_tpl typedefs
* Legacy Xml classes cleanup

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* Remove unused `Vec3ToVector3D` helper

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

* fix - remove unused static variable

Signed-off-by: nemerle <96597+nemerle@users.noreply.github.com>

Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
2021-11-29 11:21:06 -07:00

1972 lines
75 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "SandboxIntegration.h"
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/RTTI/AttributeReader.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/numeric.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/Interface/Interface.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Physics/Material.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Visibility/BoundsBus.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EditorEntityAPI.h>
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/Commands/EntityStateCommand.h>
#include <AzToolsFramework/Commands/SelectionCommand.h>
#include <AzToolsFramework/Commands/SliceDetachEntityCommand.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <AzToolsFramework/Slice/SliceRequestBus.h>
#include <AzToolsFramework/Slice/SliceUtilities.h>
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
#include <AzToolsFramework/Undo/UndoSystem.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
#include <AzToolsFramework/UI/Layer/AddToLayerMenu.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
#include <AzToolsFramework/UI/Layer/NameConflictWarning.hxx>
#include <AzToolsFramework/ViewportSelection/EditorHelpers.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <MathConversion.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h>
#include "Objects/ComponentEntityObject.h"
#include "ISourceControl.h"
#include "UI/QComponentEntityEditorMainWindow.h"
#include <LmbrCentral/Rendering/EditorLightComponentBus.h>
#include <LmbrCentral/Scripting/TagComponentBus.h>
#include <LmbrCentral/Scripting/EditorTagComponentBus.h>
// Sandbox imports.
#include <Editor/ActionManager.h>
#include <Editor/CryEditDoc.h>
#include <Editor/GameEngine.h>
#include <Editor/DisplaySettings.h>
#include <Editor/IconManager.h>
#include <Editor/Settings.h>
#include <Editor/StringDlg.h>
#include <Editor/QtViewPaneManager.h>
#include <Editor/EditorViewportSettings.h>
#include <Editor/Util/PathUtil.h>
#include "CryEdit.h"
#include "Undo/Undo.h"
#include <QMenu>
#include <QAction>
#include <QWidgetAction>
#include <QHBoxLayout>
#include "MainWindow.h"
#include "Include/IObjectManager.h"
#include <AzCore/std/algorithm.h>
#ifdef CreateDirectory
#undef CreateDirectory
#endif
//////////////////////////////////////////////////////////////////////////
// Gathers all selected entities, culling any that have an ancestor in the selection.
void GetSelectedEntitiesSetWithFlattenedHierarchy(AzToolsFramework::EntityIdSet& out)
{
AzToolsFramework::EntityIdList entities;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
entities,
&AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
for (const AZ::EntityId& entityId : entities)
{
bool selectionIncludesTransformHeritage = false;
AZ::EntityId parent = entityId;
do
{
AZ::EntityId nextParentId;
AZ::TransformBus::EventResult(
/*result*/ nextParentId,
/*address*/ parent,
&AZ::TransformBus::Events::GetParentId);
parent = nextParentId;
if (!parent.IsValid())
{
break;
}
for (const AZ::EntityId& parentCheck : entities)
{
if (parentCheck == parent)
{
selectionIncludesTransformHeritage = true;
break;
}
}
} while (parent.IsValid() && !selectionIncludesTransformHeritage);
if (!selectionIncludesTransformHeritage)
{
out.insert(entityId);
}
}
}
SandboxIntegrationManager::SandboxIntegrationManager()
: m_startedUndoRecordingNestingLevel(0)
, m_dc(nullptr)
, m_notificationWindowManager(new AzToolsFramework::SliceOverridesNotificationWindowManager())
{
// Required to receive events from the Cry Engine undo system
GetIEditor()->GetUndoManager()->AddListener(this);
// Only create the PrefabIntegrationManager if prefabs are enabled
bool prefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (prefabSystemEnabled)
{
m_prefabIntegrationManager = aznew AzToolsFramework::Prefab::PrefabIntegrationManager();
}
}
SandboxIntegrationManager::~SandboxIntegrationManager()
{
GetIEditor()->GetUndoManager()->RemoveListener(this);
delete m_prefabIntegrationManager;
m_prefabIntegrationManager = nullptr;
}
void SandboxIntegrationManager::Setup()
{
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
AzToolsFramework::ToolsApplicationEvents::Bus::Handler::BusConnect();
AzToolsFramework::EditorRequests::Bus::Handler::BusConnect();
AzToolsFramework::EditorWindowRequests::Bus::Handler::BusConnect();
AzToolsFramework::EditorContextMenuBus::Handler::BusConnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusConnect();
AzFramework::DisplayContextRequestBus::Handler::BusConnect();
MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_FILE_SAVE_SLICE_TO_ROOT, [this]() {
SaveSlice(false);
});
MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_FILE_SAVE_SELECTED_SLICE, [this]() {
SaveSlice(true);
});
// Keep a reference to the interface EditorEntityUiInterface.
// This is used to register layer entities to their UI handler when the layer component is activated.
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert((m_editorEntityUiInterface != nullptr),
"SandboxIntegrationManager requires a EditorEntityUiInterface instance to be present on Setup().");
bool prefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (prefabSystemEnabled)
{
m_prefabIntegrationInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabIntegrationInterface>::Get();
AZ_Assert(
(m_prefabIntegrationInterface != nullptr),
"SandboxIntegrationManager requires a PrefabIntegrationInterface instance to be present on Setup().");
}
m_editorEntityAPI = AZ::Interface<AzToolsFramework::EditorEntityAPI>::Get();
AZ_Assert(m_editorEntityAPI, "SandboxIntegrationManager requires an EditorEntityAPI instance to be present on Setup().");
AzToolsFramework::Layers::EditorLayerComponentNotificationBus::Handler::BusConnect();
}
void SandboxIntegrationManager::SaveSlice(const bool& QuickPushToFirstLevel)
{
AzToolsFramework::EntityIdList selectedEntities;
GetSelectedEntities(selectedEntities);
if (selectedEntities.size() == 0)
{
m_notificationWindowManager->CreateNotificationWindow(AzToolsFramework::SliceOverridesNotificationWindow::EType::TypeError, "Nothing selected - Select a slice entity with overrides and try again");
return;
}
AzToolsFramework::EntityIdList relevantEntities;
AZ::u32 entitiesInSlices;
AZStd::vector<AZ::SliceComponent::SliceInstanceAddress> sliceInstances;
GetEntitiesInSlices(selectedEntities, entitiesInSlices, sliceInstances);
if (entitiesInSlices > 0)
{
// Gather the set of relevant entities from the selected entities and all descendants
AzToolsFramework::EntityIdSet relevantEntitiesSet;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(relevantEntitiesSet,
&AzToolsFramework::ToolsApplicationRequestBus::Events::GatherEntitiesAndAllDescendents, selectedEntities);
relevantEntities.reserve(relevantEntitiesSet.size());
for (AZ::EntityId& id : relevantEntitiesSet)
{
relevantEntities.push_back(id);
}
}
int numEntitiesToAdd = 0;
int numEntitiesToRemove = 0;
int numEntitiesToUpdate = 0;
if (AzToolsFramework::SliceUtilities::SaveSlice(relevantEntities, numEntitiesToAdd, numEntitiesToRemove, numEntitiesToUpdate, QuickPushToFirstLevel))
{
if (numEntitiesToAdd > 0 || numEntitiesToRemove > 0 || numEntitiesToUpdate > 0)
{
m_notificationWindowManager->CreateNotificationWindow(
AzToolsFramework::SliceOverridesNotificationWindow::EType::TypeSuccess,
QString("Save slice to parent - %1 saved successfully").arg(numEntitiesToUpdate + numEntitiesToAdd + numEntitiesToRemove));
}
else
{
m_notificationWindowManager->CreateNotificationWindow(
AzToolsFramework::SliceOverridesNotificationWindow::EType::TypeError,
"Selected has no overrides - Select a slice entity with overrides and try again");
}
}
else
{
m_notificationWindowManager->CreateNotificationWindow(
AzToolsFramework::SliceOverridesNotificationWindow::EType::TypeError,
"Save slice to parent - Failed");
}
}
// This event handler is queued on main thread.
void SandboxIntegrationManager::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
{
bool prefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (!prefabSystemEnabled)
{
AZ::SliceComponent* editorRootSlice = nullptr;
AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::BroadcastResult(
editorRootSlice, &AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Events::GetEditorRootSlice);
AZ_Assert(editorRootSlice, "Editor root slice missing!");
for (auto restoreItr = m_sliceAssetDeletionErrorRestoreInfos.begin(); restoreItr != m_sliceAssetDeletionErrorRestoreInfos.end();)
{
if (restoreItr->m_assetId == assetId)
{
for (const auto& entityRestore : restoreItr->m_entityRestoreInfos)
{
const AZ::EntityId& entityId = entityRestore.first;
const AZ::SliceComponent::EntityRestoreInfo& restoreInfo = entityRestore.second;
AZ::Entity* entity = editorRootSlice->FindEntity(entityId);
if (entity)
{
AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Broadcast(
&AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Events::RestoreSliceEntity, entity, restoreInfo,
AzToolsFramework::SliceEntityRestoreType::Detached);
}
else
{
AZ_Error(
"DetachSliceEntity", entity, "Unable to find previous detached entity of Id %s. Cannot undo \"Detach\" action.",
entityId.ToString().c_str());
}
}
restoreItr = m_sliceAssetDeletionErrorRestoreInfos.erase(restoreItr);
}
else
{
restoreItr++;
}
}
}
}
// No mutex is used for now because the only
// operation writing to shared resource is queued on main thread.
void SandboxIntegrationManager::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& assetInfo)
{
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
// Check to see if the removed slice asset has any instance in the level, then check if
// those dangling instances are directly under the root slice (not sub-slices). If yes,
// detach them and save necessary information so they can be restored when their slice asset
// comes back.
if (!isPrefabSystemEnabled && assetInfo.m_assetType == AZ::AzTypeInfo<AZ::SliceAsset>::Uuid())
{
AZ::SliceComponent* rootSlice = nullptr;
AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::BroadcastResult(rootSlice,
&AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Events::GetEditorRootSlice);
AZ_Assert(rootSlice, "Editor root slice missing!");
AZStd::vector<AZ::EntityId> entitiesToDetach;
const AZ::SliceComponent::SliceList& subSlices = rootSlice->GetSlices();
for (const AZ::SliceComponent::SliceReference& subSliceRef : subSlices)
{
if (subSliceRef.GetSliceAsset().GetId() == assetId)
{
for (const AZ::SliceComponent::SliceInstance& sliceInst : subSliceRef.GetInstances())
{
const AZ::SliceComponent::InstantiatedContainer* instContainer = sliceInst.GetInstantiated();
for (AZ::Entity* entity : instContainer->m_entities)
{
entitiesToDetach.emplace_back(entity->GetId());
}
}
}
}
AZ_Error("Editor", false, "The slice asset %s is deleted from disk, to prevent further data corruption, all of its root level slice instances are detached. "
"Restoring the slice asset on disk will revert the detaching operation.", assetInfo.m_relativePath.c_str());
AZ::SystemTickBus::QueueFunction([this, assetId, entitiesToDetach]() {
AZStd::vector<AZStd::pair<AZ::EntityId, AZ::SliceComponent::EntityRestoreInfo>> restoreInfos;
bool detachSuccess = false;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
detachSuccess, &AzToolsFramework::ToolsApplicationRequestBus::Events::DetachEntities, entitiesToDetach, restoreInfos);
if (detachSuccess)
{
m_sliceAssetDeletionErrorRestoreInfos.emplace_back(assetId, AZStd::move(restoreInfos));
}
});
}
}
void SandboxIntegrationManager::GetEntitiesInSlices(
const AzToolsFramework::EntityIdList& selectedEntities,
AZ::u32& entitiesInSlices,
AZStd::vector<AZ::SliceComponent::SliceInstanceAddress>& sliceInstances)
{
// Identify all slice instances affected by the selected entity set.
entitiesInSlices = 0;
for (const AZ::EntityId& entityId : selectedEntities)
{
AZ::SliceComponent::SliceInstanceAddress sliceAddress;
AzFramework::SliceEntityRequestBus::EventResult(sliceAddress, entityId,
&AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
if (sliceAddress.IsValid())
{
++entitiesInSlices;
if (sliceInstances.end() == AZStd::find(sliceInstances.begin(), sliceInstances.end(), sliceAddress))
{
sliceInstances.push_back(sliceAddress);
}
}
}
}
void SandboxIntegrationManager::Teardown()
{
AzToolsFramework::Layers::EditorLayerComponentNotificationBus::Handler::BusDisconnect();
AzFramework::DisplayContextRequestBus::Handler::BusDisconnect();
AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect();
AzToolsFramework::EditorWindowRequests::Bus::Handler::BusDisconnect();
AzToolsFramework::EditorRequests::Bus::Handler::BusDisconnect();
AzToolsFramework::ToolsApplicationEvents::Bus::Handler::BusDisconnect();
}
void SandboxIntegrationManager::SetDC(DisplayContext* dc)
{
m_dc = dc;
}
DisplayContext* SandboxIntegrationManager::GetDC()
{
return m_dc;
}
void SandboxIntegrationManager::OnBeginUndo([[maybe_unused]] const char* label)
{
AzToolsFramework::UndoSystem::URSequencePoint* currentBatch = nullptr;
EBUS_EVENT_RESULT(currentBatch, AzToolsFramework::ToolsApplicationRequests::Bus, GetCurrentUndoBatch);
AZ_Assert(currentBatch, "No undo batch is active.");
// Only generate a Sandbox placeholder for root-level undo batches.
if (nullptr == currentBatch->GetParent())
{
// start Cry Undo
if (!CUndo::IsRecording())
{
GetIEditor()->BeginUndo();
// flag that we started recording the undo batch
m_startedUndoRecordingNestingLevel = 1;
}
}
else
{
if (m_startedUndoRecordingNestingLevel)
{
// if we previously started recording the undo, increment the nesting level so we can
// detect when we need to accept the undo in OnEndUndo()
m_startedUndoRecordingNestingLevel++;
}
}
}
void SandboxIntegrationManager::OnEndUndo(const char* label, bool changed)
{
// Add the undo only after we know it's got a legit change, we can't remove undos from the cry undo system so we do it here instead of OnBeginUndo
if (changed && CUndo::IsRecording())
{
CUndo::Record(new CToolsApplicationUndoLink());
}
if (m_startedUndoRecordingNestingLevel)
{
m_startedUndoRecordingNestingLevel--;
if (m_startedUndoRecordingNestingLevel == 0)
{
if (changed)
{
// only accept the undo batch that we initially started undo recording on
GetIEditor()->AcceptUndo(label);
}
else
{
GetIEditor()->CancelUndo();
}
}
}
}
void SandboxIntegrationManager::EntityParentChanged(
const AZ::EntityId entityId,
const AZ::EntityId newParentId,
const AZ::EntityId oldParentId)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
if (m_unsavedEntities.find(entityId) != m_unsavedEntities.end())
{
// New layers need the level to be saved.
bool isEntityLayer = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
isEntityLayer,
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (isEntityLayer)
{
AzToolsFramework::Layers::EditorLayerComponentRequestBus::Event(
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::AddLevelSaveDependency);
}
// Don't need to track any other unsaved changes, this is a new entity that hasn't been saved yet.
return;
}
// If an entity is moved to or from a layer, then that layer can only safely be saved when the other layer or level saves, to prevent
// accidental duplication of entities.
// This logic doesn't clear the dependency flag if an entity changes parents multiple times between saves, so if an entity visits many layers
// before finally being saved, it will result in all of those layers saving, too.
AZ::EntityId oldAncestor = oldParentId;
AZ::EntityId oldLayer;
do
{
if (!oldAncestor.IsValid())
{
break;
}
bool isOldAncestorLayer = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
isOldAncestorLayer,
oldAncestor,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (isOldAncestorLayer)
{
oldLayer = oldAncestor;
break;
}
// Must pass in an invalid id, because if no parent then nothing will modify the id variable to be invalid and stop at the no-parent case
AZ::EntityId nextParentId;
AZ::TransformBus::EventResult(
/*result*/ nextParentId,
/*address*/ oldAncestor,
&AZ::TransformBus::Events::GetParentId);
oldAncestor = nextParentId;
} while (oldAncestor.IsValid());
AZ::EntityId newAncestor = newParentId;
AZ::EntityId newLayer;
do
{
if (!newAncestor.IsValid())
{
break;
}
bool isNewAncestorLayer = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
isNewAncestorLayer,
newAncestor,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (isNewAncestorLayer)
{
newLayer = newAncestor;
break;
}
// The parent may not be connected to the bus yet, so start with an invalid entity ID
// to prevent an infinite loop.
AZ::EntityId ancestorParent;
AZ::TransformBus::EventResult(
/*result*/ ancestorParent,
/*address*/ newAncestor,
&AZ::TransformBus::Events::GetParentId);
newAncestor = ancestorParent;
} while (newAncestor.IsValid());
if (oldLayer.IsValid() && newLayer != oldLayer)
{
if (newLayer.IsValid())
{
AzToolsFramework::Layers::EditorLayerComponentRequestBus::Event(
oldLayer,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::AddLayerSaveDependency,
newLayer);
}
else
{
AzToolsFramework::Layers::EditorLayerComponentRequestBus::Event(
oldLayer,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::AddLevelSaveDependency);
}
}
if (newLayer.IsValid() && newLayer != oldLayer)
{
if (oldLayer.IsValid())
{
AzToolsFramework::Layers::EditorLayerComponentRequestBus::Event(
newLayer,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::AddLayerSaveDependency,
oldLayer);
}
else
{
AzToolsFramework::Layers::EditorLayerComponentRequestBus::Event(
newLayer,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::AddLevelSaveDependency);
}
}
}
void SandboxIntegrationManager::OnSaveLevel()
{
m_unsavedEntities.clear();
}
int SandboxIntegrationManager::GetMenuPosition() const
{
return aznumeric_cast<int>(AzToolsFramework::EditorContextMenuOrdering::TOP);
}
void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags)
{
if (!IsLevelDocumentOpen())
{
return;
}
if (flags & AzToolsFramework::EditorEvents::eECMF_USE_VIEWPORT_CENTER)
{
CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport();
int width = 0;
int height = 0;
// If there is no 3D Viewport active to aid in the positioning of context menu
// operations, we don't need to store anything but default values here. Any code
// using these numbers for placement should default to the origin when there's
// no 3D viewport to raycast into.
if (view)
{
view->GetDimensions(&width, &height);
}
m_contextMenuViewPoint.Set(static_cast<float>(width / 2), static_cast<float>(height / 2));
}
else
{
m_contextMenuViewPoint = point;
}
CGameEngine* gameEngine = GetIEditor()->GetGameEngine();
if (!gameEngine || !gameEngine->IsLevelLoaded())
{
return;
}
menu->setToolTipsVisible(true);
AzToolsFramework::EntityIdList selected;
GetSelectedOrHighlightedEntities(selected);
bool prefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
QAction* action = nullptr;
// when nothing is selected, entity is created at root level
if (selected.size() == 0)
{
action = menu->addAction(QObject::tr("Create entity"));
QObject::connect(
action, &QAction::triggered, action,
[this]
{
ContextMenu_NewEntity();
});
}
// when a single entity is selected, entity is created as its child
else if (selected.size() == 1)
{
auto containerEntityInterface = AZ::Interface<AzToolsFramework::ContainerEntityInterface>::Get();
if (!prefabSystemEnabled || (containerEntityInterface && containerEntityInterface->IsContainerOpen(selected.front())))
{
action = menu->addAction(QObject::tr("Create entity"));
QObject::connect(
action, &QAction::triggered, action,
[selected]
{
AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequestBus::Handler::CreateNewEntityAsChild, selected.front());
}
);
}
}
if (!prefabSystemEnabled)
{
menu->addSeparator();
action = menu->addAction(QObject::tr("Create layer"));
QObject::connect(action, &QAction::triggered, [this] { ContextMenu_NewLayer(); });
AzToolsFramework::EntityIdList entities;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
entities,
&AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
SetupLayerContextMenu(menu);
AzToolsFramework::EntityIdSet flattenedSelection = AzToolsFramework::GetCulledEntityHierarchy(entities);
AzToolsFramework::SetupAddToLayerMenu(menu, flattenedSelection, [this] { return ContextMenu_NewLayer(); });
SetupSliceContextMenu(menu);
}
action = menu->addAction(QObject::tr("Duplicate"));
QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_Duplicate(); });
if (selected.size() == 0)
{
action->setDisabled(true);
}
if (!prefabSystemEnabled)
{
action = menu->addAction(QObject::tr("Delete"));
QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_DeleteSelected(); });
if (selected.size() == 0)
{
action->setDisabled(true);
}
}
menu->addSeparator();
if (selected.size() > 0)
{
action = menu->addAction(QObject::tr("Open pinned Inspector"));
QObject::connect(action, &QAction::triggered, action, [this, selected]
{
AzToolsFramework::EntityIdSet pinnedEntities(selected.begin(), selected.end());
OpenPinnedInspector(pinnedEntities);
});
if (selected.size() > 0)
{
action = menu->addAction(QObject::tr("Find in Entity Outliner"));
QObject::connect(action, &QAction::triggered, [selected]
{
AzToolsFramework::EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::OnFocusInEntityOutliner, selected);
});
}
menu->addSeparator();
}
}
void SandboxIntegrationManager::OpenPinnedInspector(const AzToolsFramework::EntityIdSet& entities)
{
QDockWidget* dockWidget = InstanceViewPane(LyViewPane::EntityInspectorPinned);
if (dockWidget)
{
QComponentEntityEditorInspectorWindow* editor = static_cast<QComponentEntityEditorInspectorWindow*>(dockWidget->widget());
if (editor && editor->GetPropertyEditor())
{
editor->GetPropertyEditor()->SetOverrideEntityIds(entities);
AZStd::string widgetTitle;
if (entities.size() == 1)
{
AZStd::string entityName;
AZ::ComponentApplicationBus::BroadcastResult(entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, *entities.begin());
widgetTitle = AZStd::string::format("%s Inspector", entityName.c_str());
QObject::connect(editor->GetPropertyEditor(), &AzToolsFramework::EntityPropertyEditor::SelectedEntityNameChanged, [dockWidget](const AZ::EntityId& /*entityId*/, const AZStd::string& name)
{
AZStd::string newTitle = AZStd::string::format("%s Inspector", name.c_str());
dockWidget->setWindowTitle(newTitle.c_str());
});
}
else
{
widgetTitle = AZStd::string::format("%zu Entities - Inspector", entities.size());
}
dockWidget->setWindowTitle(widgetTitle.c_str());
}
}
}
void SandboxIntegrationManager::ClosePinnedInspector(AzToolsFramework::EntityPropertyEditor* editor)
{
QWidget* currentWidget = editor->parentWidget();
while (currentWidget)
{
QDockWidget* dockWidget = qobject_cast<QDockWidget*>(currentWidget);
if (dockWidget)
{
QtViewPaneManager::instance()->ClosePaneInstance(LyViewPane::EntityInspectorPinned, dockWidget);
return;
}
currentWidget = currentWidget->parentWidget();
}
}
void SandboxIntegrationManager::SetupLayerContextMenu(QMenu* menu)
{
// First verify that the selection contains some layers.
AzToolsFramework::EntityIdList selectedEntities;
GetSelectedOrHighlightedEntities(selectedEntities);
// Nothing selected, no layer context menus to add.
if (selectedEntities.empty())
{
return;
}
AZStd::unordered_set<AZ::EntityId> layersInSelection;
for (AZ::EntityId entityId : selectedEntities)
{
bool isLayerEntity = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
isLayerEntity,
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (isLayerEntity)
{
layersInSelection.insert(entityId);
}
}
// No layers directly selected, do not add context, even if a selected entity
// is a child of a layer.
if (layersInSelection.empty())
{
return;
}
menu->addSeparator();
const int selectedLayerCount = static_cast<int>(layersInSelection.size());
QString saveTitle = QObject::tr("Save layer");
if(selectedLayerCount > 1)
{
saveTitle = QObject::tr("Save %0 layers").arg(selectedLayerCount);
}
QAction* saveLayerAction = menu->addAction(saveTitle);
saveLayerAction->setToolTip(QObject::tr("Save the selected layers."));
QObject::connect(saveLayerAction, &QAction::triggered, [this, layersInSelection] { ContextMenu_SaveLayers(layersInSelection); });
if (layersInSelection.size() == 1)
{
const AZ::EntityId& id = *layersInSelection.begin();
AZ::Outcome<AZStd::string, AZStd::string> layerFullFilePathResult = AZ::Failure(AZStd::string());
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
layerFullFilePathResult,
id,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::GetLayerFullFilePath,
Path::GetPath(GetIEditor()->GetDocument()->GetActivePathName()));
// Only add option to find the layer in the Asset Browser if the layer has been saved to disk
if (layerFullFilePathResult.IsSuccess())
{
AZStd::string fullFilePath = layerFullFilePathResult.GetValue();
QAction* findLayerAssetAction = menu->addAction(QObject::tr("Find layer in Asset Browser"));
findLayerAssetAction->setToolTip(QObject::tr("Selects this layer in the Asset Browser"));
QObject::connect(findLayerAssetAction, &QAction::triggered, [fullFilePath] {
QtViewPaneManager::instance()->OpenPane(LyViewPane::AssetBrowser);
AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast(
&AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Events::ClearFilter);
AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast(
&AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Events::SelectFileAtPath,
fullFilePath);
});
}
}
}
void SandboxIntegrationManager::SetupSliceContextMenu(QMenu* menu)
{
AZ_PROFILE_FUNCTION(Editor);
AzToolsFramework::EntityIdList selectedEntities;
GetSelectedOrHighlightedEntities(selectedEntities);
menu->addSeparator();
if (!selectedEntities.empty())
{
bool anySelectedEntityFromExistingSlice = false;
bool layerInSelection = false;
for (AZ::EntityId entityId : selectedEntities)
{
if (!anySelectedEntityFromExistingSlice)
{
AZ::SliceComponent::SliceInstanceAddress sliceAddress(nullptr, nullptr);
AzFramework::SliceEntityRequestBus::EventResult(sliceAddress, entityId,
&AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
if (sliceAddress.GetReference())
{
anySelectedEntityFromExistingSlice = true;
}
}
if (!layerInSelection)
{
bool isLayerEntity = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
isLayerEntity,
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (isLayerEntity)
{
layerInSelection = true;
}
}
// Everything being searched for was found, so exit the loop.
if (layerInSelection && anySelectedEntityFromExistingSlice)
{
break;
}
}
// Layers can't be in slices.
if (!layerInSelection)
{
QAction* createAction = menu->addAction(QObject::tr("Create slice..."));
createAction->setToolTip(QObject::tr("Creates a slice out of the currently selected entities."));
if (anySelectedEntityFromExistingSlice)
{
QObject::connect(createAction, &QAction::triggered, createAction, [this, selectedEntities] { ContextMenu_MakeSlice(selectedEntities); });
}
else
{
QObject::connect(createAction, &QAction::triggered, createAction, [this, selectedEntities] { ContextMenu_InheritSlice(selectedEntities); });
}
}
}
QAction* instantiateAction = menu->addAction(QObject::tr("Instantiate slice..."));
instantiateAction->setToolTip(QObject::tr("Instantiates a pre-existing slice asset into the level"));
QObject::connect(instantiateAction, &QAction::triggered, instantiateAction, [this] { ContextMenu_InstantiateSlice(); });
AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::Bus::Events::PopulateEditorGlobalContextMenu_SliceSection, menu, AZ::Vector2::CreateZero(),
AzToolsFramework::EditorEvents::eECMF_HIDE_ENTITY_CREATION | AzToolsFramework::EditorEvents::eECMF_USE_VIEWPORT_CENTER);
if (selectedEntities.empty())
{
return;
}
AZ::u32 entitiesInSlices;
AZStd::vector<AZ::SliceComponent::SliceInstanceAddress> sliceInstances;
GetEntitiesInSlices(selectedEntities, entitiesInSlices, sliceInstances);
// Offer slice-related options if any selected entities belong to slice instances.
if (0 == entitiesInSlices)
{
return;
}
// Setup push and revert options (quick push and 'advanced' push UI).
SetupSliceContextMenu_Modify(menu, selectedEntities, entitiesInSlices);
menu->addSeparator();
// PopulateFindSliceMenu takes a callback to run when a slice is selected, which is called before the slice is selected in the asset browser.
// This is so the AssetBrowser can be opened first, which can only be done from a Sandbox module. PopulateFindSliceMenu exists in the AzToolsFramework
// module in SliceUtilities, so it can share logic with similar menus, like Quick Push.
// Similarly, it takes a callback for the SliceRelationshipView.
AzToolsFramework::SliceUtilities::PopulateSliceSubMenus(*menu, selectedEntities,
[]
{
// This will open the AssetBrowser if it's not open, and do nothing if it's already opened.
QtViewPaneManager::instance()->OpenPane(LyViewPane::AssetBrowser);
},
[]
{
//open SliceRelationshipView if necessary, and populate it
QtViewPaneManager::instance()->OpenPane(LyViewPane::SliceRelationships);
});
}
void SandboxIntegrationManager::SetupSliceContextMenu_Modify(QMenu* menu, const AzToolsFramework::EntityIdList& selectedEntities, [[maybe_unused]] const AZ::u32 numEntitiesInSlices)
{
AZ_PROFILE_FUNCTION(Editor);
using namespace AzToolsFramework;
// Gather the set of relevant entities from the selected entities and all descendants
AzToolsFramework::EntityIdSet relevantEntitiesSet;
EBUS_EVENT_RESULT(relevantEntitiesSet, AzToolsFramework::ToolsApplicationRequests::Bus, GatherEntitiesAndAllDescendents, selectedEntities);
AzToolsFramework::EntityIdList relevantEntities;
relevantEntities.reserve(relevantEntitiesSet.size());
for (AZ::EntityId& id : relevantEntitiesSet)
{
relevantEntities.push_back(id);
}
SliceUtilities::PopulateQuickPushMenu(*menu, relevantEntities);
SliceUtilities::PopulateDetachMenu(*menu, selectedEntities, relevantEntitiesSet);
bool canRevert = false;
for (AZ::EntityId id : relevantEntitiesSet)
{
bool entityHasOverrides = false;
AzToolsFramework::EditorEntityInfoRequestBus::EventResult(entityHasOverrides, id, &AzToolsFramework::EditorEntityInfoRequestBus::Events::HasSliceEntityOverrides);
if (entityHasOverrides)
{
canRevert = true;
break;
}
}
QAction* revertAction = menu->addAction(QObject::tr("Revert overrides"));
revertAction->setToolTip(QObject::tr("Revert all slice entities and their children to their last saved state."));
QObject::connect(revertAction, &QAction::triggered, [this, relevantEntities]
{
ContextMenu_ResetToSliceDefaults(relevantEntities);
});
revertAction->setEnabled(canRevert);
}
void SandboxIntegrationManager::CreateEditorRepresentation(AZ::Entity* entity)
{
IEditor* editor = GetIEditor();
if (CComponentEntityObject* existingObject = CComponentEntityObject::FindObjectForEntity(entity->GetId()))
{
// Refresh sandbox object if one already exists for this AZ::EntityId.
existingObject->AssignEntity(entity, false);
return;
}
CBaseObjectPtr object = editor->NewObject("ComponentEntity", "", entity->GetName().c_str(), 0.f, 0.f, 0.f, false);
if (object)
{
static_cast<CComponentEntityObject*>(object.get())->AssignEntity(entity);
// If this object type was hidden by category, re-display it.
int hideMask = editor->GetDisplaySettings()->GetObjectHideMask();
hideMask = hideMask & ~(object->GetType());
editor->GetDisplaySettings()->SetObjectHideMask(hideMask);
}
}
bool SandboxIntegrationManager::DestroyEditorRepresentation(AZ::EntityId entityId, bool deleteAZEntity)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
IEditor* editor = GetIEditor();
if (editor->GetObjectManager())
{
CEntityObject* object = nullptr;
EBUS_EVENT_ID_RESULT(object, entityId, AzToolsFramework::ComponentEntityEditorRequestBus, GetSandboxObject);
if (object && (object->GetType() == OBJTYPE_AZENTITY))
{
static_cast<CComponentEntityObject*>(object)->AssignEntity(nullptr, deleteAZEntity);
{
AZ_PROFILE_SCOPE(AzToolsFramework, "SandboxIntegrationManager::DestroyEditorRepresentation:ObjManagerDeleteObject");
editor->GetObjectManager()->DeleteObject(object);
}
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void SandboxIntegrationManager::GoToSelectedOrHighlightedEntitiesInViewports()
{
AzToolsFramework::EntityIdList entityIds;
GetSelectedOrHighlightedEntities(entityIds);
GoToEntitiesInViewports(entityIds);
}
//////////////////////////////////////////////////////////////////////////
void SandboxIntegrationManager::GoToSelectedEntitiesInViewports()
{
AzToolsFramework::EntityIdList entityIds;
GetSelectedEntities(entityIds);
GoToEntitiesInViewports(entityIds);
}
bool SandboxIntegrationManager::CanGoToSelectedEntitiesInViewports()
{
AzToolsFramework::EntityIdList entityIds;
GetSelectedEntities(entityIds);
if (entityIds.size() == 0)
{
return false;
}
for (const AZ::EntityId& entityId : entityIds)
{
if (CanGoToEntityOrChildren(entityId))
{
return true;
}
}
return false;
}
bool SandboxIntegrationManager::CanGoToEntityOrChildren(const AZ::EntityId& entityId) const
{
bool isLayerEntity = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
isLayerEntity,
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
// If this entity is not a layer
if (!isLayerEntity)
{
// check if the entity exists to determine if we can go to it (e.g. system & internal entities are not visible in the viewport)
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId);
return entity != nullptr;
}
AZStd::vector<AZ::EntityId> layerChildren;
AZ::TransformBus::EventResult(
/*result*/ layerChildren,
/*address*/ entityId,
&AZ::TransformBus::Events::GetChildren);
for (const AZ::EntityId& childId : layerChildren)
{
if (CanGoToEntityOrChildren(childId))
{
return true;
}
}
return false;
}
AZ::Vector3 SandboxIntegrationManager::GetWorldPositionAtViewportCenter()
{
if (GetIEditor() && GetIEditor()->GetViewManager())
{
CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport();
if (view)
{
int width, height;
view->GetDimensions(&width, &height);
return LYVec3ToAZVec3(view->ViewToWorld(QPoint(width / 2, height / 2)));
}
}
return AZ::Vector3::CreateZero();
}
int SandboxIntegrationManager::GetIconTextureIdFromEntityIconPath(const AZStd::string& entityIconPath)
{
return GetIEditor()->GetIconManager()->GetIconTexture(entityIconPath.c_str());
}
void SandboxIntegrationManager::ClearRedoStack()
{
// We have two separate undo systems that are assumed to be kept in sync,
// So here we tell the legacy Undo system to clear the redo stack and at the same time
// tell the new AZ undo system to clear redo stack ("slice" the stack)
// Clear legacy redo stack
GetIEditor()->ClearRedoStack();
// Clear AZ redo stack
AzToolsFramework::UndoSystem::UndoStack* undoStack = nullptr;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(undoStack, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetUndoStack);
if (undoStack)
{
undoStack->Slice();
}
}
//////////////////////////////////////////////////////////////////////////
void SandboxIntegrationManager::CloneSelection(bool& handled)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
AzToolsFramework::EntityIdList entities;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
entities,
&AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entities);
if (!duplicationSet.empty())
{
bool prefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (prefabSystemEnabled)
{
m_editorEntityAPI->DuplicateSelected();
handled = true;
}
else
{
AZStd::unordered_set<AZ::EntityId> clonedEntities;
handled = AzToolsFramework::CloneInstantiatedEntities(duplicationSet, clonedEntities);
m_unsavedEntities.insert(clonedEntities.begin(), clonedEntities.end());
}
}
else
{
handled = false;
}
}
void SandboxIntegrationManager::DeleteSelectedEntities([[maybe_unused]] const bool includeDescendants)
{
AzToolsFramework::EntityIdList selectedEntityIds;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntityIds, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
&AzToolsFramework::ToolsApplicationRequests::DeleteEntitiesAndAllDescendants, selectedEntityIds);
}
AZ::EntityId SandboxIntegrationManager::CreateNewEntity(AZ::EntityId parentId)
{
AZ::Vector3 position = AZ::Vector3::CreateZero();
bool parentIsValid = parentId.IsValid();
if (parentIsValid)
{
// If a valid parent is a Layer, we should get our position from the viewport as all Layers are positioned at 0,0,0.
bool parentIsLayer = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
parentIsLayer,
parentId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
parentIsValid = !parentIsLayer;
}
// If we have an invalid parent, base new entity's position on the viewport.
if (!parentIsValid)
{
position = GetWorldPositionAtViewportCenter();
}
return CreateNewEntityAtPosition(position, parentId);
}
AZ::EntityId SandboxIntegrationManager::CreateNewEntityAsChild(AZ::EntityId parentId)
{
AZ_Assert(parentId.IsValid(), "Entity created as a child of an invalid parent entity.");
auto newEntityId = CreateNewEntity(parentId);
// Some modules need to know that this special action has taken place; broadcast an event.
ToolsApplicationEvents::Bus::Broadcast(&ToolsApplicationEvents::EntityCreatedAsChild, newEntityId, parentId);
return newEntityId;
}
AZ::EntityId SandboxIntegrationManager::CreateNewEntityAtPosition(const AZ::Vector3& pos, AZ::EntityId parentId)
{
using namespace AzToolsFramework;
bool prefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
AZ::EntityId newEntityId;
if (!prefabSystemEnabled)
{
const AZStd::string name = AZStd::string::format("Entity%d", GetIEditor()->GetObjectManager()->GetObjectCount() + 1);
EditorEntityContextRequestBus::BroadcastResult(newEntityId, &EditorEntityContextRequests::CreateNewEditorEntity, name.c_str());
if (newEntityId.IsValid())
{
m_unsavedEntities.insert(newEntityId);
AZ::Transform transform = AZ::Transform::CreateIdentity();
transform.SetTranslation(pos);
if (parentId.IsValid())
{
AZ::TransformBus::Event(newEntityId, &AZ::TransformInterface::SetParent, parentId);
AZ::TransformBus::Event(newEntityId, &AZ::TransformInterface::SetLocalTM, transform);
}
else
{
AZ::TransformBus::Event(newEntityId, &AZ::TransformInterface::SetWorldTM, transform);
}
// Select the new entity (and deselect others).
AzToolsFramework::EntityIdList selection = {newEntityId};
ScopedUndoBatch undo("New Entity");
auto selectionCommand = AZStd::make_unique<AzToolsFramework::SelectionCommand>(selection, "");
selectionCommand->SetParent(undo.GetUndoBatch());
selectionCommand.release();
EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus, SetSelectedEntities, selection);
}
}
else
{
newEntityId = m_prefabIntegrationInterface->CreateNewEntityAtPosition(pos, parentId);
}
return newEntityId;
}
AzFramework::EntityContextId SandboxIntegrationManager::GetEntityContextId()
{
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
return editorEntityContextId;
}
//////////////////////////////////////////////////////////////////////////
QWidget* SandboxIntegrationManager::GetAppMainWindow()
{
return MainWindow::instance();
}
QWidget* SandboxIntegrationManager::GetMainWindow()
{
return MainWindow::instance();
}
IEditor* SandboxIntegrationManager::GetEditor()
{
return GetIEditor();
}
//////////////////////////////////////////////////////////////////////////
bool SandboxIntegrationManager::GetUndoSliceOverrideSaveValue()
{
return GetIEditor()->GetEditorSettings()->m_undoSliceOverrideSaveValue;
}
//////////////////////////////////////////////////////////////////////////
bool SandboxIntegrationManager::GetShowCircularDependencyError()
{
return GetIEditor()->GetEditorSettings()->m_showCircularDependencyError;
}
void SandboxIntegrationManager::SetShowCircularDependencyError(const bool& showCircularDependencyError)
{
GetIEditor()->GetEditorSettings()->m_showCircularDependencyError = showCircularDependencyError;
}
//////////////////////////////////////////////////////////////////////////
void SandboxIntegrationManager::LaunchLuaEditor(const char* files)
{
CCryEditApp::instance()->OpenLUAEditor(files);
}
bool SandboxIntegrationManager::IsLevelDocumentOpen()
{
return (GetIEditor() && GetIEditor()->GetDocument() && GetIEditor()->GetDocument()->IsDocumentReady());
}
AZStd::string SandboxIntegrationManager::GetLevelName()
{
return AZStd::string(GetIEditor()->GetGameEngine()->GetLevelName().toUtf8().constData());
}
void SandboxIntegrationManager::OnContextReset()
{
// Deselect everything.
EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus, SetSelectedEntities, AzToolsFramework::EntityIdList());
std::vector<CBaseObject*> objects;
objects.reserve(128);
IObjectManager* objectManager = GetIEditor()->GetObjectManager();
objectManager->FindObjectsOfType(OBJTYPE_AZENTITY, objects);
for (CBaseObject* object : objects)
{
CComponentEntityObject* componentEntity = static_cast<CComponentEntityObject*>(object);
componentEntity->AssignEntity(nullptr, false);
objectManager->DeleteObject(componentEntity);
}
}
void SandboxIntegrationManager::OnSliceInstantiated(const AZ::Data::AssetId& /*sliceAssetId*/, AZ::SliceComponent::SliceInstanceAddress& sliceAddress, const AzFramework::SliceInstantiationTicket& /*ticket*/)
{
// The instantiated slice isn't valid. Other systems will report this as an error.
// Bail out here, this is nothing to track in this case.
if (!sliceAddress.GetInstance())
{
return;
}
const AZ::SliceComponent::EntityIdToEntityIdMap& sliceInstanceEntityIdMap = sliceAddress.GetInstance()->GetEntityIdMap();
for (const auto& sliceInstantEntityIdPair : sliceInstanceEntityIdMap)
{
// The second in the pair is the local instance's entity ID.
m_unsavedEntities.insert(sliceInstantEntityIdPair.second);
}
}
void SandboxIntegrationManager::OnLayerComponentActivated(AZ::EntityId entityId)
{
m_editorEntityUiInterface->RegisterEntity(entityId, m_layerUiOverrideHandler.GetHandlerId());
}
void SandboxIntegrationManager::OnLayerComponentDeactivated(AZ::EntityId entityId)
{
m_editorEntityUiInterface->UnregisterEntity(entityId);
}
void SandboxIntegrationManager::ContextMenu_NewEntity()
{
AZ::Vector3 worldPosition = AZ::Vector3::CreateZero();
// If we don't have a viewport active to aid in placement, the object
// will be created at the origin.
if (CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport())
{
worldPosition = AzToolsFramework::FindClosestPickIntersection(
view->GetViewportId(), AzFramework::ScreenPointFromVector2(m_contextMenuViewPoint), AzToolsFramework::EditorPickRayLength,
GetDefaultEntityPlacementDistance());
}
CreateNewEntityAtPosition(worldPosition);
}
AZ::EntityId SandboxIntegrationManager::ContextMenu_NewLayer()
{
const int objectCount = GetIEditor()->GetObjectManager()->GetObjectCount();
const AZStd::string name = AZStd::string::format("Layer%d", objectCount + 1);
// Make sure the color is created fully opaque.
static QColor newLayerDefaultColor = GetIEditor()->GetColorByName("NewLayerDefaultColor");
AZ::Color newLayerColor(
aznumeric_cast<float>(newLayerDefaultColor.redF()),
aznumeric_cast<float>(newLayerDefaultColor.greenF()),
aznumeric_cast<float>(newLayerDefaultColor.blueF()),
aznumeric_cast<float>(newLayerDefaultColor.alphaF()));
AZ::EntityId newEntityId = AzToolsFramework::Layers::EditorLayerComponent::CreateLayerEntity(
name, newLayerColor, AzToolsFramework::Layers::LayerProperties::SaveFormat::Xml);
if (!newEntityId.IsValid())
{
// CreateLayerEntity already handled reporting errors if it couldn't make a new layer.
return AZ::EntityId();
}
m_unsavedEntities.insert(newEntityId);
return newEntityId;
}
void SandboxIntegrationManager::ContextMenu_SaveLayers(const AZStd::unordered_set<AZ::EntityId>& layers)
{
AZStd::unordered_map<AZStd::string, int> nameConflictMapping;
for (AZ::EntityId layerEntityId : layers)
{
AzToolsFramework::Layers::EditorLayerComponentRequestBus::Event(
layerEntityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::UpdateLayerNameConflictMapping,
nameConflictMapping);
}
if (!nameConflictMapping.empty())
{
AzToolsFramework::Layers::NameConflictWarning* nameConflictWarning = new AzToolsFramework::Layers::NameConflictWarning(GetMainWindow(), nameConflictMapping);
nameConflictWarning->exec();
return;
}
AZStd::unordered_set<AZ::EntityId> allLayersToSave(layers);
bool mustSaveLevel = false;
bool mustSaveOtherContent = false;
for (AZ::EntityId layerEntityId : layers)
{
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
mustSaveOtherContent,
layerEntityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::GatherSaveDependencies,
allLayersToSave,
mustSaveLevel);
}
if (mustSaveOtherContent)
{
QWidget* mainWindow = nullptr;
AzToolsFramework::EditorRequestBus::BroadcastResult(
mainWindow,
&AzToolsFramework::EditorRequestBus::Events::GetMainWindow);
QMessageBox saveAdditionalContentMessage(mainWindow);
saveAdditionalContentMessage.setWindowTitle(QObject::tr("Unsaved content"));
saveAdditionalContentMessage.setText(QObject::tr("You have moved entities to or from the layer(s) that you are trying to save."));
if (mustSaveLevel)
{
saveAdditionalContentMessage.setInformativeText(QObject::tr("The level and all layers will be saved."));
}
else
{
saveAdditionalContentMessage.setInformativeText(QObject::tr("All relevant layers will be saved."));
}
saveAdditionalContentMessage.setIcon(QMessageBox::Warning);
saveAdditionalContentMessage.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
saveAdditionalContentMessage.setDefaultButton(QMessageBox::Save);
int saveAdditionalContentMessageResult = saveAdditionalContentMessage.exec();
switch (saveAdditionalContentMessageResult)
{
case QMessageBox::Cancel:
// The user chose to cancel this operation.
return;
case QMessageBox::Save:
default:
break;
}
if (mustSaveLevel)
{
// Saving the level causes all layers to save.
GetIEditor()->GetDocument()->Save();
return;
}
}
QString levelAbsoluteFolder = Path::GetPath(GetIEditor()->GetDocument()->GetActivePathName());
// Not used here, but needed for the ebus event.
AZStd::vector<AZ::Entity*> layerEntities;
AZ::SliceComponent::SliceReferenceToInstancePtrs instancesInLayers;
for (AZ::EntityId layerEntityId : allLayersToSave)
{
AzToolsFramework::Layers::LayerResult layerSaveResult(AzToolsFramework::Layers::LayerResult::CreateSuccess());
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
layerSaveResult,
layerEntityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::WriteLayerAndGetEntities,
levelAbsoluteFolder,
layerEntities,
instancesInLayers);
AzToolsFramework::Layers::EditorLayerComponentRequestBus::Event(
layerEntityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::RestoreEditorData);
layerSaveResult.MessageResult();
m_unsavedEntities.erase(layerEntityId);
}
// Update the unsaved entities list so these entities are no longer tracked as unsaved.
for (const AZ::Entity* entity : layerEntities)
{
m_unsavedEntities.erase(entity->GetId());
}
}
void SandboxIntegrationManager::ContextMenu_MakeSlice(AzToolsFramework::EntityIdList entities)
{
QChar bulletChar(0x2022);
QMessageBox createSliceBox(GetMainWindow());
createSliceBox.setWindowTitle(QObject::tr("Create Slice"));
createSliceBox.setText(QString(QObject::tr("Your selection contains slice instances. What kind of slice do you want to create?"))
+ "\n\n" + QString(bulletChar) + " " + "Fresh slice that doesn't inherit existing slice references."
+ "\n" + QString(bulletChar) + " " + "Nested slice that inherits existing slice references."
+ "\n\n");
createSliceBox.setIcon(QMessageBox::Warning);
QPushButton* freshSliceButton = createSliceBox.addButton(QObject::tr("Fresh Slice"), QMessageBox::ActionRole);
QPushButton* nestedSliceButton = createSliceBox.addButton(QObject::tr("Nested Slice"), QMessageBox::ActionRole);
createSliceBox.addButton(QMessageBox::Cancel);
createSliceBox.exec();
if (createSliceBox.clickedButton() == freshSliceButton)
{
MakeSliceFromEntities(entities, false, GetIEditor()->GetEditorSettings()->sliceSettings.dynamicByDefault);
}
else if (createSliceBox.clickedButton() == nestedSliceButton)
{
ContextMenu_InheritSlice(entities);
}
}
void SandboxIntegrationManager::ContextMenu_InheritSlice(AzToolsFramework::EntityIdList entities)
{
MakeSliceFromEntities(entities, true, GetIEditor()->GetEditorSettings()->sliceSettings.dynamicByDefault);
}
void SandboxIntegrationManager::ContextMenu_InstantiateSlice()
{
AssetSelectionModel selection = AssetSelectionModel::AssetTypeSelection("Slice");
BrowseForAssets(selection);
if (selection.IsValid())
{
auto product = azrtti_cast<const ProductAssetBrowserEntry*>(selection.GetResult());
AZ_Assert(product, "Incorrect entry type selected. Expected product.");
InstantiateSliceFromAssetId(product->GetAssetId());
}
}
void SandboxIntegrationManager::InstantiateSliceFromAssetId(const AZ::Data::AssetId& assetId)
{
AZ::Transform sliceWorldTransform = AZ::Transform::CreateIdentity();
CViewport* view = GetIEditor()->GetViewManager()->GetGameViewport();
// If we don't have a viewport active to aid in placement, the slice
// will be instantiated at the origin.
if (view)
{
const QPoint viewPoint(static_cast<int>(m_contextMenuViewPoint.GetX()), static_cast<int>(m_contextMenuViewPoint.GetY()));
sliceWorldTransform = AZ::Transform::CreateTranslation(LYVec3ToAZVec3(view->SnapToGrid(view->ViewToWorld(viewPoint))));
}
AzToolsFramework::SliceRequestBus::Broadcast(&AzToolsFramework::SliceRequests::InstantiateSliceFromAssetId, assetId, sliceWorldTransform);
}
//////////////////////////////////////////////////////////////////////////
// Returns true if at least one non-layer entity was found.
bool CollectEntityBoundingBoxesForZoom(const AZ::EntityId& entityId, AABB& selectionBounds)
{
bool isLayerEntity = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
isLayerEntity,
entityId,
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (isLayerEntity)
{
// If a layer is in the selection, zoom to its children and ignore the layer itself.
AZStd::vector<AZ::EntityId> layerChildren;
AZ::TransformBus::EventResult(
/*result*/ layerChildren,
/*address*/ entityId,
&AZ::TransformBus::Events::GetChildren);
bool childResults = false;
for (const AZ::EntityId& childId : layerChildren)
{
if (CollectEntityBoundingBoxesForZoom(childId, selectionBounds))
{
// At least one child is not a layer.
childResults = true;
}
}
return childResults;
}
else
{
AABB entityBoundingBox;
CEntityObject* componentEntityObject = nullptr;
AzToolsFramework::ComponentEntityEditorRequestBus::EventResult(
/*result*/ componentEntityObject,
/*address*/ entityId,
&AzToolsFramework::ComponentEntityEditorRequestBus::Events::GetSandboxObject);
if (componentEntityObject)
{
componentEntityObject->GetBoundBox(entityBoundingBox);
selectionBounds.Add(entityBoundingBox.min);
selectionBounds.Add(entityBoundingBox.max);
}
return true;
}
}
//////////////////////////////////////////////////////////////////////////
void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework::EntityIdList& entityIds)
{
if (entityIds.empty())
{
return;
}
const AZ::Aabb aabb = AZStd::accumulate(
AZStd::begin(entityIds), AZStd::end(entityIds), AZ::Aabb::CreateNull(), [](AZ::Aabb acc, const AZ::EntityId entityId) {
const AZ::Aabb aabb = AzFramework::CalculateEntityWorldBoundsUnion(AzToolsFramework::GetEntityById(entityId));
acc.AddAabb(aabb);
return acc;
});
float radius;
AZ::Vector3 center;
aabb.GetAsSphere(center, radius);
// minimum center size is 40cm
const float minSelectionRadius = 0.4f;
const float selectionSize = AZ::GetMax(minSelectionRadius, radius);
auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
const int viewCount = GetIEditor()->GetViewManager()->GetViewCount(); // legacy call
for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
{
if (auto viewportContext = viewportContextManager->GetViewportContextById(viewIndex))
{
const AZ::Transform cameraTransform = viewportContext->GetCameraTransform();
// do not attempt to interpolate to where we currently are
if (cameraTransform.GetTranslation().IsClose(center))
{
continue;
}
const AZ::Vector3 forward = (center - cameraTransform.GetTranslation()).GetNormalized();
// move camera 25% further back than required
const float centerScale = 1.25f;
// compute new camera transform
const float fov = AzFramework::RetrieveFov(viewportContext->GetCameraProjectionMatrix());
const float fovScale = (1.0f / AZStd::tan(fov * 0.5f));
const float distanceToLookAt = selectionSize * fovScale * centerScale;
const AZ::Transform nextCameraTransform =
AZ::Transform::CreateLookAt(aabb.GetCenter() - (forward * distanceToLookAt), aabb.GetCenter());
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
viewportContext->GetId(),
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform);
}
}
}
void SandboxIntegrationManager::ContextMenu_SelectSlice()
{
AzToolsFramework::EntityIdList selectedEntities;
GetSelectedOrHighlightedEntities(selectedEntities);
AzToolsFramework::EntityIdList newSelectedEntities;
for (const AZ::EntityId& entityId : selectedEntities)
{
AZ::SliceComponent::SliceInstanceAddress sliceAddress;
AzFramework::SliceEntityRequestBus::EventResult(sliceAddress, entityId,
&AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
if (sliceAddress.IsValid())
{
const AZ::SliceComponent::InstantiatedContainer* instantiated = sliceAddress.GetInstance()->GetInstantiated();
if (instantiated)
{
for (AZ::Entity* entityInSlice : instantiated->m_entities)
{
newSelectedEntities.push_back(entityInSlice->GetId());
}
}
}
}
EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus,
SetSelectedEntities, newSelectedEntities);
}
void SandboxIntegrationManager::ContextMenu_PushEntitiesToSlice(AzToolsFramework::EntityIdList entities,
AZ::SliceComponent::EntityAncestorList ancestors,
AZ::Data::AssetId targetAncestorId,
bool affectEntireHierarchy)
{
(void)ancestors;
(void)targetAncestorId;
(void)affectEntireHierarchy;
AZ::SerializeContext* serializeContext = nullptr;
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(serializeContext, "No serialize context");
AzToolsFramework::SliceUtilities::PushEntitiesModal(GetMainWindow(), entities, serializeContext);
}
void SandboxIntegrationManager::ContextMenu_Duplicate()
{
bool handled = true;
AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequests::CloneSelection, handled);
}
void SandboxIntegrationManager::ContextMenu_DeleteSelected()
{
DeleteSelectedEntities(true);
}
void SandboxIntegrationManager::ContextMenu_ResetToSliceDefaults(AzToolsFramework::EntityIdList entities)
{
AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Broadcast(
&AzToolsFramework::SliceEditorEntityOwnershipServiceRequests::ResetEntitiesToSliceDefaults, entities);
}
void SandboxIntegrationManager::GetSelectedEntities(AzToolsFramework::EntityIdList& entities)
{
EBUS_EVENT_RESULT(entities,
AzToolsFramework::ToolsApplicationRequests::Bus,
GetSelectedEntities);
}
void SandboxIntegrationManager::GetSelectedOrHighlightedEntities(AzToolsFramework::EntityIdList& entities)
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::EntityIdList highlightedEntities;
EBUS_EVENT_RESULT(selectedEntities,
AzToolsFramework::ToolsApplicationRequests::Bus,
GetSelectedEntities);
EBUS_EVENT_RESULT(highlightedEntities,
AzToolsFramework::ToolsApplicationRequests::Bus,
GetHighlightedEntities);
entities = AZStd::move(selectedEntities);
for (AZ::EntityId highlightedId : highlightedEntities)
{
if (entities.end() == AZStd::find(entities.begin(), entities.end(), highlightedId))
{
entities.push_back(highlightedId);
}
}
}
AZStd::string SandboxIntegrationManager::GetComponentEditorIcon(const AZ::Uuid& componentType, AZ::Component* component)
{
AZStd::string iconPath = GetComponentIconPath(componentType, AZ::Edit::Attributes::Icon, component);
return iconPath;
}
AZStd::string SandboxIntegrationManager::GetComponentTypeEditorIcon(const AZ::Uuid& componentType)
{
return GetComponentEditorIcon(componentType, nullptr);
}
AZStd::string SandboxIntegrationManager::GetComponentIconPath(const AZ::Uuid& componentType,
AZ::Crc32 componentIconAttrib, AZ::Component* component)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
if (componentIconAttrib != AZ::Edit::Attributes::Icon
&& componentIconAttrib != AZ::Edit::Attributes::ViewportIcon
&& componentIconAttrib != AZ::Edit::Attributes::HideIcon)
{
AZ_Warning("SandboxIntegration", false, "Unrecognized component icon attribute!");
}
// return blank path if component shouldn't have icon at all
AZStd::string iconPath;
AZ::SerializeContext* serializeContext = nullptr;
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(serializeContext, "No serialize context");
auto classData = serializeContext->FindClassData(componentType);
if (classData && classData->m_editData)
{
// check if component icon should be hidden
bool hideIcon = false;
auto editorElementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
if (editorElementData)
{
if (auto hideIconAttribute = editorElementData->FindAttribute(AZ::Edit::Attributes::HideIcon))
{
auto hideIconAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<bool>*>(hideIconAttribute);
if (hideIconAttributeData)
{
hideIcon = hideIconAttributeData->Get(nullptr);
}
}
}
if (!hideIcon)
{
// component should have icon. start with default
iconPath = GetDefaultComponentEditorIcon();
// check for specific icon
if (editorElementData)
{
if (auto iconAttribute = editorElementData->FindAttribute(componentIconAttrib))
{
if (auto iconAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(iconAttribute))
{
AZStd::string iconAttributeValue = iconAttributeData->Get(component);
if (!iconAttributeValue.empty())
{
iconPath = AZStd::move(iconAttributeValue);
}
}
auto iconOverrideAttribute = editorElementData->FindAttribute(AZ::Edit::Attributes::DynamicIconOverride);
// If it has an override and we're given an instance, then get any potential override from the instance here
if (component &&
(componentIconAttrib == AZ::Edit::Attributes::Icon || componentIconAttrib == AZ::Edit::Attributes::ViewportIcon) &&
iconOverrideAttribute)
{
AZStd::string iconValue;
AZ::AttributeReader iconReader(const_cast<AZ::Component*>(component), iconOverrideAttribute);
iconReader.Read<AZStd::string>(iconValue);
if (!iconValue.empty())
{
iconPath = AZStd::move(iconValue);
}
}
}
}
// If Qt doesn't know where the relative path is we have to use the more expensive full path
if (!QFile::exists(QString(iconPath.c_str())))
{
// use absolute path if possible
AZStd::string iconFullPath;
bool pathFound = false;
using AssetSysReqBus = AzToolsFramework::AssetSystemRequestBus;
AssetSysReqBus::BroadcastResult(
pathFound, &AssetSysReqBus::Events::GetFullSourcePathFromRelativeProductPath,
iconPath, iconFullPath);
if (pathFound)
{
iconPath = AZStd::move(iconFullPath);
}
}
}
}
return iconPath;
}
void SandboxIntegrationManager::UndoStackFlushed()
{
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequestBus::Events::FlushUndo);
}
void SandboxIntegrationManager::MakeSliceFromEntities(const AzToolsFramework::EntityIdList& entities, bool inheritSlices, bool setAsDynamic)
{
// expand the list of entities to include all transform descendant entities
AzToolsFramework::EntityIdSet entitiesAndDescendants;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(entitiesAndDescendants,
&AzToolsFramework::ToolsApplicationRequestBus::Events::GatherEntitiesAndAllDescendents, entities);
const AZStd::string slicesAssetsPath = "@projectroot@/Slices";
if (!gEnv->pFileIO->Exists(slicesAssetsPath.c_str()))
{
gEnv->pFileIO->CreatePath(slicesAssetsPath.c_str());
}
char path[AZ_MAX_PATH_LEN] = { 0 };
gEnv->pFileIO->ResolvePath(slicesAssetsPath.c_str(), path, AZ_MAX_PATH_LEN);
AzToolsFramework::SliceUtilities::MakeNewSlice(entitiesAndDescendants, path, inheritSlices, setAsDynamic);
}
void SandboxIntegrationManager::RegisterViewPane(const char* name, const char* category, const AzToolsFramework::ViewPaneOptions& viewOptions, const WidgetCreationFunc& widgetCreationFunc)
{
QtViewPaneManager::instance()->RegisterPane(name, category, widgetCreationFunc, viewOptions);
}
void SandboxIntegrationManager::RegisterCustomViewPane(const char* name, const char* category, const AzToolsFramework::ViewPaneOptions& viewOptions)
{
QtViewPaneManager::instance()->RegisterPane(name, category, nullptr, viewOptions);
}
void SandboxIntegrationManager::UnregisterViewPane(const char* name)
{
QtViewPaneManager::instance()->UnregisterPane(name);
}
QWidget* SandboxIntegrationManager::GetViewPaneWidget(const char* viewPaneName)
{
return FindViewPane<QWidget>(viewPaneName);
}
void SandboxIntegrationManager::OpenViewPane(const char* paneName)
{
const QtViewPane* pane = QtViewPaneManager::instance()->OpenPane(paneName);
if (pane)
{
pane->m_dockWidget->raise();
pane->m_dockWidget->activateWindow();
}
}
QDockWidget* SandboxIntegrationManager::InstanceViewPane(const char* paneName)
{
return QtViewPaneManager::instance()->InstancePane(paneName);
}
void SandboxIntegrationManager::CloseViewPane(const char* paneName)
{
QtViewPaneManager::instance()->ClosePane(paneName);
}
void SandboxIntegrationManager::BrowseForAssets(AssetSelectionModel& selection)
{
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, GetMainWindow());
}
bool SandboxIntegrationManager::DisplayHelpersVisible()
{
return GetIEditor()->GetDisplaySettings()->IsDisplayHelpers();
}