Merge branch 'main' into LYN-1767-AB

This commit is contained in:
igarri
2021-06-02 12:03:22 +01:00
1611 changed files with 60962 additions and 37160 deletions
@@ -60,10 +60,20 @@ namespace AzToolsFramework
//! and is generally checked into source control.
virtual const char* GetAbsoluteDevRootFolderPath() = 0;
/// Convert a full source path like "c:\\dev\gamename\\blah\\test.tga" into a relative product path.
/// Convert a full source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative product path.
/// asset paths never mention their alias and are relative to the asset cache root
virtual bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) = 0;
/** Convert a source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative source path, like "blah/test.tga".
* If no valid relative path could be created, the input source path will be returned in relativePath.
* @param sourcePath partial or full path to a source file. (The file doesn't need to exist)
* @param relativePath the output relative path for the source file, if a valid one could be created
* @param rootFilePath the root path that relativePath is relative to
* @return true if a valid relative path was created, false if it wasn't
*/
virtual bool GenerateRelativeSourcePath(
const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFilePath) = 0;
/// Convert a relative asset path like "blah/test.tga" to a full source path path.
/// Once the asset processor has finished building, this function is capable of handling even when the extension changes
/// or when the source is in a different folder or in a different location (such as inside gems)
@@ -110,14 +120,14 @@ namespace AzToolsFramework
/**
* Query to see if a specific asset platform is enabled
* @param platform the asset platform to check e.g. es3, ios, etc.
* @param platform the asset platform to check e.g. android, ios, etc.
* @return true if enabled, false otherwise
*/
virtual bool IsAssetPlatformEnabled(const char* platform) = 0;
/**
* Get the total number of pending assets left to process for a specific asset platform
* @param platform the asset platform to check e.g. es3, ios, etc.
* @param platform the asset platform to check e.g. android, ios, etc.
* @return -1 if the process fails, a positive number otherwise
*/
virtual int GetPendingAssetsForPlatform(const char* platform) = 0;
@@ -302,7 +312,7 @@ namespace AzToolsFramework
inline const char* GetHostAssetPlatform()
{
#if defined(AZ_PLATFORM_MAC)
return "osx_gl";
return "mac";
#elif defined(AZ_PLATFORM_WINDOWS)
return "pc";
#elif defined(AZ_PLATFORM_LINUX)
@@ -52,6 +52,21 @@ namespace AzToolsFramework
* Deletes all entities in the provided list, as well as their transform descendants.
*/
virtual void DeleteEntitiesAndAllDescendants(const EntityIdList& entities) = 0;
/**
* Duplicate all currently-selected entities.
*/
virtual void DuplicateSelected() = 0;
/**
* Duplicates the specified entity.
*/
virtual void DuplicateEntityById(AZ::EntityId entityId) = 0;
/**
* Duplicates all specified entities.
*/
virtual void DuplicateEntities(const EntityIdList& entities) = 0;
};
} // namespace AzToolsFramework
@@ -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.
*
*/
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Math/Color.h>
#include <AzFramework/Viewport/ViewportId.h>
namespace AzToolsFramework
{
//! An interface for loading simple icon assets and rendering them to screen on a per-viewport basis.
class EditorViewportIconDisplayInterface
{
public:
AZ_RTTI(EditorViewportIconDisplayInterface, "{D5190B58-2561-4F3F-B793-F1E7D454CDF2}");
using IconId = AZ::s32;
static constexpr IconId InvalidIconId = -1;
enum class CoordinateSpace : AZ::u8
{
ScreenSpace,
WorldSpace
};
//! These draw parameters control rendering for a single icon to a single viewport.
struct DrawParameters
{
//! The ViewportId to render to.
AzFramework::ViewportId m_viewport = AzFramework::InvalidViewportId;
//! The icon ID, retrieved from GetOrLoadIconForPath, to render to screen.
IconId m_icon = InvalidIconId;
//! The color, including opacity, to render the icon with. White will render the icon as opaque in its original color.
AZ::Color m_color = AZ::Colors::White;
//! The position to render the icon to, in world or screen space depending on m_positionSpace.
AZ::Vector3 m_position;
//! The coordinate system to use for m_position.
//! ScreenSpace will accept m_position in the form of [X, Y, Depth], where X & Y are screen coordinates in
//! pixels and Depth is a z-ordering depth value from 0.0f to 1.0f.
//! WorldSpace will accept a 3D vector in world space coordinates that will be translated back into screen
//! space when the icon is rendered.
CoordinateSpace m_positionSpace = CoordinateSpace::ScreenSpace;
//! The size to render the icon as, in pixels.
AZ::Vector2 m_size;
};
//! The current load status of an icon retrieved by GetOrLoadIconForPath.
enum class IconLoadStatus : AZ::u8
{
Unloaded,
Loading,
Loaded,
Error
};
//! Draws an icon to a viewport given a set of draw parameters.
//! Requires an IconId retrieved from GetOrLoadIconForPath.
virtual void DrawIcon(const DrawParameters& drawParameters) = 0;
//! Retrieves a reusable IconId for an icon at a given path.
//! This will load the icon, if it has not already been loaded.
//! @param path should be a relative asset path to an icon image asset.
//! png and svg icons are currently supported.
virtual IconId GetOrLoadIconForPath(AZStd::string_view path) = 0;
//! Gets the current load status of an icon retrieved via GetOrLoadIconForPath.
virtual IconLoadStatus GetIconLoadStatus(IconId icon) = 0;
};
using EditorViewportIconDisplay = AZ::Interface<EditorViewportIconDisplayInterface>;
} //namespace AzToolsFramework
@@ -239,6 +239,11 @@ namespace AzToolsFramework
*/
virtual int RemoveDirtyEntity(AZ::EntityId target) = 0;
/*!
* Clears the dirty entity set.
*/
virtual void ClearDirtyEntities() = 0;
/*!
* \return true if an undo/redo operation is in progress.
*/
@@ -43,7 +43,7 @@ namespace AzToolsFramework
void EditorEntityManager::DeleteEntityById(AZ::EntityId entityId)
{
DeleteEntities({entityId});
DeleteEntities(EntityIdList{ entityId });
}
void EditorEntityManager::DeleteEntities(const EntityIdList& entities)
@@ -53,12 +53,30 @@ namespace AzToolsFramework
void EditorEntityManager::DeleteEntityAndAllDescendants(AZ::EntityId entityId)
{
DeleteEntitiesAndAllDescendants({entityId});
DeleteEntitiesAndAllDescendants(EntityIdList{ entityId });
}
void EditorEntityManager::DeleteEntitiesAndAllDescendants(const EntityIdList& entities)
{
m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance(entities);
}
void EditorEntityManager::DuplicateSelected()
{
EntityIdList selectedEntities;
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities);
m_prefabPublicInterface->DuplicateEntitiesInInstance(selectedEntities);
}
void EditorEntityManager::DuplicateEntityById(AZ::EntityId entityId)
{
DuplicateEntities(EntityIdList{ entityId });
}
void EditorEntityManager::DuplicateEntities(const EntityIdList& entities)
{
m_prefabPublicInterface->DuplicateEntitiesInInstance(entities);
}
}
@@ -31,6 +31,9 @@ namespace AzToolsFramework
void DeleteEntities(const EntityIdList& entities) override;
void DeleteEntityAndAllDescendants(AZ::EntityId entityId) override;
void DeleteEntitiesAndAllDescendants(const EntityIdList& entities) override;
void DuplicateSelected() override;
void DuplicateEntityById(AZ::EntityId entityId) override;
void DuplicateEntities(const EntityIdList& entities) override;
private:
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
@@ -1354,6 +1354,11 @@ namespace AzToolsFramework
return static_cast<int>(m_dirtyEntities.erase(entityId));
}
void ToolsApplication::ClearDirtyEntities()
{
m_dirtyEntities.clear();
}
void ToolsApplication::UndoPressed()
{
if (m_undoStack)
@@ -85,6 +85,7 @@ namespace AzToolsFramework
void AddDirtyEntity(AZ::EntityId entityId) override;
int RemoveDirtyEntity(AZ::EntityId entityId) override;
void ClearDirtyEntities() override;
bool IsDuringUndoRedo() override { return m_isDuringUndoRedo; }
void UndoPressed() override;
void RedoPressed() override;
@@ -265,6 +265,30 @@ namespace AzToolsFramework
return response.m_resolved;
}
bool AssetSystemComponent::GenerateRelativeSourcePath(
const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFilePath)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
relativePath = sourcePath;
return false;
}
AzFramework::AssetSystem::GenerateRelativeSourcePathRequest request(sourcePath);
AzFramework::AssetSystem::GenerateRelativeSourcePathResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GenerateRelativeSourcePath request for %s", sourcePath.c_str());
relativePath = sourcePath;
return false;
}
relativePath = response.m_relativeSourcePath;
rootFilePath = response.m_rootFolder;
return response.m_resolved;
}
bool AssetSystemComponent::GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullPath)
{
auto foundIt = m_assetSourceRelativePathToFullPathCache.find(relPath);
@@ -63,6 +63,8 @@ namespace AzToolsFramework
const char* GetAbsoluteDevGameFolderPath() override;
const char* GetAbsoluteDevRootFolderPath() override;
bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& outputPath) override;
bool GenerateRelativeSourcePath(
const AZStd::string& sourcePath, AZStd::string& outputPath, AZStd::string& watchFolder) override;
bool GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullPath) override;
bool GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath) override;
bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override;
@@ -81,9 +81,20 @@ namespace AzToolsFramework
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_" + name);
bool selectedAsset = false;
for (auto& assetId : selection.GetSelectedAssetIds())
{
m_ui->m_assetBrowserTreeViewWidget->SelectProduct(assetId);
if (assetId.IsValid())
{
selectedAsset = true;
m_ui->m_assetBrowserTreeViewWidget->SelectProduct(assetId);
}
}
if (!selectedAsset)
{
m_ui->m_assetBrowserTreeViewWidget->SelectFolder(selection.GetDefaultDirectory());
}
setWindowTitle(tr("Pick %1").arg(m_selection.GetTitle()));
@@ -93,6 +93,16 @@ namespace AzToolsFramework
m_selectedAssetIds.push_back(selectedAssetId);
}
void AssetSelectionModel::SetDefaultDirectory(AZStd::string_view defaultDirectory)
{
m_defaultDirectory = defaultDirectory;
}
AZStd::string_view AssetSelectionModel::GetDefaultDirectory() const
{
return m_defaultDirectory;
}
AZStd::vector<const AssetBrowserEntry*>& AssetSelectionModel::GetResults()
{
return m_results;
@@ -47,6 +47,9 @@ namespace AzToolsFramework
const AZStd::vector<AZ::Data::AssetId>& GetSelectedAssetIds() const;
void SetSelectedAssetIds(const AZStd::vector<AZ::Data::AssetId>& selectedAssetIds);
void SetSelectedAssetId(const AZ::Data::AssetId& selectedAssetId);
void SetDefaultDirectory(AZStd::string_view defaultDirectory);
AZStd::string_view GetDefaultDirectory() const;
AZStd::vector<const AssetBrowserEntry*>& GetResults();
const AssetBrowserEntry* GetResult();
@@ -72,6 +75,7 @@ namespace AzToolsFramework
AZStd::vector<AZ::Data::AssetId> m_selectedAssetIds;
AZStd::vector<const AssetBrowserEntry*> m_results;
AZStd::string m_defaultDirectory;
QString m_title;
};
@@ -14,6 +14,7 @@
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/StringFunc/StringFunc.h>
@@ -278,7 +279,20 @@ namespace AzToolsFramework
return false;
}
bool AssetBrowserTreeView::SelectEntry(const QModelIndex& idxParent, const AZStd::vector<AZStd::string>& entries, const uint32_t entryPathIndex)
void AssetBrowserTreeView::SelectFolder(AZStd::string_view folderPath)
{
if (folderPath.size() == 0)
{
return;
}
AZStd::vector<AZStd::string> entries;
AZ::StringFunc::Tokenize(folderPath, entries, "/");
SelectEntry(QModelIndex(), entries, 0, true);
}
bool AssetBrowserTreeView::SelectEntry(const QModelIndex& idxParent, const AZStd::vector<AZStd::string>& entries, const uint32_t entryPathIndex, bool useDisplayName)
{
if (entries.empty())
{
@@ -293,30 +307,43 @@ namespace AzToolsFramework
auto rowIdx = model()->index(idx, 0, idxParent);
auto rowEntry = GetEntryFromIndex<AssetBrowserEntry>(rowIdx);
// Check if this entry name matches the query
if (rowEntry && AzFramework::StringFunc::Equal(entry.c_str(), rowEntry->GetName().c_str(), true))
if (rowEntry)
{
// Final entry found - set it as the selected element
if (entryPathIndex == entries.size() - 1)
{
selectionModel()->clear();
selectionModel()->select(rowIdx, QItemSelectionModel::Select);
setCurrentIndex(rowIdx);
return true;
}
// Check if this entry name matches the query
AZStd::string_view compareName = useDisplayName ? (const char*)(rowEntry->GetDisplayName().toUtf8()) : rowEntry->GetName().c_str();
// If this isn't the final entry, it needs to be a folder for the path to be valid (otherwise, early out)
if (rowEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
if (AzFramework::StringFunc::Equal(entry.c_str(), compareName, true))
{
// Folder found - if the final entry is found, expand this folder so the final entry is viewable in the Asset Browser (otherwise, early out)
if (SelectEntry(rowIdx, entries, entryPathIndex + 1))
// Final entry found - set it as the selected element
if (entryPathIndex == entries.size() - 1)
{
expand(rowIdx);
if (rowEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
{
// Expand the item itself if it is a folder
expand(rowIdx);
}
selectionModel()->clear();
selectionModel()->select(rowIdx, QItemSelectionModel::Select);
setCurrentIndex(rowIdx);
return true;
}
// If this isn't the final entry, it needs to be a folder for the path to be valid (otherwise, early out)
if (rowEntry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
{
// Folder found - if the final entry is found, expand this folder so the final entry is viewable in the Asset
// Browser (otherwise, early out)
if (SelectEntry(rowIdx, entries, entryPathIndex + 1, useDisplayName))
{
expand(rowIdx);
return true;
}
}
return false;
}
return false;
}
}
@@ -60,6 +60,8 @@ namespace AzToolsFramework
AZStd::vector<AssetBrowserEntry*> GetSelectedAssets() const;
void SelectFolder(AZStd::string_view folderPath);
//////////////////////////////////////////////////////////////////////////
// AssetBrowserViewRequestBus
void SelectProduct(AZ::Data::AssetId assetID) override;
@@ -67,6 +69,7 @@ namespace AzToolsFramework
void ClearFilter() override;
void Update() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
@@ -105,7 +108,7 @@ namespace AzToolsFramework
QString m_name;
bool SelectProduct(const QModelIndex& idxParent, AZ::Data::AssetId assetID);
bool SelectEntry(const QModelIndex& idxParent, const AZStd::vector<AZStd::string>& entryPathTokens, const uint32_t entryPathIndex = 0);
bool SelectEntry(const QModelIndex& idxParent, const AZStd::vector<AZStd::string>& entryPathTokens, const uint32_t entryPathIndex = 0, bool useDisplayName = false);
//! Grab one entry from the source thumbnail list and update it
void UpdateSCThumbnails();
@@ -483,6 +483,8 @@ namespace AzToolsFramework
}
}
m_dirty = false;
AddRecentPath(targetFilePath);
SetStatusText(Status::assetCreated);
@@ -56,5 +56,7 @@ namespace AzToolsFramework
virtual void StartPlayInEditor() = 0;
virtual void StopPlayInEditor() = 0;
virtual void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) = 0;
};
}
@@ -14,9 +14,11 @@
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Script/ScriptSystemBus.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h>
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
@@ -55,7 +57,6 @@ namespace AzToolsFramework
"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({}, {}, "NewLevel.prefab"));
m_sliceOwnershipService.BusConnect(m_entityContextId);
m_sliceOwnershipService.m_shouldAssertForLegacySlicesUsage = m_shouldAssertForLegacySlicesUsage;
m_editorSliceOwnershipService.BusConnect();
@@ -89,14 +90,17 @@ namespace AzToolsFramework
void PrefabEditorEntityOwnershipService::Reset()
{
Prefab::TemplateId templateId = m_rootInstance->GetTemplateId();
if (templateId != Prefab::InvalidTemplateId)
if (m_rootInstance)
{
m_rootInstance->SetTemplateId(Prefab::InvalidTemplateId);
m_prefabSystemComponent->RemoveTemplate(templateId);
Prefab::TemplateId templateId = m_rootInstance->GetTemplateId();
if (templateId != Prefab::InvalidTemplateId)
{
m_rootInstance->SetTemplateId(Prefab::InvalidTemplateId);
m_prefabSystemComponent->RemoveTemplate(templateId);
}
m_rootInstance->Reset();
m_rootInstance->SetContainerEntityName("Level");
}
m_rootInstance->Reset();
m_rootInstance->SetContainerEntityName("Level");
AzFramework::EntityOwnershipServiceNotificationBus::Event(
m_entityContextId, &AzFramework::EntityOwnershipServiceNotificationBus::Events::OnEntityOwnershipServiceReset);
@@ -200,7 +204,7 @@ namespace AzToolsFramework
}
m_rootInstance->SetTemplateId(templateId);
m_rootInstance->SetTemplateSourcePath(m_loaderInterface->GetRelativePathToProject(filename));
m_rootInstance->SetTemplateSourcePath(m_loaderInterface->GenerateRelativePath(filename));
m_rootInstance->SetContainerEntityName("Level");
m_prefabSystemComponent->PropagateTemplateChanges(templateId);
@@ -218,16 +222,15 @@ namespace AzToolsFramework
bool PrefabEditorEntityOwnershipService::SaveToStream(AZ::IO::GenericStream& stream, AZStd::string_view filename)
{
AZ::IO::Path relativePath = m_loaderInterface->GetRelativePathToProject(filename);
AZ::IO::Path relativePath = m_loaderInterface->GenerateRelativePath(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()});
HandleEntitiesAdded({ m_rootInstance->m_containerEntity.get() });
AzToolsFramework::Prefab::PrefabDom dom;
bool success = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*m_rootInstance, dom);
@@ -236,7 +239,8 @@ namespace AzToolsFramework
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));
templateId = m_prefabSystemComponent->AddTemplate(relativePath, AZStd::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));
@@ -263,6 +267,71 @@ namespace AzToolsFramework
return false;
}
void PrefabEditorEntityOwnershipService::CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename)
{
AZ::IO::Path relativePath = m_loaderInterface->GenerateRelativePath(filename);
AzToolsFramework::Prefab::TemplateId templateId = m_prefabSystemComponent->GetTemplateIdFromFilePath(relativePath);
m_rootInstance->SetTemplateSourcePath(relativePath);
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
bool sourceInfoFound = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, templateFilename.c_str(),
assetInfo, watchFolder);
if (sourceInfoFound)
{
AZStd::string fullPath;
AZ::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), fullPath);
// Get the default prefab and copy the Dom over to the new template being saved
Prefab::TemplateId defaultId = m_loaderInterface->LoadTemplateFromFile(fullPath.c_str());
Prefab::PrefabDom& dom = m_prefabSystemComponent->FindTemplateDom(defaultId);
Prefab::PrefabDom levelDefaultDom;
levelDefaultDom.CopyFrom(dom, levelDefaultDom.GetAllocator());
Prefab::PrefabDomPath sourcePath("/Source");
sourcePath.Set(levelDefaultDom, relativePath.c_str());
templateId = m_prefabSystemComponent->AddTemplate(relativePath, AZStd::move(levelDefaultDom));
}
else
{
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;
}
templateId = m_prefabSystemComponent->AddTemplate(relativePath, std::move(dom));
}
if (templateId == AzToolsFramework::Prefab::InvalidTemplateId)
{
AZ_Error("Prefab", false, "Couldn't create new template id '%i' when creating new level '%.*s'", templateId, AZ_STRING_ARG(filename));
return;
}
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);
}
m_prefabSystemComponent->PropagateTemplateChanges(templateId);
}
Prefab::InstanceOptionalReference PrefabEditorEntityOwnershipService::CreatePrefab(
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Prefab::Instance>>&& nestedPrefabInstances,
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder)
@@ -311,7 +380,12 @@ namespace AzToolsFramework
Prefab::InstanceOptionalReference PrefabEditorEntityOwnershipService::GetRootPrefabInstance()
{
AZ_Assert(m_rootInstance, "A valid root prefab instance couldn't be found in PrefabEditorEntityOwnershipService.");
return *m_rootInstance;
if (m_rootInstance)
{
return *m_rootInstance;
}
return AZStd::nullopt;
}
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData()
@@ -170,6 +170,8 @@ namespace AzToolsFramework
void StartPlayInEditor() override;
void StopPlayInEditor() override;
void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) override;
protected:
AZ::SliceComponent::SliceInstanceAddress GetOwningSlice() override;
@@ -614,7 +614,7 @@ namespace AzToolsFramework
AZ::Quaternion oldEntityRotation;
AZ::TransformBus::EventResult(oldEntityRotation, id, &AZ::TransformBus::Events::GetWorldRotationQuaternion);
transformComponent->SetRotationQuaternion(oldEntityRotation);
transformComponent->SetWorldRotationQuaternion(oldEntityRotation);
// Ensure the existing hierarchy is maintained
AZ::EntityId oldParentEntityId;
@@ -31,7 +31,7 @@ namespace AzToolsFramework
rayProportion, lineSegmentProportion, worldClosestPositionRay, worldClosestPositionLineSegment);
AZ::Transform worldFromLocalNormalized = worldFromLocal;
const AZ::Vector3 scale = worldFromLocalNormalized.ExtractScale() * nonUniformScale;
const AZ::Vector3 scale = worldFromLocalNormalized.ExtractUniformScale() * nonUniformScale;
const AZ::Transform localFromWorldNormalized = worldFromLocalNormalized.GetInverse();
return { (localFromWorldNormalized.TransformPoint(worldClosestPositionLineSegment)) / scale };
@@ -59,7 +59,7 @@ namespace AzToolsFramework
? CalculateSnappedOffset(localTransform.GetTranslation(), axis, gridSize * scaleRecip)
: AZ::Vector3::CreateZero();
const AZ::Vector3 localScale = localTransform.GetScale();
const AZ::Vector3 localScale = AZ::Vector3(localTransform.GetUniformScale());
const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform);
// calculate scale amount to snap, to align to round scale value
const AZ::Vector3 scaleSnapOffset = snapping && !gridSnapAction.m_localSnapping
@@ -113,7 +113,7 @@ namespace AzToolsFramework
/// noise in the value returned when dealing with values far from the origin.
inline float ScaleReciprocal(const AZ::Transform& transform)
{
return Round3(transform.GetScale().GetReciprocal().GetMinElement());
return Round3(1.0f / transform.GetUniformScale());
}
/// Find the reciprocal of the non-uniform scale.
@@ -39,7 +39,7 @@ namespace AzToolsFramework
AZ::Transform result;
result.SetRotation(m_space.GetRotation() * localTransform.GetRotation());
result.SetTranslation(m_space.TransformPoint(m_nonUniformScale * localTransform.GetTranslation()));
result.SetScale(m_space.GetScale() * localTransform.GetScale());
result.SetUniformScale(m_space.GetUniformScale() * localTransform.GetUniformScale());
return result;
}
@@ -447,17 +447,14 @@ namespace AzToolsFramework
m_radius * viewScale);
debugDisplay.SetColor(ViewColor(manipulatorState.m_mouseOver, m_color, m_mouseOverColor).GetAsVector4());
// show wireframe if the axis has been corrected/flipped
// note: please see IRenderAuxGeom.h for the definition of e_FillModeWireframe and e_FillModeSolid.
// it is not possible to include IRenderAuxGeom from here and we also don't want to introduce that dependency.
// these legacy enums should be wrapped so set SetFillMode can be used in a type safe way, until then,
// use the values directly until the API has been updated.
const AZ::u32 prevFillMode = debugDisplay.SetFillMode(
m_shouldCorrect ? /*e_FillModeWireframe =*/ 0x1 << 26 : /*e_FillModeSolid =*/ 0);
debugDisplay.DrawCone(coneBound.m_base, coneBound.m_axis, coneBound.m_radius, coneBound.m_height, false);
debugDisplay.SetFillMode(prevFillMode);
if (m_shouldCorrect)
{
debugDisplay.DrawWireCone(coneBound.m_base, coneBound.m_axis, coneBound.m_radius, coneBound.m_height);
}
else
{
debugDisplay.DrawSolidCone(coneBound.m_base, coneBound.m_axis, coneBound.m_radius, coneBound.m_height, false);
}
RefreshBoundInternal(managerId, manipulatorId, coneBound);
}
@@ -82,9 +82,7 @@ namespace AzToolsFramework
m_uniformScaleManipulator->SetVisualOrientationOverride(
QuaternionFromTransformNoScaling(localTransform));
m_uniformScaleManipulator->SetLocalTransform(
AZ::Transform::CreateTranslation(localTransform.GetTranslation()) *
AZ::Transform::CreateScale(localTransform.GetScale()));
m_uniformScaleManipulator->SetLocalOrientation(AZ::Quaternion::CreateIdentity());
}
void ScaleManipulators::SetLocalPositionImpl(const AZ::Vector3& localPosition)
@@ -23,7 +23,7 @@ namespace AzToolsFramework
inline AZ::Transform TransformNormalizedScale(const AZ::Transform& transform)
{
AZ::Transform transformNormalizedScale = transform;
transformNormalizedScale.SetScale(AZ::Vector3::CreateOne());
transformNormalizedScale.SetUniformScale(1.0f);
return transformNormalizedScale;
}
@@ -33,8 +33,7 @@ namespace AzToolsFramework
inline AZ::Transform TransformUniformScale(const AZ::Transform& transform)
{
AZ::Transform transformUniformScale = transform;
const float maxScale = transformUniformScale.GetScale().GetMaxElement();
transformUniformScale.SetScale(AZ::Vector3(maxScale));
transformUniformScale.SetUniformScale(transformUniformScale.GetUniformScale());
return transformUniformScale;
}
@@ -124,7 +124,7 @@ namespace AzToolsFramework
"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);
instance->m_templateSourcePath = loaderInterface->GenerateRelativePath(instance->m_templateSourcePath);
TemplateId templateId = prefabSystemComponentInterface->GetTemplateIdFromFilePath(instance->GetTemplateSourcePath());
@@ -276,18 +276,14 @@ namespace AzToolsFramework
PrefabDomValueReference linkPatchesReference =
PrefabDomUtils::FindPrefabDomValue(linkDom, PrefabDomUtils::PatchesName);
// This logic only covers addition of patches. If patches already exists, the given list of patches must be appended to them.
if (!linkPatchesReference.has_value())
{
/*
If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the
linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to
associate them with the linkDom's allocator.
*/
PrefabDom patchesCopy;
patchesCopy.CopyFrom(patches, linkDom.GetAllocator());
linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator());
}
/*
If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the
linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to
associate them with the linkDom's allocator.
*/
PrefabDom patchesCopy;
patchesCopy.CopyFrom(patches, linkDom.GetAllocator());
linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator());
}
}
}
@@ -234,5 +234,10 @@ namespace AzToolsFramework
}
}
PrefabDomValueReference Link::GetLinkPatches()
{
return PrefabDomUtils::FindPrefabDomValue(m_linkDom, PrefabDomUtils::PatchesName);
}
} // namespace Prefab
} // namespace AzToolsFramework
@@ -79,6 +79,8 @@ namespace AzToolsFramework
*/
void AddLinkIdToInstanceDom(PrefabDomValue& instanceDomValue);
PrefabDomValueReference GetLinkPatches();
private:
/**
@@ -28,6 +28,7 @@ namespace AzToolsFramework
inline static const char* PatchesName = "Patches";
inline static const char* SourceName = "Source";
inline static const char* LinkIdName = "LinkId";
inline static const char* EntityIdName = "Id";
inline static const char* EntitiesName = "Entities";
inline static const char* ContainerEntityName = "ContainerEntity";
@@ -18,7 +18,9 @@
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/FileFunc/FileFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
@@ -41,7 +43,7 @@ namespace AzToolsFramework
[[maybe_unused]] bool result =
settingsRegistry->Get(m_projectPathWithOsSeparator.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath);
AZ_Assert(result, "Couldn't retrieve project root path");
AZ_Warning("Prefab", result, "Couldn't retrieve project root path");
m_projectPathWithSlashSeparator = AZ::IO::Path(m_projectPathWithOsSeparator.Native(), '/').MakePreferred();
AZ::Interface<PrefabLoaderInterface>::Register(this);
@@ -112,7 +114,7 @@ namespace AzToolsFramework
return InvalidTemplateId;
}
AZ::IO::Path relativePath = GetRelativePathToProject(originPath);
AZ::IO::Path relativePath = GenerateRelativePath(originPath);
// Cyclical dependency detected if the prefab file is already part of the progressed
// file path set.
@@ -301,6 +303,45 @@ namespace AzToolsFramework
return true;
}
bool PrefabLoader::SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath)
{
AZ_Assert(absolutePath.IsAbsolute(), "SaveTemplateToFile requires an absolute path for saving the initial prefab file.");
const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId);
if (!domAndFilepath)
{
return false;
}
// Verify that the absolute path provided to this matches the relative path saved in the template.
// Otherwise, the saved prefab won't be able to be loaded.
auto relativePath = GenerateRelativePath(absolutePath);
if (relativePath != domAndFilepath->second)
{
AZ_Error(
"Prefab", false,
"PrefabLoader::SaveTemplateToFile - "
"Failed to save template '%s' to location '%.*s'."
"Error: Relative path '%.*s' for location didn't match template name.",
domAndFilepath->second.c_str(), AZ_STRING_ARG(absolutePath.Native()), AZ_STRING_ARG(relativePath.Native()));
return false;
}
auto outcome = AzFramework::FileFunc::WriteJsonFile(domAndFilepath->first, absolutePath);
if (!outcome.IsSuccess())
{
AZ_Error(
"Prefab", false,
"PrefabLoader::SaveTemplateToFile - "
"Failed to save template '%s' to location '%.*s'."
"Error: %s",
domAndFilepath->second.c_str(), AZ_STRING_ARG(absolutePath.Native()), 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);
@@ -385,21 +426,100 @@ namespace AzToolsFramework
AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path).MakePreferred();
if (pathWithOSSeparator.IsAbsolute())
{
// If an absolute path was passed in, just return it as-is.
return path;
}
return AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator);
// A relative path was passed in, so try to turn it back into an absolute path.
AZ::IO::Path fullPath;
bool pathFound = false;
AZ::Data::AssetInfo assetInfo;
AZStd::string rootFolder;
AZStd::string inputPath(path.Native());
// Given an input path that's expected to exist, try to look it up.
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
pathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath,
inputPath.c_str(), assetInfo, rootFolder);
if (pathFound)
{
// The asset system provided us with a valid root folder and relative path, so return it.
fullPath = AZ::IO::Path(rootFolder) / assetInfo.m_relativePath;
}
else
{
// If for some reason the Asset system couldn't provide a relative path, provide some fallback logic.
// Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow
// the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside
// a unit test, so just execute the fallback logic without an error.
[[maybe_unused]] bool assetProcessorReady = false;
AzFramework::AssetSystemRequestBus::BroadcastResult(
assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady);
AZ_Error(
"Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.",
AZ_STRING_ARG(path.Native()));
// If a relative path was passed in, make it relative to the project root.
fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator);
}
return fullPath;
}
AZ::IO::Path PrefabLoader::GetRelativePathToProject(AZ::IO::PathView path)
AZ::IO::Path PrefabLoader::GenerateRelativePath(AZ::IO::PathView path)
{
AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path.Native()).MakePreferred();
if (!pathWithOSSeparator.IsAbsolute())
bool pathFound = false;
AZStd::string relativePath;
AZStd::string rootFolder;
AZ::IO::Path finalPath;
// The asset system allows for paths to be relative to multiple root folders, using a priority system.
// This request will make the input path relative to the most appropriate, highest-priority root folder.
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
pathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GenerateRelativeSourcePath, path.Native(),
relativePath, rootFolder);
if (pathFound && !relativePath.empty())
{
return path;
// A relative path was generated successfully, so return it.
finalPath = relativePath;
}
else
{
// If for some reason the Asset system couldn't provide a relative path, provide some fallback logic.
// Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow
// the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside
// a unit test, so just execute the fallback logic without an error.
[[maybe_unused]] bool assetProcessorReady = false;
AzFramework::AssetSystemRequestBus::BroadcastResult(
assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady);
AZ_Error("Prefab", !assetProcessorReady,
"Relative source path for '%.*s' could not be determined. Using project path as relative root.",
AZ_STRING_ARG(path.Native()));
AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path.Native()).MakePreferred();
if (pathWithOSSeparator.IsAbsolute())
{
// If an absolute path was passed in, make it relative to the project path.
finalPath = AZ::IO::Path(path.Native(), '/').MakePreferred().LexicallyRelative(m_projectPathWithSlashSeparator);
}
else
{
// If a relative path was passed in, just return it.
finalPath = path;
}
}
return AZ::IO::Path(path.Native(), '/').MakePreferred().LexicallyRelative(m_projectPathWithSlashSeparator);
return finalPath;
}
AZ::IO::Path PrefabLoaderInterface::GeneratePath()
@@ -72,6 +72,16 @@ namespace AzToolsFramework
*/
bool SaveTemplate(TemplateId templateId) override;
/**
* Saves a Prefab Template to the provided absolute source path, which needs to match the relative path in 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
* @param absolutePath Absolute path to save the file to
* @return bool on whether the operation succeeded or not
*/
bool SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) override;
/**
* Saves a Prefab Template into the provided output string.
* Converts Prefab Template form into .prefab form by collapsing nested Template info
@@ -91,9 +101,11 @@ namespace AzToolsFramework
//! 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;
//! Converts path into a path that's relative to the highest-priority containing folder of all the folders registered
//! with the engine.
//! This path will be the path that appears in the .prefab file.
//! The path will always use the '/' separator.
AZ::IO::Path GenerateRelativePath(AZ::IO::PathView path) override;
//! Returns if the path is a valid path for a prefab
static bool IsValidPrefabPath(AZ::IO::PathView path);
@@ -60,6 +60,16 @@ namespace AzToolsFramework
*/
virtual bool SaveTemplate(TemplateId templateId) = 0;
/**
* Saves a Prefab Template to the provided absolute source path, which needs to match the relative path in 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
* @param absolutePath Absolute path to save the file to
* @return bool on whether the operation succeeded or not
*/
virtual bool SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) = 0;
/**
* Saves a Prefab Template into the provided output string.
* Converts Prefab Template form into .prefab form by collapsing nested Template info
@@ -74,9 +84,11 @@ namespace AzToolsFramework
//! 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;
//! Converts path into a path that's relative to the highest-priority containing folder of all the folders registered
//! with the engine.
//! This path will be the path that appears in the .prefab file.
//! The path will always use the '/' separator.
virtual AZ::IO::Path GenerateRelativePath(AZ::IO::PathView path) = 0;
protected:
@@ -10,9 +10,9 @@
*
*/
#include <AzToolsFramework/Prefab/PrefabPublicHandler.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/JSON/stringbuffer.h>
#include <AzCore/JSON/writer.h>
#include <AzCore/Utils/TypeHash.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
@@ -26,11 +26,14 @@
#include <AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicHandler.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/PrefabUndo.h>
#include <AzToolsFramework/Prefab/PrefabUndoHelpers.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <QString>
namespace AzToolsFramework
{
namespace Prefab
@@ -61,7 +64,7 @@ namespace AzToolsFramework
m_prefabUndoCache.Destroy();
}
PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath)
PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView absolutePath)
{
EntityList inputEntityList, topLevelEntities;
AZ::EntityId commonRootEntityId;
@@ -73,6 +76,8 @@ namespace AzToolsFramework
return findCommonRootOutcome;
}
AZ_Assert(absolutePath.IsAbsolute(), "CreatePrefab requires an absolute path for saving the initial prefab file.");
InstanceOptionalReference instanceToCreate;
{
// Initialize Undo Batch object
@@ -83,7 +88,8 @@ namespace AzToolsFramework
commonRootInstanceDomBeforeCreate, commonRootEntityOwningInstance->get());
AZStd::vector<AZ::Entity*> entities;
AZStd::vector<AZStd::unique_ptr<Instance>> instances;
AZStd::vector<AZStd::unique_ptr<Instance>> instancePtrs;
AZStd::vector<Instance*> instances;
AZStd::unordered_map<Instance*, PrefabDom> nestedInstanceLinkPatchesMap;
// Retrieve all entities affected and identify Instances
@@ -93,21 +99,38 @@ namespace AzToolsFramework
AZStd::string("Could not create a new prefab out of the entities provided - invalid selection."));
}
AZStd::unordered_map<AZ::EntityId, AZStd::string> oldEntityAliases;
// Detach the retrieved entities
for (AZ::Entity* entity : entities)
{
AZ::EntityId entityId = entity->GetId();
oldEntityAliases.emplace(entityId, commonRootEntityOwningInstance->get().GetEntityAlias(entityId)->get());
commonRootEntityOwningInstance->get().DetachEntity(entity->GetId()).release();
}
// When we create a prefab with other prefab instances, we have to remove the existing links between the source and
// target templates of the other instances.
for (auto& nestedInstance : instances)
{
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId());
AZStd::unique_ptr<Instance> outInstance = commonRootEntityOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias());
if (linkRef.has_value())
{
PrefabDom oldLinkPatches;
oldLinkPatches.CopyFrom(linkRef->get().GetLinkDom(), oldLinkPatches.GetAllocator());
LinkId detachingInstanceLinkId = nestedInstance->GetLinkId();
auto linkRef = m_prefabSystemComponentInterface->FindLink(detachingInstanceLinkId);
AZ_Assert(linkRef.has_value(), "Unable to find link with id '%llu' during prefab creation.", detachingInstanceLinkId);
nestedInstanceLinkPatchesMap.emplace(nestedInstance.get(), AZStd::move(oldLinkPatches));
}
PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches();
AZ_Assert(
linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.",
detachingInstanceLinkId);
PrefabDom linkPatchesCopy;
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
nestedInstanceLinkPatchesMap.emplace(nestedInstance, AZStd::move(linkPatchesCopy));
RemoveLink(nestedInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch());
RemoveLink(outInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch());
instancePtrs.emplace_back(AZStd::move(outInstance));
}
PrefabUndoHelpers::UpdatePrefabInstance(
@@ -123,7 +146,8 @@ namespace AzToolsFramework
// Create the Prefab
instanceToCreate = prefabEditorEntityOwnershipInterface->CreatePrefab(
entities, AZStd::move(instances), filePath, commonRootEntityOwningInstance);
entities, AZStd::move(instancePtrs), m_prefabLoaderInterface->GenerateRelativePath(absolutePath),
commonRootEntityOwningInstance);
if (!instanceToCreate)
{
@@ -167,6 +191,24 @@ namespace AzToolsFramework
if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get()))
{
previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
previousPatch.Accept(writer);
QString previousPatchString(buffer.GetString());
for (AZ::Entity* entity : entities)
{
AZ::EntityId entityId = entity->GetId();
AZStd::string oldEntityAlias = oldEntityAliases[entityId];
EntityAliasOptionalReference newEntityAlias = instanceToCreate->get().GetEntityAlias(entityId);
AZ_Assert(
newEntityAlias.has_value(),
"Could not fetch entity alias for entity with id '%llu' during prefab creation.",
static_cast<AZ::u64>(entityId));
ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get());
}
previousPatch.Parse(previousPatchString.toUtf8().constData());
}
// These link creations shouldn't be undone because that would put the template in a non-usable state if a user
@@ -188,36 +230,23 @@ namespace AzToolsFramework
m_instanceToTemplateInterface->GeneratePatch(reparentPatch, containerEntityDomBefore, containerEntityDomAfter);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(reparentPatch, nestedInstanceContainerEntityId);
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes as a separate step
m_prefabUndoCache.Store(nestedInstanceContainerEntityId, AZStd::move(containerEntityDomAfter));
// Save these changes as patches to the link
PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(nestedInstanceContainerEntityId)));
linkUpdate->SetParent(undoBatch.GetUndoBatch());
linkUpdate->Capture(reparentPatch, nestedInstance->GetLinkId());
linkUpdate->Redo();
// We won't parent this undo node to the undo batch so that the newly created template and link will remain
// unaffected by undo actions. This is needed so that any future instantiations of the template will work.
PrefabUndoLinkUpdate linkUpdate = PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(nestedInstanceContainerEntityId)));
linkUpdate.Capture(reparentPatch, nestedInstance->GetLinkId());
linkUpdate.Redo();
}
});
// Create a link between the templates of the newly created instance and the instance it's being parented under.
CreateLink(
instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(),
AZStd::move(patch));
for (AZ::Entity* topLevelEntity : topLevelEntities)
{
AZ::EntityId topLevelEntityId = topLevelEntity->GetId();
if (topLevelEntityId.IsValid())
{
m_prefabUndoCache.UpdateCache(topLevelEntity->GetId());
// Parenting entities would mark entities as dirty. But we want to unmark the top level entities as dirty because
// if we don't, the template created would be updated and cause issues with undo operation followed by instantiation.
ToolsApplicationRequests::Bus::Broadcast(
&ToolsApplicationRequests::Bus::Events::RemoveDirtyEntity, topLevelEntity->GetId());
}
}
// This clears any entities marked as dirty due to reparenting of entities during the process of creating a prefab.
// We are doing this so that the changes in those enities are not queued up twice for propagation.
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
&AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
// Select Container Entity
{
@@ -228,7 +257,7 @@ namespace AzToolsFramework
}
// Save Template to file
m_prefabLoaderInterface->SaveTemplate(instanceToCreate->get().GetTemplateId());
m_prefabLoaderInterface->SaveTemplateToFile(instanceToCreate->get().GetTemplateId(), absolutePath);
return AZ::Success();
}
@@ -292,7 +321,7 @@ namespace AzToolsFramework
}
//Detect whether this instantiation would produce a cyclical dependency
auto relativePath = m_prefabLoaderInterface->GetRelativePathToProject(filePath);
auto relativePath = m_prefabLoaderInterface->GenerateRelativePath(filePath);
Prefab::TemplateId templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(relativePath);
if (templateId == InvalidTemplateId)
@@ -388,7 +417,7 @@ namespace AzToolsFramework
// Find common root and top level entities
bool entitiesHaveCommonRoot = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
entitiesHaveCommonRoot, &AzToolsFramework::ToolsApplicationRequests::FindCommonRootInactive, inputEntityList,
commonRootEntityId, &topLevelEntities);
@@ -710,6 +739,143 @@ namespace AzToolsFramework
return DeleteFromInstance(entityIds, true);
}
PrefabOperationResult PrefabPublicHandler::DuplicateEntitiesInInstance(const EntityIdList& entityIds)
{
if (entityIds.empty())
{
return AZ::Failure(AZStd::string("No entities to duplicate."));
}
if (!EntitiesBelongToSameInstance(entityIds))
{
return AZ::Failure(AZStd::string("Cannot duplicate multiple "
"entities belonging to different instances with one operation."));
}
// We've already verified the entities are all owned by the same instance,
// so we can just retrieve our instance from the first entity in the list.
InstanceOptionalReference commonEntityOwningInstance = GetOwnerInstanceByEntityId(entityIds[0]);
AZ_Assert(
commonEntityOwningInstance.has_value(),
"Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided");
// This will cull out any entities that have ancestors in the list, since we will end up duplicating
// the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances
AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIds);
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
ScopedUndoBatch undoBatch("Duplicate Entities");
{
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "DuplicateEntitiesInInstance::UndoCaptureAndDuplicateEntities");
// Take a snapshot of the instance DOM before we manipulate it
Prefab::PrefabDom instanceDomBefore;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonEntityOwningInstance->get());
AZStd::vector<AZ::Entity*> entities;
AZStd::vector<Instance*> instances;
// Gather all entities/instances in the hierarchy, but don't detach them because we are duplicating not deleting.
EntityList inputEntityList = EntityIdSetToEntityList(duplicationSet);
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonEntityOwningInstance->get(), entities, instances);
if (!success)
{
return AZ::Failure(AZStd::string("Failed to retrieve entities and instances from the given list of entity ids for duplication"));
}
// Make a copy of our before instance DOM where we will add our duplicated entities
Prefab::PrefabDom instanceDomAfter;
instanceDomAfter.CopyFrom(instanceDomBefore, instanceDomAfter.GetAllocator());
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
for (AZ::Entity* entity : entities)
{
EntityAliasOptionalReference oldAliasRef = commonEntityOwningInstance->get().GetEntityAlias(entity->GetId());
AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM");
EntityAlias oldAlias = oldAliasRef.value();
// Give this the outer allocator so that the memory reference will be valid when
// it gets used for AddMember
Prefab::PrefabDom entityDomBefore(&instanceDomAfter.GetAllocator());
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity);
// Keep track of the old alias <-> new alias mapping for this duplicated entity
// so we can fixup references later
EntityAlias newEntityAlias = Instance::GenerateEntityAlias();
oldAliasToNewAliasMap.insert(AZStd::make_pair(oldAlias, newEntityAlias));
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
entityDomBefore.Accept(writer);
// Store our duplicated Entity DOM with its new alias as a string
// so that we can fixup entity alias references before adding it
// to the Entities member of our instance DOM
QString entityDomString(buffer.GetString());
aliasToEntityDomMap.insert(AZStd::make_pair(newEntityAlias, entityDomString));
}
auto entitiesIter = instanceDomAfter.FindMember(PrefabDomUtils::EntitiesName);
AZ_Assert(entitiesIter != instanceDomAfter.MemberEnd(), "Instance DOM missing the Entities member.");
// Now that all the duplicated Entity DOMs have been created, we need to iterate
// through them and replace any previous EntityAlias references with the new ones.
// These are more than just parent entity references for nested entities, this will
// also cover any EntityId references that were made in the components between them.
for (auto aliasEntityPair : aliasToEntityDomMap)
{
EntityAlias newEntityAlias = aliasEntityPair.first;
QString newEntityDomString = aliasEntityPair.second;
// Replace all of the old alias references with the new ones
// We bookend the aliases with \" and also with a / as an extra precaution to prevent
// inadvertently replacing a matching string vs. where an actual EntityId is expected
// This will cover both cases where an alias could be used in a normal entity vs. an instance
for (auto aliasMapIter : oldAliasToNewAliasMap)
{
ReplaceOldAliases(newEntityDomString, aliasMapIter.first, aliasMapIter.second);
}
// Create the new Entity DOM from parsing the JSON string
Prefab::PrefabDom entityDomAfter(&instanceDomAfter.GetAllocator());
entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
// Add the new Entity DOM to the Entities member of the instance
rapidjson::Value aliasName(newEntityAlias.c_str(), newEntityAlias.length(), instanceDomAfter.GetAllocator());
entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, instanceDomAfter.GetAllocator());
}
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity duplication");
command->SetParent(undoBatch.GetUndoBatch());
command->Capture(instanceDomBefore, instanceDomAfter, commonEntityOwningInstance->get().GetTemplateId());
command->RunRedo();
EntityIdList duplicatedEntityIds;
for (auto aliasMapIter : oldAliasToNewAliasMap)
{
EntityAlias newEntityAlias = aliasMapIter.second;
AliasPath absoluteEntityPath = commonEntityOwningInstance->get().GetAbsoluteInstanceAliasPath();
absoluteEntityPath.Append(newEntityAlias);
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
duplicatedEntityIds.push_back(newEntityId);
}
// Select the duplicated entities
auto selectionUndo = aznew SelectionCommand(duplicatedEntityIds, "Select Duplicated Entities");
selectionUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
}
return AZ::Success();
}
PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants)
{
if (entityIds.empty())
@@ -737,17 +903,7 @@ namespace AzToolsFramework
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.");
}
ScopedUndoBatch undoBatch("Delete Selected");
// In order to undo DeleteSelected, we have to create a selection command which selects the current selection
// and then add the deletion as children.
@@ -775,7 +931,7 @@ namespace AzToolsFramework
if (deleteDescendants)
{
AZStd::vector<AZ::Entity*> entities;
AZStd::vector<AZStd::unique_ptr<Instance>> instances;
AZStd::vector<Instance*> instances;
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
@@ -786,13 +942,15 @@ namespace AzToolsFramework
for (AZ::Entity* entity : entities)
{
commonOwningInstance->get().DetachEntity(entity->GetId()).release();
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, entity->GetId());
}
for (auto& nestedInstance : instances)
{
RemoveLink(nestedInstance, commonOwningInstance->get().GetTemplateId(), currentUndoBatch);
nestedInstance.reset();
AZStd::unique_ptr<Instance> outInstance = commonOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias());
RemoveLink(outInstance, commonOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch());
outInstance.reset();
}
}
else
@@ -804,7 +962,7 @@ namespace AzToolsFramework
if (owningInstance->get().GetContainerEntityId() == entityId)
{
auto instancePtr = commonOwningInstance->get().DetachNestedInstance(owningInstance->get().GetInstanceAlias());
RemoveLink(instancePtr, commonOwningInstance->get().GetTemplateId(), currentUndoBatch);
RemoveLink(instancePtr, commonOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch());
}
else
{
@@ -822,17 +980,12 @@ namespace AzToolsFramework
command->SetParent(selCommand);
}
selCommand->SetParent(currentUndoBatch);
selCommand->SetParent(undoBatch.GetUndoBatch());
{
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DeleteEntities:RunRedo");
selCommand->RunRedo();
}
if (createdUndo)
{
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::EndUndoBatch);
}
return AZ::Success();
}
@@ -944,7 +1097,7 @@ namespace AzToolsFramework
bool PrefabPublicHandler::RetrieveAndSortPrefabEntitiesAndInstances(
const EntityList& inputEntities, Instance& commonRootEntityOwningInstance,
EntityList& outEntities, AZStd::vector<AZStd::unique_ptr<Instance>>& outInstances) const
EntityList& outEntities, AZStd::vector<Instance*>& outInstances) const
{
if (inputEntities.size() == 0)
{
@@ -1028,14 +1181,14 @@ namespace AzToolsFramework
for (AZ::Entity* entity : entities)
{
outEntities.emplace_back(commonRootEntityOwningInstance.DetachEntity(entity->GetId()).release());
outEntities.emplace_back(entity);
}
outInstances.clear();
outInstances.reserve(instances.size());
for (Instance* instancePtr : instances)
{
outInstances.push_back(AZStd::move(commonRootEntityOwningInstance.DetachNestedInstance(instancePtr->GetInstanceAlias())));
outInstances.push_back(instancePtr);
}
return (outEntities.size() + outInstances.size()) > 0;
@@ -1086,5 +1239,18 @@ namespace AzToolsFramework
return true;
}
void PrefabPublicHandler::ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias)
{
QString oldAliasQuotes = QString("\"%1\"").arg(oldAlias.data());
QString newAliasQuotes = QString("\"%1\"").arg(newAlias.data());
stringToReplace.replace(oldAliasQuotes, newAliasQuotes);
QString oldAliasPathRef = QString("/%1").arg(oldAlias.data());
QString newAliasPathRef = QString("/%1").arg(newAlias.data());
stringToReplace.replace(oldAliasPathRef, newAliasPathRef);
}
} // namespace Prefab
} // namespace AzToolsFramework
@@ -14,12 +14,15 @@
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/string/string_view.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/PrefabUndoCache.h>
class QString;
namespace AzToolsFramework
{
using EntityList = AZStd::vector<AZ::Entity*>;
@@ -27,7 +30,6 @@ namespace AzToolsFramework
namespace Prefab
{
class Instance;
class InstanceEntityMapperInterface;
class InstanceToTemplateInterface;
class PrefabLoaderInterface;
@@ -44,7 +46,7 @@ namespace AzToolsFramework
void UnregisterPrefabPublicHandlerInterface();
// PrefabPublicInterface...
PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath) override;
PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView absolutePath) override;
PrefabOperationResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) override;
PrefabOperationResult SavePrefab(AZ::IO::Path filePath) override;
PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) override;
@@ -60,11 +62,12 @@ namespace AzToolsFramework
PrefabOperationResult DeleteEntitiesInInstance(const EntityIdList& entityIds) override;
PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) override;
PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) override;
private:
PrefabOperationResult DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants);
bool RetrieveAndSortPrefabEntitiesAndInstances(const EntityList& inputEntities, Instance& commonRootEntityOwningInstance,
EntityList& outEntities, AZStd::vector<AZStd::unique_ptr<Instance>>& outInstances) const;
EntityList& outEntities, AZStd::vector<Instance*>& outInstances) const;
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
@@ -129,6 +132,8 @@ namespace AzToolsFramework
bool IsCyclicalDependencyFound(
InstanceOptionalConstReference instance, const AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths);
void ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias);
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);
@@ -46,10 +46,10 @@ namespace AzToolsFramework
* Create a prefab out of the entities provided, at the path provided.
* Automatically detects descendants of entities, and discerns between entities and child instances.
* @param entityIds The entities that should form the new prefab (along with their descendants).
* @param filePath The path for the new prefab file.
* @param filePath The absolute path for the new prefab file.
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
*/
virtual PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath) = 0;
virtual PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView absolutePath) = 0;
/**
* Instantiate a prefab from a prefab file.
@@ -143,6 +143,13 @@ namespace AzToolsFramework
* @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;
/**
* Duplicates all entities in the owning instance. Bails if the entities don't all belong to the same instance.
* @param entities The entities to duplicate.
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
*/
virtual PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) = 0;
};
} // namespace Prefab
@@ -95,7 +95,7 @@ namespace AzToolsFramework
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity, bool shouldCreateLinks)
{
AZ::IO::Path relativeFilePath = m_prefabLoader.GetRelativePathToProject(filePath);
AZ::IO::Path relativeFilePath = m_prefabLoader.GenerateRelativePath(filePath);
if (GetTemplateIdFromFilePath(relativeFilePath) != InvalidTemplateId)
{
AZ_Error("Prefab", false,
@@ -10,7 +10,7 @@
*
*/
#include <AzCore/Casting/lossy_cast.h>
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
#include <AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h>
namespace AzToolsFramework::Prefab::PrefabConversionUtils
@@ -73,8 +73,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
uint32_t ProcessedObjectStore::BuildSubId(AZStd::string_view id)
{
AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size());
return azlossy_caster(subIdHash.GetHash());
return AzFramework::SpawnableAssetHandler::BuildSubId(id);
}
const AZStd::string& ProcessedObjectStore::GetId() const
@@ -24,17 +24,6 @@
namespace AzToolsFramework::Prefab::SpawnableUtils
{
AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom)
{
AzFramework::Spawnable spawnable;
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> referencedAssets;
[[maybe_unused]] bool result = CreateSpawnable(spawnable, prefabDom, referencedAssets);
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)
{
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> referencedAssets;
@@ -17,7 +17,6 @@
namespace AzToolsFramework::Prefab::SpawnableUtils
{
AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom);
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom);
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets);
@@ -1475,10 +1475,8 @@ namespace AzToolsFramework
// to avoid pushing them to the slice.
// Only scale is preserved on the root entity of a slice.
transformComponent->SetParent(AZ::EntityId());
AZ::Vector3 scale = transformComponent->GetLocalScale();
transformComponent->SetWorldTranslation(AZ::Vector3::CreateZero());
transformComponent->SetLocalRotation(AZ::Vector3::CreateZero());
transformComponent->SetLocalScale(scale);
}
}
@@ -1,22 +1,22 @@
/*
* 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.
*
*/
* 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 <AzToolsFramework/ToolsComponents/EditorVisibilityBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityBus.h>
namespace AzToolsFramework
{
@@ -31,7 +31,7 @@ namespace AzToolsFramework
To use the EditorComponentAdapter, 3 classes are required:
- a class that implements the functions required for TController (see below)
- a configuration struct/class which extends AZ::ComponentConfig
- A runtime component that will be generated by the editor comoinent on export
- A runtime component that will be generated by the editor component on export
The concrete component extends the adapter and implements behavior which is unique to the component.
@@ -64,15 +64,15 @@ namespace AzToolsFramework
the EditContext. TController can friend itself to the editor component to make this work if required.
*/
template<typename TController, typename TRuntimeComponent, typename TConfiguration = AZ::ComponentConfig>
class EditorComponentAdapter
: public EditorComponentBase
class EditorComponentAdapter : public EditorComponentBase
{
public:
AZ_RTTI((EditorComponentAdapter, "{2F5A3669-FFE9-4CD7-B9E2-7FC8100CF1A2}", TController, TRuntimeComponent, TConfiguration), EditorComponentBase);
AZ_RTTI(
(EditorComponentAdapter, "{2F5A3669-FFE9-4CD7-B9E2-7FC8100CF1A2}", TController, TRuntimeComponent, TConfiguration),
EditorComponentBase);
EditorComponentAdapter() = default;
EditorComponentAdapter(const TConfiguration& configuration);
explicit EditorComponentAdapter(const TConfiguration& configuration);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
@@ -86,7 +86,6 @@ namespace AzToolsFramework
void BuildGameEntity(AZ::Entity* gameEntity) override;
protected:
static void Reflect(AZ::ReflectContext* context);
// AZ::Component overrides ...
@@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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 <AzFramework/Components/ComponentAdapterHelpers.h>
@@ -28,23 +28,21 @@ namespace AzToolsFramework
template<typename TController, typename TRuntimeComponent, typename TConfiguration>
void EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<EditorComponentAdapter, EditorComponentBase>()
->Version(1)
->Field("Controller", &EditorComponentAdapter::m_controller)
;
serializeContext->Class<EditorComponentAdapter, EditorComponentBase>()->Version(1)->Field(
"Controller", &EditorComponentAdapter::m_controller);
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorComponentAdapter>(
"EditorComponentAdapter", "")
// clang-format off
editContext->Class<EditorComponentAdapter>("EditorComponentAdapter", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorComponentAdapter::m_controller, "Controller", "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorComponentAdapter::OnConfigurationChanged)
;
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorComponentAdapter::OnConfigurationChanged);
// clang-format on
}
}
}
@@ -53,27 +51,35 @@ namespace AzToolsFramework
// Get*Services functions
template<typename TController, typename TRuntimeComponent, typename TConfiguration>
void EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
void EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::GetProvidedServices(
AZ::ComponentDescriptor::DependencyArrayType& services)
{
AzFramework::Components::GetProvidedServicesHelper<TController>(services, typename AZ::HasComponentProvidedServices<TController>::type());
AzFramework::Components::GetProvidedServicesHelper<TController>(
services, typename AZ::HasComponentProvidedServices<TController>::type());
}
template<typename TController, typename TRuntimeComponent, typename TConfiguration>
void EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
void EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::GetRequiredServices(
AZ::ComponentDescriptor::DependencyArrayType& services)
{
AzFramework::Components::GetRequiredServicesHelper<TController>(services, typename AZ::HasComponentRequiredServices<TController>::type());
AzFramework::Components::GetRequiredServicesHelper<TController>(
services, typename AZ::HasComponentRequiredServices<TController>::type());
}
template<typename TController, typename TRuntimeComponent, typename TConfiguration>
void EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
void EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::GetIncompatibleServices(
AZ::ComponentDescriptor::DependencyArrayType& services)
{
AzFramework::Components::GetIncompatibleServicesHelper<TController>(services, typename AZ::HasComponentIncompatibleServices<TController>::type());
AzFramework::Components::GetIncompatibleServicesHelper<TController>(
services, typename AZ::HasComponentIncompatibleServices<TController>::type());
}
template<typename TController, typename TRuntimeComponent, typename TConfiguration>
void EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services)
void EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::GetDependentServices(
AZ::ComponentDescriptor::DependencyArrayType& services)
{
AzFramework::Components::GetDependentServicesHelper<TController>(services, typename AZ::HasComponentDependentServices<TController>::type());
AzFramework::Components::GetDependentServicesHelper<TController>(
services, typename AZ::HasComponentDependentServices<TController>::type());
}
//////////////////////////////////////////////////////////////////////////
@@ -99,7 +105,8 @@ namespace AzToolsFramework
if (ShouldActivateController())
{
m_controller.Activate(GetEntityId());
AzFramework::Components::ComponentActivateHelper<TController>::Activate(
m_controller, AZ::EntityComponentIdPair(GetEntityId(), GetId()));
}
}
@@ -122,7 +129,8 @@ namespace AzToolsFramework
}
template<typename TController, typename TRuntimeComponent, typename TConfiguration>
bool EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
bool EditorComponentAdapter<TController, TRuntimeComponent, TConfiguration>::WriteOutConfig(
AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<TConfiguration*>(outBaseConfig))
{
@@ -139,7 +147,8 @@ namespace AzToolsFramework
if (ShouldActivateController())
{
m_controller.Activate(GetEntityId());
AzFramework::Components::ComponentActivateHelper<TController>::Activate(
m_controller, AZ::EntityComponentIdPair(GetEntityId(), GetId()));
}
return AZ::Edit::PropertyRefreshLevels::None;
@@ -17,6 +17,7 @@
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EditorViewportIconDisplayInterface.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityBus.h>
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
@@ -313,8 +314,7 @@ namespace AzToolsFramework
// if we do not yet have a valid texture id, request it using the entity icon path
if (m_entityIconTextureId == 0)
{
EditorRequestBus::BroadcastResult(
m_entityIconTextureId, &EditorRequests::GetIconTextureIdFromEntityIconPath, m_entityIconPath);
m_entityIconTextureId = EditorViewportIconDisplay::Get()->GetOrLoadIconForPath(m_entityIconPath);
}
return m_entityIconTextureId;
@@ -28,7 +28,7 @@ namespace AzToolsFramework
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(worldFromLocal, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
worldFromLocal.ExtractScale();
worldFromLocal.ExtractUniformScale();
m_manipulators = AZStd::make_unique<ScaleManipulators>(worldFromLocal);
m_manipulators->Register(g_mainManipulatorManagerId);
m_manipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
@@ -32,7 +32,6 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/ToolsComponents/TransformComponentBus.h>
#include <AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.h>
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponentBus.h>
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionBus.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
@@ -50,10 +49,10 @@ namespace AzToolsFramework
{
const AZ::u32 ParentEntityCRC = AZ_CRC("Parent Entity", 0x5b1b276c);
// Decompose a transform into euler angles in degrees, scale (along basis, any shear will be dropped), and translation.
void DecomposeTransform(const AZ::Transform& transform, AZ::Vector3& translation, AZ::Vector3& rotation, AZ::Vector3& scale)
// Decompose a transform into euler angles in degrees, uniform scale, and translation.
void DecomposeTransform(const AZ::Transform& transform, AZ::Vector3& translation, AZ::Vector3& rotation, float& scale)
{
scale = transform.GetScale();
scale = transform.GetUniformScale();
translation = transform.GetTranslation();
rotation = transform.GetRotation().GetEulerDegrees();
}
@@ -120,7 +119,7 @@ namespace AzToolsFramework
// Decompose the old slice-relative transform and set it as a our editor transform,
// since the entity is now our parent.
EditorTransform editorTransform;
DecomposeTransform(sliceRelTransform, editorTransform.m_translate, editorTransform.m_rotate, editorTransform.m_scale);
DecomposeTransform(sliceRelTransform, editorTransform.m_translate, editorTransform.m_rotate, editorTransform.m_uniformScale);
editorTransformElement.Convert<EditorTransform>(context);
editorTransformElement.SetData(context, editorTransform);
}
@@ -170,6 +169,23 @@ namespace AzToolsFramework
return true;
}
bool EditorTransformDataConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() < 3)
{
// version 3 replaces vector scale with uniform scale but does not yet delete the legacy scale data
// in order to allow for migration
AZ::Vector3 vectorScale;
if (classElement.FindSubElementAndGetData<AZ::Vector3>(AZ_CRC_CE("Scale"), vectorScale))
{
const float uniformScale = vectorScale.GetMaxElement();
classElement.AddElementWithData(context, "UniformScale", uniformScale);
}
}
return true;
}
} // namespace Internal
TransformComponent::TransformComponent()
@@ -357,7 +373,7 @@ namespace AzToolsFramework
AZ::Transform TransformComponent::GetLocalScaleTM() const
{
return AZ::Transform::CreateScale(m_editorTransform.m_scale);
return AZ::Transform::CreateUniformScale(m_editorTransform.m_uniformScale);
}
const AZ::Transform& TransformComponent::GetLocalTM()
@@ -374,12 +390,13 @@ namespace AzToolsFramework
// given a local transform, update local transform.
void TransformComponent::SetLocalTM(const AZ::Transform& finalTx)
{
AZ::Vector3 tx, rot, scale;
Internal::DecomposeTransform(finalTx, tx, rot, scale);
AZ::Vector3 tx, rot;
float uniformScale;
Internal::DecomposeTransform(finalTx, tx, rot, uniformScale);
m_editorTransform.m_translate = tx;
m_editorTransform.m_rotate = rot;
m_editorTransform.m_scale = scale;
m_editorTransform.m_uniformScale = uniformScale;
TransformChanged();
}
@@ -520,91 +537,13 @@ namespace AzToolsFramework
return m_editorTransform.m_translate.GetZ();
}
void TransformComponent::SetRotation(const AZ::Vector3& eulerAnglesRadians)
void TransformComponent::SetWorldRotationQuaternion(const AZ::Quaternion& quaternion)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotation is deprecated, please use SetLocalRotation");
AZ::Transform newWorldTransform = GetWorldTM();
newWorldTransform.SetRotation(AZ::ConvertEulerRadiansToQuaternion(eulerAnglesRadians));
SetWorldTM(newWorldTransform);
}
void TransformComponent::SetRotationQuaternion(const AZ::Quaternion& quaternion)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationQuaternion is deprecated, please use SetLocalRotation");
AZ::Transform newWorldTransform = GetWorldTM();
newWorldTransform.SetRotation(quaternion);
SetWorldTM(newWorldTransform);
}
void TransformComponent::SetRotationX(float eulerAngleRadians)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationX is deprecated, please use SetLocalRotation");
AZ::Transform newWorldTransform = GetWorldTM();
newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationX(eulerAngleRadians));
SetWorldTM(newWorldTransform);
}
void TransformComponent::SetRotationY(float eulerAngleRadians)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationY is deprecated, please use SetLocalRotation");
AZ::Transform newWorldTransform = GetWorldTM();
newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationY(eulerAngleRadians));
SetWorldTM(newWorldTransform);
}
void TransformComponent::SetRotationZ(float eulerAngleRadians)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "SetRotationZ is deprecated, please use SetLocalRotation");
AZ::Transform newWorldTransform = GetWorldTM();
newWorldTransform.SetRotation(AZ::Quaternion::CreateRotationZ(eulerAngleRadians));
SetWorldTM(newWorldTransform);
}
void TransformComponent::RotateByX(float eulerAngleRadians)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "RotateByX is deprecated, please use RotateAroundLocalX");
SetWorldTM(GetWorldTM() * AZ::Transform::CreateRotationX(eulerAngleRadians));
}
void TransformComponent::RotateByY(float eulerAngleRadians)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "RotateByY is deprecated, please use RotateAroundLocalY");
SetWorldTM(GetWorldTM() * AZ::Transform::CreateRotationY(eulerAngleRadians));
}
void TransformComponent::RotateByZ(float eulerAngleRadians)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "RotateByZ is deprecated, please use RotateAroundLocalZ");
SetWorldTM(GetWorldTM() * AZ::Transform::CreateRotationZ(eulerAngleRadians));
}
AZ::Vector3 TransformComponent::GetRotationEulerRadians()
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "GetRotationEulerRadians is deprecated, please use GetWorldRotation");
return GetWorldTM().GetRotation().GetEulerRadians();
}
AZ::Quaternion TransformComponent::GetRotationQuaternion()
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "GetRotationQuaternion is deprecated, please use GetWorldRotationQuaternion");
return GetWorldTM().GetRotation();
}
float TransformComponent::GetRotationX()
{
return GetRotationEulerRadians().GetX();
}
float TransformComponent::GetRotationY()
{
return GetRotationEulerRadians().GetY();
}
float TransformComponent::GetRotationZ()
{
return GetRotationEulerRadians().GetZ();
}
AZ::Vector3 TransformComponent::GetWorldRotation()
{
return GetWorldTM().GetRotation().GetEulerRadians();
@@ -677,108 +616,26 @@ namespace AzToolsFramework
return result;
}
void TransformComponent::SetScale(const AZ::Vector3& newScale)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "SetScale is deprecated, please use SetLocalScale");
AZ::Transform newWorldTransform = GetWorldTM();
AZ::Vector3 prevScale = newWorldTransform.ExtractScale();
if (!prevScale.IsClose(newScale))
{
newWorldTransform.MultiplyByScale(newScale);
SetWorldTM(newWorldTransform);
}
}
void TransformComponent::SetScaleX(float newScale)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "SetScaleX is deprecated, please use SetLocalScaleX");
AZ::Transform newWorldTransform = GetWorldTM();
AZ::Vector3 scale = newWorldTransform.ExtractScale();
scale.SetX(newScale);
newWorldTransform.MultiplyByScale(scale);
SetWorldTM(newWorldTransform);
}
void TransformComponent::SetScaleY(float newScale)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "SetScaleY is deprecated, please use SetLocalScaleY");
AZ::Transform newWorldTransform = GetWorldTM();
AZ::Vector3 scale = newWorldTransform.ExtractScale();
scale.SetY(newScale);
newWorldTransform.MultiplyByScale(scale);
SetWorldTM(newWorldTransform);
}
void TransformComponent::SetScaleZ(float newScale)
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "SetScaleZ is deprecated, please use SetLocalScaleZ");
AZ::Transform newWorldTransform = GetWorldTM();
AZ::Vector3 scale = newWorldTransform.ExtractScale();
scale.SetZ(newScale);
newWorldTransform.MultiplyByScale(scale);
SetWorldTM(newWorldTransform);
}
AZ::Vector3 TransformComponent::GetScale()
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "GetScale is deprecated, please use GetLocalScale");
return GetWorldTM().GetScale();
}
float TransformComponent::GetScaleX()
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "GetScaleX is deprecated, please use GetLocalScale");
return GetWorldTM().GetScale().GetX();
}
float TransformComponent::GetScaleY()
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "GetScaleY is deprecated, please use GetLocalScale");
return GetWorldTM().GetScale().GetY();
}
float TransformComponent::GetScaleZ()
{
AZ_Warning("AzToolsFramework::TransformComponent", false, "GetScaleZ is deprecated, please use GetLocalScale");
return GetWorldTM().GetScale().GetZ();
}
void TransformComponent::SetLocalScale(const AZ::Vector3& scale)
{
m_editorTransform.m_scale = scale;
TransformChanged();
}
void TransformComponent::SetLocalScaleX(float scaleX)
{
m_editorTransform.m_scale.SetX(scaleX);
TransformChanged();
}
void TransformComponent::SetLocalScaleY(float scaleY)
{
m_editorTransform.m_scale.SetY(scaleY);
TransformChanged();
}
void TransformComponent::SetLocalScaleZ(float scaleZ)
{
m_editorTransform.m_scale.SetZ(scaleZ);
TransformChanged();
}
AZ::Vector3 TransformComponent::GetLocalScale()
{
return m_editorTransform.m_scale;
AZ_WarningOnce("TransformComponent", false, "GetLocalScale is deprecated, please use GetLocalUniformScale instead");
return m_editorTransform.m_legacyScale;
}
AZ::Vector3 TransformComponent::GetWorldScale()
void TransformComponent::SetLocalUniformScale(float scale)
{
return GetWorldTM().GetScale();
m_editorTransform.m_uniformScale = scale;
TransformChanged();
}
float TransformComponent::GetLocalUniformScale()
{
return m_editorTransform.m_uniformScale;
}
float TransformComponent::GetWorldUniformScale()
{
return GetWorldTM().GetUniformScale();
}
const AZ::Transform& TransformComponent::GetParentWorldTM() const
@@ -1183,12 +1040,6 @@ namespace AzToolsFramework
ModifyEditorTransform(m_editorTransform.m_rotate, data, parent);
}
void TransformComponent::ScaleBy(const AZ::Vector3& data)
{
//scale is always local
ModifyEditorTransform(m_editorTransform.m_scale, data, AZ::Transform::Identity());
}
AZ::EntityId TransformComponent::GetSliceEntityParentId()
{
return GetParentId();
@@ -1297,9 +1148,10 @@ namespace AzToolsFramework
serializeContext->Class<EditorTransform>()->
Field("Translate", &EditorTransform::m_translate)->
Field("Rotate", &EditorTransform::m_rotate)->
Field("Scale", &EditorTransform::m_scale)->
Field("Scale", &EditorTransform::m_legacyScale)->
Field("Locked", &EditorTransform::m_locked)->
Version(2);
Field("UniformScale", &EditorTransform::m_uniformScale)->
Version(3, &Internal::EditorTransformDataConverter);
serializeContext->Class<Components::TransformComponent, EditorComponentBase>()->
Field("Parent Entity", &TransformComponent::m_parentEntityId)->
@@ -1358,7 +1210,7 @@ namespace AzToolsFramework
Attribute(AZ::Edit::Attributes::Suffix, " deg")->
Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)->
Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)->
DataElement(TransformScaleHandler, &EditorTransform::m_scale, "Scale", "Local Scale")->
DataElement(AZ::Edit::UIHandlers::Default, &EditorTransform::m_uniformScale, "Uniform Scale", "Local Uniform Scale")->
Attribute(AZ::Edit::Attributes::Step, 0.1f)->
Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)
;
@@ -1386,7 +1238,8 @@ namespace AzToolsFramework
{
AzToolsFramework::ScopedUndoBatch undo("Reset transform values");
m_editorTransform.m_translate = AZ::Vector3::CreateZero();
m_editorTransform.m_scale = AZ::Vector3::CreateOne();
m_editorTransform.m_legacyScale = AZ::Vector3::CreateOne();
m_editorTransform.m_uniformScale = 1.0f;
m_editorTransform.m_rotate = AZ::Vector3::CreateZero();
OnTransformChanged();
SetDirty();
@@ -99,22 +99,7 @@ namespace AzToolsFramework
float GetLocalZ() override;
// Rotation modifiers
void SetRotation(const AZ::Vector3& eulerAnglesRadians) override;
void SetRotationQuaternion(const AZ::Quaternion& quaternion) override;
void SetRotationX(float eulerAngleRadians) override;
void SetRotationY(float eulerAngleRadians) override;
void SetRotationZ(float eulerAngleRadians) override;
void RotateByX(float eulerAngleRadians) override;
void RotateByY(float eulerAngleRadians) override;
void RotateByZ(float eulerAngleRadians) override;
AZ::Vector3 GetRotationEulerRadians() override;
AZ::Quaternion GetRotationQuaternion() override;
float GetRotationX() override;
float GetRotationY() override;
float GetRotationZ() override;
void SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) override;
AZ::Vector3 GetWorldRotation() override;
AZ::Quaternion GetWorldRotationQuaternion() override;
@@ -130,23 +115,11 @@ namespace AzToolsFramework
AZ::Quaternion GetLocalRotationQuaternion() override;
// Scale Modifiers
void SetScale(const AZ::Vector3& newScale) override;
void SetScaleX(float newScale) override;
void SetScaleY(float newScale) override;
void SetScaleZ(float newScale) override;
AZ::Vector3 GetScale() override;
float GetScaleX() override;
float GetScaleY() override;
float GetScaleZ() override;
void SetLocalScale(const AZ::Vector3& scale) override;
void SetLocalScaleX(float scaleX) override;
void SetLocalScaleY(float scaleY) override;
void SetLocalScaleZ(float scaleZ) override;
AZ::Vector3 GetLocalScale() override;
AZ::Vector3 GetWorldScale() override;
void SetLocalUniformScale(float scale) override;
float GetLocalUniformScale() override;
float GetWorldUniformScale() override;
AZ::EntityId GetParentId() override;
AZ::TransformInterface* GetParent() override;
@@ -161,7 +134,6 @@ namespace AzToolsFramework
// TransformComponentMessages::Bus
void TranslateBy(const AZ::Vector3&) override;
void RotateBy(const AZ::Vector3&) override; // euler in degrees
void ScaleBy(const AZ::Vector3&) override;
const EditorTransform& GetLocalEditorTransform() override;
void SetLocalEditorTransform(const EditorTransform& dest) override;
bool IsTransformLocked() override;
@@ -30,7 +30,8 @@ namespace AzToolsFramework
EditorTransform()
{
m_translate = AZ::Vector3::CreateZero();
m_scale = AZ::Vector3::CreateOne();
m_legacyScale = AZ::Vector3::CreateOne();
m_uniformScale = 1.0f;
m_rotate = AZ::Vector3::CreateZero();
m_locked = false;
}
@@ -40,9 +41,10 @@ namespace AzToolsFramework
return EditorTransform();
}
AZ::Vector3 m_translate; //! Translation in engine units (meters)
AZ::Vector3 m_scale;
AZ::Vector3 m_rotate; //! Rotation in degrees
AZ::Vector3 m_translate; //!< Translation in engine units (meters)
AZ::Vector3 m_legacyScale; //!< Legacy vector scale value, retained only for migration.
float m_uniformScale; //!< Single scale value applied uniformly.
AZ::Vector3 m_rotate; //!< Rotation in degrees
bool m_locked;
};
@@ -65,7 +67,6 @@ namespace AzToolsFramework
virtual void TranslateBy(const AZ::Vector3&) = 0;
virtual void RotateBy(const AZ::Vector3&) = 0;
virtual void ScaleBy(const AZ::Vector3&) = 0;
virtual bool IsTransformLocked() = 0;
};
@@ -1,82 +0,0 @@
/*
* 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_precompiled.h"
#include <ToolsComponents/TransformScalePropertyHandler.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector3.h>
namespace AzToolsFramework
{
void RegisterTransformScaleHandler()
{
PropertyTypeRegistrationMessages::Bus::Broadcast(&PropertyTypeRegistrationMessages::RegisterPropertyType, aznew Components::TransformScalePropertyHandler());
}
namespace Components
{
AZ::u32 TransformScalePropertyHandler::GetHandlerName(void) const
{
return TransformScaleHandler;
}
QWidget* TransformScalePropertyHandler::CreateGUI(QWidget* parent)
{
AzQtComponents::DoubleSpinBox* newCtrl = new AzQtComponents::DoubleSpinBox(parent);
connect(newCtrl, QOverload<double>::of(&AzQtComponents::DoubleSpinBox::valueChanged), newCtrl, [newCtrl]()
{
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestWrite, newCtrl);
});
newCtrl->setMinimum(AZ::MinTransformScale);
newCtrl->setMaximum(AZ::MaxTransformScale);
return newCtrl;
}
void TransformScalePropertyHandler::ConsumeAttribute(AzQtComponents::DoubleSpinBox* GUI, AZ::u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
{
if (attrib == AZ::Edit::Attributes::Suffix)
{
AZStd::string label;
if (attrValue->Read<AZStd::string>(label))
{
GUI->setSuffix(label.c_str());
}
}
}
void TransformScalePropertyHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, AzQtComponents::DoubleSpinBox* GUI,
AZ::Vector3& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
const float value = aznumeric_cast<float>(GUI->value());
const float currentMaxElement = instance.GetMaxElement();
if (currentMaxElement != 0.0f)
{
instance *= value / currentMaxElement;
}
else
{
instance = AZ::Vector3(value);
}
}
bool TransformScalePropertyHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, AzQtComponents::DoubleSpinBox* GUI,
const AZ::Vector3& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
QSignalBlocker signalBlocker(GUI);
GUI->setValue(instance.GetMaxElement());
return true;
}
} // namespace Components
} // namespace AzToolsFramework
@@ -1,56 +0,0 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzQtComponents/Components/Widgets/SpinBox.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzCore/Math/Vector3.h>
#endif
namespace AzToolsFramework
{
namespace Components
{
static const AZ::Crc32 TransformScaleHandler = AZ_CRC_CE("TransformScale");
//! Handler to allow the scale field inside the Transform Component to be represented as a single value in
//! the editor, but stored internally as a Vector3.
//! The purpose for this is to prevent any new entities being created with non-uniform scale on the Transform
//! Component, but preserve the data required for migrating any existing entities to use the Non-Uniform Scale
//! Component, until all migration work is completed.
//! The value shown in the editor will be the maximum value from the scale vector, and changing the value in
//! the editor will update the vector so that its maximum value matches the newly edited value, but its
//! components retain their existing proportion.
//! For example, if the current vector scale is (2, 3, 4), the value in the editor will appear as 4. If the value
//! in the editor is updated to 2, then the vector scale will update to (1, 1.5, 2), keeping the same proportion
//! between the x, y and z components.
class TransformScalePropertyHandler
: public QObject
, public AzToolsFramework::PropertyHandler<AZ::Vector3, AzQtComponents::DoubleSpinBox>
{
Q_OBJECT //AUTOMOC
public:
AZ_CLASS_ALLOCATOR(TransformScalePropertyHandler, AZ::SystemAllocator, 0);
AZ::u32 GetHandlerName(void) const override;
QWidget* CreateGUI(QWidget* parent) override;
void ConsumeAttribute(AzQtComponents::DoubleSpinBox* GUI, AZ::u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, AzQtComponents::DoubleSpinBox* GUI,
AZ::Vector3& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, AzQtComponents::DoubleSpinBox* GUI,
const AZ::Vector3& instance, AzToolsFramework::InstanceDataNode* node) override;
};
} // namespace Components
} // namespace AzToolsFramework
@@ -63,6 +63,7 @@
#include <AzToolsFramework/UI/Outliner/EntityOutlinerDisplayOptionsMenu.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerSortFilterProxyModel.hxx>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerCacheBus.h>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
////////////////////////////////////////////////////////////////////////////
@@ -1409,6 +1410,16 @@ namespace AzToolsFramework
{
(void)name;
QueueEntityUpdate(entityId);
bool isSelected = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
isSelected, &AzToolsFramework::ToolsApplicationRequests::IsSelected, entityId);
if (isSelected)
{
// Ask the system to scroll to the entity in case it is off screen after the rename
EntityOutlinerModelNotificationBus::Broadcast(&EntityOutlinerModelNotifications::QueueScrollToNewContent, entityId);
}
}
void EntityOutlinerListModel::OnEntityInfoUpdatedUnsavedChanges(AZ::EntityId entityId)
@@ -172,7 +172,7 @@ namespace AzToolsFramework
const int autoExpandDelayMilliseconds = 2500;
m_gui->m_objectTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_gui->m_objectTree->setEditTriggers(QAbstractItemView::EditKeyPressed);
SetDefaultTreeViewEditTriggers();
m_gui->m_objectTree->setAutoExpandDelay(autoExpandDelayMilliseconds);
m_gui->m_objectTree->setDragEnabled(true);
m_gui->m_objectTree->setDropIndicatorShown(true);
@@ -850,6 +850,11 @@ namespace AzToolsFramework
addAction(m_actionGoToEntitiesInViewport);
}
void EntityOutlinerWidget::SetDefaultTreeViewEditTriggers()
{
m_gui->m_objectTree->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed);
}
void EntityOutlinerWidget::OnEntityPickModeStarted()
{
m_gui->m_objectTree->setDragEnabled(false);
@@ -862,7 +867,7 @@ namespace AzToolsFramework
{
m_gui->m_objectTree->setDragEnabled(true);
m_gui->m_objectTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_gui->m_objectTree->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
SetDefaultTreeViewEditTriggers();
m_inObjectPickMode = false;
}
@@ -166,6 +166,8 @@ namespace AzToolsFramework
// to a given entity
void QueueScrollToNewContent(const AZ::EntityId& entityId) override;
void SetDefaultTreeViewEditTriggers();
void ScrollToNewContent();
bool m_scrollToNewContentQueued;
bool m_scrollToSelectedEntity;
@@ -333,7 +333,7 @@ namespace AzToolsFramework
}
}
auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, s_prefabLoaderInterface->GetRelativePathToProject(prefabFilePath.data()));
auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, prefabFilePath.data());
if (!createPrefabOutcome.IsSuccess())
{
@@ -83,7 +83,7 @@ namespace AzToolsFramework
protected:
QWidget* GetFirstInTabOrder() override;
QWidget* GetLastInTabOrder() override;
void UpdateTabOrder() override;
void UpdateTabOrder() override;
void onChildComboBoxValueChange(int comboBoxIndex) override;
@@ -93,7 +93,7 @@ namespace AzToolsFramework
void addElementImpl(const AZStd::pair<T, AZStd::string>& genericValue);
QLabel* m_warningLabel = nullptr;
QLabel* m_warningLabel = nullptr;
DHQComboBox* m_pComboBox;
AZStd::vector<AZStd::pair<T, AZStd::string>> m_values;
AZ::AttributeFunction <void(const T&)>* m_postChangeNotifyCB{};
@@ -131,6 +131,11 @@ namespace AzToolsFramework
template<typename T>
AzToolsFramework::PropertyHandlerBase* RegisterGenericComboBoxHandler()
{
if (!AzToolsFramework::PropertyTypeRegistrationMessages::Bus::FindFirstHandler())
{
return nullptr;
}
auto propertyHandler = aznew GenericComboBoxHandler<T>();
AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(&AzToolsFramework::PropertyTypeRegistrationMessages::RegisterPropertyType, propertyHandler);
return propertyHandler;
@@ -140,8 +140,7 @@ namespace AzToolsFramework
ProductAssetBrowserEntry* productEntry = static_cast<ProductAssetBrowserEntry*>(childEntry);
AZStd::string assetName;
AzFramework::StringFunc::Path::GetFileName(productEntry->GetFullPath().c_str(), assetName);
m_assets.push_back({
assetName, productEntry->GetFullPath(), productEntry->GetAssetId()
m_assets.push_back({ productEntry->GetName(), productEntry->GetFullPath(), productEntry->GetAssetId()
});
}
@@ -769,6 +769,31 @@ namespace AzToolsFramework
// Request the AssetBrowser Dialog and set a type filter
AssetSelectionModel selection = GetAssetSelectionModel();
selection.SetSelectedAssetId(m_selectedAssetID);
AZStd::string defaultDirectory;
if (m_defaultDirectoryCallback)
{
m_defaultDirectoryCallback->Invoke(m_editNotifyTarget, defaultDirectory);
selection.SetDefaultDirectory(defaultDirectory);
}
if (m_hideProductFilesInAssetPicker)
{
FilterConstType displayFilter = selection.GetDisplayFilter();
EntryTypeFilter* productsFilter = new EntryTypeFilter();
productsFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Product);
InverseFilter* noProductsFilter = new InverseFilter();
noProductsFilter->SetFilter(FilterConstType(productsFilter));
CompositeFilter* compFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND);
compFilter->AddFilter(FilterConstType(displayFilter));
compFilter->AddFilter(FilterConstType(noProductsFilter));
selection.SetDisplayFilter(FilterConstType(compFilter));
}
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, parentWidget());
if (selection.IsValid())
{
@@ -928,11 +953,16 @@ namespace AzToolsFramework
return;
}
const AZ::Data::AssetId assetID = GetCurrentAssetID();
m_currentAssetHint = "";
if (!m_unnamedType)
const AZStd::string& folderPath = GetFolderSelection();
if (!folderPath.empty())
{
m_currentAssetHint = folderPath;
}
else
{
const AZ::Data::AssetId assetID = GetCurrentAssetID();
m_currentAssetHint = "";
AZ::Outcome<AssetSystem::JobInfoContainer> jobOutcome = AZ::Failure();
AssetSystemJobRequestBus::BroadcastResult(jobOutcome, &AssetSystemJobRequestBus::Events::GetAssetJobsInfoByAssetID, assetID, false, false);
@@ -946,7 +976,7 @@ namespace AzToolsFramework
if (!jobs.empty())
{
// The default behavior is show to the source filename.
// The default behavior is to show the source filename.
assetPath = jobs[0].m_sourceFile;
AZStd::string errorLog;
@@ -1080,6 +1110,11 @@ namespace AzToolsFramework
m_editNotifyCallback = editNotifyCallback;
}
void PropertyAssetCtrl::SetDefaultDirectoryCallback(DefaultDirectoryCallbackType* callback)
{
m_defaultDirectoryCallback = callback;
}
void PropertyAssetCtrl::SetClearNotifyCallback(ClearCallbackType* clearNotifyCallback)
{
m_clearNotifyCallback = clearNotifyCallback;
@@ -1159,6 +1194,16 @@ namespace AzToolsFramework
return m_showProductAssetName;
}
void PropertyAssetCtrl::SetHideProductFilesInAssetPicker(bool hide)
{
m_hideProductFilesInAssetPicker = hide;
}
bool PropertyAssetCtrl::GetHideProductFilesInAssetPicker() const
{
return m_hideProductFilesInAssetPicker;
}
void PropertyAssetCtrl::SetShowThumbnail(bool enable)
{
m_showThumbnail = enable;
@@ -1214,6 +1259,11 @@ namespace AzToolsFramework
GUI->SetTitle(title.c_str());
}
}
else if (attrib == AZ_CRC_CE("DefaultStartingDirectoryCallback"))
{
// This is assumed to be an Asset Browser path to a specific folder to be used as a default by the asset picker if provided
GUI->SetDefaultDirectoryCallback(azdynamic_cast<PropertyAssetCtrl::DefaultDirectoryCallbackType*>(attrValue->GetAttribute()));
}
else if (attrib == AZ_CRC("EditCallback", 0xb74f2ee1))
{
PropertyAssetCtrl::EditCallbackType* func = azdynamic_cast<PropertyAssetCtrl::EditCallbackType*>(attrValue->GetAttribute());
@@ -1279,6 +1329,14 @@ namespace AzToolsFramework
GUI->SetShowProductAssetName(showProductAssetName);
}
}
else if(attrib == AZ::Edit::Attributes::HideProductFilesInAssetPicker)
{
bool hideProductFilesInAssetPicker = false;
if (attrValue->Read<bool>(hideProductFilesInAssetPicker))
{
GUI->SetHideProductFilesInAssetPicker(hideProductFilesInAssetPicker);
}
}
else if (attrib == AZ::Edit::Attributes::ClearNotify)
{
PropertyAssetCtrl::ClearCallbackType* func = azdynamic_cast<PropertyAssetCtrl::ClearCallbackType*>(attrValue->GetAttribute());
@@ -68,6 +68,7 @@ namespace AzToolsFramework
// This is meant to be used with the "EditCallback" Attribute
using EditCallbackType = AZ::Edit::AttributeFunction<void(const AZ::Data::AssetId&, const AZ::Data::AssetType&)>;
using ClearCallbackType = AZ::Edit::AttributeFunction<void()>;
using DefaultDirectoryCallbackType = AZ::Edit::AttributeFunction<void(AZStd::string&)>;
PropertyAssetCtrl(QWidget *pParent = NULL, QString optionalValidDragDropExtensions = QString());
virtual ~PropertyAssetCtrl();
@@ -119,6 +120,7 @@ namespace AzToolsFramework
EditCallbackType* m_editNotifyCallback = nullptr;
ClearCallbackType* m_clearNotifyCallback = nullptr;
QString m_optionalValidDragDropExtensions;
DefaultDirectoryCallbackType* m_defaultDirectoryCallback = nullptr;
//! The number of characters after which the autocompleter dropdown will be shown.
// Prevents showing too many options.
@@ -156,6 +158,10 @@ namespace AzToolsFramework
//! Assets can be either source or product assets generated from source assets. By default, source assets are shown in the property asset. You can override that with this flag.
bool m_showProductAssetName = true;
//! Assets can be either source or product assets generated from source assets.
//! By default the asset picker shows both on an AZ::Asset<> property. You can hide product assets with this flag.
bool m_hideProductFilesInAssetPicker = false;
bool m_showThumbnail = false;
bool m_showThumbnailDropDownButton = false;
EditCallbackType* m_thumbnailCallback = nullptr;
@@ -196,6 +202,7 @@ namespace AzToolsFramework
void SetEditNotifyTarget(void* editNotifyTarget);
void SetEditNotifyCallback(EditCallbackType* editNotifyCallback); // This is meant to be used with the "EditCallback" Attribute
void SetClearNotifyCallback(ClearCallbackType* clearNotifyCallback); // This is meant to be used with the "ClearNotify" Attribute
void SetDefaultDirectoryCallback(DefaultDirectoryCallbackType* callback); // This is meant to be used with the "DefaultStartingDirectoryCallback" Attribute
void SetEditButtonEnabled(bool enabled);
void SetEditButtonVisible(bool visible);
void SetEditButtonIcon(const QIcon& icon);
@@ -208,6 +215,9 @@ namespace AzToolsFramework
void SetShowProductAssetName(bool enable);
bool GetShowProductAssetName() const;
void SetHideProductFilesInAssetPicker(bool hide);
bool GetHideProductFilesInAssetPicker() const;
void SetShowThumbnail(bool enable);
bool GetShowThumbnail() const;
void SetShowThumbnailDropDownButton(bool enable);
@@ -16,7 +16,6 @@
#include <AzToolsFramework/ToolsComponents/EditorEntityIdContainer.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyAudioCtrlTypes.h>
#include <AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.h>
#include <AzToolsFramework/ToolsComponents/TransformScalePropertyHandler.h>
namespace AzToolsFramework
{
@@ -38,7 +37,6 @@ namespace AzToolsFramework
void RegisterButtonPropertyHandlers();
void RegisterMultiLineEditHandler();
void RegisterCrcHandler();
void RegisterTransformScaleHandler();
void ReflectPropertyEditor(AZ::ReflectContext* context);
namespace Components
@@ -192,7 +190,6 @@ namespace AzToolsFramework
RegisterVectorHandlers();
RegisterButtonPropertyHandlers();
RegisterMultiLineEditHandler();
RegisterTransformScaleHandler();
// GenericComboBoxHandlers
RegisterGenericComboBoxHandler<AZ::Crc32>();
@@ -138,7 +138,7 @@ namespace UnitTest
if (!GetApplication())
{
// Create & Start a new ToolsApplication if there's no existing one
m_app = AZStd::make_unique<ToolsTestApplication>("ToolsApplication");
m_app = CreateTestApplication();
m_app->Start(AzFramework::Application::Descriptor());
}
@@ -216,6 +216,12 @@ namespace UnitTest
TestEditorActions m_editorActions;
ToolsApplicationMessageHandler m_messageHandler; // used to suppress trace messages in test output
// Override this if your test fixture needs to use a custom TestApplication
virtual AZStd::unique_ptr<ToolsTestApplication> CreateTestApplication()
{
return AZStd::make_unique<ToolsTestApplication>("ToolsApplication");
}
private:
AZStd::unique_ptr<ToolsTestApplication> m_app;
};
@@ -165,15 +165,18 @@ namespace AzToolsFramework
virtual bool AngleSnappingEnabled() = 0;
/// Return the angle snapping/step size.
virtual float AngleStep() = 0;
/// Transform a point in world space to screen space coordinates.
/// Transform a point in world space to screen space coordinates in Qt Widget space.
/// Multiply by DeviceScalingFactor to get the position in viewport pixel space.
virtual AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0;
/// Transform a point in screen space coordinates to a vector in world space based on clip space depth.
/// Transform a point from Qt widget screen space to world space based on the given clip space depth.
/// Depth specifies a relative camera depth to project in the range of [0.f, 1.f].
/// Returns the world space position if successful.
virtual AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) = 0;
/// Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane.
/// Returns a ray containing the ray's origin and a direction normal, if successful.
virtual AZStd::optional<ProjectedViewportRay> ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0;
/// Gets the DPI scaling factor that translates Qt widget space into viewport pixel space.
virtual float DeviceScalingFactor() = 0;
protected:
~ViewportInteractionRequests() = default;
@@ -21,6 +21,7 @@
#include <AzToolsFramework/Viewport/ViewportTypes.h>
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AzToolsFramework/API/EditorViewportIconDisplayInterface.h>
AZ_CVAR(
bool, ed_visibility_showAggregateEntitySelectionBounds, false, nullptr, AZ::ConsoleFunctorFlags::Null,
@@ -232,10 +233,14 @@ namespace AzToolsFramework
return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f);
}();
debugDisplay.SetColor(iconHighlight);
debugDisplay.DrawTextureLabel(
iconTextureId, entityPosition, iconSize, iconSize,
/*DisplayContext::ETextureIconFlags::TEXICON_ON_TOP=*/ 0x0008);
EditorViewportIconDisplay::Get()->DrawIcon({
viewportInfo.m_viewportId,
iconTextureId,
iconHighlight,
entityPosition,
EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace,
AZ::Vector2{iconSize, iconSize}
});
}
}
}
@@ -435,7 +435,7 @@ namespace AzToolsFramework
}
}
static void DestroyTransformModeSelectionCluster(const ViewportUi::ClusterId clusterId)
static void DestroyCluster(const ViewportUi::ClusterId clusterId)
{
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId,
@@ -483,6 +483,26 @@ namespace AzToolsFramework
return worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, pivot));
}
void EditorTransformComponentSelection::UpdateSpaceCluster(const ReferenceFrame referenceFrame)
{
auto buttonIdFromFrameFn = [this](const ReferenceFrame referenceFrame) {
switch (referenceFrame)
{
case ReferenceFrame::Local:
return m_spaceCluster.m_localButtonId;
case ReferenceFrame::Parent:
return m_spaceCluster.m_parentButtonId;
case ReferenceFrame::World:
return m_spaceCluster.m_worldButtonId;
}
return m_spaceCluster.m_parentButtonId;
};
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, m_spaceCluster.m_spaceClusterId,
buttonIdFromFrameFn(referenceFrame));
}
namespace ETCS
{
PivotOrientationResult CalculatePivotOrientation(
@@ -789,14 +809,12 @@ namespace AzToolsFramework
EntityIdManipulators& entityIdManipulators,
OptionalFrame& pivotOverrideFrame,
ViewportInteraction::KeyboardModifiers& prevModifiers,
bool& transformChangedInternally)
bool& transformChangedInternally, const AZStd::optional<ReferenceFrame> spaceLock)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
entityIdManipulators.m_manipulators->SetLocalPosition(action.LocalPosition());
const ReferenceFrame referenceFrame = ReferenceFrameFromModifiers(action.m_modifiers);
if (action.m_modifiers.Ctrl())
{
// moving with ctrl - setting override
@@ -806,6 +824,8 @@ namespace AzToolsFramework
}
else
{
const ReferenceFrame referenceFrame = spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers));
// note: used for parent and world depending on the current reference frame
const auto pivotOrientation =
ETCS::CalculateSelectionPivotOrientation(
@@ -1027,6 +1047,7 @@ namespace AzToolsFramework
EditorManipulatorCommandUndoRedoRequestBus::Handler::BusConnect(entityContextId);
CreateTransformModeSelectionCluster();
CreateSpaceSelectionCluster();
RegisterActions();
SetupBoxSelect();
RefreshSelectedEntityIdsAndRegenerateManipulators();
@@ -1037,7 +1058,9 @@ namespace AzToolsFramework
m_selectedEntityIds.clear();
DestroyManipulators(m_entityIdManipulators);
DestroyTransformModeSelectionCluster(m_transformModeClusterId);
DestroyCluster(m_transformModeClusterId);
DestroyCluster(m_spaceCluster.m_spaceClusterId);
UnregisterActions();
m_pivotOverrideFrame.Reset();
@@ -1274,8 +1297,8 @@ namespace AzToolsFramework
[this, prevModifiers, manipulatorEntityIds](const LinearManipulator::Action& action) mutable -> void
{
UpdateTranslationManipulator(
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators,
m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally);
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
m_transformChangedInternally, m_spaceCluster.m_spaceLock);
});
translationManipulators->InstallLinearManipulatorMouseUpCallback(
@@ -1305,8 +1328,8 @@ namespace AzToolsFramework
[this, prevModifiers, manipulatorEntityIds](const PlanarManipulator::Action& action) mutable -> void
{
UpdateTranslationManipulator(
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators,
m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally);
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
m_transformChangedInternally, m_spaceCluster.m_spaceLock);
});
translationManipulators->InstallPlanarManipulatorMouseUpCallback(
@@ -1335,8 +1358,8 @@ namespace AzToolsFramework
[this, prevModifiers, manipulatorEntityIds](const SurfaceManipulator::Action& action) mutable -> void
{
UpdateTranslationManipulator(
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators,
m_pivotOverrideFrame, prevModifiers, m_transformChangedInternally);
action, manipulatorEntityIds->m_entityIds, m_entityIdManipulators, m_pivotOverrideFrame, prevModifiers,
m_transformChangedInternally, m_spaceCluster.m_spaceLock);
});
translationManipulators->InstallSurfaceManipulatorMouseUpCallback(
@@ -1414,8 +1437,7 @@ namespace AzToolsFramework
[this, prevModifiers, sharedRotationState]
(const AngularManipulator::Action& action) mutable -> void
{
const ReferenceFrame referenceFrame = ReferenceFrameFromModifiers(action.m_modifiers);
const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(action.m_modifiers));
const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta;
// store the pivot override frame when positioning the manipulator manually (ctrl)
// so we don't lose the orientation when adding/removing entities from the selection
@@ -1474,7 +1496,7 @@ namespace AzToolsFramework
{
const AZ::Quaternion rotation = entityIdLookupIt->second.m_initial.GetRotation().GetNormalized();
const AZ::Vector3 position = entityIdLookupIt->second.m_initial.GetTranslation();
const AZ::Vector3 scale = entityIdLookupIt->second.m_initial.GetScale();
const float scale = entityIdLookupIt->second.m_initial.GetUniformScale();
const AZ::Vector3 centerOffset = CalculateCenterOffset(entityId, m_pivotMode);
@@ -1485,7 +1507,7 @@ namespace AzToolsFramework
AZ::Transform::CreateFromQuaternion(rotation) *
AZ::Transform::CreateTranslation(centerOffset) * offsetRotation *
AZ::Transform::CreateTranslation(-centerOffset) *
AZ::Transform::CreateScale(scale));
AZ::Transform::CreateUniformScale(scale));
}
break;
case ReferenceFrame::Parent:
@@ -1597,16 +1619,15 @@ namespace AzToolsFramework
}
const AZ::Transform initial = entityIdLookupIt->second.m_initial;
const AZ::Vector3 initialScale = initial.GetScale();
const float initialScale = initial.GetUniformScale();
const auto sumVectorElements = [](const AZ::Vector3& vec) {
return vec.GetX() + vec.GetY() + vec.GetZ();
};
const AZ::Vector3 uniformScale = AZ::Vector3(action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset()));
const AZ::Vector3 scale = (AZ::Vector3::CreateOne() +
(uniformScale / initialScale)).GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale));
const AZ::Transform scaleTransform = AZ::Transform::CreateScale(scale);
const float uniformScale = action.m_start.m_sign * sumVectorElements(action.LocalScaleOffset());
const float scale = AZ::GetClamp(1.0f + uniformScale / initialScale, AZ::MinTransformScale, AZ::MaxTransformScale);
const AZ::Transform scaleTransform = AZ::Transform::CreateUniformScale(scale);
if (action.m_modifiers.Alt())
{
@@ -1872,7 +1893,7 @@ namespace AzToolsFramework
CopyOrientationToSelectedEntitiesGroup(QuaternionFromTransformNoScaling(worldFromLocal));
break;
case Mode::Scale:
CopyScaleToSelectedEntitiesIndividualWorld(worldFromLocal.GetScale());
CopyScaleToSelectedEntitiesIndividualWorld(worldFromLocal.GetUniformScale());
break;
case Mode::Translation:
CopyTranslationToSelectedEntitiesGroup(worldFromLocal.GetTranslation());
@@ -1901,7 +1922,7 @@ namespace AzToolsFramework
CopyOrientationToSelectedEntitiesIndividual(QuaternionFromTransformNoScaling(worldFromLocal));
break;
case Mode::Scale:
CopyScaleToSelectedEntitiesIndividualWorld(worldFromLocal.GetScale());
CopyScaleToSelectedEntitiesIndividualWorld(worldFromLocal.GetUniformScale());
break;
case Mode::Translation:
CopyTranslationToSelectedEntitiesIndividual(worldFromLocal.GetTranslation());
@@ -2394,7 +2415,7 @@ namespace AzToolsFramework
ResetOrientationForSelectedEntitiesLocal();
break;
case Mode::Scale:
CopyScaleToSelectedEntitiesIndividualLocal(AZ::Vector3::CreateOne());
CopyScaleToSelectedEntitiesIndividualLocal(1.0f);
break;
case Mode::Translation:
ResetTranslationForSelectedEntitiesLocal();
@@ -2420,7 +2441,7 @@ namespace AzToolsFramework
ResetOrientationForSelectedEntitiesLocal();
break;
case Mode::Scale:
CopyScaleToSelectedEntitiesIndividualWorld(AZ::Vector3::CreateOne());
CopyScaleToSelectedEntitiesIndividualWorld(1.0f);
break;
case Mode::Translation:
// do nothing
@@ -2567,6 +2588,64 @@ namespace AzToolsFramework
m_transformModeSelectionHandler);
}
void EditorTransformComponentSelection::CreateSpaceSelectionCluster()
{
// create the cluster for switching spaces/reference frames
ViewportUi::ViewportUiRequestBus::EventResult(
m_spaceCluster.m_spaceClusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster,
ViewportUi::Alignment::TopRight);
// create and register the buttons (strings correspond to icons even if the values appear different)
m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "World");
m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Parent");
m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Local");
auto onButtonClicked = [this](ViewportUi::ButtonId buttonId) {
if (buttonId == m_spaceCluster.m_localButtonId)
{
// Unlock
if (m_spaceCluster.m_spaceLock.has_value() && m_spaceCluster.m_spaceLock.value() == ReferenceFrame::Local)
{
m_spaceCluster.m_spaceLock = AZStd::nullopt;
}
else
{
m_spaceCluster.m_spaceLock = ReferenceFrame::Local;
}
}
else if (buttonId == m_spaceCluster.m_parentButtonId)
{
// Unlock
if (m_spaceCluster.m_spaceLock.has_value() && m_spaceCluster.m_spaceLock.value() == ReferenceFrame::Parent)
{
m_spaceCluster.m_spaceLock = AZStd::nullopt;
}
else
{
m_spaceCluster.m_spaceLock = ReferenceFrame::Parent;
}
}
else if (buttonId == m_spaceCluster.m_worldButtonId)
{
// Unlock
if (m_spaceCluster.m_spaceLock.has_value() && m_spaceCluster.m_spaceLock.value() == ReferenceFrame::World)
{
m_spaceCluster.m_spaceLock = AZStd::nullopt;
}
else
{
m_spaceCluster.m_spaceLock = ReferenceFrame::World;
}
}
};
m_spaceCluster.m_spaceSelectionHandler = AZ::Event<ViewportUi::ButtonId>::Handler(onButtonClicked);
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler,
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_spaceSelectionHandler);
}
EditorTransformComponentSelectionRequests::Mode EditorTransformComponentSelection::GetTransformMode()
{
return m_mode;
@@ -2940,7 +3019,7 @@ namespace AzToolsFramework
}
}
void EditorTransformComponentSelection::CopyScaleToSelectedEntitiesIndividualWorld(const AZ::Vector3& scale)
void EditorTransformComponentSelection::CopyScaleToSelectedEntitiesIndividualWorld(float scale)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@@ -2955,7 +3034,7 @@ namespace AzToolsFramework
const auto transformsBefore = RecordTransformsBefore(manipulatorEntityIds.m_entityIds);
// update scale relative to initial
const AZ::Transform scaleTransform = AZ::Transform::CreateScale(scale);
const AZ::Transform scaleTransform = AZ::Transform::CreateUniformScale(scale);
for (AZ::EntityId entityId : manipulatorEntityIds.m_entityIds)
{
ScopedUndoBatch::MarkEntityDirty(entityId);
@@ -2964,7 +3043,7 @@ namespace AzToolsFramework
if (transformIt != transformsBefore.end())
{
AZ::Transform transformBefore = transformIt->second;
transformBefore.ExtractScale();
transformBefore.ExtractUniformScale();
AZ::Transform newWorldFromLocal = transformBefore * scaleTransform;
SetEntityWorldTransform(entityId, newWorldFromLocal);
@@ -2974,7 +3053,7 @@ namespace AzToolsFramework
RefreshUiAfterChange(manipulatorEntityIds.m_entityIds);
}
void EditorTransformComponentSelection::CopyScaleToSelectedEntitiesIndividualLocal(const AZ::Vector3& scale)
void EditorTransformComponentSelection::CopyScaleToSelectedEntitiesIndividualLocal(float scale)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@@ -3020,9 +3099,9 @@ namespace AzToolsFramework
if (transformIt != transformsBefore.end())
{
AZ::Transform newWorldFromLocal = transformIt->second;
const AZ::Vector3 scale = newWorldFromLocal.GetScale();
const float scale = newWorldFromLocal.GetUniformScale();
newWorldFromLocal.SetRotation(orientation);
newWorldFromLocal *= AZ::Transform::CreateScale(scale);
newWorldFromLocal *= AZ::Transform::CreateUniformScale(scale);
SetEntityWorldTransform(entityId, newWorldFromLocal);
}
@@ -3278,7 +3357,9 @@ namespace AzToolsFramework
ViewportInteraction::BuildMouseButtons(
QGuiApplication::mouseButtons()), m_boxSelect.Active());
const ReferenceFrame referenceFrame = ReferenceFrameFromModifiers(modifiers);
const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(modifiers));
UpdateSpaceCluster(referenceFrame);
bool refresh = false;
if (referenceFrame != m_referenceFrame)
@@ -3669,7 +3750,7 @@ namespace AzToolsFramework
}
void EditorTransformComponentSelection::SetEntityLocalScale(
const AZ::EntityId entityId, const AZ::Vector3& localScale)
const AZ::EntityId entityId, const float localScale)
{
ETCS::SetEntityLocalScale(entityId, localScale, m_transformChangedInternally);
}
@@ -3722,11 +3803,11 @@ namespace AzToolsFramework
entityId, &AZ::TransformBus::Events::SetWorldTM, worldTransform);
}
void SetEntityLocalScale(AZ::EntityId entityId, const AZ::Vector3& localScale, bool& internal)
void SetEntityLocalScale(AZ::EntityId entityId, float localScale, bool& internal)
{
ScopeSwitch sw(internal);
AZ::TransformBus::Event(
entityId, &AZ::TransformBus::Events::SetLocalScale, localScale);
entityId, &AZ::TransformBus::Events::SetLocalUniformScale, localScale);
}
void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation, bool& internal)
@@ -106,6 +106,22 @@ namespace AzToolsFramework
World, //!< World space (space aligned to world axes - identity).
};
//! Grouping of viewport ui related state for controlling the current reference space of the Editor.
struct SpaceCluster
{
SpaceCluster() = default;
// disable copying and moving (implicit)
SpaceCluster(const SpaceCluster&) = delete;
SpaceCluster& operator=(const SpaceCluster&) = delete;
ViewportUi::ClusterId m_spaceClusterId; //!< The id identifying the reference space cluster.
ViewportUi::ButtonId m_localButtonId; //!< Local reference space button id.
ViewportUi::ButtonId m_parentButtonId; //!< Parent reference space button id.
ViewportUi::ButtonId m_worldButtonId; //!< World reference space button id.
AZ::Event<ViewportUi::ButtonId>::Handler m_spaceSelectionHandler; //!< Callback for when a space cluster button is pressed.
AZStd::optional<ReferenceFrame> m_spaceLock; //!< Locked reference frame to use if set.
};
//! Entity selection/interaction handling.
//! Provide a suite of functionality for manipulating entities, primarily through their TransformComponent.
class EditorTransformComponentSelection
@@ -160,6 +176,7 @@ namespace AzToolsFramework
void RegenerateManipulators();
void CreateTransformModeSelectionCluster();
void CreateSpaceSelectionCluster();
void ClearManipulatorTranslationOverride();
void ClearManipulatorOrientationOverride();
@@ -214,8 +231,8 @@ namespace AzToolsFramework
void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation);
void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation);
void ResetOrientationForSelectedEntitiesLocal();
void CopyScaleToSelectedEntitiesIndividualLocal(const AZ::Vector3& scale);
void CopyScaleToSelectedEntitiesIndividualWorld(const AZ::Vector3& scale);
void CopyScaleToSelectedEntitiesIndividualLocal(float scale);
void CopyScaleToSelectedEntitiesIndividualWorld(float scale);
// EditorManipulatorCommandUndoRedoRequestBus ...
void UndoRedoEntityManipulatorCommand(
@@ -250,9 +267,12 @@ namespace AzToolsFramework
void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation);
void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation);
void SetEntityWorldTransform(AZ::EntityId entityId, const AZ::Transform& worldTransform);
void SetEntityLocalScale(AZ::EntityId entityId, const AZ::Vector3& localScale);
void SetEntityLocalScale(AZ::EntityId entityId, float localScale);
void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation);
// Responsible for keeping the space cluster in sync with the current reference frame.
void UpdateSpaceCluster(ReferenceFrame referenceFrame);
AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any).
AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display.
AZ::EntityId m_editorCameraComponentEntityId; //!< The EditorCameraComponent EntityId if it is set.
@@ -285,6 +305,7 @@ namespace AzToolsFramework
AZ::Event<ViewportUi::ButtonId>::Handler m_transformModeSelectionHandler; //!< Event handler for the Viewport UI cluster.
AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click.
AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame.
SpaceCluster m_spaceCluster; //!< Related viewport ui state for controlling the current reference space.
};
//! The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
@@ -320,7 +341,7 @@ namespace AzToolsFramework
void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation, bool& internal);
void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation, bool& internal);
void SetEntityWorldTransform(AZ::EntityId entityId, const AZ::Transform& worldTransform, bool& internal);
void SetEntityLocalScale(AZ::EntityId entityId, const AZ::Vector3& localScale, bool& internal);
void SetEntityLocalScale(AZ::EntityId entityId, float localScale, bool& internal);
void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation, bool& internal);
} // namespace ETCS
} // namespace AzToolsFramework
@@ -101,10 +101,10 @@ namespace AzToolsFramework
virtual void ResetOrientationForSelectedEntitiesLocal() = 0;
/// Copy scale to each individual entity in local space without moving position.
virtual void CopyScaleToSelectedEntitiesIndividualLocal(const AZ::Vector3& scale) = 0;
virtual void CopyScaleToSelectedEntitiesIndividualLocal(float scale) = 0;
/// Copy scale to to each individual entity in world (absolute) space.
virtual void CopyScaleToSelectedEntitiesIndividualWorld(const AZ::Vector3& scale) = 0;
virtual void CopyScaleToSelectedEntitiesIndividualWorld(float scale) = 0;
protected:
~EditorTransformComponentSelectionRequests() = default;
@@ -46,6 +46,7 @@ set(FILES
API/EditorWindowRequestBus.h
API/EntityCompositionRequestBus.h
API/EntityCompositionNotificationBus.h
API/EditorViewportIconDisplayInterface.h
API/ViewPaneOptions.h
Application/Ticker.h
Application/Ticker.cpp
@@ -292,8 +293,6 @@ set(FILES
ToolsComponents/TransformComponent.h
ToolsComponents/TransformComponent.cpp
ToolsComponents/TransformComponentBus.h
ToolsComponents/TransformScalePropertyHandler.cpp
ToolsComponents/TransformScalePropertyHandler.h
ToolsComponents/ScriptEditorComponent.cpp
ToolsComponents/ScriptEditorComponent.h
ToolsComponents/ToolsAssetCatalogComponent.cpp
@@ -15,6 +15,7 @@
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzFramework/Asset/AssetRegistry.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/Asset/AssetCatalog.h>
#include <AzFramework/StringFunc/StringFunc.h>
@@ -62,6 +63,12 @@ namespace UnitTest
m_assetSeedManager = new AzToolsFramework::AssetSeedManager();
m_assetRegistry = new AzFramework::AssetRegistry();
AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
auto projectPathKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
registry->Set(projectPathKey, "AutomatedTesting");
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
m_application->Start(AzFramework::Application::Descriptor());
for (int idx = 0; idx < s_totalAssets; idx++)
@@ -75,7 +82,7 @@ namespace UnitTest
}
m_testPlatforms[0] = AzFramework::PlatformId::PC;
m_testPlatforms[1] = AzFramework::PlatformId::ES3;
m_testPlatforms[1] = AzFramework::PlatformId::ANDROID_ID;
int platformCount = 0;
for(auto thisPlatform : m_testPlatforms)
@@ -163,20 +170,20 @@ namespace UnitTest
AzFramework::AssetCatalog assetCatalog(useRequestBus);
AZStd::string pcCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::PC);
AZStd::string es3CatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ES3);
AZStd::string androidCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID);
if (!assetCatalog.SaveCatalog(pcCatalogFile.c_str(), m_assetRegistry))
{
GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to save the asset catalog (PC) file.\n").c_str());
}
if (!assetCatalog.SaveCatalog(es3CatalogFile.c_str(), m_assetRegistry))
if (!assetCatalog.SaveCatalog(androidCatalogFile.c_str(), m_assetRegistry))
{
GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to save the asset catalog (ES3) file.\n").c_str());
GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to save the asset catalog (ANDROID) file.\n").c_str());
}
m_pcCatalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::PC);
m_es3Catalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::ES3);
m_androidCatalog = new AzToolsFramework::PlatformAddressedAssetCatalog(AzFramework::PlatformId::ANDROID_ID);
const AZStd::string engroot = AZ::Test::GetEngineRootPath();
AZ::IO::FileIOBase::GetInstance()->SetAlias("@engroot@", engroot.c_str());
@@ -220,21 +227,21 @@ namespace UnitTest
}
auto pcCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::PC);
auto es3CatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ES3);
auto androidCatalogFile = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID);
if (fileIO->Exists(pcCatalogFile.c_str()))
{
fileIO->Remove(pcCatalogFile.c_str());
}
if (fileIO->Exists(es3CatalogFile.c_str()))
if (fileIO->Exists(androidCatalogFile.c_str()))
{
fileIO->Remove(es3CatalogFile.c_str());
fileIO->Remove(androidCatalogFile.c_str());
}
delete m_assetSeedManager;
delete m_assetRegistry;
delete m_pcCatalog;
delete m_es3Catalog;
delete m_androidCatalog;
m_application->Stop();
delete m_application;
}
@@ -335,10 +342,10 @@ namespace UnitTest
m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_PC);
// Step we are testing
m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ES3);
m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ANDROID_ID);
// Verification
AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3;
AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID;
for (const auto& seedInfo : m_assetSeedManager->GetAssetSeedList())
{
EXPECT_EQ(seedInfo.m_platformFlags, expectedPlatformFlags);
@@ -351,14 +358,14 @@ namespace UnitTest
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC);
m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_PC);
m_es3Catalog->UnregisterAsset(assets[2]);
m_androidCatalog->UnregisterAsset(assets[2]);
m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_PC);
// Step we are testing
m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ES3);
m_assetSeedManager->AddPlatformToAllSeeds(AzFramework::PlatformId::ANDROID_ID);
// Verification
AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3;
AzFramework::PlatformFlags expectedPlatformFlags = AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID;
for (const auto& seedInfo : m_assetSeedManager->GetAssetSeedList())
{
if (seedInfo.m_assetId == assets[2])
@@ -376,14 +383,14 @@ namespace UnitTest
{
// Setup
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_ES3);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_ANDROID);
m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_PC);
m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_ES3);
m_assetSeedManager->AddSeedAsset(assets[1], AzFramework::PlatformFlags::Platform_ANDROID);
m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_PC);
m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_ES3);
m_assetSeedManager->AddSeedAsset(assets[2], AzFramework::PlatformFlags::Platform_ANDROID);
// Step we are testing
m_assetSeedManager->RemovePlatformFromAllSeeds(AzFramework::PlatformId::ES3);
m_assetSeedManager->RemovePlatformFromAllSeeds(AzFramework::PlatformId::ANDROID_ID);
// Verification
for (const auto& seedInfo : m_assetSeedManager->GetAssetSeedList())
@@ -507,8 +514,8 @@ namespace UnitTest
void DependencyValidation_MultipleAssetSeeds_MultiplePlatformFlags_ListValid()
{
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3);
m_assetSeedManager->AddSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID);
m_assetSeedManager->AddSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID);
AzToolsFramework::AssetFileInfoList assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC);
@@ -524,7 +531,7 @@ namespace UnitTest
assetList.m_fileInfoList.clear();
m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3);
m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC);
@@ -540,7 +547,7 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList, assets[8]));
assetList.m_fileInfoList.clear();
m_assetSeedManager->RemoveSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ES3);
m_assetSeedManager->RemoveSeedAsset(assets[5], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_ANDROID);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC);
@@ -555,7 +562,7 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList, assets[8]));
// Removing the android flag from the asset should still produce the same result
m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ES3);
m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC);
@@ -569,7 +576,7 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList, assets[7]));
EXPECT_TRUE(Search(assetList, assets[8]));
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ES3);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID);
EXPECT_EQ(assetList.m_fileInfoList.size(), 5);
EXPECT_TRUE(Search(assetList, assets[0]));
@@ -579,8 +586,8 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList, assets[4]));
// Adding the android flag again to the asset
m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ES3);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ES3);
m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID);
EXPECT_EQ(assetList.m_fileInfoList.size(), 8);
EXPECT_TRUE(Search(assetList, assets[0]));
@@ -766,7 +773,7 @@ namespace UnitTest
AzFramework::AssetRegistry* m_assetRegistry;
ToolsTestApplication* m_application;
AzToolsFramework::PlatformAddressedAssetCatalog* m_pcCatalog;
AzToolsFramework::PlatformAddressedAssetCatalog* m_es3Catalog;
AzToolsFramework::PlatformAddressedAssetCatalog* m_androidCatalog;
AZ::IO::FileIOStream m_fileStreams[s_totalTestPlatforms][s_totalAssets];
AzFramework::PlatformId m_testPlatforms[s_totalTestPlatforms];
AZStd::string m_assetsPath[s_totalAssets];
@@ -929,7 +936,7 @@ namespace UnitTest
TEST_F(AssetSeedManagerTest, AddSeedAssetForValidPlatforms_AllPlatformsValid_SeedAddedForEveryInputPlatform)
{
using namespace AzFramework;
PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ES3;
PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ANDROID;
AZStd::pair<AZ::Data::AssetId, PlatformFlags> result = m_assetSeedManager->AddSeedAssetForValidPlatforms(TestDynamicSliceAssetPath, validPlatforms);
// Verify the function outputs
@@ -946,8 +953,8 @@ namespace UnitTest
TEST_F(AssetSeedManagerTest, AddSeedAssetForValidPlatforms_SomePlatformsValid_SeedAddedForEveryValidPlatform)
{
using namespace AzFramework;
PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ES3;
PlatformFlags inputPlatforms = validPlatforms | PlatformFlags::Platform_OSX;
PlatformFlags validPlatforms = PlatformFlags::Platform_PC | PlatformFlags::Platform_ANDROID;
PlatformFlags inputPlatforms = validPlatforms | PlatformFlags::Platform_MAC;
AZStd::pair<AZ::Data::AssetId, PlatformFlags> result = m_assetSeedManager->AddSeedAssetForValidPlatforms(TestDynamicSliceAssetPath, inputPlatforms);
// Verify the function outputs
@@ -964,7 +971,7 @@ namespace UnitTest
TEST_F(AssetSeedManagerTest, AddSeedAssetForValidPlatforms_NoPlatformsValid_NoSeedAdded)
{
using namespace AzFramework;
PlatformFlags inputPlatforms = PlatformFlags::Platform_OSX;
PlatformFlags inputPlatforms = PlatformFlags::Platform_MAC;
AZStd::pair<AZ::Data::AssetId, PlatformFlags> result = m_assetSeedManager->AddSeedAssetForValidPlatforms(TestDynamicSliceAssetPath, inputPlatforms);
// Verify the function outputs
@@ -978,30 +985,30 @@ namespace UnitTest
TEST_F(AssetSeedManagerTest, Valid_Seed_Remove_ForAllPlatform_OK)
{
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC);
m_assetSeedManager->RemoveSeedAsset(assets[0].ToString<AZStd::string>(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->RemoveSeedAsset(assets[0].ToString<AZStd::string>(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC);
const AzFramework::AssetSeedList& seedList = m_assetSeedManager->GetAssetSeedList();
EXPECT_EQ(seedList.size(), 0);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC);
m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC);
const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList();
EXPECT_EQ(secondSeedList.size(), 0);
}
TEST_F(AssetSeedManagerTest, Valid_Seed_Remove_ForSpecificPlatform_OK)
{
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC);
m_assetSeedManager->RemoveSeedAsset(assets[0].ToString<AZStd::string>(), AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->RemoveSeedAsset(assets[0].ToString<AZStd::string>(), AzFramework::PlatformFlags::Platform_MAC);
const AzFramework::AssetSeedList& seedList = m_assetSeedManager->GetAssetSeedList();
EXPECT_EQ(seedList.size(), 1);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC);
m_assetSeedManager->RemoveSeedAsset("asset0.txt", AzFramework::PlatformFlags::Platform_PC);
const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList();
@@ -1010,14 +1017,14 @@ namespace UnitTest
TEST_F(AssetSeedManagerTest, Invalid_NotRemove_SeedForAllPlatform_Ok)
{
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->AddSeedAsset(assets[0], AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC);
m_assetSeedManager->RemoveSeedAsset(assets[1].ToString<AZStd::string>(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->RemoveSeedAsset(assets[1].ToString<AZStd::string>(), AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC);
const AzFramework::AssetSeedList& seedList = m_assetSeedManager->GetAssetSeedList();
EXPECT_EQ(seedList.size(), 1);
m_assetSeedManager->RemoveSeedAsset("asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_OSX);
m_assetSeedManager->RemoveSeedAsset("asset1.txt", AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_MAC);
const AzFramework::AssetSeedList& secondSeedList = m_assetSeedManager->GetAssetSeedList();
EXPECT_EQ(secondSeedList.size(), 1);
}
@@ -25,6 +25,8 @@ namespace UnitTests
MOCK_METHOD0(GetAbsoluteDevGameFolderPath, const char* ());
MOCK_METHOD0(GetAbsoluteDevRootFolderPath, const char* ());
MOCK_METHOD2(GetRelativeProductPathFromFullSourceOrProductPath, bool(const AZStd::string& fullPath, AZStd::string& relativeProductPath));
MOCK_METHOD3(GenerateRelativeSourcePath,
bool(const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder));
MOCK_METHOD2(GetFullSourcePathFromRelativeProductPath, bool(const AZStd::string& relPath, AZStd::string& fullSourcePath));
MOCK_METHOD5(GetAssetInfoById, bool(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath));
MOCK_METHOD3(GetSourceInfoBySourcePath, bool(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder));
@@ -10,6 +10,8 @@
*
*/
#include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalogManager.h>
#include <AzFramework/Asset/AssetRegistry.h>
@@ -49,6 +51,14 @@ namespace UnitTest
using namespace AZ::Data;
m_application = new ToolsTestApplication("AddressedAssetCatalogManager"); // Shorter name because Setting Registry
// specialization are 32 characters max.
AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
auto projectPathKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
registry->Set(projectPathKey, "AutomatedTesting");
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
m_application->Start(AzFramework::Application::Descriptor());
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
@@ -174,13 +184,13 @@ namespace UnitTest
TEST_F(PlatformAddressedAssetCatalogManagerTest, PlatformAddressedAssetCatalogManager_CatalogExistsChecks_Success)
{
EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ES3), true);
AZStd::string es3CatalogPath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ES3);
if (AZ::IO::FileIOBase::GetInstance()->Exists(es3CatalogPath.c_str()))
EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ANDROID_ID), true);
AZStd::string androidCatalogPath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(AzFramework::PlatformId::ANDROID_ID);
if (AZ::IO::FileIOBase::GetInstance()->Exists(androidCatalogPath.c_str()))
{
AZ::IO::FileIOBase::GetInstance()->Remove(es3CatalogPath.c_str());
AZ::IO::FileIOBase::GetInstance()->Remove(androidCatalogPath.c_str());
}
EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ES3), false);
EXPECT_EQ(AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(AzFramework::PlatformId::ANDROID_ID), false);
}
class PlatformAddressedAssetCatalogMessageTest : public AzToolsFramework::PlatformAddressedAssetCatalog
@@ -241,7 +251,7 @@ namespace UnitTest
AzFramework::AssetSystem::NetworkAssetUpdateInterface* notificationInterface = AZ::Interface<AzFramework::AssetSystem::NetworkAssetUpdateInterface>::Get();
EXPECT_NE(notificationInterface, nullptr);
auto* mockCatalog = new ::testing::NiceMock<PlatformAddressedAssetCatalogMessageTest>(AzFramework::PlatformId::ES3);
auto* mockCatalog = new ::testing::NiceMock<PlatformAddressedAssetCatalogMessageTest>(AzFramework::PlatformId::ANDROID_ID);
AZStd::unique_ptr< ::testing::NiceMock<PlatformAddressedAssetCatalogMessageTest>> catalogHolder;
catalogHolder.reset(mockCatalog);
@@ -249,7 +259,7 @@ namespace UnitTest
EXPECT_CALL(*mockCatalog, AssetChanged(testing::_)).Times(0);
notificationInterface->AssetChanged(testMessage);
testMessage.m_platform = "es3";
testMessage.m_platform = "android";
EXPECT_CALL(*mockCatalog, AssetChanged(testing::_)).Times(1);
notificationInterface->AssetChanged(testMessage);
@@ -260,7 +270,7 @@ namespace UnitTest
EXPECT_CALL(*mockCatalog, AssetRemoved(testing::_)).Times(0);
notificationInterface->AssetRemoved(testMessage);
testMessage.m_platform = "es3";
testMessage.m_platform = "android";
EXPECT_CALL(*mockCatalog, AssetRemoved(testing::_)).Times(1);
notificationInterface->AssetRemoved(testMessage);
}
@@ -34,7 +34,8 @@ namespace Benchmark
{
state.PauseTiming();
auto spawnable = ::AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(prefabDom);
AzFramework::Spawnable spawnable;
AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom);
state.ResumeTiming();
}
@@ -0,0 +1,129 @@
/*
* 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/ToolsComponents/TransformComponent.h>
#include <Prefab/PrefabTestComponent.h>
#include <Prefab/PrefabTestDomUtils.h>
#include <Prefab/PrefabTestFixture.h>
namespace UnitTest
{
using PrefabDuplicateTest = PrefabTestFixture;
TEST_F(PrefabDuplicateTest, PrefabDuplicate_DuplicateSingleEntitySucceeds)
{
AZStd::string entityName("Same Name");
AZ::Entity* entity1 = CreateEntity(entityName.c_str());
entity1->Deactivate();
entity1->CreateComponent<PrefabTestComponent>();
entity1->Activate();
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, AzToolsFramework::EntityList{ entity1 });
AZStd::unique_ptr<Instance> newInstance = m_prefabSystemComponent->CreatePrefab(
{ entity1 },
{},
PrefabMockFilePath);
// We've created a prefab with a single Entity, so there should only be one EntityAlias in our instance
EXPECT_EQ(newInstance->GetEntityAliases().size(), 1);
// Duplicate the Entity and trigger the UpdateTemplateInstancesInQueue so the changes get propagated
m_prefabPublicInterface->DuplicateEntitiesInInstance(AzToolsFramework::EntityIdList{ entity1->GetId() });
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
// We duplicated a single Entity, so there should now be two EntityAliases
EXPECT_EQ(newInstance->GetEntityAliases().size(), 2);
newInstance->GetConstEntities([&](const AZ::Entity& entity)
{
// Both of the entities should have the same name
EXPECT_EQ(entity.GetName(), entityName);
// Both of the entities should have the PrefabTestComponent we added
auto testComponent = entity.FindComponent<PrefabTestComponent>();
EXPECT_NE(nullptr, testComponent);
return true;
});
}
TEST_F(PrefabDuplicateTest, PrefabDuplicate_DuplicateMultipleEntitiesAndFixesReferences)
{
AZ::Entity* parentEntity = CreateEntity("Parent Entity");
AZ::Entity* childEntity = CreateEntity("Child Entity");
childEntity->Deactivate();
auto newComponent = childEntity->CreateComponent<PrefabTestComponent>();
childEntity->Activate();
// Set the EntityId reference property on our PrefabTestComponent so we can
// verify that arbitrary EntityId's are fixed up properly
newComponent->m_entityIdProperty = parentEntity->GetId();
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, AzToolsFramework::EntityList{ parentEntity, childEntity });
AZStd::unique_ptr<Instance> newInstance = m_prefabSystemComponent->CreatePrefab(
{ parentEntity, childEntity },
{},
PrefabMockFilePath);
// We've created a prefab with two entities, so there should be two EntityAliases in our instance
EXPECT_EQ(newInstance->GetEntityAliases().size(), 2);
// Duplicate the entities and trigger the UpdateTemplateInstancesInQueue so the changes get propagated
m_prefabPublicInterface->DuplicateEntitiesInInstance(AzToolsFramework::EntityIdList{ parentEntity->GetId(), childEntity->GetId() });
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
// We duplicated two entities, so there should now be four EntityAliases
EXPECT_EQ(newInstance->GetEntityAliases().size(), 4);
AzToolsFramework::EntityIdList parentEntityIds;
newInstance->GetConstEntities([&](const AZ::Entity& entity)
{
// Gather the parent EntityIds by tracking which entities don't have a PrefabTestComponent
auto testComponent = entity.FindComponent<PrefabTestComponent>();
if (!testComponent)
{
parentEntityIds.push_back(entity.GetId());
}
return true;
});
// There should only be two parents
EXPECT_EQ(parentEntityIds.size(), 2);
// Verify that the EntityId reference on the PrefabTestComponent on the children correspond
// to unique entities, which will verify that the EntityIds are fixed up on duplicate
newInstance->GetConstEntities([&](const AZ::Entity& entity)
{
// Only the child entities have a PrefabTestComponent
auto testComponent = entity.FindComponent<PrefabTestComponent>();
if (testComponent)
{
auto it = AZStd::find(parentEntityIds.begin(), parentEntityIds.end(), testComponent->m_entityIdProperty);
EXPECT_NE(it, parentEntityIds.end());
// Erase when we find it so that the matches will be unique
parentEntityIds.erase(it);
}
return true;
});
// Verify we matched each of the parent EntityIds
EXPECT_EQ(parentEntityIds.size(), 0);
}
}
@@ -20,6 +20,17 @@
namespace UnitTest
{
PrefabTestToolsApplication::PrefabTestToolsApplication(AZStd::string appName)
: ToolsTestApplication(AZStd::move(appName))
{
}
bool PrefabTestToolsApplication::IsPrefabSystemEnabled() const
{
// Make sure our prefab tests always run with prefabs enabled
return true;
}
void PrefabTestFixture::SetUpEditorFixtureImpl()
{
// Acquire the system entity
@@ -32,6 +43,9 @@ namespace UnitTest
m_prefabLoaderInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
EXPECT_TRUE(m_prefabLoaderInterface);
m_prefabPublicInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabPublicInterface>::Get();
EXPECT_TRUE(m_prefabPublicInterface);
m_instanceUpdateExecutorInterface = AZ::Interface<AzToolsFramework::Prefab::InstanceUpdateExecutorInterface>::Get();
EXPECT_TRUE(m_instanceUpdateExecutorInterface);
@@ -41,6 +55,11 @@ namespace UnitTest
GetApplication()->RegisterComponentDescriptor(PrefabTestComponent::CreateDescriptor());
}
AZStd::unique_ptr<ToolsTestApplication> PrefabTestFixture::CreateTestApplication()
{
return AZStd::make_unique<PrefabTestToolsApplication>("PrefabTestApplication");
}
AZ::Entity* PrefabTestFixture::CreateEntity(const char* entityName, const bool shouldActivate)
{
// Circumvent the EntityContext system and generate a new entity with a transformcomponent
@@ -31,6 +31,16 @@ namespace UnitTest
using namespace AzToolsFramework::Prefab;
using namespace PrefabTestUtils;
class PrefabTestToolsApplication
: public ToolsTestApplication
{
public:
PrefabTestToolsApplication(AZStd::string appName);
// Make sure our prefab tests always run with prefabs enabled
bool IsPrefabSystemEnabled() const override;
};
class PrefabTestFixture
: public ToolsApplicationFixture,
public UnitTest::TraceBusRedirector
@@ -45,6 +55,8 @@ namespace UnitTest
void SetUpEditorFixtureImpl() override;
AZStd::unique_ptr<ToolsTestApplication> CreateTestApplication() override;
AZ::Entity* CreateEntity(const char* entityName, const bool shouldActivate = true);
void CompareInstances(const Instance& instanceA, const Instance& instanceB, bool shouldCompareLinkIds = true,
@@ -57,6 +69,7 @@ namespace UnitTest
PrefabSystemComponent* m_prefabSystemComponent = nullptr;
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
PrefabPublicInterface* m_prefabPublicInterface = nullptr;
InstanceUpdateExecutorInterface* m_instanceUpdateExecutorInterface = nullptr;
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
};
@@ -40,7 +40,8 @@ namespace UnitTest
//Create Spawnable
auto& prefabDom = m_prefabSystemComponent->FindTemplateDom(instance->GetTemplateId());
auto spawnable = ::AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(prefabDom);
AzFramework::Spawnable spawnable;
AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom);
EXPECT_EQ(spawnable.GetEntities().size() - 1, normalEntityCount); // 1 for container entity
const auto& spawnableEntities = spawnable.GetEntities();
@@ -84,7 +85,8 @@ namespace UnitTest
//Create Spawnable
auto& prefabDom = m_prefabSystemComponent->FindTemplateDom(thirdInstance->GetTemplateId());
auto spawnable = ::AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(prefabDom);
AzFramework::Spawnable spawnable;
AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom);
EXPECT_EQ(spawnable.GetEntities().size() - 1, normalEntityCount); // 1 for container entity
const auto& spawnableEntities = spawnable.GetEntities();
@@ -483,6 +483,9 @@ namespace UnitTest
{
AUTO_RESULT_IF_SETTING_TRUE(UnitTest::prefabSystemSetting, true)
// Swallow deprecation warnings from the Transform component as they are not relevant to this test
UnitTest::ErrorHandler errorHandler("GetScale is deprecated");
// Create a parent entity with a transform component
AZ::Entity* parentEntity = aznew AZ::Entity("TestParentEntity");
parentEntity->CreateComponent<AzToolsFramework::Components::TransformComponent>();
@@ -141,7 +141,7 @@ namespace UnitTest
// Set the new entity's transform to non zero values
// This helps validate in comparison tests that the transform values of created entities persist during slice operations
entityTransform->SetLocalScale(AZ::Vector3(5, 5, 5));
entityTransform->SetLocalUniformScale(5);
entityTransform->SetLocalRotation(AZ::Vector3RadToDeg(AZ::Vector3(90, 90, 90)));
entityTransform->SetLocalTranslation(AZ::Vector3(100, 100, 100));
@@ -149,6 +149,9 @@ namespace UnitTest
const char* GetAbsoluteDevGameFolderPath() override { return ""; }
const char* GetAbsoluteDevRootFolderPath() override { return ""; }
bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) override { return false; }
bool GenerateRelativeSourcePath(
[[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath,
[[maybe_unused]] AZStd::string& watchFolder) override { return false; }
bool GetFullSourcePathFromRelativeProductPath([[maybe_unused]] const AZStd::string& relPath, [[maybe_unused]] AZStd::string& fullSourcePath) override { return false; }
bool GetAssetInfoById([[maybe_unused]] const AZ::Data::AssetId& assetId, [[maybe_unused]] const AZ::Data::AssetType& assetType, [[maybe_unused]] const AZStd::string& platformName, [[maybe_unused]] AZ::Data::AssetInfo& assetInfo, [[maybe_unused]] AZStd::string& rootFilePath) override { return false; }
bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override;
@@ -610,15 +610,15 @@ namespace AzToolsFramework
m_layerEntity.m_layer->ClearUnsavedChanges();
// Change the scale of the child entity so it registers as an unsaved change on the layer.
AZ::Vector3 scale(-1.0f,0.0f,0.0f);
float scale = 0.0f;
AZ::TransformBus::EventResult(
scale,
childEntity->GetId(),
&AZ::TransformBus::Events::GetLocalScale);
scale.SetX(scale.GetX() + 1.0f);
&AZ::TransformBus::Events::GetLocalUniformScale);
scale += 1.0f;
AZ::TransformBus::Event(
childEntity->GetId(),
&AZ::TransformBus::Events::SetLocalScale,
&AZ::TransformBus::Events::SetLocalUniformScale,
scale);
bool hasUnsavedChanges = false;
@@ -52,19 +52,19 @@ namespace AzToolsFramework
TransformTestEntityHierarchy hierarchy = BuildTestHierarchy();
// Set scale to parent entity
const AZ::Vector3 parentScale(2.0f, 1.0f, 3.0f);
AZ::TransformBus::Event(hierarchy.m_parentId, &AZ::TransformInterface::SetLocalScale, parentScale);
const float parentScale = 2.0f;
AZ::TransformBus::Event(hierarchy.m_parentId, &AZ::TransformInterface::SetLocalUniformScale, parentScale);
// Set scale to child entity
const AZ::Vector3 childScale(5.0f, 6.0f, 10.0f);
AZ::TransformBus::Event(hierarchy.m_childId, &AZ::TransformInterface::SetLocalScale, childScale);
const float childScale = 5.0f;
AZ::TransformBus::Event(hierarchy.m_childId, &AZ::TransformInterface::SetLocalUniformScale, childScale);
const AZ::Vector3 expectedScale = childScale * parentScale;
const float expectedScale = childScale * parentScale;
AZ::Vector3 childWorldScale = AZ::Vector3::CreateOne();
AZ::TransformBus::EventResult(childWorldScale, hierarchy.m_childId, &AZ::TransformBus::Events::GetWorldScale);
float childWorldScale = 1.0f;
AZ::TransformBus::EventResult(childWorldScale, hierarchy.m_childId, &AZ::TransformBus::Events::GetWorldUniformScale);
EXPECT_THAT(childWorldScale, UnitTest::IsClose(expectedScale));
EXPECT_NEAR(childWorldScale, expectedScale, AZ::Constants::Tolerance);
}
TEST_F(EditorTransformComponentTest, TransformTests_GetChildren_DirectChildrenMatchHierarchy)
@@ -54,6 +54,7 @@ set(FILES
Prefab/Spawnable/SpawnableMetaDataTests.cpp
Prefab/MockPrefabFileIOActionValidator.cpp
Prefab/MockPrefabFileIOActionValidator.h
Prefab/PrefabDuplicateTests.cpp
Prefab/PrefabEntityAliasTests.cpp
Prefab/PrefabInstanceToTemplatePropagatorTests.cpp
Prefab/PrefabInstantiateTests.cpp