Integrating up through commit 90f050496
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
using EntityIdList = AZStd::vector<AZ::EntityId>;
|
||||
|
||||
/*!
|
||||
* EditorEntityAPI
|
||||
* Handles basic Entity operations
|
||||
*/
|
||||
class EditorEntityAPI
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(EditorEntityAPI, "{3E217E21-046F-462E-8FA2-1347FBDDFDE7}");
|
||||
|
||||
/**
|
||||
* Delete all currently-selected entities.
|
||||
*/
|
||||
virtual void DeleteSelected() = 0;
|
||||
|
||||
/**
|
||||
* Deletes the specified entity.
|
||||
*/
|
||||
virtual void DeleteEntityById(AZ::EntityId entityId) = 0;
|
||||
|
||||
/**
|
||||
* Deletes all specified entities.
|
||||
*/
|
||||
virtual void DeleteEntities(const EntityIdList& entities) = 0;
|
||||
|
||||
/**
|
||||
* Deletes the specified entity, as well as any transform descendants.
|
||||
*/
|
||||
virtual void DeleteEntityAndAllDescendants(AZ::EntityId entityId) = 0;
|
||||
|
||||
/**
|
||||
* Deletes all entities in the provided list, as well as their transform descendants.
|
||||
*/
|
||||
virtual void DeleteEntitiesAndAllDescendants(const EntityIdList& entities) = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -588,13 +588,6 @@ namespace AzToolsFramework
|
||||
*/
|
||||
virtual const char* GetEngineVersion() const = 0;
|
||||
|
||||
/**
|
||||
* Retrieves if Legacy Slice System is enabled
|
||||
*/
|
||||
virtual bool IsLegacySliceSystemEnabled() const = 0;
|
||||
|
||||
virtual bool ShouldAssertForLegacySlicesUsage() 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
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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/Application/EditorEntityManager.h>
|
||||
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
void EditorEntityManager::Start()
|
||||
{
|
||||
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
|
||||
AZ_Assert(m_prefabPublicInterface, "EditorEntityManager - Could not retrieve instance of PrefabPublicInterface");
|
||||
|
||||
AZ::Interface<EditorEntityAPI>::Register(this);
|
||||
}
|
||||
|
||||
EditorEntityManager::~EditorEntityManager()
|
||||
{
|
||||
// Attempting to Unregister if we never registerd (e.g. if Start() was never called) throws an error, so check that first
|
||||
EditorEntityAPI* editorEntityInterface = AZ::Interface<EditorEntityAPI>::Get();
|
||||
if (editorEntityInterface == this)
|
||||
{
|
||||
AZ::Interface<EditorEntityAPI>::Unregister(this);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorEntityManager::DeleteSelected()
|
||||
{
|
||||
EntityIdList selectedEntities;
|
||||
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities);
|
||||
m_prefabPublicInterface->DeleteEntitiesInInstance(selectedEntities);
|
||||
}
|
||||
|
||||
void EditorEntityManager::DeleteEntityById(AZ::EntityId entityId)
|
||||
{
|
||||
DeleteEntities({entityId});
|
||||
}
|
||||
|
||||
void EditorEntityManager::DeleteEntities(const EntityIdList& entities)
|
||||
{
|
||||
m_prefabPublicInterface->DeleteEntitiesInInstance(entities);
|
||||
}
|
||||
|
||||
void EditorEntityManager::DeleteEntityAndAllDescendants(AZ::EntityId entityId)
|
||||
{
|
||||
DeleteEntitiesAndAllDescendants({entityId});
|
||||
}
|
||||
|
||||
void EditorEntityManager::DeleteEntitiesAndAllDescendants(const EntityIdList& entities)
|
||||
{
|
||||
m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance(entities);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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/API/EditorEntityAPI.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class EditorEntityManager
|
||||
: public EditorEntityAPI
|
||||
{
|
||||
public:
|
||||
~EditorEntityManager();
|
||||
|
||||
void Start();
|
||||
|
||||
// EditorEntityAPI...
|
||||
void DeleteSelected() override;
|
||||
void DeleteEntityById(AZ::EntityId entityId) override;
|
||||
void DeleteEntities(const EntityIdList& entities) override;
|
||||
void DeleteEntityAndAllDescendants(AZ::EntityId entityId) override;
|
||||
void DeleteEntitiesAndAllDescendants(const EntityIdList& entities) override;
|
||||
|
||||
private:
|
||||
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
+111
-58
@@ -68,6 +68,8 @@
|
||||
#include <AzToolsFramework/AssetEditor/AssetEditorBus.h>
|
||||
#include <AzToolsFramework/Render/EditorIntersectorComponent.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiSystemComponent.h>
|
||||
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
|
||||
#include <QtWidgets/QMessageBox>
|
||||
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer<QFileInfoPrivate>' needs to have dll-interface to be used by clients of class 'QFileInfo'
|
||||
@@ -78,7 +80,6 @@ AZ_POP_DISABLE_OVERRIDE_WARNING
|
||||
#include <QJsonObject>
|
||||
#include <QMap>
|
||||
|
||||
|
||||
// Not possible to use AZCore's operator new overrides until we address the overall problems
|
||||
// with allocators, or more likely convert AzToolsFramework to a DLL and restrict overloading to
|
||||
// within the DLL. Since this is currently linked as a lib, overriding new and delete would require
|
||||
@@ -95,9 +96,6 @@ namespace AzToolsFramework
|
||||
|
||||
static const char* s_startupLogWindow = "Startup";
|
||||
|
||||
static const char* s_prefabSystemKey = "/Amazon/Editor/Preferences/EnablePrefabSystem";
|
||||
static const char* s_legacySlicesAssertKey = "/Amazon/Editor/Preferences/ShouldAssertForLegacySlicesUsage";
|
||||
|
||||
template<typename IdContainerType>
|
||||
void DeleteEntities(const IdContainerType& entityIds)
|
||||
{
|
||||
@@ -341,8 +339,9 @@ namespace AzToolsFramework
|
||||
, m_isInIsolationMode(false)
|
||||
{
|
||||
ToolsApplicationRequests::Bus::Handler::BusConnect();
|
||||
m_engineConfigImpl.reset(new ToolsApplication::EngineConfigImpl(AzToolsFramework::Internal::s_startupLogWindow,
|
||||
AzToolsFramework::Internal::s_engineConfigFileName));
|
||||
m_engineConfigImpl.reset(new ToolsApplication::EngineConfigImpl(AzToolsFramework::Internal::s_startupLogWindow, AzToolsFramework::Internal::s_engineConfigFileName));
|
||||
|
||||
m_undoCache.RegisterToUndoCacheInterface();
|
||||
}
|
||||
|
||||
ToolsApplication::~ToolsApplication()
|
||||
@@ -393,6 +392,11 @@ namespace AzToolsFramework
|
||||
{
|
||||
Application::Start(descriptor, startupParameters);
|
||||
InitializeEngineConfig();
|
||||
|
||||
m_editorEntityManager.Start();
|
||||
|
||||
m_editorEntityAPI = AZ::Interface<EditorEntityAPI>::Get();
|
||||
AZ_Assert(m_editorEntityAPI, "ToolsApplication - Could not retrieve instance of EditorEntityAPI");
|
||||
}
|
||||
|
||||
void ToolsApplication::InitializeEngineConfig()
|
||||
@@ -416,7 +420,11 @@ namespace AzToolsFramework
|
||||
{
|
||||
FlushUndo();
|
||||
|
||||
m_undoCache.Clear();
|
||||
auto undoCacheInterface = AZ::Interface<UndoSystem::UndoCacheInterface>::Get();
|
||||
if (undoCacheInterface)
|
||||
{
|
||||
undoCacheInterface->Clear();
|
||||
}
|
||||
|
||||
delete m_undoStack;
|
||||
m_undoStack = nullptr;
|
||||
@@ -426,6 +434,19 @@ namespace AzToolsFramework
|
||||
m_highlightedEntities.set_capacity(0);
|
||||
m_dirtyEntities = {};
|
||||
|
||||
bool isPrefabSystemEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
|
||||
if (isPrefabSystemEnabled)
|
||||
{
|
||||
// This resets the editor context thereby asking the systems that own the entities to destroy them. By doing this, we are
|
||||
// duly giving the authority to delete the entities to the systems that owns them, rather than leaving it to the
|
||||
// ComponentApplication to do the cleanup.
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorEntityContextRequestBus::Events::ResetEditorContext);
|
||||
}
|
||||
|
||||
GetSerializeContext()->DestroyEditContext();
|
||||
|
||||
Application::Stop();
|
||||
@@ -554,7 +575,11 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
m_undoCache.PurgeCache(entity->GetId());
|
||||
auto undoCacheInterface = AZ::Interface<UndoSystem::UndoCacheInterface>::Get();
|
||||
if (undoCacheInterface)
|
||||
{
|
||||
undoCacheInterface->PurgeCache(entity->GetId());
|
||||
}
|
||||
|
||||
MarkEntityDeselected(entity->GetId());
|
||||
SetEntityHighlighted(entity->GetId(), false);
|
||||
@@ -724,7 +749,6 @@ namespace AzToolsFramework
|
||||
void ToolsApplication::SetEntityHighlighted(AZ::EntityId entityId, bool highlighted)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
AZ_Assert(entityId.IsValid(), "Attempting to mark an invalid entity as highlighted.");
|
||||
|
||||
auto foundIter = AZStd::find(m_highlightedEntities.begin(), m_highlightedEntities.end(), entityId);
|
||||
if (foundIter != m_highlightedEntities.end())
|
||||
@@ -893,16 +917,34 @@ namespace AzToolsFramework
|
||||
|
||||
void ToolsApplication::DeleteSelected()
|
||||
{
|
||||
if (IsPrefabSystemEnabled())
|
||||
{
|
||||
m_editorEntityAPI->DeleteSelected();
|
||||
return;
|
||||
}
|
||||
|
||||
Internal::DeleteEntities(m_selectedEntities);
|
||||
}
|
||||
|
||||
void ToolsApplication::DeleteEntityAndAllDescendants(AZ::EntityId entityId)
|
||||
{
|
||||
if (IsPrefabSystemEnabled())
|
||||
{
|
||||
m_editorEntityAPI->DeleteEntityAndAllDescendants(entityId);
|
||||
return;
|
||||
}
|
||||
|
||||
DeleteEntitiesAndAllDescendants({ entityId });
|
||||
}
|
||||
|
||||
void ToolsApplication::DeleteEntitiesAndAllDescendants(const EntityIdList& entities)
|
||||
{
|
||||
if (IsPrefabSystemEnabled())
|
||||
{
|
||||
m_editorEntityAPI->DeleteEntitiesAndAllDescendants(entities);
|
||||
return;
|
||||
}
|
||||
|
||||
const EntityIdSet entitiesAndDescendants = GatherEntitiesAndAllDescendents(entities);
|
||||
Internal::DeleteEntities(entitiesAndDescendants);
|
||||
}
|
||||
@@ -1367,11 +1409,23 @@ namespace AzToolsFramework
|
||||
|
||||
void ToolsApplication::DeleteEntityById(AZ::EntityId entityId)
|
||||
{
|
||||
if (IsPrefabSystemEnabled())
|
||||
{
|
||||
m_editorEntityAPI->DeleteEntityById(entityId);
|
||||
return;
|
||||
}
|
||||
|
||||
DeleteEntities({ entityId });
|
||||
}
|
||||
|
||||
void ToolsApplication::DeleteEntities(const EntityIdList& entities)
|
||||
{
|
||||
if (IsPrefabSystemEnabled())
|
||||
{
|
||||
m_editorEntityAPI->DeleteEntities(entities);
|
||||
return;
|
||||
}
|
||||
|
||||
Internal::DeleteEntities(entities);
|
||||
}
|
||||
|
||||
@@ -1572,7 +1626,6 @@ namespace AzToolsFramework
|
||||
else
|
||||
{
|
||||
// we're at the root
|
||||
|
||||
// only undo at bottom of scope (first invoked ScopedUndoBatch in
|
||||
// chain/hierarchy must go out of scope)
|
||||
CreateUndosForDirtyEntities();
|
||||
@@ -1612,48 +1665,68 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
// If the current undo batch has commands in it, then we have to check that we do not add duplicates
|
||||
// However if it starts out empty, we can just add things straight from the Set to the undo batch
|
||||
bool mustCheckDuplicates = !m_currentBatchUndo->GetChildren().empty();
|
||||
|
||||
for (AZ::EntityId entityId : m_dirtyEntities)
|
||||
if (!IsPrefabSystemEnabled())
|
||||
{
|
||||
AZ::Entity* entity = nullptr;
|
||||
EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, entityId);
|
||||
// If the current undo batch has commands in it, then we have to check that we do not add duplicates
|
||||
// However if it starts out empty, we can just add things straight from the Set to the undo batch
|
||||
bool mustCheckDuplicates = !m_currentBatchUndo->GetChildren().empty();
|
||||
|
||||
if (entity)
|
||||
for (AZ::EntityId entityId : m_dirtyEntities)
|
||||
{
|
||||
EntityStateCommand* state = nullptr;
|
||||
AZ::Entity* entity = nullptr;
|
||||
EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, entityId);
|
||||
|
||||
if (mustCheckDuplicates)
|
||||
if (entity)
|
||||
{
|
||||
// Check if this entity is already in the current undo batch
|
||||
state = azdynamic_cast<EntityStateCommand*>(m_currentBatchUndo->Find(
|
||||
static_cast<AZ::u64>(entityId), AZ::AzTypeInfo<EntityStateCommand>::Uuid()));
|
||||
EntityStateCommand* state = nullptr;
|
||||
|
||||
if (mustCheckDuplicates)
|
||||
{
|
||||
// Check if this entity is already in the current undo batch
|
||||
state = azdynamic_cast<EntityStateCommand*>(
|
||||
m_currentBatchUndo->Find(static_cast<AZ::u64>(entityId), AZ::AzTypeInfo<EntityStateCommand>::Uuid()));
|
||||
}
|
||||
|
||||
if (!state)
|
||||
{
|
||||
state = aznew EntityStateCommand(static_cast<AZ::u64>(entityId));
|
||||
state->SetParent(m_currentBatchUndo);
|
||||
|
||||
// capture initial state of entity (before undo)
|
||||
state->Capture(entity, true);
|
||||
}
|
||||
|
||||
// capture last state of entity (after undo) - for redo
|
||||
state->Capture(entity, false);
|
||||
}
|
||||
|
||||
if (!state)
|
||||
{
|
||||
state = aznew EntityStateCommand(static_cast<AZ::u64>(entityId));
|
||||
state->SetParent(m_currentBatchUndo);
|
||||
|
||||
// capture initial state of entity (before undo)
|
||||
state->Capture(entity, true);
|
||||
}
|
||||
|
||||
// capture last state of entity (after undo) - for redo
|
||||
state->Capture(entity, false);
|
||||
m_undoCache.UpdateCache(entityId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
|
||||
if (prefabPublicInterface)
|
||||
{
|
||||
// Compared to the preemptive undo cache, we can avoid the duplicate check.
|
||||
// Multiple changes to the same entity are just split between different undo nodes.
|
||||
for (AZ::EntityId entityId : m_dirtyEntities)
|
||||
{
|
||||
prefabPublicInterface->GenerateUndoNodesForEntityChangeAndUpdateCache(entityId, m_currentBatchUndo);
|
||||
}
|
||||
}
|
||||
|
||||
m_undoCache.UpdateCache(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
void ToolsApplication::ConsistencyCheckUndoCache()
|
||||
{
|
||||
for (auto && entityEntry : m_entities)
|
||||
auto undoCacheInterface = AZ::Interface<UndoSystem::UndoCacheInterface>::Get();
|
||||
if (undoCacheInterface)
|
||||
{
|
||||
m_undoCache.Validate((entityEntry.second)->GetId());
|
||||
for (auto&& entityEntry : m_entities)
|
||||
{
|
||||
undoCacheInterface->Validate((entityEntry.second)->GetId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1769,26 +1842,6 @@ namespace AzToolsFramework
|
||||
return m_engineConfigImpl->GetEngineVersion();
|
||||
}
|
||||
|
||||
bool ToolsApplication::IsLegacySliceSystemEnabled() const
|
||||
{
|
||||
bool value = false;
|
||||
if (auto* registry = AZ::SettingsRegistry::Get())
|
||||
{
|
||||
registry->Get(value, Internal::s_prefabSystemKey);
|
||||
}
|
||||
return !value;
|
||||
}
|
||||
|
||||
bool ToolsApplication::ShouldAssertForLegacySlicesUsage() const
|
||||
{
|
||||
bool value = false;
|
||||
if (auto* registry = AZ::SettingsRegistry::Get())
|
||||
{
|
||||
registry->Get(value, Internal::s_legacySlicesAssertKey);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void ToolsApplication::CreateAndAddEntityFromComponentTags(const AZStd::vector<AZ::Crc32>& requiredTags, const char* entityName)
|
||||
{
|
||||
if (!entityName || !entityName[0])
|
||||
|
||||
@@ -17,12 +17,19 @@
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <AzFramework/Asset/SimpleAsset.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/API/EditorEntityAPI.h>
|
||||
#include <AzToolsFramework/Application/EditorEntityManager.h>
|
||||
#include <AzToolsFramework/Commands/PreemptiveUndoCache.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace UndoSystem
|
||||
{
|
||||
class UndoCacheInterface;
|
||||
}
|
||||
|
||||
class ToolsApplication
|
||||
: public AzFramework::Application
|
||||
, public ToolsApplicationRequests::Bus::Handler
|
||||
@@ -145,8 +152,6 @@ namespace AzToolsFramework
|
||||
bool IsEditorInIsolationMode() override;
|
||||
const char* GetEngineRootPath() const override;
|
||||
const char* GetEngineVersion() const override;
|
||||
bool IsLegacySliceSystemEnabled() const override;
|
||||
bool ShouldAssertForLegacySlicesUsage() const override;
|
||||
|
||||
void CreateAndAddEntityFromComponentTags(const AZStd::vector<AZ::Crc32>& requiredTags, const char* entityName) override;
|
||||
|
||||
@@ -183,6 +188,12 @@ namespace AzToolsFramework
|
||||
|
||||
class EngineConfigImpl;
|
||||
AZStd::unique_ptr<EngineConfigImpl> m_engineConfigImpl;
|
||||
|
||||
EditorEntityAPI* m_editorEntityAPI = nullptr;
|
||||
|
||||
EditorEntityManager m_editorEntityManager;
|
||||
|
||||
UndoSystem::UndoCacheInterface* m_undoCacheInterface = nullptr;
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/FolderThumbnail.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/AssetBrowserProductThumbnail.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.h>
|
||||
#include <AzToolsFramework/Slice/SliceUtilities.h>
|
||||
|
||||
|
||||
+5
-4
@@ -17,7 +17,7 @@
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/AssetBrowserProductThumbnail.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
|
||||
|
||||
#include <QVariant>
|
||||
@@ -120,13 +120,14 @@ namespace AzToolsFramework
|
||||
|
||||
void ProductAssetBrowserEntry::ThumbnailUpdated()
|
||||
{
|
||||
// if source is displaying product's thumbnail, then it needs to also listen to its ThumbnailUpdated
|
||||
if (m_parentAssetEntry)
|
||||
if (EntryCache* cache = EntryCache::GetInstance())
|
||||
{
|
||||
if (EntryCache* cache = EntryCache::GetInstance())
|
||||
// if source is displaying product's thumbnail, then it needs to also listen to its ThumbnailUpdated
|
||||
if (m_parentAssetEntry)
|
||||
{
|
||||
cache->m_dirtyThumbnailsSet.insert(m_parentAssetEntry);
|
||||
}
|
||||
cache->m_dirtyThumbnailsSet.insert(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -207,6 +207,7 @@ namespace AzToolsFramework
|
||||
auto source = azrtti_cast<SourceAssetBrowserEntry*>(itFile->second);
|
||||
source->m_sourceId = sourceWithFileIdEntry.second.m_sourceID;
|
||||
source->m_sourceUuid = sourceWithFileIdEntry.second.m_sourceGuid;
|
||||
source->PathsUpdated(); // update thumbnailkey to valid uuid
|
||||
EntryCache::GetInstance()->m_sourceUuidMap[source->m_sourceUuid] = source;
|
||||
EntryCache::GetInstance()->m_sourceIdMap[source->m_sourceId] = source;
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ namespace AzToolsFramework
|
||||
|
||||
SharedThumbnailKey SourceAssetBrowserEntry::CreateThumbnailKey()
|
||||
{
|
||||
return MAKE_TKEY(SourceThumbnailKey, m_fullPath.c_str());
|
||||
return MAKE_TKEY(SourceThumbnailKey, m_sourceUuid);
|
||||
}
|
||||
|
||||
SharedThumbnailKey SourceAssetBrowserEntry::GetSourceControlThumbnailKey() const
|
||||
|
||||
+17
-2
@@ -14,7 +14,7 @@
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/AssetBrowserProductThumbnail.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
|
||||
#include <QPixmap>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -37,6 +37,21 @@ namespace AzToolsFramework
|
||||
|
||||
const AZ::Data::AssetType& ProductThumbnailKey::GetAssetType() const { return m_assetType; }
|
||||
|
||||
size_t ProductThumbnailKey::GetHash() const
|
||||
{
|
||||
return m_assetType.GetHash();
|
||||
}
|
||||
|
||||
bool ProductThumbnailKey::Equals(const ThumbnailKey* other) const
|
||||
{
|
||||
if (!ThumbnailKey::Equals(other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// products displayed in Asset Browser have icons based on asset type, so multiple different products with same asset type will have same thumbnail
|
||||
return m_assetId == azrtti_cast<const ProductThumbnailKey*>(other)->GetAssetId();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ProductThumbnail
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -93,7 +108,7 @@ namespace AzToolsFramework
|
||||
// ProductThumbnailCache
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ProductThumbnailCache::ProductThumbnailCache()
|
||||
: ThumbnailCache<ProductThumbnail, ProductKeyHash, ProductKeyEqual>() {}
|
||||
: ThumbnailCache<ProductThumbnail>() {}
|
||||
|
||||
ProductThumbnailCache::~ProductThumbnailCache() = default;
|
||||
|
||||
|
||||
+9
-1
@@ -39,6 +39,14 @@ namespace AzToolsFramework
|
||||
return m_isGem;
|
||||
}
|
||||
|
||||
bool FolderThumbnailKey::Equals(const ThumbnailKey* other) const
|
||||
{
|
||||
if (!ThumbnailKey::Equals(other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return m_isGem == azrtti_cast<const FolderThumbnailKey*>(other)->IsGem();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// FolderThumbnail
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -68,7 +76,7 @@ namespace AzToolsFramework
|
||||
// FolderThumbnailCache
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
FolderThumbnailCache::FolderThumbnailCache()
|
||||
: ThumbnailCache<FolderThumbnail, FolderKeyHash, FolderKeyEqual>() {}
|
||||
: ThumbnailCache<FolderThumbnail>() {}
|
||||
|
||||
FolderThumbnailCache::~FolderThumbnailCache() = default;
|
||||
|
||||
|
||||
+2
-27
@@ -33,6 +33,7 @@ namespace AzToolsFramework
|
||||
FolderThumbnailKey(const char* folderPath, bool isGem);
|
||||
const AZStd::string& GetFolderPath() const;
|
||||
bool IsGem() const;
|
||||
bool Equals(const ThumbnailKey* other) const override;
|
||||
|
||||
protected:
|
||||
//! Absolute folder path
|
||||
@@ -50,36 +51,10 @@ namespace AzToolsFramework
|
||||
void LoadThread() override;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
class FolderKeyHash
|
||||
{
|
||||
public:
|
||||
size_t operator() (const SharedThumbnailKey& /*val*/) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class FolderKeyEqual
|
||||
{
|
||||
public:
|
||||
bool operator()(const SharedThumbnailKey& val1, const SharedThumbnailKey& val2) const
|
||||
{
|
||||
auto folderThumbnailKey1 = azrtti_cast<const FolderThumbnailKey*>(val1.data());
|
||||
auto folderThumbnailKey2 = azrtti_cast<const FolderThumbnailKey*>(val2.data());
|
||||
if (!folderThumbnailKey1 || !folderThumbnailKey2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// There are only two thumbnails in this cache, one for gem icon and one for folder icon
|
||||
return folderThumbnailKey1->IsGem() == folderThumbnailKey2->IsGem();
|
||||
}
|
||||
};
|
||||
}
|
||||
//! FolderAssetBrowserEntry thumbnails
|
||||
class FolderThumbnailCache
|
||||
: public ThumbnailCache<FolderThumbnail, FolderKeyHash, FolderKeyEqual>
|
||||
: public ThumbnailCache<FolderThumbnail>
|
||||
{
|
||||
public:
|
||||
FolderThumbnailCache();
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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 <AzCore/Asset/AssetTypeInfoBus.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
|
||||
#include <QPixmap>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ProductThumbnailKey
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ProductThumbnailKey::ProductThumbnailKey(const AZ::Data::AssetId& assetId)
|
||||
: ThumbnailKey()
|
||||
, m_assetId(assetId)
|
||||
{
|
||||
AZ::Data::AssetInfo info;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(info, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, m_assetId);
|
||||
m_assetType = info.m_assetType;
|
||||
}
|
||||
|
||||
const AZ::Data::AssetId& ProductThumbnailKey::GetAssetId() const { return m_assetId; }
|
||||
|
||||
const AZ::Data::AssetType& ProductThumbnailKey::GetAssetType() const { return m_assetType; }
|
||||
|
||||
size_t ProductThumbnailKey::GetHash() const
|
||||
{
|
||||
return m_assetType.GetHash();
|
||||
}
|
||||
|
||||
bool ProductThumbnailKey::Equals(const ThumbnailKey* other) const
|
||||
{
|
||||
if (!ThumbnailKey::Equals(other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// products displayed in Asset Browser have icons based on asset type, so multiple different products with same asset type will have same thumbnail
|
||||
return m_assetId == azrtti_cast<const ProductThumbnailKey*>(other)->GetAssetId();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ProductThumbnail
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static const char* DEFAULT_PRODUCT_ICON_PATH = "Editor/Icons/AssetBrowser/DefaultProduct_16.svg";
|
||||
|
||||
ProductThumbnail::ProductThumbnail(Thumbnailer::SharedThumbnailKey key, int thumbnailSize)
|
||||
: Thumbnail(key, thumbnailSize)
|
||||
{}
|
||||
|
||||
void ProductThumbnail::LoadThread()
|
||||
{
|
||||
auto productKey = azrtti_cast<const ProductThumbnailKey*>(m_key.data());
|
||||
AZ_Assert(productKey, "Incorrect key type, excpected ProductThumbnailKey");
|
||||
|
||||
QString iconPath;
|
||||
AZ::AssetTypeInfoBus::EventResult(iconPath, productKey->GetAssetType(), &AZ::AssetTypeInfo::GetBrowserIcon);
|
||||
if (!iconPath.isEmpty())
|
||||
{
|
||||
// is it an embedded resource or absolute path?
|
||||
bool isUsablePath = (iconPath.startsWith(":") || (!AzFramework::StringFunc::Path::IsRelative(iconPath.toUtf8().constData())));
|
||||
|
||||
if (!isUsablePath)
|
||||
{
|
||||
// getting here means it needs resolution. Can we find the real path of the file? This also searches in gems for sources.
|
||||
bool foundIt = false;
|
||||
AZStd::string watchFolder;
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, iconPath.toUtf8().constData(), assetInfo, watchFolder);
|
||||
|
||||
if (foundIt)
|
||||
{
|
||||
// the absolute path is join(watchfolder, relativepath); // since its relative to the watch folder.
|
||||
AZStd::string finalPath;
|
||||
AzFramework::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), finalPath);
|
||||
iconPath = QString::fromUtf8(finalPath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no pixmap specified - use default.
|
||||
iconPath = QString::fromUtf8(DEFAULT_PRODUCT_ICON_PATH);
|
||||
}
|
||||
|
||||
m_icon = QIcon(iconPath);
|
||||
|
||||
if (m_icon.isNull())
|
||||
{
|
||||
m_state = State::Failed;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ProductThumbnailCache
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ProductThumbnailCache::ProductThumbnailCache()
|
||||
: ThumbnailCache<ProductThumbnail>() {}
|
||||
|
||||
ProductThumbnailCache::~ProductThumbnailCache() = default;
|
||||
|
||||
const char* ProductThumbnailCache::GetProviderName() const
|
||||
{
|
||||
return ProviderName;
|
||||
}
|
||||
|
||||
bool ProductThumbnailCache::IsSupportedThumbnail(Thumbnailer::SharedThumbnailKey key) const
|
||||
{
|
||||
return azrtti_istypeof<const ProductThumbnailKey*>(key.data());
|
||||
}
|
||||
|
||||
} // namespace AssetBrowser
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
#include "AssetBrowser/Thumbnails/moc_ProductThumbnail.cpp"
|
||||
+3
-34
@@ -31,6 +31,8 @@ namespace AzToolsFramework
|
||||
explicit ProductThumbnailKey(const AZ::Data::AssetId& assetId);
|
||||
const AZ::Data::AssetId& GetAssetId() const;
|
||||
const AZ::Data::AssetType& GetAssetType() const;
|
||||
size_t GetHash() const override;
|
||||
bool Equals(const ThumbnailKey* other) const override;
|
||||
|
||||
protected:
|
||||
AZ::Data::AssetId m_assetId;
|
||||
@@ -48,42 +50,9 @@ namespace AzToolsFramework
|
||||
void LoadThread() override;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
class ProductKeyHash
|
||||
{
|
||||
public:
|
||||
size_t operator() (const Thumbnailer::SharedThumbnailKey& val) const
|
||||
{
|
||||
auto productThumbnailKey = azrtti_cast<const ProductThumbnailKey*>(val.data());
|
||||
if (!productThumbnailKey)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return productThumbnailKey->GetAssetType().GetHash();
|
||||
}
|
||||
};
|
||||
|
||||
class ProductKeyEqual
|
||||
{
|
||||
public:
|
||||
bool operator()(const Thumbnailer::SharedThumbnailKey& val1, const Thumbnailer::SharedThumbnailKey& val2) const
|
||||
{
|
||||
auto productThumbnailKey1 = azrtti_cast<const ProductThumbnailKey*>(val1.data());
|
||||
auto productThumbnailKey2 = azrtti_cast<const ProductThumbnailKey*>(val2.data());
|
||||
if (!productThumbnailKey1 || !productThumbnailKey2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// products displayed in Asset Browser have icons based on asset type, so multiple different products with same asset type will have same thumbnail
|
||||
return productThumbnailKey1->GetAssetType() == productThumbnailKey2->GetAssetType();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//! ProductAssetBrowserEntry thumbnails
|
||||
class ProductThumbnailCache
|
||||
: public Thumbnailer::ThumbnailCache<ProductThumbnail, ProductKeyHash, ProductKeyEqual>
|
||||
: public Thumbnailer::ThumbnailCache<ProductThumbnail>
|
||||
{
|
||||
public:
|
||||
ProductThumbnailCache();
|
||||
+55
-40
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/EBus/Results.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
@@ -26,21 +27,29 @@ namespace AzToolsFramework
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// SourceThumbnailKey
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SourceThumbnailKey::SourceThumbnailKey(const char* fileName)
|
||||
SourceThumbnailKey::SourceThumbnailKey(const AZ::Uuid& sourceUuid)
|
||||
: ThumbnailKey()
|
||||
, m_fileName(fileName)
|
||||
, m_sourceUuid(sourceUuid)
|
||||
{
|
||||
AzFramework::StringFunc::Path::Split(fileName, nullptr, nullptr, nullptr, &m_extension);
|
||||
}
|
||||
|
||||
const AZStd::string& SourceThumbnailKey::GetFileName() const
|
||||
const AZ::Uuid& SourceThumbnailKey::GetSourceUuid() const
|
||||
{
|
||||
return m_fileName;
|
||||
return m_sourceUuid;
|
||||
}
|
||||
|
||||
const AZStd::string& SourceThumbnailKey::GetExtension() const
|
||||
size_t SourceThumbnailKey::GetHash() const
|
||||
{
|
||||
return m_extension;
|
||||
return m_sourceUuid.GetHash();
|
||||
}
|
||||
|
||||
bool SourceThumbnailKey::Equals(const ThumbnailKey* other) const
|
||||
{
|
||||
if (!ThumbnailKey::Equals(other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return m_sourceUuid == azrtti_cast<const SourceThumbnailKey*>(other)->GetSourceUuid();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -59,48 +68,54 @@ namespace AzToolsFramework
|
||||
auto sourceKey = azrtti_cast<const SourceThumbnailKey*>(m_key.data());
|
||||
AZ_Assert(sourceKey, "Incorrect key type, excpected SourceThumbnailKey");
|
||||
|
||||
// note that there might not actually be a UUID for this yet. So we don't look it up.
|
||||
AZ::EBusAggregateResults<SourceFileDetails> results;
|
||||
AssetBrowserInteractionNotificationBus::BroadcastResult(results, &AssetBrowserInteractionNotificationBus::Events::GetSourceFileDetails, sourceKey->GetFileName().c_str());
|
||||
// there could be multiple listeners to this, return the first one with actual image path
|
||||
auto it = AZStd::find_if(results.values.begin(), results.values.end(), [](const SourceFileDetails& details) { return !details.m_sourceThumbnailPath.empty(); });
|
||||
bool foundIt = false;
|
||||
AZStd::string watchFolder;
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AssetSystemRequestBus::BroadcastResult(foundIt, &AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, sourceKey->GetSourceUuid(), assetInfo, watchFolder);
|
||||
|
||||
QString iconPathToUse;
|
||||
|
||||
if (it != results.values.end())
|
||||
if (foundIt)
|
||||
{
|
||||
const char* resultPath = it->m_sourceThumbnailPath.c_str();
|
||||
// its an ordered bus, though, so first one wins.
|
||||
// we have to massage this though. there are three valid possibilities
|
||||
// 1. its a relative path to source, in which case we have to find the full path
|
||||
// 2. its an absolute path, in which case we use it as-is
|
||||
// 3. its an embedded resource, in which case we use it as is.
|
||||
// note that there might not actually be a UUID for this yet. So we don't look it up.
|
||||
AZ::EBusAggregateResults<SourceFileDetails> results;
|
||||
AssetBrowserInteractionNotificationBus::BroadcastResult(results, &AssetBrowserInteractionNotificationBus::Events::GetSourceFileDetails, assetInfo.m_relativePath.c_str());
|
||||
// there could be multiple listeners to this, return the first one with actual image path
|
||||
auto it = AZStd::find_if(results.values.begin(), results.values.end(), [](const SourceFileDetails& details) { return !details.m_sourceThumbnailPath.empty(); });
|
||||
|
||||
// is it an embedded resource or absolute path?
|
||||
if ((resultPath[0] == ':')||(!AzFramework::StringFunc::Path::IsRelative(resultPath)))
|
||||
if (it != results.values.end())
|
||||
{
|
||||
iconPathToUse = QString::fromUtf8(resultPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// getting here means its a relative path. Can we find the real path of the file? This also searches in gems for sources.
|
||||
bool foundIt = false;
|
||||
AZStd::string watchFolder;
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, resultPath, assetInfo, watchFolder);
|
||||
const char* resultPath = it->m_sourceThumbnailPath.c_str();
|
||||
// its an ordered bus, though, so first one wins.
|
||||
// we have to massage this though. there are three valid possibilities
|
||||
// 1. its a relative path to source, in which case we have to find the full path
|
||||
// 2. its an absolute path, in which case we use it as-is
|
||||
// 3. its an embedded resource, in which case we use it as is.
|
||||
|
||||
AZ_WarningOnce("Asset Browser", foundIt, "Unable to find source icon file in any source folders or gems: %s\n", resultPath);
|
||||
|
||||
if (foundIt)
|
||||
// is it an embedded resource or absolute path?
|
||||
if ((resultPath[0] == ':') || (!AzFramework::StringFunc::Path::IsRelative(resultPath)))
|
||||
{
|
||||
// the absolute path is join(watchfolder, relativepath); // since its relative to the watch folder.
|
||||
AZStd::string finalPath;
|
||||
AzFramework::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), finalPath);
|
||||
iconPathToUse = QString::fromUtf8(finalPath.c_str());
|
||||
iconPathToUse = QString::fromUtf8(resultPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// getting here means its a relative path. Can we find the real path of the file? This also searches in gems for sources.
|
||||
foundIt = false;
|
||||
AssetSystemRequestBus::BroadcastResult(foundIt, &AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, resultPath, assetInfo, watchFolder);
|
||||
|
||||
AZ_WarningOnce("Asset Browser", foundIt, "Unable to find source icon file in any source folders or gems: %s\n", resultPath);
|
||||
|
||||
if (foundIt)
|
||||
{
|
||||
// the absolute path is join(watchfolder, relativepath); // since its relative to the watch folder.
|
||||
AZStd::string finalPath;
|
||||
AzFramework::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), finalPath);
|
||||
iconPathToUse = QString::fromUtf8(finalPath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (iconPathToUse.isEmpty())
|
||||
{
|
||||
const char* engineRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
|
||||
@@ -116,7 +131,7 @@ namespace AzToolsFramework
|
||||
// SourceThumbnailCache
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SourceThumbnailCache::SourceThumbnailCache()
|
||||
: ThumbnailCache<SourceThumbnail, SourceKeyHash, SourceKeyEqual>() {}
|
||||
: ThumbnailCache<SourceThumbnail>() {}
|
||||
|
||||
SourceThumbnailCache::~SourceThumbnailCache() = default;
|
||||
|
||||
|
||||
+6
-41
@@ -12,7 +12,6 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
|
||||
#include <QMutex>
|
||||
#endif
|
||||
@@ -31,15 +30,13 @@ namespace AzToolsFramework
|
||||
public:
|
||||
AZ_RTTI(SourceThumbnailKey, "{4AF3F33A-4B16-491D-93A5-0CC96DC59814}", ThumbnailKey);
|
||||
|
||||
explicit SourceThumbnailKey(const char* fileName);
|
||||
const AZStd::string& GetFileName() const;
|
||||
const AZStd::string& GetExtension() const;
|
||||
explicit SourceThumbnailKey(const AZ::Uuid& sourceUuid);
|
||||
const AZ::Uuid& GetSourceUuid() const;
|
||||
size_t GetHash() const override;
|
||||
bool Equals(const ThumbnailKey* other) const override;
|
||||
|
||||
protected:
|
||||
//! absolute path
|
||||
AZStd::string m_fileName;
|
||||
//! file extension
|
||||
AZStd::string m_extension;
|
||||
AZ::Uuid m_sourceUuid;
|
||||
};
|
||||
|
||||
class SourceThumbnail
|
||||
@@ -56,41 +53,9 @@ namespace AzToolsFramework
|
||||
static QMutex m_mutex;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
class SourceKeyHash
|
||||
{
|
||||
public:
|
||||
size_t operator() (const SharedThumbnailKey& val) const
|
||||
{
|
||||
auto sourceThumbnailKey = azrtti_cast<const SourceThumbnailKey*>(val.data());
|
||||
if (!sourceThumbnailKey)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return AZStd::hash<AZStd::string>()(sourceThumbnailKey->GetFileName());
|
||||
}
|
||||
};
|
||||
|
||||
class SourceKeyEqual
|
||||
{
|
||||
public:
|
||||
bool operator()(const SharedThumbnailKey& val1, const SharedThumbnailKey& val2) const
|
||||
{
|
||||
auto sourceThumbnailKey1 = azrtti_cast<const SourceThumbnailKey*>(val1.data());
|
||||
auto sourceThumbnailKey2 = azrtti_cast<const SourceThumbnailKey*>(val2.data());
|
||||
if (!sourceThumbnailKey1 || !sourceThumbnailKey2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return sourceThumbnailKey1->GetFileName() == sourceThumbnailKey2->GetFileName();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//! SourceAssetBrowserEntry thumbnails
|
||||
class SourceThumbnailCache
|
||||
: public ThumbnailCache<SourceThumbnail, SourceKeyHash, SourceKeyEqual>
|
||||
: public ThumbnailCache<SourceThumbnail>
|
||||
{
|
||||
public:
|
||||
SourceThumbnailCache();
|
||||
|
||||
+5
-1
@@ -307,6 +307,10 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool usePrefabSystemForLevels = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
|
||||
|
||||
for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList)
|
||||
{
|
||||
AZ::u64 fileSize = 0;
|
||||
@@ -323,7 +327,7 @@ namespace AzToolsFramework
|
||||
AZ_Warning(logWindowName, false, "File (%s) size (%d) is bigger than the max bundle size (%d).\n", assetFileInfo.m_assetRelativePath.c_str(), fileSize, maxSizeInBytes);
|
||||
}
|
||||
|
||||
if (AzFramework::StringFunc::EndsWith(assetFileInfo.m_assetRelativePath, "level.pak"))
|
||||
if (!usePrefabSystemForLevels && (AzFramework::StringFunc::EndsWith(assetFileInfo.m_assetRelativePath, "level.pak")))
|
||||
{
|
||||
AZStd::string levelFolder;
|
||||
AzFramework::StringFunc::Path::GetFolderPath(assetFileInfo.m_assetRelativePath.c_str(), levelFolder);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <AzToolsFramework/Slice/SliceDependencyBrowserComponent.h>
|
||||
#include <AzToolsFramework/Slice/SliceMetadataEntityContextComponent.h>
|
||||
#include <AzToolsFramework/Slice/SliceRequestComponent.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
|
||||
#include <AzToolsFramework/SourceControl/PerforceComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/AzToolsFrameworkConfigurationSystemComponent.h>
|
||||
@@ -73,6 +74,7 @@ namespace AzToolsFramework
|
||||
SliceMetadataEntityContextComponent::CreateDescriptor(),
|
||||
SliceRequestComponent::CreateDescriptor(),
|
||||
Prefab::PrefabSystemComponent::CreateDescriptor(),
|
||||
Prefab::EditorPrefabComponent::CreateDescriptor(),
|
||||
Components::EditorEntityActionComponent::CreateDescriptor(),
|
||||
Components::EditorEntityIconComponent::CreateDescriptor(),
|
||||
Components::EditorInspectorComponent::CreateDescriptor(),
|
||||
|
||||
+8
-5
@@ -16,7 +16,7 @@
|
||||
#include "EntityTransformCommand.h"
|
||||
#include <HexEdFramework/FrameworkCore/SelectionMessages.h>
|
||||
#include <HexEd/WorldEditor/ToolsComponents/TransformComponentBus.h>
|
||||
#include "PreemptiveUndoCache.h"
|
||||
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -31,6 +31,9 @@ namespace AzToolsFramework
|
||||
m_priorTransforms[*it] = current;
|
||||
m_nextTransforms[*it] = current;
|
||||
}
|
||||
|
||||
m_undoCacheInterface = AZ::Interface<UndoSystem::UndoCacheInterface>::Get();
|
||||
AZ_Assert(m_undoCacheInterface, "Could not get UndoCacheInterface on TransformCommand construction.");
|
||||
}
|
||||
|
||||
void TransformCommand::Post()
|
||||
@@ -46,7 +49,7 @@ namespace AzToolsFramework
|
||||
|
||||
for (auto it = m_priorTransforms.begin(); it != m_priorTransforms.end(); ++it)
|
||||
{
|
||||
PreemptiveUndoCache::Get()->UpdateCache(it->first);
|
||||
m_undoCacheInterface->UpdateCache(it->first);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +58,7 @@ namespace AzToolsFramework
|
||||
for (auto it = m_priorTransforms.begin(); it != m_priorTransforms.end(); ++it)
|
||||
{
|
||||
EBUS_EVENT_ID(it->first, TransformComponentMessages::Bus, SetLocalSRT, it->second);
|
||||
PreemptiveUndoCache::Get()->UpdateCache(it->first);
|
||||
m_undoCacheInterface->UpdateCache(it->first);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +67,7 @@ namespace AzToolsFramework
|
||||
for (auto it = m_nextTransforms.begin(); it != m_nextTransforms.end(); ++it)
|
||||
{
|
||||
EBUS_EVENT_ID(it->first, TransformComponentMessages::Bus, SetLocalSRT, it->second);
|
||||
PreemptiveUndoCache::Get()->UpdateCache(it->first);
|
||||
m_undoCacheInterface->UpdateCache(it->first);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,4 +90,4 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace UndoSystem
|
||||
{
|
||||
class UndoCacheInterface;
|
||||
}
|
||||
|
||||
typedef AZStd::vector<AZ::EntityId> EntityList;
|
||||
|
||||
// transform command specializes undo to just care about the transform of an entity instead of the entire thing, for performance.
|
||||
@@ -54,9 +59,12 @@ namespace AzToolsFramework
|
||||
|
||||
CapturedTransforms m_priorTransforms;
|
||||
CapturedTransforms m_nextTransforms;
|
||||
|
||||
private:
|
||||
UndoCacheInterface* m_undoCacheInterface;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // disabled
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -53,6 +53,11 @@ namespace AzToolsFramework
|
||||
s_cache->m_instance = this;
|
||||
}
|
||||
|
||||
void PreemptiveUndoCache::RegisterToUndoCacheInterface()
|
||||
{
|
||||
AZ::Interface<UndoSystem::UndoCacheInterface>::Register(this);
|
||||
}
|
||||
|
||||
PreemptiveUndoCache::~PreemptiveUndoCache()
|
||||
{
|
||||
if (s_cache)
|
||||
@@ -60,6 +65,11 @@ namespace AzToolsFramework
|
||||
s_cache->m_instance = nullptr;
|
||||
s_cache.Reset();
|
||||
}
|
||||
|
||||
if (AZ::Interface<UndoSystem::UndoCacheInterface>::Get() == this)
|
||||
{
|
||||
AZ::Interface<UndoSystem::UndoCacheInterface>::Unregister(this);
|
||||
}
|
||||
}
|
||||
|
||||
PreemptiveUndoCache* PreemptiveUndoCache::Get()
|
||||
@@ -173,4 +183,4 @@ namespace AzToolsFramework
|
||||
return it->second;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
|
||||
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
|
||||
|
||||
// Enable to generate warnings for entity data changes not caught by undo batches.
|
||||
#if defined(AZ_DEBUG_BUILD) && !defined(ENABLE_UNDOCACHE_CONSISTENCY_CHECKS)
|
||||
# define ENABLE_UNDOCACHE_CONSISTENCY_CHECKS
|
||||
@@ -29,6 +31,7 @@ namespace AzToolsFramework
|
||||
// so that the user does not have to inform us before they make a change, only after they make a change
|
||||
// it also allows us to detect errors with change notification and not have to do multiple snapshots (before and after)
|
||||
class PreemptiveUndoCache
|
||||
: UndoSystem::UndoCacheInterface
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PreemptiveUndoCache, AZ::SystemAllocator, 0);
|
||||
@@ -37,23 +40,21 @@ namespace AzToolsFramework
|
||||
PreemptiveUndoCache();
|
||||
~PreemptiveUndoCache();
|
||||
|
||||
void RegisterToUndoCacheInterface();
|
||||
|
||||
// UndoCacheInterface...
|
||||
void UpdateCache(const AZ::EntityId& entityId) override;
|
||||
void PurgeCache(const AZ::EntityId& entityId) override;
|
||||
void Clear() override;
|
||||
void Validate(const AZ::EntityId& entityId) override;
|
||||
|
||||
// whenever an entity appears in the world, as an atomic operation (ie, after all of its loading is complete, and the entity is active)
|
||||
// we snapshot it here. then, whenever an entity changes, we have what it used to be.
|
||||
typedef AZStd::vector<AZ::u8> CacheLineType;
|
||||
|
||||
void Validate(const AZ::EntityId& entityId);
|
||||
|
||||
// Store the new entity state or replace the old state
|
||||
void UpdateCache(const AZ::EntityId& entityId);
|
||||
|
||||
// remove the cache line for the entity, if there is one
|
||||
void PurgeCache(const AZ::EntityId& entityId);
|
||||
|
||||
// retrieve the last known state for an entity
|
||||
const CacheLineType& Retrieve(const AZ::EntityId& entityId);
|
||||
|
||||
// clear the entire cache:
|
||||
void Clear();
|
||||
protected:
|
||||
typedef AZStd::unordered_map<AZ::EntityId, CacheLineType> EntityStateMap;
|
||||
|
||||
|
||||
+22
-12
@@ -55,6 +55,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
|
||||
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -163,10 +164,10 @@ namespace AzToolsFramework
|
||||
//=========================================================================
|
||||
void EditorEntityContextComponent::Activate()
|
||||
{
|
||||
m_isLegacySliceService = true;
|
||||
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(m_isLegacySliceService,
|
||||
&AzToolsFramework::ToolsApplicationRequests::IsLegacySliceSystemEnabled);
|
||||
bool prefabSystemEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled,
|
||||
&AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
m_isLegacySliceService = !prefabSystemEnabled;
|
||||
|
||||
if (m_isLegacySliceService)
|
||||
{
|
||||
@@ -190,6 +191,7 @@ namespace AzToolsFramework
|
||||
EditorLegacyGameModeNotificationBus::Handler::BusConnect();
|
||||
|
||||
m_entityVisibilityBoundsUnionSystem.Connect();
|
||||
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -449,7 +451,8 @@ namespace AzToolsFramework
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(!m_entityOwnershipService->m_shouldAssertForLegacySlicesUsage, "Not implemented");
|
||||
loadedSuccessfully = static_cast<PrefabEditorEntityOwnershipService*>(m_entityOwnershipService.get())->LoadFromStream(
|
||||
stream, AZStd::string_view(levelPakFile.toUtf8(), levelPakFile.size()) );
|
||||
}
|
||||
|
||||
LoadFromStreamComplete(loadedSuccessfully);
|
||||
@@ -495,7 +498,10 @@ namespace AzToolsFramework
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(!m_entityOwnershipService->m_shouldAssertForLegacySlicesUsage, "Not implemented");
|
||||
auto* service = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
|
||||
AZ_Assert(service, "Start play in editor could not start because there was no implementation for "
|
||||
"PrefabEditorEntityOwnershipInterface");
|
||||
service->StartPlayInEditor();
|
||||
}
|
||||
|
||||
m_isRunningGame = true;
|
||||
@@ -523,7 +529,10 @@ namespace AzToolsFramework
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(!m_entityOwnershipService->m_shouldAssertForLegacySlicesUsage, "Not implemented");
|
||||
auto* service = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
|
||||
AZ_Assert(service, "Stop play in editor could not complete because there was no implementation for "
|
||||
"PrefabEditorEntityOwnershipInterface");
|
||||
service->StopPlayInEditor();
|
||||
}
|
||||
|
||||
ToolsApplicationRequests::Bus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, m_selectedBeforeStartingGame);
|
||||
@@ -663,10 +672,6 @@ namespace AzToolsFramework
|
||||
// need to be associated with the root metadata info component.
|
||||
editorEntityOwnershipService->AssociateToRootMetadataEntity(entities);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(!m_entityOwnershipService->m_shouldAssertForLegacySlicesUsage, "Not implemented");
|
||||
}
|
||||
|
||||
SetupEditorEntities(entities);
|
||||
}
|
||||
@@ -742,6 +747,11 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
if (m_undoCacheInterface == nullptr)
|
||||
{
|
||||
m_undoCacheInterface = AZ::Interface<UndoSystem::UndoCacheInterface>::Get();
|
||||
}
|
||||
|
||||
// After activating all the entities, refresh their entries in the undo cache.
|
||||
// We need to wait until after all the activations are complete. Otherwise, it's possible that the data for an entity
|
||||
// will change based on other activations. For example, if we activate a child entity before its parent, the Transform
|
||||
@@ -749,7 +759,7 @@ namespace AzToolsFramework
|
||||
// out of sync with the child data.
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
PreemptiveUndoCache::Get()->UpdateCache(entity->GetId());
|
||||
m_undoCacheInterface->UpdateCache(entity->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@ namespace AzFramework
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace UndoSystem
|
||||
{
|
||||
class UndoCacheInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* System component responsible for owning the edit-time entity context.
|
||||
*
|
||||
@@ -186,6 +191,8 @@ namespace AzToolsFramework
|
||||
//! Edit time visibility management integrating entities with the IVisibilitySystem.
|
||||
AzFramework::EntityVisibilityBoundsUnionSystem m_entityVisibilityBoundsUnionSystem;
|
||||
bool m_isLegacySliceService;
|
||||
|
||||
UndoSystem::UndoCacheInterface* m_undoCacheInterface = nullptr;
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <AzCore/std/sort.h>
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
#include <AzToolsFramework/API/ComponentEntityObjectBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
@@ -116,6 +117,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
EditorEntityModel::EditorEntityModel()
|
||||
{
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(m_isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
|
||||
EntityCompositionNotificationBus::Handler::BusConnect();
|
||||
EditorOnlyEntityComponentNotificationBus::Handler::BusConnect();
|
||||
EditorEntityRuntimeActivationChangeNotificationBus::Handler::BusConnect();
|
||||
@@ -562,7 +565,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
//retrieve or add an entity entry to the table
|
||||
//the entry must exist, even if not connected, so children and other data can be assigned
|
||||
auto& entityInfo = m_entityInfoTable[entityId];
|
||||
[[maybe_unused]] auto [it, inserted] = m_entityInfoTable.try_emplace(entityId, m_isPrefabEnabled);
|
||||
auto& entityInfo = it->second;
|
||||
|
||||
//the entity id defaults to invalid and must be set to match the requested id
|
||||
//disconnect and reassign if there's a mismatch
|
||||
@@ -878,7 +882,8 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
EditorEntityModel::EditorEntityModelEntry::EditorEntityModelEntry()
|
||||
EditorEntityModel::EditorEntityModelEntry::EditorEntityModelEntry(bool isPrefabEnabled)
|
||||
: m_isPrefabEnabled(isPrefabEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1208,13 +1213,28 @@ namespace AzToolsFramework
|
||||
auto childItr = m_childIndexCache.find(childId);
|
||||
if (childItr != m_childIndexCache.end())
|
||||
{
|
||||
m_children.erase(m_children.begin() + childItr->second);
|
||||
|
||||
//rebuild index cache for faster lookup
|
||||
m_childIndexCache.clear();
|
||||
for (auto childIdToCache : m_children)
|
||||
if (m_isPrefabEnabled)
|
||||
{
|
||||
m_childIndexCache[childIdToCache] = static_cast<AZ::u64>(m_childIndexCache.size());
|
||||
// Take the last entry and move it into the removed spot instead of deleting the entry and having to move all
|
||||
// following entries one step down.
|
||||
AZ::EntityId backEntity = m_children.back();
|
||||
m_children[childItr->second] = backEntity;
|
||||
// Update cached index for the moved id to the new index.
|
||||
m_childIndexCache[backEntity] = childItr->second;
|
||||
// Now remove the deleted id from the children and cache.
|
||||
m_childIndexCache.erase(childId);
|
||||
m_children.erase(m_children.end() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_children.erase(m_children.begin() + childItr->second);
|
||||
|
||||
// rebuild index cache for faster lookup
|
||||
m_childIndexCache.clear();
|
||||
for (auto childIdToCache : m_children)
|
||||
{
|
||||
m_childIndexCache[childIdToCache] = static_cast<AZ::u64>(m_childIndexCache.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace AzToolsFramework
|
||||
, public PropertyEditorEntityChangeNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
EditorEntityModelEntry();
|
||||
explicit EditorEntityModelEntry(bool isPrefabEnabled);
|
||||
~EditorEntityModelEntry();
|
||||
|
||||
// Separately connect to EditorEntityInfoRequestBus and refresh Entity
|
||||
@@ -336,6 +336,7 @@ namespace AzToolsFramework
|
||||
bool m_visible = true;
|
||||
bool m_locked = false;
|
||||
bool m_connected = false;
|
||||
bool m_isPrefabEnabled = false;
|
||||
AZStd::string m_name;
|
||||
AZStd::string m_sliceAssetName;
|
||||
AZStd::unordered_map<AZ::EntityId, AZ::u64> m_childIndexCache;
|
||||
@@ -374,5 +375,6 @@ namespace AzToolsFramework
|
||||
AZ::EntityId m_postInstantiateBeforeEntity;
|
||||
AZ::EntityId m_postInstantiateSliceParent;
|
||||
bool m_gotInstantiateSliceDetails = false;
|
||||
bool m_isPrefabEnabled = false;
|
||||
};
|
||||
}
|
||||
|
||||
+9
-1
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
@@ -20,6 +21,7 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
|
||||
class PrefabEditorEntityOwnershipInterface
|
||||
{
|
||||
public:
|
||||
@@ -33,7 +35,13 @@ namespace AzToolsFramework
|
||||
//! /return The optional reference to the prefab created.
|
||||
virtual Prefab::InstanceOptionalReference CreatePrefab(
|
||||
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Prefab::Instance>>&& nestedPrefabInstances,
|
||||
const AZStd::string& filePath, Prefab::Instance& instanceToParentUnder) = 0;
|
||||
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder = AZStd::nullopt) = 0;
|
||||
virtual Prefab::InstanceOptionalReference GetRootPrefabInstance() = 0;
|
||||
|
||||
virtual bool LoadFromStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0;
|
||||
virtual bool SaveToStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0;
|
||||
|
||||
virtual void StartPlayInEditor() = 0;
|
||||
virtual void StopPlayInEditor() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
+252
-21
@@ -11,14 +11,17 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Script/ScriptSystemBus.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabLoader.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabLoader.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
PrefabEditorEntityOwnershipService::PrefabEditorEntityOwnershipService(const AzFramework::EntityContextId& entityContextId,
|
||||
@@ -26,8 +29,8 @@ namespace AzToolsFramework
|
||||
: m_entityContextId(entityContextId)
|
||||
, m_serializeContext(serializeContext)
|
||||
{
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
m_shouldAssertForLegacySlicesUsage, &AzToolsFramework::ToolsApplicationRequests::ShouldAssertForLegacySlicesUsage);
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
m_shouldAssertForLegacySlicesUsage, &AzFramework::ApplicationRequests::ShouldAssertForLegacySlicesUsage);
|
||||
AZ::Interface<PrefabEditorEntityOwnershipInterface>::Register(this);
|
||||
}
|
||||
|
||||
@@ -46,7 +49,7 @@ namespace AzToolsFramework
|
||||
AZ_Assert(m_loaderInterface != nullptr,
|
||||
"Couldn't get prefab loader interface, it's a requirement for PrefabEntityOwnership system to work");
|
||||
|
||||
m_rootInstance = AZStd::unique_ptr<Prefab::Instance>(m_prefabSystemComponent->CreatePrefab({}, {}, "/"));
|
||||
m_rootInstance = AZStd::unique_ptr<Prefab::Instance>(m_prefabSystemComponent->CreatePrefab({}, {}, "NewLevel.prefab"));
|
||||
|
||||
m_sliceOwnershipService.BusConnect(m_entityContextId);
|
||||
m_sliceOwnershipService.m_shouldAssertForLegacySlicesUsage = m_shouldAssertForLegacySlicesUsage;
|
||||
@@ -61,14 +64,33 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabEditorEntityOwnershipService::Destroy()
|
||||
{
|
||||
StopPlayInEditor();
|
||||
m_editorSliceOwnershipService.BusDisconnect();
|
||||
m_sliceOwnershipService.BusDisconnect();
|
||||
m_rootInstance.reset();
|
||||
|
||||
if (m_rootInstance != nullptr)
|
||||
{
|
||||
// Need to save off the template id to remove the template after the instance is deleted.
|
||||
Prefab::TemplateId templateId = m_rootInstance->GetTemplateId();
|
||||
m_rootInstance.reset();
|
||||
if (templateId != Prefab::InvalidTemplateId)
|
||||
{
|
||||
// Remove the template here so that if we're in a Deactivate/Activate cycle, it can recreate the template/rootInstance
|
||||
// correctly
|
||||
m_prefabSystemComponent->RemoveTemplate(templateId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::Reset()
|
||||
{
|
||||
// TBD
|
||||
Prefab::TemplateId templateId = m_rootInstance->GetTemplateId();
|
||||
if (templateId != Prefab::InvalidTemplateId)
|
||||
{
|
||||
m_rootInstance->SetTemplateId(Prefab::InvalidTemplateId);
|
||||
m_prefabSystemComponent->RemoveTemplate(templateId);
|
||||
}
|
||||
m_rootInstance->Reset();
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::AddEntity(AZ::Entity* entity)
|
||||
@@ -97,15 +119,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_Assert(IsInitialized(), "Tried to destroy an entity without initializing the Entity Ownership Service");
|
||||
AZ_Assert(m_entitiesRemovedCallback, "Callback function for DestroyEntityById has not been set.");
|
||||
AZStd::unique_ptr<AZ::Entity> detachedEntity = m_rootInstance->DetachEntity(entityId);
|
||||
if (detachedEntity)
|
||||
{
|
||||
AzFramework::SliceEntityRequestBus::MultiHandler::BusDisconnect(detachedEntity->GetId());
|
||||
m_entitiesRemovedCallback({ entityId });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// The detached entity now gets deleted because the unique_ptr gets out of scope
|
||||
OnEntityRemoved(entityId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::GetNonPrefabEntities(EntityList& entities)
|
||||
@@ -133,25 +148,121 @@ namespace AzToolsFramework
|
||||
m_entitiesAddedCallback(entities);
|
||||
}
|
||||
|
||||
bool PrefabEditorEntityOwnershipService::LoadFromStream(AZ::IO::GenericStream& /*stream*/, bool /*remapIds*/,
|
||||
EntityIdToEntityIdMap* /*idRemapTable*/, const AZ::ObjectStream::FilterDescriptor& /*filterDesc*/)
|
||||
bool PrefabEditorEntityOwnershipService::LoadFromStream(AZ::IO::GenericStream& stream, AZStd::string_view filename)
|
||||
{
|
||||
Reset();
|
||||
// Make loading from stream to behave the same in terms of filesize as regular loading of prefabs
|
||||
// This may need to be revisited in the future for supporting higher sizes along with prefab loading
|
||||
if (stream.GetLength() > Prefab::MaxPrefabFileSize)
|
||||
{
|
||||
AZ_Error("Prefab", false, "'%.*s' prefab content is bigger than the max supported size (%f MB)", AZ_STRING_ARG(filename), Prefab::MaxPrefabFileSize / (1024.f * 1024.f));
|
||||
return false;
|
||||
}
|
||||
const size_t bufSize = stream.GetLength();
|
||||
AZStd::unique_ptr<char[]> buf(new char[bufSize]);
|
||||
AZ::IO::SizeType bytes = stream.Read(bufSize, buf.get());
|
||||
|
||||
Prefab::TemplateId templateId = m_loaderInterface->LoadTemplateFromString(AZStd::string_view(buf.get(), bytes), filename);
|
||||
if (templateId == Prefab::InvalidTemplateId)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Couldn't load prefab content from '%.*s'", AZ_STRING_ARG(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_rootInstance->SetTemplateId(templateId);
|
||||
m_rootInstance->SetTemplateSourcePath(m_loaderInterface->GetRelativePathToProject(filename));
|
||||
m_prefabSystemComponent->PropagateTemplateChanges(templateId);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrefabEditorEntityOwnershipService::LoadFromStream(
|
||||
[[maybe_unused]] AZ::IO::GenericStream& stream,
|
||||
[[maybe_unused]] bool remapIds,
|
||||
[[maybe_unused]] EntityIdToEntityIdMap* idRemapTable,
|
||||
[[maybe_unused]] const AZ::ObjectStream::FilterDescriptor& filterDesc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PrefabEditorEntityOwnershipService::SaveToStream(AZ::IO::GenericStream& stream, AZStd::string_view filename)
|
||||
{
|
||||
AZ::IO::Path relativePath = m_loaderInterface->GetRelativePathToProject(filename);
|
||||
AzToolsFramework::Prefab::TemplateId templateId = m_prefabSystemComponent->GetTemplateIdFromFilePath(relativePath);
|
||||
|
||||
m_rootInstance->SetTemplateSourcePath(relativePath);
|
||||
if (templateId == AzToolsFramework::Prefab::InvalidTemplateId)
|
||||
{
|
||||
// This has not been loaded yet, this is the case of being saved with a different name.
|
||||
// Create it
|
||||
m_rootInstance->m_containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent());
|
||||
HandleEntitiesAdded({m_rootInstance->m_containerEntity.get()});
|
||||
|
||||
AzToolsFramework::Prefab::PrefabDom dom;
|
||||
bool success = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, dom);
|
||||
if (!success)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to convert current root instance into a DOM when saving file '%.*s'", AZ_STRING_ARG(filename));
|
||||
return false;
|
||||
}
|
||||
templateId = m_prefabSystemComponent->AddTemplate(relativePath, std::move(dom));
|
||||
if (templateId == AzToolsFramework::Prefab::InvalidTemplateId)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Couldn't add new template id '%i' when saving file '%.*s'", templateId, AZ_STRING_ARG(filename));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The template is already loaded, this is the case of either saving as same name or different name(loaded from before).
|
||||
// Update the template with the changes
|
||||
AzToolsFramework::Prefab::PrefabDom dom;
|
||||
bool success = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, dom);
|
||||
if (!success)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to convert current root instance into a DOM when saving file '%.*s'", AZ_STRING_ARG(filename));
|
||||
return false;
|
||||
}
|
||||
m_prefabSystemComponent->UpdatePrefabTemplate(templateId, dom);
|
||||
}
|
||||
|
||||
Prefab::TemplateId prevTemplateId = m_rootInstance->GetTemplateId();
|
||||
m_rootInstance->SetTemplateId(templateId);
|
||||
|
||||
if (prevTemplateId != Prefab::InvalidTemplateId && templateId != prevTemplateId)
|
||||
{
|
||||
// Make sure we only have one level template loaded at a time
|
||||
m_prefabSystemComponent->RemoveTemplate(prevTemplateId);
|
||||
}
|
||||
|
||||
AZStd::string out;
|
||||
if (m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out))
|
||||
{
|
||||
const size_t bytesToWrite = out.size();
|
||||
const size_t bytesWritten = stream.Write(bytesToWrite, out.data());
|
||||
return bytesWritten == bytesToWrite;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Prefab::InstanceOptionalReference PrefabEditorEntityOwnershipService::CreatePrefab(
|
||||
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Prefab::Instance>>&& nestedPrefabInstances,
|
||||
const AZStd::string& filePath, Prefab::Instance& instanceToParentUnder)
|
||||
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder)
|
||||
{
|
||||
AZStd::unique_ptr<Prefab::Instance> createdPrefabInstance =
|
||||
m_prefabSystemComponent->CreatePrefab(entities, AZStd::move(nestedPrefabInstances), filePath);
|
||||
|
||||
if (!instanceToParentUnder)
|
||||
{
|
||||
instanceToParentUnder = *m_rootInstance;
|
||||
}
|
||||
|
||||
if (createdPrefabInstance)
|
||||
{
|
||||
Prefab::Instance& addedInstance = instanceToParentUnder.AddInstance(AZStd::move(createdPrefabInstance));
|
||||
Prefab::Instance& addedInstance = instanceToParentUnder->get().AddInstance(AZStd::move(createdPrefabInstance));
|
||||
HandleEntitiesAdded({addedInstance.m_containerEntity.get()});
|
||||
return addedInstance;
|
||||
}
|
||||
HandleEntitiesAdded(entities);
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
@@ -161,6 +272,12 @@ namespace AzToolsFramework
|
||||
return *m_rootInstance;
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::OnEntityRemoved(AZ::EntityId entityId)
|
||||
{
|
||||
AzFramework::SliceEntityRequestBus::MultiHandler::BusDisconnect(entityId);
|
||||
m_entitiesRemovedCallback({ entityId });
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::SetEntitiesAddedCallback(OnEntitiesAddedCallback onEntitiesAddedCallback)
|
||||
{
|
||||
m_entitiesAddedCallback = AZStd::move(onEntitiesAddedCallback);
|
||||
@@ -176,6 +293,120 @@ namespace AzToolsFramework
|
||||
m_validateEntitiesCallback = AZStd::move(validateEntitiesCallback);
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::StartPlayInEditor()
|
||||
{
|
||||
if (m_rootInstance && !m_playInEditorData.m_isEnabled)
|
||||
{
|
||||
// Construct the runtime entities and products
|
||||
Prefab::TemplateReference templateReference = m_prefabSystemComponent->FindTemplate(m_rootInstance->GetTemplateId());
|
||||
if (templateReference.has_value())
|
||||
{
|
||||
bool converterLoaded = m_playInEditorData.m_converter.IsLoaded();
|
||||
if (!converterLoaded)
|
||||
{
|
||||
converterLoaded = m_playInEditorData.m_converter.LoadStackProfile("PlayInEditor");
|
||||
}
|
||||
if (converterLoaded)
|
||||
{
|
||||
// Use a random uuid as this is only a temporary source.
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext context(AZ::Uuid::CreateRandom());
|
||||
Prefab::PrefabDom copy;
|
||||
copy.CopyFrom(templateReference->get().GetPrefabDom(), copy.GetAllocator(), false);
|
||||
context.AddPrefab(DefaultMainSpawnableName, AZStd::move(copy));
|
||||
m_playInEditorData.m_converter.ProcessPrefab(context);
|
||||
if (context.HasCompletedSuccessfully())
|
||||
{
|
||||
static constexpr size_t NoRootSpawnable = AZStd::numeric_limits<size_t>::max();
|
||||
size_t rootSpawnableIndex = NoRootSpawnable;
|
||||
|
||||
// Create temporary assets from the processed data.
|
||||
for (auto& product : context.GetProcessedObjects())
|
||||
{
|
||||
if (product.GetAssetType() == AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid() &&
|
||||
product.GetId() ==
|
||||
AZStd::string::format("%s.%s", DefaultMainSpawnableName, AzFramework::Spawnable::FileExtension))
|
||||
{
|
||||
rootSpawnableIndex = m_playInEditorData.m_assets.size();
|
||||
}
|
||||
|
||||
AZ::Data::AssetInfo info;
|
||||
info.m_assetId = product.GetAsset().GetId();
|
||||
info.m_assetType = product.GetAssetType();
|
||||
info.m_relativePath = product.GetId();
|
||||
|
||||
AZ::Data::AssetCatalogRequestBus::Broadcast(
|
||||
&AZ::Data::AssetCatalogRequestBus::Events::RegisterAsset, product.GetAsset().GetId(), info);
|
||||
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();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to convert the prefab into assets.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to create a prefab processing stack from key 'PlayInEditor'.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_rootInstance->GetNestedEntities([this](AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
AZ_Assert(entity, "Invalid entity found in root instance while starting play in editor.");
|
||||
if (entity->GetState() == AZ::Entity::State::Active)
|
||||
{
|
||||
entity->Deactivate();
|
||||
m_playInEditorData.m_deactivatedEntities.push_back(entity.get());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
m_playInEditorData.m_isEnabled = true;
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::StopPlayInEditor()
|
||||
{
|
||||
if (m_rootInstance && m_playInEditorData.m_isEnabled)
|
||||
{
|
||||
auto end = m_playInEditorData.m_deactivatedEntities.rend();
|
||||
for (auto it = m_playInEditorData.m_deactivatedEntities.rbegin(); it != end; ++it)
|
||||
{
|
||||
AZ_Assert(*it, "Invalid entity added to list for re-activation after play-in-editor stopped.");
|
||||
(*it)->Activate();
|
||||
}
|
||||
m_playInEditorData.m_deactivatedEntities.clear();
|
||||
|
||||
m_playInEditorData.m_entities.DespawnAllEntities();
|
||||
m_playInEditorData.m_entities.Alert(
|
||||
[assets = AZStd::move(m_playInEditorData.m_assets)]([[maybe_unused]]uint32_t generation) mutable
|
||||
{
|
||||
for (auto& asset : assets)
|
||||
{
|
||||
if (asset)
|
||||
{
|
||||
// Explicitly release because this needs to happen before the asset is unregistered.
|
||||
asset.Release();
|
||||
AZ::Data::AssetCatalogRequestBus::Broadcast(
|
||||
&AZ::Data::AssetCatalogRequestBus::Events::UnregisterAsset, asset.GetId());
|
||||
}
|
||||
}
|
||||
AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::GarbageCollect);
|
||||
});
|
||||
m_playInEditorData.m_entities.Clear();
|
||||
}
|
||||
|
||||
m_playInEditorData.m_isEnabled = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Slice Buses implementation with Assert(false), this will exist only during Slice->Prefab
|
||||
// development to pinpoint and replace specific calls to Slice system
|
||||
|
||||
+24
-4
@@ -15,8 +15,10 @@
|
||||
#include <AzFramework/Entity/PrefabEntityOwnershipService.h>
|
||||
#include <AzFramework/Entity/SliceEntityOwnershipServiceBus.h>
|
||||
#include <AzFramework/Slice/SliceEntityBus.h>
|
||||
#include <AzFramework/Spawnable/SpawnableEntitiesContainer.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -104,7 +106,9 @@ namespace AzToolsFramework
|
||||
using OnEntitiesRemovedCallback = AzFramework::OnEntitiesRemovedCallback;
|
||||
using ValidateEntitiesCallback = AzFramework::ValidateEntitiesCallback;
|
||||
|
||||
explicit PrefabEditorEntityOwnershipService(
|
||||
static inline constexpr const char* DefaultMainSpawnableName = "Root";
|
||||
|
||||
PrefabEditorEntityOwnershipService(
|
||||
const AzFramework::EntityContextId& entityContextId, AZ::SerializeContext* serializeContext);
|
||||
|
||||
~PrefabEditorEntityOwnershipService();
|
||||
@@ -154,27 +158,44 @@ namespace AzToolsFramework
|
||||
// To be removed in the future
|
||||
bool LoadFromStream(AZ::IO::GenericStream& stream, bool remapIds,
|
||||
EntityIdToEntityIdMap* idRemapTable = nullptr,
|
||||
const AZ::ObjectStream::FilterDescriptor& filterDesc = AZ::ObjectStream::FilterDescriptor());
|
||||
const AZ::ObjectStream::FilterDescriptor& filterDesc = AZ::ObjectStream::FilterDescriptor()) override;
|
||||
|
||||
void SetEntitiesAddedCallback(OnEntitiesAddedCallback onEntitiesAddedCallback) override;
|
||||
void SetEntitiesRemovedCallback(OnEntitiesRemovedCallback onEntitiesRemovedCallback) override;
|
||||
void SetValidateEntitiesCallback(ValidateEntitiesCallback validateEntitiesCallback) override;
|
||||
|
||||
bool LoadFromStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) override;
|
||||
bool SaveToStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) override;
|
||||
|
||||
void StartPlayInEditor() override;
|
||||
void StopPlayInEditor() override;
|
||||
|
||||
protected:
|
||||
|
||||
AZ::SliceComponent::SliceInstanceAddress GetOwningSlice() override;
|
||||
|
||||
private:
|
||||
struct PlayInEditorData
|
||||
{
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabConversionPipeline m_converter;
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> m_assets;
|
||||
AZStd::vector<AZ::Entity*> m_deactivatedEntities;
|
||||
AzFramework::SpawnableEntitiesContainer m_entities;
|
||||
bool m_isEnabled{ false };
|
||||
};
|
||||
PlayInEditorData m_playInEditorData;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PrefabSystemComponentInterface interface implementation
|
||||
Prefab::InstanceOptionalReference CreatePrefab(
|
||||
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Prefab::Instance>>&& nestedPrefabInstances,
|
||||
const AZStd::string& filePath, Prefab::Instance& instanceToParentUnder) override;
|
||||
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override;
|
||||
|
||||
Prefab::InstanceOptionalReference GetRootPrefabInstance() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void OnEntityRemoved(AZ::EntityId entityId);
|
||||
|
||||
OnEntitiesAddedCallback m_entitiesAddedCallback;
|
||||
OnEntitiesRemovedCallback m_entitiesRemovedCallback;
|
||||
ValidateEntitiesCallback m_validateEntitiesCallback;
|
||||
@@ -185,7 +206,6 @@ namespace AzToolsFramework
|
||||
AZStd::string m_rootPath;
|
||||
AZStd::unique_ptr<Prefab::Instance> m_rootInstance;
|
||||
Prefab::PrefabSystemComponentInterface* m_prefabSystemComponent;
|
||||
|
||||
Prefab::PrefabLoaderInterface* m_loaderInterface;
|
||||
AzFramework::EntityContextId m_entityContextId;
|
||||
AZ::SerializeContext m_serializeContext;
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/AssetBrowserProductThumbnail.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
|
||||
#include <AzToolsFramework/MaterialBrowser/MaterialBrowserBus.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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/EditorPrefabComponent.h>
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerWidgetInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
void EditorPrefabComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<EditorPrefabComponent, EditorComponentBase>();
|
||||
|
||||
AZ::EditContext* editContext = serializeContext->GetEditContext();
|
||||
if (editContext)
|
||||
{
|
||||
editContext->Class<EditorPrefabComponent>("Prefab Component", "")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)
|
||||
->Attribute(AZ::Edit::Attributes::HideIcon, true)
|
||||
->Attribute(
|
||||
AZ::Edit::Attributes::SliceFlags,
|
||||
AZ::Edit::SliceFlags::HideOnAdd | AZ::Edit::SliceFlags::PushWhenHidden |
|
||||
AZ::Edit::SliceFlags::DontGatherReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPrefabComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
services.push_back(AZ_CRC("EditorPrefabInstanceContainerService"));
|
||||
}
|
||||
|
||||
void EditorPrefabComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
services.push_back(AZ_CRC("EditorPrefabInstanceContainerService"));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
void EditorPrefabComponent::Deactivate()
|
||||
{
|
||||
PrefabInstanceContainerNotificationBus::Broadcast(
|
||||
&PrefabInstanceContainerNotifications::OnPrefabComponentDeactivate, GetEntityId());
|
||||
}
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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/ToolsComponents/EditorComponentBase.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class EditorPrefabComponent : public AzToolsFramework::Components::EditorComponentBase
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(EditorPrefabComponent, "{756E5F9C-3E08-4F8D-855C-A5AEEFB6FCDD}", EditorComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
|
||||
|
||||
private:
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
};
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
@@ -26,6 +26,11 @@ namespace AzToolsFramework
|
||||
namespace Prefab
|
||||
{
|
||||
Instance::Instance()
|
||||
: Instance(AZStd::make_unique<AZ::Entity>())
|
||||
{
|
||||
}
|
||||
|
||||
Instance::Instance(AZStd::unique_ptr<AZ::Entity> containerEntity)
|
||||
{
|
||||
m_instanceEntityMapper = AZ::Interface<InstanceEntityMapperInterface>::Get();
|
||||
|
||||
@@ -41,27 +46,16 @@ namespace AzToolsFramework
|
||||
"It is a requirement for the Prefab Instance class. "
|
||||
"Check that it is being correctly initialized.");
|
||||
|
||||
m_containerEntity = AZStd::make_unique<AZ::Entity>();
|
||||
m_alias = GenerateInstanceAlias();
|
||||
m_containerEntity = containerEntity ? AZStd::move(containerEntity)
|
||||
: AZStd::make_unique<AZ::Entity>();
|
||||
EntityAlias containerEntityAlias = GenerateEntityAlias();
|
||||
RegisterEntity(m_containerEntity->GetId(), containerEntityAlias);
|
||||
}
|
||||
|
||||
Instance::~Instance()
|
||||
{
|
||||
// Clean up Instance associations.
|
||||
if (m_templateId != InvalidTemplateId &&
|
||||
!m_templateInstanceMapper->UnregisterInstance(*this))
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"Prefab - Attempted to unregister Instance from Template on file path '%s' with Id '%u'. "
|
||||
"Instance may never have been registered or was unregistered early.",
|
||||
m_templateSourcePath.c_str(),
|
||||
m_templateId);
|
||||
}
|
||||
|
||||
ClearEntities();
|
||||
|
||||
m_nestedInstances.clear();
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Instance::Reflect(AZ::ReflectContext* context)
|
||||
@@ -121,31 +115,31 @@ namespace AzToolsFramework
|
||||
return m_linkId;
|
||||
}
|
||||
|
||||
const AZStd::string& Instance::GetTemplateSourcePath() const
|
||||
const AZ::IO::Path& Instance::GetTemplateSourcePath() const
|
||||
{
|
||||
return m_templateSourcePath;
|
||||
}
|
||||
|
||||
void Instance::SetTemplateSourcePath(AZStd::string sourcePath)
|
||||
void Instance::SetTemplateSourcePath(AZ::IO::PathView sourcePath)
|
||||
{
|
||||
m_templateSourcePath = AZStd::move(sourcePath);
|
||||
|
||||
AZStd::string filename;
|
||||
if (AZ::StringFunc::Path::GetFileName(m_templateSourcePath.c_str(), filename))
|
||||
{
|
||||
m_containerEntity->SetName(filename);
|
||||
}
|
||||
m_templateSourcePath = sourcePath;
|
||||
m_containerEntity->SetName(sourcePath.Filename().Native());
|
||||
}
|
||||
|
||||
bool Instance::AddEntity(AZ::Entity& entity)
|
||||
{
|
||||
EntityAlias newEntityAlias = GenerateEntityAlias();
|
||||
if (!RegisterEntity(entity.GetId(), newEntityAlias))
|
||||
return AddEntity(entity, newEntityAlias);
|
||||
}
|
||||
|
||||
bool Instance::AddEntity(AZ::Entity& entity, EntityAlias entityAlias)
|
||||
{
|
||||
if (!RegisterEntity(entity.GetId(), entityAlias))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_entities.emplace(AZStd::make_pair(newEntityAlias, &entity)).second)
|
||||
if (!m_entities.emplace(AZStd::make_pair(entityAlias, &entity)).second)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -167,6 +161,7 @@ namespace AzToolsFramework
|
||||
"This happens when the entity is not correctly removed from all the prefab system entity maps.",
|
||||
entityId.ToString().c_str(), m_templateSourcePath.c_str());
|
||||
}
|
||||
|
||||
return DetachEntity(entityAliasToRemove);
|
||||
}
|
||||
|
||||
@@ -217,6 +212,31 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::Reset()
|
||||
{
|
||||
// Clean up Instance associations.
|
||||
if (m_templateId != InvalidTemplateId && !m_templateInstanceMapper->UnregisterInstance(*this))
|
||||
{
|
||||
AZ_Assert(
|
||||
false,
|
||||
"Prefab - Attempted to unregister Instance from Template on file path '%s' with Id '%u'. "
|
||||
"Instance may never have been registered or was unregistered early.",
|
||||
m_templateSourcePath.c_str(), m_templateId);
|
||||
}
|
||||
|
||||
ClearEntities();
|
||||
|
||||
m_nestedInstances.clear();
|
||||
|
||||
if (m_containerEntity)
|
||||
{
|
||||
m_instanceEntityMapper->UnregisterEntity(m_containerEntity->GetId());
|
||||
m_containerEntity.reset(aznew AZ::Entity());
|
||||
RegisterEntity(m_containerEntity->GetId(), GenerateEntityAlias());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Instance::RemoveEntities(
|
||||
const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter)
|
||||
{
|
||||
@@ -400,7 +420,9 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
entities.reserve(entities.size() + currentInstance->m_entities.size());
|
||||
// Size increases by 1 for each instance because we have to count the container entity also.
|
||||
entities.reserve(entities.size() + currentInstance->m_entities.size() + 1);
|
||||
entities.push_back(m_containerEntity.get());
|
||||
for (const auto& entityByAlias : currentInstance->m_entities)
|
||||
{
|
||||
entities.push_back(entityByAlias.second.get());
|
||||
@@ -441,55 +463,53 @@ namespace AzToolsFramework
|
||||
return nestedInstanceAliases;
|
||||
}
|
||||
|
||||
AliasPath Instance::GetAbsoluteInstanceAliasPath() const
|
||||
{
|
||||
// Reset the path using our preferred separator
|
||||
AliasPath aliasPathResult = AliasPath(s_aliasPathSeparator);
|
||||
const Instance* currentInstance = this;
|
||||
|
||||
// If no parent instance we are a root instance and our absolute path is empty
|
||||
AZStd::vector<const Instance*> pathOfInstances;
|
||||
|
||||
while (currentInstance->m_parent)
|
||||
{
|
||||
pathOfInstances.emplace_back(currentInstance);
|
||||
currentInstance = currentInstance->m_parent;
|
||||
}
|
||||
|
||||
pathOfInstances.emplace_back(currentInstance);
|
||||
|
||||
for (auto instanceIter = pathOfInstances.rbegin(); instanceIter != pathOfInstances.rend(); ++instanceIter)
|
||||
{
|
||||
aliasPathResult.Append((*instanceIter)->m_alias);
|
||||
}
|
||||
|
||||
return aliasPathResult;
|
||||
}
|
||||
|
||||
EntityAlias Instance::GenerateEntityAlias()
|
||||
{
|
||||
return "Entity_" + AZ::Entity::MakeId().ToString();
|
||||
return AZStd::string::format("Entity_%s", AZ::Entity::MakeId().ToString().c_str());
|
||||
}
|
||||
|
||||
InstanceAlias Instance::GenerateInstanceAlias()
|
||||
{
|
||||
return "Instance_" + AZ::Entity::MakeId().ToString();
|
||||
return AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str());
|
||||
}
|
||||
|
||||
void Instance::InitializeNestedEntities()
|
||||
void Instance::ActivateContainerEntity()
|
||||
{
|
||||
InitializeEntities();
|
||||
AZ_Assert(m_containerEntity, "Container entity of instance is null.");
|
||||
|
||||
for (const auto&[instanceAlias, instance] : m_nestedInstances)
|
||||
if (AZ::Entity::State::Constructed == m_containerEntity->GetState())
|
||||
{
|
||||
instance->InitializeNestedEntities();
|
||||
m_containerEntity->Init();
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::InitializeEntities()
|
||||
{
|
||||
for (const auto&[entityAlias, entity] : m_entities)
|
||||
if (AZ::Entity::State::Init == m_containerEntity->GetState())
|
||||
{
|
||||
if (AZ::Entity::State::Constructed == entity->GetState())
|
||||
{
|
||||
entity->Init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::ActivateNestedEntities()
|
||||
{
|
||||
ActivateEntities();
|
||||
|
||||
for (const auto&[instanceAlias, instance] : m_nestedInstances)
|
||||
{
|
||||
instance->ActivateNestedEntities();
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::ActivateEntities()
|
||||
{
|
||||
for (const auto&[entityAlias, entity] : m_entities)
|
||||
{
|
||||
if(AZ::Entity::State::Init == entity->GetState())
|
||||
{
|
||||
entity->Activate();
|
||||
}
|
||||
m_containerEntity->Activate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,5 +565,38 @@ namespace AzToolsFramework
|
||||
{
|
||||
return m_containerEntity->GetId();
|
||||
}
|
||||
|
||||
bool Instance::HasContainerEntity() const
|
||||
{
|
||||
return m_containerEntity.get() != nullptr;
|
||||
}
|
||||
|
||||
EntityOptionalReference Instance::GetContainerEntity()
|
||||
{
|
||||
if (m_containerEntity)
|
||||
{
|
||||
return *m_containerEntity;
|
||||
}
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
EntityOptionalConstReference Instance::GetContainerEntity() const
|
||||
{
|
||||
if (m_containerEntity)
|
||||
{
|
||||
return *m_containerEntity;
|
||||
}
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
void Instance::SetContainerEntity(AZ::Entity& entity)
|
||||
{
|
||||
m_containerEntity.reset(&entity);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> Instance::DetachContainerEntity()
|
||||
{
|
||||
return AZStd::move(m_containerEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Math/Uuid.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
@@ -38,8 +39,10 @@ namespace AzToolsFramework
|
||||
class InstanceEntityMapperInterface;
|
||||
class TemplateInstanceMapperInterface;
|
||||
|
||||
using InstanceAlias = AZStd::string;
|
||||
using AliasPath = AZ::IO::Path;
|
||||
using AliasPathView = AZ::IO::PathView;
|
||||
using EntityAlias = AZStd::string;
|
||||
using InstanceAlias = AZStd::string;
|
||||
|
||||
class Instance;
|
||||
using EntityAliasOptionalReference = AZStd::optional<AZStd::reference_wrapper<EntityAlias>>;
|
||||
@@ -47,6 +50,8 @@ namespace AzToolsFramework
|
||||
using InstanceOptionalConstReference = AZStd::optional<AZStd::reference_wrapper<const Instance>>;
|
||||
using InstanceSet = AZStd::unordered_set<Instance*>;
|
||||
using InstanceSetConstReference = AZStd::optional<AZStd::reference_wrapper<const InstanceSet>>;
|
||||
using EntityOptionalReference = AZStd::optional<AZStd::reference_wrapper<AZ::Entity>>;
|
||||
using EntityOptionalConstReference = AZStd::optional<AZStd::reference_wrapper<const AZ::Entity>>;
|
||||
|
||||
// A prefab instance is the container for when a Prefab Template is Instantiated.
|
||||
class Instance
|
||||
@@ -62,6 +67,7 @@ namespace AzToolsFramework
|
||||
using EntityList = AZStd::vector<AZ::Entity*>;
|
||||
|
||||
Instance();
|
||||
explicit Instance(AZStd::unique_ptr<AZ::Entity> containerEntity);
|
||||
virtual ~Instance();
|
||||
|
||||
Instance(const Instance& rhs) = delete;
|
||||
@@ -72,14 +78,17 @@ namespace AzToolsFramework
|
||||
const TemplateId& GetTemplateId() const;
|
||||
void SetTemplateId(const TemplateId& templateId);
|
||||
|
||||
const AZStd::string& GetTemplateSourcePath() const;
|
||||
void SetTemplateSourcePath(AZStd::string sourcePath);
|
||||
const AZ::IO::Path& GetTemplateSourcePath() const;
|
||||
void SetTemplateSourcePath(AZ::IO::PathView sourcePath);
|
||||
|
||||
bool AddEntity(AZ::Entity& entity);
|
||||
bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias);
|
||||
AZStd::unique_ptr<AZ::Entity> DetachEntity(const AZ::EntityId& entityId);
|
||||
void DetachNestedEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void RemoveNestedEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
|
||||
|
||||
void Reset();
|
||||
|
||||
Instance& AddInstance(AZStd::unique_ptr<Instance> instance);
|
||||
AZStd::unique_ptr<Instance> DetachNestedInstance(const InstanceAlias& instanceAlias);
|
||||
|
||||
@@ -128,17 +137,7 @@ namespace AzToolsFramework
|
||||
*/
|
||||
AZStd::vector<InstanceAlias> GetNestedInstanceAliases(TemplateId templateId) const;
|
||||
|
||||
/**
|
||||
* Initializes all entities, including those in nested entities
|
||||
*/
|
||||
void InitializeNestedEntities();
|
||||
void InitializeEntities();
|
||||
|
||||
/**
|
||||
* Activates all entities, including those in nested entities
|
||||
*/
|
||||
void ActivateNestedEntities();
|
||||
void ActivateEntities();
|
||||
void ActivateContainerEntity();
|
||||
|
||||
InstanceOptionalReference FindNestedInstance(const InstanceAlias& nestedInstanceAlias);
|
||||
|
||||
@@ -158,6 +157,18 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::EntityId GetContainerEntityId() const;
|
||||
|
||||
bool HasContainerEntity() const;
|
||||
|
||||
EntityOptionalReference GetContainerEntity();
|
||||
EntityOptionalConstReference GetContainerEntity() const;
|
||||
|
||||
void SetContainerEntity(AZ::Entity& entity);
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> DetachContainerEntity();
|
||||
|
||||
static EntityAlias GenerateEntityAlias();
|
||||
AliasPath GetAbsoluteInstanceAliasPath() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Gets the entities owned by this instance
|
||||
@@ -165,6 +176,7 @@ namespace AzToolsFramework
|
||||
void GetEntities(EntityList& entities, bool includeNestedEntities = false);
|
||||
|
||||
private:
|
||||
static constexpr const char s_aliasPathSeparator = '/';
|
||||
|
||||
void ClearEntities();
|
||||
|
||||
@@ -174,7 +186,6 @@ namespace AzToolsFramework
|
||||
bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias);
|
||||
AZStd::unique_ptr<AZ::Entity> DetachEntity(const EntityAlias& entityAlias);
|
||||
|
||||
static EntityAlias GenerateEntityAlias();
|
||||
static InstanceAlias GenerateInstanceAlias();
|
||||
|
||||
// Provide access to private data members in the serializer
|
||||
@@ -202,7 +213,7 @@ namespace AzToolsFramework
|
||||
InstanceAlias m_alias;
|
||||
|
||||
// The source path of the template this instance represents
|
||||
AZStd::string m_templateSourcePath;
|
||||
AZ::IO::Path m_templateSourcePath;
|
||||
|
||||
// The unique ID of the template this Instance belongs to.
|
||||
TemplateId m_templateId = InvalidTemplateId;
|
||||
|
||||
+46
-105
@@ -11,7 +11,10 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Math/Sfmt.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzCore/Utils/TypeHash.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapper.h>
|
||||
@@ -44,19 +47,15 @@ namespace AzToolsFramework
|
||||
|
||||
if (!inputAlias.empty())
|
||||
{
|
||||
if (m_isEntityReference)
|
||||
{
|
||||
m_unresolvedEntityAliases[m_loadingInstance].emplace_back(inputAlias, &outputValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
mappedValue = AZ::Entity::MakeId();
|
||||
AliasPath absoluteEntityPath = m_loadingInstance->GetAbsoluteInstanceAliasPath();
|
||||
absoluteEntityPath.Append(inputAlias);
|
||||
absoluteEntityPath = absoluteEntityPath.LexicallyNormal();
|
||||
|
||||
if (m_loadingInstance->RegisterEntity(mappedValue, inputAlias))
|
||||
{
|
||||
m_resolvedEntityAliases[m_loadingInstance].emplace_back(inputAlias, mappedValue);
|
||||
}
|
||||
else
|
||||
mappedValue = GenerateEntityIdForAliasPath(absoluteEntityPath, m_randomSeed);
|
||||
|
||||
if (!m_isEntityReference)
|
||||
{
|
||||
if (!m_loadingInstance->RegisterEntity(mappedValue, inputAlias))
|
||||
{
|
||||
mappedValue = AZ::EntityId(AZ::EntityId::InvalidEntityId);
|
||||
context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
|
||||
@@ -67,7 +66,7 @@ namespace AzToolsFramework
|
||||
|
||||
outputValue = mappedValue;
|
||||
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Succesfully mapped Entity Id For Prefab Instance load");
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully mapped Entity Id For Prefab Instance load");
|
||||
}
|
||||
|
||||
AZ::JsonSerializationResult::Result InstanceEntityIdMapper::MapIdToJson(rapidjson::Value& outputValue,
|
||||
@@ -77,9 +76,6 @@ namespace AzToolsFramework
|
||||
|
||||
if (!m_storingInstance)
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"Attempted to map an EntityId in Prefab Instance without setting the Storing Prefab Instance");
|
||||
|
||||
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed,
|
||||
"Attempted to map an EntityId in Prefab Instance without setting the Storing Prefab Instance");
|
||||
}
|
||||
@@ -104,8 +100,6 @@ namespace AzToolsFramework
|
||||
AZStd::string defaultErrorMessage =
|
||||
"Entity with Id " + inputValue.ToString() +
|
||||
" could not be found within its owning instance. Defaulting to invalid Id for Store.";
|
||||
|
||||
AZ_Assert(false, defaultErrorMessage.c_str());
|
||||
context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, defaultErrorMessage);
|
||||
}
|
||||
}
|
||||
@@ -113,13 +107,40 @@ namespace AzToolsFramework
|
||||
|
||||
outputValue.SetString(rapidjson::StringRef(mappedValue.c_str(), mappedValue.length()), context.GetJsonAllocator());
|
||||
|
||||
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Succesfully mapped Entity Id For Prefab Instance store");
|
||||
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Successfully mapped Entity Id For Prefab Instance store");
|
||||
}
|
||||
|
||||
void InstanceEntityIdMapper::SetEntityIdGenerationApproach(EntityIdGenerationApproach approach)
|
||||
{
|
||||
m_entityIdGenerationApproach = approach;
|
||||
|
||||
if (m_entityIdGenerationApproach == EntityIdGenerationApproach::Hashed)
|
||||
{
|
||||
m_randomSeed = SeedKey;
|
||||
}
|
||||
else if (m_entityIdGenerationApproach == EntityIdGenerationApproach::Random)
|
||||
{
|
||||
m_randomSeed = AZ::Sfmt::GetInstance().Rand64();
|
||||
|
||||
// Sanity check to avoid collision with hashed mode
|
||||
if (m_randomSeed == SeedKey)
|
||||
{
|
||||
m_randomSeed++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Unsupported option in EntityIdGenerationApproach encountered."
|
||||
" Defaulting to Hashed");
|
||||
|
||||
m_randomSeed = SeedKey;
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceEntityIdMapper::SetStoringInstance(const Instance& storingInstance)
|
||||
{
|
||||
m_storingInstance = &storingInstance;
|
||||
GetAbsoluteInstanceAliasPath(m_storingInstance, m_instanceAbsolutePath);
|
||||
m_instanceAbsolutePath = m_storingInstance->GetAbsoluteInstanceAliasPath();
|
||||
}
|
||||
|
||||
void InstanceEntityIdMapper::SetLoadingInstance(Instance& loadingInstance)
|
||||
@@ -127,78 +148,6 @@ namespace AzToolsFramework
|
||||
m_loadingInstance = &loadingInstance;
|
||||
}
|
||||
|
||||
void InstanceEntityIdMapper::FixUpUnresolvedEntityReferences()
|
||||
{
|
||||
// Nothing to resolve
|
||||
if (m_unresolvedEntityAliases.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the absolute path to each instance based on its position in the hierarchy
|
||||
// We'll use this to generate the absolute paths of resolved and unresolved aliases
|
||||
AZStd::unordered_map<Instance*, AliasPath> absoluteInstanceAliasPaths;
|
||||
absoluteInstanceAliasPaths.reserve(m_resolvedEntityAliases.bucket_count());
|
||||
|
||||
// Also calculate the absolute path to each resolved entity based on its position in the hierarchy
|
||||
AZStd::unordered_map<AliasPath, AZ::EntityId> resolvedAbsoluteEntityAliasPaths;
|
||||
|
||||
for (auto& [instance, resolvedAliasIdList] : m_resolvedEntityAliases)
|
||||
{
|
||||
AliasPath absoluteInstancePath;
|
||||
GetAbsoluteInstanceAliasPath(instance, absoluteInstancePath);
|
||||
|
||||
absoluteInstanceAliasPaths.emplace(instance, absoluteInstancePath);
|
||||
|
||||
for (auto& [entityAlias, entityId] : resolvedAliasIdList)
|
||||
{
|
||||
resolvedAbsoluteEntityAliasPaths.emplace(absoluteInstancePath / entityAlias, entityId);
|
||||
}
|
||||
}
|
||||
|
||||
// Using the absolute paths of the instances containing the unresolved aliases
|
||||
// Attempt to find a match within the resolved paths
|
||||
// A match will allow us to update the unresolved reference id to match the id it's referencing
|
||||
for (auto& [instance, unresolvedAliasIdList] : m_unresolvedEntityAliases)
|
||||
{
|
||||
auto findResolvedInstance = absoluteInstanceAliasPaths.find(instance);
|
||||
|
||||
if (findResolvedInstance != absoluteInstanceAliasPaths.end())
|
||||
{
|
||||
AliasPath& absoluteInstancePath = findResolvedInstance->second;
|
||||
|
||||
for (auto& [entityAlias, entityPointer] : unresolvedAliasIdList)
|
||||
{
|
||||
AliasPath absoluteEntityReferencePath = (absoluteInstancePath / entityAlias).LexicallyNormal();
|
||||
|
||||
auto foundResolvedPath =
|
||||
resolvedAbsoluteEntityAliasPaths.find(absoluteEntityReferencePath);
|
||||
|
||||
if (foundResolvedPath != resolvedAbsoluteEntityAliasPaths.end())
|
||||
{
|
||||
*entityPointer = foundResolvedPath->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("Prefabs", false,
|
||||
"Unable to resolve entity reference alias path [%s] while loading Prefab instance. "
|
||||
"The reference was likely made on a parent or sibling prefab without the use of an override. "
|
||||
"Defaulting the reference to an invalid EntityId.",
|
||||
absoluteEntityReferencePath.String().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Prefabs - "
|
||||
"Attempted to resolve entity alias path(s) but the instance owning the unresolved reference has no entities. "
|
||||
"An Entity Reference can only come from an entity/component property.");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
EntityAlias InstanceEntityIdMapper::ResolveReferenceId(const AZ::EntityId& entityId)
|
||||
{
|
||||
// Acquire the owning instance of our entity
|
||||
@@ -209,7 +158,7 @@ namespace AzToolsFramework
|
||||
AliasPath relativeEntityAliasPath;
|
||||
if (!owningInstanceReference)
|
||||
{
|
||||
AZ_Assert(false,
|
||||
AZ_Warning("Prefabs", false,
|
||||
"Prefab - EntityIdMapper: Entity with Id %s has no registered owning instance",
|
||||
entityId.ToString().c_str());
|
||||
|
||||
@@ -220,24 +169,16 @@ namespace AzToolsFramework
|
||||
|
||||
// Build out the absolute path of this alias
|
||||
// so we can compare it to the absolute path of our currently scoped instance
|
||||
GetAbsoluteInstanceAliasPath(owningInstance, relativeEntityAliasPath);
|
||||
relativeEntityAliasPath = owningInstance->GetAbsoluteInstanceAliasPath();
|
||||
relativeEntityAliasPath.Append(owningInstance->GetEntityAlias(entityId)->get());
|
||||
|
||||
return relativeEntityAliasPath.LexicallyRelative(m_instanceAbsolutePath).String();
|
||||
}
|
||||
|
||||
void InstanceEntityIdMapper::GetAbsoluteInstanceAliasPath(const Instance* instance, AliasPath& aliasPathResult)
|
||||
AZ::EntityId InstanceEntityIdMapper::GenerateEntityIdForAliasPath(const AliasPathView& aliasPath, uint64_t seedKey)
|
||||
{
|
||||
// Reset the path using our preferred seperator
|
||||
aliasPathResult = AliasPath(m_aliasPathSeperator);
|
||||
const Instance* currentInstance = instance;
|
||||
|
||||
// If no parent instance we are a root instance and our absolute path is empty
|
||||
while (currentInstance->m_parent)
|
||||
{
|
||||
aliasPathResult.Append(currentInstance->m_alias);
|
||||
currentInstance = currentInstance->m_parent;
|
||||
}
|
||||
const AZ::HashValue64 seed = aznumeric_cast<AZ::HashValue64>(seedKey);
|
||||
return AZ::EntityId(aznumeric_cast<AZ::u64>((AZ::TypeHash64(aliasPath.Native().data(), seed))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-17
@@ -25,41 +25,37 @@ namespace AzToolsFramework
|
||||
: public AZ::JsonEntityIdSerializer::JsonEntityIdMapper
|
||||
{
|
||||
public:
|
||||
enum class EntityIdGenerationApproach
|
||||
{
|
||||
Hashed, //! Creates an entity id that's stable between runs. (Default)
|
||||
Random //! Entities get a new randomly generate id.
|
||||
};
|
||||
|
||||
AZ_RTTI(InstanceEntityIdMapper, "{EA76C3A7-1210-4DFB-A1C0-2F8E8B0B888E}", AZ::JsonEntityIdSerializer::JsonEntityIdMapper);
|
||||
|
||||
static inline constexpr uint64_t SeedKey = 5915587277; // Random prime number
|
||||
|
||||
AZ::JsonSerializationResult::Result MapJsonToId(AZ::EntityId& outputValue, const rapidjson::Value& inputValue, AZ::JsonDeserializerContext& context) override;
|
||||
AZ::JsonSerializationResult::Result MapIdToJson(rapidjson::Value& outputValue, const AZ::EntityId& inputValue, AZ::JsonSerializerContext& context) override;
|
||||
|
||||
void SetEntityIdGenerationApproach(EntityIdGenerationApproach approach);
|
||||
|
||||
void SetStoringInstance(const Instance& storingInstance);
|
||||
void SetLoadingInstance(Instance& loadingInstance);
|
||||
|
||||
/**
|
||||
* Fixes up any unresolved entity references by assigning them to the id value of the entity it references.
|
||||
* This is done by computing the absolute alias paths of all EntityIds and EntityId references
|
||||
* then matching the references to the id values they reference.
|
||||
* During a load instance and alias information is stored to build out these paths,
|
||||
* but will be incomplete until the whole Load call is finished.
|
||||
* Calling this after Load and the mapper have finished will give complete information
|
||||
* on all entities and references discovered in the load
|
||||
*/
|
||||
void FixUpUnresolvedEntityReferences();
|
||||
static AZ::EntityId GenerateEntityIdForAliasPath(const AliasPathView& aliasPath, uint64_t seedKey = SeedKey);
|
||||
|
||||
private:
|
||||
using AliasPath = AZ::IO::Path;
|
||||
|
||||
EntityAlias ResolveReferenceId(const AZ::EntityId& entityId);
|
||||
|
||||
void GetAbsoluteInstanceAliasPath(const Instance* instance, AliasPath& aliasPathResult);
|
||||
|
||||
AliasPath m_instanceAbsolutePath;
|
||||
|
||||
const Instance* m_storingInstance = nullptr;
|
||||
Instance* m_loadingInstance = nullptr;
|
||||
|
||||
static constexpr const char m_aliasPathSeperator = '/';
|
||||
uint64_t m_randomSeed = SeedKey;
|
||||
|
||||
AZStd::unordered_map<Instance*, AZStd::vector<AZStd::pair<EntityAlias, AZ::EntityId>>> m_resolvedEntityAliases;
|
||||
AZStd::unordered_map<Instance*, AZStd::vector<AZStd::pair<EntityAlias, AZ::EntityId*>>> m_unresolvedEntityAliases;
|
||||
EntityIdGenerationApproach m_entityIdGenerationApproach { EntityIdGenerationApproach::Hashed };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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/Instance/InstanceEntityScrubber.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
InstanceEntityScrubber::InstanceEntityScrubber(Instance::EntityList& entities)
|
||||
: m_entities(entities)
|
||||
{}
|
||||
|
||||
void InstanceEntityScrubber::AddEntitiesToScrub(const EntityList& entities)
|
||||
{
|
||||
m_entities.insert(m_entities.end(), entities.begin(), entities.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class Entity;
|
||||
}
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
using EntityList = AZStd::vector<AZ::Entity*>;
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
//! Collects the entities added during deserialization
|
||||
class InstanceEntityScrubber
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(InstanceEntityScrubber, "{0BC12562-C240-48AD-89C6-EDF572C9B485}");
|
||||
|
||||
explicit InstanceEntityScrubber(Instance::EntityList& entities);
|
||||
|
||||
void AddEntitiesToScrub(const EntityList& entities);
|
||||
|
||||
private:
|
||||
Instance::EntityList& m_entities;
|
||||
};
|
||||
}
|
||||
}
|
||||
+67
-19
@@ -13,9 +13,11 @@
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceSerializer.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -46,8 +48,8 @@ namespace AzToolsFramework
|
||||
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||
{
|
||||
AZ::ScopedContextPath subPathSource(context, "m_templateSourcePath");
|
||||
const AZStd::string* sourcePath = &instance->GetTemplateSourcePath();
|
||||
const AZStd::string* defaultSourcePath = defaultInstance ? &defaultInstance->GetTemplateSourcePath() : nullptr;
|
||||
const AZStd::string* sourcePath = &(instance->GetTemplateSourcePath().Native());
|
||||
const AZStd::string* defaultSourcePath = defaultInstance ? &defaultInstance->GetTemplateSourcePath().Native() : nullptr;
|
||||
|
||||
result = ContinueStoringToJsonObjectField(outputValue, "Source", sourcePath, defaultSourcePath, azrtti_typeid<AZStd::string>(), context);
|
||||
}
|
||||
@@ -64,7 +66,6 @@ namespace AzToolsFramework
|
||||
AZ::ScopedContextPath subPathEntities(context, "m_entities");
|
||||
const Instance::AliasToEntityMap* entities = &instance->m_entities;
|
||||
const Instance::AliasToEntityMap* defaultEntities = defaultInstance ? &defaultInstance->m_entities : nullptr;
|
||||
|
||||
JSR::ResultCode resultEntities =
|
||||
ContinueStoringToJsonObjectField(outputValue, "Entities",
|
||||
entities, defaultEntities, azrtti_typeid<Instance::AliasToEntityMap>(), context);
|
||||
@@ -116,6 +117,15 @@ namespace AzToolsFramework
|
||||
AZ_Assert(prefabSystemComponentInterface,
|
||||
"PrefabSystemComponentInterface could not be found. It is required to load Prefab Instances");
|
||||
|
||||
PrefabLoaderInterface* loaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
|
||||
|
||||
AZ_Assert(
|
||||
loaderInterface,
|
||||
"PrefabLoaderInterface could not be found. It is required to load Prefab Instances");
|
||||
|
||||
// Make sure we have a relative path
|
||||
instance->m_templateSourcePath = loaderInterface->GetRelativePathToProject(instance->m_templateSourcePath);
|
||||
|
||||
TemplateId templateId = prefabSystemComponentInterface->GetTemplateIdFromFilePath(instance->GetTemplateSourcePath());
|
||||
|
||||
instance->SetTemplateId(templateId);
|
||||
@@ -125,25 +135,35 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
{
|
||||
// An already filled instance should be cleared if inputValue's Instances member is empty
|
||||
// The Json serializer will not do this by default as it will not attempt to load a missing member
|
||||
if (!instance->m_nestedInstances.empty() && !inputValue.HasMember("Instances"))
|
||||
{
|
||||
instance->m_nestedInstances.clear();
|
||||
}
|
||||
instance->m_nestedInstances.clear();
|
||||
|
||||
JSR::ResultCode instanceResult =
|
||||
ContinueLoadingFromJsonObjectField(&instance->m_nestedInstances, azrtti_typeid<Instance::AliasToInstanceMap>(), inputValue, "Instances", context);
|
||||
|
||||
if (instanceResult.GetProcessing() != JSR::Processing::Halted)
|
||||
// Load nested instances iteratively
|
||||
// We want to first create the nested instance object and assign its alias and parent pointer
|
||||
// These values are needed for the idmapper to properly resolve alias paths
|
||||
auto instancesMemberIter = inputValue.FindMember("Instances");
|
||||
if (instancesMemberIter != inputValue.MemberEnd() && instancesMemberIter->value.IsObject())
|
||||
{
|
||||
for (auto& nestedInstance : instance->m_nestedInstances)
|
||||
for (auto& instanceIter : instancesMemberIter->value.GetObject())
|
||||
{
|
||||
nestedInstance.second->m_parent = instance;
|
||||
nestedInstance.second->m_alias = nestedInstance.first;
|
||||
const rapidjson::Value& instanceAliasValue = instanceIter.name;
|
||||
|
||||
InstanceAlias instanceAlias(instanceAliasValue.GetString(),
|
||||
instanceAliasValue.GetStringLength());
|
||||
|
||||
AZStd::unique_ptr<Instance> nestedInstance = AZStd::make_unique<Instance>();
|
||||
|
||||
nestedInstance->m_alias = instanceAlias;
|
||||
nestedInstance->m_parent = instance;
|
||||
|
||||
result.Combine(
|
||||
ContinueLoading(&nestedInstance, azrtti_typeid<decltype(nestedInstance)>(),
|
||||
instanceIter.value, context));
|
||||
|
||||
instance->m_nestedInstances.emplace(
|
||||
instanceAlias,
|
||||
AZStd::move(nestedInstance));
|
||||
}
|
||||
}
|
||||
result.Combine(instanceResult);
|
||||
}
|
||||
|
||||
// An already filled instance should be cleared if inputValue's Entities member is empty
|
||||
@@ -153,6 +173,7 @@ namespace AzToolsFramework
|
||||
if (instance->m_containerEntity)
|
||||
{
|
||||
instance->m_instanceEntityMapper->UnregisterEntity(instance->m_containerEntity->GetId());
|
||||
instance->m_containerEntity.reset();
|
||||
}
|
||||
|
||||
if (idMapper && *idMapper)
|
||||
@@ -168,7 +189,10 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
{
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(&instance->m_entities, azrtti_typeid<Instance::AliasToEntityMap>(), inputValue, "Entities", context));
|
||||
JSR::ResultCode entitiesResult = ContinueLoadingFromJsonObjectField(
|
||||
&instance->m_entities, azrtti_typeid<Instance::AliasToEntityMap>(), inputValue, "Entities", context);
|
||||
AddEntitiesToScrub(instance, context);
|
||||
result.Combine(entitiesResult);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -179,5 +203,29 @@ namespace AzToolsFramework
|
||||
result.GetProcessing() == JSR::Processing::Completed ? "Succesfully loaded instance information for prefab." :
|
||||
"Failed to load instance information for prefab");
|
||||
}
|
||||
}
|
||||
|
||||
void JsonInstanceSerializer::AddEntitiesToScrub(const Instance* instance, AZ::JsonDeserializerContext& jsonDeserializerContext)
|
||||
{
|
||||
EntityList entitiesInInstance;
|
||||
entitiesInInstance.reserve(instance->m_entities.size() + 1);
|
||||
|
||||
if (instance->m_containerEntity->GetId().IsValid())
|
||||
{
|
||||
entitiesInInstance.emplace_back(instance->m_containerEntity.get());
|
||||
}
|
||||
|
||||
for (const auto& [entityAlias, entity] : instance->m_entities)
|
||||
{
|
||||
entitiesInInstance.emplace_back(entity.get());
|
||||
}
|
||||
|
||||
InstanceEntityScrubber** instanceEntityScrubber = jsonDeserializerContext.GetMetadata().Find<InstanceEntityScrubber*>();
|
||||
if (instanceEntityScrubber && (*instanceEntityScrubber))
|
||||
|
||||
{
|
||||
(*instanceEntityScrubber)->AddEntitiesToScrub(entitiesInInstance);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Prefab
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::JsonSerializationResult::Result Load(void* outputValue, const AZ::Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
|
||||
AZ::JsonDeserializerContext& context) override;
|
||||
|
||||
private:
|
||||
//! Adds the entities of an instance to a InstanceEntityScrubber object in the metadata of JsonDeserializerContext
|
||||
//! so that they can be scrubbed later.
|
||||
void AddEntitiesToScrub(const Instance* instance, AZ::JsonDeserializerContext& jsonDeserializercontext);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -14,7 +14,10 @@
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Link/Link.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -40,12 +43,14 @@ namespace AzToolsFramework
|
||||
const PrefabDom& modifiedState, const LinkId linkId) = 0;
|
||||
|
||||
//! Updates the affected template for a given entityId using the providedPatch
|
||||
virtual void PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId) = 0;
|
||||
virtual bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId) = 0;
|
||||
|
||||
virtual void PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId) = 0;
|
||||
virtual bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId) = 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 AzToolsFramework::Prefab::TemplateId& templateId) = 0;
|
||||
virtual void PatchTemplate(PrefabDomValue& providedPatch, const TemplateId& templateId) = 0;
|
||||
|
||||
virtual void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) = 0;
|
||||
|
||||
|
||||
+82
-13
@@ -81,7 +81,7 @@ namespace AzToolsFramework
|
||||
bool InstanceToTemplatePropagator::GeneratePatch(PrefabDom& generatedPatch, const PrefabDom& initialState,
|
||||
const PrefabDom& modifiedState)
|
||||
{
|
||||
//generate patch using jsonserialization CreatePatch
|
||||
//generate patch using json serialization CreatePatch
|
||||
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::CreatePatch(generatedPatch,
|
||||
generatedPatch.GetAllocator(), initialState, modifiedState, AZ::JsonMergeApproach::JsonPatch);
|
||||
|
||||
@@ -107,11 +107,17 @@ namespace AzToolsFramework
|
||||
return result.GetProcessing() != AZ::JsonSerializationResult::Processing::Halted;
|
||||
}
|
||||
|
||||
void InstanceToTemplatePropagator::PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId)
|
||||
bool InstanceToTemplatePropagator::PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId)
|
||||
{
|
||||
InstanceOptionalReference instanceOptionalReference = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
AZ_Error("Prefab", instanceOptionalReference,
|
||||
"Failed to find an owning instance for the entity with id %llu.", static_cast<AZ::u64>(entityId));
|
||||
|
||||
if (!instanceOptionalReference)
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false, "Failed to find an owning instance for the entity with id %llu.",
|
||||
static_cast<AZ::u64>(entityId));
|
||||
return false;
|
||||
}
|
||||
|
||||
//get template space associated with instance
|
||||
Instance& instance = instanceOptionalReference->get();
|
||||
@@ -119,33 +125,95 @@ namespace AzToolsFramework
|
||||
|
||||
//alias entity goes by in template -> get via owning instance map
|
||||
AZStd::optional<EntityAlias> entityAlias = instance.GetEntityAlias(entityId);
|
||||
AZ_Error("Prefab", entityAlias != AZStd::nullopt,
|
||||
"Failed to find an entity alias for the provided entity");
|
||||
|
||||
if (!entityAlias)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to find an entity alias for the provided entity");
|
||||
return false;
|
||||
}
|
||||
|
||||
PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(templateId);
|
||||
|
||||
PatchEntityInTemplate(providedPatch, entityAlias.value(), templateId);
|
||||
return PatchEntityInTemplate(providedPatch, entityAlias.value(), templateId);
|
||||
}
|
||||
|
||||
void InstanceToTemplatePropagator::PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& 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());
|
||||
AZ_Error("Prefab", entity != AZStd::nullopt, "Failed to aquire entity value reference")
|
||||
|
||||
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")
|
||||
AZ_Error("Prefab", result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success, "Patch was not successfully applied")
|
||||
|
||||
//update the Dom and trigger propogation
|
||||
//trigger propagation
|
||||
m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void InstanceToTemplatePropagator::AppendEntityAliasToPatchPaths(PrefabDom& providedPatch, const AZ::EntityId& entityId)
|
||||
{
|
||||
if (!providedPatch.IsArray())
|
||||
{
|
||||
AZ_Error("Prefab", false, "Patch is not an array of updates. Update failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
//create the prefix for the update - choosing between container and regular entities
|
||||
AZStd::string prefix = "/";
|
||||
|
||||
//grab the owning instance so we can use the entityIdMapper in settings
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
AZ_Assert(owningInstance != AZStd::nullopt, "Owning Instance is null");
|
||||
|
||||
bool isContainerEntity = entityId == owningInstance->get().GetContainerEntityId();
|
||||
|
||||
if (isContainerEntity)
|
||||
{
|
||||
prefix += PrefabDomUtils::ContainerEntityName;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityAliasOptionalReference entityAliasRef = owningInstance->get().GetEntityAlias(entityId);
|
||||
prefix += PrefabDomUtils::EntitiesName;
|
||||
prefix += "/";
|
||||
prefix += entityAliasRef->get();
|
||||
}
|
||||
|
||||
//update all entities or just the single container
|
||||
for (auto& patchValue : providedPatch.GetArray())
|
||||
{
|
||||
auto pathIter = patchValue.FindMember("path");
|
||||
|
||||
if (pathIter == patchValue.MemberEnd() || !(pathIter->value.IsString()))
|
||||
{
|
||||
AZ_Error("Prefab", false, "Was not able to find path member within patch dom. "
|
||||
"A non prefab dom patch may have been passed in.");
|
||||
continue;
|
||||
}
|
||||
|
||||
AZStd::string path = prefix + pathIter->value.GetString();
|
||||
|
||||
pathIter->value.SetString(path.c_str(), path.length(), providedPatch.GetAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, const TemplateId& templateId)
|
||||
@@ -159,9 +227,10 @@ namespace AzToolsFramework
|
||||
AZ_Error("Prefab", result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success,
|
||||
"Patch was not successfully applied");
|
||||
|
||||
//update the Dom and trigger propogation
|
||||
//trigger propagation
|
||||
if (result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success)
|
||||
{
|
||||
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, true);
|
||||
m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -31,8 +31,10 @@ 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;
|
||||
void PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId) override;
|
||||
void PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId) override;
|
||||
bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId) override;
|
||||
bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId) override;
|
||||
|
||||
void AppendEntityAliasToPatchPaths(PrefabDom& providedPatch, const AZ::EntityId& entityId) override;
|
||||
|
||||
InstanceOptionalReference GetTopMostInstanceInHierarchy(AZ::EntityId entityId);
|
||||
|
||||
@@ -40,8 +42,11 @@ namespace AzToolsFramework
|
||||
|
||||
void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) override;
|
||||
|
||||
private:
|
||||
void AddPatchesToLink(PrefabDom& patches, Link& link);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface;
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface;
|
||||
};
|
||||
|
||||
+72
-29
@@ -12,12 +12,16 @@
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.h>
|
||||
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.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/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerWidgetInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -72,42 +76,81 @@ namespace AzToolsFramework
|
||||
|
||||
bool InstanceUpdateExecutor::UpdateTemplateInstancesInQueue()
|
||||
{
|
||||
const int instanceCountToUpdateInBatch = m_instanceCountToUpdateInBatch == 0 ? m_instancesUpdateQueue.size() : m_instanceCountToUpdateInBatch;
|
||||
TemplateId currentTemplateId = InvalidTemplateId;
|
||||
TemplateReference currentTemplateReference = AZStd::nullopt;
|
||||
bool isUpdateSuccessful = true;
|
||||
|
||||
for (int i = 0; i < instanceCountToUpdateInBatch; ++i)
|
||||
if (!m_updatingTemplateInstancesInQueue)
|
||||
{
|
||||
Instance* instanceToUpdate = m_instancesUpdateQueue.front();
|
||||
TemplateId instanceTemplateId = instanceToUpdate->GetTemplateId();
|
||||
if (currentTemplateId != instanceTemplateId)
|
||||
m_updatingTemplateInstancesInQueue = true;
|
||||
|
||||
const int instanceCountToUpdateInBatch =
|
||||
m_instanceCountToUpdateInBatch == 0 ? m_instancesUpdateQueue.size() : m_instanceCountToUpdateInBatch;
|
||||
TemplateId currentTemplateId = InvalidTemplateId;
|
||||
TemplateReference currentTemplateReference = AZStd::nullopt;
|
||||
|
||||
if (instanceCountToUpdateInBatch > 0)
|
||||
{
|
||||
currentTemplateId = instanceTemplateId;
|
||||
currentTemplateReference = m_prefabSystemComponentInterface->FindTemplate(currentTemplateId);
|
||||
if (!currentTemplateReference.has_value())
|
||||
EntityIdList selectedEntityIds;
|
||||
ToolsApplicationRequestBus::BroadcastResult(selectedEntityIds, &ToolsApplicationRequests::GetSelectedEntities);
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, EntityIdList());
|
||||
|
||||
// Disable the Outliner to avoid showing the propagation steps
|
||||
EntityOutlinerWidgetInterface* entityOutlinerWidgetInterface = AZ::Interface<EntityOutlinerWidgetInterface>::Get();
|
||||
if (entityOutlinerWidgetInterface)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"InstanceUpdateExecutor::UpdateTemplateInstancesInQueue - "
|
||||
"Could not find Template using Id '%llu'. Unable to update Instance.",
|
||||
currentTemplateId);
|
||||
|
||||
isUpdateSuccessful = false;
|
||||
entityOutlinerWidgetInterface->SetUpdatesEnabled(false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < instanceCountToUpdateInBatch; ++i)
|
||||
{
|
||||
Instance* instanceToUpdate = m_instancesUpdateQueue.front();
|
||||
TemplateId instanceTemplateId = instanceToUpdate->GetTemplateId();
|
||||
if (currentTemplateId != instanceTemplateId)
|
||||
{
|
||||
currentTemplateId = instanceTemplateId;
|
||||
currentTemplateReference = m_prefabSystemComponentInterface->FindTemplate(currentTemplateId);
|
||||
if (!currentTemplateReference.has_value())
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"InstanceUpdateExecutor::UpdateTemplateInstancesInQueue - "
|
||||
"Could not find Template using Id '%llu'. Unable to update Instance.",
|
||||
currentTemplateId);
|
||||
|
||||
isUpdateSuccessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
Template& currentTemplate = currentTemplateReference->get();
|
||||
Instance::EntityList newEntities;
|
||||
if (!PrefabDomUtils::LoadInstanceFromPrefabDom(
|
||||
*instanceToUpdate, newEntities, currentTemplate.GetPrefabDom()))
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"InstanceUpdateExecutor::UpdateTemplateInstancesInQueue - "
|
||||
"Could not load Instance from Prefab DOM of Template with Id '%llu' on file path '%s'.",
|
||||
currentTemplateId, currentTemplate.GetFilePath().c_str());
|
||||
|
||||
isUpdateSuccessful = false;
|
||||
}
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, newEntities);
|
||||
|
||||
m_instancesUpdateQueue.pop();
|
||||
|
||||
}
|
||||
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds);
|
||||
|
||||
// Enable the Outliner
|
||||
AZ::SystemTickBus::QueueFunction([entityOutlinerWidgetInterface]() {
|
||||
if (entityOutlinerWidgetInterface)
|
||||
{
|
||||
entityOutlinerWidgetInterface->SetUpdatesEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Template& currentTemplate = currentTemplateReference->get();
|
||||
if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*instanceToUpdate, currentTemplate.GetPrefabDom(), true))
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"InstanceUpdateExecutor::UpdateTemplateInstancesInQueue - "
|
||||
"Could not load Instance from Prefab DOM of Template with Id '%llu' on file path '%s'.",
|
||||
currentTemplateId, currentTemplate.GetFilePath().c_str());
|
||||
|
||||
isUpdateSuccessful = false;
|
||||
}
|
||||
|
||||
m_instancesUpdateQueue.pop();
|
||||
m_updatingTemplateInstancesInQueue = false;
|
||||
}
|
||||
|
||||
return isUpdateSuccessful;
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ namespace AzToolsFramework
|
||||
TemplateInstanceMapperInterface* m_templateInstanceMapperInterface = nullptr;
|
||||
int m_instanceCountToUpdateInBatch = 0;
|
||||
AZStd::queue<Instance*> m_instancesUpdateQueue;
|
||||
bool m_updatingTemplateInstancesInQueue { false };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace AzToolsFramework
|
||||
using PrefabDomPath = rapidjson::Pointer;
|
||||
using PrefabDomList = AZStd::vector<PrefabDom>;
|
||||
|
||||
using PrefabDomReference = AZStd::optional<AZStd::reference_wrapper<PrefabDom>>;
|
||||
using PrefabDomValueReference = AZStd::optional<AZStd::reference_wrapper<PrefabDomValue>>;
|
||||
using PrefabDomValueConstReference = AZStd::optional<AZStd::reference_wrapper<const PrefabDomValue>>;
|
||||
|
||||
|
||||
@@ -10,12 +10,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/JSON/prettywriter.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceSerializer.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
@@ -71,15 +76,21 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadInstanceFromPrefabDom(Instance& instance, const PrefabDom& prefabDom, bool shouldClearContainers)
|
||||
bool LoadInstanceFromPrefabDom(Instance& instance, const PrefabDom& prefabDom, LoadInstanceFlags flags)
|
||||
{
|
||||
InstanceEntityIdMapper entityIdMapper;
|
||||
entityIdMapper.SetLoadingInstance(instance);
|
||||
if ((flags & LoadInstanceFlags::AssignRandomEntityId) == LoadInstanceFlags::AssignRandomEntityId)
|
||||
{
|
||||
entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random);
|
||||
}
|
||||
|
||||
AZ::JsonDeserializerSettings settings;
|
||||
// The InstanceEntityIdMapper is registered twice because it's used in several places during deserialization where one is
|
||||
// specific for the InstanceEntityIdMapper and once for the generic JsonEntityIdMapper. Because the Json Serializer's meta
|
||||
// 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_clearContainers = shouldClearContainers;
|
||||
|
||||
AZ::JsonSerializationResult::ResultCode result =
|
||||
AZ::JsonSerialization::Load(instance, prefabDom, settings);
|
||||
@@ -93,12 +104,54 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
entityIdMapper.FixUpUnresolvedEntityReferences();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadInstanceFromPrefabDom(
|
||||
Instance& instance, Instance::EntityList& newlyAddedEntities, const PrefabDom& prefabDom, LoadInstanceFlags flags)
|
||||
{
|
||||
InstanceEntityIdMapper entityIdMapper;
|
||||
entityIdMapper.SetLoadingInstance(instance);
|
||||
if ((flags & LoadInstanceFlags::AssignRandomEntityId) == LoadInstanceFlags::AssignRandomEntityId)
|
||||
{
|
||||
entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random);
|
||||
}
|
||||
|
||||
AZ::JsonDeserializerSettings settings;
|
||||
// The InstanceEntityIdMapper is registered twice because it's used in several places during deserialization where one is
|
||||
// specific for the InstanceEntityIdMapper and once for the generic JsonEntityIdMapper. Because the Json Serializer's meta
|
||||
// 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);
|
||||
|
||||
InstanceEntityScrubber instanceEntityScrubber(newlyAddedEntities);
|
||||
settings.m_metadata.Add(&instanceEntityScrubber);
|
||||
|
||||
AZ::JsonSerializationResult::ResultCode result =
|
||||
AZ::JsonSerialization::Load(instance, prefabDom, settings);
|
||||
|
||||
if (result.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"Failed to de-serialize Prefab Instance from Prefab DOM. "
|
||||
"Unable to proceed.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrintPrefabDomValue(
|
||||
[[maybe_unused]] const AZStd::string_view printMessage,
|
||||
[[maybe_unused]] const PrefabDomValue& prefabDomValue)
|
||||
{
|
||||
rapidjson::StringBuffer prefabBuffer;
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(prefabBuffer);
|
||||
prefabDomValue.Accept(writer);
|
||||
|
||||
qDebug() << printMessage.data() << "\n" << prefabBuffer.GetString();
|
||||
}
|
||||
} // namespace PrefabDomUtils
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -27,6 +28,7 @@ namespace AzToolsFramework
|
||||
inline static const char* SourceName = "Source";
|
||||
inline static const char* LinkIdName = "LinkId";
|
||||
inline static const char* EntitiesName = "Entities";
|
||||
inline static const char* ContainerEntityName = "ContainerEntity";
|
||||
|
||||
/**
|
||||
* Find Prefab value from given parent value and target value's name.
|
||||
@@ -45,6 +47,16 @@ namespace AzToolsFramework
|
||||
*/
|
||||
bool StoreInstanceInPrefabDom(const Instance& instance, PrefabDom& prefabDom);
|
||||
|
||||
enum class LoadInstanceFlags : uint8_t
|
||||
{
|
||||
//! No flags used during the call to LoadInstanceFromPrefabDom.
|
||||
None = 0,
|
||||
//! By default entities will get a stable id when they're deserialized. In cases where the new entities need to be kept
|
||||
//! unique, e.g. when they are duplicates of live entities, this flag will assign them a random new id.
|
||||
AssignRandomEntityId = 1 << 0
|
||||
};
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(LoadInstanceFlags)
|
||||
|
||||
/**
|
||||
* Loads a valid Prefab Instance from a Prefab Dom. Useful for generating Instances.
|
||||
* @param instance The Instance to load.
|
||||
@@ -52,7 +64,21 @@ namespace AzToolsFramework
|
||||
* @param shouldClearContainers whether to clear containers in Instance while loading.
|
||||
* @return bool on whether the operation succeeded.
|
||||
*/
|
||||
bool LoadInstanceFromPrefabDom(Instance& instance, const PrefabDom& prefabDom, bool shouldClearContainers);
|
||||
bool LoadInstanceFromPrefabDom(
|
||||
Instance& instance, const PrefabDom& prefabDom, LoadInstanceFlags flags = LoadInstanceFlags::None);
|
||||
|
||||
/**
|
||||
* Loads a valid Prefab Instance from a Prefab Dom. Useful for generating Instances.
|
||||
* @param instance The Instance to load.
|
||||
* @param newlyAddedEntities The new instances added during deserializing the instance. These are the entities found
|
||||
* in the prefabDom.
|
||||
* @param prefabDom the prefabDom that will be used to load the Instance data.
|
||||
* @param shouldClearContainers whether to clear containers in Instance while loading.
|
||||
* @return bool on whether the operation succeeded.
|
||||
*/
|
||||
bool LoadInstanceFromPrefabDom(
|
||||
Instance& instance, Instance::EntityList& newlyAddedEntities, const PrefabDom& prefabDom,
|
||||
LoadInstanceFlags flags = LoadInstanceFlags::None);
|
||||
|
||||
inline PrefabDomPath GetPrefabDomInstancePath(const char* instanceName)
|
||||
{
|
||||
@@ -61,6 +87,15 @@ namespace AzToolsFramework
|
||||
.Append(instanceName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Prints the contents of the given prefab DOM value to the debug output console in a readable format.
|
||||
* @param printMessage The message that will be printed before printing the PrefabDomValue
|
||||
* @param prefabDomValue The DOM value to be printed. A 'PrefabDom' type can also be passed into this variable.
|
||||
*/
|
||||
void PrintPrefabDomValue(
|
||||
[[maybe_unused]] const AZStd::string_view printMessage,
|
||||
[[maybe_unused]] const AzToolsFramework::Prefab::PrefabDomValue& prefabDomValue);
|
||||
|
||||
} // namespace PrefabDomUtils
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -10,10 +10,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabLoader.h>
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
|
||||
#include <AzFramework/FileFunc/FileFunc.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
|
||||
@@ -30,6 +36,14 @@ namespace AzToolsFramework
|
||||
"It is a requirement for the PrefabLoader class. "
|
||||
"Check that it is being correctly initialized.");
|
||||
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
AZ_Assert(settingsRegistry, "Settings registry is not set");
|
||||
|
||||
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();
|
||||
|
||||
AZ::Interface<PrefabLoaderInterface>::Register(this);
|
||||
}
|
||||
|
||||
@@ -38,57 +52,114 @@ namespace AzToolsFramework
|
||||
AZ::Interface<PrefabLoaderInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
TemplateId PrefabLoader::LoadTemplate(const AZStd::string& filePath)
|
||||
TemplateId PrefabLoader::LoadTemplateFromFile(AZ::IO::PathView filePath)
|
||||
{
|
||||
AZStd::unordered_set<AZStd::string> progressedFilePathsSet;
|
||||
TemplateId newTemplateId = LoadTemplate(filePath, progressedFilePathsSet);
|
||||
AZStd::unordered_set<AZ::IO::Path> progressedFilePathsSet;
|
||||
TemplateId newTemplateId = LoadTemplateFromFile(filePath, progressedFilePathsSet);
|
||||
return newTemplateId;
|
||||
}
|
||||
|
||||
TemplateId PrefabLoader::LoadTemplate(
|
||||
const AZStd::string& filePath,
|
||||
AZStd::unordered_set<AZStd::string>& progressedFilePathsSet)
|
||||
TemplateId PrefabLoader::LoadTemplateFromFile(AZ::IO::PathView filePath, AZStd::unordered_set<AZ::IO::Path>& progressedFilePathsSet)
|
||||
{
|
||||
if (!IsValidPrefabPath(filePath))
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadTemplateFromFile - "
|
||||
"Invalid file path: '%.*s'.",
|
||||
AZ_STRING_ARG(filePath.Native())
|
||||
);
|
||||
return InvalidTemplateId;
|
||||
}
|
||||
|
||||
auto readResult = AZ::Utils::ReadFile(GetFullPath(filePath).Native(), MaxPrefabFileSize);
|
||||
if (!readResult.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadTemplate - Failed to load Prefab file from '%.*s'."
|
||||
"Error message: '%s'",
|
||||
AZ_STRING_ARG(filePath.Native()),
|
||||
readResult.GetError().c_str()
|
||||
);
|
||||
return InvalidTemplateId;
|
||||
}
|
||||
|
||||
return LoadTemplateFromString(readResult.GetValue(), filePath, progressedFilePathsSet);
|
||||
}
|
||||
|
||||
TemplateId PrefabLoader::LoadTemplateFromString(
|
||||
AZStd::string_view content, AZ::IO::PathView originPath)
|
||||
{
|
||||
AZStd::unordered_set<AZ::IO::Path> progressedFilePathsSet;
|
||||
TemplateId newTemplateId = LoadTemplateFromString(content, originPath, progressedFilePathsSet);
|
||||
return newTemplateId;
|
||||
}
|
||||
|
||||
TemplateId PrefabLoader::LoadTemplateFromString(
|
||||
AZStd::string_view fileContent,
|
||||
AZ::IO::PathView originPath,
|
||||
AZStd::unordered_set<AZ::IO::Path>& progressedFilePathsSet)
|
||||
{
|
||||
if (!IsValidPrefabPath(originPath))
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadTemplateFromString - "
|
||||
"Invalid origin path: '%.*s'",
|
||||
AZ_STRING_ARG(originPath.Native())
|
||||
);
|
||||
return InvalidTemplateId;
|
||||
}
|
||||
|
||||
AZ::IO::Path relativePath = GetRelativePathToProject(originPath);
|
||||
|
||||
// Cyclical dependency detected if the prefab file is already part of the progressed
|
||||
// file path set.
|
||||
if (progressedFilePathsSet.contains(filePath))
|
||||
if (progressedFilePathsSet.contains(relativePath))
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"PrefabLoader::LoadTemplate - "
|
||||
"Prefab file %s has been detected to directly or indirectly depend on itself."
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadTemplateFromString - "
|
||||
"Prefab file '%.*s' has been detected to directly or indirectly depend on itself."
|
||||
"Terminating any further loading of this branch of its prefab hierarchy.",
|
||||
filePath.c_str());
|
||||
AZ_STRING_ARG(originPath.Native())
|
||||
);
|
||||
return InvalidTemplateId;
|
||||
}
|
||||
|
||||
// Directly return loaded Template id.
|
||||
TemplateId loadedTemplateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(filePath);
|
||||
TemplateId loadedTemplateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(relativePath);
|
||||
if (loadedTemplateId != InvalidTemplateId)
|
||||
{
|
||||
return loadedTemplateId;
|
||||
}
|
||||
|
||||
// Read Template's prefab file from disk and parse Prefab DOM from file.
|
||||
AZ::Outcome<PrefabDom, AZStd::string> readPrefabFileResult = AzFramework::FileFunc::ReadJsonFile(AZ::IO::Path(filePath));
|
||||
AZ::Outcome<PrefabDom, AZStd::string> readPrefabFileResult = AzFramework::FileFunc::ReadJsonFromString(fileContent);
|
||||
if (!readPrefabFileResult.IsSuccess())
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"PrefabLoader::LoadPrefabFile - Failed to load Prefab file from '%s'."
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadTemplate - Failed to load Prefab file from '%.*s'."
|
||||
"Error message: '%s'",
|
||||
filePath.c_str(), readPrefabFileResult.GetError().c_str());
|
||||
AZ_STRING_ARG(originPath.Native()),
|
||||
readPrefabFileResult.GetError().c_str());
|
||||
|
||||
return InvalidTemplateId;
|
||||
}
|
||||
|
||||
// Create new Template with the Prefab DOM.
|
||||
TemplateId newTemplateId = m_prefabSystemComponentInterface->AddTemplate(filePath, readPrefabFileResult.TakeValue());
|
||||
TemplateId newTemplateId = m_prefabSystemComponentInterface->AddTemplate(relativePath, readPrefabFileResult.TakeValue());
|
||||
if (newTemplateId == InvalidTemplateId)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadTemplate - "
|
||||
"Failed to create a template from instance with source file path '%s': "
|
||||
"Failed to create a template from instance with source file path '%.*s': "
|
||||
"invalid template id returned.",
|
||||
filePath.c_str());
|
||||
AZ_STRING_ARG(originPath.Native())
|
||||
);
|
||||
|
||||
return InvalidTemplateId;
|
||||
}
|
||||
@@ -97,7 +168,7 @@ namespace AzToolsFramework
|
||||
Template& newTemplate = newTemplateReference->get();
|
||||
|
||||
// Mark the file as being in progress.
|
||||
progressedFilePathsSet.emplace(filePath);
|
||||
progressedFilePathsSet.emplace(relativePath);
|
||||
|
||||
// Get 'Instances' value from Template.
|
||||
bool isLoadedWithErrors = false;
|
||||
@@ -108,57 +179,58 @@ namespace AzToolsFramework
|
||||
|
||||
// For each instance value in 'instances', try to create source Templates for target Template's nested instance data.
|
||||
// Also create Links between source/target Templates if source Template loaded successfully.
|
||||
for (PrefabDomValue::MemberIterator instanceIterator = instances.MemberBegin(); instanceIterator != instances.MemberEnd(); ++instanceIterator)
|
||||
for (PrefabDomValue::MemberIterator instanceIterator = instances.MemberBegin(); instanceIterator != instances.MemberEnd();
|
||||
++instanceIterator)
|
||||
{
|
||||
const PrefabDomValue& instance = instanceIterator->value;
|
||||
if (!LoadNestedInstance(instanceIterator, newTemplateId, progressedFilePathsSet))
|
||||
{
|
||||
isLoadedWithErrors = true;
|
||||
AZ_Error("Prefab", false,
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadTemplate - "
|
||||
"Loading nested instance '%s' in target Template '%u' from Prefab file '%s' failed.",
|
||||
instanceIterator->name.GetString(),
|
||||
newTemplateId,
|
||||
filePath.c_str());
|
||||
"Loading nested instance '%s' in target Template '%u' from Prefab file '%.*s' failed.",
|
||||
instanceIterator->name.GetString(), newTemplateId,
|
||||
AZ_STRING_ARG(originPath.Native())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
newTemplate.MarkAsLoadedWithErrors(isLoadedWithErrors);
|
||||
|
||||
// Un-mark the file as being in progress.
|
||||
progressedFilePathsSet.erase(filePath);
|
||||
progressedFilePathsSet.erase(originPath);
|
||||
|
||||
// Return target Template id.
|
||||
return newTemplateId;
|
||||
}
|
||||
|
||||
bool PrefabLoader::LoadNestedInstance(
|
||||
PrefabDomValue::MemberIterator& instanceIterator,
|
||||
TemplateId targetTemplateId,
|
||||
AZStd::unordered_set<AZStd::string>& progressedFilePathsSet)
|
||||
PrefabDomValue::MemberIterator& instanceIterator, TemplateId targetTemplateId,
|
||||
AZStd::unordered_set<AZ::IO::Path>& progressedFilePathsSet)
|
||||
{
|
||||
const PrefabDomValue& instance = instanceIterator->value;
|
||||
AZ::IO::PathView instancePath = AZStd::string_view(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
|
||||
|
||||
if (instanceIterator->name.GetStringLength() == 0)
|
||||
if (!IsValidPrefabPath(instancePath))
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadNestedInstance - "
|
||||
"There's an Instance without a name in the target Template on file path '%s'.",
|
||||
"There's an Instance with an invalid path '%s' in the target Template on file path '%s'.",
|
||||
instanceIterator->name.GetString(),
|
||||
m_prefabSystemComponentInterface->FindTemplate(targetTemplateId)->get().GetFilePath().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get source Template's path for getting nested instance data.
|
||||
PrefabDomValueConstReference sourceReference =
|
||||
PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::SourceName);
|
||||
if (!sourceReference.has_value() ||
|
||||
!sourceReference->get().IsString() ||
|
||||
sourceReference->get().GetStringLength() == 0)
|
||||
PrefabDomValueConstReference sourceReference = PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::SourceName);
|
||||
if (!sourceReference.has_value() || !sourceReference->get().IsString() || sourceReference->get().GetStringLength() == 0)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadNestedInstance - "
|
||||
"Can't get '%s' string value in Instance value '%s'of Template's Prefab DOM from file '%s'.",
|
||||
"Can't get '%s' string value in Instance value '%s' of Template's Prefab DOM from file '%s'.",
|
||||
PrefabDomUtils::SourceName, instanceIterator->name.GetString(),
|
||||
m_prefabSystemComponentInterface->FindTemplate(targetTemplateId)->get().GetFilePath().c_str());
|
||||
return false;
|
||||
@@ -167,31 +239,36 @@ namespace AzToolsFramework
|
||||
AZStd::string_view nestedTemplatePath(source.GetString(), source.GetStringLength());
|
||||
|
||||
// Get Template id of nested instance from its path.
|
||||
// If source Template is already loaded, get the id from Template File Path To Id Map,
|
||||
// If source Template is already loaded, get the id from Template File Path To Id Map,
|
||||
// else load the source Template by calling 'LoadTemplate' recursively.
|
||||
TemplateId nestedTemplateId = LoadTemplate(nestedTemplatePath, progressedFilePathsSet);
|
||||
|
||||
TemplateId nestedTemplateId = LoadTemplateFromFile(nestedTemplatePath, progressedFilePathsSet);
|
||||
TemplateReference nestedTemplateReference = m_prefabSystemComponentInterface->FindTemplate(nestedTemplateId);
|
||||
if (!nestedTemplateReference.has_value() ||
|
||||
!nestedTemplateReference->get().IsValid())
|
||||
if (!nestedTemplateReference.has_value() || !nestedTemplateReference->get().IsValid())
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadNestedInstance - "
|
||||
"Error occurred while loading nested Prefab file '%.*s' from Prefab file '%s'.",
|
||||
aznumeric_cast<int>(nestedTemplatePath.size()), nestedTemplatePath.data(),
|
||||
m_prefabSystemComponentInterface->FindTemplate(targetTemplateId)->get().GetFilePath().c_str());
|
||||
AZ_STRING_ARG(nestedTemplatePath),
|
||||
m_prefabSystemComponentInterface->FindTemplate(targetTemplateId)->get().GetFilePath().c_str()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// After source template has been loaded, create Link between source/target Template.
|
||||
LinkId newLinkId = m_prefabSystemComponentInterface->AddLink(nestedTemplateId, targetTemplateId, instanceIterator, AZStd::nullopt);
|
||||
LinkId newLinkId =
|
||||
m_prefabSystemComponentInterface->AddLink(nestedTemplateId, targetTemplateId, instanceIterator, AZStd::nullopt);
|
||||
if (newLinkId == InvalidLinkId)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::LoadNestedInstance - "
|
||||
"Failed to add a new Link to Nested Template Instance '%s' which connects source Template '%.*s' and target Template '%s'.",
|
||||
instanceIterator->name.GetString(),
|
||||
aznumeric_cast<int>(nestedTemplatePath.size()), nestedTemplatePath.data(),
|
||||
m_prefabSystemComponentInterface->FindTemplate(targetTemplateId)->get().GetFilePath().c_str());
|
||||
"Failed to add a new Link to Nested Template Instance '%s' which connects source Template '%.*s' and target Template "
|
||||
"'%s'.",
|
||||
instanceIterator->name.GetString(), AZ_STRING_ARG(nestedTemplatePath),
|
||||
m_prefabSystemComponentInterface->FindTemplate(targetTemplateId)->get().GetFilePath().c_str()
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -200,45 +277,136 @@ namespace AzToolsFramework
|
||||
return !nestedTemplateReference->get().IsLoadedWithErrors();
|
||||
}
|
||||
|
||||
bool PrefabLoader::SaveTemplate(const TemplateId& templateId)
|
||||
bool PrefabLoader::SaveTemplate(TemplateId templateId)
|
||||
{
|
||||
const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId);
|
||||
if (!domAndFilepath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto outcome = AzFramework::FileFunc::WriteJsonFile(domAndFilepath->first, GetFullPath(domAndFilepath->second));
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::SaveTemplate - "
|
||||
"Failed to save template '%s'."
|
||||
"Error: %s",
|
||||
domAndFilepath->second.c_str(),
|
||||
outcome.GetError().c_str()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrefabLoader::SaveTemplateToString(TemplateId templateId, AZStd::string& output)
|
||||
{
|
||||
const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId);
|
||||
if (!domAndFilepath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto outcome = AzFramework::FileFunc::WriteJsonToString(domAndFilepath->first, output);
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::SaveTemplateToString - "
|
||||
"Failed to serialize template '%s' into a string."
|
||||
"Error: %s",
|
||||
domAndFilepath->second.c_str(),
|
||||
outcome.GetError().c_str()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
AZStd::optional<AZStd::pair<PrefabDom, AZ::IO::Path>> PrefabLoader::StoreTemplateIntoFileFormat(TemplateId templateId)
|
||||
{
|
||||
// Acquire the template we are saving
|
||||
TemplateReference templateToSaveReference = m_prefabSystemComponentInterface->FindTemplate(templateId);
|
||||
if (!templateToSaveReference.has_value())
|
||||
{
|
||||
AZ_Warning("Prefab", false,
|
||||
AZ_Warning(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::SaveTemplate - Unable to save prefab template with id: '%llu'. "
|
||||
"Template with that id could not be found",
|
||||
templateId);
|
||||
templateId
|
||||
);
|
||||
|
||||
return false;
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
Template& templateToSave = templateToSaveReference->get();
|
||||
if (!templateToSave.IsValid())
|
||||
{
|
||||
AZ_Warning("Prefab", false,
|
||||
AZ_Warning(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::SaveTemplate - Unable to save Prefab Template with id: %llu. "
|
||||
"Template with that id is invalid",
|
||||
templateId);
|
||||
templateId
|
||||
);
|
||||
|
||||
return false;
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
// Make a copy of a our prefab DOM where nested instances become file references with patch data
|
||||
PrefabDom templateDomToSave;
|
||||
if (!templateToSave.CopyTemplateIntoPrefabFileFormat(templateDomToSave))
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"PrefabLoader::SaveTemplate - Unable to store a collapsed version of prefab Template while attempting to save to %s"
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"PrefabLoader::SaveTemplate - Unable to store a collapsed version of prefab Template while attempting to save to %s."
|
||||
"Save cannot continue",
|
||||
templateToSave.GetFilePath().c_str());
|
||||
templateToSave.GetFilePath().c_str()
|
||||
);
|
||||
|
||||
return false;
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
// Save the file
|
||||
return AzFramework::FileFunc::WriteJsonFile(templateDomToSave, templateToSave.GetFilePath()).IsSuccess();
|
||||
return { { AZStd::move(templateDomToSave), templateToSave.GetFilePath() } };
|
||||
}
|
||||
|
||||
bool PrefabLoader::IsValidPrefabPath(AZ::IO::PathView path)
|
||||
{
|
||||
// Check for OS invalid character and paths ending on '/' '\\' separators as final char
|
||||
AZStd::string_view pathStr = path.Native();
|
||||
|
||||
return !path.empty() &&
|
||||
(pathStr.find_first_of(AZ_FILESYSTEM_INVALID_CHARACTERS) == AZStd::string::npos) &&
|
||||
(pathStr.back() != '\\' && pathStr.back() != '/');
|
||||
}
|
||||
|
||||
AZ::IO::Path PrefabLoader::GetFullPath(AZ::IO::PathView path)
|
||||
{
|
||||
AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path).MakePreferred();
|
||||
if (pathWithOSSeparator.IsAbsolute())
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator);
|
||||
}
|
||||
|
||||
AZ::IO::Path PrefabLoader::GetRelativePathToProject(AZ::IO::PathView path)
|
||||
{
|
||||
AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path.Native()).MakePreferred();
|
||||
if (!pathWithOSSeparator.IsAbsolute())
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return AZ::IO::Path(path.Native(), '/').MakePreferred().LexicallyRelative(m_projectPathWithSlashSeparator);
|
||||
}
|
||||
|
||||
AZ::IO::Path PrefabLoaderInterface::GeneratePath()
|
||||
{
|
||||
return AZStd::string::format("Prefab_%s", AZ::Entity::MakeId().ToString().c_str());
|
||||
}
|
||||
|
||||
} // namespace Prefab
|
||||
|
||||
@@ -14,15 +14,19 @@
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
|
||||
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
namespace AZ
|
||||
{
|
||||
class Entity;
|
||||
} // namespace AZ
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabSystemComponentInterface;
|
||||
@@ -42,37 +46,81 @@ namespace AzToolsFramework
|
||||
|
||||
/**
|
||||
* Load Prefab Template from given file path to memory and return the id of loaded Template.
|
||||
* Converts Prefab Asset form into Prefab Template form by expanding source path and patch info
|
||||
* Converts .prefab into Prefab Template form by expanding source path and patch info
|
||||
* into fully formed nested template info.
|
||||
* @param filePath A Prefab Template file path.
|
||||
* @return A unique id of Template on filePath loaded. Return null id if loading Template on filePath failed.
|
||||
* @return A unique id of Template on filePath loaded. Return invalid template id if loading Template on filePath failed.
|
||||
*/
|
||||
TemplateId LoadTemplate(const AZStd::string& filePath) override;
|
||||
TemplateId LoadTemplateFromFile(AZ::IO::PathView filePath) override;
|
||||
|
||||
/**
|
||||
* Saves a Prefab Template to the the source path registered with the template.
|
||||
* Converts Prefab Template form into Prefab Asset form by collapsing nested Template info
|
||||
* into a source path and patches.
|
||||
* @param templateId Id of the template to be saved
|
||||
* @return bool on whether the operation succeeded or not
|
||||
*/
|
||||
bool SaveTemplate(const TemplateId& templateId) override;
|
||||
* Load Prefab Template from given content string to memory and return the id of loaded Template.
|
||||
* Converts .prefab form into Prefab Template form by expanding source path and patch info
|
||||
* into fully formed nested template info.
|
||||
* @param content Json content of the prefab
|
||||
* @param originPath Path that will be used for the prefab in case of saved into a file.
|
||||
* @return A unique id of Template on filePath loaded. Return invalid template id if loading Template on filePath failed.
|
||||
*/
|
||||
TemplateId LoadTemplateFromString(AZStd::string_view content, AZ::IO::PathView originPath = GeneratePath()) override;
|
||||
|
||||
/**
|
||||
* Saves a Prefab Template to the the source path registered with the template.
|
||||
* Converts Prefab Template form into .prefab form by collapsing nested Template info
|
||||
* into a source path and patches.
|
||||
* @param templateId Id of the template to be saved
|
||||
* @return bool on whether the operation succeeded or not
|
||||
*/
|
||||
bool SaveTemplate(TemplateId templateId) override;
|
||||
|
||||
/**
|
||||
* Saves a Prefab Template into the provided output string.
|
||||
* Converts Prefab Template form into .prefab form by collapsing nested Template info
|
||||
* into a source path and patches.
|
||||
* @param templateId Id of the template to be saved
|
||||
* @param outputJson Will contain the serialized template json on success
|
||||
* @return bool on whether the operation succeeded or not
|
||||
*/
|
||||
bool SaveTemplateToString(TemplateId templateId, AZStd::string& outputJson) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterPrefabLoaderInterface();
|
||||
|
||||
void UnregisterPrefabLoaderInterface();
|
||||
|
||||
//! Converts path into full absolute path. This will be used by loading/save IO operations.
|
||||
//! The path will always have the correct separator for the current OS
|
||||
AZ::IO::Path GetFullPath(AZ::IO::PathView path) override;
|
||||
|
||||
//! Converts path into a relative path to the project, this will be the paths in .prefab file.
|
||||
//! The path will always have '/' separator.
|
||||
AZ::IO::Path GetRelativePathToProject(AZ::IO::PathView path) override;
|
||||
|
||||
//! Returns if the path is a valid path for a prefab
|
||||
static bool IsValidPrefabPath(AZ::IO::PathView path);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Load Prefab Template from given file path to memory and return the id of loaded Template.
|
||||
* @param filePath A path to a Prefab Template file.
|
||||
* @param progressedFilePathsSet An unordered_set to track if there's any cyclical dependency between Templates.
|
||||
* @return A unique id of Template on filePath loaded. Return invalid id if loading Template on filePath failed.
|
||||
*/
|
||||
TemplateId LoadTemplate(
|
||||
const AZStd::string& filePath,
|
||||
AZStd::unordered_set<AZStd::string>& progressedFilePathsSet);
|
||||
TemplateId LoadTemplateFromFile(
|
||||
AZ::IO::PathView filePath,
|
||||
AZStd::unordered_set<AZ::IO::Path>& progressedFilePathsSet);
|
||||
|
||||
/**
|
||||
* Load Prefab Template from given string to memory and return the id of loaded Template.
|
||||
* @param fileContent Json content of the template
|
||||
* @param filePath Path that will be used for the template if saved to file.
|
||||
* @param progressedFilePathsSet An unordered_set to track if there's any cyclical dependency between Templates.
|
||||
* @return A unique id of Template on filePath loaded. Return invalid id if loading Template on filePath failed.
|
||||
*/
|
||||
TemplateId LoadTemplateFromString(
|
||||
AZStd::string_view fileContent,
|
||||
AZ::IO::PathView filePath,
|
||||
AZStd::unordered_set<AZ::IO::Path>& progressedFilePathsSet);
|
||||
|
||||
/**
|
||||
* Load nested instance given a nested instance value iterator and target Template with its id.
|
||||
@@ -84,9 +132,14 @@ namespace AzToolsFramework
|
||||
bool LoadNestedInstance(
|
||||
PrefabDomValue::MemberIterator& instanceIterator,
|
||||
TemplateId targetTemplateId,
|
||||
AZStd::unordered_set<AZStd::string>& progressedFilePathsSet);
|
||||
AZStd::unordered_set<AZ::IO::Path>& progressedFilePathsSet);
|
||||
|
||||
//! Retrieves Dom content and its path from a template id
|
||||
AZStd::optional<AZStd::pair<PrefabDom, AZ::IO::Path>> StoreTemplateIntoFileFormat(TemplateId templateId);
|
||||
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||
AZ::IO::Path m_projectPathWithOsSeparator;
|
||||
AZ::IO::Path m_projectPathWithSlashSeparator;
|
||||
};
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||
|
||||
@@ -20,6 +21,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
constexpr size_t MaxPrefabFileSize = 1024 * 1024;
|
||||
|
||||
/*!
|
||||
* PrefabLoaderInterface
|
||||
* Interface for saving/loading Prefab files.
|
||||
@@ -34,9 +37,19 @@ namespace AzToolsFramework
|
||||
* Converts Prefab Asset form into Prefab Template form by expanding source path and patch info
|
||||
* into fully formed nested template info.
|
||||
* @param filePath A Prefab Template file path.
|
||||
* @return A unique id of Template on filePath loaded. Return null id if loading Template on filePath failed.
|
||||
* @return A unique id of Template on filePath loaded. Return invalid template id if loading Template on filePath failed.
|
||||
*/
|
||||
virtual TemplateId LoadTemplate(const AZStd::string& filePath) = 0;
|
||||
virtual TemplateId LoadTemplateFromFile(AZ::IO::PathView filePath) = 0;
|
||||
|
||||
/**
|
||||
* Load Prefab Template from given content string to memory and return the id of loaded Template.
|
||||
* Converts .prefab form into Prefab Template form by expanding source path and patch info
|
||||
* into fully formed nested template info.
|
||||
* @param content Json content of the prefab
|
||||
* @param originPath Path that will be used for the prefab in case of saved into a file.
|
||||
* @return A unique id of Template on filePath loaded. Return invalid template id if loading Template on filePath failed.
|
||||
*/
|
||||
virtual TemplateId LoadTemplateFromString(AZStd::string_view content, AZ::IO::PathView originPath = GeneratePath()) = 0;
|
||||
|
||||
/**
|
||||
* Saves a Prefab Template to the the source path registered with the template.
|
||||
@@ -45,9 +58,31 @@ namespace AzToolsFramework
|
||||
* @param templateId Id of the template to be saved
|
||||
* @return bool on whether the operation succeeded or not
|
||||
*/
|
||||
virtual bool SaveTemplate(const TemplateId& templateId) = 0;
|
||||
};
|
||||
virtual bool SaveTemplate(TemplateId templateId) = 0;
|
||||
|
||||
/**
|
||||
* Saves a Prefab Template into the provided output string.
|
||||
* Converts Prefab Template form into .prefab form by collapsing nested Template info
|
||||
* into a source path and patches.
|
||||
* @param templateId Id of the template to be saved
|
||||
* @param outputJson Will contain the serialized template json on success
|
||||
* @return bool on whether the operation succeeded or not
|
||||
*/
|
||||
virtual bool SaveTemplateToString(TemplateId templateId, AZStd::string& outputJson) = 0;
|
||||
|
||||
//! Converts path into full absolute path. This will be used by loading/save IO operations.
|
||||
//! The path will always have the correct separator for the current OS
|
||||
virtual AZ::IO::Path GetFullPath(AZ::IO::PathView path) = 0;
|
||||
|
||||
//! Converts path into a relative path to the current project, this will be the paths in .prefab file.
|
||||
//! The path will always have '/' separator.
|
||||
virtual AZ::IO::Path GetRelativePathToProject(AZ::IO::PathView path) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
// Generates a new path
|
||||
static AZ::IO::Path GeneratePath();
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -13,46 +13,55 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicHandler.h>
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Utils/TypeHash.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/PrefabLoaderInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabUndo.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
PrefabPublicHandler::PrefabPublicHandler()
|
||||
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_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
AZ_Assert(m_prefabSystemComponentInterface, "Could not get PrefabSystemComponentInterface on PrefabPublicHandler construction.");
|
||||
|
||||
m_prefabUndoCache.Initialize();
|
||||
|
||||
AZ::Interface<PrefabPublicInterface>::Register(this);
|
||||
}
|
||||
|
||||
PrefabPublicHandler::~PrefabPublicHandler()
|
||||
void PrefabPublicHandler::UnregisterPrefabPublicHandlerInterface()
|
||||
{
|
||||
AZ::Interface<PrefabPublicInterface>::Unregister(this);
|
||||
|
||||
m_prefabUndoCache.Destroy();
|
||||
}
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZStd::string_view filePath)
|
||||
{
|
||||
// Retrieve entityList from entityIds
|
||||
EntityList inputEntityList;
|
||||
inputEntityList.reserve(entityIds.size());
|
||||
|
||||
for (AZ::EntityId entityId : entityIds)
|
||||
{
|
||||
if (entityId.IsValid())
|
||||
{
|
||||
inputEntityList.emplace_back(GetEntityById(entityId));
|
||||
}
|
||||
}
|
||||
EntityIdListToEntityList(entityIds, inputEntityList);
|
||||
|
||||
// Find common root and top level entities
|
||||
bool entitiesHaveCommonRoot = false;
|
||||
@@ -80,7 +89,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
// Retrieve the owning instance of the common root entity, which will be our new instance's parent instance.
|
||||
InstanceOptionalReference commonRootEntityOwningInstance = GetCommonRootEntityOwningInstance(commonRootEntityId);
|
||||
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");
|
||||
|
||||
@@ -101,7 +110,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
InstanceOptionalReference instance = prefabEditorEntityOwnershipInterface->CreatePrefab(
|
||||
entities, AZStd::move(instances), filePath, commonRootEntityOwningInstance->get());
|
||||
entities, AZStd::move(instances), filePath, commonRootEntityOwningInstance);
|
||||
|
||||
if (!instance)
|
||||
{
|
||||
@@ -109,12 +118,6 @@ namespace AzToolsFramework
|
||||
"(A null instance is returned)."));
|
||||
}
|
||||
|
||||
auto prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
|
||||
if (prefabLoaderInterface == nullptr)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Could not create a new prefab out of the entities provided - internal error (PrefabLoaderInterface unavailable)."));
|
||||
}
|
||||
|
||||
AZ::EntityId containerEntityId = instance->get().GetContainerEntityId();
|
||||
AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero());
|
||||
AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero());
|
||||
@@ -128,23 +131,18 @@ namespace AzToolsFramework
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Register container entity to PrefabUiHandler
|
||||
auto editorEntityUiInterface = AZ::Interface<EditorEntityUiInterface>::Get();
|
||||
|
||||
if (editorEntityUiInterface != nullptr)
|
||||
{
|
||||
editorEntityUiInterface->RegisterEntity(containerEntityId, 1);
|
||||
}
|
||||
|
||||
// Save Template
|
||||
prefabLoaderInterface->SaveTemplate(instance->get().GetTemplateId());
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
@@ -153,25 +151,391 @@ namespace AzToolsFramework
|
||||
return AZ::Failure(AZStd::string("Prefab - InstantiatePrefab is yet to be implemented."));
|
||||
}
|
||||
|
||||
bool PrefabPublicHandler::IsInstanceContainerEntity(AZ::EntityId entityId)
|
||||
PrefabOperationResult PrefabPublicHandler::SavePrefab(AZ::IO::Path filePath)
|
||||
{
|
||||
AZ::Entity* entity = GetEntityById(entityId);
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entity->GetId());
|
||||
auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
if (!prefabSystemComponentInterface)
|
||||
{
|
||||
AZ_Assert(
|
||||
false,
|
||||
"Prefab - PrefabPublicHandler - "
|
||||
"Prefab System Component Interface could not be found. "
|
||||
"Check that it is being correctly initialized.");
|
||||
return AZ::Failure(
|
||||
AZStd::string("SavePrefab - Internal error (Prefab System Component Interface could not be found)."));
|
||||
}
|
||||
|
||||
auto templateId = prefabSystemComponentInterface->GetTemplateIdFromFilePath(filePath.c_str());
|
||||
|
||||
if (templateId == InvalidTemplateId)
|
||||
{
|
||||
return AZ::Failure(
|
||||
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))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Could not save prefab - internal error (Json write operation failure)."));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
PrefabEntityResult PrefabPublicHandler::CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position)
|
||||
{
|
||||
InstanceOptionalReference owningInstanceOfParentEntity = GetOwnerInstanceByEntityId(parentId);
|
||||
if (!owningInstanceOfParentEntity)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Cannot add entity because the owning instance of parent entity with id '%llu' could not be found.",
|
||||
static_cast<AZ::u64>(parentId)));
|
||||
}
|
||||
|
||||
EntityAlias entityAlias = Instance::GenerateEntityAlias();
|
||||
|
||||
AliasPath absoluteEntityPath = owningInstanceOfParentEntity->get().GetAbsoluteInstanceAliasPath();
|
||||
absoluteEntityPath.Append(entityAlias);
|
||||
|
||||
AZ::EntityId entityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
|
||||
AZStd::string entityName = AZStd::string::format("Entity%llu", static_cast<AZ::u64>(m_newEntityCounter++));
|
||||
|
||||
AZ::Entity* entity = aznew AZ::Entity(entityId, entityName.c_str());
|
||||
|
||||
Instance& entityOwningInstance = owningInstanceOfParentEntity->get();
|
||||
|
||||
PrefabDom instanceDomBeforeUpdate;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBeforeUpdate, entityOwningInstance);
|
||||
|
||||
ScopedUndoBatch undoBatch("Add Entity");
|
||||
|
||||
entityOwningInstance.AddEntity(*entity, entityAlias);
|
||||
|
||||
EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequestBus::Events::HandleEntitiesAdded, EntityList{entity});
|
||||
|
||||
AZ::Transform transform = AZ::Transform::CreateIdentity();
|
||||
transform.SetTranslation(position);
|
||||
|
||||
EntityOptionalReference owningInstanceContainerEntity = entityOwningInstance.GetContainerEntity();
|
||||
if (owningInstanceContainerEntity && !parentId.IsValid())
|
||||
{
|
||||
parentId = owningInstanceContainerEntity->get().GetId();
|
||||
}
|
||||
|
||||
if (parentId.IsValid())
|
||||
{
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformInterface::SetParent, parentId);
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformInterface::SetLocalTM, transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformInterface::SetWorldTM, transform);
|
||||
}
|
||||
|
||||
|
||||
// Select the new entity (and deselect others).
|
||||
AzToolsFramework::EntityIdList selection = {entityId};
|
||||
|
||||
SelectionCommand* selectionCommand = aznew SelectionCommand(selection, "");
|
||||
selectionCommand->SetParent(undoBatch.GetUndoBatch());
|
||||
|
||||
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();
|
||||
|
||||
return AZ::Success(entityId);
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::GenerateUndoNodesForEntityChangeAndUpdateCache(
|
||||
AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch)
|
||||
{
|
||||
// Create Undo node on entities if they belong to an instance
|
||||
InstanceOptionalReference instanceOptionalReference = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
if (instanceOptionalReference.has_value())
|
||||
{
|
||||
PrefabDom beforeState;
|
||||
m_prefabUndoCache.Retrieve(entityId, beforeState);
|
||||
|
||||
PrefabDom afterState;
|
||||
AZ::Entity* entity = GetEntityById(entityId);
|
||||
if (entity)
|
||||
{
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity);
|
||||
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState);
|
||||
|
||||
if (patch.IsArray() && !patch.Empty() && beforeState.IsObject())
|
||||
{
|
||||
// Update the state of the entity
|
||||
PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
state->SetParent(parentUndoBatch);
|
||||
state->Capture(beforeState, afterState, entityId);
|
||||
|
||||
state->Redo();
|
||||
}
|
||||
|
||||
// Update the cache
|
||||
m_prefabUndoCache.Store(entityId, AZStd::move(afterState));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabPublicHandler::IsInstanceContainerEntity(AZ::EntityId entityId) const
|
||||
{
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
return owningInstance && (owningInstance->get().GetContainerEntityId() == entityId);
|
||||
}
|
||||
|
||||
AZ::EntityId PrefabPublicHandler::GetInstanceContainerEntityId(AZ::EntityId entityId)
|
||||
bool PrefabPublicHandler::IsLevelInstanceContainerEntity(AZ::EntityId entityId) const
|
||||
{
|
||||
// Get owning instance
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
// Get level root instance
|
||||
auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
|
||||
if (!prefabEditorEntityOwnershipInterface)
|
||||
{
|
||||
AZ_Assert(
|
||||
false,
|
||||
"Could not get owning instance of common root entity :"
|
||||
"PrefabEditorEntityOwnershipInterface unavailable.");
|
||||
}
|
||||
InstanceOptionalReference levelInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
|
||||
|
||||
return owningInstance
|
||||
&& levelInstance
|
||||
&& (&owningInstance->get() == &levelInstance->get())
|
||||
&& (owningInstance->get().GetContainerEntityId() == entityId);
|
||||
}
|
||||
|
||||
AZ::EntityId PrefabPublicHandler::GetInstanceContainerEntityId(AZ::EntityId entityId) const
|
||||
{
|
||||
AZ::Entity* entity = GetEntityById(entityId);
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entity->GetId());
|
||||
if (owningInstance)
|
||||
if (entity)
|
||||
{
|
||||
return owningInstance->get().GetContainerEntityId();
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entity->GetId());
|
||||
if (owningInstance)
|
||||
{
|
||||
return owningInstance->get().GetContainerEntityId();
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::EntityId();
|
||||
}
|
||||
|
||||
AZ::EntityId PrefabPublicHandler::GetLevelInstanceContainerEntityId() const
|
||||
{
|
||||
auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
|
||||
if (!prefabEditorEntityOwnershipInterface)
|
||||
{
|
||||
AZ_Assert(
|
||||
false,
|
||||
"Could not get owning instance of common root entity :"
|
||||
"PrefabEditorEntityOwnershipInterface unavailable.");
|
||||
return AZ::EntityId();
|
||||
}
|
||||
|
||||
auto rootInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
|
||||
|
||||
if (!rootInstance.has_value())
|
||||
{
|
||||
return AZ::EntityId();
|
||||
}
|
||||
|
||||
return rootInstance->get().GetContainerEntityId();
|
||||
}
|
||||
|
||||
AZ::IO::Path PrefabPublicHandler::GetOwningInstancePrefabPath(AZ::EntityId entityId) const
|
||||
{
|
||||
AZ::IO::Path path;
|
||||
InstanceOptionalReference instance = GetOwnerInstanceByEntityId(entityId);
|
||||
|
||||
if (instance.has_value())
|
||||
{
|
||||
path = instance->get().GetTemplateSourcePath();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
PrefabRequestResult PrefabPublicHandler::HasUnsavedChanges(AZ::IO::Path prefabFilePath) const
|
||||
{
|
||||
auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
if (!prefabSystemComponentInterface)
|
||||
{
|
||||
AZ_Assert(
|
||||
false,
|
||||
"Prefab - PrefabPublicHandler - "
|
||||
"Prefab System Component Interface could not be found. "
|
||||
"Check that it is being correctly initialized.");
|
||||
return AZ::Failure(
|
||||
AZStd::string("HasUnsavedChanges - Internal error (Prefab System Component Interface could not be found)."));
|
||||
}
|
||||
|
||||
auto templateId = prefabSystemComponentInterface->GetTemplateIdFromFilePath(prefabFilePath.c_str());
|
||||
|
||||
if (templateId == InvalidTemplateId)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("HasUnsavedChanges - Path error. Path could be invalid, or the prefab may not be loaded in this level."));
|
||||
}
|
||||
|
||||
return AZ::Success(prefabSystemComponentInterface->IsTemplateDirty(templateId));
|
||||
}
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::DeleteEntitiesInInstance(const EntityIdList& entityIds)
|
||||
{
|
||||
return DeleteFromInstance(entityIds, false);
|
||||
}
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds)
|
||||
{
|
||||
return DeleteFromInstance(entityIds, true);
|
||||
}
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants)
|
||||
{
|
||||
if (entityIds.empty())
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
if (!EntitiesBelongToSameInstance(entityIds))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("DeleteEntitiesAndAllDescendantsInInstance - Deletion Error. Cannot delete multiple "
|
||||
"entities belonging to different instances with one operation."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference instance = GetOwnerInstanceByEntityId(entityIds[0]);
|
||||
|
||||
// Retrieve entityList from entityIds
|
||||
EntityList inputEntityList;
|
||||
EntityIdListToEntityList(entityIds, inputEntityList);
|
||||
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
UndoSystem::URSequencePoint* currentUndoBatch = nullptr;
|
||||
ToolsApplicationRequests::Bus::BroadcastResult(currentUndoBatch, &ToolsApplicationRequests::Bus::Events::GetCurrentUndoBatch);
|
||||
|
||||
bool createdUndo = false;
|
||||
if (!currentUndoBatch)
|
||||
{
|
||||
createdUndo = true;
|
||||
ToolsApplicationRequests::Bus::BroadcastResult(
|
||||
currentUndoBatch, &ToolsApplicationRequests::Bus::Events::BeginUndoBatch, "Delete Selected");
|
||||
AZ_Assert(currentUndoBatch, "Failed to create new undo batch.");
|
||||
}
|
||||
|
||||
// In order to undo DeleteSelected, we have to create a selection command which selects the current selection
|
||||
// and then add the deletion as children.
|
||||
// Commands always execute themselves first and then their children (when going forwards)
|
||||
// and do the opposite when going backwards.
|
||||
EntityIdList selectedEntities;
|
||||
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities);
|
||||
SelectionCommand* selCommand = aznew SelectionCommand(selectedEntities, "Delete Entities");
|
||||
|
||||
// We insert a "deselect all" command before we delete the entities. This ensures the delete operations aren't changing
|
||||
// selection state, which triggers expensive UI updates. By deselecting up front, we are able to do those expensive
|
||||
// UI updates once at the start instead of once for each entity.
|
||||
{
|
||||
EntityIdList deselection;
|
||||
SelectionCommand* deselectAllCommand = aznew SelectionCommand(deselection, "Deselect Entities");
|
||||
deselectAllCommand->SetParent(selCommand);
|
||||
}
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DeleteEntities:UndoCaptureAndPurgeEntities");
|
||||
|
||||
Prefab::PrefabDom instanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, instance->get());
|
||||
|
||||
if (deleteDescendants)
|
||||
{
|
||||
AZStd::vector<AZ::Entity*> entities;
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>> instances;
|
||||
|
||||
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, instance->get(), entities, instances);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("DeleteEntitiesAndAllDescendantsInInstance"));
|
||||
}
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, entity->GetId());
|
||||
}
|
||||
|
||||
for (auto& nestedInstance : instances)
|
||||
{
|
||||
nestedInstance.reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
auto instancePtr = instance->get().DetachNestedInstance(owningInstance->get().GetInstanceAlias());
|
||||
instancePtr.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
instance->get().DetachEntity(entityId);
|
||||
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, entityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Prefab::PrefabDom instanceDomAfter;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, instance->get());
|
||||
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance deletion");
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, instance->get().GetTemplateId());
|
||||
command->SetParent(selCommand);
|
||||
}
|
||||
|
||||
selCommand->SetParent(currentUndoBatch);
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DeleteEntities:RunRedo");
|
||||
selCommand->RunRedo();
|
||||
}
|
||||
|
||||
if (createdUndo)
|
||||
{
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::EndUndoBatch);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::GenerateContainerEntityTransform(const EntityList& topLevelEntities,
|
||||
AZ::Vector3& translation, AZ::Quaternion& rotation)
|
||||
{
|
||||
@@ -231,19 +595,19 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
InstanceOptionalReference PrefabPublicHandler::GetCommonRootEntityOwningInstance(AZ::EntityId entityId)
|
||||
InstanceOptionalReference PrefabPublicHandler::GetOwnerInstanceByEntityId(AZ::EntityId entityId) const
|
||||
{
|
||||
if (entityId.IsValid())
|
||||
{
|
||||
return m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
}
|
||||
|
||||
// If the commonRootEntity is invalid, then the owning instance would be the root prefab instance of the
|
||||
// If the entityId is invalid, then the owning instance would be the root prefab instance of the
|
||||
// PrefabEditorEntityOwnershipService.
|
||||
auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
|
||||
if (!prefabEditorEntityOwnershipInterface)
|
||||
{
|
||||
AZ_Assert(false, "Could not get owining instance of common root entity :"
|
||||
AZ_Assert(false, "Could not get owning instance of common root entity :"
|
||||
"PrefabEditorEntityOwnershipInterface unavailable.");
|
||||
}
|
||||
return prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
|
||||
@@ -368,5 +732,52 @@ namespace AzToolsFramework
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrefabPublicHandler::EntitiesBelongToSameInstance(const EntityIdList& entityIds) const
|
||||
{
|
||||
if (entityIds.size() <= 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
InstanceOptionalReference sharedInstance = AZStd::nullopt;
|
||||
|
||||
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)
|
||||
{
|
||||
owningInstance = owningInstance->get().GetParentInstance();
|
||||
}
|
||||
|
||||
if (!sharedInstance.has_value())
|
||||
{
|
||||
sharedInstance = owningInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (&sharedInstance->get() != &owningInstance->get())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,18 +14,22 @@
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
|
||||
|
||||
using EntityList = AZStd::vector<AZ::Entity*>;
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabUndoCache.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
using EntityList = AZStd::vector<AZ::Entity*>;
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
class Instance;
|
||||
class InstanceEntityMapperInterface;
|
||||
class InstanceToTemplateInterface;
|
||||
class PrefabSystemComponentInterface;
|
||||
|
||||
class PrefabPublicHandler final
|
||||
: public PrefabPublicInterface
|
||||
@@ -34,27 +38,48 @@ namespace AzToolsFramework
|
||||
AZ_CLASS_ALLOCATOR(PrefabPublicHandler, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(PrefabPublicHandler, "{35802943-6B60-430F-9DED-075E3A576A25}", PrefabPublicInterface);
|
||||
|
||||
PrefabPublicHandler();
|
||||
~PrefabPublicHandler();
|
||||
void RegisterPrefabPublicHandlerInterface();
|
||||
void UnregisterPrefabPublicHandlerInterface();
|
||||
|
||||
// PrefabPublicInterface...
|
||||
PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZStd::string_view 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;
|
||||
|
||||
void GenerateUndoNodesForEntityChangeAndUpdateCache(AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch) override;
|
||||
|
||||
bool IsInstanceContainerEntity(AZ::EntityId entityId) override;
|
||||
AZ::EntityId GetInstanceContainerEntityId(AZ::EntityId entityId) override;
|
||||
bool IsInstanceContainerEntity(AZ::EntityId entityId) const override;
|
||||
bool IsLevelInstanceContainerEntity(AZ::EntityId entityId) const override;
|
||||
AZ::EntityId GetInstanceContainerEntityId(AZ::EntityId entityId) const override;
|
||||
AZ::EntityId GetLevelInstanceContainerEntityId() const override;
|
||||
AZ::IO::Path GetOwningInstancePrefabPath(AZ::EntityId entityId) const override;
|
||||
PrefabRequestResult HasUnsavedChanges(AZ::IO::Path prefabFilePath) const override;
|
||||
|
||||
PrefabOperationResult DeleteEntitiesInInstance(const EntityIdList& entityIds) override;
|
||||
PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) override;
|
||||
|
||||
private:
|
||||
|
||||
PrefabOperationResult DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants);
|
||||
bool RetrieveAndSortPrefabEntitiesAndInstances(const EntityList& inputEntities, const Instance& commonRootEntityOwningInstance,
|
||||
EntityList& outEntities, AZStd::vector<AZStd::unique_ptr<Instance>>& outInstances) const;
|
||||
|
||||
//! Gets the owning instance of a valid commonRootEntity and the root prefab instance for an invalid commonRootEntity.
|
||||
InstanceOptionalReference GetCommonRootEntityOwningInstance(AZ::EntityId entityId);
|
||||
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
|
||||
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
|
||||
|
||||
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;
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
|
||||
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||
|
||||
// Caches entity states for undo/redo purposes
|
||||
PrefabUndoCache m_prefabUndoCache;
|
||||
|
||||
uint64_t m_newEntityCounter = 1;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,14 +13,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
using EntityIdList = AZStd::vector<AZ::EntityId>;
|
||||
|
||||
namespace UndoSystem
|
||||
{
|
||||
class URSequencePoint;
|
||||
}
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
typedef AZ::Outcome<void, AZStd::string> PrefabOperationResult;
|
||||
typedef AZ::Outcome<bool, AZStd::string> PrefabRequestResult;
|
||||
typedef AZ::Outcome<AZ::EntityId, AZStd::string> PrefabEntityResult;
|
||||
|
||||
/*!
|
||||
* PrefabPublicInterface
|
||||
@@ -49,22 +59,91 @@ namespace AzToolsFramework
|
||||
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
|
||||
*/
|
||||
virtual PrefabOperationResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, AZ::Vector3 position) = 0;
|
||||
|
||||
/**
|
||||
* Saves changes to prefab to disk.
|
||||
* @param filePath The path to the prefab to save.
|
||||
* @return Returns Success if the file was saved, or an error message otherwise.
|
||||
*/
|
||||
virtual PrefabOperationResult SavePrefab(AZ::IO::Path filePath) = 0;
|
||||
|
||||
/**
|
||||
* Creates a new entity under the entity with id 'parentId' and propagates a change to the template
|
||||
* of the owning instance of parentId.
|
||||
*
|
||||
* @param parentId The id of the parent entity to parent the newly added entity under.
|
||||
* @param position The transform position of the entity being added.
|
||||
* @return Returns the entityId of the newly created entity, or an error message if the operation failed.
|
||||
*/
|
||||
virtual PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) = 0;
|
||||
|
||||
/**
|
||||
* Store the changes between the current entity state and its last cached state into undo/redo commands.
|
||||
* These changes are stored as patches to the owning prefab instance template, as appropriate.
|
||||
* The function also triggers the redo() of the nodes it creates, triggering propagation on the next tick.
|
||||
*
|
||||
* @param entityId The entity to patch.
|
||||
* @param parentUndoBatch The undo batch the undo nodes should be parented to.
|
||||
*/
|
||||
virtual void GenerateUndoNodesForEntityChangeAndUpdateCache(
|
||||
AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch) = 0;
|
||||
|
||||
/**
|
||||
* Detects if an entity is the container entity for its owning prefab instance.
|
||||
* @param entityId The entity to query.
|
||||
* @return True if the entity is the container entity for its owning prefab instance, false otherwise.
|
||||
*/
|
||||
virtual bool IsInstanceContainerEntity(AZ::EntityId entityId) = 0;
|
||||
virtual bool IsInstanceContainerEntity(AZ::EntityId entityId) const = 0;
|
||||
|
||||
/**
|
||||
* Detects if an entity is the container entity for the level prefab instance.
|
||||
* @param entityId The entity to query.
|
||||
* @return True if the entity is the container entity for the level prefab instance, false otherwise.
|
||||
*/
|
||||
virtual bool IsLevelInstanceContainerEntity(AZ::EntityId entityId) const = 0;
|
||||
|
||||
/**
|
||||
* Gets the entity id for the instance container of the owning instance.
|
||||
* @param entityId The id of the entity to query.
|
||||
* @return The entity id of the instance container owning the queried entity.
|
||||
*/
|
||||
virtual AZ::EntityId GetInstanceContainerEntityId(AZ::EntityId entityId) = 0;
|
||||
};
|
||||
virtual AZ::EntityId GetInstanceContainerEntityId(AZ::EntityId entityId) const = 0;
|
||||
|
||||
/**
|
||||
* Gets the entity id for the instance container of the level instance.
|
||||
* @return The entity id of the instance container for the currently loaded level.
|
||||
*/
|
||||
virtual AZ::EntityId GetLevelInstanceContainerEntityId() const = 0;
|
||||
|
||||
/**
|
||||
* Get the file path to the prefab file for the prefab instance owning the entity provided.
|
||||
* @param entityId The id for the entity being queried.
|
||||
* @return Returns the path to the prefab, or an empty path if the entity is owned by the level.
|
||||
*/
|
||||
virtual AZ::IO::Path GetOwningInstancePrefabPath(AZ::EntityId entityId) const = 0;
|
||||
|
||||
/**
|
||||
* Gets whether the prefab has unsaved changes.
|
||||
* @param filePath The path to the prefab to query.
|
||||
* @return Returns true if the prefab has unsaved changes, false otherwise. If path is invalid, returns an error message.
|
||||
*/
|
||||
virtual PrefabRequestResult HasUnsavedChanges(AZ::IO::Path prefabFilePath) const = 0;
|
||||
|
||||
/**
|
||||
* Deletes all entities from the owning instance. Bails if the entities don't all belong to the same instance.
|
||||
* @param entities The entities to delete.
|
||||
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
|
||||
*/
|
||||
virtual PrefabOperationResult DeleteEntitiesInInstance(const EntityIdList& entityIds) = 0;
|
||||
|
||||
/**
|
||||
* Deletes all entities and their descendants from the owning instance. Bails if the entities don't all belong to the same
|
||||
* instance.
|
||||
* @param entities The entities to delete. Their descendants will be discovered by this function.
|
||||
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
|
||||
*/
|
||||
virtual PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
|
||||
@@ -21,6 +22,7 @@
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -36,10 +38,14 @@ namespace AzToolsFramework
|
||||
m_prefabLoader.RegisterPrefabLoaderInterface();
|
||||
m_instanceUpdateExecutor.RegisterInstanceUpdateExecutorInterface();
|
||||
m_instanceToTemplatePropagator.RegisterInstanceToTemplateInterface();
|
||||
m_prefabPublicHandler.RegisterPrefabPublicHandlerInterface();
|
||||
AZ::SystemTickBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::Deactivate()
|
||||
{
|
||||
AZ::SystemTickBus::Handler::BusDisconnect();
|
||||
m_prefabPublicHandler.UnregisterPrefabPublicHandlerInterface();
|
||||
m_instanceToTemplatePropagator.UnregisterInstanceToTemplateInterface();
|
||||
m_instanceUpdateExecutor.UnregisterInstanceUpdateExecutorInterface();
|
||||
m_prefabLoader.UnregisterPrefabLoaderInterface();
|
||||
@@ -80,18 +86,26 @@ namespace AzToolsFramework
|
||||
{
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Instance> PrefabSystemComponent::CreatePrefab(const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume, const AZStd::string& filePath)
|
||||
void PrefabSystemComponent::OnSystemTick()
|
||||
{
|
||||
if (GetTemplateIdFromFilePath(filePath) != InvalidTemplateId)
|
||||
m_instanceUpdateExecutor.UpdateTemplateInstancesInQueue();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Instance> PrefabSystemComponent::CreatePrefab(const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
|
||||
AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity)
|
||||
{
|
||||
AZ::IO::Path relativeFilePath = m_prefabLoader.GetRelativePathToProject(filePath);
|
||||
if (GetTemplateIdFromFilePath(relativeFilePath) != InvalidTemplateId)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"Filepath %s has already been registered with the Prefab System Component",
|
||||
filePath.c_str());
|
||||
relativeFilePath.c_str());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Instance> newInstance{ aznew Instance() };
|
||||
|
||||
AZStd::unique_ptr<Instance> newInstance = AZStd::make_unique<Instance>(AZStd::move(containerEntity));
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
@@ -107,14 +121,14 @@ namespace AzToolsFramework
|
||||
newInstance->AddInstance(AZStd::move(instance));
|
||||
}
|
||||
|
||||
newInstance->SetTemplateSourcePath(filePath);
|
||||
newInstance->SetTemplateSourcePath(relativeFilePath);
|
||||
|
||||
TemplateId newTemplateId = CreateTemplateFromInstance(*newInstance);
|
||||
if (newTemplateId == InvalidTemplateId)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"Failed to create a Template associated with file path %s during CreatePrefab.",
|
||||
filePath.c_str());
|
||||
relativeFilePath.c_str());
|
||||
|
||||
newInstance = nullptr;
|
||||
}
|
||||
@@ -154,10 +168,6 @@ namespace AzToolsFramework
|
||||
void PrefabSystemComponent::UpdatePrefabInstances(const TemplateId& templateId)
|
||||
{
|
||||
m_instanceUpdateExecutor.AddTemplateInstancesToQueue(templateId);
|
||||
const bool updateResult = m_instanceUpdateExecutor.UpdateTemplateInstancesInQueue();
|
||||
AZ_Assert(updateResult,
|
||||
"Prefab - Error occurred while updating Instances of Template with id '%llu'.",
|
||||
templateId);
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::UpdateLinkedInstances(AZStd::queue<LinkIds>& linkIdsQueue)
|
||||
@@ -255,24 +265,26 @@ namespace AzToolsFramework
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Instance* newInstance = aznew Instance();
|
||||
if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*newInstance, instantiatingTemplate->get().GetPrefabDom(), false))
|
||||
auto newInstance = AZStd::make_unique<Instance>();
|
||||
Instance::EntityList newEntities;
|
||||
if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*newInstance, newEntities, instantiatingTemplate->get().GetPrefabDom()))
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"Failed to Load Prefab Template associated with path %s. Instantiation Failed",
|
||||
instantiatingTemplate->get().GetFilePath().c_str());
|
||||
|
||||
delete newInstance;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AZStd::unique_ptr<Instance>(newInstance);
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, newEntities);
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
TemplateId PrefabSystemComponent::CreateTemplateFromInstance(Instance& instance)
|
||||
{
|
||||
// We will register the template to match the path the instance has
|
||||
const AZStd::string& templateSourcePath = instance.GetTemplateSourcePath();
|
||||
const AZ::IO::Path& templateSourcePath = instance.GetTemplateSourcePath();
|
||||
if (templateSourcePath.empty())
|
||||
{
|
||||
AZ_Assert(false,
|
||||
@@ -290,7 +302,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
// Generate a new template and store the dom data
|
||||
const AZStd::string& instanceSourcePath = instance.GetTemplateSourcePath();
|
||||
const AZ::IO::Path& instanceSourcePath = instance.GetTemplateSourcePath();
|
||||
TemplateId newTemplateId = AddTemplate(instanceSourcePath, AZStd::move(serializedInstance));
|
||||
if (newTemplateId == InvalidTemplateId)
|
||||
{
|
||||
@@ -354,7 +366,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
TemplateId PrefabSystemComponent::AddTemplate(const AZStd::string& filePath, PrefabDom prefabDom)
|
||||
TemplateId PrefabSystemComponent::AddTemplate(const AZ::IO::Path& filePath, PrefabDom prefabDom)
|
||||
{
|
||||
TemplateId newTemplateId = CreateUniqueTemplateId();
|
||||
Template& newTemplate = m_templateIdMap.emplace(
|
||||
@@ -378,6 +390,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
m_templateFilePathToIdMap.emplace(AZStd::make_pair(filePath, newTemplateId));
|
||||
newTemplate.MarkAsDirty(true);
|
||||
|
||||
return newTemplateId;
|
||||
}
|
||||
@@ -451,6 +464,23 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::RemoveAllTemplates()
|
||||
{
|
||||
AZStd::vector<TemplateId> templateIds;
|
||||
templateIds.reserve(m_templateIdMap.size());
|
||||
|
||||
// Make a copy of the keys, we don't want to iterate over the map while we're removing items from it
|
||||
for (const auto& [id, templateObject] : m_templateIdMap)
|
||||
{
|
||||
templateIds.emplace_back(id);
|
||||
}
|
||||
|
||||
for (auto id : templateIds)
|
||||
{
|
||||
RemoveTemplate(id);
|
||||
}
|
||||
}
|
||||
|
||||
LinkId PrefabSystemComponent::AddLink(
|
||||
const TemplateId& sourceTemplateId,
|
||||
const TemplateId& targetTemplateId,
|
||||
@@ -472,8 +502,8 @@ namespace AzToolsFramework
|
||||
Template& targetTemplate = targetTemplateReference->get();
|
||||
|
||||
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
|
||||
const AZStd::string& targetTemplateFilePath = targetTemplate.GetFilePath();
|
||||
const AZStd::string& sourceTemplateFilePath = sourceTemplate.GetFilePath();
|
||||
const AZStd::string& targetTemplateFilePath = targetTemplate.GetFilePath().Native();
|
||||
const AZStd::string& sourceTemplateFilePath = sourceTemplate.GetFilePath().Native();
|
||||
|
||||
LinkId newLinkId = CreateUniqueLinkId();
|
||||
Link newLink(newLinkId);
|
||||
@@ -518,6 +548,7 @@ namespace AzToolsFramework
|
||||
const TemplateId& linkTargetId,
|
||||
const TemplateId& linkSourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
const PrefabDomReference linkPatch,
|
||||
const LinkId& linkId)
|
||||
{
|
||||
if (linkTargetId == InvalidTemplateId)
|
||||
@@ -551,12 +582,6 @@ namespace AzToolsFramework
|
||||
newLinkId = CreateUniqueLinkId();
|
||||
}
|
||||
|
||||
//setup initial link values
|
||||
Link newLink(newLinkId);
|
||||
newLink.SetTargetTemplateId(linkTargetId);
|
||||
newLink.SetSourceTemplateId(linkSourceId);
|
||||
newLink.SetInstanceName(instanceAlias.c_str());
|
||||
|
||||
//get owner template and add the link
|
||||
Template& targetTemplate = targetTemplateRef->get();
|
||||
|
||||
@@ -590,12 +615,22 @@ namespace AzToolsFramework
|
||||
|
||||
instancesValue->get().AddMember(rapidjson::StringRef(instanceAlias.c_str()), PrefabDomValue(), targetTemplateDom.GetAllocator());
|
||||
|
||||
//setup the link dom
|
||||
Template& sourceTemplate = sourceTemplateRef->get();
|
||||
|
||||
//setup initial link values and link dom
|
||||
Link newLink(newLinkId);
|
||||
newLink.SetTargetTemplateId(linkTargetId);
|
||||
newLink.SetSourceTemplateId(linkSourceId);
|
||||
newLink.SetInstanceName(instanceAlias.c_str());
|
||||
newLink.GetLinkDom().SetObject();
|
||||
newLink.GetLinkDom().AddMember(rapidjson::StringRef(PrefabDomUtils::SourceName),
|
||||
rapidjson::StringRef(sourceTemplate.GetFilePath().c_str()), newLink.GetLinkDom().GetAllocator());
|
||||
|
||||
if (linkPatch && linkPatch->get().IsArray() && !(linkPatch->get().Empty()))
|
||||
{
|
||||
m_instanceToTemplatePropagator.AddPatchesToLink(linkPatch.value(), newLink);
|
||||
}
|
||||
|
||||
//update the target template dom to have the proper values for the source template dom
|
||||
if (!newLink.UpdateTarget())
|
||||
{
|
||||
@@ -643,7 +678,7 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
TemplateId PrefabSystemComponent::GetTemplateIdFromFilePath(AZStd::string_view filePath) const
|
||||
TemplateId PrefabSystemComponent::GetTemplateIdFromFilePath(AZ::IO::PathView filePath) const
|
||||
{
|
||||
auto found = m_templateFilePathToIdMap.find(filePath);
|
||||
if (found != m_templateFilePathToIdMap.end())
|
||||
@@ -656,6 +691,28 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabSystemComponent::IsTemplateDirty(const TemplateId& templateId)
|
||||
{
|
||||
auto templateRef = FindTemplate(templateId);
|
||||
|
||||
if (templateRef.has_value())
|
||||
{
|
||||
return templateRef->get().IsDirty();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty)
|
||||
{
|
||||
auto templateRef = FindTemplate(templateId);
|
||||
|
||||
if (templateRef.has_value())
|
||||
{
|
||||
templateRef->get().MarkAsDirty(dirty);
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabSystemComponent::ConnectTemplates(
|
||||
Link& link,
|
||||
TemplateId sourceTemplateId,
|
||||
@@ -677,7 +734,7 @@ namespace AzToolsFramework
|
||||
Template& targetTemplate = targetTemplateReference->get();
|
||||
|
||||
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
|
||||
const AZStd::string& targetTemplateFilePath = targetTemplate.GetFilePath();
|
||||
const AZStd::string& targetTemplateFilePath = targetTemplate.GetFilePath().Native();
|
||||
|
||||
link.SetSourceTemplateId(sourceTemplateId);
|
||||
link.SetTargetTemplateId(targetTemplateId);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
@@ -30,10 +31,13 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
namespace AZ
|
||||
{
|
||||
class Entity;
|
||||
} // namespace AZ
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
using EntityList = AZStd::vector<AZ::Entity*>;
|
||||
|
||||
namespace Prefab
|
||||
@@ -49,6 +53,7 @@ namespace AzToolsFramework
|
||||
class PrefabSystemComponent
|
||||
: public AZ::Component
|
||||
, private PrefabSystemComponentInterface
|
||||
, private AZ::SystemTickBus::Handler
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -72,6 +77,9 @@ namespace AzToolsFramework
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
|
||||
// SystemTickBus...
|
||||
void OnSystemTick() override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PrefabSystemComponentInterface interface implementation
|
||||
/**
|
||||
@@ -94,7 +102,7 @@ namespace AzToolsFramework
|
||||
* @param prefabDom A Prefab DOM presenting this Template.
|
||||
* @return A unique id for the new Template.
|
||||
*/
|
||||
TemplateId AddTemplate(const AZStd::string& filePath, PrefabDom prefabDom) override;
|
||||
TemplateId AddTemplate(const AZ::IO::Path& filePath, PrefabDom prefabDom) override;
|
||||
|
||||
/**
|
||||
* Remove the Template associated with the given id from Prefab System Component.
|
||||
@@ -102,6 +110,11 @@ namespace AzToolsFramework
|
||||
*/
|
||||
void RemoveTemplate(const TemplateId& templateId) override;
|
||||
|
||||
/**
|
||||
* Remove all Templates from the Prefab System Component.
|
||||
*/
|
||||
void RemoveAllTemplates() override;
|
||||
|
||||
/**
|
||||
* Generates a new Prefab Instance based on the Template referenced by templateId
|
||||
* @param templateId the id of the template being instantiated
|
||||
@@ -136,6 +149,7 @@ namespace AzToolsFramework
|
||||
const TemplateId& linkTargetId,
|
||||
const TemplateId& linkSourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
const PrefabDomReference linkPatch,
|
||||
const LinkId& linkId = InvalidLinkId) override;
|
||||
|
||||
/**
|
||||
@@ -149,7 +163,22 @@ namespace AzToolsFramework
|
||||
* @param filePath A path to a Prefab Template file.
|
||||
* @return A unique id of Template on filePath. Return InvalidTemplateId if Template on filePath doesn't exist.
|
||||
*/
|
||||
TemplateId GetTemplateIdFromFilePath(AZStd::string_view filePath) const override;
|
||||
TemplateId GetTemplateIdFromFilePath(AZ::IO::PathView filePath) const override;
|
||||
|
||||
/**
|
||||
* Gets the value of the dirty flag of the template with the id provided.
|
||||
* @param templateId The id of the template to query.
|
||||
* @return The value of the dirty flag on the template.
|
||||
*/
|
||||
bool IsTemplateDirty(const TemplateId& templateId) override;
|
||||
|
||||
/**
|
||||
* Sets the dirty flag of the template to the value provided.
|
||||
* @param templateId The id of the template to flag.
|
||||
* @param dirty The new value of the dirty flag.
|
||||
*/
|
||||
void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@@ -161,8 +190,8 @@ namespace AzToolsFramework
|
||||
* @param filePath the path to associate the template of the new instance to
|
||||
* @return A pointer to the newly created instance. nullptr on failure
|
||||
*/
|
||||
AZStd::unique_ptr<Instance> CreatePrefab(const AZStd::vector<AZ::Entity*> & entities,
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume, const AZStd::string& filePath) override;
|
||||
AZStd::unique_ptr<Instance> CreatePrefab(const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
|
||||
AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr) override;
|
||||
|
||||
PrefabDom& FindTemplateDom(TemplateId templateId) override;
|
||||
|
||||
@@ -302,7 +331,7 @@ namespace AzToolsFramework
|
||||
AZStd::unordered_map<TemplateId, Template> m_templateIdMap;
|
||||
|
||||
// A container for mapping Templates' file paths to their Template ids.
|
||||
AZStd::unordered_map<AZStd::string, TemplateId> m_templateFilePathToIdMap;
|
||||
AZStd::unordered_map<AZ::IO::Path, TemplateId> m_templateFilePathToIdMap;
|
||||
|
||||
// A container of Prefab Links mapped by their Link ids.
|
||||
AZStd::unordered_map<LinkId, Link> m_linkIdMap;
|
||||
|
||||
+10
-4
@@ -35,19 +35,24 @@ namespace AzToolsFramework
|
||||
virtual TemplateReference FindTemplate(const TemplateId& id) = 0;
|
||||
virtual LinkReference FindLink(const LinkId& id) = 0;
|
||||
|
||||
virtual TemplateId AddTemplate(const AZStd::string& filePath, PrefabDom prefabDom) = 0;
|
||||
virtual TemplateId AddTemplate(const AZ::IO::Path& filePath, PrefabDom prefabDom) = 0;
|
||||
virtual void RemoveTemplate(const TemplateId& templateId) = 0;
|
||||
virtual void RemoveAllTemplates() = 0;
|
||||
|
||||
virtual LinkId AddLink(const TemplateId& sourceTemplateId, const TemplateId& targetTemplateId,
|
||||
PrefabDomValue::MemberIterator& instanceIterator, InstanceOptionalReference instance) = 0;
|
||||
|
||||
//creates a new Link
|
||||
virtual LinkId CreateLink(const TemplateId& linkTargetId, const TemplateId& linkSourceId,
|
||||
const InstanceAlias& instanceAlias, const LinkId& linkId = InvalidLinkId) = 0;
|
||||
const InstanceAlias& instanceAlias, const PrefabDomReference linkPatch,
|
||||
const LinkId& linkId = InvalidLinkId) = 0;
|
||||
|
||||
virtual void RemoveLink(const LinkId& linkId) = 0;
|
||||
|
||||
virtual TemplateId GetTemplateIdFromFilePath(AZStd::string_view filePath) const = 0;
|
||||
virtual TemplateId GetTemplateIdFromFilePath(AZ::IO::PathView filePath) const = 0;
|
||||
|
||||
virtual bool IsTemplateDirty(const TemplateId& templateId) = 0;
|
||||
virtual void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) = 0;
|
||||
|
||||
virtual PrefabDom& FindTemplateDom(TemplateId templateId) = 0;
|
||||
virtual void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) = 0;
|
||||
@@ -55,7 +60,8 @@ namespace AzToolsFramework
|
||||
|
||||
virtual AZStd::unique_ptr<Instance> InstantiatePrefab(const TemplateId& templateId) = 0;
|
||||
virtual AZStd::unique_ptr<Instance> CreatePrefab(const AZStd::vector<AZ::Entity*>& entities,
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume, const AZStd::string& filePath) = 0;
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume, AZ::IO::PathView filePath,
|
||||
AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
|
||||
#include <Prefab/PrefabUndo.h>
|
||||
#include <Prefab/PrefabDomUtils.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -28,7 +28,6 @@ namespace AzToolsFramework
|
||||
AZ_Assert(m_instanceToTemplateInterface, "Failed to grab instance to template interface");
|
||||
}
|
||||
|
||||
|
||||
//PrefabInstanceUndo
|
||||
PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName)
|
||||
: PrefabUndoBase(undoOperationName)
|
||||
@@ -85,12 +84,22 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabUndoEntityUpdate::Undo()
|
||||
{
|
||||
m_instanceToTemplateInterface->PatchEntityInTemplate(m_undoPatch, m_entityAlias, m_templateId);
|
||||
bool isPatchApplicationSuccessful =
|
||||
m_instanceToTemplateInterface->PatchEntityInTemplate(m_undoPatch, m_entityAlias, 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(),
|
||||
m_templateId);
|
||||
}
|
||||
|
||||
void PrefabUndoEntityUpdate::Redo()
|
||||
{
|
||||
m_instanceToTemplateInterface->PatchEntityInTemplate(m_redoPatch, m_entityAlias, m_templateId);
|
||||
bool isPatchApplicationSuccessful =
|
||||
m_instanceToTemplateInterface->PatchEntityInTemplate(m_redoPatch, m_entityAlias, 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(),
|
||||
m_templateId);
|
||||
}
|
||||
|
||||
//PrefabInstanceLinkUndo
|
||||
@@ -100,7 +109,7 @@ namespace AzToolsFramework
|
||||
, m_sourceId(InvalidTemplateId)
|
||||
, m_instanceAlias("")
|
||||
, m_linkId(InvalidLinkId)
|
||||
, m_link(Link())
|
||||
, m_linkDom(PrefabDom())
|
||||
, m_linkStatus(LinkStatus::LINKSTATUS)
|
||||
{
|
||||
m_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
@@ -111,14 +120,18 @@ namespace AzToolsFramework
|
||||
const TemplateId& targetId,
|
||||
const TemplateId& sourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
const LinkId& linkId,
|
||||
const Link& link)
|
||||
const PrefabDomReference linkDom,
|
||||
const LinkId linkId)
|
||||
{
|
||||
m_targetId = targetId;
|
||||
m_sourceId = sourceId;
|
||||
m_instanceAlias = instanceAlias;
|
||||
m_linkId = linkId;
|
||||
m_link = link;
|
||||
|
||||
if (linkDom.has_value())
|
||||
{
|
||||
m_linkDom = AZStd::move(linkDom->get());
|
||||
}
|
||||
|
||||
//if linkId is invalid, set as ADD
|
||||
if (m_linkId == InvalidLinkId)
|
||||
@@ -169,21 +182,133 @@ namespace AzToolsFramework
|
||||
m_prefabSystemComponentInterface->PropagateTemplateChanges(m_targetId);
|
||||
}
|
||||
|
||||
LinkId PrefabUndoInstanceLink::GetLinkId()
|
||||
{
|
||||
return m_linkId;
|
||||
}
|
||||
|
||||
void PrefabUndoInstanceLink::AddLink()
|
||||
{
|
||||
m_linkId = m_prefabSystemComponentInterface->CreateLink(m_targetId, m_sourceId, m_instanceAlias, m_linkId);
|
||||
|
||||
//if data already exists, repopulate
|
||||
if (m_linkStatus == LinkStatus::REMOVE)
|
||||
{
|
||||
LinkReference link = m_prefabSystemComponentInterface->FindLink(m_linkId);
|
||||
link = m_link;
|
||||
}
|
||||
m_linkId = m_prefabSystemComponentInterface->CreateLink(m_targetId, m_sourceId, m_instanceAlias, m_linkDom, m_linkId);
|
||||
}
|
||||
|
||||
void PrefabUndoInstanceLink::RemoveLink()
|
||||
{
|
||||
m_prefabSystemComponentInterface->RemoveLink(m_linkId);
|
||||
}
|
||||
|
||||
//PrefabUndoLinkUpdate
|
||||
PrefabUndoLinkUpdate::PrefabUndoLinkUpdate(const AZStd::string& undoOperationName)
|
||||
: PrefabUndoBase(undoOperationName)
|
||||
, m_linkId(InvalidLinkId)
|
||||
, m_linkDomNext(PrefabDom())
|
||||
, m_linkDomPrevious(PrefabDom())
|
||||
{
|
||||
m_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
AZ_Assert(m_instanceToTemplateInterface, "Failed to grab interface");
|
||||
}
|
||||
|
||||
void PrefabUndoLinkUpdate::Capture(
|
||||
const PrefabDom& patch,
|
||||
const LinkId linkId)
|
||||
{
|
||||
m_linkId = linkId;
|
||||
|
||||
//acquire link and existing values
|
||||
LinkReference link = m_prefabSystemComponentInterface->FindLink(m_linkId);
|
||||
if (link == AZStd::nullopt)
|
||||
{
|
||||
AZ_Error("Prefab", false, "PrefabUndoLinkUpdate: Link not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (link.has_value())
|
||||
{
|
||||
m_linkDomPrevious = AZStd::move(link->get().GetLinkDom());
|
||||
}
|
||||
|
||||
//get source templateDom
|
||||
TemplateReference sourceTemplate = m_prefabSystemComponentInterface->FindTemplate(link->get().GetSourceTemplateId());
|
||||
|
||||
if (sourceTemplate == AZStd::nullopt)
|
||||
{
|
||||
AZ_Error("Prefab", false, "PrefabUndoLinkUpdate: Source template not found");
|
||||
return;
|
||||
}
|
||||
|
||||
PrefabDomReference sourceDom = sourceTemplate->get().GetPrefabDom();
|
||||
|
||||
//use instance pointer to reach position
|
||||
PrefabDomValueReference instanceDomRef = link->get().GetLinkedInstanceDom();
|
||||
|
||||
//copy the target instance the link is pointing to
|
||||
PrefabDom instanceDom;
|
||||
instanceDom.CopyFrom(instanceDomRef->get(), instanceDom.GetAllocator());
|
||||
|
||||
//apply the patch to the template within the target
|
||||
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::ApplyPatch(instanceDom,
|
||||
instanceDom.GetAllocator(), patch, AZ::JsonMergeApproach::JsonPatch);
|
||||
|
||||
//remove the link id placed into the instance
|
||||
auto linkIdIter = instanceDom.FindMember(PrefabDomUtils::LinkIdName);
|
||||
if (linkIdIter != instanceDom.MemberEnd())
|
||||
{
|
||||
instanceDom.RemoveMember(PrefabDomUtils::LinkIdName);
|
||||
}
|
||||
|
||||
//we use this to diff our copy against the vanilla template (source template)
|
||||
PrefabDom patchLink;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patchLink, sourceDom->get(), instanceDom);
|
||||
|
||||
// Create a copy of patchLink by providing the allocator of m_linkDomNext so that the patch doesn't become invalid when
|
||||
// the patch goes out of scope in this function.
|
||||
PrefabDom patchLinkCopy;
|
||||
patchLinkCopy.CopyFrom(patchLink, m_linkDomNext.GetAllocator());
|
||||
|
||||
m_linkDomNext.CopyFrom(m_linkDomPrevious, m_linkDomNext.GetAllocator());
|
||||
auto patchesIter = m_linkDomNext.FindMember(PrefabDomUtils::PatchesName);
|
||||
|
||||
if (patchesIter == m_linkDomNext.MemberEnd())
|
||||
{
|
||||
m_linkDomNext.AddMember(
|
||||
rapidjson::GenericStringRef(PrefabDomUtils::PatchesName), patchLinkCopy, m_linkDomNext.GetAllocator());
|
||||
}
|
||||
else
|
||||
{
|
||||
patchesIter->value = AZStd::move(patchLinkCopy.GetArray());
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabUndoLinkUpdate::Undo()
|
||||
{
|
||||
UpdateLink(m_linkDomPrevious);
|
||||
}
|
||||
|
||||
void PrefabUndoLinkUpdate::Redo()
|
||||
{
|
||||
UpdateLink(m_linkDomNext);
|
||||
}
|
||||
|
||||
void PrefabUndoLinkUpdate::UpdateLink(PrefabDom& linkDom)
|
||||
{
|
||||
LinkReference link = m_prefabSystemComponentInterface->FindLink(m_linkId);
|
||||
|
||||
if (link == AZStd::nullopt)
|
||||
{
|
||||
AZ_Error("Prefab", false, "PrefabUndoLinkUpdate: Link not found");
|
||||
return;
|
||||
}
|
||||
|
||||
PrefabDom moveLink;
|
||||
moveLink.CopyFrom(linkDom, linkDom.GetAllocator());
|
||||
link->get().GetLinkDom() = AZStd::move(moveLink);
|
||||
|
||||
//propagate the link changes
|
||||
link->get().UpdateTarget();
|
||||
m_prefabSystemComponentInterface->PropagateTemplateChanges(link->get().GetTargetTemplateId());
|
||||
|
||||
//mark as dirty
|
||||
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(link->get().GetTargetTemplateId(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,9 @@ namespace AzToolsFramework
|
||||
: public PrefabUndoBase
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(PrefabUndoEntityUpdate, "{6D60C5A6-9535-45B3-8897-E5F6382FDC93}", PrefabUndoBase);
|
||||
AZ_CLASS_ALLOCATOR(PrefabUndoEntityUpdate, AZ::SystemAllocator, 0);
|
||||
|
||||
explicit PrefabUndoEntityUpdate(const AZStd::string& undoOperationName);
|
||||
|
||||
void Capture(
|
||||
@@ -88,7 +91,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
ADD,
|
||||
REMOVE,
|
||||
UPDATE,
|
||||
LINKSTATUS
|
||||
};
|
||||
|
||||
@@ -99,12 +101,14 @@ namespace AzToolsFramework
|
||||
const TemplateId& targetId,
|
||||
const TemplateId& sourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
const LinkId& linkId = InvalidLinkId,
|
||||
const Link& link = Link());
|
||||
const PrefabDomReference linkDom = PrefabDomReference(),
|
||||
const LinkId linkId = InvalidLinkId);
|
||||
|
||||
void Undo() override;
|
||||
void Redo() override;
|
||||
|
||||
LinkId GetLinkId();
|
||||
|
||||
private:
|
||||
//used for special cases of add/delete
|
||||
void AddLink();
|
||||
@@ -116,10 +120,35 @@ namespace AzToolsFramework
|
||||
InstanceAlias m_instanceAlias;
|
||||
|
||||
LinkId m_linkId;
|
||||
Link m_link; //data for delete/update
|
||||
PrefabDom m_linkDom; //data for delete/update
|
||||
LinkStatus m_linkStatus;
|
||||
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||
};
|
||||
|
||||
class PrefabUndoLinkUpdate
|
||||
: public PrefabUndoBase
|
||||
{
|
||||
public:
|
||||
explicit PrefabUndoLinkUpdate(const AZStd::string& undoOperationName);
|
||||
|
||||
//capture for add/remove
|
||||
void Capture(
|
||||
const PrefabDom& patch,
|
||||
const LinkId linkId = InvalidLinkId);
|
||||
|
||||
void Undo() override;
|
||||
void Redo() override;
|
||||
|
||||
private:
|
||||
void UpdateLink(PrefabDom& linkDom);
|
||||
|
||||
LinkId m_linkId;
|
||||
PrefabDom m_linkDomNext; //data for delete/update
|
||||
PrefabDom m_linkDomPrevious; //stores the data for undo
|
||||
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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/PrefabUndoCache.h>
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
void PrefabUndoCache::Initialize()
|
||||
{
|
||||
m_instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
|
||||
AZ_Assert(m_instanceToTemplateInterface, "PrefabUndoCache - Could not retrieve instance of InstanceToTemplateInterface");
|
||||
|
||||
m_instanceEntityMapperInterface = AZ::Interface<InstanceEntityMapperInterface>::Get();
|
||||
AZ_Assert(m_instanceEntityMapperInterface, "PrefabUndoCache - Could not retrieve instance of InstanceEntityMapperInterface");
|
||||
|
||||
bool prefabSystemEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
|
||||
if (prefabSystemEnabled)
|
||||
{
|
||||
// By default, ToolsApplication will register the regular PreemptiveCache as the handler of this interface.
|
||||
// Since the SettingsRegistry isn't active when ToolsApplication is constructed, and Start and StartCommon
|
||||
// aren't called during tests, we have to resort to unregistering the Preemptive cache here, and registering
|
||||
// the PrefabCache in its place. Both caches check if they're registered before unregistering on destroy.
|
||||
auto preemptiveCache = AZ::Interface<UndoSystem::UndoCacheInterface>::Get();
|
||||
if (preemptiveCache)
|
||||
{
|
||||
AZ::Interface<UndoSystem::UndoCacheInterface>::Unregister(preemptiveCache);
|
||||
}
|
||||
AZ::Interface<UndoSystem::UndoCacheInterface>::Register(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Destroy()
|
||||
{
|
||||
if (AZ::Interface<UndoSystem::UndoCacheInterface>::Get() == this)
|
||||
{
|
||||
AZ::Interface<UndoSystem::UndoCacheInterface>::Unregister(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Validate(const AZ::EntityId& entityId)
|
||||
{
|
||||
(void)entityId;
|
||||
|
||||
#if defined(ENABLE_UNDOCACHE_CONSISTENCY_CHECKS)
|
||||
if (entityId == AZ::SystemEntityId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrefabDom oldData;
|
||||
Retrieve(entityId, oldData);
|
||||
|
||||
UpdateCache(entityId);
|
||||
|
||||
PrefabDom newData;
|
||||
Retrieve(entityId, newData);
|
||||
|
||||
if (newData != oldData)
|
||||
{
|
||||
// display a useful message
|
||||
AZ::Entity* entity = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId);
|
||||
|
||||
if (!entity)
|
||||
{
|
||||
AZ_Warning(
|
||||
"Undo", false,
|
||||
"Undo system wasn't informed about the deletion of entity %p - make sure you call DeleteEntity, instead of "
|
||||
"directly deleting it.\n",
|
||||
entityId);
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_Warning(
|
||||
"Undo", false,
|
||||
"Undo system has inconsistent data for entity %p (%s)\n Ensure that SetDirty is "
|
||||
"being called (or WorldEditor::WorldEditorMessages::Bus, AddDirtyEntity...) for modified entities.",
|
||||
entityId, entity->GetName().c_str());
|
||||
}
|
||||
|
||||
// Clear out newly generated data and
|
||||
// replace with original data to ensure debug mode has the same data as profile/release
|
||||
// in the event of the consistency check failing.
|
||||
m_entitySavedStates[entityId] = AZStd::move(oldData);
|
||||
|
||||
#endif // ENABLE_UNDOCACHE_CONSISTENCY_CHECKS
|
||||
}
|
||||
|
||||
void PrefabUndoCache::UpdateCache(const AZ::EntityId& entityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
AZ::Entity* entity = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId);
|
||||
|
||||
if (!entity)
|
||||
{
|
||||
AZ_Warning(
|
||||
"Undo", false,
|
||||
"PrefabUndoCache was told to update the cache for entity of id %llu, but that entity is not available in "
|
||||
"FindEntity",
|
||||
static_cast<AZ::u64>(entityId)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
InstanceOptionalReference instanceOptionalReference = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
if (!instanceOptionalReference.has_value())
|
||||
{
|
||||
AZ_Warning(
|
||||
"Undo", false,
|
||||
"PrefabUndoCache was told to update the cache for entity of id %p (%s), but that entity does not have an owning instance.",
|
||||
entityId, entity->GetName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Capture it
|
||||
PrefabDom entityDom;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(entityDom, *entity);
|
||||
m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(entityDom)));
|
||||
|
||||
AZLOG("Prefab Undo", "Correctly updated cache for entity of id %llu (%s)", static_cast<AZ::u64>(entityId), entity->GetName().c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void PrefabUndoCache::PurgeCache(const AZ::EntityId& entityId)
|
||||
{
|
||||
m_entitySavedStates.erase(entityId);
|
||||
}
|
||||
|
||||
bool PrefabUndoCache::Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom)
|
||||
{
|
||||
auto it = m_entitySavedStates.find(entityId);
|
||||
|
||||
if (it == m_entitySavedStates.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
outDom = AZStd::move(m_entitySavedStates[entityId]);
|
||||
m_entitySavedStates.erase(entityId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Store(const AZ::EntityId& entityId, PrefabDom&& dom)
|
||||
{
|
||||
m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(dom)));
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Clear()
|
||||
{
|
||||
m_entitySavedStates.clear();
|
||||
}
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace UndoSystem
|
||||
{
|
||||
class URSequencePoint;
|
||||
}
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
class InstanceEntityMapperInterface;
|
||||
class InstanceToTemplateInterface;
|
||||
|
||||
class PrefabUndoCache
|
||||
: UndoSystem::UndoCacheInterface
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabUndoCache, AZ::SystemAllocator, 0);
|
||||
|
||||
void Initialize();
|
||||
void Destroy();
|
||||
|
||||
// UndoCacheInterface...
|
||||
void UpdateCache(const AZ::EntityId& entityId) override;
|
||||
void PurgeCache(const AZ::EntityId& entityId) override;
|
||||
void Clear() override;
|
||||
void Validate(const AZ::EntityId& entityId) override;
|
||||
|
||||
// Retrieve the last known state for an entity
|
||||
bool Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom);
|
||||
|
||||
// Store dom as the cached state of entityId
|
||||
void Store(const AZ::EntityId& entityId, PrefabDom&& dom);
|
||||
|
||||
private:
|
||||
typedef AZStd::unordered_map<AZ::EntityId, PrefabDom> EntityDomMap;
|
||||
EntityDomMap m_entitySavedStates;
|
||||
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
|
||||
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
+30
-14
@@ -40,18 +40,18 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
return;
|
||||
}
|
||||
|
||||
prefabProcessorContext.ListPrefabs([this, &serializeContext, &prefabProcessorContext](AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
{
|
||||
auto result = RemoveEditorInfo(prefab, serializeContext, prefabProcessorContext);
|
||||
if (!result)
|
||||
prefabProcessorContext.ListPrefabs(
|
||||
[this, &serializeContext, &prefabProcessorContext](AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"Converting to runtime Prefab '%.*s' failed, Error: %s .",
|
||||
AZ_STRING_ARG(prefabName),
|
||||
result.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
});
|
||||
auto result = RemoveEditorInfo(prefab, serializeContext, prefabProcessorContext);
|
||||
if (!result)
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false, "Converting to runtime Prefab '%.*s' failed, Error: %s .", AZ_STRING_ARG(prefabName),
|
||||
result.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EditorInfoRemover::Reflect(AZ::ReflectContext* context)
|
||||
@@ -74,6 +74,12 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
}
|
||||
);
|
||||
|
||||
if (instance->HasContainerEntity())
|
||||
{
|
||||
auto containerEntityReference = instance->GetContainerEntity();
|
||||
result.emplace_back(&containerEntityReference->get());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -519,7 +525,8 @@ exportComponent, prefabProcessorContext);
|
||||
|
||||
// convert Prefab DOM into Prefab Instance.
|
||||
AZStd::unique_ptr<Instance> instance(aznew Instance());
|
||||
if (!Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*instance, prefab, false))
|
||||
if (!Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*instance, prefab,
|
||||
Prefab::PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId))
|
||||
{
|
||||
PrefabDomValueReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName);
|
||||
|
||||
@@ -613,19 +620,28 @@ exportComponent, prefabProcessorContext);
|
||||
[&exportEntitiesMap](AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
auto entityId = entity->GetId();
|
||||
entity.release();
|
||||
entity.reset(exportEntitiesMap[entityId]);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
if (instance->HasContainerEntity())
|
||||
{
|
||||
if (auto found = exportEntitiesMap.find(instance->GetContainerEntityId()); found != exportEntitiesMap.end())
|
||||
{
|
||||
instance->SetContainerEntity(*found->second);
|
||||
}
|
||||
}
|
||||
|
||||
// save the final result in the target Prefab DOM.
|
||||
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*instance, prefab))
|
||||
PrefabDom filteredPrefab;
|
||||
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*instance, filteredPrefab))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Saving exported Prefab Instance within a Prefab Dom failed.")
|
||||
);
|
||||
}
|
||||
prefab.Swap(filteredPrefab);
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
+49
-16
@@ -16,6 +16,7 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h>
|
||||
|
||||
@@ -23,9 +24,11 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
void PrefabCatchmentProcessor::Process(PrefabProcessorContext& context)
|
||||
{
|
||||
context.ListPrefabs([&context](AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
AZ::DataStream::StreamType serializationFormat = m_serializationFormat == SerializationFormats::Binary ?
|
||||
AZ::DataStream::StreamType::ST_BINARY : AZ::DataStream::StreamType::ST_XML;
|
||||
context.ListPrefabs([&context, serializationFormat](AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
{
|
||||
ProcessPrefab(context, prefabName, prefab);
|
||||
ProcessPrefab(context, prefabName, prefab, serializationFormat);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,31 +36,61 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<PrefabCatchmentProcessor, PrefabProcessor>()->Version(1);
|
||||
serializeContext->Enum<SerializationFormats>()
|
||||
->Value("Binary", SerializationFormats::Binary)
|
||||
->Value("Text", SerializationFormats::Text);
|
||||
|
||||
serializeContext->Class<PrefabCatchmentProcessor, PrefabProcessor>()
|
||||
->Version(2)
|
||||
->Field("SerializationFormat", &PrefabCatchmentProcessor::m_serializationFormat);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabCatchmentProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
void PrefabCatchmentProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab,
|
||||
AZ::DataStream::StreamType serializationFormat)
|
||||
{
|
||||
|
||||
AZStd::string uniqueName = prefabName;
|
||||
uniqueName += '.';
|
||||
uniqueName += AzFramework::Spawnable::FileExtension;
|
||||
uniqueName += AzFramework::Spawnable::DotFileExtension;
|
||||
|
||||
auto serializer = [](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool
|
||||
auto serializer = [serializationFormat](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool
|
||||
{
|
||||
AZ::IO::ByteContainerStream stream(&output);
|
||||
return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::StreamType::ST_BINARY,
|
||||
AZStd::any_cast<void>(&object.GetObject()), object.GetObject().type());
|
||||
auto& asset = object.GetAsset();
|
||||
return AZ::Utils::SaveObjectToStream(stream, serializationFormat, &asset, asset.GetType());
|
||||
};
|
||||
|
||||
auto spawnable = SpawnableUtils::CreateSpawnable(prefab);
|
||||
SpawnableUtils::SortEntitiesByTransformHierarchy(spawnable);
|
||||
AZStd::any spawnableAny(AZStd::move(spawnable));
|
||||
auto&& [object, spawnable] = ProcessedObjectStore::Create<AzFramework::Spawnable>(
|
||||
AZStd::move(uniqueName), context.GetSourceUuid(), AZStd::move(serializer));
|
||||
AZ_Assert(spawnable, "Failed to create a new spawnable.");
|
||||
|
||||
context.GetProcessedObjects().emplace_back(AZStd::move(uniqueName), AZStd::move(spawnableAny),
|
||||
AZStd::move(serializer), AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid());
|
||||
bool result = SpawnableUtils::CreateSpawnable(*spawnable, prefab);
|
||||
if (result)
|
||||
{
|
||||
AzFramework::Spawnable::EntityList& entities = spawnable->GetEntities();
|
||||
for (auto it = entities.begin(); it != entities.end(); )
|
||||
{
|
||||
(*it)->InvalidateDependencies();
|
||||
AZ::Entity::DependencySortOutcome evaluation = (*it)->EvaluateDependenciesGetDetails();
|
||||
if (evaluation.IsSuccess())
|
||||
{
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Prefabs", false, "Entity '%s' %s cannot be activated for the following reason: %s",
|
||||
(*it)->GetName().c_str(), (*it)->GetId().ToString().c_str(), evaluation.GetError().m_message.c_str());
|
||||
it = entities.erase(it);
|
||||
}
|
||||
}
|
||||
SpawnableUtils::SortEntitiesByTransformHierarchy(*spawnable);
|
||||
context.GetProcessedObjects().push_back(AZStd::move(object));
|
||||
|
||||
context.RemovePrefab(prefabName);
|
||||
context.RemovePrefab(prefabName);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Prefabs", false, "Failed to convert prefab '%.*s' to a spawnable.", AZ_STRING_ARG(prefabName));
|
||||
context.ErrorEncountered();
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
+17
-1
@@ -30,6 +30,13 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
AZ_RTTI(AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor,
|
||||
"{F71E2FBA-22ED-44C7-B4C8-D2CF4B2C7B97}", PrefabProcessor);
|
||||
|
||||
//! The format the remaining spawnables are going to be stored in.
|
||||
enum class SerializationFormats
|
||||
{
|
||||
Binary, //!< Binary is generally preferable for performance.
|
||||
Text //!< Store in text format which is usually slower but helps with debugging.
|
||||
};
|
||||
|
||||
~PrefabCatchmentProcessor() override = default;
|
||||
|
||||
void Process(PrefabProcessorContext& context) override;
|
||||
@@ -37,6 +44,15 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
protected:
|
||||
static void ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab);
|
||||
static void ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab,
|
||||
AZ::DataStream::StreamType serializationFormat);
|
||||
|
||||
SerializationFormats m_serializationFormat{ SerializationFormats::Binary };
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_TYPE_INFO_SPECIALIZE(AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor::SerializationFormats,
|
||||
"{0FBE2482-B514-4256-8716-EA3ECDF8CD49}");
|
||||
}
|
||||
|
||||
+49
-1
@@ -25,7 +25,30 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
auto registry = AZ::SettingsRegistry::Get();
|
||||
AZ_Assert(registry, "PrefabConversionPipeline is created before the Settings Registry is available.");
|
||||
return registry->GetObject(m_processors, registryKey);
|
||||
bool result = registry->GetObject(m_processors, registryKey);
|
||||
if (!result)
|
||||
{
|
||||
m_processors.clear();
|
||||
}
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
m_fingerprint = CalculateProcessorFingerprint(serializeContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("PrefabConversionPipeline", false, "Failed to get serialization context");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PrefabConversionPipeline::IsLoaded() const
|
||||
{
|
||||
return !m_processors.empty();
|
||||
}
|
||||
|
||||
void PrefabConversionPipeline::ProcessPrefab(PrefabProcessorContext& context)
|
||||
@@ -35,6 +58,26 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
processor->Process(context);
|
||||
}
|
||||
}
|
||||
size_t PrefabConversionPipeline::CalculateProcessorFingerprint(AZ::SerializeContext* context)
|
||||
{
|
||||
size_t fingerprint = 0;
|
||||
|
||||
for (const auto& processor : m_processors)
|
||||
{
|
||||
const AZ::SerializeContext::ClassData* classData = context->FindClassData(processor->RTTI_GetType());
|
||||
|
||||
if (!classData)
|
||||
{
|
||||
AZ_Warning("PrefabConversionPipeline", false, "Class data for processor type %s not found. Cannot get version for fingerprinting", processor->RTTI_GetType().ToString<AZStd::string>().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
AZStd::hash_combine(fingerprint, processor->RTTI_GetType());
|
||||
AZStd::hash_combine(fingerprint, classData->m_version);
|
||||
}
|
||||
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
void PrefabConversionPipeline::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
@@ -45,4 +88,9 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
serializeContext->RegisterGenericType<PrefabProcessorListEntry>();
|
||||
}
|
||||
}
|
||||
|
||||
size_t PrefabConversionPipeline::GetFingerprint() const
|
||||
{
|
||||
return m_fingerprint;
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
+7
-1
@@ -31,12 +31,18 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
using PrefabProcessorList = AZStd::vector<PrefabProcessorListEntry>;
|
||||
|
||||
bool LoadStackProfile(AZStd::string_view stackProfile);
|
||||
bool IsLoaded() const;
|
||||
|
||||
void ProcessPrefab(PrefabProcessorContext& context);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
size_t GetFingerprint() const;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
private:
|
||||
size_t CalculateProcessorFingerprint(AZ::SerializeContext* context);
|
||||
|
||||
PrefabProcessorList m_processors;
|
||||
size_t m_fingerprint{};
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
+23
@@ -14,6 +14,10 @@
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
PrefabProcessorContext::PrefabProcessorContext(const AZ::Uuid& sourceUuid)
|
||||
: m_sourceUuid(sourceUuid)
|
||||
{}
|
||||
|
||||
bool PrefabProcessorContext::AddPrefab(AZStd::string prefabName, PrefabDom prefab)
|
||||
{
|
||||
auto result = m_prefabs.emplace(AZStd::move(prefabName), AZStd::move(prefab));
|
||||
@@ -76,9 +80,28 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
return m_products;
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::SetPlatformTags(AZ::PlatformTagSet tags)
|
||||
{
|
||||
m_platformTags = AZStd::move(tags);
|
||||
}
|
||||
|
||||
const AZ::PlatformTagSet& PrefabProcessorContext::GetPlatformTags() const
|
||||
{
|
||||
return m_platformTags;
|
||||
}
|
||||
|
||||
const AZ::Uuid& PrefabProcessorContext::GetSourceUuid() const
|
||||
{
|
||||
return m_sourceUuid;
|
||||
}
|
||||
|
||||
bool PrefabProcessorContext::HasCompletedSuccessfully() const
|
||||
{
|
||||
return m_completedSuccessfully;
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::ErrorEncountered()
|
||||
{
|
||||
m_completedSuccessfully = false;
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
+8
@@ -33,6 +33,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
AZ_CLASS_ALLOCATOR(PrefabProcessorContext, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(PrefabProcessorContext, "{C7D77E3A-C544-486B-B774-7C82C38FE22F}");
|
||||
|
||||
explicit PrefabProcessorContext(const AZ::Uuid& sourceUuid);
|
||||
virtual ~PrefabProcessorContext() = default;
|
||||
|
||||
virtual bool AddPrefab(AZStd::string prefabName, PrefabDom prefab);
|
||||
@@ -44,7 +45,12 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
virtual ProcessedObjectStoreContainer& GetProcessedObjects();
|
||||
virtual const ProcessedObjectStoreContainer& GetProcessedObjects() const;
|
||||
|
||||
virtual void SetPlatformTags(AZ::PlatformTagSet tags);
|
||||
virtual const AZ::PlatformTagSet& GetPlatformTags() const;
|
||||
virtual const AZ::Uuid& GetSourceUuid() const;
|
||||
|
||||
virtual bool HasCompletedSuccessfully() const;
|
||||
virtual void ErrorEncountered();
|
||||
|
||||
protected:
|
||||
using NamedPrefabContainer = AZStd::unordered_map<AZStd::string, PrefabDom>;
|
||||
@@ -53,6 +59,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
ProcessedObjectStoreContainer m_products;
|
||||
AZStd::vector<AZStd::string> m_delayedDelete;
|
||||
AZ::PlatformTagSet m_platformTags;
|
||||
AZ::Uuid m_sourceUuid;
|
||||
bool m_isIterating{ false };
|
||||
bool m_completedSuccessfully{ true };
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
+33
-24
@@ -11,25 +11,21 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
#include <AzCore/Math/Uuid.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
ProcessedObjectStore::ProcessedObjectStore(AZStd::string uniqueId, AZStd::any object, SerializerFunction objectSerializer,
|
||||
AZ::Data::AssetType assetType)
|
||||
ProcessedObjectStore::ProcessedObjectStore(AZStd::string uniqueId, AZStd::unique_ptr<AZ::Data::AssetData> asset, SerializerFunction assetSerializer)
|
||||
: m_uniqueId(AZStd::move(uniqueId))
|
||||
, m_object(AZStd::move(object))
|
||||
, m_objectSerializer(AZStd::move(objectSerializer))
|
||||
, m_assetType(AZStd::move(assetType))
|
||||
{
|
||||
}
|
||||
, m_assetSerializer(AZStd::move(assetSerializer))
|
||||
, m_asset(AZStd::move(asset))
|
||||
{}
|
||||
|
||||
bool ProcessedObjectStore::Serialize(AZStd::vector<uint8_t>& output) const
|
||||
{
|
||||
if (m_objectSerializer)
|
||||
if (m_assetSerializer)
|
||||
{
|
||||
return m_objectSerializer(output, *this);
|
||||
return m_assetSerializer(output, *this);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -37,25 +33,38 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
}
|
||||
}
|
||||
|
||||
const AZStd::any& ProcessedObjectStore::GetObject() const
|
||||
bool ProcessedObjectStore::HasAsset() const
|
||||
{
|
||||
return m_object;
|
||||
}
|
||||
|
||||
AZStd::any ProcessedObjectStore::ReleaseObject()
|
||||
{
|
||||
return AZStd::move(m_object);
|
||||
}
|
||||
|
||||
uint32_t ProcessedObjectStore::BuildSubId() const
|
||||
{
|
||||
AZ::Uuid subIdHash = AZ::Uuid::CreateData(m_uniqueId.data(), m_uniqueId.size());
|
||||
return azlossy_caster(subIdHash.GetHash());
|
||||
return m_asset != nullptr;
|
||||
}
|
||||
|
||||
const AZ::Data::AssetType& ProcessedObjectStore::GetAssetType() const
|
||||
{
|
||||
return m_assetType;
|
||||
AZ_Assert(m_asset, "Called GetAssetType on ProcessedObjectStore when there was no valid asset.");
|
||||
return m_asset->GetType();
|
||||
}
|
||||
|
||||
AZ::Data::AssetData& ProcessedObjectStore::GetAsset()
|
||||
{
|
||||
AZ_Assert(m_asset, "Called GetAsset on ProcessedObjectStore when there was no valid asset.");
|
||||
return *m_asset;
|
||||
}
|
||||
|
||||
const AZ::Data::AssetData& ProcessedObjectStore::GetAsset() const
|
||||
{
|
||||
AZ_Assert(m_asset, "Called GetAsset on ProcessedObjectStore when there was no valid asset.");
|
||||
return *m_asset;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Data::AssetData> ProcessedObjectStore::ReleaseAsset()
|
||||
{
|
||||
return AZStd::move(m_asset);
|
||||
}
|
||||
|
||||
uint32_t ProcessedObjectStore::BuildSubId(AZStd::string_view id)
|
||||
{
|
||||
AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size());
|
||||
return azlossy_caster(subIdHash.GetHash());
|
||||
}
|
||||
|
||||
const AZStd::string& ProcessedObjectStore::GetId() const
|
||||
|
||||
+34
-17
@@ -13,10 +13,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/std/any.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/utils.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/typetraits/typetraits.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
@@ -31,26 +32,42 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
//! Constructs a new instance.
|
||||
//! @param uniqueId A name for the object that's unique within the scope of the Prefab. This name will be used to generate a sub id for the product
|
||||
//! which requires that the name is stable between runs.
|
||||
//! @param object The object that generated during processing of a Prefab.
|
||||
//! @param objectSerializer The callback used to convert the provided object into a binary stream.
|
||||
//! @param assetType The asset type of the asset.
|
||||
//! @param storagePath The relative path where the asset will be stored if/when committed to disk.
|
||||
ProcessedObjectStore(AZStd::string uniqueId, AZStd::any object, SerializerFunction objectSerializer, AZ::Data::AssetType assetType);
|
||||
|
||||
//! which requires that the name to be stable between runs.
|
||||
//! @param sourceId The uuid for the source file.
|
||||
//! @param assetSerializer The callback used to convert the provided asset into a binary version.
|
||||
template<typename T>
|
||||
static AZStd::pair<ProcessedObjectStore, T*> Create(AZStd::string uniqueId, const AZ::Uuid& sourceId,
|
||||
SerializerFunction assetSerializer);
|
||||
|
||||
bool Serialize(AZStd::vector<uint8_t>& output) const;
|
||||
uint32_t BuildSubId() const;
|
||||
|
||||
const AZStd::any& GetObject() const;
|
||||
AZStd::any ReleaseObject();
|
||||
static uint32_t BuildSubId(AZStd::string_view id);
|
||||
|
||||
bool HasAsset() const;
|
||||
const AZ::Data::AssetType& GetAssetType() const;
|
||||
const AZ::Data::AssetData& GetAsset() const;
|
||||
AZ::Data::AssetData& GetAsset();
|
||||
AZStd::unique_ptr<AZ::Data::AssetData> ReleaseAsset();
|
||||
|
||||
const AZStd::string& GetId() const;
|
||||
|
||||
private:
|
||||
AZStd::any m_object;
|
||||
SerializerFunction m_objectSerializer;
|
||||
AZ::Data::AssetType m_assetType;
|
||||
ProcessedObjectStore(AZStd::string uniqueId, AZStd::unique_ptr<AZ::Data::AssetData> asset, SerializerFunction assetSerializer);
|
||||
|
||||
SerializerFunction m_assetSerializer;
|
||||
AZStd::unique_ptr<AZ::Data::AssetData> m_asset;
|
||||
AZStd::string m_uniqueId;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
AZStd::pair<ProcessedObjectStore, T*> ProcessedObjectStore::Create(AZStd::string uniqueId, const AZ::Uuid& sourceId,
|
||||
SerializerFunction assetSerializer)
|
||||
{
|
||||
static_assert(AZStd::is_base_of_v<AZ::Data::AssetData, T>,
|
||||
"ProcessedObjectStore can only be created from a class that derives from AZ::Data::AssetData.");
|
||||
AZ::Data::AssetId assetId(sourceId, BuildSubId(uniqueId));
|
||||
auto instance = AZStd::make_unique<T>(assetId, AZ::Data::AssetData::AssetStatus::Ready);
|
||||
ProcessedObjectStore resultLeft(AZStd::move(uniqueId), AZStd::move(instance), AZStd::move(assetSerializer));
|
||||
T* resultRight = static_cast<T*>(&resultLeft.GetAsset());
|
||||
return AZStd::make_pair<ProcessedObjectStore, T*>(AZStd::move(resultLeft), resultRight);
|
||||
}
|
||||
} // namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
+26
-12
@@ -17,9 +17,10 @@
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
{
|
||||
@@ -27,22 +28,35 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom)
|
||||
{
|
||||
AzFramework::Spawnable spawnable;
|
||||
AZStd::unique_ptr<Instance> instance(aznew Instance());
|
||||
if (!Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*instance, prefabDom, false))
|
||||
[[maybe_unused]] bool result = CreateSpawnable(spawnable, prefabDom);
|
||||
AZ_Assert(result,
|
||||
"Failed to Load Prefab Instance from given Prefab DOM while Spawnable creation.");
|
||||
return spawnable;
|
||||
}
|
||||
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom)
|
||||
{
|
||||
Instance instance;
|
||||
if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(instance, prefabDom,
|
||||
Prefab::PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is
|
||||
// going to be used to create clones of the entities.
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"Failed to Load Prefab Instance from given Prefab DOM while Spawnable creation.");
|
||||
AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities();
|
||||
if (instance.HasContainerEntity())
|
||||
{
|
||||
entities.emplace_back(AZStd::move(instance.DetachContainerEntity()));
|
||||
}
|
||||
instance.DetachNestedEntities(
|
||||
[&entities](AZStd::unique_ptr<AZ::Entity> entity)
|
||||
{
|
||||
entities.emplace_back(AZStd::move(entity));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities();
|
||||
instance->DetachNestedEntities([&entities](AZStd::unique_ptr<AZ::Entity> entity)
|
||||
{
|
||||
entities.emplace_back(AZStd::move(entity));
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return spawnable;
|
||||
}
|
||||
|
||||
void OrganizeEntitiesForSorting(
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
{
|
||||
AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom);
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom);
|
||||
|
||||
void SortEntitiesByTransformHierarchy(AzFramework::Spawnable& spawnable);
|
||||
} // namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
Template::Template(const AZStd::string& filePath, PrefabDom prefabDom)
|
||||
Template::Template(const AZ::IO::Path& filePath, PrefabDom prefabDom)
|
||||
: m_filePath(filePath)
|
||||
, m_prefabDom(AZStd::move(prefabDom))
|
||||
{
|
||||
@@ -83,6 +83,16 @@ namespace AzToolsFramework
|
||||
m_isLoadedWithErrors = loadedWithErrors;
|
||||
}
|
||||
|
||||
bool Template::IsDirty() const
|
||||
{
|
||||
return m_isDirty;
|
||||
}
|
||||
|
||||
void Template::MarkAsDirty(bool dirty)
|
||||
{
|
||||
m_isDirty = dirty;
|
||||
}
|
||||
|
||||
bool Template::AddLink(LinkId newLinkId)
|
||||
{
|
||||
if (newLinkId == InvalidLinkId)
|
||||
@@ -236,7 +246,7 @@ namespace AzToolsFramework
|
||||
return findInstancesResult->get();
|
||||
}
|
||||
|
||||
const AZStd::string& Template::GetFilePath() const
|
||||
const AZ::IO::Path& Template::GetFilePath() const
|
||||
{
|
||||
return m_filePath;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
@@ -37,7 +38,7 @@ namespace AzToolsFramework
|
||||
using Links = AZStd::unordered_set<LinkId>;
|
||||
|
||||
Template() = default;
|
||||
Template(const AZStd::string& filePath, PrefabDom prefabDom);
|
||||
Template(const AZ::IO::Path& filePath, PrefabDom prefabDom);
|
||||
Template(const Template& other);
|
||||
Template& operator=(const Template& other);
|
||||
|
||||
@@ -47,10 +48,13 @@ namespace AzToolsFramework
|
||||
virtual ~Template() noexcept = default;
|
||||
|
||||
bool IsValid() const;
|
||||
bool IsLoadedWithErrors() const;
|
||||
|
||||
bool IsLoadedWithErrors() const;
|
||||
void MarkAsLoadedWithErrors(bool loadedWithErrors);
|
||||
|
||||
bool IsDirty() const;
|
||||
void MarkAsDirty(bool dirty);
|
||||
|
||||
bool AddLink(LinkId newLinkId);
|
||||
bool RemoveLink(LinkId linkId);
|
||||
bool HasLink(LinkId linkId) const;
|
||||
@@ -64,7 +68,7 @@ namespace AzToolsFramework
|
||||
PrefabDomValueReference GetInstancesValue();
|
||||
PrefabDomValueConstReference GetInstancesValue() const;
|
||||
|
||||
const AZStd::string& GetFilePath() const;
|
||||
const AZ::IO::Path& GetFilePath() const;
|
||||
|
||||
private:
|
||||
// Container for keeping links representing the Template's nested instances.
|
||||
@@ -74,10 +78,13 @@ namespace AzToolsFramework
|
||||
PrefabDom m_prefabDom;
|
||||
|
||||
// File path of this Prefab Template.
|
||||
AZStd::string m_filePath;
|
||||
AZ::IO::Path m_filePath;
|
||||
|
||||
// Flag to tell if this Template and all its nested instances loaded with any error.
|
||||
bool m_isLoadedWithErrors = false;
|
||||
|
||||
// Flag to tell if this Template has changes that have yet to be saved to file.
|
||||
bool m_isDirty = false;
|
||||
};
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+4
-3
@@ -20,6 +20,7 @@
|
||||
#include <AzToolsFramework/ToolsComponents/EditorDisabledCompositionComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionComponent.h>
|
||||
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
|
||||
|
||||
#include "SliceMetadataEntityContextComponent.h"
|
||||
|
||||
@@ -196,10 +197,10 @@ namespace AzToolsFramework
|
||||
SliceMetadataEntityContextNotificationBus::Broadcast(&SliceMetadataEntityContextNotifications::OnMetadataEntityAdded, metadataEntity.GetId());
|
||||
|
||||
// Register the metadata entity with the pre-emptive undo cache (if exists) so it has an initial state
|
||||
auto* preemptiveUndoCache = PreemptiveUndoCache::Get();
|
||||
if (preemptiveUndoCache)
|
||||
auto undoCacheInterface = AZ::Interface<UndoSystem::UndoCacheInterface>::Get();
|
||||
if (undoCacheInterface)
|
||||
{
|
||||
preemptiveUndoCache->UpdateCache(metadataEntity.GetId());
|
||||
undoCacheInterface->UpdateCache(metadataEntity.GetId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -52,6 +52,15 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SourceControlThumbnailKey::Equals(const ThumbnailKey* other) const
|
||||
{
|
||||
if (!ThumbnailKey::Equals(other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return m_fileName == azrtti_cast<const SourceControlThumbnailKey*>(other)->GetFileName();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// SourceControlThumbnail
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -142,7 +151,7 @@ namespace AzToolsFramework
|
||||
// SourceControlThumbnailCache
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SourceControlThumbnailCache::SourceControlThumbnailCache()
|
||||
: ThumbnailCache<SourceControlThumbnail, SourceControlKeyHash, SourceControlKeyEqual>()
|
||||
: ThumbnailCache<SourceControlThumbnail>()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -36,6 +36,8 @@ namespace AzToolsFramework
|
||||
|
||||
bool UpdateThumbnail() override;
|
||||
|
||||
bool Equals(const ThumbnailKey* other) const override;
|
||||
|
||||
protected:
|
||||
//! absolute path
|
||||
AZStd::string m_fileName;
|
||||
@@ -107,7 +109,7 @@ namespace AzToolsFramework
|
||||
|
||||
//! Stores products' thumbnails
|
||||
class SourceControlThumbnailCache
|
||||
: public ThumbnailCache<SourceControlThumbnail, SourceControlKeyHash, SourceControlKeyEqual>
|
||||
: public ThumbnailCache<SourceControlThumbnail>
|
||||
{
|
||||
public:
|
||||
SourceControlThumbnailCache();
|
||||
|
||||
@@ -39,6 +39,16 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t ThumbnailKey::GetHash() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ThumbnailKey::Equals(const ThumbnailKey* other) const
|
||||
{
|
||||
return RTTI_GetType() == other->RTTI_GetType();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Thumbnail
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -49,19 +49,23 @@ namespace AzToolsFramework
|
||||
|
||||
virtual bool UpdateThumbnail();
|
||||
|
||||
Q_SIGNALS:
|
||||
virtual size_t GetHash() const;
|
||||
|
||||
virtual bool Equals(const ThumbnailKey* other) const;
|
||||
Q_SIGNALS:
|
||||
//! Updated signal is dispatched whenever thumbnail data was changed. Anyone using this thumbnail should listen to this.
|
||||
void ThumbnailUpdatedSignal() const;
|
||||
//! Force update mapped thumbnails
|
||||
void UpdateThumbnailSignal() const;
|
||||
|
||||
|
||||
private:
|
||||
bool m_ready = false;
|
||||
};
|
||||
|
||||
typedef QSharedPointer<ThumbnailKey> SharedThumbnailKey;
|
||||
|
||||
#define MAKE_TKEY(type, ...) QSharedPointer<type>(new type(__VA_ARGS__))
|
||||
#define MAKE_TKEY(type, ...) QSharedPointer<type>(new type(__VA_ARGS__))
|
||||
|
||||
//! Thumbnail is the base class in thumbnailer system.
|
||||
/*
|
||||
@@ -92,7 +96,7 @@ Q_SIGNALS:
|
||||
SharedThumbnailKey GetKey() const;
|
||||
State GetState() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
Q_SIGNALS:
|
||||
void Updated() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
@@ -128,16 +132,44 @@ Q_SIGNALS:
|
||||
};
|
||||
|
||||
typedef QSharedPointer<ThumbnailProvider> SharedThumbnailProvider;
|
||||
}
|
||||
}
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
// hash specialization
|
||||
template <>
|
||||
struct hash<AzToolsFramework::Thumbnailer::SharedThumbnailKey>
|
||||
{
|
||||
AZ_FORCE_INLINE size_t operator()(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const
|
||||
{
|
||||
return key->GetHash();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AZStd::equal_to<AzToolsFramework::Thumbnailer::SharedThumbnailKey>
|
||||
{
|
||||
AZ_FORCE_INLINE bool operator()(const AzToolsFramework::Thumbnailer::SharedThumbnailKey& left, const AzToolsFramework::Thumbnailer::SharedThumbnailKey& right) const
|
||||
{
|
||||
return left->Equals(right.data());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Thumbnailer
|
||||
{
|
||||
//! ThumbnailCache manages thumbnails of specific type, derive your custom provider from this
|
||||
/*
|
||||
ThumbnailType - type of thumbnails managed
|
||||
HasherType - hashing function for storing thumbnail keys in the hashtable
|
||||
Hasher - hashing function for storing thumbnail keys in the hashtable
|
||||
EqualKey - equality function for storing thumbnail keys in the hashtable
|
||||
HasherType and EqualKey need to be provided on individual basis depending on
|
||||
Hasher and EqualKey need to be provided on individual basis depending on
|
||||
what constitutes a unique key and how should the key collection be optimized
|
||||
*/
|
||||
template<typename ThumbnailType, typename HasherType, typename EqualKey>
|
||||
template<class ThumbnailType, class Hasher = AZStd::hash<SharedThumbnailKey>, class EqualKey = AZStd::equal_to<SharedThumbnailKey>>
|
||||
class ThumbnailCache
|
||||
: public ThumbnailProvider
|
||||
, public AZ::TickBus::Handler
|
||||
@@ -157,7 +189,7 @@ Q_SIGNALS:
|
||||
|
||||
protected:
|
||||
int m_thumbnailSize;
|
||||
AZStd::unordered_map<SharedThumbnailKey, SharedThumbnail, HasherType, EqualKey> m_cache;
|
||||
AZStd::unordered_map<SharedThumbnailKey, SharedThumbnail, Hasher, EqualKey> m_cache;
|
||||
|
||||
//! Check if thumbnail key is handled by this provider, overload in derived class
|
||||
virtual bool IsSupportedThumbnail(SharedThumbnailKey key) const = 0;
|
||||
@@ -169,4 +201,5 @@ Q_SIGNALS:
|
||||
|
||||
Q_DECLARE_METATYPE(AzToolsFramework::Thumbnailer::SharedThumbnailKey)
|
||||
|
||||
|
||||
#include <AzToolsFramework/Thumbnails/Thumbnail.inl>
|
||||
|
||||
@@ -16,21 +16,21 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Thumbnailer
|
||||
{
|
||||
template <typename ThumbnailType, typename HasherType, typename EqualKey>
|
||||
ThumbnailCache<ThumbnailType, HasherType, EqualKey>::ThumbnailCache()
|
||||
template <class ThumbnailType, class Hasher, class EqualKey>
|
||||
ThumbnailCache<ThumbnailType, Hasher, EqualKey>::ThumbnailCache()
|
||||
: m_thumbnailSize(0)
|
||||
{
|
||||
BusConnect();
|
||||
}
|
||||
|
||||
template <typename ThumbnailType, typename HasherType, typename EqualKey>
|
||||
ThumbnailCache<ThumbnailType, HasherType, EqualKey>::~ThumbnailCache()
|
||||
template <class ThumbnailType, class Hasher, class EqualKey>
|
||||
ThumbnailCache<ThumbnailType, Hasher, EqualKey>::~ThumbnailCache()
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
|
||||
template <typename ThumbnailType, typename HasherType, typename EqualKey>
|
||||
void ThumbnailCache<ThumbnailType, HasherType, EqualKey>::OnTick(float deltaTime, AZ::ScriptTimePoint /*time*/)
|
||||
template <class ThumbnailType, class Hasher, class EqualKey>
|
||||
void ThumbnailCache<ThumbnailType, Hasher, EqualKey>::OnTick(float deltaTime, AZ::ScriptTimePoint /*time*/)
|
||||
{
|
||||
for (auto& kvp : m_cache)
|
||||
{
|
||||
@@ -38,9 +38,8 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ThumbnailType, typename HasherType, typename EqualKey>
|
||||
bool ThumbnailCache<ThumbnailType, HasherType, EqualKey>::GetThumbnail(
|
||||
SharedThumbnailKey key, SharedThumbnail& thumbnail)
|
||||
template <class ThumbnailType, class Hasher, class EqualKey>
|
||||
bool ThumbnailCache<ThumbnailType, Hasher, EqualKey>::GetThumbnail(SharedThumbnailKey key, SharedThumbnail& thumbnail)
|
||||
{
|
||||
auto it = m_cache.find(key);
|
||||
if (it != m_cache.end())
|
||||
@@ -57,8 +56,8 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ThumbnailType, typename HasherType, typename EqualKey>
|
||||
void ThumbnailCache<ThumbnailType, HasherType, EqualKey>::SetThumbnailSize(int thumbnailSize)
|
||||
template <class ThumbnailType, class Hasher, class EqualKey>
|
||||
void ThumbnailCache<ThumbnailType, Hasher, EqualKey>::SetThumbnailSize(int thumbnailSize)
|
||||
{
|
||||
m_thumbnailSize = thumbnailSize;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace AzToolsFramework
|
||||
static const bool EnableEventQueue = true;
|
||||
typedef AZStd::recursive_mutex MutexType;
|
||||
|
||||
virtual void RenderThumbnail(AZ::Data::AssetId assetId, int thumbnailSize) = 0;
|
||||
virtual void RenderThumbnail(SharedThumbnailKey thumbnailKey, int thumbnailSize) = 0;
|
||||
|
||||
virtual bool Installed() const { return false; }
|
||||
};
|
||||
@@ -79,7 +79,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
typedef AZ::Data::AssetId BusIdType;
|
||||
typedef SharedThumbnailKey BusIdType;
|
||||
|
||||
//! notify product thumbnail that the data is ready
|
||||
virtual void ThumbnailRendered(QPixmap& thumbnailImage) = 0;
|
||||
|
||||
-3
@@ -67,10 +67,7 @@ namespace AzToolsFramework
|
||||
incompatible.push_back(AZ_CRC_CE("LookAtService"));
|
||||
incompatible.push_back(AZ_CRC_CE("SequenceService"));
|
||||
incompatible.push_back(AZ_CRC_CE("ClothMeshService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXColliderService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXTriggerService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXJointService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXShapeColliderService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXRagdollService"));
|
||||
incompatible.push_back(AZ_CRC_CE("WhiteBoxService"));
|
||||
|
||||
+1
-2
@@ -35,6 +35,7 @@ namespace AzToolsFramework
|
||||
public:
|
||||
AZ_EDITOR_COMPONENT(ScriptEditorComponent, "{b5fc8679-fa2a-4c7c-ac42-dcc279ea613a}")
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static bool DoComponentsMatch(const ScriptEditorComponent* thisComponent, const ScriptEditorComponent* otherComponent);
|
||||
|
||||
ScriptEditorComponent() = default;
|
||||
@@ -79,8 +80,6 @@ namespace AzToolsFramework
|
||||
float m_sortOrder; // Sort order of the property as defined by using the "order" attribute, by default the order is FLT_MAX which means alphabetical sort will be used
|
||||
};
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
void LoadProperties();
|
||||
// make sure internal script (m_scriptComponent.m_script) is set before loading
|
||||
void LoadScript();
|
||||
|
||||
-1
@@ -196,7 +196,6 @@ namespace AssetProcessor
|
||||
AZ_UNUSED(autoRegisterIfNotFound);
|
||||
AZ_Assert(autoRegisterIfNotFound == false, "Auto registration is invalid during asset processing.");
|
||||
AZ_UNUSED(typeToRegister);
|
||||
AZ_Assert(typeToRegister == AZ::Data::s_invalidAssetType, "Can not register types during asset processing.");
|
||||
|
||||
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
|
||||
if (!engineConnection || !engineConnection->IsConnected())
|
||||
|
||||
+24
-1
@@ -23,9 +23,11 @@
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponentBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
@@ -1054,11 +1056,32 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::u32 TransformComponent::ParentChanged()
|
||||
{
|
||||
AZ::u32 refreshLevel = AZ::Edit::PropertyRefreshLevels::None;
|
||||
|
||||
if (!m_parentEntityId.IsValid())
|
||||
{
|
||||
// If Prefabs are enabled, reroute the invalid id to the level root
|
||||
bool isPrefabSystemEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
|
||||
if (isPrefabSystemEnabled)
|
||||
{
|
||||
auto prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
|
||||
|
||||
if (prefabPublicInterface)
|
||||
{
|
||||
m_parentEntityId = prefabPublicInterface->GetLevelInstanceContainerEntityId();
|
||||
refreshLevel = AZ::Edit::PropertyRefreshLevels::ValuesOnly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto parentId = m_parentEntityId;
|
||||
m_parentEntityId = m_previousParentEntityId;
|
||||
SetParent(parentId);
|
||||
|
||||
return AZ::Edit::PropertyRefreshLevels::None;
|
||||
return refreshLevel;
|
||||
}
|
||||
|
||||
AZ::u32 TransformComponent::TransformChanged()
|
||||
|
||||
+6
-1
@@ -63,7 +63,12 @@ namespace AzToolsFramework
|
||||
void EditorEntityUiHandlerBase::PaintDescendantBackground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/,
|
||||
const QModelIndex& /*descendantIndex*/) const
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void EditorEntityUiHandlerBase::PaintDescendantBranchBackground(QPainter* /*painter*/, const QTreeView* /*view*/, const QRect& /*rect*/,
|
||||
const QModelIndex& /*index*/, const QModelIndex& /*descendantIndex*/) const
|
||||
{
|
||||
}
|
||||
|
||||
void EditorEntityUiHandlerBase::PaintItemForeground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const
|
||||
{
|
||||
|
||||
+4
@@ -20,6 +20,7 @@
|
||||
#include <QStyleOptionViewItem>
|
||||
|
||||
class QPainter;
|
||||
class QTreeView;
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -50,6 +51,9 @@ namespace AzToolsFramework
|
||||
//! Paints the background of the descendants of the item in the Outliner.
|
||||
virtual void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
|
||||
const QModelIndex& descendantIndex) const;
|
||||
//! Paints the background of the descendant branches of the item in the Outliner.
|
||||
virtual void PaintDescendantBranchBackground(QPainter* painter, const QTreeView* view, const QRect& rect,
|
||||
const QModelIndex& index, const QModelIndex& descendantIndex) const;
|
||||
|
||||
//! Paints visual elements on the foreground of the item in the Outliner.
|
||||
virtual void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||
|
||||
@@ -21,29 +21,17 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
const int LayerUiHandler::m_layerSquareSize = 20;
|
||||
const int LayerUiHandler::m_layerStripeWidth = 1;
|
||||
const int LayerUiHandler::m_layerDividerLineHeight = 1;
|
||||
const int LayerUiHandler::m_lastEntityInLayerDividerLineHeight = 1;
|
||||
const QColor LayerUiHandler::m_layerBackgroundColor = QColor("#2F2F2F");
|
||||
const QColor LayerUiHandler::m_layerDescendantBackgroundColor = QColor("#333333");
|
||||
const QColor LayerUiHandler::m_layerBorderTopColor = QColor("#515151");
|
||||
const QColor LayerUiHandler::m_layerBorderBottomColor = QColor("#252525");
|
||||
const QString LayerUiHandler::m_layerIconPath = QString(":/Icons/layer_icon.svg");
|
||||
const QString LayerUiHandler::m_layerIconPath = QString(":/Entity/layer.svg");
|
||||
|
||||
QString LayerUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const
|
||||
{
|
||||
QString result;
|
||||
bool isLayerEntity = false;
|
||||
AZ::Outcome<AZStd::string, AZStd::string> layerBaseNameResult;
|
||||
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
|
||||
isLayerEntity,
|
||||
entityId,
|
||||
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
|
||||
|
||||
if (!isLayerEntity)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
layerBaseNameResult, entityId, &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::GetLayerBaseFileName);
|
||||
|
||||
bool hasUnsavedLayerChanges = false;
|
||||
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
|
||||
@@ -51,16 +39,28 @@ namespace AzToolsFramework
|
||||
entityId,
|
||||
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasUnsavedChanges);
|
||||
|
||||
QString result = "<span style=\"font-style: italic; font-weight: 400;\">";
|
||||
|
||||
if (layerBaseNameResult.IsSuccess())
|
||||
{
|
||||
result += QString("(%1.layer").arg(layerBaseNameResult.GetValue().c_str());
|
||||
}
|
||||
|
||||
if (hasUnsavedLayerChanges)
|
||||
{
|
||||
result = QObject::tr("*");
|
||||
result += QString("*");
|
||||
}
|
||||
|
||||
if (layerBaseNameResult.IsSuccess())
|
||||
{
|
||||
result += QString(")");
|
||||
}
|
||||
|
||||
result += "</span>";
|
||||
|
||||
bool isLayerNameValid = false;
|
||||
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
|
||||
isLayerNameValid,
|
||||
entityId,
|
||||
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::IsLayerNameValid);
|
||||
isLayerNameValid, entityId, &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::IsLayerNameValid);
|
||||
|
||||
if (!isLayerNameValid)
|
||||
{
|
||||
@@ -75,22 +75,7 @@ namespace AzToolsFramework
|
||||
return QPixmap(m_layerIconPath);
|
||||
}
|
||||
|
||||
void LayerUiHandler::PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
|
||||
{
|
||||
QPainterPath backgroundPath;
|
||||
backgroundPath.addRect(option.rect);
|
||||
painter->fillPath(backgroundPath, m_layerBackgroundColor);
|
||||
}
|
||||
|
||||
void LayerUiHandler::PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& /*index*/,
|
||||
const QModelIndex& /*descendantIndex*/) const
|
||||
{
|
||||
QPainterPath backgroundPath;
|
||||
backgroundPath.addRect(option.rect);
|
||||
painter->fillPath(backgroundPath, m_layerDescendantBackgroundColor);
|
||||
}
|
||||
|
||||
void LayerUiHandler::PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
void LayerUiHandler::PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
{
|
||||
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
|
||||
|
||||
@@ -99,11 +84,18 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
// Dark Grey Background
|
||||
QPainterPath backgroundPath;
|
||||
backgroundPath.addRect(option.rect);
|
||||
painter->fillPath(backgroundPath, m_layerBackgroundColor);
|
||||
|
||||
// Left rect with the layer color
|
||||
QColor layerColor;
|
||||
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(
|
||||
layerColor,
|
||||
entityId,
|
||||
&AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::GetLayerColor);
|
||||
layerColor, entityId, &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::GetLayerColor);
|
||||
|
||||
const QTreeView* outlinerTreeView(qobject_cast<const QTreeView*>(option.widget));
|
||||
int indentation = outlinerTreeView->indentation();
|
||||
@@ -114,39 +106,31 @@ namespace AzToolsFramework
|
||||
if (isFirstColumn)
|
||||
{
|
||||
// Layer colored box
|
||||
QPainterPath layerIconPath;
|
||||
QPainterPath iconBackgroundPath;
|
||||
QPoint layerBoxOffset(0, (option.rect.height() - m_layerSquareSize) / 2);
|
||||
QRect layerIconRect(option.rect.topLeft() + layerBoxOffset, QSize(m_layerSquareSize, m_layerSquareSize));
|
||||
layerIconPath.addRect(layerIconRect);
|
||||
painter->fillPath(layerIconPath, layerColor);
|
||||
iconBackgroundPath.addRect(layerIconRect);
|
||||
painter->fillPath(iconBackgroundPath, layerColor);
|
||||
|
||||
// Left border
|
||||
PaintLayerStripeAndBorder(
|
||||
painter,
|
||||
option.rect.left(),
|
||||
option.rect.top(),
|
||||
option.rect.bottom(),
|
||||
m_layerBorderBottomColor,
|
||||
layerColor);
|
||||
painter, option.rect.left() - 1, option.rect.top(), option.rect.bottom(), m_layerBorderBottomColor, layerColor);
|
||||
}
|
||||
|
||||
QModelIndex nameColumn = index.sibling(index.row(), EntityOutlinerListModel::Column::ColumnName);
|
||||
QModelIndex sibling = index.sibling(index.row() + 1, index.column());
|
||||
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
QPoint lineBottomLeft(option.rect.bottomLeft());
|
||||
QPoint lineTopLeft(option.rect.topLeft());
|
||||
|
||||
if (isFirstColumn)
|
||||
{
|
||||
lineTopLeft.setX(lineTopLeft.x() + m_layerStripeWidth);
|
||||
lineTopLeft.setX(lineTopLeft.x() + m_layerStripeWidth - 1);
|
||||
}
|
||||
QPen topLinePen(m_layerBorderTopColor, m_layerDividerLineHeight);
|
||||
painter->setPen(topLinePen);
|
||||
painter->drawLine(lineTopLeft, option.rect.topRight());
|
||||
|
||||
|
||||
if (isFirstColumn)
|
||||
{
|
||||
lineBottomLeft.setX(lineBottomLeft.x());
|
||||
@@ -156,11 +140,43 @@ namespace AzToolsFramework
|
||||
lineBottomLeft.setX(lineBottomLeft.x() + m_layerStripeWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QPen bottomLinePen(m_layerBorderBottomColor, m_layerDividerLineHeight);
|
||||
painter->setPen(bottomLinePen);
|
||||
painter->drawLine(lineBottomLeft, option.rect.bottomRight());
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void LayerUiHandler::PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& /*index*/, const QModelIndex& /*descendantIndex*/) const
|
||||
{
|
||||
QPainterPath backgroundPath;
|
||||
backgroundPath.addRect(option.rect);
|
||||
painter->fillPath(backgroundPath, m_layerDescendantBackgroundColor);
|
||||
}
|
||||
|
||||
void LayerUiHandler::PaintDescendantBranchBackground(QPainter* painter, const QTreeView* view, const QRect& rect,
|
||||
const QModelIndex& index, const QModelIndex& /*descendantIndex*/) const
|
||||
{
|
||||
if (!painter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
QPainterPath backgroundPath;
|
||||
|
||||
const int ancestorLeft = view->visualRect(index).left();
|
||||
|
||||
// Find the rect that extends fully to the left
|
||||
QRect fullRect = rect;
|
||||
fullRect.setLeft(ancestorLeft);
|
||||
backgroundPath.addRect(fullRect);
|
||||
|
||||
painter->fillPath(backgroundPath, m_layerDescendantBackgroundColor);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
@@ -182,7 +198,7 @@ namespace AzToolsFramework
|
||||
const QTreeView* outlinerTreeView(qobject_cast<const QTreeView*>(option.widget));
|
||||
bool isFirstColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnName;
|
||||
bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle;
|
||||
int ancestorLeft = outlinerTreeView->visualRect(index).left();
|
||||
int ancestorLeft = outlinerTreeView->visualRect(index).left() - 1;
|
||||
|
||||
// Draw the left stripe for this layer on the descendant's rect
|
||||
if (isFirstColumn)
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace AzToolsFramework
|
||||
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
|
||||
const QModelIndex& descendantIndex) const override;
|
||||
void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
void PaintDescendantBranchBackground(QPainter* painter, const QTreeView* view, const QRect& rect,
|
||||
const QModelIndex& index, const QModelIndex& descendantIndex) const override;
|
||||
void PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
|
||||
const QModelIndex& descendantIndex) const override;
|
||||
|
||||
@@ -43,12 +44,16 @@ namespace AzToolsFramework
|
||||
static QModelIndex GetLastVisibleChild(const QModelIndex& parent);
|
||||
static QModelIndex Internal_GetLastVisibleChild(const QAbstractItemModel* model, const QModelIndex& index);
|
||||
|
||||
static const int m_layerSquareSize;
|
||||
static const int m_layerStripeWidth;
|
||||
static const int m_layerDividerLineHeight;
|
||||
static const int m_lastEntityInLayerDividerLineHeight;
|
||||
static constexpr int m_layerSquareSize = 22;
|
||||
static constexpr int m_layerStripeWidth = 1;
|
||||
static constexpr int m_layerDividerLineHeight = 1;
|
||||
static constexpr int m_lastEntityInLayerDividerLineHeight = 1;
|
||||
static const QColor m_layerBackgroundColor;
|
||||
static const QColor m_layerBackgroundHoveredColor;
|
||||
static const QColor m_layerBackgroundSelectedColor;
|
||||
static const QColor m_layerDescendantBackgroundColor;
|
||||
static const QColor m_layerDescendantBackgroundHoveredColor;
|
||||
static const QColor m_layerDescendantBackgroundSelectedColor;
|
||||
static const QColor m_layerBorderTopColor;
|
||||
static const QColor m_layerBorderBottomColor;
|
||||
static const QString m_layerIconPath;
|
||||
|
||||
@@ -44,11 +44,11 @@ AzToolsFramework--EntityOutlinerCheckBox
|
||||
border: 0px solid transparent;
|
||||
border-radius: 0px;
|
||||
spacing: 0px;
|
||||
padding: 0px;
|
||||
padding: 0;
|
||||
padding-right: 2px;
|
||||
line-height: 0px;
|
||||
font-size: 0px;
|
||||
margin: 0px;
|
||||
background-color: transparent;
|
||||
max-height: 20px;
|
||||
max-width: 18px;
|
||||
}
|
||||
@@ -58,14 +58,15 @@ AzToolsFramework--EntityOutlinerCheckBox::indicator
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
image-position:center;
|
||||
|
||||
border: 1px solid transparent;
|
||||
image: none;
|
||||
spacing: 0px;
|
||||
padding: 0px;
|
||||
line-height: 0px;
|
||||
font-size: 0px;
|
||||
margin: 0;
|
||||
margin: 3px 0 0 0;
|
||||
max-width: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
AzToolsFramework--EntityOutlinerCheckBox#VisibilityMixed::indicator:checked
|
||||
@@ -83,7 +84,6 @@ AzToolsFramework--EntityOutlinerCheckBox#VisibilityMixed::indicator:checked
|
||||
{
|
||||
background: rgba(0, 0, 0, 80);
|
||||
border-radius: 5px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ AzToolsFramework--EntityOutlinerCheckBox#Visibility::indicator:unchecked
|
||||
, AzToolsFramework--EntityOutlinerCheckBox#VisibilityMixed::indicator:unchecked
|
||||
{
|
||||
image: url(:/visibility_on.svg);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverridden::indicator:checked
|
||||
@@ -109,6 +110,7 @@ AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverridden::indicator:checked
|
||||
AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverridden::indicator:unchecked
|
||||
{
|
||||
image: url(:/visibility_on_transparent.svg);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
AzToolsFramework--EntityOutlinerCheckBox#VisibilityHover::indicator:checked
|
||||
@@ -116,6 +118,7 @@ AzToolsFramework--EntityOutlinerCheckBox#VisibilityHover::indicator:checked
|
||||
, AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverriddenHover::indicator:checked
|
||||
{
|
||||
image: url(:/visibility_default_hover.svg);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
AzToolsFramework--EntityOutlinerCheckBox#VisibilityHover::indicator:unchecked
|
||||
@@ -123,6 +126,7 @@ AzToolsFramework--EntityOutlinerCheckBox#VisibilityHover::indicator:unchecked
|
||||
, AzToolsFramework--EntityOutlinerCheckBox#VisibilityOverriddenHover::indicator:unchecked
|
||||
{
|
||||
image: url(:/visibility_on_hover.svg);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +136,7 @@ AzToolsFramework--EntityOutlinerCheckBox#Lock::indicator:checked
|
||||
, AzToolsFramework--EntityOutlinerCheckBox#LockMixed::indicator:checked
|
||||
{
|
||||
image: url(:/lock_on.svg);
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
AzToolsFramework--EntityOutlinerCheckBox#Lock::indicator:unchecked
|
||||
@@ -148,6 +153,7 @@ AzToolsFramework--EntityOutlinerCheckBox#LockOverridden::indicator:checked
|
||||
AzToolsFramework--EntityOutlinerCheckBox#LockOverridden::indicator:unchecked
|
||||
{
|
||||
image: url(:/lock_default_transparent.svg);
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
AzToolsFramework--EntityOutlinerCheckBox#LockHover::indicator:checked
|
||||
@@ -155,6 +161,7 @@ AzToolsFramework--EntityOutlinerCheckBox#LockHover::indicator:checked
|
||||
, AzToolsFramework--EntityOutlinerCheckBox#LockOverriddenHover::indicator:checked
|
||||
{
|
||||
image: url(:/lock_on_hover.svg);
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
AzToolsFramework--EntityOutlinerCheckBox#LockHover::indicator:unchecked
|
||||
@@ -162,4 +169,5 @@ AzToolsFramework--EntityOutlinerCheckBox#LockHover::indicator:unchecked
|
||||
, AzToolsFramework--EntityOutlinerCheckBox#LockOverriddenHover::indicator:unchecked
|
||||
{
|
||||
image: url(:/lock_default_hover.svg);
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
+36
-41
@@ -1970,6 +1970,13 @@ namespace AzToolsFramework
|
||||
|
||||
void EntityOutlinerItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
{
|
||||
// Customize option to prevent Qt from painting the default focus rectangle
|
||||
QStyleOptionViewItem customOption{option};
|
||||
if (customOption.state & QStyle::State_HasFocus)
|
||||
{
|
||||
customOption.state ^= QStyle::State_HasFocus;
|
||||
}
|
||||
|
||||
// Retrieve the Entity UI Handler
|
||||
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
|
||||
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
|
||||
@@ -1977,6 +1984,9 @@ namespace AzToolsFramework
|
||||
const bool isSelected = (option.state & QStyle::State_Selected);
|
||||
const bool isHovered = (option.state & QStyle::State_MouseOver) && (option.state & QStyle::State_Enabled);
|
||||
|
||||
// Paint the Selection/Hover Rect
|
||||
PaintSelectionHoverRect(painter, option, index, isSelected, isHovered);
|
||||
|
||||
// Paint Ancestor Backgrounds
|
||||
PaintAncestorBackgrounds(painter, option, index);
|
||||
|
||||
@@ -1986,25 +1996,6 @@ namespace AzToolsFramework
|
||||
entityUiHandler->PaintItemBackground(painter, option, index);
|
||||
}
|
||||
|
||||
// Paint the Selection/Hover Rect
|
||||
PaintSelectionHoverRect(painter, option, index, isSelected, isHovered);
|
||||
|
||||
// Paint Ancestor Foregrounds
|
||||
PaintAncestorForegrounds(painter, option, index);
|
||||
|
||||
// Paint the Foreground
|
||||
if (entityUiHandler != nullptr)
|
||||
{
|
||||
entityUiHandler->PaintItemForeground(painter, option, index);
|
||||
}
|
||||
|
||||
// Customize option to prevent Qt from painting the default focus rectangle
|
||||
QStyleOptionViewItem customOption{ option };
|
||||
if (customOption.state & QStyle::State_HasFocus)
|
||||
{
|
||||
customOption.state ^= QStyle::State_HasFocus;
|
||||
}
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case EntityOutlinerListModel::ColumnVisibilityToggle:
|
||||
@@ -2033,6 +2024,15 @@ namespace AzToolsFramework
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Paint Ancestor Foregrounds
|
||||
PaintAncestorForegrounds(painter, option, index);
|
||||
|
||||
// Paint the Foreground
|
||||
if (entityUiHandler != nullptr)
|
||||
{
|
||||
entityUiHandler->PaintItemForeground(painter, option, index);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityOutlinerItemDelegate::PaintAncestorBackgrounds(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
@@ -2069,17 +2069,18 @@ namespace AzToolsFramework
|
||||
|
||||
if (isSelected || isHovered)
|
||||
{
|
||||
QPainterPath layerBGPath;
|
||||
QRect layerBGRect(option.rect);
|
||||
layerBGPath.addRect(layerBGRect);
|
||||
QPainterPath backgroundPath;
|
||||
QRect backgroundRect(option.rect);
|
||||
|
||||
QColor layerBG = m_hoverColor;
|
||||
backgroundPath.addRect(backgroundRect);
|
||||
|
||||
QColor backgroundColor = m_hoverColor;
|
||||
if (isSelected)
|
||||
{
|
||||
layerBG = m_selectedColor;
|
||||
backgroundColor = m_selectedColor;
|
||||
}
|
||||
|
||||
painter->fillPath(layerBGPath, layerBG);
|
||||
painter->fillPath(backgroundPath, backgroundColor);
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
@@ -2212,13 +2213,9 @@ namespace AzToolsFramework
|
||||
textWidthAvailable -= fontMetrics.horizontalAdvance(QObject::tr("..."));
|
||||
if (!infoString.isEmpty())
|
||||
{
|
||||
// The info string includes HTML markup, which can cause an issue computing the width.
|
||||
// The markup on the text is light (just color, italic or bold), so an approximate width
|
||||
// is computed by taking the width of the non-HTML portion of the string and padding it a bit.
|
||||
QString htmlStripped = infoString;
|
||||
htmlStripped.remove(htmlMarkupRegex);
|
||||
const float layerInfoPadding = 1.2f;
|
||||
textWidthAvailable -= fontMetrics.horizontalAdvance(htmlStripped) * layerInfoPadding;
|
||||
textWidthAvailable -= fontMetrics.horizontalAdvance(htmlStripped) + 5;
|
||||
}
|
||||
|
||||
entityNameRichText = fontMetrics.elidedText(optionV4.text, Qt::TextElideMode::ElideRight, textWidthAvailable);
|
||||
@@ -2226,7 +2223,12 @@ namespace AzToolsFramework
|
||||
|
||||
if (!infoString.isEmpty())
|
||||
{
|
||||
entityNameRichText = QObject::tr("%1%2").arg(entityNameRichText).arg(infoString);
|
||||
entityNameRichText = QString("<table width=\"100%\" style=\"border-collapse: collapse; border-spacing: 0;\">")
|
||||
+ QString("<tr><td>")
|
||||
+ QString(entityNameRichText)
|
||||
+ QString("</td><td align=\"right\">")
|
||||
+ QString(infoString)
|
||||
+ QString("</td></tr></table>");
|
||||
}
|
||||
|
||||
// delete the text from the item so we can use the standard painter to draw the icon
|
||||
@@ -2238,7 +2240,7 @@ namespace AzToolsFramework
|
||||
textDoc.setDefaultFont(optionV4.font);
|
||||
textDoc.setDefaultStyleSheet("body {color: white}");
|
||||
textDoc.setHtml("<body>" + entityNameRichText + "</body>");
|
||||
painter->translate(textRect.topLeft() + QPoint(0, -1));
|
||||
painter->translate(textRect.topLeft());
|
||||
textDoc.setTextWidth(textRect.width());
|
||||
textDoc.drawContents(painter, QRectF(0, 0, textRect.width(), textRect.height()));
|
||||
|
||||
@@ -2246,7 +2248,7 @@ namespace AzToolsFramework
|
||||
EntityOutlinerListModel::s_paintingName = false;
|
||||
}
|
||||
|
||||
QSize EntityOutlinerItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
QSize EntityOutlinerItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
|
||||
{
|
||||
// Get the height of a tall character...
|
||||
// we do this only once per 'tick'
|
||||
@@ -2262,14 +2264,7 @@ namespace AzToolsFramework
|
||||
QTimer::singleShot(0, resetFunction);
|
||||
}
|
||||
|
||||
QSize sh = QSize(0, m_cachedBoundingRectOfTallCharacter.height() + EntityOutlinerListModel::s_OutlinerSpacing);
|
||||
|
||||
if (index.column() == EntityOutlinerListModel::ColumnVisibilityToggle || index.column() == EntityOutlinerListModel::ColumnLockToggle)
|
||||
{
|
||||
sh.setWidth(m_toggleColumnWidth);
|
||||
}
|
||||
|
||||
return sh;
|
||||
return QSize(0, m_cachedBoundingRectOfTallCharacter.height() + EntityOutlinerListModel::s_OutlinerSpacing);
|
||||
}
|
||||
|
||||
bool EntityOutlinerItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
|
||||
|
||||
+1
-3
@@ -107,7 +107,7 @@ namespace AzToolsFramework
|
||||
};
|
||||
|
||||
// Spacing is appropriate and matches the outliner concept work from the UI team.
|
||||
static const int s_OutlinerSpacing = 5;
|
||||
static const int s_OutlinerSpacing = 7;
|
||||
|
||||
static bool s_paintingName;
|
||||
|
||||
@@ -358,8 +358,6 @@ namespace AzToolsFramework
|
||||
mutable CheckboxGroup m_visibilityCheckBoxes;
|
||||
mutable CheckboxGroup m_lockCheckBoxes;
|
||||
|
||||
const int m_toggleColumnWidth = 16;
|
||||
|
||||
// this is a cache, and is hence mutable
|
||||
mutable QRect m_cachedBoundingRectOfTallCharacter;
|
||||
|
||||
|
||||
+1
-8
@@ -183,8 +183,6 @@ namespace AzToolsFramework
|
||||
// Paint the branch background as defined by the entity's handler, or its closes ancestor's.
|
||||
PaintBranchBackground(painter, rect, index);
|
||||
|
||||
// Make sure the base class is called after the layer rect is drawn,
|
||||
// so that the foldout arrow draws on top of the layer color.
|
||||
QTreeView::drawBranches(painter, rect, index);
|
||||
}
|
||||
|
||||
@@ -209,12 +207,7 @@ namespace AzToolsFramework
|
||||
|
||||
if (ancestorUiHandler != nullptr)
|
||||
{
|
||||
int ancestorLeft = visualRect(ancestorIndex).left();
|
||||
QStyleOptionViewItem option;
|
||||
option.rect = rect;
|
||||
option.rect.setLeft(ancestorLeft);
|
||||
|
||||
ancestorUiHandler->PaintDescendantBackground(painter, option, ancestorIndex, index);
|
||||
ancestorUiHandler->PaintDescendantBranchBackground(painter, this, rect, ancestorIndex, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
-2
@@ -171,7 +171,7 @@ namespace AzToolsFramework
|
||||
|
||||
const int autoExpandDelayMilliseconds = 2500;
|
||||
m_gui->m_objectTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
m_gui->m_objectTree->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
|
||||
m_gui->m_objectTree->setEditTriggers(QAbstractItemView::EditKeyPressed);
|
||||
m_gui->m_objectTree->setAutoExpandDelay(autoExpandDelayMilliseconds);
|
||||
m_gui->m_objectTree->setDragEnabled(true);
|
||||
m_gui->m_objectTree->setDropIndicatorShown(true);
|
||||
@@ -179,9 +179,11 @@ namespace AzToolsFramework
|
||||
m_gui->m_objectTree->setDragDropOverwriteMode(false);
|
||||
m_gui->m_objectTree->setDragDropMode(QAbstractItemView::DragDrop);
|
||||
m_gui->m_objectTree->setDefaultDropAction(Qt::CopyAction);
|
||||
m_gui->m_objectTree->setExpandsOnDoubleClick(false);
|
||||
m_gui->m_objectTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_gui->m_objectTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
m_gui->m_objectTree->setAutoScrollMargin(20);
|
||||
m_gui->m_objectTree->setIndentation(24);
|
||||
connect(m_gui->m_objectTree, &QTreeView::customContextMenuRequested, this, &EntityOutlinerWidget::OnOpenTreeContextMenu);
|
||||
|
||||
// custom item delegate
|
||||
@@ -193,6 +195,7 @@ namespace AzToolsFramework
|
||||
|
||||
// Link up signals for informing the model of tree changes using the proxy as an intermediary
|
||||
connect(m_gui->m_objectTree, &QTreeView::clicked, this, &EntityOutlinerWidget::OnTreeItemClicked);
|
||||
connect(m_gui->m_objectTree, &QTreeView::doubleClicked, this, &EntityOutlinerWidget::OnTreeItemDoubleClicked);
|
||||
connect(m_gui->m_objectTree, &QTreeView::expanded, this, &EntityOutlinerWidget::OnTreeItemExpanded);
|
||||
connect(m_gui->m_objectTree, &QTreeView::collapsed, this, &EntityOutlinerWidget::OnTreeItemCollapsed);
|
||||
connect(m_gui->m_objectTree, &EntityOutlinerTreeView::ItemDropped, this, &EntityOutlinerWidget::OnDropEvent);
|
||||
@@ -223,8 +226,12 @@ namespace AzToolsFramework
|
||||
m_gui->m_objectTree->header()->setStretchLastSection(false);
|
||||
|
||||
// resize the icon columns so that the Visibility and Lock toggle icon columns stay right-justified
|
||||
m_gui->m_objectTree->header()->setStretchLastSection(false);
|
||||
m_gui->m_objectTree->header()->setMinimumSectionSize(0);
|
||||
m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnName, QHeaderView::Stretch);
|
||||
m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnVisibilityToggle, QHeaderView::ResizeToContents);
|
||||
m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnVisibilityToggle, QHeaderView::Fixed);
|
||||
m_gui->m_objectTree->header()->resizeSection(EntityOutlinerListModel::ColumnVisibilityToggle, 20);
|
||||
m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnLockToggle, QHeaderView::Fixed);
|
||||
m_gui->m_objectTree->header()->resizeSection(EntityOutlinerListModel::ColumnLockToggle, 24);
|
||||
|
||||
connect(m_gui->m_objectTree->selectionModel(),
|
||||
@@ -271,10 +278,12 @@ namespace AzToolsFramework
|
||||
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
|
||||
GetEntityContextId());
|
||||
EditorEntityInfoNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<EntityOutlinerWidgetInterface>::Register(this);
|
||||
}
|
||||
|
||||
EntityOutlinerWidget::~EntityOutlinerWidget()
|
||||
{
|
||||
AZ::Interface<EntityOutlinerWidgetInterface>::Unregister(this);
|
||||
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
|
||||
EditorEntityInfoNotificationBus::Handler::BusDisconnect();
|
||||
EditorPickModeNotificationBus::Handler::BusDisconnect();
|
||||
@@ -798,6 +807,8 @@ namespace AzToolsFramework
|
||||
#ifdef Q_OS_MAC
|
||||
// "Alt+Return" translates to Option+Return on macOS
|
||||
m_actionToRenameSelection->setShortcut(tr("Alt+Return"));
|
||||
#elseif Q_OS_WIN
|
||||
m_actionToRenameSelection->setShortcut(tr("F2"));
|
||||
#endif
|
||||
m_actionToRenameSelection->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
connect(m_actionToRenameSelection, &QAction::triggered, this, &EntityOutlinerWidget::DoRenameSelection);
|
||||
@@ -860,6 +871,10 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void EntityOutlinerWidget::OnTreeItemDoubleClicked(const QModelIndex& /*index*/)
|
||||
{
|
||||
}
|
||||
|
||||
void EntityOutlinerWidget::OnTreeItemExpanded(const QModelIndex& index)
|
||||
{
|
||||
m_listModel->OnEntityExpanded(GetEntityIdFromIndex(index));
|
||||
@@ -1075,6 +1090,30 @@ namespace AzToolsFramework
|
||||
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)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
QTimer::singleShot(1, this, [this]() {
|
||||
m_gui->m_objectTree->setUpdatesEnabled(true);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_gui->m_objectTree->setUpdatesEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityOutlinerWidget::OnEntityInfoUpdatedAddChildEnd(AZ::EntityId /*parentId*/, AZ::EntityId childId)
|
||||
{
|
||||
QueueContentUpdateSort(childId);
|
||||
|
||||
+9
-2
@@ -21,8 +21,9 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/ToolsMessaging/EntityHighlightBus.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerSearchWidget.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerCacheBus.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerSearchWidget.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerWidgetInterface.h>
|
||||
#include <AzToolsFramework/UI/SearchWidget/SearchWidgetTypes.hxx>
|
||||
|
||||
#include <QIcon>
|
||||
@@ -60,12 +61,13 @@ namespace AzToolsFramework
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
, private EditorEntityInfoNotificationBus::Handler
|
||||
, private ComponentModeFramework::EditorComponentModeNotificationBus::Handler
|
||||
, private EntityOutlinerWidgetInterface
|
||||
{
|
||||
Q_OBJECT;
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(EntityOutlinerWidget, AZ::SystemAllocator, 0)
|
||||
|
||||
EntityOutlinerWidget(QWidget* pParent = NULL, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
explicit EntityOutlinerWidget(QWidget* pParent = NULL, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
virtual ~EntityOutlinerWidget();
|
||||
|
||||
private Q_SLOTS:
|
||||
@@ -103,6 +105,10 @@ namespace AzToolsFramework
|
||||
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
|
||||
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
|
||||
|
||||
// EntityOutlinerWidgetInterface
|
||||
void SetRootEntity(AZ::EntityId rootEntityId) override;
|
||||
void SetUpdatesEnabled(bool enable) override;
|
||||
|
||||
// Build a selection object from the given entities. Entities already in the Widget's selection buffers are ignored.
|
||||
template <class EntityIdCollection>
|
||||
QItemSelection BuildSelectionFromEntities(const EntityIdCollection& entityIds);
|
||||
@@ -138,6 +144,7 @@ namespace AzToolsFramework
|
||||
QAction* m_actionGoToEntitiesInViewport;
|
||||
|
||||
void OnTreeItemClicked(const QModelIndex& index);
|
||||
void OnTreeItemDoubleClicked(const QModelIndex& index);
|
||||
void OnTreeItemExpanded(const QModelIndex& index);
|
||||
void OnTreeItemCollapsed(const QModelIndex& index);
|
||||
void OnExpandEntity(const AZ::EntityId& entityId, bool expand);
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class EntityOutlinerWidgetInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(EntityOutlinerWidgetInterface, "{30C0F252-EC84-4196-BF59-EB9E73B8ADCB}");
|
||||
|
||||
virtual void SetRootEntity(AZ::EntityId rootEntityId) = 0;
|
||||
virtual void SetUpdatesEnabled(bool enable) = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 <AzCore/Component/Entity.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabInstanceContainerNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
virtual ~PrefabInstanceContainerNotifications() = default;
|
||||
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
|
||||
virtual void OnPrefabComponentActivate(AZ::EntityId entityId) = 0;
|
||||
virtual void OnPrefabComponentDeactivate(AZ::EntityId entityId) = 0;
|
||||
};
|
||||
using PrefabInstanceContainerNotificationBus = AZ::EBus<PrefabInstanceContainerNotifications>;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabIntegrationInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(PrefabIntegrationInterface, "{B88AE045-7711-49BC-8336-45003D1C9116}");
|
||||
|
||||
/**
|
||||
* Create a new entity at the position provided.
|
||||
* @param position The position the new entity will be created at.
|
||||
* @param parentId The id of the parent of the newly created entity.
|
||||
* @return The id of the newly created entity.
|
||||
*/
|
||||
virtual AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user