Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,71 @@
/*
* 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.
*
*/
#ifndef AZTOOLSFRAMEWORK_ASSETDATABASEAPI_H
#define AZTOOLSFRAMEWORK_ASSETDATABASEAPI_H
#include <AzCore/base.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Asset/AssetCommon.h>
namespace AzToolsFramework
{
namespace AssetDatabase
{
class ProductDatabaseEntry;
class SourceDatabaseEntry;
typedef AZStd::vector<ProductDatabaseEntry> ProductDatabaseEntryContainer;
/**
* Bus used by the Tools Asset Database itself to talk to the running application environment
* Functions on this bus could be implemented by different parts of the application
* and are thus not result-based, but instead passed in params which are expected to not be touched
* unless you have an answer to give.
*/
class AssetDatabaseRequests
: public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<AssetDatabaseRequests>;
/*!
* Used to retrieve the current database location from the running environment
*/
virtual bool GetAssetDatabaseLocation(AZStd::string& location) = 0;
};
using AssetDatabaseRequestsBus = AZ::EBus<AssetDatabaseRequests>;
class AssetDatabaseNotifications
: public AZ::EBusTraits
{
public:
typedef AZStd::recursive_mutex MutexType;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; // multiple listeners
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
virtual ~AssetDatabaseNotifications() = default;
virtual void OnSourceFileChanged(const SourceDatabaseEntry& /*entry*/) {}
virtual void OnSourceFileRemoved(AZ::s64 /*sourceId*/) {}
virtual void OnProductFileChanged(const ProductDatabaseEntry& /*entry*/) {}
virtual void OnProductFileRemoved(AZ::s64 /*productId*/) {}
virtual void OnProductFilesRemoved(const ProductDatabaseEntryContainer& /*products*/) {}
};
using AssetDatabaseNotificationBus = AZ::EBus<AssetDatabaseNotifications>;
} // namespace AssetDatabase
} // namespace AzToolsFramework
#endif // AZTOOLSFRAMEWORK_ASSETDATABASEAPI_H
@@ -0,0 +1,78 @@
/*
* 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/EBus/EBus.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzToolsFramework/ToolsComponents/EditorSelectionAccentSystemComponent.h>
class CEntityObject;
namespace AzToolsFramework
{
/**
* Bus for querying about sandbox data associated with a given Entity.
*/
class ComponentEntityEditorRequests
: public AZ::ComponentBus
{
public:
virtual ~ComponentEntityEditorRequests() {}
/// Retrieve sandbox object associated with the entity.
virtual CEntityObject* GetSandboxObject() = 0;
/// Returns true if the object is highlighted.
virtual bool IsSandboxObjectHighlighted() = 0;
// Sets accent for the component entity
virtual void SetSandboxObjectAccent(EntityAccentType accent) = 0;
// Set the component entity's isolation flag when the editor is in Isolation Mode
virtual void SetSandBoxObjectIsolated(bool isIsolated) = 0;
// Returns if the component entity is isolated when the editor is in Isolation Mode
virtual bool IsSandBoxObjectIsolated() = 0;
/// Updates the entity to match the visibility and lock state of its hierarchy.
/// Necessary because ancestors that are layers can override the current entity's visibility and lock state.
virtual void RefreshVisibilityAndLock() = 0;
};
using ComponentEntityEditorRequestBus = AZ::EBus < ComponentEntityEditorRequests >;
class ComponentEntityObjectRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
typedef void* BusIdType; // ID'd on CComponentEntityObject pointer.
//////////////////////////////////////////////////////////////////////////
virtual ~ComponentEntityObjectRequests() {}
/// Retrieve AZ Entity Id associated with this sandbox object.
virtual AZ::EntityId GetAssociatedEntityId() = 0;
/// Updates the undo cache for this sandbox object
virtual void UpdatePreemptiveUndoCache() = 0;
};
using ComponentEntityObjectRequestBus = AZ::EBus < ComponentEntityObjectRequests >;
} // namespace AzToolsFramework
@@ -0,0 +1,107 @@
/*
* 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/EBus/EBus.h>
#include <AzCore/Component/EntityId.h>
#include <AzFramework/Render/GeometryIntersectionStructures.h>
#include <AzToolsFramework/ToolsComponents/EditorSelectionAccentSystemComponent.h>
class CEntityObject;
namespace AzFramework
{
struct ViewportInfo;
}
namespace AzToolsFramework
{
/// Bus for customizing Entity selection logic from within the EditorComponents.
/// Used to provide with custom implementation for Ray intersection tests, specifying AABB, etc.
class EditorComponentSelectionRequests
: public AZ::ComponentBus
{
public:
/// @brief Returns an AABB that encompasses the object.
/// @return AABB that encompasses the object.
/// @note ViewportInfo may be necessary if the all or part of the object
/// stays at a constant size regardless of camera position.
virtual AZ::Aabb GetEditorSelectionBoundsViewport(
const AzFramework::ViewportInfo& /*viewportInfo*/)
{
AZ_Assert(!SupportsEditorRayIntersect(),
"Component claims to support ray intersection but GetEditorSelectionBoundsViewport "
"has not been implemented in the derived class");
return AZ::Aabb::CreateNull();
}
/// @brief Returns true if editor selection ray intersects with the handler.
/// @return True if the editor selection ray intersects the handler.
/// @note ViewportInfo may be necessary if the all or part of the object
/// stays at a constant size regardless of camera position.
virtual bool EditorSelectionIntersectRayViewport(
const AzFramework::ViewportInfo& /*viewportInfo*/,
const AZ::Vector3& /*src*/, const AZ::Vector3& /*dir*/, float& /*distance*/)
{
AZ_Assert(!SupportsEditorRayIntersect(),
"Component claims to support ray intersection but EditorSelectionIntersectRayViewport "
"has not been implemented in the derived class");
return false;
}
/// @brief Returns true if the component overrides EditorSelectionIntersectRay method,
/// otherwise selection will be based only on AABB test.
/// @return True if EditorSelectionIntersectRay method is implemented.
virtual bool SupportsEditorRayIntersect() { return false; }
protected:
~EditorComponentSelectionRequests() = default;
};
/// Type to inherit to implement EditorComponentSelectionRequests.
using EditorComponentSelectionRequestsBus = AZ::EBus<EditorComponentSelectionRequests>;
/// Bus that provides notifications about selection events of the parent Entity.
class EditorComponentSelectionNotifications
: public AZ::EBusTraits
{
public:
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
typedef AZ::EntityId BusIdType;
/// @brief Notifies listeners about in-editor selection events (mouse hover, selected, etc.)
virtual void OnAccentTypeChanged(EntityAccentType /*accent*/) {}
protected:
~EditorComponentSelectionNotifications() = default;
};
/// Type to inherit to implement EditorComponentSelectionNotifications.
using EditorComponentSelectionNotificationsBus = AZ::EBus<EditorComponentSelectionNotifications>;
/// Returns the union of all editor selection bounds on a given Entity.
/// @note The returned Aabb is in world space.
inline AZ::Aabb CalculateEditorEntitySelectionBounds(
const AZ::EntityId entityId, const AzFramework::ViewportInfo& viewportInfo)
{
AZ::EBusReduceResult<AZ::Aabb, AzFramework::AabbUnionAggregator> aabbResult(AZ::Aabb::CreateNull());
EditorComponentSelectionRequestsBus::EventResult(
aabbResult, entityId, &EditorComponentSelectionRequests::GetEditorSelectionBoundsViewport, viewportInfo);
return aabbResult.value;
}
} // namespace AzToolsFramework
@@ -0,0 +1,39 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
namespace AzToolsFramework
{
/**
* This bus allows you to make editor specific requests of the Animation System
*/
class EditorAnimationSystemRequests : public AZ::EBusTraits
{
public:
virtual ~EditorAnimationSystemRequests() = default;
enum AnimationSystem
{
EMotionFX,
CryAnimation
};
/**
* Determines if the given animation system is active
* @param systemType the type of system to query for
*/
virtual bool IsSystemActive([[maybe_unused]] AnimationSystem systemType) { return false; }
};
using EditorAnimationSystemRequestsBus = AZ::EBus<EditorAnimationSystemRequests>;
}
@@ -0,0 +1,320 @@
/*
* 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/EBus/EBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/PlatformDef.h>
namespace AzToolsFramework
{
namespace AssetSystem
{
class AssetJobsInfoResponse;
class AssetJobsInfoRequest;
//! A bus to talk to the asset system as a tool or editor
//! This contains things that only tools or editors should be given access to
//! If you want to talk to it as if a game engine component or runtime component
//! \ref AssetSystemBus.h
//! in the common header location.
class AssetSystemRequest
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; // single listener
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; // single bus
using MutexType = AZStd::recursive_mutex;
// don't lock this bus during dispatch - its mainly just a forwarder of socket-based network requests
// so when one thread is asking for status of an asset, its okay for another thread to do the same.
static const bool LocklessDispatch = true;
virtual ~AssetSystemRequest() = default;
//! Retrieve the absolute path for the Asset Database Location
virtual bool GetAbsoluteAssetDatabaseLocation(AZStd::string& /*result*/) { return false; }
//! Retrieve the absolute folder path to the current game's source assets (the ones that go into source control)
//! This may include the current mod path, if a mod is being edited by the editor
virtual const char* GetAbsoluteDevGameFolderPath() = 0;
//! Retrieve the absolute folder path to the current developer root ('dev'), which contains source artifacts
//! 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.
/// 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 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)
virtual bool GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath) = 0;
//! retrieve an Az::Data::AssetInfo class for the given assetId. this may map to source too in which case rootFilePath will be non-empty.
virtual bool GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath) = 0;
/**
* Given a path to a source file, retrieve its actual watch folder path and details.
* @param sourcePath is either a relative or absolute path to a source file.
* @param assetInfo is a /ref AZ::Data::AssetInfo filled out with details about the asset including its relative path to its watch folder
* note that inside assetInfo is a AssetId, but only the UUID-part will ever have a value since we are dealing with a source file (no subid)
* @param watchFolder is the scan folder that it was found inside (the path in the assetInfo is relative to this folder).
* If you Path::Join the watchFolder and the assetInfo relative path, you get the full path.
* returns false if it cannot find the source, true otherwise.
*/
virtual bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) = 0;
/**
* Given a UUID of a source file, retrieve its actual watch folder path and other details.
* @param sourceUUID is the UUID of a source file - If you have an AssetID, its the m_guid member of that assetId
* @param assetInfo is a /ref AZ::Data::AssetInfo filled out with details about the asset including its relative path to its watch folder
* note that inside assetInfo is a AssetId, but only the UUID-part will ever have a value since we are dealing with a source file (no subid)
* @param watchFolder is the scan folder that it was found inside (the path in the assetInfo is relative to this folder).
* If you Path::Join the watchFolder and the assetInfo relative path, you get the full path to the source file on physical media
* returns false if it cannot find the source, true otherwise.
*/
virtual bool GetSourceInfoBySourceUUID(const AZ::Uuid& sourceUuid, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) = 0;
/**
* Returns a list of scan folders recorded in the database.
* @param scanFolder gets appended with the found folders.
*/
virtual bool GetScanFolders(AZStd::vector<AZStd::string>& scanFolders) = 0;
/**
* Populates a list with folders that are safe to store assets in.
* This is a subset of the scan folders.
* @param scanFolder gets appended with the found folders.
* @return false if this process fails.
*/
virtual bool GetAssetSafeFolders(AZStd::vector<AZStd::string>& assetSafeFolders) = 0;
/**
* Query to see if a specific asset platform is enabled
* @param platform the asset platform to check e.g. es3, 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.
* @return -1 if the process fails, a positive number otherwise
*/
virtual int GetPendingAssetsForPlatform(const char* platform) = 0;
/**
* Given a UUID of a source file, retrieve the products info.
* @param sourceUUID is the UUID of a source file - If you have an AssetID, its the m_guid member of that assetId
* @param productsAssetInfo is a /ref AZStd::vector<AZ::Data::AssetInfo> filled out with details about the products
* returns false if it cannot find the source, true otherwise.
*/
virtual bool GetAssetsProducedBySourceUUID(const AZ::Uuid& sourceUuid, AZStd::vector<AZ::Data::AssetInfo>& productsAssetInfo) = 0;
};
//! AssetSystemBusTraits
//! This bus is for events that concern individual assets and is addressed by file extension
class AssetSystemNotifications
: public AZ::EBusTraits
{
public:
typedef AZStd::recursive_mutex MutexType;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; // multiple listeners
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const bool EnableEventQueue = true; // enabled queued events, asset messages come from any thread
virtual ~AssetSystemNotifications() = default;
//! Called by the AssetProcessor when a source of an asset has been modified.
virtual void SourceFileChanged(AZStd::string /*relativePath*/, AZStd::string /*scanFolder*/, AZ::Uuid /*sourceUUID*/) {}
//! Called by the AssetProcessor when a source of an asset has been removed.
virtual void SourceFileRemoved(AZStd::string /*relativePath*/, AZStd::string /*scanFolder*/, AZ::Uuid /*sourceUUID*/) {}
//! This will be used by the asset processor to notify whenever a source file fails to process.
virtual void SourceFileFailed(AZStd::string /*relativePath*/, AZStd::string /*scanFolder*/, AZ::Uuid /*sourceUUID*/) {}
};
//! This enum have all the different job states
//! Please note that these job status are written to the database, so it is very important that any new status should be added at the end else the database might get corrupted
enum class JobStatus
{
Any = -1, //used exclusively by the database to indicate any state query, no job should ever actually be in this state, we start in Queued and progress from there
Queued, // its in the queue and will be built shortly
InProgress, // its being compiled right now.
Failed,
Failed_InvalidSourceNameExceedsMaxLimit, // use this enum to indicate that the job failed because the source file name length exceeds the maximum length allowed
Completed, // built successfully (no failure occurred)
Missing //indicate that the job is not present for example if the source file is not there, or if job key is not there
};
inline const char* JobStatusString(JobStatus status)
{
switch(status)
{
case JobStatus::Any: return "Any";
case JobStatus::Queued: return "Queued";
case JobStatus::InProgress: return "InProgress";
case JobStatus::Failed: return "Failed";
case JobStatus::Failed_InvalidSourceNameExceedsMaxLimit: return "Failed_InvalidSourceNameExceedsMaxLimit";
case JobStatus::Completed: return "Completed";
case JobStatus::Missing: return "Missing";
}
return nullptr;
}
//! This struct is used for responses and requests about Asset Processor Jobs
struct JobInfo
{
AZ_TYPE_INFO(JobInfo, "{276C9DE3-0C81-4721-91FE-F7C961D28DA8}")
JobInfo()
{
m_jobRunKey = rand();
}
AZ::u32 GetHash() const
{
AZ::Crc32 crc(m_sourceFile.c_str());
crc.Add(m_platform.c_str());
crc.Add(m_jobKey.c_str());
crc.Add(m_builderGuid.ToString<AZStd::string>().c_str());
return crc;
}
static void Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<JobInfo>()
->Version(4)
->Field("sourceFile", &JobInfo::m_sourceFile)
->Field("platform", &JobInfo::m_platform)
->Field("builderUuid", &JobInfo::m_builderGuid)
->Field("jobKey", &JobInfo::m_jobKey)
->Field("jobRunKey", &JobInfo::m_jobRunKey)
->Field("status", &JobInfo::m_status)
->Field("firstFailLogTime", &JobInfo::m_firstFailLogTime)
->Field("firstFailLogFile", &JobInfo::m_firstFailLogFile)
->Field("lastFailLogTime", &JobInfo::m_lastFailLogTime)
->Field("lastFailLogFile", &JobInfo::m_lastFailLogFile)
->Field("lastLogTime", &JobInfo::m_lastLogTime)
->Field("lastLogFile", &JobInfo::m_lastLogFile)
->Field("jobID", &JobInfo::m_jobID)
->Field("watchFolder", &JobInfo::m_watchFolder)
->Field("errorCount", &JobInfo::m_errorCount)
->Field("warningCount", &JobInfo::m_warningCount)
;
}
}
//! the file from which this job was originally spawned. Is just the relative source file name ("whatever/something.tif", not an absolute path)
AZStd::string m_sourceFile;
//! the watchfolder for the file from which this job was originally spawned.
AZStd::string m_watchFolder;
//! which platform this is for. Will be something like "pc" or "android"
AZStd::string m_platform;
//! The uuid of the builder
AZ::Uuid m_builderGuid = AZ::Uuid::CreateNull();
//! Job Key is arbitrarily defined by the builder. Used to differentiate between different jobs emitted for the same input file, for the same platform, for the same builder.
//! for example, you might want to split a particularly complicated and time consuming job into multiple sub-jobs. In which case they'd all have the same input file,
//! the same platform, the same builder UUID (since its the UUID of the builder itself)
//! but would have different job keys.
AZStd::string m_jobKey;
//random int made to identify this attempt to process this job
AZ::u64 m_jobRunKey = 0;
//current status
JobStatus m_status = JobStatus::Queued;
//logging
AZ::s64 m_firstFailLogTime = 0;
AZStd::string m_firstFailLogFile;
AZ::s64 m_lastFailLogTime = 0;
AZStd::string m_lastFailLogFile;
AZ::s64 m_lastLogTime = 0;
AZStd::string m_lastLogFile;
AZ::s64 m_errorCount = 0;
AZ::s64 m_warningCount = 0;
AZ::s64 m_jobID = 0; // this is the actual database row. Client is unlikely to need this.
};
typedef AZStd::vector<JobInfo> JobInfoContainer;
//! This Ebus will be used to retrieve all the job related information from AP
class AssetSystemJobRequest
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; // single listener
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; //single bus
virtual ~AssetSystemJobRequest() = default;
/// Retrieve Jobs information for the given source file, setting escalteJobs to true will escalate all queued jobs
virtual AZ::Outcome<JobInfoContainer> GetAssetJobsInfo(const AZStd::string& sourcePath, const bool escalateJobs) = 0;
/// Retrieve Jobs information for the given assetId, setting escalteJobs to true will escalate all queued jobs
/// you can also specify whether fencing is required
virtual AZ::Outcome<JobInfoContainer> GetAssetJobsInfoByAssetID(const AZ::Data::AssetId& assetId, const bool escalateJobs, bool requireFencing) = 0;
/// Retrieve Jobs information for the given jobKey
virtual AZ::Outcome<JobInfoContainer> GetAssetJobsInfoByJobKey(const AZStd::string& jobKey, const bool escalateJobs) = 0;
/// Retrieve Job Status for the given jobKey.
/// If no jobs are present, return missing,
/// else, if any matching jobs have failed, it will return failed
/// else, if any of the matching jobs are queued, it will return queued
/// else, if any matching jobs are in progress, will return inprogress
/// else it will return the completed job status.
virtual AZ::Outcome<JobStatus> GetAssetJobsStatusByJobKey(const AZStd::string& jobKey, const bool escalateJobs) = 0;
/// Retrieve the actual log content for a particular job. you can retrieve the run key from the above info function.
virtual AZ::Outcome<AZStd::string> GetJobLog(AZ::u64 jobrunkey) = 0;
};
inline const char* GetHostAssetPlatform()
{
#if defined(AZ_PLATFORM_MAC)
return "osx_gl";
#elif defined(AZ_PLATFORM_WINDOWS)
return "pc";
#elif defined(AZ_PLATFORM_LINUX)
// set this to pc because that's what bootstrap.cfg currently defines the platform to "pc", even on Linux
return "pc";
#else
#error Unimplemented Host Asset Platform
#endif
}
} // namespace AssetSystem
using AssetSystemBus = AZ::EBus<AssetSystem::AssetSystemNotifications>;
using AssetSystemRequestBus = AZ::EBus<AssetSystem::AssetSystemRequest>;
using AssetSystemJobRequestBus = AZ::EBus<AssetSystem::AssetSystemJobRequest>;
} // namespace AzToolsFramework
@@ -0,0 +1,33 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/RTTI/BehaviorContext.h>
#include "EditorCameraBus.h"
namespace Camera
{
void EditorCameraRequests::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<EditorCameraRequestBus>("EditorCameraRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "editor")
->Event("SetViewFromEntityPerspective", &EditorCameraRequestBus::Events::SetViewFromEntityPerspective)
->Event("SetViewAndMovementLockFromEntityPerspective", &EditorCameraRequestBus::Events::SetViewAndMovementLockFromEntityPerspective)
->Event("GetCurrentViewEntityId", &EditorCameraRequestBus::Events::GetCurrentViewEntityId)
->Event("GetActiveCameraPosition", &EditorCameraRequestBus::Events::GetActiveCameraPosition)
;
}
}
}
@@ -0,0 +1,111 @@
/*
* 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/Math/Vector3.h>
#include <AzCore/Component/Component.h>
namespace Camera
{
/**
* This bus allows you to get and set the current editor viewport camera
*/
class EditorCameraRequests : public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<EditorCameraRequests>;
virtual ~EditorCameraRequests() = default;
static void Reflect(AZ::ReflectContext* context);
/**
* Sets the view from the entity's perspective
* @param entityId the id of the entity whose perspective is to be used
*/
virtual void SetViewFromEntityPerspective(const AZ::EntityId& /*entityId*/) {}
/**
* Sets the view from the entity's perspective
* @param entityId the id of the entity whose perspective is to be used
* @param lockCameraMovement disallow camera movement from user input in the editor render viewport.
*/
virtual void SetViewAndMovementLockFromEntityPerspective(const AZ::EntityId& /*entityId*/, bool /*lockCameraMovement*/) {}
/**
* Gets the id of the current view entity. Invalid EntityId is returned for the default editor camera
* @return the entityId of the entity currently being used as the view. The Invalid entity id is returned for the default editor camera
*/
virtual AZ::EntityId GetCurrentViewEntityId() { return AZ::EntityId(); }
/**
* Gets the position of the currently active Editor camera.
* The Editor can have multiple viewports displayed, though at most only one is active at any point in time.
* (Active is not the same as "has focus" - a different editor pane can have focus, but there's still one
* active viewport that's updating every frame, and the others are not)
* @param cameraPos On return, the current camera position in the one active Editor viewport.
* @return True if the camera position was successfully retrieved, false if not.
*/
virtual bool GetActiveCameraPosition(AZ::Vector3& /*cameraPos*/) { return false; }
};
using EditorCameraRequestBus = AZ::EBus<EditorCameraRequests>;
/**
* This is the bus to interface with the camera system component
*/
class EditorCameraSystemRequests : public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<EditorCameraSystemRequests>;
virtual ~EditorCameraSystemRequests() = default;
virtual void CreateCameraEntityFromViewport() {}
};
using EditorCameraSystemRequestBus = AZ::EBus<EditorCameraSystemRequests>;
/**
* Handle this bus to be notified when the current editor viewport entity id changes
*/
class EditorCameraNotifications : public AZ::EBusTraits
{
public:
virtual ~EditorCameraNotifications() = default;
/**
* Handle this message to know when the current viewports view entity has changed
* @param newViewId the id of the entity the current view has switched to
*/
virtual void OnViewportViewEntityChanged(const AZ::EntityId& /*newViewId*/) {}
};
using EditorCameraNotificationBus = AZ::EBus<EditorCameraNotifications>;
/**
* This bus is for requesting any camera-view-related changes
*/
class EditorCameraViewRequests : public AZ::ComponentBus
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
virtual ~EditorCameraViewRequests() = default;
/**
* Sets this camera as the active view in the scene, otherwise restores the default editor camera if it was already active
*/
virtual void ToggleCameraAsActiveView() = 0;
};
using EditorCameraViewRequestBus = AZ::EBus<EditorCameraViewRequests>;
} // namespace Camera
@@ -0,0 +1,29 @@
/*
* 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/EBus/EBus.h>
namespace AzToolsFramework
{
class EditorLevelNotifications : public AZ::EBusTraits
{
public:
virtual ~EditorLevelNotifications() = default;
//! Invoked when a new level is created in the editor
virtual void OnNewLevelCreated() {}
};
using EditorLevelNotificationBus = AZ::EBus<EditorLevelNotifications>;
}
@@ -0,0 +1,109 @@
/*
* 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/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
namespace AzToolsFramework
{
//! Interface into the Python virtual machine's data
class EditorPythonConsoleInterface
{
protected:
EditorPythonConsoleInterface() = default;
virtual ~EditorPythonConsoleInterface() = default;
EditorPythonConsoleInterface(EditorPythonConsoleInterface&&) = delete;
EditorPythonConsoleInterface& operator=(EditorPythonConsoleInterface&&) = delete;
public:
AZ_RTTI(EditorPythonConsoleInterface, "{CAE877C8-DAA8-4535-BCBF-F392EAA7CEA9}");
//! Returns the known list of modules exported to Python
virtual void GetModuleList(AZStd::vector<AZStd::string_view>& moduleList) const = 0;
//! Returns the known list of global functions inside a Python module
struct GlobalFunction
{
AZStd::string_view m_moduleName;
AZStd::string_view m_functionName;
AZStd::string_view m_description;
};
using GlobalFunctionCollection = AZStd::vector<GlobalFunction>;
virtual void GetGlobalFunctionList(GlobalFunctionCollection& globalFunctionCollection) const = 0;
};
//! Interface to signal the phases for the Python virtual machine
class EditorPythonEventsInterface
{
protected:
EditorPythonEventsInterface() = default;
virtual ~EditorPythonEventsInterface() = default;
EditorPythonEventsInterface(EditorPythonEventsInterface&&) = delete;
EditorPythonEventsInterface& operator=(EditorPythonEventsInterface&&) = delete;
public:
AZ_RTTI(EditorPythonEventsInterface, "{F50AE641-2C80-4E07-B4B3-7CB34FFAB393}");
//! Signal the Python handler to start
virtual bool StartPython(bool silenceWarnings = false) = 0;
//! Signal the Python handler to stop
virtual bool StopPython(bool silenceWarnings = false) = 0;
//! Determines if the caller needs to wait for the Python VM to initialize (non-main thread only)
virtual void WaitForInitialization() {}
//! Acquires the Python global interpreter lock (GIL) and executed the callback
virtual void ExecuteWithLock(AZStd::function<void()> executionCallback) = 0;
};
//! A bus to handle post notifications to the console views of Python output
class EditorPythonConsoleNotifications
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::MultipleAndOrdered;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
// used with the AZ::EBusHandlerPolicy::MultipleAndOrdered HandlerPolicy
struct BusHandlerOrderCompare
{
bool operator()(EditorPythonConsoleNotifications* left, EditorPythonConsoleNotifications* right) const
{
return left->GetOrder() < right->GetOrder();
}
};
/**
* Specifies the order a handler receives events relative to other handlers
* @return a value specifying this handler's relative order
*/
virtual int GetOrder()
{
return std::numeric_limits<int>::max();
}
//////////////////////////////////////////////////////////////////////////
//! post a normal message to the console
virtual void OnTraceMessage(AZStd::string_view message) = 0;
//! post an error message to the console
virtual void OnErrorMessage(AZStd::string_view message) = 0;
//! post an internal Python exception from a script call
virtual void OnExceptionMessage(AZStd::string_view message) = 0;
};
using EditorPythonConsoleNotificationBus = AZ::EBus<EditorPythonConsoleNotifications>;
}
@@ -0,0 +1,46 @@
/*
* 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/EBus/EBus.h>
namespace AzToolsFramework
{
/**
* Provides a bus to run Python scripts
*/
class EditorPythonRunnerRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
//! executes a Python script using a string, prints the result if printResult is true and script is an expression
virtual void ExecuteByString(AZStd::string_view script, bool printResult) { AZ_UNUSED(script); AZ_UNUSED(printResult); }
//! executes a Python script using a filename
virtual void ExecuteByFilename(AZStd::string_view filename) { AZ_UNUSED(filename); }
//! executes a Python script using a filename and args
virtual void ExecuteByFilenameWithArgs(AZStd::string_view filename, const AZStd::vector<AZStd::string_view>& args) { AZ_UNUSED(filename); AZ_UNUSED(args); }
//! executes a Python script as a test
virtual void ExecuteByFilenameAsTest(AZStd::string_view filename, const AZStd::vector<AZStd::string_view>& args) { AZ_UNUSED(filename); AZ_UNUSED(args); }
};
using EditorPythonRunnerRequestBus = AZ::EBus<EditorPythonRunnerRequests>;
} // namespace AzToolsFramework
@@ -0,0 +1,46 @@
/*
* 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/base.h>
#include <AzCore/EBus/EBus.h>
class CVegetationMap;
struct CVegetationInstance;
namespace AzToolsFramework
{
namespace EditorVegetation
{
/**
* Bus used to talk to VegetationMap across the application
*/
class EditorVegetationRequests
: public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<EditorVegetationRequests>;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
typedef CVegetationMap* BusIdType;
virtual ~EditorVegetationRequests() {}
virtual AZStd::vector<CVegetationInstance*> GetObjectInstances(const AZ::Vector2& min, const AZ::Vector2& max) = 0;
virtual void DeleteObjectInstance(CVegetationInstance* instance) = 0;
};
using EditorVegetationRequestsBus = AZ::EBus<EditorVegetationRequests>;
}
}
@@ -0,0 +1,48 @@
/*
* 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.
*
*/
#ifndef AZTOOLSFRAMEWORK_EDITORWINDOWREQUESTBUS_H
#define AZTOOLSFRAMEWORK_EDITORWINDOWREQUESTBUS_H
#include <AzCore/base.h>
#pragma once
#include <AzCore/EBus/EBus.h>
class QWidget;
namespace AzToolsFramework
{
/**
* Bus for general editor window requests to be intercepted by the application.
*/
class EditorWindowRequests
: public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<EditorWindowRequests>;
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
//////////////////////////////////////////////////////////////////////////
/// Retrieve the main application window.
virtual QWidget* GetAppMainWindow() { return nullptr; }
};
using EditorWindowRequestBus = AZ::EBus<EditorWindowRequests>;
} // namespace AzToolsFramework
#endif // AZTOOLSFRAMEWORK_EDITORWINDOWREQUESTBUS_H
@@ -0,0 +1,55 @@
/*
* 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/base.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Component/Entity.h>
namespace AzToolsFramework
{
using EntityIdList = AZStd::vector<AZ::EntityId>;
class EntityCompositionNotifications
: public AZ::EBusTraits
{
public:
/*!
* Notification that the specified entities are about to have their composition changed due to user interaction in the editor
*
* \param entityIds Entities about to be changed
*/
virtual void OnEntityCompositionChanging(const AzToolsFramework::EntityIdList& /*entityIds*/) {};
/*!
* Notification that the specified entities had their composition changed due to user interaction in the editor
*
* \param entityIds Entities changed
*/
virtual void OnEntityCompositionChanged(const AzToolsFramework::EntityIdList& /*entityIds*/) {};
/*!@{
* Discrete composition events for adding, removing, enabling and disabling components
*/
virtual void OnEntityComponentAdded(const AZ::EntityId& /*entityId*/, const AZ::ComponentId& /*componentId*/) {};
virtual void OnEntityComponentRemoved(const AZ::EntityId& /*entityId*/, const AZ::ComponentId& /*componentId*/) {};
virtual void OnEntityComponentEnabled(const AZ::EntityId& /*entityId*/, const AZ::ComponentId& /*componentId*/) {};
virtual void OnEntityComponentDisabled(const AZ::EntityId& /*entityId*/, const AZ::ComponentId& /*componentId*/) {};
//!@}
};
using EntityCompositionNotificationBus = AZ::EBus<EntityCompositionNotifications>;
}
@@ -0,0 +1,320 @@
/*
* 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/base.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
namespace AzToolsFramework
{
class EntityCompositionRequests
: public AZ::EBusTraits
{
public:
struct AddComponentsResults
{
// This is the original list of components added (whether or not they are pending, in the order of class data requested)
AZ::Entity::ComponentArrayType m_componentsAdded;
/*!
* Adding a component can only cause the following to occur:
* 1) Component gets added to the pending list
* 2) Components Gets added to the entity as a valid component
* 3) Cause pending components to be added to the entity as valid components (by satisfying previously missing services)
*
* The following three vectors represent each occurrence, respectively.
*/
AZ::Entity::ComponentArrayType m_addedPendingComponents;
AZ::Entity::ComponentArrayType m_addedValidComponents;
AZ::Entity::ComponentArrayType m_additionalValidatedComponents;
};
/*!
* Stores a map of entity ids to component results that were added during AddComponentsToEntities.
* You can use this to look up what exactly happened to each entity involved.
* Components requested to be added will be stored in either addedPendingComponents or addedValidComponents
* Any other previously pending components that are now valid will be stored in additionalValidatedComponents
*/
using EntityToAddedComponentsMap = AZStd::unordered_map<AZ::EntityId, AddComponentsResults>;
/*!
* Outcome will be true if successful and return the above results structure to indicate what happened
* Outcome will be false if critical underlying system failure occurred (which is not expected) and an error string will describe the problem
*/
using AddComponentsOutcome = AZ::Outcome<EntityToAddedComponentsMap, AZStd::string>;
/*!
* Outcome will be true if successful and return one instance of the above AddComponentsResults structure (since only one entity is involved)
*/
using AddExistingComponentsOutcome = AZ::Outcome<AddComponentsResults, AZStd::string>;
/*!
* Add the specified component types to the specified entities.
*
* \param entityIds Entities to receive the new components.
* \param componentsToAdd A list of AZ::Uuid representing the unique id of the type of components to add.
*
* \return Returns a successful outcome if components were added to entities.
* If the operation could not be completed then the failed
* outcome contains a string describing what went wrong.
*/
virtual AddComponentsOutcome AddComponentsToEntities(const EntityIdList& entityIds, const AZ::ComponentTypeList& componentsToAdd) = 0;
/*!
* Add the specified existing components to the specified entity.
*
* \param entityId The AZ::EntityId to add the existing components to, with full editor-level checking with pending component support
* \param componentsToAdd A list of AZ::Component* containing existing components to add. (Note: These components must not already be tied to another entity!)
*
* \return Returns a successful outcome if components were added to entities.
* If the operation could not be completed then the failed
* outcome contains a string describing what went wrong.
*/
virtual AddExistingComponentsOutcome AddExistingComponentsToEntityById(const AZ::EntityId& entityId, const AZStd::vector<AZ::Component*>& componentsToAdd) = 0;
// Removing a component can only cause the following to occur:
// 1) Invalidate other components by removing missing services
// 2) Validate other components by removing conflicting pending services
struct RemoveComponentsResults
{
//! Invalidated Components are those that were previously valid but no longer are valid.
AZ::Entity::ComponentArrayType m_invalidatedComponents;
//! Validated Components are those that were previously invalid (and being held back) and are now valid.
//! Note that during a "Scrub" operation, this remains true - it will only list those that were previously
//! inactive, and were activated by the scrub, it will not contain the list of previously active that remain active.
AZ::Entity::ComponentArrayType m_validatedComponents;
};
using EntityToRemoveComponentsResultMap = AZStd::unordered_map<AZ::EntityId, RemoveComponentsResults>;
using RemoveComponentsOutcome = AZ::Outcome<EntityToRemoveComponentsResultMap, AZStd::string>;
/*!
* Removes the specified components from the specified entities.
*
* \param componentsToRemove List of component pointers to remove (from their respective entities).
* \return true if the components were successfully removed or false otherwise.
*/
virtual RemoveComponentsOutcome RemoveComponents(const AZStd::vector<AZ::Component*>& componentsToRemove) = 0;
using ScrubEntityResults = RemoveComponentsResults;
using EntityToScrubEntityResultsMap = AZStd::unordered_map<AZ::EntityId, ScrubEntityResults>;
using ScrubEntitiesOutcome = AZ::Outcome<EntityToScrubEntityResultsMap, AZStd::string>;
/*!
* Scrub entities so that they can be activated.
* Components will be moved to the pending list if they cannot be activated.
* If a component had been pending, but can now be activated, then it will be re-enabled.
*
* ScrubEntities() may be called on entities before they are initialized.
*
* \return If successful, outcome contains details about the scrubbing.
* If unsuccessful, outcome contains a string describing what went wrong.
*
* To decipher the outcome, understand that when you run the scrub an entity, 4 possible things can happen to each component:
* 1) Component was active before and remains active now
* --> These can be retrieved from Entity::GetComponents() and are unchanged
* 2) Component was active before, but is now INACTIVE due to invalid requirements.
* --> These are on the Outcome's m_invalidatedComponents list.
* --> They are also added to a EditorPendingCompositionComponent on the entity. This component's job is to keep track
* of invalid components and save their data in case they become active again in a later scrub.
* 3) Components which were inactive before (because of #2 above, in a previous scrub), but now have their requirements
* satisfied during this new scrub.
* --> These will be in Entity::GetComponents() but also the m_validated components list to distinguish them from the first case 1) above.
* 4) "Hidden" built-in components may be deprecated or invalid.
* ---> These will be deleted and a warning will be issued. They will not be in any list, since they are deleted.
*/
virtual ScrubEntitiesOutcome ScrubEntities(const EntityList& entities) = 0;
/*!
* Removes the given components from their respective entities (currently only single entity is supported) and copies the data to the clipboard if successful
* \param components vector of components to cut (this method will delete the components provided on successful removal)
*/
virtual void CutComponents(const AZStd::vector<AZ::Component*>& components) = 0;
/*!
* Copies the given components from their respective entities (multiple source entities are supported) into mime data on the clipboard for pasting elsewhere
* \param components vector of components to copy
*/
virtual void CopyComponents(const AZStd::vector<AZ::Component*>& components) = 0;
/*!
* Pastes components from the mime data on the clipboard (assuming it is component data) to the given entity
* \param entityId the Id of the entity to paste to
*/
virtual void PasteComponentsToEntity(AZ::EntityId entityId) = 0;
/*!
* Checks if there is component data available to paste into an entity
* \return true if paste is available, false otherwise
*/
virtual bool HasComponentsToPaste() = 0;
/*!
* Enables the given components
* \param components vector of components to enable
*/
virtual void EnableComponents(const AZStd::vector<AZ::Component*>& components) = 0;
/*!
* Disables the given components
* \param components vector of components to disable
*/
virtual void DisableComponents(const AZStd::vector<AZ::Component*>& components) = 0;
using ComponentServicesList = AZStd::vector<AZ::ComponentServiceType>;
/*!
* Info detailing why a pending component cannot be activated.
*/
struct PendingComponentInfo
{
AZ::Entity::ComponentArrayType m_validComponentsThatAreIncompatible;
AZ::Entity::ComponentArrayType m_pendingComponentsWithRequiredServices;
AZ::Entity::StringWarningArray m_warnings;
ComponentServicesList m_missingRequiredServices;
ComponentServicesList m_incompatibleServices;
};
/*
* Returns detailed info regarding a pending component.
* Pending components are those that cannot be activated due to
* missing requirements or incompatibilities with another component.
*/
virtual PendingComponentInfo GetPendingComponentInfo(const AZ::Component* component) = 0;
/*!
* Returns a name for the given component Note: This will always dig into the underlying type. e.g. you will never get the GenericComponentWrapper name, but always the actual underlying component
* \param component the pointer to the component for which you want the name.
*/
virtual AZStd::string GetComponentName(const AZ::Component* component) = 0;
};
using EntityCompositionRequestBus = AZ::EBus<EntityCompositionRequests>;
//! Return whether component should appear in an entity's "Add Component" menu.
//! \param entityType The type of entity (ex: "Game", "System")
static bool AppearsInAddComponentMenu(const AZ::SerializeContext::ClassData& classData, const AZ::Crc32& entityType);
//! ComponentFilter for components that users can add to game entities.
static bool AppearsInGameComponentMenu(const AZ::SerializeContext::ClassData&);
//! ComponentFilter for components that can be added to system entities.
static bool AppearsInSystemComponentMenu(const AZ::SerializeContext::ClassData&);
//
// Implementation
//
inline bool AppearsInAddComponentMenu(const AZ::SerializeContext::ClassData& classData, const AZ::Crc32& entityType)
{
if (classData.m_editData)
{
if (auto editorDataElement = classData.m_editData->FindElementData(AZ::Edit::ClassElements::EditorData))
{
for (const AZ::Edit::AttributePair& attribPair : editorDataElement->m_attributes)
{
if (attribPair.first == AZ::Edit::Attributes::AppearsInAddComponentMenu)
{
PropertyAttributeReader reader(nullptr, attribPair.second);
AZ::Crc32 classEntityType = 0;
AZStd::vector<AZ::Crc32> classEntityTypes;
if (reader.Read<AZ::Crc32>(classEntityType))
{
if (static_cast<AZ::u32>(entityType) == classEntityType)
{
return true;
}
}
else if (reader.Read<AZStd::vector<AZ::Crc32>>(classEntityTypes))
{
if (AZStd::find(classEntityTypes.begin(), classEntityTypes.end(), entityType) != classEntityTypes.end())
{
return true;
}
}
}
}
}
}
return false;
}
inline bool AppearsInGameComponentMenu(const AZ::SerializeContext::ClassData& classData)
{
// We don't call AppearsInAddComponentMenu(...) because we support legacy values.
// AppearsInAddComponentMenu used to be a bool,
// and it used to only be applied to components on in-game entities.
if (classData.m_editData)
{
if (auto editorDataElement = classData.m_editData->FindElementData(AZ::Edit::ClassElements::EditorData))
{
for (const AZ::Edit::AttributePair& attribPair : editorDataElement->m_attributes)
{
if (attribPair.first == AZ::Edit::Attributes::AppearsInAddComponentMenu)
{
PropertyAttributeReader reader(nullptr, attribPair.second);
AZ::Crc32 classEntityType;
AZStd::vector<AZ::Crc32> classEntityTypes;
if (reader.Read<AZ::Crc32>(classEntityType))
{
if (classEntityType == AZ_CRC("Game", 0x232b318c))
{
return true;
}
}
else if (reader.Read<AZStd::vector<AZ::Crc32>>(classEntityTypes))
{
if (AZStd::find(classEntityTypes.begin(), classEntityTypes.end(), AZ_CRC("Game", 0x232b318c)) != classEntityTypes.end())
{
return true;
}
}
bool legacyAppearsInComponentMenu = false;
if (reader.Read<bool>(legacyAppearsInComponentMenu))
{
AZ_WarningOnce(classData.m_name, false, "%s %s 'AppearsInAddComponentMenu' uses legacy value 'true', should be 'AZ_CRC(\"Game\")'.",
classData.m_name, classData.m_typeId.ToString<AZStd::string>().c_str());
return legacyAppearsInComponentMenu;
}
}
}
}
}
return false;
}
inline bool AppearsInSystemComponentMenu(const AZ::SerializeContext::ClassData& classData)
{
return AppearsInAddComponentMenu(classData, AZ_CRC("System", 0xc94d118b));
}
inline bool AppearsInLayerComponentMenu(const AZ::SerializeContext::ClassData& classData)
{
return AppearsInAddComponentMenu(classData, AZ_CRC("Layer", 0xe4db211a));
}
inline bool AppearsInLevelComponentMenu(const AZ::SerializeContext::ClassData& classData)
{
return AppearsInAddComponentMenu(classData, AZ_CRC("Level", 0x9aeacc13));
}
inline bool AppearsInAnyComponentMenu(const AZ::SerializeContext::ClassData& classData)
{
return (AppearsInGameComponentMenu(classData) || AppearsInSystemComponentMenu(classData) || AppearsInLayerComponentMenu(classData) || AppearsInLevelComponentMenu(classData));
}
}
@@ -0,0 +1,37 @@
/*
* 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/EBus/EBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
namespace AzToolsFramework
{
//! Requests to be made of all EntityPropertyEditorRequests
//! Beware, there may be more than one EntityPropertyEditor that can respond
//! Broadcast should be used for accessing these functions
class EntityPropertyEditorRequests
: public AZ::EBusTraits
{
public:
//! Allows a component to get the list of selected entities or if in a pinned window, the list of entities in that window
//! \param selectedEntityIds the return vector holding the entities required
virtual void GetSelectedAndPinnedEntities(EntityIdList& selectedEntityIds) = 0;
//! Allows a component to get the list of selected entities
//! \param selectedEntityIds the return vector holding the entities required
virtual void GetSelectedEntities(EntityIdList& selectedEntityIds) = 0;
};
using EntityPropertyEditorRequestBus = AZ::EBus<EntityPropertyEditorRequests>;
} // namespace AzToolsFramework
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,53 @@
/*
* 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/TypeInfo.h>
#include <QRect>
#include <QKeySequence>
#include <QString>
namespace AzToolsFramework
{
struct ViewPaneOptions
{
AZ_TYPE_INFO(ViewPaneOptions, "{E9FB803A-2A47-4BCF-8A50-AB4C9D73AED2}");
QRect paneRect = QRect(50, 50, 1000, 800); ///< default size/position of the view pane, if no previous state is saved
Qt::DockWidgetArea preferedDockingArea = Qt::NoDockWidgetArea; ///< default docking area to place the view pane in, if no previous state is saved
bool isDeletable = true; ///< set to false if you want the view pane to hide on close, instead of being deleted
bool isStandard = false; ///< for internal use; leave set to false
bool showInMenu = true; ///< set to false if you'd like to register a view pane and have it NOT appear under the Tools menu
bool canHaveMultipleInstances = false; ///< ignored; left for backwards code compatibility
int viewportType = -1; ///< for internal use; leave set to -1
bool isPreview = false; ///< indicates if a view pane is still pre-release
QKeySequence shortcut; ///< default shortcut to allow the user to open the view pane
int builtInActionId = -1; ///< for internal use; leave set to -1
bool isDockable = true; ///< set to false if the view pane should not be dockable; this can be necessary in certain cases, such as with QOpenGLWidgets
QString optionalMenuText; ///< set this to the text you'd like to appear under the Tools menu; leave it blank to use the view pane name under the Tools menu instead
bool isLegacy = false; ///< set this to true if you are marking this as a legacy (and likely to be deprecated) viewpane
bool isLegacyReplacement = false; ///< set this to true if this is a viewpane to replace an older viewpane
QString saveKeyName; ///< can be zero length; set this if you want to use a name other than the viewpane name set in RegisterViewPane.
bool detachedWindow = false; ///< set to true if the view pane should use a detached, non-dockable widget. This is to workaround a problem with QOpenGLWidget on macOS. Currently this has no effect on other platforms.
bool isDisabledInSimMode = false; ///< set to true if the view pane should not be openable from level editor menu when editor is in simulation mode.
};
} // namespace AzToolsFramework
// Left in for backwards compatibility, so that any code forward declaring
// QtViewOptions will continue to work.
struct QtViewOptions : public AzToolsFramework::ViewPaneOptions
{
};
@@ -0,0 +1,60 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "Ticker.h"
namespace AzToolsFramework
{
Ticker::Ticker(QObject* parent /*= nullptr*/, float timeoutMS)
: QObject(parent)
, m_thread(nullptr)
, m_cancelled(false)
, m_timeoutMS(timeoutMS)
{
m_thread = azcreate(QThread, (parent));
}
Ticker::~Ticker()
{
Cancel();
}
void Ticker::Start()
{
moveToThread(m_thread);
QMetaObject::invokeMethod(this, "Loop", Qt::QueuedConnection);
m_thread->start();
m_cancelled = false;
}
void Ticker::Cancel()
{
if (!m_cancelled)
{
m_cancelled = true;
m_thread->quit();
m_thread->wait();
azdestroy(m_thread);
}
}
void Ticker::Loop()
{
if (!m_cancelled)
{
Q_EMIT Tick();
QTimer::singleShot(static_cast<int>(m_timeoutMS), Qt::PreciseTimer, this, &Ticker::Loop);
}
}
}
#include "Application/moc_Ticker.cpp"
@@ -0,0 +1,58 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if !defined(Q_MOC_RUN)
#include <QtCore/QObject>
#include <QtCore/QTimer>
#include <QtCore/QThread>
#include <AzCore/Memory/SystemAllocator.h>
#endif
#pragma once
namespace AzToolsFramework
{
//! Qt suppresses all timer events during modal dialogs, this object allows us to
//! emit a Tick event from which we can broadcast a SystemTick event.
class Ticker
: public QObject
{
public:
Q_OBJECT
public:
explicit Ticker(QObject* parent = nullptr, float timeoutMS = 10.f);
virtual ~Ticker();
//! Starts the ticking on a thread
void Start();
//! Cancels and destroys the ticking thread
void Cancel();
Q_SIGNALS:
//! Connect to this signal to handle the tick
void Tick();
private Q_SLOTS:
//! Single shot event that emits the Tick signal
void Loop();
private:
QThread* m_thread;
bool m_cancelled;
float m_timeoutMS;
};
}
@@ -0,0 +1,187 @@
/*
* 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.
*
*/
#ifndef AZTOOLSFRAMEWORK_TOOLSAPPLICATION_H
#define AZTOOLSFRAMEWORK_TOOLSAPPLICATION_H
#include <AzCore/base.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Asset/SimpleAsset.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Commands/PreemptiveUndoCache.h>
#pragma once
namespace AzToolsFramework
{
class ToolsApplication
: public AzFramework::Application
, public ToolsApplicationRequests::Bus::Handler
{
public:
AZ_RTTI(ToolsApplication, "{2895561E-BE90-4CC3-8370-DD46FCF74C01}", AzFramework::Application);
AZ_CLASS_ALLOCATOR(ToolsApplication, AZ::SystemAllocator, 0);
ToolsApplication(int* argc = nullptr, char*** argv = nullptr);
~ToolsApplication();
void Stop();
void CreateReflectionManager() override;
void Reflect(AZ::ReflectContext* context) override;
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
AzToolsFramework::ToolsApplicationRequests::ResolveToolPathOutcome ResolveConfigToolsPath(const char* toolApplicationName) const override;
//////////////////////////////////////////////////////////////////////////
// AzFramework::Application
void Start(const Descriptor& descriptor, const StartupParameters& startupParameters = StartupParameters()) override;
protected:
//////////////////////////////////////////////////////////////////////////
// AzFramework::Application
void StartCommon(AZ::Entity* systemEntity) override;
void CreateStaticModules(AZStd::vector<AZ::Module*>& outModules) override;
bool AddEntity(AZ::Entity* entity) override;
bool RemoveEntity(AZ::Entity* entity) override;
const char* GetCurrentConfigurationName() const override;
void SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AzFramework::ApplicationRequests::Bus overrides ...
void QueryApplicationType(AZ::ApplicationTypeQuery& appType) const override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// ToolsApplicationRequests::Bus::Handler
void PreExportEntity(AZ::Entity& source, AZ::Entity& target) override;
void PostExportEntity(AZ::Entity& source, AZ::Entity& target) override;
void MarkEntitySelected(AZ::EntityId entityId) override;
void MarkEntitiesSelected(const EntityIdList& entitiesToSelect) override;
void MarkEntityDeselected(AZ::EntityId entityId) override;
void MarkEntitiesDeselected(const EntityIdList& entitiesToDeselect) override;
void SetEntityHighlighted(AZ::EntityId entityId, bool highlighted) override;
void AddDirtyEntity(AZ::EntityId entityId) override;
int RemoveDirtyEntity(AZ::EntityId entityId) override;
bool IsDuringUndoRedo() override { return m_isDuringUndoRedo; }
void UndoPressed() override;
void RedoPressed() override;
void FlushUndo() override;
void FlushRedo() override;
UndoSystem::URSequencePoint* BeginUndoBatch(const char* label) override;
UndoSystem::URSequencePoint* ResumeUndoBatch(UndoSystem::URSequencePoint* token, const char* label) override;
void EndUndoBatch() override;
bool IsEntityEditable(AZ::EntityId entityId) override;
bool AreEntitiesEditable(const EntityIdList& entityIds) override;
void CheckoutPressed() override;
SourceControlFileInfo GetSceneSourceControlInfo() override;
bool AreAnyEntitiesSelected() override { return !m_selectedEntities.empty(); }
const EntityIdList& GetSelectedEntities() override { return m_selectedEntities; }
const EntityIdList& GetHighlightedEntities() override { return m_highlightedEntities; }
void SetSelectedEntities(const EntityIdList& selectedEntities) override;
bool IsSelectable(const AZ::EntityId& entityId) override;
bool IsSelected(const AZ::EntityId& entityId) override;
bool IsSliceRootEntity(const AZ::EntityId& entityId) override;
UndoSystem::UndoStack* GetUndoStack() override { return m_undoStack; }
UndoSystem::URSequencePoint* GetCurrentUndoBatch() override { return m_currentBatchUndo; }
PreemptiveUndoCache* GetUndoCache() override { return &m_undoCache; }
EntityIdSet GatherEntitiesAndAllDescendents(const EntityIdList& inputEntities) override;
AZ::EntityId CreateNewEntity(AZ::EntityId parentId = AZ::EntityId()) override;
AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& pos, AZ::EntityId parentId = AZ::EntityId()) override;
AZ::EntityId GetExistingEntity(AZ::u64 id) override;
void DeleteSelected() override;
void DeleteEntityById(AZ::EntityId entityId) override;
void DeleteEntities(const EntityIdList& entities) override;
void DeleteEntityAndAllDescendants(AZ::EntityId entityId) override;
void DeleteEntitiesAndAllDescendants(const EntityIdList& entities) override;
bool DetachEntities(const AZStd::vector<AZ::EntityId>& entitiesToDetach, AZStd::vector<AZStd::pair<AZ::EntityId, AZ::SliceComponent::EntityRestoreInfo>>& restoreInfos) override;
/**
* Detaches the supplied subslices from their owning slice instance
* @param subsliceRootList A list of SliceInstanceAddresses paired with a mapping from the sub slices asset entityId's to the owing slice instance's live entityIds
See SliceComponent::GetMappingBetweenSubsliceAndSourceInstanceEntityIds for a helper to acquire this mapping
* @param restoreInfos A list of EntityRestoreInfo's to be filled with information on how to restore the entities in the subslices back to their original state before this operation
* @return Returns true on operation success, false otherwise
*/
bool DetachSubsliceInstances(const AZ::SliceComponent::SliceInstanceEntityIdRemapList& subsliceRootList,
AZStd::vector<AZStd::pair<AZ::EntityId, AZ::SliceComponent::EntityRestoreInfo>>& restoreInfos) override;
bool FindCommonRoot(const EntityIdSet& entitiesToBeChecked, AZ::EntityId& commonRootEntityId, EntityIdList* topLevelEntities = nullptr) override;
bool FindCommonRootInactive(const EntityList& entitiesToBeChecked, AZ::EntityId& commonRootEntityId, EntityList* topLevelEntities = nullptr) override;
void FindTopLevelEntityIdsInactive(const EntityIdList& entityIdsToCheck, EntityIdList& topLevelEntityIds) override;
AZ::SliceComponent::SliceInstanceAddress FindCommonSliceInstanceAddress(const EntityIdList& entityIds) override;
AZ::EntityId GetRootEntityIdOfSliceInstance(AZ::SliceComponent::SliceInstanceAddress sliceAddress) override;
AZ::EntityId GetCurrentLevelEntityId() override;
bool RequestEditForFileBlocking(const char* assetPath, const char* progressMessage, const RequestEditProgressCallback& progressCallback) override;
bool CheckSourceControlConnectionAndRequestEditForFileBlocking(const char* assetPath, const char* progressMessage, const RequestEditProgressCallback& progressCallback) override;
void RequestEditForFile(const char* assetPath, RequestEditResultCallback resultCallback) override;
void CheckSourceControlConnectionAndRequestEditForFile(const char* assetPath, RequestEditResultCallback resultCallback) override;
void EnterEditorIsolationMode() override;
void ExitEditorIsolationMode() override;
bool IsEditorInIsolationMode() override;
const char* GetEngineRootPath() const override;
const char* GetEngineVersion() const override;
void CreateAndAddEntityFromComponentTags(const AZStd::vector<AZ::Crc32>& requiredTags, const char* entityName) override;
/* LUMBERYARD INTERNAL USE ONLY. */
void RunRedoSeparately(UndoSystem::URSequencePoint* redoCommand) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AzFramework::SimpleAssetRequests::Bus::Handler
struct PathAssetEntry
{
explicit PathAssetEntry(const char* path)
: m_path(path) {}
explicit PathAssetEntry(AZStd::string&& path)
: m_path(AZStd::move(path)) {}
AZStd::string m_path;
};
//////////////////////////////////////////////////////////////////////////
void CreateUndosForDirtyEntities();
void ConsistencyCheckUndoCache();
void InitializeEngineConfig();
AZ::Aabb m_selectionBounds;
EntityIdList m_selectedEntities;
EntityIdList m_highlightedEntities;
UndoSystem::UndoStack* m_undoStack;
UndoSystem::URSequencePoint* m_currentBatchUndo;
AZStd::unordered_set<AZ::EntityId> m_dirtyEntities;
PreemptiveUndoCache m_undoCache;
bool m_isDuringUndoRedo;
bool m_isInIsolationMode;
EntityIdSet m_isolatedEntityIdSet;
class EngineConfigImpl;
AZStd::unique_ptr<EngineConfigImpl> m_engineConfigImpl;
};
} // namespace AzToolsFramework
#endif // AZTOOLSFRAMEWORK_TOOLSAPPLICATION_H
@@ -0,0 +1,110 @@
/*
* 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/base.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Math/Uuid.h>
namespace AzToolsFramework
{
// use bind if you need additional context.
// Parameters:
// bool - If the archive command was successful or not.
typedef AZStd::function<void(bool)> ArchiveResponseCallback;
// bool - If the archive command was successful or not.
// AZStd::string - The console output from the command.
typedef AZStd::function<void(bool, AZStd::string)> ArchiveResponseOutputCallback;
//! ArchiveCommands
//! This bus handles messages relating to archive commands
//! archive commands are ASYNCHRONOUS
//! archive formats officially supported are .zip
//! do not block the main thread waiting for a response, it is not okay
//! you will not get a message delivered unless you tick the tickbus anyway!
class ArchiveCommands
: public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<ArchiveCommands>;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
typedef AZStd::recursive_mutex MutexType;
static const bool LocklessDispatch = true;
virtual ~ArchiveCommands() {}
//! Start an async task to extract an archive to the target directory
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
//! Multiple tasks can be associated with the same handle
virtual void ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback) = 0;
virtual void ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
// Maintaining backwards API compatibility - ExtractArchiveBlocking below passes in extractWithRoot as an option
virtual void ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
//! Start a sync task to extract an archive to the target directory
//! If you do not want to extract the root folder then set extractWithRootDirectory to false.
virtual bool ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory) = 0;
//! Extract a single file asynchronously from the archive to the destination.
//! Uses cwd if destinationPath empty. overWrite = true for overwrite existing files, false for skipExisting
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
//! Multiple tasks can be associated with the same handle
virtual void ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
//! Extract a single file from the archive to the destination and block until finished.
//! Uses cwd if destinationPath empty. overWrite = true for overwrite existing files, false for skipExisting
virtual bool ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite) = 0;
//! Start an async task to create an archive of the target directory (recursively)
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
//! Multiple tasks can be associated with the same handle.
virtual void CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
//! Start a sync task to create an archive of the target directory (recursively)
virtual bool CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive) = 0;
//! Start an async task to retrieve the list of files and their relative paths within an archive (recursively)
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
//! Multiple tasks can be associated with the same handle.
virtual void ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
//! Start a sync task to retrieve the list of files and their relative paths within an archive (recursively)
virtual bool ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries) = 0;
//! Start an async task to add a file to a preexisting archive.
//! fileToAdd must be a relative path to the file from the working directory. The path to the file from the root of the archive will be the same as the relative path to the file on disk.
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
//! Multiple tasks can be associated with the same handle.
virtual void AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
//! Start a sync task to add a file to a preexisting archive.
//! fileToAdd must be a relative path to the file from the working directory. The path to the file from the root of the archive will be the same as the relative path to the file on disk.
virtual bool AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd) = 0;
//! Start an async task to add files to a archive.
//! File paths inside the list file must either be a relative path from the working directory or an absolute path.
virtual void AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
//! Start a sync task to add files to an archive.
//! File paths inside the list file must either be a relative path from the working directory or an absolute path.
virtual bool AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath) = 0;
//! Cancels tasks associtated with the given handle. Blocks until all tasks are cancelled.
virtual void CancelTasks(AZ::Uuid taskHandle) = 0;
};
using ArchiveCommandsBus = AZ::EBus<ArchiveCommands>;
}; // namespace AzToolsFramework
@@ -0,0 +1,464 @@
/*
* 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 "ArchiveComponent.h"
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Process/ProcessCommunicator.h>
#include <AzToolsFramework/Process/ProcessWatcher.h>
#include <AzFramework/FileFunc/FileFunc.h>
namespace AzToolsFramework
{
// Forward declare platform specific functions
namespace Platform
{
AZStd::string GetZipExePath();
AZStd::string GetUnzipExePath();
AZStd::string GetCreateArchiveCommand(const AZStd::string& archivePath, const AZStd::string& dirToArchive);
AZStd::string GetExtractArchiveCommand(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool includeRoot);
AZStd::string GetAddFileToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& file);
AZStd::string GetAddFilesToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& listFilePath);
AZStd::string GetExtractFileCommand(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite);
AZStd::string GetListFilesInArchiveCommand(const AZStd::string& archivePath);
void ParseConsoleOutputFromListFilesInArchive(const AZStd::string& consoleOutput, AZStd::vector<AZStd::string>& fileEntries);
}
const char s_traceName[] = "ArchiveComponent";
const unsigned int g_sleepDuration = 1;
// Echoes all results of stdout and stderr to console and never blocks
class ConsoleEchoCommunicator
{
public:
ConsoleEchoCommunicator(AzToolsFramework::ProcessCommunicator* communicator)
: m_communicator(communicator)
{
}
~ConsoleEchoCommunicator()
{
}
// Call this periodically to drain the buffers
void Pump()
{
if (m_communicator->IsValid())
{
AZ::u32 readBufferSize = 0;
AZStd::string readBuffer;
// Don't call readOutput unless there is output or else it will block...
readBufferSize = m_communicator->PeekOutput();
if (readBufferSize)
{
readBuffer.resize_no_construct(readBufferSize + 1);
readBuffer[readBufferSize] = '\0';
m_communicator->ReadOutput(readBuffer.data(), readBufferSize);
EchoBuffer(readBuffer);
}
readBufferSize = m_communicator->PeekError();
if (readBufferSize)
{
readBuffer.resize_no_construct(readBufferSize + 1);
readBuffer[readBufferSize] = '\0';
m_communicator->ReadError(readBuffer.data(), readBufferSize);
EchoBuffer(readBuffer);
}
}
}
private:
void EchoBuffer(const AZStd::string& buffer)
{
size_t startIndex = 0;
size_t endIndex = 0;
const size_t bufferSize = buffer.size();
for (size_t i = 0; i < bufferSize; ++i)
{
if (buffer[i] == '\n' || buffer[i] == '\0')
{
endIndex = i;
bool isEmptyMessage = (endIndex - startIndex == 1) && (buffer[startIndex] == '\r');
if (!isEmptyMessage)
{
AZ_Printf(s_traceName, "%s", buffer.substr(startIndex, endIndex - startIndex).c_str());
}
startIndex = endIndex + 1;
}
}
}
AzToolsFramework::ProcessCommunicator* m_communicator = nullptr;
};
void ArchiveComponent::Activate()
{
m_zipExePath = Platform::GetZipExePath();
m_unzipExePath = Platform::GetUnzipExePath();
ArchiveCommands::Bus::Handler::BusConnect();
}
void ArchiveComponent::Deactivate()
{
ArchiveCommands::Bus::Handler::BusDisconnect();
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
for (auto pair : m_threadInfoMap)
{
ThreadInfo& info = pair.second;
info.shouldStop = true;
m_cv.wait(lock, [&info]() {
return info.threads.size() == 0;
});
}
m_threadInfoMap.clear();
}
void ArchiveComponent::Reflect(AZ::ReflectContext * context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ArchiveComponent, AZ::Component>()
->Version(2)
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AZ_CRC("AssetBuilder", 0xc739c7d7) }))
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<ArchiveComponent>(
"Archive", "Handles creation and extraction of zip archives.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Editor")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
;
}
}
}
void ArchiveComponent::CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
{
AZStd::string commandLineArgs = AZStd::string::format(R"(a -tzip -mx=1 "%s" -r "%s\*")", archivePath.c_str(), dirToArchive.c_str());
LaunchZipExe(m_zipExePath, commandLineArgs, respCallback, taskHandle);
}
bool ArchiveComponent::CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive)
{
bool success = false;
auto createArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
success = result;
};
AZStd::string commandLineArgs = Platform::GetCreateArchiveCommand(archivePath, dirToArchive);
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return false;
}
LaunchZipExe(m_zipExePath, commandLineArgs, createArchiveCallback, AZ::Uuid::CreateNull(), dirToArchive, false);
return success;
}
void ArchiveComponent::ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback)
{
ArchiveResponseOutputCallback responseHandler = [respCallback](bool result, AZStd::string /*outputStr*/) { respCallback(result); };
ExtractArchiveOutput(archivePath, destinationPath, taskHandle, responseHandler);
}
void ArchiveComponent::ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
{
AZStd::string commandLineArgs = Platform::GetExtractArchiveCommand(archivePath, destinationPath, true);
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return;
}
LaunchZipExe(m_unzipExePath, commandLineArgs, respCallback, taskHandle);
}
void ArchiveComponent::ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
{
AZStd::string commandLineArgs = Platform::GetExtractArchiveCommand(archivePath, destinationPath, false);
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return;
}
LaunchZipExe(m_unzipExePath, commandLineArgs, respCallback, taskHandle);
}
void ArchiveComponent::ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
{
AZStd::string commandLineArgs = AzToolsFramework::Platform::GetExtractFileCommand(archivePath, fileInArchive, destinationPath, overWrite);
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return;
}
LaunchZipExe(m_unzipExePath, commandLineArgs, respCallback, taskHandle);
}
bool ArchiveComponent::ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite)
{
AZStd::string commandLineArgs = AzToolsFramework::Platform::GetExtractFileCommand(archivePath, fileInArchive, destinationPath, overWrite);
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return false;
}
bool success = false;
auto createArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
success = result;
};
LaunchZipExe(m_unzipExePath, commandLineArgs, createArchiveCallback);
return success;
}
void ArchiveComponent::ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
{
AZStd::string commandLineArgs = Platform::GetListFilesInArchiveCommand(archivePath);
auto parseOutput = [respCallback, taskHandle, &fileEntries](bool exitCode, AZStd::string consoleOutput)
{
Platform::ParseConsoleOutputFromListFilesInArchive(consoleOutput, fileEntries);
AZ::TickBus::QueueFunction(respCallback, exitCode, AZStd::move(consoleOutput));
};
LaunchZipExe(m_unzipExePath, commandLineArgs, parseOutput, taskHandle, "", true);
}
bool ArchiveComponent::ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries)
{
AZStd::string listOutput;
AZStd::string commandLineArgs = Platform::GetListFilesInArchiveCommand(archivePath.c_str());
bool success = false;
auto parseOutput = [&success, &fileEntries](bool result, AZStd::string consoleOutput)
{
Platform::ParseConsoleOutputFromListFilesInArchive(consoleOutput, fileEntries);
success = result;
};
LaunchZipExe(m_unzipExePath, commandLineArgs, parseOutput, AZ::Uuid::CreateNull(), "", true);
return success;
}
void ArchiveComponent::AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
{
AZStd::string commandLineArgs = Platform::GetAddFileToArchiveCommand(archivePath, fileToAdd);
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return;
}
LaunchZipExe(m_zipExePath, commandLineArgs, respCallback, taskHandle, workingDirectory);
}
bool ArchiveComponent::AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd)
{
AZStd::string commandLineArgs = Platform::GetAddFileToArchiveCommand(archivePath, fileToAdd);
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return false;
}
bool success = false;
auto addFileToArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
success = result;
};
LaunchZipExe(m_zipExePath, commandLineArgs, addFileToArchiveCallback, AZ::Uuid::CreateNull(), workingDirectory);
return success;
}
bool ArchiveComponent::AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath)
{
bool success = false;
auto addFileToArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
success = result;
};
AZStd::string commandLineArgs = Platform::GetAddFilesToArchiveCommand(archivePath.c_str(), listFilePath.c_str());
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return false;
}
LaunchZipExe(m_zipExePath, commandLineArgs, addFileToArchiveCallback, AZ::Uuid::CreateNull(), workingDirectory);
return success;
}
void ArchiveComponent::AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
{
AZStd::string commandLineArgs = Platform::GetAddFilesToArchiveCommand(archivePath, listFilePath);
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return;
}
LaunchZipExe(m_zipExePath, commandLineArgs, respCallback, taskHandle, workingDirectory);
}
bool ArchiveComponent::ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory)
{
AZStd::string commandLineArgs = Platform::GetExtractArchiveCommand(archivePath, destinationPath, extractWithRootDirectory);
if (commandLineArgs.empty())
{
// The platform-specific implementation has already thrown its own error, no need to throw another one
return false;
}
bool success = false;
auto extractArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
success = result;
};
LaunchZipExe(m_unzipExePath, commandLineArgs, extractArchiveCallback);
return success;
}
void ArchiveComponent::CancelTasks(AZ::Uuid taskHandle)
{
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
auto it = m_threadInfoMap.find(taskHandle);
if (it == m_threadInfoMap.end())
{
return;
}
ThreadInfo& info = it->second;
info.shouldStop = true;
m_cv.wait(lock, [&info]() {
return info.threads.size() == 0;
});
m_threadInfoMap.erase(it);
}
void ArchiveComponent::LaunchZipExe(const AZStd::string& exePath, const AZStd::string& commandLineArgs, const ArchiveResponseOutputCallback& respCallback, AZ::Uuid taskHandle, const AZStd::string& workingDir, bool captureOutput)
{
auto sevenZJob = [=]()
{
if (!taskHandle.IsNull())
{
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
m_threadInfoMap[taskHandle].threads.insert(AZStd::this_thread::get_id());
m_cv.notify_all();
}
ProcessLauncher::ProcessLaunchInfo info;
info.m_commandlineParameters = exePath + " " + commandLineArgs;
info.m_showWindow = false;
if (!workingDir.empty())
{
info.m_workingDirectory = workingDir;
}
AZStd::unique_ptr<ProcessWatcher> watcher(ProcessWatcher::LaunchProcess(info, ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT));
AZStd::string consoleOutput;
AZ::u32 exitCode = static_cast<AZ::u32>(SevenZipExitCode::UserStoppedProcess);
if (watcher)
{
// callback requires output captured from 7z
if (captureOutput)
{
AZStd::string consoleBuffer;
while (watcher->IsProcessRunning(&exitCode))
{
if (!taskHandle.IsNull())
{
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
if (m_threadInfoMap[taskHandle].shouldStop)
{
watcher->TerminateProcess(static_cast<AZ::u32>(SevenZipExitCode::UserStoppedProcess));
}
}
watcher->WaitForProcessToExit(g_sleepDuration, &exitCode);
AZ::u32 outputSize = watcher->GetCommunicator()->PeekOutput();
if (outputSize)
{
consoleBuffer.resize(outputSize);
watcher->GetCommunicator()->ReadOutput(consoleBuffer.data(), outputSize);
consoleOutput += consoleBuffer;
}
}
}
else
{
ConsoleEchoCommunicator echoCommunicator(watcher->GetCommunicator());
while (watcher->IsProcessRunning(&exitCode))
{
if (!taskHandle.IsNull())
{
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
if (m_threadInfoMap[taskHandle].shouldStop)
{
watcher->TerminateProcess(static_cast<AZ::u32>(SevenZipExitCode::UserStoppedProcess));
}
}
watcher->WaitForProcessToExit(g_sleepDuration, &exitCode);
echoCommunicator.Pump();
}
}
}
if (taskHandle.IsNull())
{
respCallback(exitCode == static_cast<AZ::u32>(SevenZipExitCode::NoError), AZStd::move(consoleOutput));
}
else
{
AZ::TickBus::QueueFunction(respCallback, (exitCode == static_cast<AZ::u32>(SevenZipExitCode::NoError)), AZStd::move(consoleOutput));
}
if (!taskHandle.IsNull())
{
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
ThreadInfo& tInfo = m_threadInfoMap[taskHandle];
tInfo.threads.erase(AZStd::this_thread::get_id());
m_cv.notify_all();
}
};
if (!taskHandle.IsNull())
{
AZStd::thread processThread(sevenZJob);
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
ThreadInfo& info = m_threadInfoMap[taskHandle];
m_cv.wait(lock, [&info, &processThread]() {
return info.threads.find(processThread.get_id()) != info.threads.end();
});
processThread.detach();
}
else
{
sevenZJob();
}
}
} // namespace AzToolsFramework
@@ -0,0 +1,93 @@
/*
* 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/base.h>
#include <AzCore/Component/Component.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/parallel/conditional_variable.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzToolsFramework/Archive/ArchiveAPI.h>
namespace AzToolsFramework
{
enum class SevenZipExitCode : AZ::u32
{
NoError = 0,
Warning = 1,
FatalError = 2,
CommandLineError = 7,
NotEnoughMemory = 8,
UserStoppedProcess = 255
};
// the ArchiveComponent's job is to execute zip commands.
// it parses the status of zip commands and returns results.
class ArchiveComponent
: public AZ::Component
, private ArchiveCommands::Bus::Handler
{
public:
AZ_COMPONENT(ArchiveComponent, "{A19EEA33-3736-447F-ACF7-DAA4B6A179AA}")
ArchiveComponent() = default;
~ArchiveComponent() override = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component overrides
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
private:
static void Reflect(AZ::ReflectContext* context);
//////////////////////////////////////////////////////////////////////////
// ArchiveCommands::Bus::Handler overrides
void CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
bool CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive) override;
bool ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory) override;
void ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback) override;
void ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
void ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
void ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
bool ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite) override;
void ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
bool ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries) override;
void AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
bool AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd) override;
bool AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath) override;
void AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
void CancelTasks(AZ::Uuid taskHandle) override;
//////////////////////////////////////////////////////////////////////////
// Launches the input zip exe as a background child process in a detached background thread, if the task handle is not null
// otherwise launches input zip exe in the calling thread.
void LaunchZipExe(const AZStd::string& exePath, const AZStd::string& commandLineArgs, const ArchiveResponseOutputCallback& respCallback, AZ::Uuid taskHandle = AZ::Uuid::CreateNull(), const AZStd::string& workingDir = "", bool captureOutput = false);
AZStd::string m_zipExePath;
AZStd::string m_unzipExePath;
// Struct for tracking background threads/tasks
struct ThreadInfo
{
bool shouldStop = false;
AZStd::set<AZStd::thread::id> threads;
};
AZStd::mutex m_threadControlMutex; // Guards m_threadInfoMap
AZStd::condition_variable m_cv;
AZStd::unordered_map<AZ::Uuid, ThreadInfo> m_threadInfoMap;
};
} // namespace AzToolsFramework
@@ -0,0 +1,119 @@
/*
* 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 "NullArchiveComponent.h"
#include <AzCore/Component/TickBus.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzToolsFramework
{
void NullArchiveComponent::Activate()
{
ArchiveCommands::Bus::Handler::BusConnect();
}
void NullArchiveComponent::Deactivate()
{
ArchiveCommands::Bus::Handler::BusDisconnect();
}
bool NullArchiveComponent::ExtractArchiveBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*destinationPath*/, bool /*extractWithRootDirectory*/)
{
return false;
}
void NullArchiveComponent::ExtractArchive(const AZStd::string& /*archivePath*/, const AZStd::string& /*destinationPath*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseCallback& respCallback)
{
AZ::TickBus::QueueFunction(respCallback, false);
}
void NullArchiveComponent::ExtractArchiveOutput(const AZStd::string& /*archivePath*/, const AZStd::string& /*destinationPath*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
{
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
}
void NullArchiveComponent::ExtractArchiveWithoutRoot(const AZStd::string& /*archivePath*/, const AZStd::string& /*destinationPath*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
{
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
}
void NullArchiveComponent::ExtractFile(const AZStd::string& /*archivePath*/, const AZStd::string& /*fileInArchive*/, const AZStd::string& /*destinationPath*/, bool /*overWrite*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
{
// Always report we failed to extract
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
}
bool NullArchiveComponent::ExtractFileBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*fileInArchive*/, const AZStd::string& /*destinationPath*/, bool /*overWrite*/)
{
return false;
}
void NullArchiveComponent::ListFilesInArchive(const AZStd::string& /*archivePath*/, AZStd::vector<AZStd::string>& /*consoleOutput*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
{
// Always report we failed to extract
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
}
bool NullArchiveComponent::ListFilesInArchiveBlocking(const AZStd::string& /*archivePath*/, AZStd::vector<AZStd::string>& /*consoleOutput*/)
{
return false;
}
void NullArchiveComponent::AddFileToArchive(const AZStd::string& /*archivePath*/, const AZStd::string& /*fileToAdd*/, const AZStd::string& /*pathInArchive*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
{
// Always report we failed to extract
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
}
bool NullArchiveComponent::AddFileToArchiveBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*fileToAdd*/, const AZStd::string& /*pathInArchive*/)
{
return false;
}
bool NullArchiveComponent::AddFilesToArchiveBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*workingDirectory*/, const AZStd::string& /*listFilePath*/)
{
return false;
}
void NullArchiveComponent::AddFilesToArchive(const AZStd::string& /*archivePath*/, const AZStd::string& /*workingDirectory*/, const AZStd::string& /*listFilePath*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
{
// Always report we failed to extract
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
}
void NullArchiveComponent::CreateArchive(const AZStd::string& /*archivePath*/, const AZStd::string& /*dirToArchive*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
{
// Always report we failed to extract
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
}
bool NullArchiveComponent::CreateArchiveBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*dirToArchive*/)
{
return false;
}
void NullArchiveComponent::CancelTasks(AZ::Uuid /*taskHandle*/)
{
}
void NullArchiveComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<NullArchiveComponent, AZ::Component>()
;
}
}
} // namespace AzToolsFramework
@@ -0,0 +1,58 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzToolsFramework/Archive/ArchiveAPI.h>
namespace AzToolsFramework
{
class NullArchiveComponent
: public AZ::Component
, private ArchiveCommands::Bus::Handler
{
public:
AZ_COMPONENT(NullArchiveComponent, "{D665B6B1-5FF4-4203-B19F-BBDB82587129}")
NullArchiveComponent() = default;
~NullArchiveComponent() override = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component overrides
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
private:
static void Reflect(AZ::ReflectContext* context);
//////////////////////////////////////////////////////////////////////////
// ArchiveCommands::Bus::Handler overrides
// ArchiveCommands::Bus::Handler overrides
void CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
bool CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive) override;
bool ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory) override;
void ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback) override;
void ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
void ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
void ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
bool ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite) override;
void ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& consoleOutput, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
bool ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& consoleOutput) override;
void AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& fileToAdd, const AZStd::string& pathInArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
bool AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& fileToAdd, const AZStd::string& pathInArchive) override;
bool AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath) override;
void AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
void CancelTasks(AZ::Uuid taskHandle) override;
//////////////////////////////////////////////////////////////////////////
};
} // namespace AzToolsFramework
@@ -0,0 +1,899 @@
/*
* 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/Asset/AssetBundler.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/hash.h>
#include <AzCore/std/string/wildcard.h>
#include <AzCore/std/string/regex.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/IO/FileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
namespace
{
//! These struct are used because we have a use case where we only need to hash and
//! override the equals to method for AssetFileInfo class based only on AssetId, which might not be true for other usages of AssetFileInfo class.
struct AssetFileInfoComparatorByAssetId
{
bool operator()(const AzToolsFramework::AssetFileInfo& left, const AzToolsFramework::AssetFileInfo& right) const
{
return left.m_assetId == right.m_assetId;
}
};
struct AssetFileInfoHasherByAssetId
{
size_t operator() (const AzToolsFramework::AssetFileInfo& assetFileInfo) const
{
size_t hashValue = 0;
AZStd::hash_combine(hashValue, assetFileInfo.m_assetId);
return hashValue;
}
};
}
namespace AzToolsFramework
{
const char AssetBundleSettingsFileExtension[] = "bundlesettings";
const char BundleFileExtension[] = "pak";
const char ComparisonRulesFileExtension[] = "rules";
const char ErrorWindowName[] = "AssetBundler";
const char* AssetFileInfoListComparison::ComparisonTypeNames[] = { "delta", "union", "intersection", "complement", "filepattern", "intersectioncount" };
const char* AssetFileInfoListComparison::FilePatternTypeNames[] = { "wildcard", "regex" };
const char DefaultTypeName[] = "default";
const char TokenIdentifier = '$';
void AssetBundleSettings::Reflect(AZ::ReflectContext * context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AssetBundleSettings>()
->Version(3)
->Field("AssetFileInfoListPath", &AssetBundleSettings::m_assetFileInfoListPath)
->Field("BundleFilePath", &AssetBundleSettings::m_bundleFilePath)
->Field("BundleVersion", &AssetBundleSettings::m_bundleVersion)
->Field("maxBundleSize", &AssetBundleSettings::m_maxBundleSizeInMB)
->Field("comment", &AssetBundleSettings::m_comment);
}
}
AZ::Outcome<AssetBundleSettings, AZStd::string> AssetBundleSettings::Load(const AZStd::string& filePath)
{
auto fileExtensionOutcome = ValidateBundleSettingsFileExtension(filePath);
if (!fileExtensionOutcome.IsSuccess())
{
return AZ::Failure(fileExtensionOutcome.GetError());
}
AssetBundleSettings assetBundleSettings;
if (!AZ::Utils::LoadObjectFromFileInPlace(filePath.c_str(), assetBundleSettings))
{
return AZ::Failure(AZStd::string::format("Failed to load AssetBundleSettings file (%s) from disk.\n", filePath.c_str()));
}
assetBundleSettings.m_platform = GetPlatformFromAssetInfoFilePath(assetBundleSettings);
return AZ::Success(assetBundleSettings);
}
bool AssetBundleSettings::Save(const AssetBundleSettings& assetBundleSettings, const AZStd::string& destinationFilePath)
{
auto fileExtensionOutcome = ValidateBundleSettingsFileExtension(destinationFilePath);
if (!fileExtensionOutcome.IsSuccess())
{
AZ_Error(ErrorWindowName, false, fileExtensionOutcome.GetError().c_str());
return false;
}
if (AZ::IO::FileIOBase::GetInstance()->Exists(destinationFilePath.c_str()) && AZ::IO::FileIOBase::GetInstance()->IsReadOnly(destinationFilePath.c_str()))
{
AZ_Error(ErrorWindowName, false, "Unable to save bundle settings file (%s): file is marked Read-Only.\n", destinationFilePath.c_str());
return false;
}
if (!IsBundleSettingsFile(destinationFilePath))
{
AZ_Error(ErrorWindowName, false, "Failed to save file (%s) to disk. AssetBundleSettings files must have the extension: %s\n", destinationFilePath.c_str(), AssetBundleSettingsFileExtension);
return false;
}
if (AZ::Utils::SaveObjectToFile(destinationFilePath.c_str(), AZ::DataStream::StreamType::ST_XML, &assetBundleSettings))
{
return true;
}
AZ_Error(ErrorWindowName, false, "Failed to save file (%s) to disk.", destinationFilePath.c_str());
return false;
}
bool AssetBundleSettings::IsBundleSettingsFile(const AZStd::string& filePath)
{
return AzFramework::StringFunc::EndsWith(filePath, AssetBundleSettingsFileExtension);
}
const char* AssetBundleSettings::GetBundleSettingsFileExtension()
{
return AssetBundleSettingsFileExtension;
}
AZ::Outcome<void, AZStd::string> AssetBundleSettings::ValidateBundleSettingsFileExtension(const AZStd::string& path)
{
if (!AzFramework::StringFunc::EndsWith(path, AssetBundleSettingsFileExtension))
{
return AZ::Failure(AZStd::string::format(
"Invalid Bundle Settings file path ( %s ). Invalid file extension, Bundle Settings files can only have ( .%s ) extension.\n",
path.c_str(),
AssetBundleSettingsFileExtension));
}
return AZ::Success();
}
const char* AssetBundleSettings::GetBundleFileExtension()
{
return BundleFileExtension;
}
AZ::Outcome<void, AZStd::string> AssetBundleSettings::ValidateBundleFileExtension(const AZStd::string& path)
{
if (!AzFramework::StringFunc::EndsWith(path, BundleFileExtension))
{
return AZ::Failure(AZStd::string::format(
"Invalid Bundle file path ( %s ). Invalid file extension, Bundles can only have ( .%s ) extension.\n",
path.c_str(),
BundleFileExtension));
}
return AZ::Success();
}
AZ::u64 AssetBundleSettings::GetMaxBundleSizeInMB()
{
return MaxBundleSizeInMB;
}
AZStd::string AssetBundleSettings::GetPlatformFromAssetInfoFilePath(const AssetBundleSettings& assetBundleSettings)
{
return GetPlatformIdentifier(assetBundleSettings.m_assetFileInfoListPath);
}
bool AssetFileInfoListComparison::IsOutputPath(const AZStd::string& filePath)
{
return !filePath.empty() && !IsTokenFile(filePath);
}
bool AssetFileInfoListComparison::IsTokenFile(const AZStd::string& filePath)
{
return !filePath.empty() && filePath[0] == TokenIdentifier;
}
void AssetFileInfoListComparison::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
ComparisonData::Reflect(serializeContext);
serializeContext->Class<AssetFileInfoListComparison>()
->Version(1)
->Field("ComparisonDataListType", &AssetFileInfoListComparison::m_comparisonDataList);
}
}
bool AssetFileInfoListComparison::Save(const AZStd::string& destinationFilePath) const
{
if (!AzFramework::StringFunc::EndsWith(destinationFilePath, ComparisonRulesFileExtension))
{
AZ_Error(ErrorWindowName, false, "Unable to save comparison file (%s). Invalid file extension, comparison files can only have (.%s) extension.\n", destinationFilePath.c_str(), ComparisonRulesFileExtension);
return false;
}
if (AZ::IO::FileIOBase::GetInstance()->Exists(destinationFilePath.c_str()) && AZ::IO::FileIOBase::GetInstance()->IsReadOnly(destinationFilePath.c_str()))
{
AZ_Error(ErrorWindowName, false, "Unable to save comparison file (%s): file is marked Read-Only.\n", destinationFilePath.c_str());
return false;
}
return AZ::Utils::SaveObjectToFile(destinationFilePath, AZ::DataStream::StreamType::ST_XML, this);
}
AZ::Outcome<AssetFileInfoListComparison, AZStd::string> AssetFileInfoListComparison::Load(const AZStd::string& filePath)
{
if (!AzFramework::StringFunc::EndsWith(filePath, ComparisonRulesFileExtension))
{
return AZ::Failure(AZStd::string::format("Unable to load comparison file (%s). Invalid file extension, comparison files can only have (.%s) extension.\n", filePath.c_str(), ComparisonRulesFileExtension));
}
AssetFileInfoListComparison assetFileInfoListComparison;
if (!AZ::Utils::LoadObjectFromFileInPlace(filePath.c_str(), assetFileInfoListComparison))
{
return AZ::Failure(AZStd::string::format("Failed to load AssetFileInfoComparison file (%s) from disk.\n", filePath.c_str()));
}
return AZ::Success(AZStd::move(assetFileInfoListComparison));
}
AZ::Outcome<void, AZStd::string> AssetFileInfoListComparison::CompareAndSaveResults(const AZStd::vector<AZStd::string>& intersectionCountAssetListFiles)
{
AZ::Outcome<AssetFileInfoList, AZStd::string> result = Compare(intersectionCountAssetListFiles);
if (!result.IsSuccess())
{
return AZ::Failure(result.TakeError());
}
return SaveResults();
}
AZ::Outcome<void, AZStd::string> AssetFileInfoListComparison::SaveResults() const
{
for (auto iter = m_assetFileInfoMap.begin(); iter != m_assetFileInfoMap.end(); iter++)
{
if (IsOutputPath(iter->first))
{
if (!AssetFileInfoList::Save(iter->second, iter->first))
{
return AZ::Failure(AZStd::string::format("Failed to save result of comparison operation for file %s.\n", iter->first.c_str()));
}
}
}
return AZ::Success();
}
AZStd::vector<AZStd::string> AssetFileInfoListComparison::GetDestructiveOverwriteFilePaths()
{
AZStd::vector<AZStd::string> existingPaths;
for (auto iter = m_assetFileInfoMap.begin(); iter != m_assetFileInfoMap.end(); iter++)
{
if (IsOutputPath(iter->first) && AZ::IO::FileIOBase::GetInstance()->Exists(iter->first.c_str()))
{
existingPaths.emplace_back(iter->first);
}
}
return existingPaths;
}
AssetFileInfoList AssetFileInfoListComparison::GetComparisonResults(const AZStd::string& comparisonKey)
{
auto comparisonResults = m_assetFileInfoMap.find(comparisonKey);
if (comparisonResults == m_assetFileInfoMap.end())
{
return AssetFileInfoList();
}
return comparisonResults->second;
}
const char* AssetFileInfoListComparison::GetComparisonRulesFileExtension()
{
return ComparisonRulesFileExtension;
}
const char* AssetFileInfoListComparison::GetComparisonTypeName(ComparisonType comparisonType)
{
if (comparisonType == ComparisonType::Default)
{
return DefaultTypeName;
}
return ComparisonTypeNames[aznumeric_cast<AZ::u8>(comparisonType)];
}
const char* AssetFileInfoListComparison::GetFilePatternTypeName(FilePatternType filePatternType)
{
if (filePatternType == FilePatternType::Default)
{
return DefaultTypeName;
}
return FilePatternTypeNames[aznumeric_cast<AZ::u8>(filePatternType)];
}
const char AssetFileInfoListComparison::GetTokenIdentifier()
{
return TokenIdentifier;
}
AZ::Outcome<AssetFileInfoList, AZStd::string> AssetFileInfoListComparison::PopulateAssetFileInfo(const AZStd::string& assetFileInfoPath) const
{
AssetFileInfoList assetFileInfoList;
if (assetFileInfoPath.empty())
{
return AZ::Failure(AZStd::string::format("File path for the first asset file info list is empty.\n"));
}
if (IsOutputPath(assetFileInfoPath))
{
if (!AZ::IO::FileIOBase::GetInstance()->Exists(assetFileInfoPath.c_str()))
{
return AZ::Failure(AZStd::string::format("File ( %s ) does not exists on disk.\n", assetFileInfoPath.c_str()));
}
if (!AZ::Utils::LoadObjectFromFileInPlace(assetFileInfoPath.c_str(), assetFileInfoList))
{
return AZ::Failure(AZStd::string::format("Failed to deserialize file ( %s ).\n", assetFileInfoPath.c_str()));
}
}
else
{
auto found = m_assetFileInfoMap.find(assetFileInfoPath);
if (found != m_assetFileInfoMap.end())
{
assetFileInfoList = found->second;
}
else
{
return AZ::Failure(AZStd::string::format("Failed to find AssetFileInfoList that matches the TAG ( %s ).\n", assetFileInfoPath.c_str()));
}
}
if (!assetFileInfoList.m_fileInfoList.size())
{
return AZ::Failure(AZStd::string::format("File ( %s ) does not contain any assets.\n", assetFileInfoPath.c_str()));
}
return AZ::Success(assetFileInfoList);
}
AZ::Outcome<AssetFileInfoList, AZStd::string> AssetFileInfoListComparison::Compare(const AZStd::vector<AZStd::string>& intersectionCountAssetListFiles)
{
AssetFileInfoList lastAssetFileInfoList;
AZ::Outcome<AssetFileInfoList, AZStd::string> result = AZ::Failure(AZStd::string());
if (m_comparisonDataList.empty())
{
return AZ::Failure(AZStd::string("Comparison failed: no Comparison Steps were provided."));
}
//IntersectionCount Operation cannot be combined with other compare operations
if (m_comparisonDataList[0].m_comparisonType == ComparisonType::IntersectionCount)
{
result = IntersectionCount(intersectionCountAssetListFiles);
if (result.IsSuccess())
{
lastAssetFileInfoList = result.GetValue();
m_assetFileInfoMap[m_comparisonDataList[0].m_output] = result.TakeValue();
}
return AZ::Success(lastAssetFileInfoList);
}
for (const ComparisonData& comparisonStep : m_comparisonDataList)
{
AZ::Outcome<AssetFileInfoList, AZStd::string> assetListOutcome = PopulateAssetFileInfo(comparisonStep.m_firstInput);
if (!assetListOutcome.IsSuccess())
{
return AZ::Failure(assetListOutcome.TakeError());
}
AssetFileInfoList firstAssetList = assetListOutcome.TakeValue();
AssetFileInfoList secondAssetList;
if (comparisonStep.m_comparisonType != ComparisonType::FilePattern)
{
assetListOutcome = PopulateAssetFileInfo(comparisonStep.m_secondInput);
if (!assetListOutcome.IsSuccess())
{
return AZ::Failure(assetListOutcome.TakeError());
}
secondAssetList = assetListOutcome.TakeValue();
}
switch (comparisonStep.m_comparisonType)
{
case ComparisonType::Delta:
{
result = Delta(firstAssetList, secondAssetList);
break;
}
case ComparisonType::Union:
{
result = Union(firstAssetList, secondAssetList);
break;
}
case ComparisonType::Intersection:
{
result = Intersection(firstAssetList, secondAssetList);
break;
}
case ComparisonType::Complement:
{
result = Complement(firstAssetList, secondAssetList);
break;
}
case ComparisonType::FilePattern:
{
result = FilePattern(firstAssetList, comparisonStep);
break;
}
default:
return AZ::Failure(AZStd::string::format("Invalid comparison type ( %s ) specified.\n", ComparisonTypeNames[static_cast<int>(comparisonStep.m_comparisonType)]));
}
if (!result.IsSuccess())
{
return result;
}
lastAssetFileInfoList = result.GetValue();
m_assetFileInfoMap[comparisonStep.m_output] = result.TakeValue();
}
return AZ::Success(lastAssetFileInfoList);
}
AssetFileInfoList AssetFileInfoListComparison::Delta(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const
{
AZStd::unordered_map<AZ::Data::AssetId, AzToolsFramework::AssetFileInfo> assetIdToAssetFileInfoMap;
// Populate the map with entries from the secondAssetFileInfoList
for (const AssetFileInfo& assetFileInfo : secondAssetFileInfoList.m_fileInfoList)
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
auto found = assetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId);
if (found != assetIdToAssetFileInfoMap.end())
{
bool isHashEqual = true;
// checking the file hash
for (int idx = 0; idx < AzToolsFramework::AssetFileInfo::s_arraySize; idx++)
{
if (found->second.m_hash[idx] != assetFileInfo.m_hash[idx])
{
isHashEqual = false;
break;
}
}
if (isHashEqual)
{
assetIdToAssetFileInfoMap.erase(found);
}
}
}
AssetFileInfoList assetFileInfoList;
for (auto iter = assetIdToAssetFileInfoMap.begin(); iter != assetIdToAssetFileInfoMap.end(); iter++)
{
assetFileInfoList.m_fileInfoList.emplace_back(AZStd::move(iter->second));
}
return assetFileInfoList;
}
AssetFileInfoList AssetFileInfoListComparison::Union(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const
{
AZStd::unordered_map<AZ::Data::AssetId, AzToolsFramework::AssetFileInfo> assetIdToAssetFileInfoMap;
// Populate the map with entries from the secondAssetFileInfoList
for (const AssetFileInfo& assetFileInfo : secondAssetFileInfoList.m_fileInfoList)
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
auto found = assetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId);
if (found == assetIdToAssetFileInfoMap.end())
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
}
//populate AssetFileInfoList from map
AssetFileInfoList assetFileInfoList;
for (auto iter = assetIdToAssetFileInfoMap.begin(); iter != assetIdToAssetFileInfoMap.end(); iter++)
{
assetFileInfoList.m_fileInfoList.emplace_back(AZStd::move(iter->second));
}
return assetFileInfoList;
}
AssetFileInfoList AssetFileInfoListComparison::Intersection(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const
{
AZStd::unordered_map<AZ::Data::AssetId, AzToolsFramework::AssetFileInfo> assetIdToAssetFileInfoMap;
// Populate the map with entries from the secondAssetFileInfoList
for (const AssetFileInfo& assetFileInfo : secondAssetFileInfoList.m_fileInfoList)
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
AssetFileInfoList assetFileInfoList;
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
auto found = assetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId);
if (found != assetIdToAssetFileInfoMap.end())
{
assetFileInfoList.m_fileInfoList.emplace_back(AZStd::move(found->second));
}
}
return assetFileInfoList;
}
AssetFileInfoList AssetFileInfoListComparison::Complement(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const
{
AZStd::unordered_map<AZ::Data::AssetId, AzToolsFramework::AssetFileInfo> assetIdToAssetFileInfoMap;
// Populate the map with entries from the firstAssetFileInfoList
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
assetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = assetFileInfo;
}
AssetFileInfoList assetFileInfoList;
for (const AssetFileInfo& assetFileInfo : secondAssetFileInfoList.m_fileInfoList)
{
auto found = assetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId);
if (found == assetIdToAssetFileInfoMap.end())
{
assetFileInfoList.m_fileInfoList.emplace_back(AZStd::move(assetFileInfo));
}
}
return assetFileInfoList;
}
AZ::Outcome<AssetFileInfoList, AZStd::string> AssetFileInfoListComparison::FilePattern(const AssetFileInfoList& assetFileInfoList, const ComparisonData& comparisonData) const
{
if (comparisonData.m_filePattern.empty())
{
return AZ::Failure(AZStd::string::format("Invalid Comparison Step: %s File Pattern value cannot be empty.\n", GetFilePatternTypeName(comparisonData.m_filePatternType)));
}
bool isWildCard = comparisonData.m_filePatternType == FilePatternType::Wildcard;
AssetFileInfoList assetFileInfoListResult;
for (const AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList)
{
if (isWildCard)
{
if (AZStd::wildcard_match(comparisonData.m_filePattern, assetFileInfo.m_assetRelativePath))
{
assetFileInfoListResult.m_fileInfoList.push_back(assetFileInfo);
}
}
else
{
AZStd::regex regex(comparisonData.m_filePattern.c_str(), AZStd::regex::extended);
if (AZStd::regex_match(assetFileInfo.m_assetRelativePath.c_str(), regex))
{
assetFileInfoListResult.m_fileInfoList.push_back(assetFileInfo);
}
}
}
return AZ::Success(assetFileInfoListResult);
}
AZ::Outcome<AssetFileInfoList, AZStd::string> AssetFileInfoListComparison::IntersectionCount(const AZStd::vector<AZStd::string>& assetFileInfoPathList) const
{
AZStd::unordered_map<AssetFileInfo, unsigned int, AssetFileInfoHasherByAssetId, AssetFileInfoComparatorByAssetId> assetCountMap;
for (const auto& absoluteAssetFileInfoPath : assetFileInfoPathList)
{
AZ::Outcome<AssetFileInfoList, AZStd::string> firstResult = PopulateAssetFileInfo(absoluteAssetFileInfoPath);
if (!firstResult.IsSuccess())
{
return AZ::Failure(firstResult.GetError());
}
AssetFileInfoList firstAssetFileInfoList = firstResult.TakeValue();
for (const AssetFileInfo& assetFileInfo : firstAssetFileInfoList.m_fileInfoList)
{
auto assetFound = assetCountMap.find(assetFileInfo);
if (assetFound == assetCountMap.end())
{
assetCountMap.insert(AZStd::make_pair(assetFileInfo, 1));
}
else
{
assetFound->second++;
}
}
}
// Loop over the map and create a assetFileInfo of assets that appeared at least the number of times specified by the user
AssetFileInfoList outputAssetFileInfoList;
for (auto iter = assetCountMap.begin(); iter != assetCountMap.end(); iter++)
{
if (iter->second >= m_comparisonDataList[0].m_intersectionCount)
{
outputAssetFileInfoList.m_fileInfoList.emplace_back(iter->first);
}
}
return AZ::Success(outputAssetFileInfoList);
}
bool AssetFileInfoListComparison::AddComparisonStep(const ComparisonData& comparisonData)
{
return AddComparisonStep(comparisonData, GetNumComparisonSteps());
}
bool AssetFileInfoListComparison::AddComparisonStep(const ComparisonData& comparisonData, size_t destinationIndex)
{
if (destinationIndex >= m_comparisonDataList.size())
{
m_comparisonDataList.emplace_back(comparisonData);
}
else
{
m_comparisonDataList.insert(&m_comparisonDataList.at(destinationIndex), comparisonData);
}
return true;
}
bool AssetFileInfoListComparison::RemoveComparisonStep(size_t index)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList.erase(&m_comparisonDataList.at(index));
return true;
}
bool AssetFileInfoListComparison::MoveComparisonStep(size_t initialIndex, size_t destinationIndex)
{
size_t comparisonDataListSize = m_comparisonDataList.size();
// No need to check the destinationIndex, if it is out of bounds it will just be appended to the end
if (initialIndex >= comparisonDataListSize)
{
AZ_Error(ErrorWindowName, false, "Input indices ( %u, %u ) are invalid.", initialIndex, destinationIndex);
return false;
}
auto comparisonData = m_comparisonDataList.at(initialIndex);
m_comparisonDataList.erase(&m_comparisonDataList.at(initialIndex));
// Since we have modified the list, the indecies after the initialIndex have all shifted
size_t modifiedDestinationIndex = destinationIndex;
if (destinationIndex > initialIndex)
{
--modifiedDestinationIndex;
}
return AddComparisonStep(comparisonData, modifiedDestinationIndex);
}
size_t AssetFileInfoListComparison::GetNumComparisonSteps() const
{
return m_comparisonDataList.size();
}
AZStd::vector<AssetFileInfoListComparison::ComparisonData> AssetFileInfoListComparison::GetComparisonList() const
{
return m_comparisonDataList;
}
bool AssetFileInfoListComparison::SetComparisonType(size_t index, const ComparisonType comparisonType)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_comparisonType = comparisonType;
if (comparisonType != ComparisonType::FilePattern)
{
// Only FilePattern operations are allowed to have FilePatternType and FilePattern values
m_comparisonDataList[index].m_filePatternType = FilePatternType::Default;
m_comparisonDataList[index].m_filePattern.clear();
}
else
{
// FilePattern operations only take one input
m_comparisonDataList[index].m_secondInput.clear();
}
return true;
}
bool AssetFileInfoListComparison::SetFilePatternType(size_t index, const FilePatternType filePatternType)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
if (m_comparisonDataList[index].m_comparisonType != ComparisonType::FilePattern)
{
AZ_Error(ErrorWindowName, false,
"Unable to set File Pattern Type: Comparison Step must be of type ( %s ). Current Comparison Type is: %s",
ComparisonTypeNames[static_cast<int>(ComparisonType::FilePattern)],
ComparisonTypeNames[static_cast<int>(m_comparisonDataList[index].m_comparisonType)]);
return false;
}
m_comparisonDataList[index].m_filePatternType = filePatternType;
return true;
}
bool AssetFileInfoListComparison::SetFilePattern(size_t index, const AZStd::string& filePattern)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %i ) is invalid.", index);
return false;
}
if (m_comparisonDataList[index].m_comparisonType != ComparisonType::FilePattern)
{
AZ_Error(ErrorWindowName, false,
"Unable to set File Pattern: Comparison Step must be of type ( %s ). Current Comparison Type is: %s",
ComparisonTypeNames[static_cast<int>(ComparisonType::FilePattern)],
ComparisonTypeNames[static_cast<int>(m_comparisonDataList[index].m_comparisonType)]);
return false;
}
m_comparisonDataList[index].m_filePattern = filePattern;
return true;
}
bool AssetFileInfoListComparison::SetFirstInput(size_t index, const AZStd::string& firstInput)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %i ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_firstInput = firstInput;
return true;
}
bool AssetFileInfoListComparison::SetSecondInput(size_t index, const AZStd::string& secondInput)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %i ) is invalid.", index);
return false;
}
if (m_comparisonDataList[index].m_comparisonType == ComparisonType::FilePattern)
{
AZ_Error(ErrorWindowName, false,
"Unable to set Second Input value: Comparison Step is of type ( %s ), which only allows for one input.",
ComparisonTypeNames[static_cast<int>(ComparisonType::FilePattern)]);
return false;
}
m_comparisonDataList[index].m_secondInput = secondInput;
return true;
}
bool AssetFileInfoListComparison::SetOutput(size_t index, const AZStd::string& output)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_output = output;
return true;
}
bool AssetFileInfoListComparison::SetCachedFirstInputPath(size_t index, const AZStd::string& firstInputPath)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_cachedFirstInputPath = firstInputPath;
return true;
}
bool AssetFileInfoListComparison::SetCachedSecondInputPath(size_t index, const AZStd::string& secondInputPath)
{
if (index >= m_comparisonDataList.size())
{
AZ_Error(ErrorWindowName, false, "Input index ( %u ) is invalid.", index);
return false;
}
m_comparisonDataList[index].m_cachedSecondInputPath = secondInputPath;
return true;
}
void AssetFileInfoListComparison::FormatOutputToken(AZStd::string& tokenName)
{
if (!tokenName.starts_with(TokenIdentifier) && !tokenName.empty())
{
AzFramework::StringFunc::Prepend(tokenName, TokenIdentifier);
}
}
AssetFileInfoListComparison::ComparisonData::ComparisonData(const ComparisonType& type, const AZStd::string& destinationPath, const AZStd::string& filePattern, FilePatternType filePatternType, unsigned int intersectionCount)
: m_comparisonType(type)
, m_output(destinationPath)
, m_filePattern(filePattern)
, m_filePatternType(filePatternType)
, m_intersectionCount(intersectionCount)
{
}
void AssetFileInfoListComparison::ComparisonData::Reflect(AZ::ReflectContext * context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ComparisonData>()
->Version(3)
->Field("comparisonType", &ComparisonData::m_comparisonType)
->Field("firstInput", &ComparisonData::m_firstInput)
->Field("secondInput", &ComparisonData::m_secondInput)
->Field("filePattern", &ComparisonData::m_filePattern)
->Field("filePatternType", &ComparisonData::m_filePatternType)
->Field("destinationPath", &ComparisonData::m_output)
->Field("intersectionCount", &ComparisonData::m_intersectionCount);
}
}
/*
* Asset bundler Path and file name utils
*/
void SplitFilename(const AZStd::string& filePath, AZStd::string& baseFileName, AZStd::string& platformIdentifier)
{
AZStd::string fileName;
AzFramework::StringFunc::Path::GetFileName(filePath.c_str(), fileName);
for (AZStd::string_view platformName : AzFramework::PlatformHelper::GetPlatformsInterpreted(AzFramework::PlatformFlags::AllNamedPlatforms))
{
AZStd::string appendedPlatform = AZStd::string::format("_%.*s", aznumeric_cast<int>(platformName.size()), platformName.data());
if (AzFramework::StringFunc::EndsWith(fileName, appendedPlatform))
{
AzFramework::StringFunc::RChop(fileName, appendedPlatform.size());
baseFileName = fileName;
platformIdentifier = platformName;
return;
}
}
}
void RemovePlatformIdentifier(AZStd::string& filePath)
{
AZStd::string baseFileName;
AZStd::string platform;
SplitFilename(filePath, baseFileName, platform);
if (platform.empty())
{
return;
}
AZStd::string extension;
AzFramework::StringFunc::Path::GetExtension(filePath.c_str(), extension);
AzFramework::StringFunc::Path::ReplaceFullName(filePath, baseFileName.c_str(), extension.c_str());
}
AZStd::string GetPlatformIdentifier(const AZStd::string& filePath)
{
AZStd::string baseFileName;
AZStd::string platform;
SplitFilename(filePath, baseFileName, platform);
return platform;
}
} // namespace AzToolsFramework
@@ -0,0 +1,233 @@
/*
* 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/std/string/string.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzToolsFramework/Asset/AssetSeedManager.h>
#include <AzFramework/Asset/AssetBundleManifest.h>
namespace AZ
{
class ReflectContext;
}
namespace AzToolsFramework
{
constexpr AZ::u64 MaxBundleSizeInMB = 2 * 1024;
class AssetBundleSettings
{
public:
AZ_TYPE_INFO(AssetBundleSettings, "{B9597C91-540E-41A9-9572-80A629061914}");
static void Reflect(AZ::ReflectContext * context);
//! Loads the AssetBundleSettings file from the file path
static AZ::Outcome<AssetBundleSettings, AZStd::string> Load(const AZStd::string& filePath);
static bool Save(const AssetBundleSettings& assetBundleFileInfo, const AZStd::string& destinationFilePath);
static bool IsBundleSettingsFile(const AZStd::string& filePath);
//! Returns the Bundle Settings file extension
static const char* GetBundleSettingsFileExtension();
//! Validates that the input path has the proper file extension for a Bundle Settings file.
//! Input path can be relative or absolute.
//! Returns void on success, error message on failure.
static AZ::Outcome<void, AZStd::string> ValidateBundleSettingsFileExtension(const AZStd::string& path);
//! Returns the Bundle file extension
static const char* GetBundleFileExtension();
//! Validates that the input path has the proper file extension for a Bundle.
//! Input path can be relative or absolute.
//! Returns void on success, error message on failure.
static AZ::Outcome<void, AZStd::string> ValidateBundleFileExtension(const AZStd::string& path);
static AZ::u64 GetMaxBundleSizeInMB();
static AZStd::string GetPlatformFromAssetInfoFilePath(const AssetBundleSettings& assetBundleSettings);
AZStd::string m_platform;
AZStd::string m_assetFileInfoListPath;
AZStd::string m_bundleFilePath; // the file path where the parent bundle file should get saved to disk.
int m_bundleVersion = AzFramework::AssetBundleManifest::CurrentBundleVersion;
AZ::u64 m_maxBundleSizeInMB = MaxBundleSizeInMB;
AZStd::string m_comment;
};
/*
* This class can we used to create a new AssetFileInfoList based on the
* comparison type specified by the user
*/
class AssetFileInfoListComparison
{
public:
enum class ComparisonType : AZ::u8
{
//! Given two AssetFileInfoLists A and B, creates a new AssetFileInfoList C such that assets in it are either
//! because they are present in B but not in A or because their file hash has changed between A and B.
Delta,
//! Given two AssetFileInfoLists A and B, creates a new AssetFileInfoList C such that it contains all the assets present in A and B.
//! If the asset is contained in both A and B, it will have B's information.
Union,
//! Given two AssetFileInfoLists A and B, creates a new AssetFileInfoList C such that it contains only the assets that are present in both A and B.
//! It is not necessary that all the information of assets should match between A and B, we are only checking the assetId
//! and the asset will have B's information.
Intersection,
//! Given two AssetFileInfoLists A and B, creates a new AssetFileInfoList C such that it contains all the assets that are present in B but not in A.
//! It is not necessary that all the information of assets should match in A and B, we are only checking by assetId.
Complement,
//! Given an AssetFileInfoList A, creates a new AssetFileInfoList C based on pattern matching.
//! Pattern matching can be either wildcard or regex.
FilePattern,
//! Given a list of AssetFileInfoLists like A, B, C.. and a count N, creates a new AssetFileInfoList D such that it contains only those assets
//! that are present at least N times in the list of input AssetFileInfoLists provided.
IntersectionCount,
//! New types go above NumPatterns- if you add one be sure to add it to ComparisonTypeNames as well
NumPatterns,
Default = 255,
};
static const char* ComparisonTypeNames[aznumeric_cast<int>(ComparisonType::NumPatterns)];
enum class FilePatternType : AZ::u8
{
//! The pattern is a file wildcard pattern (glob)
Wildcard,
//! The pattern is a regular expression pattern
Regex,
//! New types go above NumPatterns- if you add one be sure to add it to FilePatternTypeNames as well
NumPatterns,
Default = 255,
};
static const char* FilePatternTypeNames[aznumeric_cast<int>(FilePatternType::NumPatterns)];
struct ComparisonData
{
AZ_TYPE_INFO(ComparisonData, "{B39A7148-AC9D-4038-A85E-2C86A0B2DEF6}");
ComparisonData(const ComparisonType& type, const AZStd::string& destinationPath, const AZStd::string& filePattern = AZStd::string(), FilePatternType filePatternType = FilePatternType::Default, unsigned int intersectionCount = 0);
ComparisonData() = default;
static void Reflect(AZ::ReflectContext * context);
ComparisonType m_comparisonType = ComparisonType::Default;
AZStd::string m_firstInput;
AZStd::string m_secondInput;
FilePatternType m_filePatternType = FilePatternType::Default;
AZStd::string m_filePattern;
AZStd::string m_output;
unsigned int m_intersectionCount = 0;
// Values that are not saved to disk
AZStd::string m_cachedFirstInputPath;
AZStd::string m_cachedSecondInputPath;
};
AZ_TYPE_INFO(AssetFileInfoListComparison, "{AC003572-3A33-476C-9B2B-ADDA4F7BB870}");
AZ_CLASS_ALLOCATOR(AssetFileInfoListComparison, AZ::SystemAllocator, 0);
AssetFileInfoListComparison() = default;
static void Reflect(AZ::ReflectContext* context);
bool AddComparisonStep(const ComparisonData& comparisonData);
bool AddComparisonStep(const ComparisonData& comparisonData, size_t destinationIndex);
bool RemoveComparisonStep(size_t index);
bool MoveComparisonStep(size_t initialIndex, size_t destinationIndex);
size_t GetNumComparisonSteps() const;
AZStd::vector<ComparisonData> GetComparisonList() const;
bool SetComparisonType(size_t index, const ComparisonType comparisonType);
bool SetFilePatternType(size_t index, const FilePatternType filePatternType);
bool SetFilePattern(size_t index, const AZStd::string& filePattern);
bool SetFirstInput(size_t index, const AZStd::string& firstInput);
bool SetSecondInput(size_t index, const AZStd::string& secondInput);
bool SetOutput(size_t index, const AZStd::string& output);
bool SetCachedFirstInputPath(size_t index, const AZStd::string& firstInputPath);
bool SetCachedSecondInputPath(size_t index, const AZStd::string& secondInputPath);
static void FormatOutputToken(AZStd::string& tokenName);
//! This can be used to serialize the AssetFileInfoListComparison to the destination file path.
bool Save(const AZStd::string& destinationFilePath) const;
//! Loads the assetFileInfoListComparison file from the file path
static AZ::Outcome<AssetFileInfoListComparison, AZStd::string> Load(const AZStd::string& filePath);
//! Determine whether the file is a token file or not
static bool IsTokenFile(const AZStd::string& filePath);
//! Tests whether the path is non empty and not a token path (Is a theoretically writable output - doesn't test writeable status)
static bool IsOutputPath(const AZStd::string& filePath);
//! Runs all Comparison Steps and returns an AssetFileInfoList
AZ::Outcome<AssetFileInfoList, AZStd::string> Compare(const AZStd::vector<AZStd::string>& intersectionCountAssetListFiles = {});
//! Runs all Comparison Steps and saves all assetFileInfoList results to the destination paths stored with each Comparison Step
AZ::Outcome<void, AZStd::string> CompareAndSaveResults(const AZStd::vector<AZStd::string>& intersectionCountAssetListFiles = {});
//! Saves all previously completed comparisons to disk if output is a file path
AZ::Outcome<void, AZStd::string> SaveResults() const;
//! Get the absolute paths of any files marked for save that currently exist on disk
AZStd::vector<AZStd::string> GetDestructiveOverwriteFilePaths();
AssetFileInfoList GetComparisonResults(const AZStd::string& comparisonKey);
static const char* GetComparisonRulesFileExtension();
static const char* GetComparisonTypeName(ComparisonType comparisonType);
static const char* GetFilePatternTypeName(FilePatternType filePatternType);
static const char GetTokenIdentifier();
protected:
AssetFileInfoList Delta(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const;
AssetFileInfoList Union(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const;
AssetFileInfoList Intersection(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const;
AssetFileInfoList Complement(const AssetFileInfoList& firstAssetFileInfoList, const AssetFileInfoList& secondAssetFileInfoList) const;
AZ::Outcome<AssetFileInfoList, AZStd::string> FilePattern(const AssetFileInfoList& assetFileInfoList, const ComparisonData& comparisonData) const;
AZ::Outcome<AssetFileInfoList, AZStd::string> IntersectionCount(const AZStd::vector<AZStd::string>& assetFileInfoPathList) const;
AZ::Outcome<AssetFileInfoList, AZStd::string> PopulateAssetFileInfo(const AZStd::string& assetFileInfoPath) const;
AZStd::vector<ComparisonData> m_comparisonDataList;
//This should not be serialized to disk because this is storing internal state that can change.
AZStd::unordered_map<AZStd::string, AssetFileInfoList> m_assetFileInfoMap;
};
/*
* Some Helpers for dealing with filenames in the AssetBundler tools
*/
// Split the file name to get base name and platform identifier
void SplitFilename(const AZStd::string& filePath, AZStd::string& baseFileName, AZStd::string& platformIdentifier);
//! Removes the platform identifier from the filename if present
void RemovePlatformIdentifier(AZStd::string& filePath);
//! Returns the platform identifier from the filename, will return an empty string if none found
AZStd::string GetPlatformIdentifier(const AZStd::string& filePath);
} // namespace AzToolsFramework
@@ -0,0 +1,252 @@
/*
* 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/Asset/AssetDebugInfo.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalogBus.h>
#include <cinttypes>
const char AssetListDebugFileExtension[] = "assetlistdebug";
namespace AzToolsFramework
{
////////////////////////////////////////////////////////////////////////////////////////////
// DependencyNode
////////////////////////////////////////////////////////////////////////////////////////////
struct DependencyNode
{
~DependencyNode()
{
for (AZStd::map<AZ::Data::AssetId, DependencyNode*>::iterator leaf = m_leaves.begin(); leaf != m_leaves.end(); ++leaf)
{
delete leaf->second;
}
}
//! Creates a string that displays the hierarchy of how a product dependency relates back to a Seed in a Seed List file.
//! Example string:
//! fonts/vera-bold.font - {13AFF67C-6673-58CD-8FC8-265447B71259}:0 - 282 bytes
//! fonts/vera.fontfamily - {6B62FA48-7032-5B8D-88DB-4EDCF0D500AE}:0
//! ui/canvas/start.uicanvas - {E7B190F9-3507-5524-BE05-35C6941B8E26}:0
//! [SEED] levels/testdependencieslevel/level.pak - {A89A2DCA-0C7B-5919-B3E0-3F67BFD8AA40}:0
void BuildHumanReadableString(const AZStd::string& tabString, AssetFileDebugInfoList& infoList)
{
AZStd::string fileName = infoList.m_fileDebugInfoList[m_assetId].m_assetRelativePath;
// Only print size on the top asset, not the graph below it.
uint64_t filesize = infoList.m_fileDebugInfoList[m_assetId].m_fileSize;
AZStd::string sizeString = tabString.empty() ? AZStd::string::format(" - %" PRIu64 " bytes", filesize) : "";
infoList.m_humanReadableString += tabString;
if (m_isCyclicalDependency)
{
infoList.m_humanReadableString += AZStd::string("[CYCLICAL DEPENDENCY] ");
}
else if (m_leaves.empty())
{
infoList.m_humanReadableString += AZStd::string("[SEED] ");
}
infoList.m_humanReadableString += AZStd::string::format("%s - %s%s\n",
fileName.c_str(),
m_assetId.ToString<AZStd::string>().c_str(),
sizeString.c_str());
for (AZStd::map<AZ::Data::AssetId, DependencyNode*>::iterator leaf = m_leaves.begin(); leaf != m_leaves.end(); ++leaf)
{
leaf->second->BuildHumanReadableString(tabString + " ", infoList);
}
}
AZ::Data::AssetId m_assetId;
DependencyNode* m_parent = nullptr;
AZStd::map<AZ::Data::AssetId, DependencyNode*> m_leaves;
bool m_isCyclicalDependency = false;
};
////////////////////////////////////////////////////////////////////////////////////////////
// AssetFileDebugInfo
////////////////////////////////////////////////////////////////////////////////////////////
void AssetFileDebugInfo::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AssetFileDebugInfo>()
->Version(1)
->Field("assetId", &AssetFileDebugInfo::m_assetId)
->Field("assetRelativePath", &AssetFileDebugInfo::m_assetRelativePath)
->Field("fileSize", &AssetFileDebugInfo::m_fileSize)
->Field("filesThatReferenceMe", &AssetFileDebugInfo::m_filesThatReferenceMe);
}
}
////////////////////////////////////////////////////////////////////////////////////////////
// AssetFileDebugInfoList
////////////////////////////////////////////////////////////////////////////////////////////
AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> AssetFileDebugInfoList::GetAllProductDependenciesDebug(
const AZ::Data::AssetId& assetId,
const AzFramework::PlatformId& platformIndex,
AssetFileDebugInfoList* debugList,
AZStd::unordered_set<AZ::Data::AssetId>* cyclicalDependencySet,
const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList,
const AZStd::vector<AZStd::string>& wildcardPatternExclusionList)
{
using namespace AzToolsFramework::AssetCatalog;
if (!cyclicalDependencySet)
{
// A failure means there were no dependencies, and not that the call failed.
return AZ::Success(AZStd::vector<AZ::Data::ProductDependency>());
}
// A core piece of debug info is the tree from seed(s) that reference an asset, to the asset itself.
// It can be useful to know that someLevel\level.pak results in someTexture.dds being included, it's more useful to know
// that someLevel\level.pak references someMesh.cgf, which references SomeMaterial.mtl, which references someTexture.dds.
AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> currentDependenciesResult = AZ::Failure(AZStd::string());
PlatformAddressedAssetCatalogRequestBus::EventResult(currentDependenciesResult, platformIndex, &PlatformAddressedAssetCatalogRequestBus::Events::GetDirectProductDependencies, assetId);
if (!currentDependenciesResult.IsSuccess())
{
// A failure means there were no dependencies, and not that the call failed.
return AZ::Success(AZStd::vector<AZ::Data::ProductDependency>());
}
AZStd::vector<AZ::Data::ProductDependency> entries = currentDependenciesResult.TakeValue();
AZStd::vector<AZ::Data::ProductDependency> allFoundProducts;
allFoundProducts.reserve(allFoundProducts.size());
cyclicalDependencySet->insert(assetId);
for (const AZ::Data::ProductDependency& productDependency : entries)
{
if (!productDependency.m_assetId.IsValid())
{
continue;
}
if (exclusionList.find(productDependency.m_assetId) != exclusionList.end())
{
continue;
}
bool wildcardPatternMatch = false;
for (const AZStd::string& wildcardPattern : wildcardPatternExclusionList)
{
PlatformAddressedAssetCatalogRequestBus::EventResult(wildcardPatternMatch, platformIndex, &PlatformAddressedAssetCatalogRequestBus::Events::DoesAssetIdMatchWildcardPattern, assetId, wildcardPattern);
if (wildcardPatternMatch)
{
break;
}
}
if (wildcardPatternMatch)
{
continue;
}
allFoundProducts.push_back(productDependency);
// Cyclical Dependency detection
if (cyclicalDependencySet->find(productDependency.m_assetId) != cyclicalDependencySet->end())
{
continue;
}
if (debugList->m_fileDebugInfoList.find(productDependency.m_assetId) == debugList->m_fileDebugInfoList.end())
{
debugList->m_fileDebugInfoList[productDependency.m_assetId].m_assetId = productDependency.m_assetId;
}
debugList->m_fileDebugInfoList[productDependency.m_assetId].m_filesThatReferenceMe.insert(assetId);
// Recurse
auto recursiveResult = GetAllProductDependenciesDebug(productDependency.m_assetId, platformIndex, debugList, cyclicalDependencySet, exclusionList);
if (!recursiveResult.IsSuccess())
{
return recursiveResult;
}
AZStd::vector<AZ::Data::ProductDependency> recursiveValues = recursiveResult.TakeValue();
allFoundProducts.insert(allFoundProducts.end(), recursiveValues.begin(), recursiveValues.end());
}
cyclicalDependencySet->erase(assetId);
return AZ::Success(allFoundProducts);
}
const char* AssetFileDebugInfoList::GetAssetListDebugFileExtension()
{
return AssetListDebugFileExtension;
}
void AssetFileDebugInfoList::BuildHumanReadableString()
{
// Start with a newline to separate it from AZ serialization output.
m_humanReadableString = "\n\n";
for (AZStd::map<AZ::Data::AssetId, AssetFileDebugInfo>::iterator assetDebugInfo = m_fileDebugInfoList.begin();
assetDebugInfo != m_fileDebugInfoList.end();
++assetDebugInfo)
{
DependencyNode root;
root.m_assetId = assetDebugInfo->second.m_assetId;
BuildNodeTree(assetDebugInfo->second.m_assetId, &root);
root.BuildHumanReadableString("", *this);
m_humanReadableString += AZStd::string("\n");
}
}
void AssetFileDebugInfoList::BuildNodeTree(AZ::Data::AssetId assetId, DependencyNode* parent)
{
if (parent->m_isCyclicalDependency)
{
return;
}
for (const AZ::Data::AssetId& referenceId : m_fileDebugInfoList[assetId].m_filesThatReferenceMe)
{
bool foundLoop = false;
for (DependencyNode* hierarchyWalk = parent; hierarchyWalk != nullptr; hierarchyWalk = hierarchyWalk->m_parent)
{
if (hierarchyWalk->m_assetId == referenceId)
{
foundLoop = true;
break;
}
}
DependencyNode* leaf = new DependencyNode();
leaf->m_parent = parent;
leaf->m_assetId = referenceId;
leaf->m_isCyclicalDependency = foundLoop;
BuildNodeTree(referenceId, leaf);
parent->m_leaves[referenceId] = leaf;
}
}
void AssetFileDebugInfoList::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AssetFileDebugInfoList>()
->Version(1)
// We are only reflecting the human readable string here because we do not plan on loading this file type
// into memory at this time, and this is the easiest way for our customers to read this info
->Field("humanReadableString", &AssetFileDebugInfoList::m_humanReadableString);
}
}
} // namespace AzToolsFramework
@@ -0,0 +1,77 @@
/*
* 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/Asset/AssetCommon.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/containers/vector.h>
#include <AzFramework/Platform/PlatformDefaults.h>
namespace AZ
{
class ReflectionContext;
}
namespace AzToolsFramework
{
struct DependencyNode;
class AssetFileDebugInfo
{
public:
AZ_TYPE_INFO(AssetFileDebugInfo, "{1F7C8B0E-4403-49CA-A11F-ACBA05BEBF6A}");
AZ_CLASS_ALLOCATOR(AssetFileDebugInfo, AZ::SystemAllocator, 0);
AssetFileDebugInfo() = default;
static void Reflect(AZ::ReflectContext* context);
AZ::Data::AssetId m_assetId;
AZStd::string m_assetRelativePath;
AZ::IO::SizeType m_fileSize = 0;
// Direct references to this file.
// A dependency graph can be built by crawling this.
AZStd::set<AZ::Data::AssetId> m_filesThatReferenceMe;
};
class AssetFileDebugInfoList
{
public:
AZ_TYPE_INFO(AssetFileDebugInfoList, "{FD66D05D-B4F4-4F48-A4E8-FFE231BCC128}");
AZ_CLASS_ALLOCATOR(AssetFileDebugInfoList, AZ::SystemAllocator, 0);
AssetFileDebugInfoList() = default;
static void Reflect(AZ::ReflectContext* context);
static AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> GetAllProductDependenciesDebug(
const AZ::Data::AssetId& assetId,
const AzFramework::PlatformId& platformIndex,
AssetFileDebugInfoList* debugList,
AZStd::unordered_set<AZ::Data::AssetId>* cyclicalDependencySet,
const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList,
const AZStd::vector<AZStd::string>& wildcardPatternExclusionList = AZStd::vector<AZStd::string>());
static const char* GetAssetListDebugFileExtension();
void BuildHumanReadableString();
void BuildNodeTree(AZ::Data::AssetId assetId, DependencyNode* parent);
AZStd::map<AZ::Data::AssetId, AssetFileDebugInfo> m_fileDebugInfoList;
AZStd::string m_humanReadableString;
};
} // namespace AzToolsFramework
@@ -0,0 +1,478 @@
/*
* 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 <AzToolsFramework/Asset/AssetProcessorMessages.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Math/Crc.h>
namespace AzToolsFramework
{
using namespace AZ;
using namespace AzFramework::AssetSystem;
namespace AssetSystem
{
//---------------------------------------------------------------------
AssetJobsInfoRequest::AssetJobsInfoRequest(const AZ::OSString& searchTerm, bool requireFencing /*= true*/)
: BaseAssetProcessorMessage(requireFencing)
, m_searchTerm(searchTerm)
{
AZ_Assert(!searchTerm.empty(), "AssetJobsInfoRequest: Search Term is empty");
}
AssetJobsInfoRequest::AssetJobsInfoRequest(const AZ::Data::AssetId& assetId, bool escalateJobs /*= true*/, bool requireFencing /*= true*/)
: BaseAssetProcessorMessage(requireFencing)
, m_assetId(assetId)
, m_escalateJobs(escalateJobs)
{
}
AssetJobsInfoRequest::AssetJobsInfoRequest(bool requireFencing /*= true*/)
: BaseAssetProcessorMessage(requireFencing)
{
}
unsigned int AssetJobsInfoRequest::GetMessageType() const
{
return MessageType;
}
void AssetJobsInfoRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetJobsInfoRequest, BaseAssetProcessorMessage>()
->Version(4)
->Field("SearchTerm", &AssetJobsInfoRequest::m_searchTerm)
->Field("EscalateJobs", &AssetJobsInfoRequest::m_escalateJobs)
->Field("IsSearchTermJobKey", &AssetJobsInfoRequest::m_isSearchTermJobKey)
->Field("AssetId", &AssetJobsInfoRequest::m_assetId);
}
}
//---------------------------------------------------------------------
AssetJobsInfoResponse::AssetJobsInfoResponse(AssetSystem::JobInfoContainer& jobList, bool isSuccess)
: m_isSuccess(isSuccess)
{
m_jobList.swap(jobList);
}
AssetJobsInfoResponse::AssetJobsInfoResponse(AssetSystem::JobInfoContainer&& jobList, bool isSuccess)
: m_isSuccess(isSuccess)
, m_jobList(AZStd::move(jobList))
{
}
unsigned int AssetJobsInfoResponse::GetMessageType() const
{
return AssetJobsInfoRequest::MessageType;
}
void AssetJobsInfoResponse::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetJobsInfoResponse, BaseAssetProcessorMessage>()
->Version(1)
->Field("JobList", &AssetJobsInfoResponse::m_jobList)
->Field("Success", &AssetJobsInfoResponse::m_isSuccess);
}
}
//---------------------------------------------------------------------
AssetJobLogRequest::AssetJobLogRequest(AZ::u64 jobRunKey, bool requireFencing /*= true*/)
: BaseAssetProcessorMessage(requireFencing)
, m_jobRunKey(jobRunKey)
{
AZ_Assert(m_jobRunKey > 0 , "AssetJobLogRequest: asset run key is invalid");
}
AssetJobLogRequest::AssetJobLogRequest(bool requireFencing /*= true*/)
: BaseAssetProcessorMessage(requireFencing)
{
}
unsigned int AssetJobLogRequest::GetMessageType() const
{
return MessageType;
}
void AssetJobLogRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetJobLogRequest, BaseAssetProcessorMessage>()
->Version(1)
->Field("JobRunKey", &AssetJobLogRequest::m_jobRunKey);
}
}
//---------------------------------------------------------------------
AssetJobLogResponse::AssetJobLogResponse(const AZStd::string& jobLog, bool isSuccess)
: m_jobLog(jobLog)
, m_isSuccess(isSuccess)
{
}
unsigned int AssetJobLogResponse::GetMessageType() const
{
return AssetJobLogRequest::MessageType;
}
void AssetJobLogResponse::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetJobLogResponse, BaseAssetProcessorMessage>()
->Version(1)
->Field("JobLog", &AssetJobLogResponse::m_jobLog)
->Field("Success", &AssetJobLogResponse::m_isSuccess);
}
}
//---------------------------------------------------------------------
SourceFileNotificationMessage::SourceFileNotificationMessage(const AZ::OSString& relativeSourcePath, const AZ::OSString& scanFolder, NotificationType type, AZ::Uuid sourceUUID)
: m_relativeSourcePath(relativeSourcePath)
, m_scanFolder(scanFolder)
, m_type(type)
, m_sourceUUID(sourceUUID)
{
AZ_Assert(!m_relativeSourcePath.empty(), "SourceFileNotificationMessage: empty relative path");
AZ_Assert(!scanFolder.empty(), "SourceFileNotificationMessage: empty scanFolder");
}
unsigned int SourceFileNotificationMessage::GetMessageType() const
{
return MessageType;
}
void SourceFileNotificationMessage::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SourceFileNotificationMessage, BaseAssetProcessorMessage>()
->Version(2)
->Field("RelativeSourcePath", &SourceFileNotificationMessage::m_relativeSourcePath)
->Field("ScanFolder", &SourceFileNotificationMessage::m_scanFolder)
->Field("NotificationType", &SourceFileNotificationMessage::m_type)
->Field("SourceUUID", &SourceFileNotificationMessage::m_sourceUUID);
}
}
unsigned int GetAbsoluteAssetDatabaseLocationRequest::GetMessageType() const
{
return MessageType;
}
void GetAbsoluteAssetDatabaseLocationRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<GetAbsoluteAssetDatabaseLocationRequest, BaseAssetProcessorMessage>()
->Version(1);
}
}
unsigned int GetAbsoluteAssetDatabaseLocationResponse::GetMessageType() const
{
return GetAbsoluteAssetDatabaseLocationRequest::MessageType;
}
void GetAbsoluteAssetDatabaseLocationResponse::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<GetAbsoluteAssetDatabaseLocationResponse, BaseAssetProcessorMessage>()
->Version(1)
->Field("AssetDatabasePath", &GetAbsoluteAssetDatabaseLocationResponse::m_absoluteAssetDatabaseLocation)
->Field("Success", &GetAbsoluteAssetDatabaseLocationResponse::m_isSuccess);
}
}
//---------------------------------------------------------------------
unsigned int GetScanFoldersRequest::GetMessageType() const
{
return MessageType;
}
void GetScanFoldersRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<GetScanFoldersRequest>()
->Version(1)
->SerializeWithNoData();
}
}
//---------------------------------------------------------------------
GetScanFoldersResponse::GetScanFoldersResponse(const AZStd::vector<AZStd::string>& scanFolders)
: m_scanFolders(scanFolders)
{
}
GetScanFoldersResponse::GetScanFoldersResponse(AZStd::vector<AZStd::string>&& scanFolders)
: m_scanFolders(AZStd::move(scanFolders))
{
}
unsigned int GetScanFoldersResponse::GetMessageType() const
{
return GetScanFoldersRequest::MessageType;
}
void GetScanFoldersResponse::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<GetScanFoldersResponse, BaseAssetProcessorMessage>()
->Version(1)
->Field("ScanFolders", &GetScanFoldersResponse::m_scanFolders);
}
}
//---------------------------------------------------------------------
unsigned int GetAssetSafeFoldersRequest::GetMessageType() const
{
return MessageType;
}
void GetAssetSafeFoldersRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<GetAssetSafeFoldersRequest>()
->Version(1)
->SerializeWithNoData();
}
}
//---------------------------------------------------------------------
GetAssetSafeFoldersResponse::GetAssetSafeFoldersResponse(const AZStd::vector<AZStd::string>& assetSafeFolders)
: m_assetSafeFolders(assetSafeFolders)
{
}
GetAssetSafeFoldersResponse::GetAssetSafeFoldersResponse(AZStd::vector<AZStd::string>&& assetSafeFolders)
: m_assetSafeFolders(AZStd::move(assetSafeFolders))
{
}
unsigned int GetAssetSafeFoldersResponse::GetMessageType() const
{
return GetAssetSafeFoldersRequest::MessageType;
}
void GetAssetSafeFoldersResponse::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<GetAssetSafeFoldersResponse, BaseAssetProcessorMessage>()
->Version(1)
->Field("AssetSafeFolders", &GetAssetSafeFoldersResponse::m_assetSafeFolders);
}
}
//---------------------------------------------------------------------
void FileInfosNotificationMessage::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<FileInfosNotificationMessage, BaseAssetProcessorMessage>()
->Field("NotificationType", &FileInfosNotificationMessage::m_type)
->Field("FileID", &FileInfosNotificationMessage::m_fileID)
->Version(1);
}
}
unsigned FileInfosNotificationMessage::GetMessageType() const
{
static unsigned int messageType = AZ_CRC("FileProcessor::FileInfosNotification", 0x001c43f5);
return messageType;
}
//---------------------------------------------------------------------
void AssetProcessorPlatformStatusRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetProcessorPlatformStatusRequest, BaseAssetProcessorMessage>()
->Version(1)
->Field("Platform", &AssetProcessorPlatformStatusRequest::m_platform);
}
}
unsigned int AssetProcessorPlatformStatusRequest::GetMessageType() const
{
return AssetProcessorPlatformStatusRequest::MessageType;
}
//------------------------------------------------------------------------
void AssetProcessorPlatformStatusResponse::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetProcessorPlatformStatusResponse, BaseAssetProcessorMessage>()
->Version(1)
->Field("IsPlatformEnabled", &AssetProcessorPlatformStatusResponse::m_isPlatformEnabled);
}
}
unsigned int AssetProcessorPlatformStatusResponse::GetMessageType() const
{
return AssetProcessorPlatformStatusRequest::MessageType;
}
//---------------------------------------------------------------------
void AssetProcessorPendingPlatformAssetsRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetProcessorPendingPlatformAssetsRequest, BaseAssetProcessorMessage>()
->Version(1)
->Field("Platform", &AssetProcessorPendingPlatformAssetsRequest::m_platform);
}
}
unsigned int AssetProcessorPendingPlatformAssetsRequest::GetMessageType() const
{
return AssetProcessorPendingPlatformAssetsRequest::MessageType;
}
//------------------------------------------------------------------------
void AssetProcessorPendingPlatformAssetsResponse::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetProcessorPendingPlatformAssetsResponse, BaseAssetProcessorMessage>()
->Version(1)
->Field("NumberOfPendingJobs", &AssetProcessorPendingPlatformAssetsResponse::m_numberOfPendingJobs);
}
}
unsigned int AssetProcessorPendingPlatformAssetsResponse::GetMessageType() const
{
return AssetProcessorPendingPlatformAssetsRequest::MessageType;
}
unsigned int WantAssetBrowserShowRequest::GetMessageType() const
{
return WantAssetBrowserShowRequest::MessageType;
}
void WantAssetBrowserShowRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<WantAssetBrowserShowRequest, BaseAssetProcessorMessage>()
->Version(1);
}
}
unsigned int WantAssetBrowserShowResponse::GetMessageType() const
{
return WantAssetBrowserShowResponse::MessageType;
}
void WantAssetBrowserShowResponse::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<WantAssetBrowserShowResponse, BaseAssetProcessorMessage>()
->Version(1)
->Field("ProcessId", &WantAssetBrowserShowResponse::m_processId);
}
}
unsigned int AssetBrowserShowRequest::GetMessageType() const
{
return AssetBrowserShowRequest::MessageType;
}
void AssetBrowserShowRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetBrowserShowRequest, BaseAssetProcessorMessage>()
->Version(1)
->Field("FilePath", &AssetBrowserShowRequest::m_filePath);
}
}
//---------------------------------------------------------------------
SourceAssetProductsInfoRequest::SourceAssetProductsInfoRequest(const AZ::Data::AssetId& assetId)
: m_assetId(assetId)
{
}
unsigned int SourceAssetProductsInfoRequest::GetMessageType() const
{
return MessageType;
}
void SourceAssetProductsInfoRequest::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SourceAssetProductsInfoRequest, BaseAssetProcessorMessage>()
->Version(1)
->Field("AssetId", &SourceAssetProductsInfoRequest::m_assetId);
}
}
//---------------------------------------------------------------------
unsigned int SourceAssetProductsInfoResponse::GetMessageType() const
{
return SourceAssetProductsInfoRequest::MessageType;
}
void SourceAssetProductsInfoResponse::Reflect(AZ::ReflectContext* context)
{
auto serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SourceAssetProductsInfoResponse, BaseAssetProcessorMessage>()
->Version(1)
->Field("Found", &SourceAssetProductsInfoResponse::m_found)
->Field("ProductsAssetInfo", &SourceAssetProductsInfoResponse::m_productsAssetInfo);
}
}
} // namespace AssetSystem
} // namespace AzToolsFramework
@@ -0,0 +1,387 @@
/*
* 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
// The Tools Framework AssetProcessorMessages header is for all of the asset processor messages that should only
// be available to tools, not the runtime.
#include <AzFramework/Asset/AssetProcessorMessages.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzCore/Asset/AssetCommon.h>
namespace AZ
{
class SerializeContext;
}
namespace AzToolsFramework
{
namespace AssetSystem
{
//! Request the jobs information for a given asset from the AssetProcessor
class AssetJobsInfoRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(AssetJobsInfoRequest, AZ::OSAllocator, 0);
AZ_RTTI(AssetJobsInfoRequest, "{E5DEF45C-C4CF-47ED-843F-97B3C4A3D5B3}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetProcessor::AssetJobsInfoRequest", 0xbd18de74);
explicit AssetJobsInfoRequest(bool requireFencing = true);
explicit AssetJobsInfoRequest(const AZ::Data::AssetId& assetId, bool m_escalateJobs = true, bool requireFencing = true);
AssetJobsInfoRequest(const AZ::OSString& searchTerm, bool requireFencing = true);
unsigned int GetMessageType() const override;
AZ::OSString m_searchTerm;
AZ::Data::AssetId m_assetId;
bool m_isSearchTermJobKey = false;
bool m_escalateJobs = true;
};
//! This will be send in response to the AssetJobsInfoRequest request,
//! and will contain jobs information for the requested asset along with the jobid
class AssetJobsInfoResponse
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(AssetJobsInfoResponse, AZ::OSAllocator, 0);
AZ_RTTI(AssetJobsInfoResponse, "{743AFB3B-F24C-4546-BEEC-2769442B52DB}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
AssetJobsInfoResponse() = default;
AssetJobsInfoResponse(AssetSystem::JobInfoContainer& jobList, bool isSuccess);
AssetJobsInfoResponse(AssetSystem::JobInfoContainer&& jobList, bool isSuccess);
unsigned int GetMessageType() const override;
bool m_isSuccess = false;
AssetSystem::JobInfoContainer m_jobList;
};
//! Request the log data for a given jobId from the AssetProcessor
class AssetJobLogRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(AssetJobLogRequest, AZ::OSAllocator, 0);
AZ_RTTI(AssetJobLogRequest, "{8E69F76E-F25D-486E-BC3F-26BB3FF5A3A3}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetProcessor::AssetJobLogRequest", 0xfbb80fd3);
explicit AssetJobLogRequest(bool requireFencing = true);
explicit AssetJobLogRequest(AZ::u64 jobRunKey, bool requireFencing = true);
unsigned int GetMessageType() const override;
AZ::u64 m_jobRunKey;
};
//! This will be sent in response to the AssetJobLogRequest request, and will contain the complete job log as a string
class AssetJobLogResponse
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(AssetJobLogResponse, AZ::OSAllocator, 0);
AZ_RTTI(AssetJobLogResponse, "{4CBB55AB-24E3-4A7A-ACB7-54069289AF2C}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
AssetJobLogResponse() = default;
AssetJobLogResponse(const AZStd::string& jobLog, bool isSuccess);
unsigned int GetMessageType() const override;
bool m_isSuccess = false;
AZStd::string m_jobLog;
};
//! Tools side message that a source file has changed or been removed
class SourceFileNotificationMessage
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
enum NotificationType : unsigned int
{
FileChanged,
FileRemoved,
FileFailed,
};
AZ_CLASS_ALLOCATOR(SourceFileNotificationMessage, AZ::OSAllocator, 0);
AZ_RTTI(SourceFileNotificationMessage, "{61126952-242A-4299-B1D6-4D0E24DB1B06}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetProcessorManager::SourceFileNotification", 0x8bfc4d1c);
SourceFileNotificationMessage() = default;
SourceFileNotificationMessage(const AZ::OSString& relPath, const AZ::OSString& scanFolder, NotificationType type, AZ::Uuid sourceUUID);
unsigned int GetMessageType() const override;
AZ::OSString m_relativeSourcePath;
AZ::OSString m_scanFolder;
AZ::Uuid m_sourceUUID;
NotificationType m_type;
};
class GetAbsoluteAssetDatabaseLocationRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(GetAbsoluteAssetDatabaseLocationRequest, AZ::OSAllocator, 0);
AZ_RTTI(GetAbsoluteAssetDatabaseLocationRequest, "{8696976E-F19D-48E3-BDDF-2GB63FA1AF23}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetProcessor::GetAbsoluteAssetDatabaseLocationRequest", 0xb3aa4931);
GetAbsoluteAssetDatabaseLocationRequest() = default;
unsigned int GetMessageType() const override;
};
class GetAbsoluteAssetDatabaseLocationResponse
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(GetAbsoluteAssetDatabaseLocationResponse, AZ::OSAllocator, 0);
AZ_RTTI(GetAbsoluteAssetDatabaseLocationResponse, "{BDF155AB-EE74-FACA-3654-54069289AF2C}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
GetAbsoluteAssetDatabaseLocationResponse() = default;
unsigned int GetMessageType() const override;
bool m_isSuccess = false;
AZStd::string m_absoluteAssetDatabaseLocation;
};
class GetScanFoldersRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(GetScanFoldersRequest, AZ::OSAllocator, 0);
AZ_RTTI(GetScanFoldersRequest, "{A3D7FD31-C260-4D6C-B970-D565B43F1316}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetProcessor::GetScanFoldersRequest", 0x01274152);
~GetScanFoldersRequest() override = default;
unsigned int GetMessageType() const override;
};
class GetScanFoldersResponse
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(GetScanFoldersResponse, AZ::OSAllocator, 0);
AZ_RTTI(GetScanFoldersResponse, "{13100365-009E-4C82-A682-A8E3646EB0E0}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
GetScanFoldersResponse() = default;
explicit GetScanFoldersResponse(const AZStd::vector<AZStd::string>& scanFolders);
explicit GetScanFoldersResponse(AZStd::vector<AZStd::string>&& scanFolders);
~GetScanFoldersResponse() override = default;
unsigned int GetMessageType() const override;
AZStd::vector<AZStd::string> m_scanFolders;
};
class GetAssetSafeFoldersRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(GetAssetSafeFoldersRequest, AZ::OSAllocator, 0);
AZ_RTTI(GetAssetSafeFoldersRequest, "{9A7951B1-257C-45F0-B334-A52A42A5A871}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetProcessor::GetAssetSafeFoldersRequest", 0xf58fd05c);
~GetAssetSafeFoldersRequest() override = default;
unsigned int GetMessageType() const override;
};
class GetAssetSafeFoldersResponse
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(GetAssetSafeFoldersResponse, AZ::OSAllocator, 0);
AZ_RTTI(GetAssetSafeFoldersResponse, "{36C1AA51-8940-4909-A01B-19454B6312E5}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
GetAssetSafeFoldersResponse() = default;
explicit GetAssetSafeFoldersResponse(const AZStd::vector<AZStd::string>& assetSafeFolders);
explicit GetAssetSafeFoldersResponse(AZStd::vector<AZStd::string>&& assetSafeFolders);
~GetAssetSafeFoldersResponse() override = default;
unsigned int GetMessageType() const override;
AZStd::vector<AZStd::string> m_assetSafeFolders;
};
class FileInfosNotificationMessage
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
enum NotificationType : unsigned int
{
Synced,
FileAdded,
FileRemoved
};
AZ_CLASS_ALLOCATOR(FileInfosNotificationMessage, AZ::OSAllocator, 0);
AZ_RTTI(FileInfosNotificationMessage, "{F5AF3ED1-1644-4972-AE21-B6A1B28D898A}", AzFramework::AssetSystem::BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
FileInfosNotificationMessage() = default;
unsigned int GetMessageType() const override;
NotificationType m_type = NotificationType::Synced;
AZ::s64 m_fileID = 0;
};
//////////////////////////////////////////////////////////////////////////
//! Request the enabled status of an asset platform
class AssetProcessorPlatformStatusRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(AssetProcessorPlatformStatusRequest, AZ::OSAllocator, 0);
AZ_RTTI(AssetProcessorPlatformStatusRequest, "{529A8549-DD78-4E66-9BEA-D633846115C6}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetSystem::AssetProcessorPlatformStatusRequest", 0x036e116e);
AssetProcessorPlatformStatusRequest() = default;
unsigned int GetMessageType() const override;
AZ::OSString m_platform;
};
//! This will be sent in response to the AssetProcessorPlatformStatusRequest request,
//! indicating if the asset platform is currently enabled or not
class AssetProcessorPlatformStatusResponse
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(AssetProcessorPlatformStatusResponse, AZ::OSAllocator, 0);
AZ_RTTI(AssetProcessorPlatformStatusResponse, "{3F804A16-3C5A-41A5-9051-7714E3CFAC9A}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
AssetProcessorPlatformStatusResponse() = default;
unsigned int GetMessageType() const override;
bool m_isPlatformEnabled = false;
};
//////////////////////////////////////////////////////////////////////////
//! Request the total number of pending jobs for an asset platform
class AssetProcessorPendingPlatformAssetsRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(AssetProcessorPendingPlatformAssetsRequest, AZ::OSAllocator, 0);
AZ_RTTI(AssetProcessorPendingPlatformAssetsRequest, "{5B16F6F2-0F94-4BAE-8238-1E8F3E66D507}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetSystem::AssetProcessorPendingPlatformAssetsRequest", 0x9582a76c);
AssetProcessorPendingPlatformAssetsRequest() = default;
unsigned int GetMessageType() const override;
AZ::OSString m_platform;
};
//! This will be sent in response to the AssetProcessorPendingPlatformAssetsRequest request,
//! indicating the number of pending assets for the specified platform
class AssetProcessorPendingPlatformAssetsResponse
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(AssetProcessorPendingPlatformAssetsResponse, AZ::OSAllocator, 0);
AZ_RTTI(AssetProcessorPendingPlatformAssetsResponse, "{E63825D6-4704-471D-8594-B96656FA4477}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
AssetProcessorPendingPlatformAssetsResponse() = default;
unsigned int GetMessageType() const override;
int m_numberOfPendingJobs = -1;
};
// WantAssetBrowserShowRequest
class WantAssetBrowserShowRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(WantAssetBrowserShowRequest, AZ::OSAllocator, 0);
AZ_RTTI(WantAssetBrowserShowRequest, "{C66852BF-1A8C-47AC-9CFF-183CC4241075}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetSystem::WantAssetBrowserShowRequest", 0xa861bc09);
WantAssetBrowserShowRequest() = default;
unsigned int GetMessageType() const override;
};
// WantAssetBrowserShowResponse
class WantAssetBrowserShowResponse
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(WantAssetBrowserShowResponse, AZ::OSAllocator, 0);
AZ_RTTI(WantAssetBrowserShowResponse, "{B3015EF2-A91F-4E7D-932A-DA6043EDB678}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetSystem::WantAssetBrowserShowResponse", 0x2784cbb9);
WantAssetBrowserShowResponse() = default;
unsigned int GetMessageType() const override;
unsigned int m_processId = 0;
};
// AssetBrowserShowRequest
class AssetBrowserShowRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(AssetBrowserShowRequest, AZ::OSAllocator, 0);
AZ_RTTI(AssetBrowserShowRequest, "{D44903DD-45D8-4CBA-8A9D-C9D5E7FFB0A6}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetSystem::AssetBrowserShowRequest", 0xb2768047);
AssetBrowserShowRequest() = default;
unsigned int GetMessageType() const override;
AZ::OSString m_filePath;
};
// SourceAssetProductsInfoRequest
class SourceAssetProductsInfoRequest
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(SourceAssetProductsInfoRequest, AZ::OSAllocator, 0);
AZ_RTTI(SourceAssetProductsInfoRequest, "{14D0994C-7096-44D9-A239-2A7B51DDC95A}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetProcessor::SourceAssetProductsInfoRequest", 0x97f169fc);
SourceAssetProductsInfoRequest() = default;
explicit SourceAssetProductsInfoRequest(const AZ::Data::AssetId& assetId);
unsigned int GetMessageType() const override;
AZ::Data::AssetId m_assetId;
};
// SourceAssetProductsInfoResponse
class SourceAssetProductsInfoResponse
: public AzFramework::AssetSystem::BaseAssetProcessorMessage
{
public:
AZ_CLASS_ALLOCATOR(SourceAssetProductsInfoResponse, AZ::OSAllocator, 0);
AZ_RTTI(SourceAssetProductsInfoResponse, "{DF0B7C57-534E-480E-8889-C4872C87C1C3}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
SourceAssetProductsInfoResponse() = default;
unsigned int GetMessageType() const override;
bool m_found = false;
AZStd::vector<AZ::Data::AssetInfo> m_productsAssetInfo;
};
} // namespace AssetSystem
} // namespace AzToolsFramework
@@ -0,0 +1,861 @@
/*
* 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/Asset/AssetSeedManager.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Math/Sha1.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/Slice/SliceAsset.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzFramework/FileTag/FileTagBus.h>
#include <AzFramework/FileTag/FileTag.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Asset/AssetDebugInfo.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalogBus.h>
#include <AzToolsFramework/AssetCatalog/PlatformAddressedAssetCatalog.h>
const char SeedFileExtension[] = "seed";
const char AssetListFileExtension[] = "assetlist";
const char ScriptCanvas[] = "scriptcanvas";
const char ScriptCanvasCompiled[] = "scriptcanvas_compiled";
const char ScriptCanvasFunction[] = "scriptcanvas_fn";
const char ScriptCanvasFunctionCompiled[] = "scriptcanvas_fn_compiled";
namespace AzToolsFramework
{
AZStd::string GetSeedPath(AZ::Data::AssetId assetId, AzFramework::PlatformFlags platformFlags)
{
using namespace AzToolsFramework;
auto platformIndices = AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(platformFlags);
for (const auto& platformId : platformIndices)
{
AZStd::string assetPath;
AssetCatalog::PlatformAddressedAssetCatalogRequestBus::EventResult(assetPath, platformId, &AssetCatalog::PlatformAddressedAssetCatalogRequestBus::Events::GetAssetPathById, assetId);
if (!assetPath.empty())
{
return assetPath;
}
}
AZ_Warning("AssetSeedManager", false, "Unable to resolve path of Seed asset (%s) for the given platforms (%s).\n", assetId.ToString<AZStd::string>().c_str(), AzFramework::PlatformHelper::GetCommaSeparatedPlatformList(platformFlags).c_str());
return {};
}
AZ::Data::AssetId GetAssetIdFromString(const AZStd::string& assetKey)
{
AZ::Data::AssetId assetId;
auto found = assetKey.find(":");
if (found != AZStd::string::npos)
{
assetId.m_subId = static_cast<AZ::u32>(atoi(assetKey.substr(found + 1).c_str()));
assetId.m_guid = AZ::Uuid(assetKey.substr(0, found).c_str());
return assetId;
}
return AZ::Data::AssetId();
}
AssetSeedManager::~AssetSeedManager()
{
m_sourceAssetTypeToRuntimeAssetTypeMap.clear();
}
bool AssetSeedManager::AddSeedAsset(AZ::Data::AssetId assetId, AzFramework::PlatformFlags platformFlags, AZStd::string path, const AZStd::string& seedListFilePath)
{
for (auto iter = m_assetSeedList.begin(); iter != m_assetSeedList.end(); ++iter)
{
if (iter->m_assetId == assetId)
{
if (iter->m_platformFlags == platformFlags)
{
AZ_TracePrintf("AssetSeedManager", "Seed Asset ( %s ) is already present in the asset seed list.\n", assetId.ToString<AZStd::string>().c_str());
return false;
}
iter->m_platformFlags = iter->m_platformFlags | platformFlags;
return true;
}
}
if (path.empty())
{
path = GetSeedPath(assetId, platformFlags);
}
m_assetSeedList.emplace_back(AzFramework::SeedInfo(assetId, platformFlags, path, seedListFilePath));
return true;
}
void AssetSeedManager::RemoveSeedAsset(AZ::Data::AssetId assetId, AzFramework::PlatformFlags platformFlags)
{
for (auto iter = m_assetSeedList.begin(); iter != m_assetSeedList.end(); ++iter)
{
if (iter->m_assetId == assetId)
{
if (iter->m_platformFlags == platformFlags)
{
m_assetSeedList.erase(iter);
}
else
{
iter->m_platformFlags = iter->m_platformFlags & (~platformFlags);
}
return;
}
}
AZ_TracePrintf("AssetSeedManager", "Seed Asset ( %s ) is not present in the asset seed list.\n", assetId.ToString<AZStd::string>().c_str());
}
void AssetSeedManager::RemoveSeedAsset(const AZStd::string& assetKey, AzFramework::PlatformFlags platformFlags)
{
bool validAssetId = false;
AZStd::string assetPathHint;
AZ::Data::AssetId assetId = GetAssetIdFromString(assetKey);
if (assetId.IsValid())
{
validAssetId = true;
}
else
{
AZStd::string normalizedAssetKey = assetKey;
AZ::StringFunc::Path::Normalize(normalizedAssetKey);
if (AZ::StringFunc::Path::IsValid(normalizedAssetKey.c_str()))
{
assetPathHint = assetKey;
AZ::StringFunc::Path::Normalize(assetPathHint);
}
}
if (assetPathHint.empty() && !validAssetId)
{
AZ_Warning("AssetSeedManager", false, "Invalid asset key ( %s ). It is neither a valid assetId nor a valid relative path.\n", assetKey.c_str());
}
bool assetFound = false;
for (auto iter = m_assetSeedList.begin(); iter != m_assetSeedList.end(); ++iter)
{
if (validAssetId && iter->m_assetId == assetId)
{
iter->m_platformFlags = iter->m_platformFlags & (~platformFlags);
if (iter->m_platformFlags == AzFramework::PlatformFlags::Platform_NONE)
{
m_assetSeedList.erase(iter);
}
assetFound = true;
break;
}
else if(!assetPathHint.empty())
{
// if we are here it implies that we need to search based on path hint
AZ::StringFunc::Path::Normalize(iter->m_assetRelativePath);
if (iter->m_assetRelativePath == assetPathHint)
{
iter->m_platformFlags = iter->m_platformFlags & (~platformFlags);
if (iter->m_platformFlags == AzFramework::PlatformFlags::Platform_NONE)
{
m_assetSeedList.erase(iter);
}
assetFound = true;
break;
}
}
}
if (!assetFound)
{
AZ_Warning("AssetSeedManager", false, "Unable to remove asset ( %s ). Please ensure that this asset exists in the seed list file(s).\n", assetKey.c_str());
}
}
bool AssetSeedManager::AddSeedAsset(const AZStd::string& assetPath, AzFramework::PlatformFlags platformFlags, const AZStd::string& seedListFilePath)
{
AZStd::string seedPath = AddAssetToSeedListHelper(assetPath);
if (seedPath.empty())
{
// Error has already been thrown
return false;
}
AZ::Data::AssetId assetId = GetAssetIdByPath(seedPath, platformFlags);
if (assetId.IsValid())
{
return AddSeedAsset(assetId, platformFlags, seedPath, seedListFilePath);
}
AZ_Warning("AssetSeedManager", false, "Unable to add asset ( %s ) to the seed list, could not find it on all requested platforms.\n", seedPath.c_str());
return false;
}
AZStd::pair<AZ::Data::AssetId, AzFramework::PlatformFlags> AssetSeedManager::AddSeedAssetForValidPlatforms(const AZStd::string& assetPath, AzFramework::PlatformFlags platformFlags)
{
using namespace AzFramework;
AZStd::pair<AZ::Data::AssetId, AzFramework::PlatformFlags> result = AZStd::make_pair(AZ::Data::AssetId(), AzFramework::PlatformFlags::Platform_NONE);
if (platformFlags == AzFramework::PlatformFlags::Platform_NONE)
{
AZ_Error("AssetSeedManager", false, "Unable to add asset ( %s ) to the seed list, no platforms were defined.\n", assetPath.c_str());
return result;
}
AZStd::string seedPath = AddAssetToSeedListHelper(assetPath);
if (seedPath.empty())
{
// Error has already been thrown
return result;
}
// Try to add it one platform at a time, that way if any of them fail, the Seed is still added
// to the list for the successes
for (AzFramework::PlatformId platformId : AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(platformFlags))
{
PlatformFlags singlePlatformFlag = AzFramework::PlatformHelper::GetPlatformFlagFromPlatformIndex(platformId);
auto tempId = GetAssetIdByPath(seedPath, singlePlatformFlag);
if (!tempId.IsValid())
{
// Try another platform
continue;
}
result.first = tempId;
if (AddSeedAsset(tempId, singlePlatformFlag))
{
result.second = result.second | singlePlatformFlag;
}
}
return result;
}
void AssetSeedManager::AddPlatformToAllSeeds(AzFramework::PlatformId platform)
{
using namespace AzToolsFramework::AssetCatalog;
AzFramework::PlatformFlags platformFlag = AzFramework::PlatformHelper::GetPlatformFlagFromPlatformIndex(platform);
AZ::Data::AssetInfo assetInfo;
bool isSpecialPlatform = AzFramework::PlatformHelper::IsSpecialPlatform(platformFlag);
for (auto& seed : m_assetSeedList)
{
if (isSpecialPlatform)
{
seed.m_platformFlags |= platformFlag;
}
else
{
assetInfo = GetAssetInfoById(seed.m_assetId, platform, seed.m_seedListFilePath, seed.m_assetRelativePath);
if (assetInfo.m_assetId.IsValid())
{
seed.m_platformFlags |= platformFlag;
}
}
}
}
void AssetSeedManager::RemovePlatformFromAllSeeds(AzFramework::PlatformId platform)
{
using namespace AzToolsFramework::AssetCatalog;
AzFramework::PlatformFlags platformFlag = AzFramework::PlatformHelper::GetPlatformFlagFromPlatformIndex(platform);
for (auto& seed : m_assetSeedList)
{
if ((seed.m_platformFlags & (~platformFlag)) == AzFramework::PlatformFlags::Platform_NONE)
{
AZ_Warning("AssetSeedManager", false, "Cannot remove platform ( %s ) from Seed ( %s ): Seed only has one platform", AzFramework::PlatformHelper::GetPlatformName(platform), seed.m_assetId.ToString<AZStd::string>().c_str());
}
else
{
seed.m_platformFlags &= (~platformFlag);
}
}
}
AZ::Data::AssetId AssetSeedManager::FindAssetIdByPathHint(const AZStd::string& pathHint) const
{
for (auto iter = m_assetSeedList.begin(); iter != m_assetSeedList.end(); ++iter)
{
if (AzFramework::StringFunc::Equal(iter->m_assetRelativePath.c_str(), pathHint.c_str()))
{
return iter->m_assetId;
}
}
return AZ::Data::AssetId();
}
// Returns the AssetId if it was valid for all the platforms in platformFlags
AZ::Data::AssetId AssetSeedManager::GetAssetIdByPath(const AZStd::string& assetPath, const AzFramework::PlatformFlags& platformFlags) const
{
using namespace AzToolsFramework::AssetCatalog;
AZ::Data::AssetId assetId;
auto platformIndices = AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(platformFlags);
bool foundInvalid = false;
for (const auto& platformNum : platformIndices)
{
AZ::Data::AssetId foundAssetId;
PlatformAddressedAssetCatalogRequestBus::EventResult(foundAssetId, platformNum, &PlatformAddressedAssetCatalogRequestBus::Events::GetAssetIdByPath, assetPath.c_str(), AZ::Data::s_invalidAssetType, false);
if (!foundAssetId.IsValid())
{
AZ_Warning("AssetSeedManager", false, "Asset catalog does not know about the asset ( %s ) on platform ( %s ).", assetPath.c_str(), AzFramework::PlatformHelper::GetPlatformName(platformNum));
foundInvalid = true;
}
else
{
assetId = foundAssetId;
}
}
if (foundInvalid)
{
return AZ::Data::AssetId();
}
return assetId;
}
AZ::Data::AssetId AssetSeedManager::GetAssetIdByAssetKey(const AZStd::string& assetKey, const AzFramework::PlatformFlags& platformFlags) const
{
AZ::Data::AssetId assetId = GetAssetIdFromString(assetKey);
if (assetId.IsValid())
{
return assetId;
}
// if we are here we will first try to query the asset catalog about the asset,
// if we are unable to find the asset in the catalog than we will try to search it based on path hints.
assetId = GetAssetIdByPath(assetKey, platformFlags);
if (!assetId.IsValid())
{
assetId = FindAssetIdByPathHint(assetKey);
}
return assetId;
}
// Returns the asset info if it exists and is the same for the platform specified by platformIndex
AZ::Data::AssetInfo AssetSeedManager::GetAssetInfoById(const AZ::Data::AssetId& assetId, const AzFramework::PlatformId& platformIndex, const AZStd::string& seedListfilePath, const AZStd::string& assetHintPath)
{
using namespace AzToolsFramework::AssetCatalog;
AZ::Data::AssetInfo assetInfo;
PlatformAddressedAssetCatalogRequestBus::EventResult(assetInfo, static_cast<AzFramework::PlatformId>(platformIndex), &PlatformAddressedAssetCatalogRequestBus::Events::GetAssetInfoById, assetId);
if (!assetInfo.m_assetId.IsValid())
{
AZStd::string errorMessage = AZStd::string::format("Could not find asset with id (%s) on platform (%s)", assetId.ToString<AZStd::string>().c_str(), AzFramework::PlatformHelper::GetPlatformName(platformIndex));
if (!seedListfilePath.empty() || !assetHintPath.empty())
{
errorMessage = AZStd::string::format("%s from Seed List (%s) Asset Hint (%s)", errorMessage.c_str(), seedListfilePath.c_str(), assetHintPath.c_str());
}
AZ_Error("AssetSeedManager", false, errorMessage.c_str());
}
return assetInfo;
}
AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> AssetSeedManager::GetAllProductDependencies(
const AZ::Data::AssetId& assetId,
const AzFramework::PlatformId& platformIndex,
const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList,
AssetFileDebugInfoList* optionalDebugList,
AZStd::unordered_set<AZ::Data::AssetId>* cyclicalDependencySet,
const AZStd::vector<AZStd::string>& wildcardPatternExclusionList) const
{
using namespace AzToolsFramework::AssetCatalog;
if (optionalDebugList)
{
return AssetFileDebugInfoList::GetAllProductDependenciesDebug(assetId, platformIndex, optionalDebugList, cyclicalDependencySet, exclusionList, wildcardPatternExclusionList);
}
// If not gathering debug info, then the recursion can happen in SQL. Call GetAllProductDependencies for the faster method of gathering.
AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> getDependenciesResult = AZ::Failure(AZStd::string());
PlatformAddressedAssetCatalogRequestBus::EventResult(getDependenciesResult, platformIndex, &PlatformAddressedAssetCatalogRequestBus::Events::GetAllProductDependenciesFilter, assetId, exclusionList, wildcardPatternExclusionList);
return getDependenciesResult;
}
AZStd::string AssetSeedManager::AddAssetToSeedListHelper(const AZStd::string& assetPath)
{
if (!m_sourceAssetTypeToRuntimeAssetTypeMap.size())
{
PopulateAssetTypeMap();
}
using namespace AzFramework::FileTag;
AZStd::vector<AZStd::string> editorTagsList = { FileTags[static_cast<unsigned int>(FileTagsIndex::EditorOnly)] };
bool editorOnlyAsset = false;
QueryFileTagsEventBus::EventResult(editorOnlyAsset, FileTagType::Exclude,
&QueryFileTagsEventBus::Events::Match, assetPath, editorTagsList);
AZStd::string seedPath = assetPath;
if (editorOnlyAsset)
{
// If we are here it implies that this is an editor only asset type.
// Please note that in those cases where we are certain about what the runtime asset type should be for this
// source asset type, we will fix the fileextension.
AZStd::string fileExtension;
AzFramework::StringFunc::Path::GetExtension(seedPath.c_str(), fileExtension, false);
auto found = m_sourceAssetTypeToRuntimeAssetTypeMap.find(fileExtension);
if (found != m_sourceAssetTypeToRuntimeAssetTypeMap.end())
{
AzFramework::StringFunc::Path::ReplaceExtension(seedPath, found->second.c_str());
AZ_Warning("AssetSeedManager", false, "( %s ) is an editor only asset. We wil use seed asset( %s ) instead.\n", assetPath.c_str(), seedPath.c_str());
}
else
{
AZ_Warning("AssetSeedManager", false, "Invalid seed asset ( %s ). This is an editor only asset. Please note that you can open the asset processor database and find \
all assets associated with this source asset and add those products instead. \n", seedPath.c_str());
return {};
}
}
return seedPath;
}
void AssetSeedManager::PopulateAssetTypeMap()
{
AZStd::string sliceFileExtension;
AzFramework::StringFunc::Path::GetExtension(AZ::SliceAsset::GetFileFilter(), sliceFileExtension, false);
AZStd::string dynamicSliceFileExtension;
AzFramework::StringFunc::Path::GetExtension(AZ::DynamicSliceAsset::GetFileFilter(), dynamicSliceFileExtension, false);
m_sourceAssetTypeToRuntimeAssetTypeMap[sliceFileExtension] = dynamicSliceFileExtension;
m_sourceAssetTypeToRuntimeAssetTypeMap[ScriptCanvas] = ScriptCanvasCompiled;
m_sourceAssetTypeToRuntimeAssetTypeMap[ScriptCanvasFunction] = ScriptCanvasFunctionCompiled;
}
const AzFramework::AssetSeedList& AssetSeedManager::GetAssetSeedList() const
{
return m_assetSeedList;
}
AZ::Outcome<void, AZStd::string> AssetSeedManager::SetSeedPlatformFlags(int index, AzFramework::PlatformFlags platformFlags)
{
if (index >= m_assetSeedList.size() || index < 0)
{
return AZ::Failure(AZStd::string("Index is out of range"));
}
m_assetSeedList[index].m_platformFlags = platformFlags;
return AZ::Success();
}
AssetSeedManager::AssetsInfoList AssetSeedManager::GetDependenciesInfo(AzFramework::PlatformId platformIndex, const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList, AssetFileDebugInfoList* optionalDebugList, const AZStd::vector<AZStd::string>& wildcardPatternExclusionList) const
{
if (!m_assetSeedList.size())
{
AZ_TracePrintf("AssetSeedManager", "Asset Seed list is empty.\n");
return {};
}
if (!AzToolsFramework::PlatformAddressedAssetCatalog::CatalogExists(platformIndex))
{
// There's no catalog loaded for this platform, so there won't be any assets
return {};
}
AssetSeedManager::AssetsInfoList assetsInfoList;
AZStd::unordered_set<AZ::Data::AssetId> assetIdSet;
AZStd::unordered_set<AZ::Data::AssetId> cyclicalDependencySet;
for (int idx = 0; idx < m_assetSeedList.size(); idx++)
{
if(!AzFramework::PlatformHelper::HasPlatformFlag(m_assetSeedList[idx].m_platformFlags,platformIndex))
{
// This asset is not valid for the platformIndex
continue;
}
AZ::Data::AssetInfo seedAssetInfo = GetAssetInfoById(m_assetSeedList[idx].m_assetId, platformIndex, m_assetSeedList[idx].m_seedListFilePath, m_assetSeedList[idx].m_assetRelativePath);
if (optionalDebugList &&
optionalDebugList->m_fileDebugInfoList.find(seedAssetInfo.m_assetId) == optionalDebugList->m_fileDebugInfoList.end())
{
optionalDebugList->m_fileDebugInfoList[seedAssetInfo.m_assetId].m_assetId = seedAssetInfo.m_assetId;
}
if (assetIdSet.find(seedAssetInfo.m_assetId) != assetIdSet.end())
{
// do not want duplicate enteries in the assets info list
continue;
}
if(exclusionList.find(seedAssetInfo.m_assetId) != exclusionList.end())
{
continue;
}
bool wildcardPatternMatch = false;
for (const AZStd::string& wildcardPattern : wildcardPatternExclusionList)
{
AzToolsFramework::AssetCatalog::PlatformAddressedAssetCatalogRequestBus::EventResult(wildcardPatternMatch, platformIndex, &AzToolsFramework::AssetCatalog::PlatformAddressedAssetCatalogRequestBus::Events::DoesAssetIdMatchWildcardPattern, seedAssetInfo.m_assetId, wildcardPattern);
if (wildcardPatternMatch)
{
break;
}
}
if (wildcardPatternMatch)
{
continue;
}
assetsInfoList.emplace_back(AZStd::move(seedAssetInfo));
assetIdSet.insert(seedAssetInfo.m_assetId);
cyclicalDependencySet.clear();
AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> getDependenciesResult =
GetAllProductDependencies(m_assetSeedList[idx].m_assetId, platformIndex, exclusionList, optionalDebugList, &cyclicalDependencySet, wildcardPatternExclusionList);
if (getDependenciesResult.IsSuccess())
{
AZStd::vector<AZ::Data::ProductDependency> entries = getDependenciesResult.TakeValue();
for (const AZ::Data::ProductDependency& productDependency : entries)
{
if (productDependency.m_assetId.IsValid() && assetIdSet.find(productDependency.m_assetId) == assetIdSet.end())
{
assetIdSet.insert(productDependency.m_assetId);
AZ::Data::AssetInfo assetInfo = GetAssetInfoById(productDependency.m_assetId, platformIndex, m_assetSeedList[idx].m_seedListFilePath);
assetsInfoList.emplace_back(AZStd::move(assetInfo));
}
}
}
else
{
AZ_Error("AssetSeedManager", false, "Unable to retrieve all product dependencies for asset ( %s ) with asset id ( %s ).\n", seedAssetInfo.m_relativePath.c_str(), m_assetSeedList[idx].m_assetId.ToString<AZStd::string>().c_str());
}
}
return assetsInfoList;
}
AssetFileInfoList AssetSeedManager::GetDependencyList(AzFramework::PlatformId platformIndex, const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList, AssetFileDebugInfoList* optionalDebugList, const AZStd::vector<AZStd::string>& wildcardPatternExclusionList) const
{
AssetSeedManager::AssetsInfoList assetInfoList = AZStd::move(GetDependenciesInfo(platformIndex, exclusionList, optionalDebugList, wildcardPatternExclusionList));
AssetFileInfoList assetFileInfoList;
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(fileIO != nullptr, "AZ::IO::FileIOBase must be ready for use.\n");
AZStd::string assetRoot = PlatformAddressedAssetCatalog::GetAssetRootForPlatform(platformIndex);
for (const AZ::Data::AssetInfo& assetInfo : assetInfoList)
{
if (assetInfo.m_assetId.IsValid())
{
if (!assetInfo.m_relativePath.empty())
{
AZStd::string assetPath;
AzFramework::StringFunc::Path::Join(assetRoot.c_str(), assetInfo.m_relativePath.c_str(), assetPath);
if (!fileIO->Exists(assetPath.c_str()))
{
AZ_Warning("AssetSeedManager", false, "Asset ( %s ) does not exist in the cache folder.\n", assetPath.c_str());
continue;
}
AZ::IO::FileIOStream fileStream;
if (!fileStream.Open(assetPath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary))
{
AZ_Warning("AssetSeedManager", false, "Failed to open asset ( %s ).\n", assetPath.c_str());
continue;
}
AZ::IO::SizeType length = fileStream.GetLength();
AZ::Sha1 hash;
AZStd::array<AZ::u32, AssetFileInfo::s_arraySize> digest = { {0} };
AZ::u32 digestArray[AssetFileInfo::s_arraySize] = { 0 };
// If there's no length, there's no data to hash.
// It's valid to have 0 length files, these can be used as markers for the file system.
if (length)
{
AZStd::vector<uint8_t> buffer;
buffer.resize_no_construct(length);
if (fileStream.Read(length, buffer.data()) != length)
{
AZ_Warning("AssetSeedManager", false, "Failed to read entire asset file ( %s ).\n", assetPath.c_str());
continue;
}
hash.ProcessBytes(buffer.data(), buffer.size());
hash.GetDigest(digestArray);
for (int idx = 0; idx < digest.size(); idx++)
{
digest[idx] = digestArray[idx];
}
}
uint64_t modTime = fileIO->ModificationTime(assetInfo.m_relativePath.c_str());
AssetFileInfo assetFileInfo(assetInfo.m_assetId, assetInfo.m_relativePath, modTime, digest);
if (optionalDebugList)
{
optionalDebugList->m_fileDebugInfoList[assetInfo.m_assetId].m_assetRelativePath = assetInfo.m_relativePath;
optionalDebugList->m_fileDebugInfoList[assetInfo.m_assetId].m_fileSize = length;
}
assetFileInfoList.m_fileInfoList.push_back(assetFileInfo);
}
else
{
AZ_Warning("AssetSeedManager", false, "Asset with asset id ( %s ) is missing relative path information in the asset catalog.\n", assetInfo.m_assetId.ToString<AZStd::string>().c_str());
}
}
}
return assetFileInfoList;
}
bool AssetSeedManager::SaveAssetFileInfo(const AZStd::string& destinationFilePath, AzFramework::PlatformFlags platformFlags, const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList, const AZStd::string& debugFilePath, const AZStd::vector<AZStd::string>& wildcardPatternExclusionList)
{
auto platformIndices = AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(platformFlags);
if (platformIndices.size() != 1)
{
AZ_Warning("AssetSeedManager", false, "AssetSeedManager::SaveAssetFileInfo can only operate on one platform at a time.\n");
return false;
}
bool useDebugInfoList = !debugFilePath.empty();
AssetFileDebugInfoList debugInfo;
AssetFileInfoList assetFileInfoList = GetDependencyList(platformIndices[0], exclusionList, useDebugInfoList ? &debugInfo : nullptr, wildcardPatternExclusionList);
if (!AssetFileInfoList::Save(assetFileInfoList, destinationFilePath))
{
// Error has already been thrown
return false;
}
if (useDebugInfoList)
{
debugInfo.BuildHumanReadableString();
return AZ::Utils::SaveObjectToFile(debugFilePath, AZ::DataStream::StreamType::ST_XML, &debugInfo);
}
return true;
}
AZ::Outcome<AssetFileInfoList, AZStd::string> AssetSeedManager::LoadAssetFileInfo(const AZStd::string& assetListFileAbsolutePath)
{
auto fileExtensionOutcome = AssetFileInfoList::ValidateAssetListFileExtension(assetListFileAbsolutePath);
if (!fileExtensionOutcome.IsSuccess())
{
return AZ::Failure(fileExtensionOutcome.GetError());
}
if (!AZ::IO::FileIOBase::GetInstance()->Exists(assetListFileAbsolutePath.c_str()))
{
return AZ::Failure(AZStd::string::format("Unable to load Asset List file ( %s ): file does not exist.", assetListFileAbsolutePath.c_str()));
}
AssetFileInfoList assetFileInfoList;
if (!AZ::Utils::LoadObjectFromFileInPlace(assetListFileAbsolutePath.c_str(), assetFileInfoList))
{
return AZ::Failure(AZStd::string::format("Unable to load Asset List file ( %s ).", assetListFileAbsolutePath.c_str()));
}
return AZ::Success(assetFileInfoList);
}
const char* AssetSeedManager::GetSeedFileExtension()
{
return SeedFileExtension;
}
AZ::Outcome<void, AZStd::string> AssetSeedManager::ValidateSeedFileExtension(const AZStd::string& path)
{
if (!AzFramework::StringFunc::EndsWith(path, SeedFileExtension))
{
return AZ::Failure(AZStd::string::format(
"Invalid Seed List file path ( %s ). Invalid file extension, Seed List files can only have ( .%s ) extension.\n",
path.c_str(),
SeedFileExtension));
}
return AZ::Success();
}
const char* AssetSeedManager::GetAssetListFileExtension()
{
return AssetListFileExtension;
}
const AZStd::string& AssetSeedManager::GetReadablePlatformList(const AzFramework::SeedInfo& seed)
{
using namespace AzFramework;
auto readablePlatformListIter = m_platformFlagsToReadablePlatformList.find(seed.m_platformFlags);
if (readablePlatformListIter != m_platformFlagsToReadablePlatformList.end())
{
return readablePlatformListIter->second;
}
m_platformFlagsToReadablePlatformList[seed.m_platformFlags] = PlatformHelper::GetCommaSeparatedPlatformList(seed.m_platformFlags);
return m_platformFlagsToReadablePlatformList.at(seed.m_platformFlags);
}
void AssetSeedManager::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AssetSeedManager>()
->Version(1)
->Field("assetSeedList", &AssetSeedManager::m_assetSeedList);
AssetFileInfo::Reflect(serializeContext);
AssetFileInfoList::Reflect(serializeContext);
AssetFileDebugInfo::Reflect(serializeContext);
AssetFileDebugInfoList::Reflect(serializeContext);
}
}
bool AssetSeedManager::Save(const AZStd::string& destinationPath)
{
auto fileExtensionOutcome = ValidateSeedFileExtension(destinationPath);
if (!fileExtensionOutcome.IsSuccess())
{
AZ_Error("AssetSeedManager", false, fileExtensionOutcome.GetError().c_str());
return false;
}
if (AZ::IO::FileIOBase::GetInstance()->Exists(destinationPath.c_str()) && AZ::IO::FileIOBase::GetInstance()->IsReadOnly(destinationPath.c_str()))
{
AZ_Error("AssetSeedManager", false, "Unable to save seed file (%s): file is marked Read-Only.\n", destinationPath.c_str());
return false;
}
return AZ::Utils::SaveObjectToFile(destinationPath, AZ::DataStream::StreamType::ST_XML, &m_assetSeedList);
}
void AssetSeedManager::UpdateSeedPath()
{
for (AzFramework::SeedInfo& seedInfo : m_assetSeedList)
{
AZStd::string assetPath = GetSeedPath(seedInfo.m_assetId, seedInfo.m_platformFlags);
if (!assetPath.empty())
{
seedInfo.m_assetRelativePath = assetPath;
}
}
}
void AssetSeedManager::RemoveSeedPath()
{
for (AzFramework::SeedInfo& seedInfo : m_assetSeedList)
{
seedInfo.m_assetRelativePath.clear();
}
}
bool AssetSeedManager::Load(const AZStd::string& sourceFilePath)
{
auto fileExtensionOutcome = ValidateSeedFileExtension(sourceFilePath);
if (!fileExtensionOutcome.IsSuccess())
{
AZ_Error("AssetSeedManager", false, fileExtensionOutcome.GetError().c_str());
return false;
}
AzFramework::AssetSeedList assetSeedList;
if (!AZ::Utils::LoadObjectFromFileInPlace(sourceFilePath.c_str(), assetSeedList))
{
return false;
}
for (AzFramework::SeedInfo& seedInfo : assetSeedList)
{
AddSeedAsset(seedInfo.m_assetId, seedInfo.m_platformFlags, seedInfo.m_assetRelativePath, sourceFilePath.c_str());
}
return true;
}
AssetFileInfo::AssetFileInfo(AZ::Data::AssetId assetId, AZStd::string assetRelativePath, uint64_t modTime, AZStd::array<AZ::u32, s_arraySize> hash)
: m_assetId(assetId)
, m_modificationTime(modTime)
, m_assetRelativePath(AZStd::move(assetRelativePath))
, m_hash(AZStd::move(hash))
{
}
void AssetFileInfo::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AssetFileInfo>()
->Version(1)
->Field("assetId", &AssetFileInfo::m_assetId)
->Field("assetRelativePath", &AssetFileInfo::m_assetRelativePath)
->Field("modificationTime", &AssetFileInfo::m_modificationTime)
->Field("hash", &AssetFileInfo::m_hash);
}
}
void AssetFileInfoList::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AssetFileInfoList>()
->Version(1)
->Field("fileInfoList", &AssetFileInfoList::m_fileInfoList);
}
}
bool AssetFileInfoList::Save(const AssetFileInfoList& assetFileInfoList, const AZStd::string& destinationFilePath)
{
if (assetFileInfoList.m_fileInfoList.empty())
{
// Don't save an empty list
AZ_Warning("AssetFileInfoList", false, "Unable to save Asset List file (%s): list is empty.\n", destinationFilePath.c_str());
return false;
}
auto fileExtensionOutcome = ValidateAssetListFileExtension(destinationFilePath);
if (!fileExtensionOutcome.IsSuccess())
{
AZ_Error("AssetFileInfoList", false, fileExtensionOutcome.GetError().c_str());
return false;
}
if (AZ::IO::FileIOBase::GetInstance()->Exists(destinationFilePath.c_str()) && AZ::IO::FileIOBase::GetInstance()->IsReadOnly(destinationFilePath.c_str()))
{
AZ_Error("AssetFileInfoList", false, "Unable to save Asset List file (%s): file is marked Read-Only.\n", destinationFilePath.c_str());
return false;
}
return AZ::Utils::SaveObjectToFile(destinationFilePath, AZ::DataStream::StreamType::ST_XML, &assetFileInfoList);
}
AZ::Outcome<void, AZStd::string> AssetFileInfoList::ValidateAssetListFileExtension(const AZStd::string& path)
{
if (!AzFramework::StringFunc::EndsWith(path, AssetListFileExtension))
{
return AZ::Failure(AZStd::string::format(
"Invalid Asset List file path ( %s ). Invalid file extension, Asset List files can only have ( .%s ) extension.\n",
path.c_str(),
AssetListFileExtension));
}
return AZ::Success();
}
} // namespace AzToolsFramework
@@ -0,0 +1,192 @@
/*
* 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/Asset/AssetCommon.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/vector.h>
#include <AzFramework/Asset/AssetRegistry.h>
#include <AzFramework/Asset/AssetSeedList.h>
#include <AzFramework/Platform/PlatformDefaults.h>
namespace AzToolsFramework
{
class AssetFileDebugInfo;
class AssetFileDebugInfoList;
// AssetFileInfo class will be used to store information
// related to the asset file including modification time
// and the the hash of the entire file content.
class AssetFileInfo
{
public:
constexpr static int s_arraySize = 5;
AZ_TYPE_INFO(AssetFileInfo, "{F1616D53-9A20-4C04-854E-D458A334B86B}");
AZ_CLASS_ALLOCATOR(AssetFileInfo, AZ::SystemAllocator, 0);
AssetFileInfo(AZ::Data::AssetId assetId, AZStd::string assetRelativePath, uint64_t modTime, AZStd::array<AZ::u32, s_arraySize> hash);
AssetFileInfo() = default;
static void Reflect(AZ::ReflectContext* context);
AZ::Data::AssetId m_assetId;
AZStd::string m_assetRelativePath;
uint64_t m_modificationTime = 0;
AZStd::array<AZ::u32, s_arraySize> m_hash;// hash of the file content
};
class AssetFileInfoList
{
public:
AZ_TYPE_INFO(AssetFileInfoList, "{61F16042-E381-47E4-8AAA-91BC532F4101}");
AZ_CLASS_ALLOCATOR(AssetFileInfoList, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
static bool Save(const AssetFileInfoList& assetFileInfoList, const AZStd::string& destinationFilePath);
//! Validates that the input path has the proper file extension for an Asset List file.
//! Input path can be relative or absolute.
//! Returns void on success, error message on failure.
static AZ::Outcome<void, AZStd::string> ValidateAssetListFileExtension(const AZStd::string& path);
AZStd::vector<AssetFileInfo> m_fileInfoList;
};
/*
* Implements an Asset Seed Manager that can be used to handle seed assets.
* Given a list of seed assets, this class can retrieve a complete
* list of all the product dependencies.
*/
class AssetSeedManager
{
public:
AZ_TYPE_INFO(AssetSeedManager, "{0DD7913A-EAD2-43DF-9A30-8A6FA6111E98}");
AZ_CLASS_ALLOCATOR(AssetSeedManager, AZ::SystemAllocator, 0);
using AssetsInfoList = AZStd::vector<AZ::Data::AssetInfo>;
~AssetSeedManager();
bool AddSeedAsset(AZ::Data::AssetId assetId, AzFramework::PlatformFlags platformFlags, AZStd::string path = AZStd::string(), const AZStd::string& seedListFilePath = AZStd::string());
bool AddSeedAsset(const AZStd::string& assetPath, AzFramework::PlatformFlags platformFlags, const AZStd::string& seedListFilePath = AZStd::string());
//! Attempts to add all platform-specific instances of the given Asset to the Seed List, if they exist.
//! Returns the Asset Id of the added Asset, and all of the Platforms that were found on-disk and added to the Seed List.
AZStd::pair<AZ::Data::AssetId, AzFramework::PlatformFlags> AddSeedAssetForValidPlatforms(const AZStd::string& assetPath, AzFramework::PlatformFlags platformFlags);
void RemoveSeedAsset(AZ::Data::AssetId assetId, AzFramework::PlatformFlags platformFlags);
//! Removes the seed from the seed list.
//! AssetKey can either be an assetid, assetpath or the path hint.
//! If you are providing an assetId than the format has to be "Uuid:SubID", i.e source Uuid and sub ID are separated by a colon
//! and subId needs to be in the decimal format.
//! Also important to note is that the asset path is relative to the second project name in the cache
//! for example if your asset absolute path is <absolute_path_to_cachefolder>/<game_name>/<platform>/<game_name_lowercase>/foo/dummy.txt
//! than the assetPath should be foo/dummy.txt
void RemoveSeedAsset(const AZStd::string& assetKey, AzFramework::PlatformFlags platformFlags);
void AddPlatformToAllSeeds(AzFramework::PlatformId platform);
void RemovePlatformFromAllSeeds(AzFramework::PlatformId platform);
//! Save the asset seed list to the destination path
bool Save(const AZStd::string& destinationFilePath);
//! Updates the seed path for all seeds.
//! If a seed is enabled for multiple platforms, it will update the path
//! with the information provided by the first asset catalog in which that seed exists.
void UpdateSeedPath();
//! Removes seed path hint for all existing seeds.
void RemoveSeedPath();
//! Loads the asset seed file from the source path and
//! adds root assets from the file to the seed list
bool Load(const AZStd::string& sourceFilePath);
const AzFramework::AssetSeedList& GetAssetSeedList() const;
AZ::Outcome<void, AZStd::string> SetSeedPlatformFlags(int index, AzFramework::PlatformFlags platformFlags);
// Using the entries in the seed list retrieves a list of product dependencies and their AssetInfo
AssetsInfoList GetDependenciesInfo(AzFramework::PlatformId platformIndex, const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList, AssetFileDebugInfoList* optionalDebugList = nullptr, const AZStd::vector<AZStd::string>& wildcardPatternExclusionList = AZStd::vector<AZStd::string>()) const;
// Creates a AssetFileInfoList comprising of all known product dependencies from the seed list.
AssetFileInfoList GetDependencyList(AzFramework::PlatformId platformIndex, const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList = {}, AssetFileDebugInfoList* optionalDebugList = nullptr, const AZStd::vector<AZStd::string>& wildcardPatternExclusionList = AZStd::vector<AZStd::string>()) const;
// Expands the current seed list to gather all dependencies based on the given platform. If given a debugFilePath,
// also stores information that is useful for understanding what is in the asset list info file and why, but not necessary to generate bundles.
bool SaveAssetFileInfo(const AZStd::string& destinationFilePath, AzFramework::PlatformFlags platformFlags, const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList = {}, const AZStd::string& debugFilePath = AZStd::string(), const AZStd::vector<AZStd::string>& wildcardPatternExclusionList = AZStd::vector<AZStd::string>());
AZ::Outcome<AssetFileInfoList, AZStd::string> LoadAssetFileInfo(const AZStd::string& assetListFileAbsolutePath);
//! Returns the Seed List file extension
static const char* GetSeedFileExtension();
//! Validates that the input path has the proper file extension for a Seed List file.
//! Input path can be relative or absolute.
//! Returns void on success, error message on failure.
static AZ::Outcome<void, AZStd::string> ValidateSeedFileExtension(const AZStd::string& path);
//! Returns the Asset List file extension
static const char* GetAssetListFileExtension();
const AZStd::string& GetReadablePlatformList(const AzFramework::SeedInfo& seed);
static void Reflect(AZ::ReflectContext* context);
AZ::Data::AssetId FindAssetIdByPathHint(const AZStd::string& pathHint) const;
AZ::Data::AssetId GetAssetIdByPath(const AZStd::string& assetPath, const AzFramework::PlatformFlags& platformFlags) const;
AZ::Data::AssetId GetAssetIdByAssetKey(const AZStd::string& assetKey, const AzFramework::PlatformFlags& platformFlags) const;
static AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& assetId, const AzFramework::PlatformId& platformIndex, const AZStd::string& seedListfilePath = AZStd::string(), const AZStd::string& asetHintPath = AZStd::string());
private:
AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> GetAllProductDependencies(
const AZ::Data::AssetId& assetId,
const AzFramework::PlatformId& platformIndex,
const AZStd::unordered_set<AZ::Data::AssetId>& exclusionList,
AssetFileDebugInfoList* optionalDebugList = nullptr,
AZStd::unordered_set<AZ::Data::AssetId>* cyclicalDependencySet = nullptr,
const AZStd::vector<AZStd::string>& wildcardPatternExclusionList = AZStd::vector<AZStd::string>()) const;
AZStd::string AddAssetToSeedListHelper(const AZStd::string& assetPath);
void PopulateAssetTypeMap();
AzFramework::AssetSeedList m_assetSeedList;
AZStd::unordered_map<AZStd::string, AZStd::string> m_sourceAssetTypeToRuntimeAssetTypeMap;
AZStd::unordered_map<AzFramework::PlatformFlags, AZStd::string> m_platformFlagsToReadablePlatformList;
};
/*
* Provides a mechanism to interface with the AssetSeedManager
*/
class AssetSeedManagerRequests : public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<AssetSeedManagerRequests>;
using AssetTypePairs = AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>;
//! Systems may provide source and destination asset mapping by file extensions
virtual AssetTypePairs GetAssetTypeMapping()
{
return {};
}
};
} // namespace AzToolsFramework
@@ -0,0 +1,712 @@
/*
* 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 <AzCore/IO/FileIO.h>
#include <AzFramework/Asset/AssetProcessorMessages.h>
#include <AzFramework/Network/AssetProcessorConnection.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Asset/AssetProcessorMessages.h>
#include <AzToolsFramework/Asset/AssetSystemComponent.h>
#include <AzToolsFramework/AssetEditor/AssetEditorBus.h>
#include <AzCore/PlatformIncl.h>
namespace AzToolsFramework
{
namespace AssetSystem
{
void OnAssetSystemMessage(unsigned int /*typeId*/, const void* buffer, unsigned int bufferSize, AZ::SerializeContext* context)
{
SourceFileNotificationMessage message;
if (!AZ::Utils::LoadObjectFromBufferInPlace(buffer, bufferSize, message, context))
{
AZ_TracePrintf("AssetSystem", "Problem deserializing SourceFileNotificationMessage");
return;
}
switch (message.m_type)
{
case SourceFileNotificationMessage::FileChanged:
AssetSystemBus::QueueBroadcast(&AssetSystemBus::Events::SourceFileChanged,
message.m_relativeSourcePath, message.m_scanFolder, message.m_sourceUUID);
break;
case SourceFileNotificationMessage::FileRemoved:
AssetSystemBus::QueueBroadcast(&AssetSystemBus::Events::SourceFileRemoved,
message.m_relativeSourcePath, message.m_scanFolder, message.m_sourceUUID);
break;
case SourceFileNotificationMessage::FileFailed :
AssetSystemBus::QueueBroadcast(&AssetSystemBus::Events::SourceFileFailed,
message.m_relativeSourcePath, message.m_scanFolder, message.m_sourceUUID);
break;
default:
AZ_TracePrintf("AssetSystem", "Unknown SourceFileNotificationMessage type");
break;
}
}
void OnAssetBrowserShowRequest(const void* buffer, unsigned int bufferSize)
{
AssetBrowserShowRequest message;
if (!AZ::Utils::LoadObjectFromBufferInPlace(buffer, bufferSize, message))
{
AZ_TracePrintf("AssetSystem", "Problem deserializing AssetBrowserShowRequest");
return;
}
QString absolutePath = QString::fromUtf8(message.m_filePath.data());
AZStd::function<void()> finalizeOnMainThread = [absolutePath]()
{
AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::SelectAsset, absolutePath);
};
AZ::SystemTickBus::QueueFunction(finalizeOnMainThread);
}
AZ::Outcome<AssetSystem::JobInfoContainer> SendAssetJobsRequest(AssetJobsInfoRequest request, AssetJobsInfoResponse &response)
{
if (!SendRequest(request, response))
{
bool hasAssetId = request.m_assetId.IsValid();
bool hasSearchTerm = !request.m_searchTerm.empty();
if(hasAssetId)
{
AZ_Error("Editor", false, "GetAssetJobsInfo request failed for AssetId: %s", request.m_assetId.ToString<AZStd::string>().c_str());
}
else if(hasSearchTerm)
{
AZ_Error("Editor", false, "GetAssetJobsInfo request failed for search term: %s", request.m_searchTerm.c_str());
}
else
{
AZ_Error("Editor", false, "GetAssetJobsInfo request failed, no AssetId or search term was provided");
}
return AZ::Failure();
}
if (response.m_isSuccess)
{
return AZ::Success(response.m_jobList);
}
return AZ::Failure();
}
void AssetSystemComponent::Activate()
{
AZ::SerializeContext* context = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
AzFramework::SocketConnection* socketConn = AzFramework::SocketConnection::GetInstance();
AZ_Assert(socketConn, "AzToolsFramework::AssetSystem::AssetSystemComponent requires a valid socket conection!");
if (socketConn)
{
m_cbHandle = socketConn->AddMessageHandler(AZ_CRC("AssetProcessorManager::SourceFileNotification", 0x8bfc4d1c),
[context](unsigned int typeId, unsigned int /*serial*/, const void* data, unsigned int dataLength)
{
OnAssetSystemMessage(typeId, data, dataLength, context);
});
m_showAssetBrowserCBHandle = socketConn->AddMessageHandler(AssetSystem::AssetBrowserShowRequest::MessageType,
[](unsigned int /*typeId*/, unsigned int /*serial*/, const void* data, unsigned int dataLength)
{
OnAssetBrowserShowRequest(data, dataLength);
});
m_wantShowAssetBrowserCBHandle = socketConn->AddMessageHandler(AssetSystem::WantAssetBrowserShowRequest::MessageType,
[](unsigned int /*typeId*/, unsigned int serial, const void* data, unsigned int dataLength)
{
Q_UNUSED(data);
Q_UNUSED(dataLength);
AssetSystem::WantAssetBrowserShowResponse message;
#ifdef AZ_PLATFORM_WINDOWS
message.m_processId = GetCurrentProcessId();
#endif // #ifdef AZ_PLATFORM_WINDOWS
SendResponse(message, serial);
});
}
AssetSystemBus::AllowFunctionQueuing(true);
AssetSystemRequestBus::Handler::BusConnect();
AssetSystemJobRequestBus::Handler::BusConnect();
AzToolsFramework::ToolsAssetSystemBus::Handler::BusConnect();
AZ::SystemTickBus::Handler::BusConnect();
}
void AssetSystemComponent::Deactivate()
{
AZ::SystemTickBus::Handler::BusDisconnect();
AzToolsFramework::ToolsAssetSystemBus::Handler::BusDisconnect();
AssetSystemJobRequestBus::Handler::BusDisconnect();
AssetSystemRequestBus::Handler::BusDisconnect();
AzFramework::SocketConnection* socketConn = AzFramework::SocketConnection::GetInstance();
AZ_Assert(socketConn, "AzToolsFramework::AssetSystem::AssetSystemComponent requires a valid socket conection!");
if (socketConn)
{
socketConn->RemoveMessageHandler(AssetSystem::WantAssetBrowserShowRequest::MessageType, m_wantShowAssetBrowserCBHandle);
socketConn->RemoveMessageHandler(AssetSystem::AssetBrowserShowRequest::MessageType, m_showAssetBrowserCBHandle);
socketConn->RemoveMessageHandler(AZ_CRC("AssetProcessorManager::SourceFileNotification", 0x8bfc4d1c), m_cbHandle);
}
AssetSystemBus::AllowFunctionQueuing(false);
AssetSystemBus::ClearQueuedEvents();
}
void AssetSystemComponent::Reflect(AZ::ReflectContext* context)
{
//source file
SourceFileNotificationMessage::Reflect(context);
// Requests
AssetJobsInfoRequest::Reflect(context);
AssetJobLogRequest::Reflect(context);
GetAbsoluteAssetDatabaseLocationRequest::Reflect(context);
GetScanFoldersRequest::Reflect(context);
GetAssetSafeFoldersRequest::Reflect(context);
AssetProcessorPlatformStatusRequest::Reflect(context);
AssetProcessorPendingPlatformAssetsRequest::Reflect(context);
WantAssetBrowserShowRequest::Reflect(context);
AssetBrowserShowRequest::Reflect(context);
SourceAssetProductsInfoRequest::Reflect(context);
// Responses
AssetJobsInfoResponse::Reflect(context);
AssetJobLogResponse::Reflect(context);
GetAbsoluteAssetDatabaseLocationResponse::Reflect(context);
GetScanFoldersResponse::Reflect(context);
GetAssetSafeFoldersResponse::Reflect(context);
AssetProcessorPlatformStatusResponse::Reflect(context);
AssetProcessorPendingPlatformAssetsResponse::Reflect(context);
WantAssetBrowserShowResponse::Reflect(context);
SourceAssetProductsInfoResponse::Reflect(context);
//JobInfo
AzToolsFramework::AssetSystem::JobInfo::Reflect(context);
//AssetSystemComponent
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AssetSystemComponent, AZ::Component>()
;
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<AssetEditor::AssetEditorRequestsBus>("AssetEditorRequestsBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "editor")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Event("CreateNewAsset", &AssetEditor::AssetEditorRequests::CreateNewAsset)
->Event("OpenAssetEditorById", &AssetEditor::AssetEditorRequests::OpenAssetEditorById)
;
behaviorContext->EBus<AssetEditor::AssetEditorWidgetRequestsBus>("AssetEditorWidgetRequestsBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "editor")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Event("CreateAsset", &AssetEditor::AssetEditorWidgetRequests::CreateAsset)
->Event("SaveAssetAs", &AssetEditor::AssetEditorWidgetRequests::SaveAssetAs)
->Event("OpenAssetById", &AssetEditor::AssetEditorWidgetRequests::OpenAssetById)
;
}
}
void AssetSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("AssetProcessorToolsConnection", 0x734669bc));
}
void AssetSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("AssetProcessorToolsConnection", 0x734669bc));
}
void AssetSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("AssetProcessorConnection", 0xf0cd75cd));
}
bool AssetSystemComponent::GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& outputPath)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
outputPath = fullPath;
return false;
}
AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathRequest request(fullPath);
AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetAssetId request for %s", fullPath.c_str());
outputPath = fullPath;
return false;
}
outputPath = response.m_relativeProductPath;
return response.m_resolved;
}
bool AssetSystemComponent::GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullPath)
{
auto foundIt = m_assetSourceRelativePathToFullPathCache.find(relPath);
if (foundIt != m_assetSourceRelativePathToFullPathCache.end())
{
fullPath = foundIt->second;
return true;
}
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
fullPath = "";
return false;
}
AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathRequest request(relPath);
AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetAssetPath request for %s", relPath.c_str());
fullPath = "";
return false;
}
if (response.m_resolved)
{
fullPath = response.m_fullSourcePath;
m_assetSourceRelativePathToFullPathCache[relPath] = fullPath;
return true;
}
else
{
fullPath = "";
return false;
}
}
bool AssetSystemComponent::GetAbsoluteAssetDatabaseLocation(AZStd::string& result)
{
result = "";
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return false;
}
AzToolsFramework::AssetSystem::GetAbsoluteAssetDatabaseLocationRequest request;
AzToolsFramework::AssetSystem::GetAbsoluteAssetDatabaseLocationResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetAbsoluteAssetDatabaseLocation request");
return false;
}
if (response.m_isSuccess)
{
result = response.m_absoluteAssetDatabaseLocation;
return true;
}
else
{
return false;
}
}
const char* AssetSystemComponent::GetAbsoluteDevGameFolderPath()
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
if (fileIO)
{
return fileIO->GetAlias("@devassets@");
}
return "";
}
const char* AssetSystemComponent::GetAbsoluteDevRootFolderPath()
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
if (fileIO)
{
return fileIO->GetAlias("@devroot@");
}
return "";
}
void AssetSystemComponent::OnSystemTick()
{
AssetSystemBus::ExecuteQueuedEvents();
}
bool AssetSystemComponent::GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& /*platformName*/, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
assetInfo.m_assetId.SetInvalid();
assetInfo.m_assetType = AZ::Data::s_invalidAssetType;
if (!engineConnection || !engineConnection->IsConnected())
{
return false;
}
AzFramework::AssetSystem::SourceAssetInfoRequest request(assetId, assetType);
AzFramework::AssetSystem::SourceAssetInfoResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetAssetInfoById request for %s", assetId.ToString<AZStd::string>().c_str());
return false;
}
if (response.m_found)
{
assetInfo = response.m_assetInfo;
rootFilePath = response.m_rootFolder;
return true;
}
return false;
}
bool AssetSystemComponent::GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
assetInfo.m_assetId.SetInvalid();
assetInfo.m_assetType = AZ::Data::s_invalidAssetType;
if (!engineConnection || !engineConnection->IsConnected())
{
return false;
}
AzFramework::AssetSystem::SourceAssetInfoRequest request(sourcePath);
AzFramework::AssetSystem::SourceAssetInfoResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetSourceInfoBySourcePath request for %s", sourcePath);
return false;
}
if (response.m_found)
{
assetInfo = response.m_assetInfo;
watchFolder = response.m_rootFolder;
}
return response.m_found;
}
bool AssetSystemComponent::GetSourceInfoBySourceUUID(const AZ::Uuid& sourceUuid, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
assetInfo.m_assetId.SetInvalid();
assetInfo.m_assetType = AZ::Data::s_invalidAssetType;
if (!engineConnection || !engineConnection->IsConnected())
{
return false;
}
AzFramework::AssetSystem::SourceAssetInfoRequest request(sourceUuid, AZ::Uuid::CreateNull());
AzFramework::AssetSystem::SourceAssetInfoResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetSourceInfoBySourceUUID request for uuid: %s", sourceUuid.ToString<AZ::OSString>().c_str());
return false;
}
if (response.m_found)
{
assetInfo = response.m_assetInfo;
watchFolder = response.m_rootFolder;
}
return response.m_found;
}
bool AssetSystemComponent::GetAssetsProducedBySourceUUID(const AZ::Uuid& sourceUuid, AZStd::vector<AZ::Data::AssetInfo>& productsAssetInfo)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return false;
}
SourceAssetProductsInfoRequest request(sourceUuid);
SourceAssetProductsInfoResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetAssetsProducedBySourceUUID request for uuid: %s", sourceUuid.ToString<AZ::OSString>().c_str());
return false;
}
if (response.m_found)
{
productsAssetInfo = response.m_productsAssetInfo;
}
return response.m_found;
}
bool AssetSystemComponent::GetScanFolders(AZStd::vector<AZStd::string>& scanFolders)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return false;
}
GetScanFoldersRequest request;
GetScanFoldersResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetScanFolders request");
return false;
}
scanFolders.insert(scanFolders.end(), response.m_scanFolders.begin(), response.m_scanFolders.end());
return !response.m_scanFolders.empty();
}
bool AssetSystemComponent::GetAssetSafeFolders(AZStd::vector<AZStd::string>& assetSafeFolders)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return false;
}
GetAssetSafeFoldersRequest request;
GetAssetSafeFoldersResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetScanFolders request");
return false;
}
assetSafeFolders.insert(assetSafeFolders.end(), response.m_assetSafeFolders.begin(), response.m_assetSafeFolders.end());
return !response.m_assetSafeFolders.empty();
}
bool AssetSystemComponent::IsAssetPlatformEnabled(const char* platform)
{
AssetProcessorPlatformStatusRequest request;
request.m_platform = platform;
AssetProcessorPlatformStatusResponse response;
if (!SendRequest(request, response))
{
return false;
}
return response.m_isPlatformEnabled;
}
int AssetSystemComponent::GetPendingAssetsForPlatform(const char* platform)
{
AssetProcessorPendingPlatformAssetsRequest request;
request.m_platform = platform;
AssetProcessorPendingPlatformAssetsResponse response;
if (!SendRequest(request, response))
{
return -1;
}
return response.m_numberOfPendingJobs;
}
AZ::Outcome<AssetSystem::JobInfoContainer> AssetSystemComponent::GetAssetJobsInfo(const AZStd::string& path, const bool escalateJobs)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return AZ::Failure();
}
AssetJobsInfoRequest request(path);
request.m_escalateJobs = escalateJobs;
AssetJobsInfoResponse response;
return SendAssetJobsRequest(request, response);
}
AZ::Outcome<AssetSystem::JobInfoContainer> AssetSystemComponent::GetAssetJobsInfoByAssetID(const AZ::Data::AssetId& assetId, const bool escalateJobs, bool requireFencing = true)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return AZ::Failure();
}
AssetJobsInfoRequest request(assetId, escalateJobs, requireFencing);
AssetJobsInfoResponse response;
return SendAssetJobsRequest(request, response);
}
AZ::Outcome<JobStatus> AssetSystemComponent::GetAssetJobsStatusByJobKey(const AZStd::string& jobKey, const bool escalateJobs)
{
// Strategy
// First find all jobInfos for the inputted jobkey.
// if there is no jobKey than return missing.
// Otherwise if even one job failed than return failed.
// If none of the job failed than check to see whether any job status are queued or inprogress, if yes than return the appropriate status.
// Otherwise return completed status
AZ::Outcome<AzToolsFramework::AssetSystem::JobInfoContainer> result = AZ::Failure();
result = GetAssetJobsInfoByJobKey(jobKey, escalateJobs);
if (!result.IsSuccess())
{
return AZ::Failure();
}
AzToolsFramework::AssetSystem::JobInfoContainer& jobInfos = result.GetValue();
if (!jobInfos.size())
{
return AZ::Success(JobStatus::Missing);
}
bool isAnyJobInProgress = false;
for (const AzToolsFramework::AssetSystem::JobInfo& jobInfo : jobInfos)
{
if (jobInfo.m_status == JobStatus::Failed)
{
return AZ::Success(JobStatus::Failed);
}
else if (jobInfo.m_status == JobStatus::Queued)
{
return AZ::Success(JobStatus::Queued);
}
else if (jobInfo.m_status == JobStatus::InProgress)
{
isAnyJobInProgress = true;
}
}
if (isAnyJobInProgress)
{
return AZ::Success(JobStatus::InProgress);
}
return AZ::Success(JobStatus::Completed);
}
AZ::Outcome<AssetSystem::JobInfoContainer> AssetSystemComponent::GetAssetJobsInfoByJobKey(const AZStd::string& jobKey, const bool escalateJobs)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return AZ::Failure();
}
AssetJobsInfoRequest request(jobKey);
request.m_isSearchTermJobKey = true;
request.m_escalateJobs = escalateJobs;
AssetJobsInfoResponse response;
return SendAssetJobsRequest(request, response);
}
AZ::Outcome<AZStd::string> AssetSystemComponent::GetJobLog(AZ::u64 jobrunkey)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return AZ::Failure();
}
AssetJobLogRequest request(jobrunkey);
AssetJobLogResponse response;
if (!SendRequest(request, response))
{
AZ_Error("Editor", false, "Failed to send GetJobLog Info for jobrunkey: %llu", jobrunkey);
return AZ::Failure();
}
if (response.m_isSuccess)
{
return AZ::Success(response.m_jobLog);
}
return AZ::Failure();
}
void AssetSystemComponent::SourceFileChanged(AZStd::string relativePath, AZStd::string /*scanFolder*/, AZ::Uuid /*sourceUUID*/)
{
m_assetSourceRelativePathToFullPathCache.erase(relativePath);
}
void AssetSystemComponent::SourceFileRemoved(AZStd::string relativePath, AZStd::string /*scanFolder*/, AZ::Uuid /*sourceUUID*/)
{
m_assetSourceRelativePathToFullPathCache.erase(relativePath);
}
void AssetSystemComponent::SourceFileFailed(AZStd::string relativePath, AZStd::string /*scanFolder*/, AZ::Uuid /*sourceUUID*/)
{
m_assetSourceRelativePathToFullPathCache.erase(relativePath);
}
void AssetSystemComponent::RegisterSourceAssetType(const AZ::Data::AssetType& assetType, const char* assetFileFilter)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return;
}
AzFramework::AssetSystem::RegisterSourceAssetRequest request(assetType, assetFileFilter);
if (!SendRequest(request))
{
AZ_Error("Editor", false, "Failed to send RegisterSourceAssetType request for asset type %s", assetType.ToString<AZStd::string>().c_str());
}
}
void AssetSystemComponent::UnregisterSourceAssetType(const AZ::Data::AssetType& assetType)
{
AzFramework::SocketConnection* engineConnection = AzFramework::SocketConnection::GetInstance();
if (!engineConnection || !engineConnection->IsConnected())
{
return;
}
AzFramework::AssetSystem::UnregisterSourceAssetRequest request(assetType);
if (!SendRequest(request))
{
AZ_Error("Editor", false, "Failed to send UnregisterSourceAssetType request for asset type %s", assetType.ToString<AZStd::string>().c_str());
}
}
} // namespace AssetSystem
} // namespace AzToolsFramework
@@ -0,0 +1,114 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/TickBus.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/Component/Component.h>
#include <AzFramework/Network/SocketConnection.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/ToolsComponents/ToolsAssetCatalogBus.h>
namespace AzToolsFramework
{
namespace AssetSystem
{
/**
* A tools level component for interacting with the asset processor
*
* Currently used to translate between full and relative asset paths,
* and to query information about asset processor jobs
*/
class AssetSystemComponent
: public AZ::Component
, private AzToolsFramework::AssetSystemRequestBus::Handler
, private AzToolsFramework::AssetSystemJobRequestBus::Handler
, private AzToolsFramework::AssetSystemBus::Handler
, private AzToolsFramework::ToolsAssetSystemBus::Handler
, private AZ::SystemTickBus::Handler
{
public:
AZ_COMPONENT(AssetSystemComponent, "{B1352D59-945B-446A-A7E1-B2D3EB717C6D}")
AssetSystemComponent() = default;
virtual ~AssetSystemComponent() = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component overrides
void Init() override {}
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
private:
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::AssetSystemRequestBus::Handler overrides
bool GetAbsoluteAssetDatabaseLocation(AZStd::string& result) override;
const char* GetAbsoluteDevGameFolderPath() override;
const char* GetAbsoluteDevRootFolderPath() override;
bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& outputPath) 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;
bool GetSourceInfoBySourceUUID(const AZ::Uuid& sourceUuid, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override;
bool GetScanFolders(AZStd::vector<AZStd::string>& scanFolders) override;
bool GetAssetSafeFolders(AZStd::vector<AZStd::string>& assetSafeFolders) override;
bool IsAssetPlatformEnabled(const char* platform) override;
int GetPendingAssetsForPlatform(const char* platform) override;
bool GetAssetsProducedBySourceUUID(const AZ::Uuid& sourceUuid, AZStd::vector<AZ::Data::AssetInfo>& productsAssetInfo) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::AssetSystemJobRequest::Bus::Handler overrides
virtual AZ::Outcome<AssetSystem::JobInfoContainer> GetAssetJobsInfo(const AZStd::string& path, const bool escalateJobs) override;
virtual AZ::Outcome<JobInfoContainer> GetAssetJobsInfoByAssetID(const AZ::Data::AssetId& assetId, const bool escalateJobs, bool requireFencing) override;
virtual AZ::Outcome<JobInfoContainer> GetAssetJobsInfoByJobKey(const AZStd::string& jobKey, const bool escalateJobs) override;
virtual AZ::Outcome<JobStatus> GetAssetJobsStatusByJobKey(const AZStd::string& jobKey, const bool escalateJobs) override;
virtual AZ::Outcome<AZStd::string> GetJobLog(AZ::u64 jobrunkey) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::::AssetSystemBus::Handler overrides
void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override;
void SourceFileRemoved(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override;
void SourceFileFailed(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::ToolsAssetSystemBus::Handler overrides
void RegisterSourceAssetType(const AZ::Data::AssetType& assetType, const char* assetFileFilter) override;
void UnregisterSourceAssetType(const AZ::Data::AssetType& assetType) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SystemTickBus::Handler overrides
void OnSystemTick() override;
//////////////////////////////////////////////////////////////////////////
AzFramework::SocketConnection::TMessageCallbackHandle m_cbHandle = 0;
AzFramework::SocketConnection::TMessageCallbackHandle m_showAssetBrowserCBHandle = 0;
AzFramework::SocketConnection::TMessageCallbackHandle m_wantShowAssetBrowserCBHandle = 0;
AZStd::unordered_map<AZStd::string, AZStd::string> m_assetSourceRelativePathToFullPathCache;
};
} // namespace AssetSystem
} // namespace AzToolsFramework
@@ -0,0 +1,460 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Module/ModuleManagerBus.h>
#include <AzCore/Module/DynamicModuleHandle.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Asset/AssetUtils.h>
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/error/en.h>
AZ_PUSH_DISABLE_WARNING(4127 4251 4800, "-Wunknown-warning-option")
#include <QDir>
#include <QFile>
#include <QSettings>
#include <QDirIterator>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetUtils
{
namespace Internal
{
const char AssetConfigPlatformDir[] = "AssetProcessorConfig";
const char RestrictedPlatformDir[] = "restricted";
QStringList FindWildcardMatches(const QString& sourceFolder, QString relativeName)
{
if (relativeName.isEmpty())
{
return QStringList();
}
const int pathLen = sourceFolder.length() + 1;
relativeName.replace('\\', '/');
QStringList returnList;
QRegExp nameMatch{ relativeName, Qt::CaseInsensitive, QRegExp::Wildcard };
QDirIterator diretoryIterator(sourceFolder, QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
QStringList files;
while (diretoryIterator.hasNext())
{
diretoryIterator.next();
if (!diretoryIterator.fileInfo().isFile())
{
continue;
}
QString pathMatch{ diretoryIterator.filePath().mid(pathLen) };
if (nameMatch.exactMatch(pathMatch))
{
returnList.append(diretoryIterator.filePath());
}
}
return returnList;
}
void ReadPlatformInfosFromConfigFile(QString configFile, QStringList& enabledPlatforms)
{
// in the inifile the platform can be missing (commented out)
// in which case it is disabled implicitly by not being there
// or it can be 'disabled' which means that it is explicitly disabled.
// or it can be 'enabled' which means that it is explicitly enabled.
if (!QFile::exists(configFile))
{
return;
}
QSettings loader(configFile, QSettings::IniFormat);
// Read in enabled platforms
loader.beginGroup("Platforms");
QStringList keys = loader.allKeys();
for (int idx = 0; idx < keys.count(); idx++)
{
QString val = loader.value(keys[idx]).toString();
QString platform = keys[idx].toLower().trimmed();
val = val.toLower().trimmed();
if (val == "enabled")
{
if (!enabledPlatforms.contains(val))
{
enabledPlatforms.push_back(platform);
}
}
else if (val == "disabled")
{
// disable platform explicitly.
int index = enabledPlatforms.indexOf(platform);
if (index != -1)
{
enabledPlatforms.removeAt(index);
}
}
}
loader.endGroup();
}
void AddGemConfigFiles(const AZStd::vector<AzToolsFramework::AssetUtils::GemInfo>& gemInfoList, QStringList& configFiles)
{
// there can only be one gam gem per project, so if we find one, we cache the name of it so that
// later we can add it to the very end of the list, giving it the ability to override all other config files.
QString gameConfigPath;
for (const GemInfo& gemElement : gemInfoList)
{
QString gemAbsolutePath(gemElement.m_absoluteFilePath.c_str());
QDir gemDir(gemAbsolutePath);
QString absPathToConfigFile = gemDir.absoluteFilePath("AssetProcessorGemConfig.ini");
if (gemElement.m_isGameGem)
{
gameConfigPath = absPathToConfigFile;
}
else
{
configFiles.push_back(absPathToConfigFile);
}
}
// if a 'game gem' was discovered during the above loop, we want to append it to the END of the list
if (!gameConfigPath.isEmpty())
{
configFiles.push_back(gameConfigPath);
}
}
bool AddPlatformConfigFilePaths(const char* root, QStringList& configFilePaths)
{
QString configWildcardName{ "*" };
configWildcardName.append(AssetProcessorPlatformConfigFileName);
QDir sourceRoot(root);
// first collect public platform configs
QStringList platformList = FindWildcardMatches(sourceRoot.filePath(AssetConfigPlatformDir), configWildcardName);
// then collect restricted platform configs
QDirIterator it(sourceRoot.filePath(RestrictedPlatformDir), QDir::NoDotAndDotDot | QDir::Dirs);
while (it.hasNext())
{
QDir platformDir(it.next());
platformList << FindWildcardMatches(platformDir.filePath(AssetConfigPlatformDir), configWildcardName);
}
for (const auto& thisConfig : platformList)
{
configFilePaths.append(thisConfig);
}
return (platformList.size() > 0);
}
}
const char* AssetProcessorPlatformConfigFileName = "AssetProcessorPlatformConfig.ini";
const char* AssetProcessorGamePlatformConfigFileName = "AssetProcessorGamePlatformConfig.ini";
const char GemsDirectoryName[] = "Gems";
GemInfo::GemInfo(AZStd::string name, AZStd::string relativeFilePath, AZStd::string absoluteFilePath, AZStd::string identifier, bool isGameGem, bool assetOnlyGem)
: m_gemName(name)
, m_relativeFilePath(relativeFilePath)
, m_absoluteFilePath(absoluteFilePath)
, m_identifier(identifier)
, m_isGameGem(isGameGem)
, m_assetOnly(assetOnlyGem)
{
}
QStringList GetEnabledPlatforms(QStringList configFiles)
{
QStringList enabledPlatforms;
// note that the current host platform is enabled by default.
enabledPlatforms.push_back(AzToolsFramework::AssetSystem::GetHostAssetPlatform());
for (const QString& configFile : configFiles)
{
Internal::ReadPlatformInfosFromConfigFile(configFile, enabledPlatforms);
}
return enabledPlatforms;
}
QStringList GetConfigFiles(const char* root, const char* assetRoot, const char* gameName, bool addPlatformConfigs, bool addGemsConfigs)
{
QStringList configFiles;
QDir configRoot(root);
QString rootConfigFile = configRoot.filePath(AssetProcessorPlatformConfigFileName);
configFiles.push_back(rootConfigFile);
if (addPlatformConfigs)
{
Internal::AddPlatformConfigFilePaths(root, configFiles);
}
if (addGemsConfigs)
{
AZStd::vector<AzToolsFramework::AssetUtils::GemInfo> gemInfoList;
if (!GetGemsInfo(root, assetRoot, gameName, gemInfoList))
{
AZ_Error("AzToolsFramework::AssetUtils", false, "Failed to read gems for game project (%s).\n", gameName);
return {};
}
Internal::AddGemConfigFiles(gemInfoList, configFiles);
}
QDir assetRootDir(assetRoot);
assetRootDir.cd(gameName);
QString projectConfigFile = assetRootDir.filePath(AssetProcessorGamePlatformConfigFileName);
configFiles.push_back(projectConfigFile);
return configFiles;
}
static GemInfo ParseGemInfo(const rapidjson::Document& gemJsonDocument)
{
constexpr AZStd::fixed_string<32> NameKey = "Name";
constexpr AZStd::fixed_string<32> UuidKey = "Uuid";
constexpr AZStd::fixed_string<32> LinkTypeKey = "LinkType";
constexpr AZStd::fixed_string<32> IsGameGemKey = "IsGameGem";
GemInfo gemInfo;
auto memberIter = gemJsonDocument.FindMember(NameKey.c_str());
if (memberIter != gemJsonDocument.MemberEnd())
{
gemInfo.m_gemName.assign(memberIter->value.GetString(), memberIter->value.GetStringLength());
}
memberIter = gemJsonDocument.FindMember(UuidKey.c_str());
if (memberIter != gemJsonDocument.MemberEnd())
{
gemInfo.m_identifier.assign(memberIter->value.GetString(), memberIter->value.GetStringLength());
}
memberIter = gemJsonDocument.FindMember(IsGameGemKey.c_str());
if (memberIter != gemJsonDocument.MemberEnd())
{
gemInfo.m_isGameGem = memberIter->value.GetBool();
}
AZStd::string_view linkTypeString;
memberIter = gemJsonDocument.FindMember(LinkTypeKey.c_str());
if (memberIter != gemJsonDocument.MemberEnd())
{
linkTypeString = AZStd::string_view{ memberIter->value.GetString(), memberIter->value.GetStringLength() };
}
gemInfo.m_assetOnly = linkTypeString == "NoCode";
return gemInfo;
}
bool GetGemsInfo([[maybe_unused]] const char* root, [[maybe_unused]] const char* assetRoot, [[maybe_unused]] const char* gameName, AZStd::vector<GemInfo>& gemInfoList)
{
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry == nullptr)
{
AZ_Error("AzToolsFramework::AssetUtils", false, "Settings Registry does not exist, cannot retrieve information about loaded Gem modules");
return false;
}
auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
if (fileIoBase == nullptr)
{
AZ_Error("AzToolsFramework::AssetUtils", false, "File IO, cannot retrieve information about loaded Gem modules");
return false;
}
constexpr AZStd::string_view GemJsonFilename{ "gem.json" };
AZStd::vector<AZ::IO::FixedMaxPath> gemModuleSourcePaths;
struct GemSourcePathsVisitor
: AZ::SettingsRegistryInterface::Visitor
{
GemSourcePathsVisitor(AZStd::vector<AZ::IO::FixedMaxPath>& gemSourcePaths)
: m_gemSourcePaths(gemSourcePaths)
{}
void Visit(AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName, [[maybe_unused]] AZ::SettingsRegistryInterface::Type type,
AZStd::string_view value) override
{
if (path.find("SourcePaths") != AZStd::string_view::npos
&& AZStd::find(m_gemSourcePaths.begin(), m_gemSourcePaths.end(), value) == m_gemSourcePaths.end())
{
m_gemSourcePaths.emplace_back(value);
}
}
AZStd::vector<AZ::IO::FixedMaxPath>& m_gemSourcePaths;
};
GemSourcePathsVisitor visitor{ gemModuleSourcePaths };
const auto gemListKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/Gems", AZ::SettingsRegistryMergeUtils::OrganizationRootKey);
AZ::SettingsRegistry::Get()->Visit(visitor, gemListKey);
AZ::IO::FixedMaxPath engineRootPath;
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
if (engineRootPath.empty())
{
AZ_TracePrintfOnce("AzToolsFramework::AssetUtils", "Engine Root Path is empty. The Gem Module Source Paths will have the @engroot@ alias prepended");
engineRootPath = "@engroot@";
}
AZStd::vector<char> gemJsonFileData;
for (const AZ::IO::FixedMaxPath& gemSourcePath : gemModuleSourcePaths)
{
AZ::IO::FixedMaxPath gemJsonPath = engineRootPath / gemSourcePath / GemJsonFilename;
if (AZ::IO::HandleType gemJsonHandle; fileIoBase->Open(gemJsonPath.c_str(), AZ::IO::OpenMode::ModeRead, gemJsonHandle))
{
AZ::IO::SizeType gemJsonFileSize;
if (AZ::IO::Result ioResult = fileIoBase->Size(gemJsonHandle, gemJsonFileSize); !ioResult)
{
AZ_Error("AzToolsFramework::AssetUtils", false, "Failed to query file size of gem json at path '%s'.\nResult code %u returned",
gemJsonPath.c_str(), aznumeric_cast<uint32_t>(ioResult.GetResultCode()));
fileIoBase->Close(gemJsonHandle);
continue;
}
gemJsonFileData.resize_no_construct(gemJsonFileSize);
AZ::IO::SizeType bytesRead{};
if (AZ::IO::Result ioResult = fileIoBase->Read(gemJsonHandle, gemJsonFileData.data(), gemJsonFileData.size(), false, &bytesRead); !ioResult)
{
AZ_Error("AzToolsFramework::AssetUtils", false, "Reading from gem json at path '%s' has failed with result code %u.\n%zu has been read",
gemJsonPath.c_str(), aznumeric_cast<uint32_t>(ioResult.GetResultCode()), bytesRead);
fileIoBase->Close(gemJsonHandle);
continue;
}
rapidjson::Document gemJsonDocument;
gemJsonDocument.Parse(gemJsonFileData.data(), gemJsonFileData.size());
if (gemJsonDocument.HasParseError())
{
AZ_Error("AzToolsFramework::AssetUtils", false, "Parsing gem json at path '%s' has json Parse Error: %s",
gemJsonPath.c_str(), rapidjson::GetParseError_En(gemJsonDocument.GetParseError()))
}
GemInfo gemInfo = ParseGemInfo(gemJsonDocument);
char gemJsonResolvePath[AZ::IO::MaxPathLength];
const bool foundFilename = fileIoBase->GetFilename(gemJsonHandle, AZStd::data(gemJsonResolvePath), AZStd::size(gemJsonResolvePath));
if (foundFilename)
{
// Set the Absolute path to the folder containing the gem.json file
gemInfo.m_absoluteFilePath = AZ::IO::PathView(gemJsonResolvePath).ParentPath().Native();
}
else
{
// If unable to retrieve the Filename from the File IO handle the gemJsonPath is used instead
gemInfo.m_absoluteFilePath = gemJsonPath.ParentPath().Native();
}
// The gemSourcePath is the relative path
gemInfo.m_relativeFilePath = static_cast<AZStd::string_view>(gemSourcePath.Native());
gemInfoList.push_back(gemInfo);
fileIoBase->Close(gemJsonHandle);
}
}
return true;
}
bool UpdateFilePathToCorrectCase(const QString& root, QString& relativePathFromRoot)
{
AZStd::string rootPath(root.toUtf8().data());
AZStd::string relPathFromRoot(relativePathFromRoot.toUtf8().data());
AZ::StringFunc::Path::Normalize(relPathFromRoot);
AZStd::vector<AZStd::string> tokens;
AZ::StringFunc::Tokenize(relPathFromRoot.c_str(), tokens, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
AZStd::string validatedPath;
if (rootPath.empty())
{
const char* appRoot = nullptr;
AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
validatedPath = AZStd::string(appRoot);
}
else
{
validatedPath = rootPath;
}
bool success = true;
for (int idx = 0; idx < tokens.size(); idx++)
{
AZStd::string element = tokens[idx];
bool foundAMatch = false;
AZ::IO::FileIOBase::GetInstance()->FindFiles(validatedPath.c_str(), "*", [&](const char* file)
{
if ( idx != tokens.size() - 1 && !AZ::IO::FileIOBase::GetInstance()->IsDirectory(file))
{
// only the last token is supposed to be a filename, we can skip filenames before that
return true;
}
AZStd::string absFilePath(file);
AZ::StringFunc::Path::Normalize(absFilePath);
auto found = absFilePath.rfind(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
size_t startingPos = found + 1;
if (found != AZStd::string::npos && absFilePath.size() > startingPos)
{
AZStd::string componentName = AZStd::string(absFilePath.begin() + startingPos, absFilePath.end());
if (AZ::StringFunc::Equal(componentName.c_str(), tokens[idx].c_str()))
{
tokens[idx] = componentName;
foundAMatch = true;
return false;
}
}
return true;
});
if (!foundAMatch)
{
success = false;
break;
}
AZStd::string absoluteFilePath;
AZ::StringFunc::Path::ConstructFull(validatedPath.c_str(), element.c_str(), absoluteFilePath);
validatedPath = absoluteFilePath; // go one step deeper.
}
if (success)
{
relPathFromRoot.clear();
AZ::StringFunc::Join(relPathFromRoot, tokens.begin(), tokens.end(), AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
relativePathFromRoot = relPathFromRoot.c_str();
}
return success;
}
} //namespace AssetUtils
} //namespace AzToolsFramework
@@ -0,0 +1,72 @@
/*
* 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/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
AZ_PUSH_DISABLE_WARNING(4127 4251 4800, "-Wunknown-warning-option")
#include <QStringList>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetUtils
{
extern const char* AssetProcessorPlatformConfigFileName;
extern const char* AssetProcessorGamePlatformConfigFileName;
//! This struct stores gem related information
struct GemInfo
{
AZ_CLASS_ALLOCATOR(GemInfo, AZ::SystemAllocator, 0);
GemInfo(AZStd::string name, AZStd::string relativeFilePath, AZStd::string absoluteFilePath, AZStd::string identifier, bool isGameGem, bool assetOnlyGem);
GemInfo() = default;
AZStd::string m_gemName; ///< A friendly display name, not to be used for any pathing stuff.
AZStd::string m_relativeFilePath; ///< Where the gem's folder is (relative to the gems search path(s))
AZStd::string m_absoluteFilePath; ///< Where the gem's folder is (as an absolute path)
AZStd::string m_identifier; ///< The UUID of the gem.
bool m_isGameGem = false; //< True if its a 'game project' gem. Only one such gem can exist for any game project.
bool m_assetOnly = false; ///< True if it is an asset only gems.
static AZStd::string GetGemAssetFolder() { return AZStd::string("Assets"); }
};
//! Returns all the enabledPlatforms by reading the specified config files in order.
QStringList GetEnabledPlatforms(QStringList configFiles);
//! Returns all the config files including the the platform and gems ones.
//! Please note that config files are order dependent
//! the root config file has the lowest priority.
//! the project configuration file has the absolutely highest priority
//! Also note that if the project has any "game project gems", then those will also be inserted last,
//! and thus have a higher priority than the root or non - project gems.
//! Also note that the game project could be in a different location to the engine therefore we need the assetRoot param.
QStringList GetConfigFiles(const char* root, const char* assetRoot, const char* gameName, bool addPlatformConfigs = true, bool addGemsConfigs = true);
//! Returns a list of GemInfo of all the gems that are active for the for the specified game project.
//! Please note that the game project could be in a different location to the engine therefore we need the assetRoot param.
bool GetGemsInfo(const char* root, const char* assetRoot, const char* gameName, AZStd::vector<GemInfo>& gemInfoList);
//! A utility function which checks the given path starting at the root and updates the relative path to be the actual case correct path.
//! For example, if you pass it "c:\lumberyard\dev" as the root and "editor\icons\whatever.ico" as the relative path.
//! It may update relativePathFromRoot to be "Editor\Icons\Whatever.ico" if such a casing is the actual physical case on disk already.
//! @param root a trusted already-case-correct path (will not be case corrected). If empty it will be set to appRoot.
//! @param relativePathFromRoot a non-trusted (may be incorrect case) path relative to rootPath,
//! which will be normalized and updated to be correct casing.
//! @return if such a file does NOT exist, it returns FALSE, else returns TRUE.
//! @note A very expensive function! Call sparingly.
bool UpdateFilePathToCorrectCase(const QString& root, QString& relativePathFromRoot);
} //namespace AssetUtils
} //namespace AzToolsFramework
@@ -0,0 +1,294 @@
/*
* 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/EBus/EBus.h>
#include <AzCore/std/function/function_fwd.h>
#include <AzCore/std/string/string.h>
// warning C4251: 'QBrush::d': class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' needs to have dll-interface to be used by clients of class 'QBrush'
AZ_PUSH_DISABLE_WARNING(4127 4251, "-Wunknown-warning-option")
#include <QIcon>
AZ_POP_DISABLE_WARNING
class QMimeData;
class QWidget;
class QImage;
class QMenu;
namespace AZ
{
namespace Data
{
struct AssetId;
}
struct Uuid;
}
namespace AzQtComponents
{
class StyledBusyLabel;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetSelectionModel;
class AssetBrowserModel;
class AssetBrowserEntry;
//////////////////////////////////////////////////////////////////////////
// AssetBrowserComponent
//////////////////////////////////////////////////////////////////////////
class AssetDatabaseLocationNotifications
: public AZ::EBusTraits
{
public:
//! Indicates that the Asset Database has been initialized
virtual void OnDatabaseInitialized() = 0;
};
using AssetDatabaseLocationNotificationBus = AZ::EBus<AssetDatabaseLocationNotifications>;
//! Sends requests to AssetBrowserComponent
class AssetBrowserComponentRequests
: public AZ::EBusTraits
{
public:
// Only a single handler is allowed
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
//! Request File Browser model
virtual AssetBrowserModel* GetAssetBrowserModel() = 0;
//! Returns true if entries were populated
virtual bool AreEntriesReady() = 0;
//! Spawn asset picker window
//! @param selection Selection filter model for asset picker window
//! @param parent Parent widget that previewer will be attached to
virtual void PickAssets(AssetSelectionModel& selection, QWidget* parent) = 0;
virtual AzQtComponents::StyledBusyLabel* GetStyledBusyLabel() = 0;
};
using AssetBrowserComponentRequestBus = AZ::EBus<AssetBrowserComponentRequests>;
//! Sends notifications from AssetBrowserComponent
class AssetBrowserComponentNotifications
: public AZ::EBusTraits
{
public:
//! Notifies when entries are physically populated in asset browser
virtual void OnAssetBrowserComponentReady() {}
};
using AssetBrowserComponentNotificationBus = AZ::EBus<AssetBrowserComponentNotifications>;
//////////////////////////////////////////////////////////////////////////
// Interaction
//////////////////////////////////////////////////////////////////////////
//! This struct is used to respond about being able to open source files.
//! See AssetBrowserInteractionNotifications::OpenSourceFileInEditor below
struct SourceFileOpenerDetails
{
//! You provide a function to call (you may use std-bind to bind to your class) if your opener is chosen to handle the open operation
typedef AZStd::function<void(const char* /*fullSourceFileName*/, const AZ::Uuid& /*source uuid*/)> SourceFileOpenerFunctionType;
AZStd::string m_identifier; ///< choose something unique for your opener. It may be used to restore state. it will not be shown to user.
//! m_displayText is used when more than one listener offers to open this kind of file and
//! we need the user to pick which one they want. They will be offered all the available openers in a menu
//! which shows this text, and the one they pick will get its SourceFileOpenerFunctionType called.
AZStd::string m_displayText;
QIcon m_iconToUse; ///< optional. Same as m_displayText. Used when there's ambiguity. If empty, no icon.
//! This is the function to call. If you fill a nullptr in here, then the default operating system behavior will be suppressed
//! but no opener will be opened. This will also cause the 'open' option in context menus to disappear if the only openers
//! are nullptr ones.
SourceFileOpenerFunctionType m_opener;
SourceFileOpenerDetails() = default;
SourceFileOpenerDetails(const char* identifier, const char* displayText, QIcon icon, SourceFileOpenerFunctionType functionToCall)
: m_identifier(identifier)
, m_displayText(displayText)
, m_iconToUse(icon)
, m_opener(functionToCall) {}
};
typedef AZStd::vector<SourceFileOpenerDetails> SourceFileOpenerList;
//! used by the API to (optionally) let systems describe details about source files
//! see /ref AssetBrowserInteractionNotifications to see how it is used.
//! The intended behavior of this is that listeners respond with a SourceFileDetails struct
//! which has only the field(s) filled in which they can fill in, and the system combines all responses
//! to fill in details from many systems.
//! More fields may be added to SourceFileDetails as more systems require other kinds of details.
struct SourceFileDetails
{
//! An openable path to a resource that can be used as the thumbnail for this type of file.
//! This can be a Qt resource system string like ":/tools/something.png" for resources embedded in your
//! dlls, or an absolute path, or relative source asset path like "editor/icons/whatever.png"
AZStd::string m_sourceThumbnailPath;
//! Update this constructor or add more constructors if you add fields so that existing ones continue to work.
SourceFileDetails(const char* thumbnailPath)
{
m_sourceThumbnailPath = thumbnailPath;
}
//! this is the function that will be used to "fold" multiple returned values from other results onto
//! one canonical result containing all the result fields. This function gets called repeatedly, once
//! for every responding listener and can be used to avoid allocations.
SourceFileDetails& operator=(SourceFileDetails&& other)
{
if (this != &other)
{
if (m_sourceThumbnailPath.empty())
{
m_sourceThumbnailPath = AZStd::move(other.m_sourceThumbnailPath);
}
}
return *this;
}
////////////////////////////////// boilerplate below here ///////////////////////////
SourceFileDetails() = default;
SourceFileDetails(const SourceFileDetails& other) = default;
SourceFileDetails(SourceFileDetails&& other)
{
if (this != &other)
{
*this = AZStd::move(other); // forward this to the below operator=&&
}
}
SourceFileDetails& operator=(const SourceFileDetails& other) = default;
};
//! Bus for interaction with asset browser widget
class AssetBrowserInteractionNotifications
: public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<AssetBrowserInteractionNotifications>;
typedef AZStd::recursive_mutex MutexType;
//! Override this to get first attempt at handling these messages. Higher priority goes first.
virtual AZ::s32 GetPriority() const { return 0; }
//! Notification that a context menu is about to be shown and offers an opportunity to add actions.
virtual void AddContextMenuActions(QWidget* /*caller*/, QMenu* /*menu*/, const AZStd::vector<AssetBrowserEntry*>& /*entries*/) {};
//! Implement AddSourceFileOpeners to provide your own editor for source files
//! This gets called to collect the list of available openers for a file.
//! Add your detail(s) to the openers list if you would like to be one of the options available to open the file.
//! You can also add more than one to the list, or check the existing list to determine your behavior.
//! If there is more than one in the list, the user will be given the choice of openers to use.
//! If nobody responds (nobody adds their entry into the openers list), then the default operating system handler
//! will be called (whatever that kind of file is associated with).
virtual void AddSourceFileOpeners(const char* /*fullSourceFileName*/, const AZ::Uuid& /*sourceUUID*/, SourceFileOpenerList& /*openers*/) {}
//! If you have an Asset Entry and would like to try to open it using the associated editor, you can use this bus to do so.
//! Note that you can override this bus with a higher-than-zero priorit handler, and set alreadyHandled to true in your handler
//! to prevent the default behavior from occuring.
//! The default behavior is to call the above function for all handlers of that asset type, to gather the openers that can open it.
//! following that, it either opens it with the opener (if there is only one) or prompts the user for which one to use.
//! If no opener is present it tries to open it using the asset editor.
//! finally, if its not a generic asset, it tries the operating system.
virtual void OpenAssetInAssociatedEditor(const AZ::Data::AssetId& /*assetId*/, bool& /*alreadyHandled*/) {}
//! Allows you to recognise the source files that your plugin cares about and provide information about the source file
//! for display in the Asset Browser. This allows you to override the default behavior if you wish to.
//! note that you'll get SourceFileDetails for every file in view, and you should only return something if its YOUR
//! kind of file that you have details to provide.
virtual SourceFileDetails GetSourceFileDetails(const char* /*fullSourceFileName*/)
{
return SourceFileDetails();
}
//! required in order to sort the busses.
inline bool Compare(const AssetBrowserInteractionNotifications* other) const
{
return GetPriority() > other->GetPriority();
}
};
using AssetBrowserInteractionNotificationBus = AZ::EBus<AssetBrowserInteractionNotifications>;
//////////////////////////////////////////////////////////////////////////
// AssetBrowserModel
//////////////////////////////////////////////////////////////////////////
//! Sends requests to AssetBrowserModel
class AssetBrowserModelRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
//! checks if AssetBrowserModel was updated at least once
virtual bool IsLoaded() const = 0;
virtual void BeginAddEntry(AssetBrowserEntry* parent) = 0;
virtual void EndAddEntry(AssetBrowserEntry* parent) = 0;
virtual void BeginRemoveEntry(AssetBrowserEntry* entry) = 0;
virtual void EndRemoveEntry() = 0;
};
using AssetBrowserModelRequestBus = AZ::EBus<AssetBrowserModelRequests>;
//! Notifies when AssetBrowserModel is updated
class AssetBrowserModelNotifications
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
virtual void EntryAdded(const AssetBrowserEntry* /*entry*/) {}
virtual void EntryRemoved(const AssetBrowserEntry* /*entry*/) {}
};
using AssetBrowserModelNotificationBus = AZ::EBus<AssetBrowserModelNotifications>;
//! Sends requests to the Asset Browser view.
class AssetBrowserViewRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
//! Requests the Asset Browser's view to select the given asset.
//! @param assetID The asset to select.
virtual void SelectProduct(AZ::Data::AssetId assetID) = 0;
/**
* Requests the Asset Browser's view to select the given asset.
* \param assetPath The path (absolute or relative) of the asset to select.
*/
virtual void SelectFileAtPath(const AZStd::string& assetPath) = 0;
virtual void ClearFilter() = 0;
virtual void Update() = 0;
};
using AssetBrowserViewRequestBus = AZ::EBus<AssetBrowserViewRequests>;
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.inl>
@@ -0,0 +1,45 @@
/*
* 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/EBus/EBus.h>
#include <AzCore/std/function/function_fwd.h>
#include <AzCore/std/string/string.h>
#include <QImage>
class QImage;
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserModel;
//! Sends requests to output preview image for texture assets. Used for internal only!
class AssetBrowserTexturePreviewRequests
: public AZ::EBusTraits
{
public:
// Only a single handler is allowed
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
//! Request to get a preview image for texture product
//@return whether the output image is valid or not
virtual bool GetProductTexturePreview(const char* /*fullProductFileName*/, QImage& /*previewImage*/, AZStd::string& /*productInfo*/, AZStd::string& /*productAlphaInfo*/) { return false; }
};
using AssetBrowserTexturePreviewRequestsBus = AZ::EBus<AssetBrowserTexturePreviewRequests>;
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,397 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/base.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/EBus/Results.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetEntryChangeset.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/FolderThumbnail.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/AssetBrowserProductThumbnail.h>
#include <AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.h>
#include <AzToolsFramework/Slice/SliceUtilities.h>
#include <chrono>
#include <QSharedPointer>
#include <QDesktopServices>
#include <QUrl>
#include <QMenu>
#include <Asset/AssetProcessorMessages.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
AssetBrowserComponent::AssetBrowserComponent()
: m_databaseConnection(aznew AssetDatabase::AssetDatabaseConnection)
, m_rootEntry(aznew RootAssetBrowserEntry)
, m_dbReady(false)
, m_waitingForMore(false)
, m_disposed(false)
, m_assetBrowserModel(aznew AssetBrowserModel)
, m_changeset(new AssetEntryChangeset(m_databaseConnection, m_rootEntry))
{
m_assetBrowserModel->SetRootEntry(m_rootEntry);
// Create a single StyledBusyLabel that entries can use while loading.
m_styledBusyLabel = new AzQtComponents::StyledBusyLabel();
m_styledBusyLabel->SetIsBusy(true);
}
AssetBrowserComponent::~AssetBrowserComponent() {}
void AssetBrowserComponent::Activate()
{
m_disposed = false;
m_waitingForMore = false;
m_thread = AZStd::thread(AZStd::bind(&AssetBrowserComponent::UpdateAssets, this));
AssetDatabaseLocationNotificationBus::Handler::BusConnect();
AssetBrowserComponentRequestBus::Handler::BusConnect();
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
AssetSystemBus::Handler::BusConnect();
AssetBrowserInteractionNotificationBus::Handler::BusConnect();
using namespace Thumbnailer;
ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(FolderThumbnailCache), ThumbnailContext::DefaultContext);
ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SourceThumbnailCache), ThumbnailContext::DefaultContext);
ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(ProductThumbnailCache), ThumbnailContext::DefaultContext);
AzFramework::SocketConnection* socketConn = AzFramework::SocketConnection::GetInstance();
AZ_Assert(socketConn, "AzToolsFramework::AssetBrowser::AssetBrowserComponent requires a valid socket conection!");
if (socketConn)
{
m_cbHandle = socketConn->AddMessageHandler(AZ_CRC("FileProcessor::FileInfosNotification", 0x001c43f5),
[this](unsigned int /*typeId*/, unsigned int /*serial*/, const void* buffer, unsigned int bufferSize)
{
HandleFileInfoNotification(buffer, bufferSize);
});
}
}
void AssetBrowserComponent::Deactivate()
{
m_disposed = true;
NotifyUpdateThread();
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
m_thread = AZStd::thread(); // destroy
}
AssetBrowserInteractionNotificationBus::Handler::BusDisconnect();
AssetDatabaseLocationNotificationBus::Handler::BusDisconnect();
AssetBrowserComponentRequestBus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AssetSystemBus::Handler::BusDisconnect();
m_assetBrowserModel.release();
EntryCache::DestroyInstance();
}
void AssetBrowserComponent::Reflect(AZ::ReflectContext* context)
{
AssetSystem::FileInfosNotificationMessage::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<AssetBrowserComponent, AZ::Component>();
}
}
void AssetBrowserComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType & services)
{
services.push_back(AZ_CRC("AssetBrowserService", 0x1e54fffb));
}
void AssetBrowserComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("ThumbnailerService", 0x65422b97));
}
void AssetBrowserComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("AssetBrowserService", 0x1e54fffb));
}
void AssetBrowserComponent::OnDatabaseInitialized()
{
m_databaseConnection->OpenDatabase();
PopulateAssets();
m_dbReady = true;
}
AssetBrowserModel* AssetBrowserComponent::GetAssetBrowserModel()
{
return m_assetBrowserModel.get();
}
bool AssetBrowserComponent::AreEntriesReady()
{
return m_entriesReady;
}
void AssetBrowserComponent::PickAssets(AssetSelectionModel& selection, QWidget* parent)
{
AssetPickerDialog dialog(selection, parent);
dialog.exec();
}
AzQtComponents::StyledBusyLabel* AssetBrowserComponent::GetStyledBusyLabel()
{
return m_styledBusyLabel;
}
void AssetBrowserComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
m_changeset->Synchronize();
if (!m_entriesReady)
{
m_entriesReady = true;
AssetBrowserComponentNotificationBus::Broadcast(&AssetBrowserComponentNotifications::OnAssetBrowserComponentReady);
}
}
// We listen to this bus so that a file that wasn't previously a 'source file' (just a file) can become a source file
// for example, when a file first appears on disk, it will come in as just a file (with a file id). Later, if its something
// that the Asset Processor actually cares about and feeds to a builder, it gets assigned a UUID and this function is called.
// we can then associate an existing file with a UUID.
void AssetBrowserComponent::SourceFileChanged(AZStd::string /*relativePath*/, AZStd::string /*scanFolder*/, AZ::Uuid sourceUuid)
{
m_changeset->AddSource(sourceUuid);
if (m_dbReady)
{
NotifyUpdateThread();
}
}
// this function handles the common built-in source file details for types built into the LMBRCENTRAL and AzToolsFramework libs
// if you are writing a gem that produces new asset types and you'd like to set an icon, just listen to this bus and return
// your own SourceFileDetails for your types of files in your gem! Don't add your gem-embedded types here.
// this is only here so that other applications (besides Editor.exe) may use AssetBrowser and see icons for types
// that are either built into Cry DLLs, or into Editor Plugins.
SourceFileDetails AssetBrowserComponent::GetSourceFileDetails(const char* fullSourceFileName)
{
using namespace AzToolsFramework::AssetBrowser;
AZStd::string extension;
if (AzFramework::StringFunc::Path::GetExtension(fullSourceFileName, extension, true))
{
if (AzFramework::StringFunc::Equal(extension.c_str(), ".abc"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/ABC_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".bnk"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/Audio_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".cgf"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/LegacyMesh_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".font"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/Font_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".fontfamily"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/Font_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".i_caf"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/LegacyAnimation_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".inputbindings"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/InputBindings_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".lua"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/Lua_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".mtl"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/Material_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), AzToolsFramework::SliceUtilities::GetSliceFileExtension().c_str()))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/Slice_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".skin"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/LegacySkin_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".ttf"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/Font_16.svg");
}
if (AzFramework::StringFunc::Equal(extension.c_str(), ".xml"))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/XML_16.svg");
}
// this is here to prevent having to include IResourceCompilerHelper, which is in CryCommon.
static const char* sourceFormats[] = { ".tif", ".bmp", ".gif", ".jpg", ".jpeg", ".jpe", ".tga", ".png" };
for (unsigned int sourceImageFormatIndex = 0, numSources = AZ_ARRAY_SIZE(sourceFormats); sourceImageFormatIndex < numSources; ++sourceImageFormatIndex)
{
const char* sourceFormatExtension = sourceFormats[sourceImageFormatIndex];
if (AzFramework::StringFunc::Equal(extension.c_str(), sourceFormatExtension))
{
return SourceFileDetails("Editor/Icons/AssetBrowser/Image_16.svg");
}
}
}
return SourceFileDetails();
}
void AssetBrowserComponent::AddFile(const AZ::s64& fileId)
{
m_changeset->AddFile(fileId);
if (m_dbReady)
{
NotifyUpdateThread();
}
}
void AssetBrowserComponent::RemoveFile(const AZ::s64& fileId)
{
m_changeset->RemoveFile(fileId);
if (m_dbReady)
{
NotifyUpdateThread();
}
}
void AssetBrowserComponent::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
{
m_changeset->AddProduct(assetId);
if (m_dbReady)
{
NotifyUpdateThread();
}
}
// sometimes this happens when there is new info about a product, so treat it as an Add!
// it can also happen when a source file disappears and then reappears very rapidly to the point where
// certain jobs are able to run so quickly that the asset was never removed.
void AssetBrowserComponent::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
{
m_changeset->AddProduct(assetId);
if (m_dbReady)
{
NotifyUpdateThread();
}
}
void AssetBrowserComponent::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& /*assetInfo*/)
{
m_changeset->RemoveProduct(assetId);
if (m_dbReady)
{
NotifyUpdateThread();
}
}
void AssetBrowserComponent::PopulateAssets()
{
m_changeset->PopulateEntries();
NotifyUpdateThread();
}
void AssetBrowserComponent::UpdateAssets()
{
while (true)
{
m_updateWait.acquire();
// kill thread if component is destroyed
if (m_disposed)
{
return;
}
// wait for db or more updates
m_waitingForMore = true;
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));
m_waitingForMore = false;
if (m_dbReady)
{
m_changeset->Update();
}
}
}
void AssetBrowserComponent::NotifyUpdateThread()
{
// do not release the sempahore again if query thread is waiting for more update requests
// otherwise it would needlessly spin another turn
if (!m_waitingForMore)
{
m_updateWait.release();
}
}
void AssetBrowserComponent::HandleFileInfoNotification(const void* buffer, unsigned bufferSize)
{
AssetSystem::FileInfosNotificationMessage message;
if (!AZ::Utils::LoadObjectFromBufferInPlace(buffer, bufferSize, message))
{
AZ_WarningOnce("AssetSystem", false, "Problem deserializing FileInfosNotificationMessage. Discarded.\n");
return;
}
switch (message.m_type)
{
case AssetSystem::FileInfosNotificationMessage::Synced:
PopulateAssets();
break;
case AssetSystem::FileInfosNotificationMessage::FileAdded:
AddFile(message.m_fileID);
break;
case AssetSystem::FileInfosNotificationMessage::FileRemoved:
RemoveFile(message.m_fileID);
break;
default:
AZ_WarningOnce("AssetSystem", false, "Unknown FileInfosNotificationMessage type");
break;
}
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,142 @@
/*
* 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/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzFramework/Network/SocketConnection.h>
#include <AzQtComponents/Components/StyledBusyLabel.h>
namespace AzToolsFramework
{
namespace AssetDatabase
{
class AssetDatabaseConnection;
}
namespace AssetBrowser
{
class AssetBrowserModel;
class SourceAssetBrowserEntry;
class FolderAssetBrowserEntry;
class RootAssetBrowserEntry;
class AssetEntryChangeset;
//! AssetBrowserComponent caches database entries
/*!
Database entries are cached so that they can be quickly accessed by asset browser views.
Additionally this class watches for any changes to the database and updates the views if such changes happen
*/
class AssetBrowserComponent
: public AZ::Component
, public AssetBrowserComponentRequestBus::Handler
, public AssetDatabaseLocationNotificationBus::Handler
, public AzFramework::AssetCatalogEventBus::Handler
, public AZ::TickBus::Handler
, public AssetSystemBus::Handler
, public AssetBrowserInteractionNotificationBus::Handler
{
public:
AZ_COMPONENT(AssetBrowserComponent, "{4BC5F93F-2F9E-412E-B00A-396C68CFB5FB}")
AssetBrowserComponent();
virtual ~AssetBrowserComponent();
//////////////////////////////////////////////////////////////////////////
// AZ::Component
//////////////////////////////////////////////////////////////////////////
void Activate() override;
void Deactivate() override;
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
//////////////////////////////////////////////////////////////////////////
// AssetDatabaseLocationNotificationBus
//////////////////////////////////////////////////////////////////////////
void OnDatabaseInitialized() override;
//////////////////////////////////////////////////////////////////////////
// AssetBrowserComponentRequestBus
//////////////////////////////////////////////////////////////////////////
AssetBrowserModel* GetAssetBrowserModel() override;
bool AreEntriesReady() override;
void PickAssets(AssetSelectionModel& selection, QWidget* parent) override;
AzQtComponents::StyledBusyLabel* GetStyledBusyLabel() override;
//////////////////////////////////////////////////////////////////////////
// AssetCatalogEventBus
//////////////////////////////////////////////////////////////////////////
void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override;
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
void OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& assetInfo) override;
//////////////////////////////////////////////////////////////////////////
// TickBus
//////////////////////////////////////////////////////////////////////////
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
//////////////////////////////////////////////////////////////////////////
// AssetSystemBus
//////////////////////////////////////////////////////////////////////////
void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUuid) override;
//////////////////////////////////////////////////////////////////////////
// AssetBrowserInteractionNotificationBus
SourceFileDetails GetSourceFileDetails(const char* fullSourceFileName) override;
//////////////////////////////////////////////////////////////////////////
void AddFile(const AZ::s64& fileId);
void RemoveFile(const AZ::s64& fileId);
void PopulateAssets();
void UpdateAssets();
private:
AZStd::shared_ptr<AssetDatabase::AssetDatabaseConnection> m_databaseConnection;
AZStd::shared_ptr<RootAssetBrowserEntry> m_rootEntry;
AZStd::binary_semaphore m_updateWait;
AZStd::thread m_thread;
//! wait until database is ready
bool m_dbReady;
//! have entries been populated yet
bool m_entriesReady = false;
//! is query waiting for more update requests
AZStd::atomic_bool m_waitingForMore;
//! should the query thread stop
AZStd::atomic_bool m_disposed;
AZStd::unique_ptr<AssetBrowserModel> m_assetBrowserModel;
AZStd::shared_ptr<AssetEntryChangeset> m_changeset;
AzFramework::SocketConnection::TMessageCallbackHandle m_cbHandle = 0;
//! Notify to start the query thread
void NotifyUpdateThread();
void HandleFileInfoNotification(const void* buffer, unsigned int bufferSize);
AzQtComponents::StyledBusyLabel* m_styledBusyLabel;
};
}
} // namespace AssetBrowser
@@ -0,0 +1,18 @@
/*
* 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/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
@@ -0,0 +1,173 @@
/*
* 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/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <QSharedPointer>
#include <QTimer>
#include <QCollator>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetBrowser
{
//////////////////////////////////////////////////////////////////////////
//AssetBrowserFilterModel
AssetBrowserFilterModel::AssetBrowserFilterModel(QObject* parent)
: QSortFilterProxyModel(parent)
{
m_showColumn.insert(AssetBrowserModel::m_column);
m_collator.setNumericMode(true);
AssetBrowserComponentNotificationBus::Handler::BusConnect();
}
AssetBrowserFilterModel::~AssetBrowserFilterModel()
{
AssetBrowserComponentNotificationBus::Handler::BusDisconnect();
}
void AssetBrowserFilterModel::SetFilter(FilterConstType filter)
{
connect(filter.data(), &AssetBrowserEntryFilter::updatedSignal, this, &AssetBrowserFilterModel::filterUpdatedSlot);
m_filter = filter;
m_invalidateFilter = true;
// asset browser entries are not guaranteed to have populated when the filter is set, delay filtering until they are
bool isAssetBrowserComponentReady = false;
AssetBrowserComponentRequestBus::BroadcastResult(isAssetBrowserComponentReady, &AssetBrowserComponentRequests::AreEntriesReady);
if (isAssetBrowserComponentReady)
{
OnAssetBrowserComponentReady();
}
}
void AssetBrowserFilterModel::OnAssetBrowserComponentReady()
{
if (m_invalidateFilter)
{
invalidateFilter();
m_invalidateFilter = false;
}
}
bool AssetBrowserFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
//get the source idx, if invalid early out
QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
if (!idx.isValid())
{
return false;
}
// no filter present, every entry is visible
if (!m_filter)
{
return true;
}
//the entry is the internal pointer of the index
auto entry = static_cast<AssetBrowserEntry*>(idx.internalPointer());
// root should return true even if its not displayed in the treeview
if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Root)
{
return true;
}
return m_filter->Match(entry);
}
bool AssetBrowserFilterModel::filterAcceptsColumn(int source_column, const QModelIndex&) const
{
//if the column is in the set we want to show it
return m_showColumn.find(source_column) != m_showColumn.end();
}
bool AssetBrowserFilterModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const
{
if (source_left.column() == source_right.column())
{
QVariant leftData = sourceModel()->data(source_left, AssetBrowserModel::Roles::EntryRole);
QVariant rightData = sourceModel()->data(source_right, AssetBrowserModel::Roles::EntryRole);
if (leftData.canConvert<const AssetBrowserEntry*>() && rightData.canConvert<const AssetBrowserEntry*>())
{
auto leftEntry = qvariant_cast<const AssetBrowserEntry*>(leftData);
auto rightEntry = qvariant_cast<const AssetBrowserEntry*>(rightData);
// folders should always come first
if (azrtti_istypeof<const FolderAssetBrowserEntry*>(leftEntry) && azrtti_istypeof<const SourceAssetBrowserEntry*>(rightEntry))
{
return false;
}
if (azrtti_istypeof<const SourceAssetBrowserEntry*>(leftEntry) && azrtti_istypeof<const FolderAssetBrowserEntry*>(rightEntry))
{
return true;
}
// if both entries are of same type, sort alphabetically
return m_collator.compare(leftEntry->GetDisplayName(), rightEntry->GetDisplayName()) > 0;
}
}
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
void AssetBrowserFilterModel::FilterUpdatedSlotImmediate()
{
auto compFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(m_filter);
if (compFilter)
{
auto& subFilters = compFilter->GetSubFilters();
auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool
{
auto assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(filter);
return !assetTypeFilter.isNull();
});
if (it != subFilters.end())
{
m_assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(*it);
}
it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool
{
auto stringFilter = qobject_cast<QSharedPointer<const StringFilter> >(filter);
return !stringFilter.isNull();
});
if (it != subFilters.end())
{
m_stringFilter = qobject_cast<QSharedPointer<const StringFilter> >(*it);
}
}
invalidateFilter();
Q_EMIT filterChanged();
}
void AssetBrowserFilterModel::filterUpdatedSlot()
{
if (!m_alreadyRecomputingFilters)
{
m_alreadyRecomputingFilters = true;
// de-bounce it, since we may get many filter updates all at once.
QTimer::singleShot(0, this, [this]()
{
m_alreadyRecomputingFilters = false;
FilterUpdatedSlotImmediate();
}
);
}
}
} // namespace AssetBrowser
} // namespace AzToolsFramework// namespace AssetBrowser
#include "AssetBrowser/moc_AssetBrowserFilterModel.cpp"
@@ -0,0 +1,83 @@
/*
* 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 <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/fixed_unordered_set.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...'
#include <QSortFilterProxyModel>
#include <QSharedPointer>
#include <QCollator>
#endif
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserFilterModel
: public QSortFilterProxyModel
, public AssetBrowserComponentNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(AssetBrowserFilterModel, AZ::SystemAllocator, 0);
explicit AssetBrowserFilterModel(QObject* parent = nullptr);
~AssetBrowserFilterModel() override;
//asset type filtering
void SetFilter(FilterConstType filter);
void FilterUpdatedSlotImmediate();
//////////////////////////////////////////////////////////////////////////
// AssetBrowserComponentNotificationBus
//////////////////////////////////////////////////////////////////////////
void OnAssetBrowserComponentReady() override;
Q_SIGNALS:
void filterChanged();
//////////////////////////////////////////////////////////////////////////
//QSortFilterProxyModel
protected:
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override;
bool filterAcceptsColumn(int source_column, const QModelIndex& /*source_parent*/) const override;
bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override;
//////////////////////////////////////////////////////////////////////////
public Q_SLOTS:
void filterUpdatedSlot();
protected:
//set for filtering columns
//if the column is in the set the column is not filtered and is shown
AZStd::fixed_unordered_set<int, 3, static_cast<int>(AssetBrowserEntry::Column::Count)> m_showColumn;
bool m_alreadyRecomputingFilters = false;
//asset source name match filter
FilterConstType m_filter;
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...'
QWeakPointer<const StringFilter> m_stringFilter;
QWeakPointer<const CompositeFilter> m_assetTypeFilter;
QCollator m_collator; // cache the collator as its somewhat expensive to constantly create and destroy one.
AZ_POP_DISABLE_WARNING
bool m_invalidateFilter = false;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,403 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Script/ScriptTimePoint.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <QMimeData>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 'QRegularExpression::d': class 'QExplicitlySharedDataPointer<QRegularExpressionPrivate>' needs to have dll-interface to be used by clients of class 'QRegularExpression'
#include <QRegularExpression>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetBrowser
{
const int AssetBrowserModel::m_column = static_cast<int>(AssetBrowserEntry::Column::DisplayName);
AssetBrowserModel::AssetBrowserModel(QObject* parent)
: QAbstractItemModel(parent)
, m_rootEntry(nullptr)
, m_loaded(false)
, m_addingEntry(false)
, m_removingEntry(false)
{
AssetBrowserModelRequestBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
}
AssetBrowserModel::~AssetBrowserModel()
{
AssetBrowserModelRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
}
QModelIndex AssetBrowserModel::findIndex(const QString& absoluteAssetPath) const
{
// Split the path based on either platform's slash
QRegularExpression regex(QStringLiteral("[\\/]"));
QStringList assetPathComponents = absoluteAssetPath.split(regex);
AssetBrowserEntry* cursor = m_rootEntry.get();
if (cursor && absoluteAssetPath.contains(cursor->GetFullPath().c_str()))
{
while (true)
{
// find the child entry that contains more
bool foundChild = false;
for (int i = 0; i < cursor->GetChildCount(); i++)
{
AssetBrowserEntry* child = cursor->GetChild(i);
if (child)
{
QString newPath = child->GetFullPath().c_str();
if (absoluteAssetPath.startsWith(newPath))
{
if (absoluteAssetPath == newPath)
{
QModelIndex index;
if (GetEntryIndex(child, index))
{
return index;
}
}
// Confirm that this is a real match as opposed to a partial match.
// For instance, an asset absolute path C:/somepath/someotherpath/blah.tga will partial match with c:/somepath/some
// and get us here.
QStringList possibleMatchComponents = newPath.split(regex);
QString possibleMatchDirectory = possibleMatchComponents.last();
Q_ASSERT(assetPathComponents.count() >= possibleMatchComponents.count());
if (possibleMatchDirectory == assetPathComponents[possibleMatchComponents.count() - 1])
{
cursor = child;
foundChild = true;
break;
}
}
}
}
if (!foundChild)
{
break;
}
}
}
return QModelIndex();
}
QModelIndex AssetBrowserModel::index(int row, int column, const QModelIndex& parent) const
{
if (!hasIndex(row, column, parent))
{
return QModelIndex();
}
AssetBrowserEntry* parentEntry;
if (!parent.isValid())
{
parentEntry = m_rootEntry.get();
}
else
{
parentEntry = reinterpret_cast<AssetBrowserEntry*>(parent.internalPointer());
}
AssetBrowserEntry* childEntry = parentEntry->m_children[row];
if (!childEntry)
{
return QModelIndex();
}
QModelIndex index;
GetEntryIndex(childEntry, index);
return index;
}
int AssetBrowserModel::rowCount(const QModelIndex& parent) const
{
if (!m_rootEntry)
{
return 0;
}
if (parent.isValid())
{
if ((parent.column() != static_cast<int>(AssetBrowserEntry::Column::DisplayName)) &&
(parent.column() != static_cast<int>(AssetBrowserEntry::Column::Name)))
{
return 0;
}
}
AssetBrowserEntry* parentAssetEntry;
if (!parent.isValid())
{
parentAssetEntry = m_rootEntry.get();
}
else
{
parentAssetEntry = static_cast<AssetBrowserEntry*>(parent.internalPointer());
}
return parentAssetEntry->GetChildCount();
}
int AssetBrowserModel::columnCount(const QModelIndex& /*parent*/) const
{
return static_cast<int>(AssetBrowserEntry::Column::Count);
}
QVariant AssetBrowserModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
const AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
return item->GetDisplayName();
}
if (role == Roles::EntryRole)
{
const AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
return QVariant::fromValue(item);
}
return QVariant();
}
Qt::ItemFlags AssetBrowserModel::flags(const QModelIndex& index) const
{
Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
if (index.isValid())
{
// allow retrieval of mimedata of sources or products only (i.e. cant drag folders or root)
AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
if (item && (item->RTTI_IsTypeOf(ProductAssetBrowserEntry::RTTI_Type()) || item->RTTI_IsTypeOf(SourceAssetBrowserEntry::RTTI_Type())))
{
return Qt::ItemIsDragEnabled | defaultFlags;
}
}
return defaultFlags;
//return Qt::ItemFlags(~Qt::ItemIsDragEnabled & defaultFlags);
}
QMimeData* AssetBrowserModel::mimeData(const QModelIndexList& indexes) const
{
QMimeData* mimeData = new QMimeData;
for (const auto& index : indexes)
{
if (index.isValid())
{
AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
if (item)
{
item->AddToMimeData(mimeData);
}
}
}
return mimeData;
}
QVariant AssetBrowserModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Roles::EntryRole)
{
return tr(AssetBrowserEntry::m_columnNames[section]);
}
return QAbstractItemModel::headerData(section, orientation, role);
}
void AssetBrowserModel::SourceIndexesToAssetIds(const QModelIndexList& indexes, AZStd::vector<AZ::Data::AssetId>& assetIds)
{
for (const auto& index : indexes)
{
if (index.isValid())
{
AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
if (item->GetEntryType() == AssetBrowserEntry::AssetEntryType::Product)
{
assetIds.push_back(static_cast<ProductAssetBrowserEntry*>(item)->GetAssetId());
}
}
}
}
void AssetBrowserModel::SourceIndexesToAssetDatabaseEntries(const QModelIndexList& indexes, AZStd::vector<AssetBrowserEntry*>& entries)
{
for (const auto& index : indexes)
{
if (index.isValid())
{
AssetBrowserEntry* item = static_cast<AssetBrowserEntry*>(index.internalPointer());
entries.push_back(item);
}
}
}
AZStd::shared_ptr<RootAssetBrowserEntry> AssetBrowserModel::GetRootEntry() const
{
return m_rootEntry;
}
void AssetBrowserModel::SetRootEntry(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry)
{
m_rootEntry = rootEntry;
}
QModelIndex AssetBrowserModel::parent(const QModelIndex& child) const
{
if (!child.isValid())
{
return QModelIndex();
}
AssetBrowserEntry* childAssetEntry = static_cast<AssetBrowserEntry*>(child.internalPointer());
AssetBrowserEntry* parentEntry = childAssetEntry->GetParent();
QModelIndex parentIndex;
if (GetEntryIndex(parentEntry, parentIndex))
{
return parentIndex;
}
return QModelIndex();
}
bool AssetBrowserModel::IsLoaded() const
{
return m_loaded;
}
void AssetBrowserModel::BeginAddEntry(AssetBrowserEntry* parent)
{
QModelIndex parentIndex;
if (GetEntryIndex(parent, parentIndex))
{
m_addingEntry = true;
int row = parent->GetChildCount();
beginInsertRows(parentIndex, row, row);
}
}
void AssetBrowserModel::EndAddEntry(AssetBrowserEntry* parent)
{
if (m_addingEntry)
{
m_addingEntry = false;
endInsertRows();
// we have to also invalidate our parent all the way up the chain.
// since in this model, the children's data is actually relevant to the filtering of a parent
// since a parent "matches" the filter if its children do.
if ((m_rootEntry) && (!m_rootEntry->IsInitialUpdate()))
{
// this is only necessary if its not the initial refresh.
while (parent)
{
QModelIndex parentIndex;
if (GetEntryIndex(parent, parentIndex))
{
Q_EMIT dataChanged(parentIndex, parentIndex);
}
parent = parent->GetParent();
}
}
}
}
void AssetBrowserModel::BeginRemoveEntry(AssetBrowserEntry* entry)
{
int row = entry->row();
QModelIndex parentIndex;
if (GetEntryIndex(entry->m_parentAssetEntry, parentIndex))
{
m_removingEntry = true;
beginRemoveRows(parentIndex, row, row);
}
}
void AssetBrowserModel::EndRemoveEntry()
{
if (m_removingEntry)
{
m_removingEntry = false;
endRemoveRows();
}
}
void AssetBrowserModel::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
// if any entries changed since last tick, notify the views
if (EntryCache* cache = EntryCache::GetInstance())
{
if (!cache->m_dirtyThumbnailsSet.empty())
{
for (AssetBrowserEntry* entry : cache->m_dirtyThumbnailsSet)
{
QModelIndex index;
if (GetEntryIndex(entry, index))
{
AZ_PUSH_DISABLE_WARNING(4127, "-Wunknown-warning-option") // conditional expression is constant
Q_EMIT dataChanged(index, index, { Roles::EntryRole });
AZ_POP_DISABLE_WARNING
}
}
cache->m_dirtyThumbnailsSet.clear();
}
}
}
bool AssetBrowserModel::GetEntryIndex(AssetBrowserEntry* entry, QModelIndex& index) const
{
if (!entry)
{
return false;
}
if (azrtti_istypeof<RootAssetBrowserEntry*>(entry))
{
index = QModelIndex();
return true;
}
if (!entry->m_parentAssetEntry)
{
return false;
}
int row = entry->row();
index = createIndex(row, m_column, entry);
return true;
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/moc_AssetBrowserModel.cpp"
@@ -0,0 +1,105 @@
/*
* 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 <AzCore/PlatformDef.h>
AZ_PUSH_DISABLE_WARNING(4127 4251, "-Wunknown-warning-option") // conditional expression is constant
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
AZ_POP_DISABLE_WARNING
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/Component/TickBus.h>
AZ_PUSH_DISABLE_WARNING(4127 4251 4800, "-Wunknown-warning-option") // 4127: conditional expression is constant
// 4251: 'QVariant::d': struct 'QVariant::Private' needs to have dll-interface to be used by clients of class 'QVariant'
// 4800: 'int': forcing value to bool 'true' or 'false' (performance warning)
#include <QAbstractTableModel>
#include <QVariant>
#include <QMimeData>
#endif
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserEntry;
class RootAssetBrowserEntry;
class AssetEntryChangeset;
class AssetBrowserModel
: public QAbstractItemModel
, public AssetBrowserModelRequestBus::Handler
, public AZ::TickBus::Handler
{
Q_OBJECT
public:
enum Roles
{
EntryRole = Qt::UserRole + 100,
};
AZ_CLASS_ALLOCATOR(AssetBrowserModel, AZ::SystemAllocator, 0);
explicit AssetBrowserModel(QObject* parent = nullptr);
~AssetBrowserModel();
QModelIndex findIndex(const QString& absoluteAssetPath) const;
//////////////////////////////////////////////////////////////////////////
// QAbstractTableModel
//////////////////////////////////////////////////////////////////////////
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QMimeData* mimeData(const QModelIndexList& indexes) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex parent(const QModelIndex& child) const override;
//////////////////////////////////////////////////////////////////////////
// AssetBrowserModelRequestBus
//////////////////////////////////////////////////////////////////////////
bool IsLoaded() const override;
void BeginAddEntry(AssetBrowserEntry* parent) override;
void EndAddEntry(AssetBrowserEntry* parent) override;
void BeginRemoveEntry(AssetBrowserEntry* entry) override;
void EndRemoveEntry() override;
//////////////////////////////////////////////////////////////////////////
// TickBus
//////////////////////////////////////////////////////////////////////////
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
AZStd::shared_ptr<RootAssetBrowserEntry> GetRootEntry() const;
void SetRootEntry(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry);
static void SourceIndexesToAssetIds(const QModelIndexList& indexes, AZStd::vector<AZ::Data::AssetId>& assetIds);
static void SourceIndexesToAssetDatabaseEntries(const QModelIndexList& indexes, AZStd::vector<AssetBrowserEntry*>& entries);
const static int m_column;
private:
AZStd::shared_ptr<RootAssetBrowserEntry> m_rootEntry;
bool m_loaded;
bool m_addingEntry;
bool m_removingEntry;
bool GetEntryIndex(AssetBrowserEntry* entry, QModelIndex& index) const;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,168 @@
/*
* 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/std/smart_ptr/shared_ptr.h>
#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
/**
* AssetEntryChange represents an atomic change performed on AssetBrowser model
*/
class AssetEntryChange
{
public:
AZ_RTTI(AssetEntryChange, "{C8D33C55-1AB9-4599-B1DD-32403E117813}");
AssetEntryChange() = default;
virtual ~AssetEntryChange() = default;
virtual bool Apply(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry) = 0;
};
class AddScanFolderChange
: public AssetEntryChange
{
public:
AZ_RTTI(AddScanFolderChange, "{CA1C5AC8-127B-4422-9431-030209A07614}", AssetEntryChange);
AZ_CLASS_ALLOCATOR(AddScanFolderChange, AZ::SystemAllocator, 0);
AddScanFolderChange(const AssetDatabase::ScanFolderDatabaseEntry& scanFolder)
: m_scanFolder(scanFolder) {}
bool Apply(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry) override
{
rootEntry->AddScanFolder(m_scanFolder);
return true;
}
private:
AssetDatabase::ScanFolderDatabaseEntry m_scanFolder;
};
class AddFileChange
: public AssetEntryChange
{
public:
AZ_RTTI(AddFileChange, "{65D8CFBB-4CD9-4E4F-9A08-0F4D25CA1326}", AssetEntryChange);
AZ_CLASS_ALLOCATOR(AddFileChange, AZ::SystemAllocator, 0);
AddFileChange(const AssetDatabase::FileDatabaseEntry& file)
: m_file(file) {}
bool Apply(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry) override
{
rootEntry->AddFile(m_file);
return true;
}
private:
AssetDatabase::FileDatabaseEntry m_file;
};
class RemoveFileChange
: public AssetEntryChange
{
public:
AZ_RTTI(RemoveFileChange, "{83758FA6-65F2-493E-8D27-C73D0AF0FB94}", AssetEntryChange);
AZ_CLASS_ALLOCATOR(RemoveFileChange, AZ::SystemAllocator, 0);
RemoveFileChange(const AZ::s64& fileId)
: m_fileId(fileId) {}
bool Apply(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry) override
{
return rootEntry->RemoveFile(m_fileId);
}
private:
AZ::s64 m_fileId;
};
class AddSourceChange
: public AssetEntryChange
{
public:
AZ_RTTI(AddSourceChange, "{E9BE4E9B-85DE-4217-96F2-4098A88F1FDE}", AssetEntryChange);
AZ_CLASS_ALLOCATOR(AddSourceChange, AZ::SystemAllocator, 0);
AddSourceChange(const SourceWithFileID& source)
: m_source(source) {}
bool Apply(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry) override
{
return rootEntry->AddSource(m_source);
}
private:
SourceWithFileID m_source;
};
class RemoveSourceChange
: public AssetEntryChange
{
public:
AZ_RTTI(RemoveSourceChange, "{D87AB1B8-6F72-4E2F-B7C1-A01BD62D8F1D}", AssetEntryChange);
AZ_CLASS_ALLOCATOR(RemoveSourceChange, AZ::SystemAllocator, 0);
RemoveSourceChange(const AZ::Uuid& sourceUuid)
: m_sourceUuid(sourceUuid) {}
bool Apply(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry) override
{
rootEntry->RemoveSource(m_sourceUuid);
return true;
}
private:
AZ::Uuid m_sourceUuid;
};
class AddProductChange
: public AssetEntryChange
{
public:
AZ_RTTI(AddProductChange, "{7127E694-4CD4-480F-A88E-B5C726B65DFE}", AssetEntryChange);
AZ_CLASS_ALLOCATOR(AddProductChange, AZ::SystemAllocator, 0);
AddProductChange(const ProductWithUuid& product)
: m_product(product) {}
bool Apply(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry) override
{
return rootEntry->AddProduct(m_product);
}
AZ::Uuid GetUuid() const { return m_product.first; }
const AZ::Data::AssetId GetAssetId() const { return AZ::Data::AssetId(GetUuid(), m_product.second.m_subID); }
private:
ProductWithUuid m_product;
};
class RemoveProductChange
: public AssetEntryChange
{
public:
AZ_RTTI(RemoveProductChange, "{7DDC4900-8842-4221-8CB7-C167DEB82BE8}", AssetEntryChange);
AZ_CLASS_ALLOCATOR(RemoveProductChange, AZ::SystemAllocator, 0);
RemoveProductChange(const AZ::Data::AssetId& assetId)
: m_assetId(assetId) {}
bool Apply(AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry) override
{
rootEntry->RemoveProduct(m_assetId);
return true;
}
private:
AZ::Data::AssetId m_assetId;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,305 @@
/*
* 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/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetEntryChangeset.h>
#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetEntryChange.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
AssetEntryChangeset::AssetEntryChangeset(
AZStd::shared_ptr<AssetDatabase::AssetDatabaseConnection> databaseConnection,
AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry)
: m_databaseConnection(databaseConnection)
, m_rootEntry(rootEntry)
, m_fullUpdate(false)
, m_updated(false)
{}
AssetEntryChangeset::~AssetEntryChangeset()
{
for (auto change : m_changes)
{
delete change;
}
}
void AssetEntryChangeset::PopulateEntries()
{
m_fullUpdate = true;
}
void AssetEntryChangeset::Update()
{
if (m_fullUpdate)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_fullUpdate = false;
m_updated = true;
QueryEntireDatabase();
m_fileIdsToAdd.clear();
m_sourceUuidsToAdd.clear();
m_productAssetIdsToAdd.clear();
}
else
{
QueryChangeset();
}
}
void AssetEntryChangeset::Synchronize()
{
using namespace AssetDatabase;
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
if (m_updated)
{
m_rootEntry->SetInitialUpdate(true);
m_rootEntry->Update(m_relativePath.c_str());
m_updated = false;
}
// iterate through new changes and try to apply them
// if application of change fails, try them again next tick
AZStd::vector<AssetEntryChange*> changesFailed;
for (auto change : m_changes)
{
if (change->Apply(m_rootEntry))
{
delete change;
}
else
{
changesFailed.push_back(change);
}
}
#if AZ_DEBUG_BUILD
if (m_changes.size() > 0)
{
AZ_TracePrintf("Asset Browser DEBUG", "%d/%d data changes applied\n", m_changes.size() - changesFailed.size(), m_changes.size());
}
#endif
// try again next time.
m_changes = changesFailed;
if (m_rootEntry->IsInitialUpdate())
{
m_rootEntry->SetInitialUpdate(false);
}
}
void AssetEntryChangeset::AddFile(const AZ::s64& fileId)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_fileIdsToAdd.push_back(fileId);
}
void AssetEntryChangeset::RemoveFile(const AZ::s64& fileId)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_changes.push_back(aznew RemoveFileChange(fileId));
}
void AssetEntryChangeset::AddSource(const AZ::Uuid& sourceUuid)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_sourceUuidsToAdd.push_back(sourceUuid);
}
void AssetEntryChangeset::RemoveSource(const AZ::Uuid& sourceUuid)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_changes.push_back(aznew RemoveSourceChange(sourceUuid));
}
void AssetEntryChangeset::AddProduct(const AZ::Data::AssetId& assetId)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_productAssetIdsToAdd.push_back(assetId);
}
void AssetEntryChangeset::RemoveProduct(const AZ::Data::AssetId& assetId)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
auto findPredicate = [&assetId](const AssetEntryChange* toCheck)
{
if (const AddProductChange* productChange = azrtti_cast<const AddProductChange*>(toCheck))
{
return productChange->GetAssetId() == assetId;
}
return false;
};
// make sure any pending "add" commands are erased.
m_productAssetIdsToAdd.erase(AZStd::remove(m_productAssetIdsToAdd.begin(), m_productAssetIdsToAdd.end(), assetId), m_productAssetIdsToAdd.end());
auto foundExisting = AZStd::find_if(m_changes.begin(), m_changes.end(), findPredicate);
if (foundExisting != m_changes.end())
{
// remove the still-pending "add Product" from the list, no longer necesary.
m_changes.erase(foundExisting);
}
m_changes.push_back(aznew RemoveProductChange(assetId));
}
void AssetEntryChangeset::QueryEntireDatabase()
{
using namespace AssetDatabase;
// querying the asset database for the root folder
m_databaseConnection->QueryScanFolderByDisplayName(
"root",
[=](ScanFolderDatabaseEntry& scanFolderDatabaseEntry)
{
m_relativePath = scanFolderDatabaseEntry.m_scanFolder.c_str();
return true;
});
// query all scanfolders
m_databaseConnection->QueryScanFoldersTable(
[&](ScanFolderDatabaseEntry& scanFolder)
{
// ignore scanfolders that are non-recursive (e.g. dev folder), as they are used generally for system assets
if (scanFolder.m_isRoot)
{
return true;
}
m_changes.push_back(aznew AddScanFolderChange(scanFolder));
return m_databaseConnection->QueryFilesByScanFolderID(scanFolder.m_scanFolderID,
[&](FileDatabaseEntry& file)
{
m_changes.push_back(aznew AddFileChange(file));
return m_databaseConnection->QuerySourceBySourceNameScanFolderID(file.m_fileName.c_str(), scanFolder.m_scanFolderID,
[&](SourceDatabaseEntry& source)
{
m_changes.push_back(aznew AddSourceChange({ file.m_fileID, source }));
return m_databaseConnection->QueryProductBySourceID(source.m_sourceID,
[&](ProductDatabaseEntry& product)
{
m_changes.push_back(aznew AddProductChange({ source.m_sourceGuid, product }));
return true;
});
});
});
});
}
// this function translates from a series of incoming change notifies into an actual
// changeset that can then be applied to the model. Change notifies are brief and may contain
// only minimal information such as fileId. This transforms them into larger sequences of changes
// that include the creation of intermediate parent(s) if necessary.
void AssetEntryChangeset::QueryChangeset()
{
using namespace AssetDatabase;
AZStd::vector<AZ::s64> fileIdsToAdd;
AZStd::vector<AZ::Uuid> sourceUuidsToAdd;
AZStd::vector<AZ::Data::AssetId> productAssetIdsToAdd;
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
fileIdsToAdd = AZStd::move(m_fileIdsToAdd);
m_fileIdsToAdd.clear();
sourceUuidsToAdd = AZStd::move(m_sourceUuidsToAdd);
m_sourceUuidsToAdd.clear();
productAssetIdsToAdd = AZStd::move(m_productAssetIdsToAdd);
m_productAssetIdsToAdd.clear();
}
for (const AZ::s64& fileId : fileIdsToAdd)
{
m_databaseConnection->QueryFileByFileID(fileId,
[&](FileDatabaseEntry& file)
{
return m_databaseConnection->QueryScanFolderByScanFolderID(file.m_scanFolderPK,
[&](ScanFolderDatabaseEntry& scanFolder)
{
// ignore scanfolders that are non-recursive (e.g. dev folder), as they are used generally for system assets
if (!scanFolder.m_isRoot)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_changes.push_back(aznew AddFileChange(file));
}
return true;
});
});
}
for (const AZ::Data::AssetId& assetId : productAssetIdsToAdd)
{
if (AZStd::find(sourceUuidsToAdd.begin(), sourceUuidsToAdd.end(), assetId.m_guid) == sourceUuidsToAdd.end())
{
sourceUuidsToAdd.push_back(assetId.m_guid);
}
m_databaseConnection->QueryProductBySourceGuidSubID(assetId.m_guid, assetId.m_subId,
[&](ProductDatabaseEntry& product)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
m_changes.push_back(aznew AddProductChange({ assetId.m_guid, product }));
return true;
});
}
for (const AZ::Uuid& sourceUuid : sourceUuidsToAdd)
{
m_databaseConnection->QuerySourceBySourceGuid(sourceUuid,
[&](SourceDatabaseEntry& source)
{
return m_databaseConnection->QueryFileByFileNameScanFolderID(source.m_sourceName.c_str(), source.m_scanFolderPK,
[&](FileDatabaseEntry& file)
{
return m_databaseConnection->QueryScanFolderByScanFolderID(file.m_scanFolderPK,
[&](ScanFolderDatabaseEntry& scanFolder)
{
AZStd::lock_guard<AZStd::mutex> locker(m_mutex);
// ignore scanfolders that are non-recursive (e.g. dev folder), as they are used generally for system assets
if (!scanFolder.m_isRoot)
{
m_changes.push_back(new AddSourceChange({ file.m_fileID, source }));
}
else
{
// if products belonging to entry in root folder are considered, remove them from changes
m_changes.erase(AZStd::remove_if(m_changes.begin(), m_changes.end(),
[sourceUuid](AssetEntryChange* change)
{
auto addProductChange = azrtti_cast<AddProductChange*>(change);
if (addProductChange && addProductChange->GetUuid() == sourceUuid)
{
delete change;
return true;
}
return false;
}),
m_changes.end());
}
return true;
});
});
});
}
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,78 @@
/*
* 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/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
namespace AzToolsFramework
{
namespace AssetDatabase
{
class AssetDatabaseConnection;
class FileDatabaseEntry;
class ScanFolderDatabaseEntry;
class SourceDatabaseEntry;
class CombinedDatabaseEntry;
class ProductDatabaseEntry;
}
namespace AssetBrowser
{
class AssetEntryChange;
class AssetEntryChangeset
{
public:
AssetEntryChangeset(
AZStd::shared_ptr<AssetDatabase::AssetDatabaseConnection> databaseConnection,
AZStd::shared_ptr<RootAssetBrowserEntry> rootEntry);
~AssetEntryChangeset();
void PopulateEntries();
void Update();
void Synchronize();
void AddFile(const AZ::s64& fileId);
void RemoveFile(const AZ::s64& fileId);
void AddSource(const AZ::Uuid& sourceUuid);
void RemoveSource(const AZ::Uuid& sourceUuid);
void AddProduct(const AZ::Data::AssetId& assetId);
void RemoveProduct(const AZ::Data::AssetId& assetId);
private:
AZStd::shared_ptr<AssetDatabase::AssetDatabaseConnection> m_databaseConnection;
AZStd::shared_ptr<RootAssetBrowserEntry> m_rootEntry;
//! protects read/write to m_entries
AZStd::mutex m_mutex;
AZStd::atomic_bool m_fullUpdate;
bool m_updated;
AZStd::vector<AssetEntryChange*> m_changes;
AZStd::vector<AZ::s64> m_fileIdsToAdd;
AZStd::vector<AZ::Uuid> m_sourceUuidsToAdd;
AZStd::vector<AZ::Data::AssetId> m_productAssetIdsToAdd;
AZStd::string m_relativePath;
void QueryEntireDatabase();
void QueryChangeset();
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,229 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/UserSettings/UserSettings.h>
#include <AzQtComponents/Components/DockBar.h>
#include <AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.h>
#include <AzToolsFramework/AssetBrowser/AssetPicker/AssetPickerDialog.h>
AZ_PUSH_DISABLE_WARNING(4251 4244, "-Wunknown-warning-option") // disable warnings spawned by QT
#include "AssetBrowser/AssetPicker/ui_AssetPickerDialog.h"
#include <QPushButton>
#include <QDialogButtonBox>
#include <QKeyEvent>
#include <QTimer>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetBrowser
{
AssetPickerDialog::AssetPickerDialog(AssetSelectionModel& selection, QWidget* parent)
: QDialog(parent)
, m_ui(new Ui::AssetPickerDialogClass())
, m_filterModel(new AssetBrowserFilterModel(parent))
, m_selection(selection)
, m_hasFilter(false)
{
m_filterStateSaver = AzToolsFramework::TreeViewState::CreateTreeViewState();
m_ui->setupUi(this);
m_ui->m_searchWidget->Setup(true, false);
m_ui->m_searchWidget->GetFilter()->AddFilter(m_selection.GetDisplayFilter());
using namespace AzToolsFramework::AssetBrowser;
AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AssetBrowserComponentRequests::GetAssetBrowserModel);
AZ_Assert(m_assetBrowserModel, "Failed to get asset browser model");
m_filterModel->setSourceModel(m_assetBrowserModel);
m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter());
QString name = m_selection.GetDisplayFilter()->GetName();
m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data());
m_ui->m_assetBrowserTreeViewWidget->setSelectionMode(selection.GetMultiselect() ?
QAbstractItemView::SelectionMode::ExtendedSelection : QAbstractItemView::SelectionMode::SingleSelection);
m_ui->m_assetBrowserTreeViewWidget->setDragEnabled(false);
// if the current selection is invalid, disable the Ok button
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(EvaluateSelection());
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setProperty("class", "Primary");
m_ui->m_buttonBox->button(QDialogButtonBox::Cancel)->setProperty("class", "Secondary");
connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot);
connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, [this]()
{
const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty();
const bool selectFirstFilteredIndex = true;
m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex);
});
connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AssetPickerDialog::DoubleClickedSlot);
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal, this,
[this](const QItemSelection&, const QItemSelection&){ AssetPickerDialog::SelectionChangedSlot(); });
connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_" + name);
for (auto& assetId : selection.GetSelectedAssetIds())
{
m_ui->m_assetBrowserTreeViewWidget->SelectProduct(assetId);
}
setWindowTitle(tr("Pick %1").arg(m_selection.GetTitle()));
m_persistentState = AZ::UserSettings::CreateFind<AzToolsFramework::QWidgetSavedState>(AZ::Crc32(("AssetBrowserTreeView_Dialog_" + name).toUtf8().data()), AZ::UserSettings::CT_GLOBAL);
QTimer::singleShot(0, this, &AssetPickerDialog::RestoreState);
SelectionChangedSlot();
}
AssetPickerDialog::~AssetPickerDialog() = default;
void AssetPickerDialog::accept()
{
SaveState();
QDialog::accept();
}
void AssetPickerDialog::reject()
{
m_selection.GetResults().clear();
SaveState();
QDialog::reject();
}
void AssetPickerDialog::OnFilterUpdated()
{
if (!m_hasFilter)
{
m_filterStateSaver->CaptureSnapshot(m_ui->m_assetBrowserTreeViewWidget);
}
m_filterModel->filterUpdatedSlot();
const bool hasFilter = m_ui->m_searchWidget->hasStringFilter();
if (hasFilter)
{
// The update slot queues the update, so we need to react after that update.
QTimer::singleShot(0, this, [this]()
{
m_ui->m_assetBrowserTreeViewWidget->expandAll();
});
}
if (m_hasFilter && !hasFilter)
{
m_filterStateSaver->ApplySnapshot(m_ui->m_assetBrowserTreeViewWidget);
m_hasFilter = false;
}
else if (!m_hasFilter && hasFilter)
{
m_hasFilter = true;
}
}
void AssetPickerDialog::keyPressEvent(QKeyEvent* e)
{
// Until search widget is revised, Return key should not close the dialog,
// it is used in search widget interaction
if (e->key() == Qt::Key_Return)
{
if (EvaluateSelection())
{
QDialog::accept();
}
}
else
{
QDialog::keyPressEvent(e);
}
}
bool AssetPickerDialog::EvaluateSelection() const
{
auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
// exactly one item must be selected, even if multi-select option is disabled, still good practice to check
if (selectedAssets.empty())
{
return false;
}
m_selection.GetResults().clear();
for (auto entry : selectedAssets)
{
m_selection.GetSelectionFilter()->Filter(m_selection.GetResults(), entry);
if (m_selection.IsValid() && !m_selection.GetMultiselect())
{
break;
}
}
return m_selection.IsValid();
}
void AssetPickerDialog::DoubleClickedSlot(const QModelIndex& index)
{
AZ_UNUSED(index);
if (EvaluateSelection())
{
QDialog::accept();
}
}
void AssetPickerDialog::UpdatePreview() const
{
auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
if (selectedAssets.size() != 1)
{
m_ui->m_previewerFrame->Clear();
return;
}
m_ui->m_previewerFrame->Display(selectedAssets.front());
}
void AssetPickerDialog::SaveState()
{
m_ui->m_assetBrowserTreeViewWidget->SaveState();
if (m_persistentState)
{
m_persistentState->CaptureGeometry(parentWidget() ? parentWidget() : this);
}
}
void AssetPickerDialog::RestoreState()
{
if (m_persistentState)
{
const auto widget = parentWidget() ? parentWidget() : this;
m_persistentState->RestoreGeometry(widget);
}
}
void AssetPickerDialog::SelectionChangedSlot()
{
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(EvaluateSelection());
UpdatePreview();
}
} // AssetBrowser
} // AzToolsFramework
#include <AssetBrowser/AssetPicker/moc_AssetPickerDialog.cpp>
@@ -0,0 +1,82 @@
/*
* 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 <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/UI/UICore/QTreeViewStateSaver.hxx>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <QDialog>
#include <QScopedPointer>
#endif
class QKeyEvent;
class QModelIndex;
class QItemSelection;
namespace Ui
{
class AssetPickerDialogClass;
}
namespace AzToolsFramework
{
class QWidgetSavedState;
namespace AssetBrowser
{
class ProductAssetBrowserEntry;
class AssetBrowserFilterModel;
class AssetBrowserModel;
class AssetSelectionModel;
class AssetPickerDialog
: public QDialog
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(AssetPickerDialog, AZ::SystemAllocator, 0);
explicit AssetPickerDialog(AssetSelectionModel& selection, QWidget* parent = nullptr);
virtual ~AssetPickerDialog();
protected:
//////////////////////////////////////////////////////////////////////////
// QDialog
//////////////////////////////////////////////////////////////////////////
void accept() override;
void reject() override;
void keyPressEvent(QKeyEvent* e) override;
private Q_SLOTS:
void DoubleClickedSlot(const QModelIndex& index);
void SelectionChangedSlot();
void RestoreState();
void OnFilterUpdated();
private:
//! Evaluate whether current selection is valid.
//! Valid selection requires exactly one item to be selected, must be source or product type, and must match the wildcard filter
bool EvaluateSelection() const;
void UpdatePreview() const;
void SaveState();
QScopedPointer<Ui::AssetPickerDialogClass> m_ui;
AssetBrowserModel* m_assetBrowserModel = nullptr;
QScopedPointer<AssetBrowserFilterModel> m_filterModel;
AssetSelectionModel& m_selection;
bool m_hasFilter;
AZStd::unique_ptr<TreeViewState> m_filterStateSaver;
AZStd::intrusive_ptr<QWidgetSavedState> m_persistentState;
};
} // AssetBrowser
} // AzToolsFramework
@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AssetPickerDialogClass</class>
<widget class="QDialog" name="AssetPickerDialogClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>650</width>
<height>400</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>650</width>
<height>400</height>
</size>
</property>
<property name="windowTitle">
<string>Select [unknown]</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QVBoxLayout" name="m_headerLayout">
<item>
<widget class="AzToolsFramework::AssetBrowser::SearchWidget" name="m_searchWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="m_line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QSplitter" name="m_splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="opaqueResize">
<bool>true</bool>
</property>
<property name="childrenCollapsible">
<bool>true</bool>
</property>
<widget class="QWidget" name="m_leftLayout" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">vertical-align: top</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTreeView" name="m_assetBrowserTreeViewWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragOnly</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="AzToolsFramework::AssetBrowser::PreviewerFrame" name="m_previewerFrame">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="m_buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzToolsFramework::AssetBrowser::SearchWidget</class>
<extends>QWidget</extends>
<header>AzToolsFramework/AssetBrowser/Search/SearchWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzToolsFramework::AssetBrowser::AssetBrowserTreeView</class>
<extends>QTreeView</extends>
<header>AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h</header>
</customwidget>
<customwidget>
<class>AzToolsFramework::AssetBrowser::PreviewerFrame</class>
<extends>QFrame</extends>
<header>AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,208 @@
/*
* 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 <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/EBusFindAssetTypeByName.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
namespace
{
FilterConstType ProductsNoFoldersFilter()
{
EntryTypeFilter* productFilter = new EntryTypeFilter();
productFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Product);
// in case entry is a source or folder, it may still contain relevant product
productFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down);
EntryTypeFilter* foldersFilter = new EntryTypeFilter();
foldersFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Folder);
InverseFilter* noFoldersFilter = new InverseFilter();
noFoldersFilter->SetFilter(FilterConstType(foldersFilter));
CompositeFilter* compFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND);
compFilter->AddFilter(FilterConstType(productFilter));
compFilter->AddFilter(FilterConstType(noFoldersFilter));
return FilterConstType(compFilter);
}
}
AssetSelectionModel::AssetSelectionModel()
: m_multiselect(false)
{
}
FilterConstType AssetSelectionModel::GetSelectionFilter() const
{
return m_selectionFilter;
}
void AssetSelectionModel::SetSelectionFilter(FilterConstType filter)
{
m_selectionFilter = filter;
}
FilterConstType AssetSelectionModel::GetDisplayFilter() const
{
return m_displayFilter;
}
void AssetSelectionModel::SetDisplayFilter(FilterConstType filter)
{
m_displayFilter = filter;
}
bool AssetSelectionModel::GetMultiselect() const
{
return m_multiselect;
}
void AssetSelectionModel::SetMultiselect(bool multiselect)
{
m_multiselect = multiselect;
}
const AZStd::vector<AZ::Data::AssetId>& AssetSelectionModel::GetSelectedAssetIds() const
{
return m_selectedAssetIds;
}
void AssetSelectionModel::SetSelectedAssetIds(const AZStd::vector<AZ::Data::AssetId>& selectedAssetIds)
{
m_selectedAssetIds = selectedAssetIds;
}
void AssetSelectionModel::SetSelectedAssetId(const AZ::Data::AssetId& selectedAssetId)
{
m_selectedAssetIds.clear();
m_selectedAssetIds.push_back(selectedAssetId);
}
AZStd::vector<const AssetBrowserEntry*>& AssetSelectionModel::GetResults()
{
return m_results;
}
const AssetBrowserEntry* AssetSelectionModel::GetResult()
{
return m_results.front();
}
bool AssetSelectionModel::IsValid() const
{
return !m_results.empty();
}
void AssetSelectionModel::SetTitle(const QString& title)
{
m_title = title;
}
QString AssetSelectionModel::GetTitle() const
{
return m_title.isEmpty() ? GetDisplayFilter()->GetName() : m_title;
}
AssetSelectionModel AssetSelectionModel::AssetTypeSelection(const AZ::Data::AssetType& assetType, bool multiselect)
{
AssetSelectionModel selection;
AssetTypeFilter* assetTypeFilter = new AssetTypeFilter();
assetTypeFilter->SetAssetType(assetType);
assetTypeFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down);
auto assetTypeFilterPtr = FilterConstType(assetTypeFilter);
selection.SetDisplayFilter(assetTypeFilterPtr);
CompositeFilter* compFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND);
compFilter->AddFilter(assetTypeFilterPtr);
compFilter->AddFilter(ProductsNoFoldersFilter());
selection.SetSelectionFilter(FilterConstType(compFilter));
selection.SetMultiselect(multiselect);
return selection;
}
AssetSelectionModel AssetSelectionModel::AssetTypeSelection(const char* assetTypeName, bool multiselect)
{
EBusFindAssetTypeByName result(assetTypeName);
AZ::AssetTypeInfoBus::BroadcastResult(result, &AZ::AssetTypeInfo::GetAssetType);
return AssetTypeSelection(result.GetAssetType(), multiselect);
}
AssetSelectionModel AssetSelectionModel::AssetTypesSelection(const AZStd::vector<AZ::Data::AssetType>& assetTypes, bool multiselect)
{
AssetSelectionModel selection;
CompositeFilter* anyAssetTypeFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::OR);
anyAssetTypeFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down);
auto anyAssetTypeFilterPtr = FilterConstType(anyAssetTypeFilter);
for (const auto& assetType : assetTypes) {
AssetTypeFilter* assetTypeFilter = new AssetTypeFilter();
assetTypeFilter->SetAssetType(assetType);
anyAssetTypeFilter->AddFilter(FilterConstType(assetTypeFilter));
}
selection.SetDisplayFilter(anyAssetTypeFilterPtr);
CompositeFilter* compFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND);
compFilter->AddFilter(anyAssetTypeFilterPtr);
compFilter->AddFilter(ProductsNoFoldersFilter());
selection.SetSelectionFilter(FilterConstType(compFilter));
selection.SetMultiselect(multiselect);
return selection;
}
AssetSelectionModel AssetSelectionModel::AssetGroupSelection(const char* group, bool multiselect)
{
AssetSelectionModel selection;
AssetGroupFilter* assetGroupFilter = new AssetGroupFilter();
assetGroupFilter->SetAssetGroup(group);
assetGroupFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down);
auto assetGroupFilterPtr = FilterConstType(assetGroupFilter);
selection.SetDisplayFilter(assetGroupFilterPtr);
CompositeFilter* compFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND);
compFilter->AddFilter(assetGroupFilterPtr);
compFilter->AddFilter(ProductsNoFoldersFilter());
selection.SetSelectionFilter(FilterConstType(compFilter));
selection.SetMultiselect(multiselect);
return selection;
}
AssetSelectionModel AssetSelectionModel::EverythingSelection(bool multiselect)
{
AssetSelectionModel selection;
CompositeFilter* compFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::OR);
selection.SetDisplayFilter(FilterConstType(compFilter));
selection.SetSelectionFilter(ProductsNoFoldersFilter());
selection.SetMultiselect(multiselect);
return selection;
}
} // namespace AssetBrowser
} // namespace AzToolsFramework.
@@ -0,0 +1,79 @@
/*
* 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/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <QString>
using namespace AzToolsFramework::AssetBrowser;
namespace AzToolsFramework
{
namespace AssetBrowser
{
//! Used in combination with Asset Browser Picker to configure selection settings and store selection results
class AssetSelectionModel
{
public:
AZ_CLASS_ALLOCATOR(AssetSelectionModel, AZ::SystemAllocator, 0)
AssetSelectionModel();
~AssetSelectionModel() = default;
FilterConstType GetSelectionFilter() const;
void SetSelectionFilter(FilterConstType filter);
FilterConstType GetDisplayFilter() const;
void SetDisplayFilter(FilterConstType filter);
bool GetMultiselect() const;
void SetMultiselect(bool multiselect);
const AZStd::vector<AZ::Data::AssetId>& GetSelectedAssetIds() const;
void SetSelectedAssetIds(const AZStd::vector<AZ::Data::AssetId>& selectedAssetIds);
void SetSelectedAssetId(const AZ::Data::AssetId& selectedAssetId);
AZStd::vector<const AssetBrowserEntry*>& GetResults();
const AssetBrowserEntry* GetResult();
bool IsValid() const;
void SetTitle(const QString& title);
QString GetTitle() const;
static AssetSelectionModel AssetTypeSelection(const AZ::Data::AssetType& assetType, bool multiselect = false);
static AssetSelectionModel AssetTypeSelection(const char* assetTypeName, bool multiselect = false);
static AssetSelectionModel AssetTypesSelection(const AZStd::vector<AZ::Data::AssetType>& assetTypes, bool multiselect = false);
static AssetSelectionModel AssetGroupSelection(const char* group, bool multiselect = false);
static AssetSelectionModel EverythingSelection(bool multiselect = false);
private:
bool m_multiselect;
// some entries like folder should always be displayed, but not always selectable, thus 2 separate filters
FilterConstType m_selectionFilter;
FilterConstType m_displayFilter;
AZStd::vector<AZ::Data::AssetId> m_selectedAssetIds;
AZStd::vector<const AssetBrowserEntry*> m_results;
QString m_title;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,62 @@
/*
* 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/Asset/AssetTypeInfoBus.h>
#include <QString>
struct EBusFindAssetTypeByName
{
explicit EBusFindAssetTypeByName(const char* name)
: m_name(name)
, m_found(false)
, m_assetType(AZ::Data::AssetType::CreateNull())
{
}
AZ_FORCE_INLINE void operator=(const AZ::Data::AssetType& assetType)
{
if (m_found)
{
return;
}
if (MatchesName(assetType))
{
m_assetType = assetType;
m_found = true;
}
}
AZ::Data::AssetType GetAssetType() const
{
return m_assetType;
}
bool Found() const
{
return m_found;
}
private:
QString m_name;
bool m_found;
AZ::Data::AssetType m_assetType;
bool MatchesName(const AZ::Data::AssetType& assetType) const
{
QString name;
AZ::AssetTypeInfoBus::EventResult(name, assetType, &AZ::AssetTypeInfo::GetAssetTypeDisplayName);
return name.compare(m_name, Qt::CaseInsensitive) == 0;
}
};
@@ -0,0 +1,285 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/regex.h>
#include <AzCore/Serialization/Utils.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/Thumbnails/SourceControlThumbnail.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <QVariant>
#include <QMimeData>
#include <QUrl>
namespace AzToolsFramework
{
namespace AssetBrowser
{
QString AssetBrowserEntry::AssetEntryTypeToString(AssetEntryType assetEntryType)
{
switch (assetEntryType)
{
case AssetEntryType::Root:
return QObject::tr("Root");
case AssetEntryType::Folder:
return QObject::tr("Folder");
case AssetEntryType::Source:
return QObject::tr("Source");
case AssetEntryType::Product:
return QObject::tr("Product");
default:
return QObject::tr("Unknown");
}
}
const char* AssetBrowserEntry::m_columnNames[] =
{
"Name",
"Source ID",
"Fingerprint",
"Guid",
"ScanFolder ID",
"Product ID",
"Job ID",
"Sub ID",
"Asset Type",
"Class ID",
"Display Name"
};
AssetBrowserEntry::AssetBrowserEntry()
: QObject()
{}
AssetBrowserEntry::~AssetBrowserEntry()
{
if (EntryCache* cache = EntryCache::GetInstance())
{
cache->m_dirtyThumbnailsSet.erase(this);
}
RemoveChildren();
}
void AssetBrowserEntry::AddChild(AssetBrowserEntry* child)
{
child->m_parentAssetEntry = this;
UpdateChildPaths(child);
AssetBrowserModelRequestBus::Broadcast(&AssetBrowserModelRequests::BeginAddEntry, this);
child->m_row = static_cast<int>(m_children.size());
m_children.push_back(child);
AssetBrowserModelRequestBus::Broadcast(&AssetBrowserModelRequests::EndAddEntry, this);
AssetBrowserModelNotificationBus::Broadcast(&AssetBrowserModelNotifications::EntryAdded, child);
}
void AssetBrowserEntry::RemoveChild(AssetBrowserEntry* child)
{
if (!child || child->m_row >= m_children.size() || child != m_children[child->m_row])
{
return;
}
AZStd::unique_ptr<AssetBrowserEntry> childToRemove(m_children[child->m_row]);
if (!childToRemove)
{
return;
}
AssetBrowserModelRequestBus::Broadcast(&AssetBrowserModelRequests::BeginRemoveEntry, childToRemove.get());
auto it = m_children.erase(m_children.begin() + child->m_row);
// decrement the row of all children after the removed child
while (it != m_children.end())
{
(*it++)->m_row--;
}
child->m_parentAssetEntry = nullptr;
AssetBrowserModelRequestBus::Broadcast(&AssetBrowserModelRequests::EndRemoveEntry);
AssetBrowserModelNotificationBus::Broadcast(&AssetBrowserModelNotifications::EntryRemoved, childToRemove.get());
}
void AssetBrowserEntry::RemoveChildren()
{
while (!m_children.empty())
{
// child entries are removed from the end of the list, because this will incur minimum effort to update their rows
RemoveChild(*m_children.rbegin());
}
}
QVariant AssetBrowserEntry::data(int column) const
{
switch (static_cast<Column>(column))
{
case Column::Name:
return QString::fromUtf8(m_name.c_str());
case Column::DisplayName:
return m_displayName;
default:
return QVariant();
}
}
int AssetBrowserEntry::row() const
{
return m_row;
}
bool AssetBrowserEntry::FromMimeData(const QMimeData* mimeData, AZStd::vector<AssetBrowserEntry*>& entries)
{
if (!mimeData)
{
return false;
}
for (auto format : mimeData->formats())
{
if (format != GetMimeType())
{
continue;
}
QByteArray arrayData = mimeData->data(format);
AZ::IO::MemoryStream ms(arrayData.constData(), arrayData.size());
AssetBrowserEntry* entry = AZ::Utils::LoadObjectFromStream<AssetBrowserEntry>(ms, nullptr);
if (entry)
{
entries.push_back(entry);
}
}
return entries.size() > 0;
}
void AssetBrowserEntry::AddToMimeData(QMimeData* mimeData) const
{
if (!mimeData)
{
return;
}
AZStd::vector<char> buffer;
AZ::IO::ByteContainerStream<AZStd::vector<char> > byteStream(&buffer);
AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, this, this->RTTI_GetType());
QByteArray dataArray(buffer.data(), static_cast<int>(sizeof(char) * buffer.size()));
mimeData->setData(GetMimeType(), dataArray);
mimeData->setUrls({ QUrl::fromLocalFile(GetFullPath().c_str()) });
}
QString AssetBrowserEntry::GetMimeType()
{
return "editor/assetinformation/entry";
}
void AssetBrowserEntry::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<AssetBrowserEntry>()
->Field("m_name", &AssetBrowserEntry::m_name)
->Field("m_children", &AssetBrowserEntry::m_children)
->Field("m_row", &AssetBrowserEntry::m_row)
->Version(1);
}
}
const AZStd::string& AssetBrowserEntry::GetName() const
{
return m_name;
}
const QString& AssetBrowserEntry::GetDisplayName() const
{
return m_displayName;
}
const AZStd::string& AssetBrowserEntry::GetRelativePath() const
{
return m_relativePath;
}
const AZStd::string& AssetBrowserEntry::GetFullPath() const
{
return m_fullPath;
}
const AssetBrowserEntry* AssetBrowserEntry::GetChild(int index) const
{
if (index < m_children.size())
{
return m_children[index];
}
return nullptr;
}
AssetBrowserEntry* AssetBrowserEntry::GetChild(int index)
{
if (index < m_children.size())
{
return m_children[index];
}
return nullptr;
}
int AssetBrowserEntry::GetChildCount() const
{
return static_cast<int>(m_children.size());
}
AssetBrowserEntry* AssetBrowserEntry::GetParent() const
{
return m_parentAssetEntry;
}
void AssetBrowserEntry::SetThumbnailKey(SharedThumbnailKey thumbnailKey)
{
if (m_thumbnailKey)
{
disconnect(m_thumbnailKey.data(), &ThumbnailKey::ThumbnailUpdatedSignal, this, &AssetBrowserEntry::ThumbnailUpdated);
}
m_thumbnailKey = thumbnailKey;
connect(m_thumbnailKey.data(), &ThumbnailKey::ThumbnailUpdatedSignal, this, &AssetBrowserEntry::ThumbnailUpdated);
}
SharedThumbnailKey AssetBrowserEntry::GetThumbnailKey() const
{
return m_thumbnailKey;
}
void AssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
{
child->PathsUpdated();
}
void AssetBrowserEntry::PathsUpdated()
{
SetThumbnailKey(CreateThumbnailKey());
}
void AssetBrowserEntry::ThumbnailUpdated()
{
if (EntryCache* cache = EntryCache::GetInstance())
{
cache->m_dirtyThumbnailsSet.insert(this);
}
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Entries/moc_AssetBrowserEntry.cpp"
@@ -0,0 +1,166 @@
/*
* 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 <AzCore/std/string/string.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Math/Uuid.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <QObject>
#include <QModelIndex>
#endif
class QMimeData;
namespace AZ
{
class ReflectContext;
}
namespace AzToolsFramework
{
using namespace Thumbnailer;
namespace AssetBrowser
{
class RootAssetBrowserEntry;
class FolderAssetBrowserEntry;
class SourceAssetBrowserEntry;
class ProductAssetBrowserEntry;
//! AssetBrowserEntry is a base class for asset tree view entry
class AssetBrowserEntry
: public QObject
{
friend class AssetBrowserModel;
friend class AssetBrowserFilterModel;
friend class AssetBrowserEntry;
friend class RootAssetBrowserEntry;
friend class FolderAssetBrowserEntry;
friend class SourceAssetBrowserEntry;
friend class ProductAssetBrowserEntry;
Q_OBJECT
public:
enum class AssetEntryType
{
Root,
Folder,
Source,
Product
};
static QString AssetEntryTypeToString(AssetEntryType assetEntryType);
//NOTE: this list should be in sync with m_columnNames[] in the cpp file
enum class Column
{
Name,
SourceID,
Fingerprint,
Guid,
ScanFolderID,
ProductID,
JobID,
SubID,
AssetType,
ClassID,
DisplayName,
Count
};
static const char* m_columnNames[static_cast<int>(Column::Count)];
protected:
AssetBrowserEntry();
public:
AZ_RTTI(AssetBrowserEntry, "{67679F9E-055D-43BE-A2D0-FB4720E5302A}");
virtual ~AssetBrowserEntry();
virtual QVariant data(int column) const;
int row() const;
static bool FromMimeData(const QMimeData* mimeData, AZStd::vector<AssetBrowserEntry*>& entries);
void AddToMimeData(QMimeData* mimeData) const;
static QString GetMimeType();
static void Reflect(AZ::ReflectContext* context);
virtual AssetEntryType GetEntryType() const = 0;
//! Actual name of the asset or folder
const AZStd::string& GetName() const;
//! Display name represents how entry is shown in asset browser
const QString& GetDisplayName() const;
//! Return path relative to scan folder
const AZStd::string& GetRelativePath() const;
//! Return absolute path. If called on product, return source absolute path
const AZStd::string& GetFullPath() const;
//! Get immediate children of specific type
template<typename EntryType>
void GetChildren(AZStd::vector<const EntryType*>& entries) const;
//! Recurse through the tree down to get all entries of specific type
template<typename EntryType>
void GetChildrenRecursively(AZStd::vector<const EntryType*>& entries) const;
///! Utility function: Given a Qt QMimeData pointer, your callbackFunction will be called for each entry of that type it finds in there.
template <typename EntryType>
static void ForEachEntryInMimeData(const QMimeData* mimeData, AZStd::function<void(const EntryType*)> callbackFunction);
//! Get child by index
const AssetBrowserEntry* GetChild(int index) const;
AssetBrowserEntry* GetChild(int index);
//! Get number of children
int GetChildCount() const;
//! Get immediate parent
AssetBrowserEntry* GetParent() const;
virtual SharedThumbnailKey GetThumbnailKey() const;
void SetThumbnailKey(SharedThumbnailKey thumbnailKey);
virtual SharedThumbnailKey CreateThumbnailKey() = 0;
protected:
AZStd::string m_name;
QString m_displayName;
AZStd::string m_relativePath;
AZStd::string m_fullPath;
AZStd::vector<AssetBrowserEntry*> m_children;
AssetBrowserEntry* m_parentAssetEntry = nullptr;
virtual void AddChild(AssetBrowserEntry* child);
void RemoveChild(AssetBrowserEntry* child);
void RemoveChildren();
//! When child is added, its paths are updated relative to this entry
virtual void UpdateChildPaths(AssetBrowserEntry* child) const;
virtual void PathsUpdated();
protected Q_SLOTS:
virtual void ThumbnailUpdated();
private:
SharedThumbnailKey m_thumbnailKey;
//! index in its parent's m_children list
int m_row = 0;
AZ_DISABLE_COPY_MOVE(AssetBrowserEntry);
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
Q_DECLARE_METATYPE(const AzToolsFramework::AssetBrowser::AssetBrowserEntry*)
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.inl>
@@ -0,0 +1,71 @@
/*
* 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.
*
*/
namespace AzToolsFramework
{
namespace AssetBrowser
{
template<typename EntryType>
void AssetBrowserEntry::GetChildren(AZStd::vector<const EntryType*>& entries) const
{
entries.reserve(entries.size() + m_children.size());
for (auto child : m_children)
{
if (auto newEntry = azrtti_cast<const EntryType*>(child))
{
entries.push_back(newEntry);
}
}
}
template<typename EntryType>
void AssetBrowserEntry::GetChildrenRecursively(AZStd::vector<const EntryType*>& entries) const
{
if (auto newEntry = azrtti_cast<const EntryType*>(this))
{
entries.push_back(newEntry);
}
for (auto child : m_children)
{
child->GetChildrenRecursively<EntryType>(entries);
}
}
template <typename EntryType>
void AssetBrowserEntry::ForEachEntryInMimeData(const QMimeData* mimeData, AZStd::function<void(const EntryType*)> callbackFunction)
{
if ((!mimeData) || (!callbackFunction))
{
return;
}
AZStd::vector<AssetBrowserEntry*> entries;
if (!AssetBrowserEntry::FromMimeData(mimeData, entries))
{
// if mimedata does not even contain product entries, no point in proceeding.
return;
}
for (auto entry : entries)
{
// note that this works even if entry itself is a product already.
AZStd::vector<const EntryType*> matchingEntries;
entry->GetChildrenRecursively<EntryType>(matchingEntries);
for (const EntryType* matchingEntry : matchingEntries)
{
callbackFunction(matchingEntry);
}
}
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,72 @@
/*
* 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/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <AzCore/Module/Environment.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
using namespace AZ;
const char* EntryCache::s_environmentVariableName = "AssetBrowserEntryCache";
EnvironmentVariable<EntryCache*> EntryCache::g_globalInstance;
EntryCache* EntryCache::GetInstance()
{
if (!g_globalInstance)
{
g_globalInstance = Environment::FindVariable<EntryCache*>(s_environmentVariableName);
}
return g_globalInstance ? (*g_globalInstance) : nullptr;
}
void EntryCache::CreateInstance()
{
if (!g_globalInstance)
{
g_globalInstance = Environment::CreateVariable<EntryCache*>(s_environmentVariableName);
(*g_globalInstance) = nullptr;
}
AZ_Assert(!(*g_globalInstance), "You may not Create instance twice.");
EntryCache* newInstance = aznew EntryCache();
(*g_globalInstance) = newInstance;
}
void EntryCache::DestroyInstance()
{
AZ_Assert(g_globalInstance, "Invalid call to DestroyInstance - no instance exists.");
AZ_Assert(*g_globalInstance, "You can only call DestroyInstance if you have called CreateInstance.");
if (g_globalInstance)
{
delete *g_globalInstance;
}
(*g_globalInstance) = nullptr;
}
void EntryCache::Clear()
{
m_scanFolderIdMap.clear();
m_fileIdMap.clear();
m_sourceUuidMap.clear();
m_sourceIdMap.clear();
m_productAssetIdMap.clear();
m_dirtyThumbnailsSet.clear();
m_knownScanFolders.clear();
m_absolutePathToFileId.clear();
}
}
}
@@ -0,0 +1,65 @@
/*
* 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/Memory/SystemAllocator.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
namespace AZ
{
namespace Data
{
struct AssetId;
}
struct Uuid;
template<class T>
class EnvironmentVariable;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
class ProductAssetBrowserEntry;
class SourceAssetBrowserEntry;
/**
* This exists to handle memory caches that the AssetBrowser system needs
* which need to be available across DLLs but still with managed life cycles and not leaking memory
*/
class EntryCache
{
AZ_CLASS_ALLOCATOR(EntryCache, AZ::SystemAllocator, 0);
public:
static EntryCache* GetInstance();
// life cycle management:
static void CreateInstance();
static void DestroyInstance();
AZStd::unordered_map<AZ::s64, AssetBrowserEntry*> m_scanFolderIdMap;
AZStd::unordered_map<AZ::s64, AssetBrowserEntry*> m_fileIdMap;
AZStd::unordered_map<AZ::Uuid, SourceAssetBrowserEntry*> m_sourceUuidMap;
AZStd::unordered_map<AZ::s64, SourceAssetBrowserEntry*> m_sourceIdMap;
AZStd::unordered_map<AZ::Data::AssetId, ProductAssetBrowserEntry*> m_productAssetIdMap;
AZStd::unordered_map<AZ::s64, AZStd::string> m_knownScanFolders;
AZStd::unordered_map<AZStd::string, AZ::s64> m_absolutePathToFileId;
AZStd::unordered_set<AssetBrowserEntry*> m_dirtyThumbnailsSet;
static const char* s_environmentVariableName;
static AZ::EnvironmentVariable<EntryCache*> g_globalInstance;
void Clear();
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,55 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/FolderThumbnail.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
void FolderAssetBrowserEntry::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<FolderAssetBrowserEntry, AssetBrowserEntry>()
->Field("m_isGemsFolder", &FolderAssetBrowserEntry::m_isGemsFolder)
->Version(1);
}
}
AssetBrowserEntry::AssetEntryType FolderAssetBrowserEntry::GetEntryType() const
{
return AssetEntryType::Folder;
}
bool FolderAssetBrowserEntry::IsGemsFolder() const
{
return m_isGemsFolder;
}
void FolderAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
{
child->m_relativePath = m_relativePath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name;
child->m_fullPath = m_fullPath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name;
AssetBrowserEntry::UpdateChildPaths(child);
}
SharedThumbnailKey FolderAssetBrowserEntry::CreateThumbnailKey()
{
return MAKE_TKEY(FolderThumbnailKey, m_fullPath.c_str(), IsGemsFolder());
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,58 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Math/Uuid.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
namespace AZ
{
class ReflectContext;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
//! FolderAssetBrowserEntry is a class for any folder.
class FolderAssetBrowserEntry
: public AssetBrowserEntry
{
friend class RootAssetBrowserEntry;
public:
AZ_RTTI(FolderAssetBrowserEntry, "{938E6FCD-1582-4B63-A7EA-5C4FD28CABDC}", AssetBrowserEntry);
AZ_CLASS_ALLOCATOR(FolderAssetBrowserEntry, AZ::SystemAllocator, 0);
FolderAssetBrowserEntry() = default;
~FolderAssetBrowserEntry() override = default;
static void Reflect(AZ::ReflectContext* context);
AssetEntryType GetEntryType() const override;
bool IsGemsFolder() const;
SharedThumbnailKey CreateThumbnailKey() override;
protected:
void UpdateChildPaths(AssetBrowserEntry* child) const override;
private:
bool m_isGemsFolder = false;
AZ_DISABLE_COPY_MOVE(FolderAssetBrowserEntry);
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,143 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/AssetBrowserProductThumbnail.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <QVariant>
using namespace AzFramework;
using namespace AzToolsFramework::AssetDatabase;
namespace AzToolsFramework
{
namespace AssetBrowser
{
ProductAssetBrowserEntry::~ProductAssetBrowserEntry()
{
if (EntryCache* cache = EntryCache::GetInstance())
{
cache->m_productAssetIdMap.erase(m_assetId);
}
}
QVariant ProductAssetBrowserEntry::data(int column) const
{
switch (static_cast<Column>(column))
{
case Column::ProductID:
{
return QVariant(m_productId);
}
case Column::JobID:
{
return QVariant(m_jobId);
}
case Column::SubID:
{
return QVariant(m_assetId.m_subId);
}
default:
{
return AssetBrowserEntry::data(column);
}
}
}
void ProductAssetBrowserEntry::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ProductAssetBrowserEntry, AssetBrowserEntry>()
->Field("m_productId", &ProductAssetBrowserEntry::m_productId)
->Field("m_jobId", &ProductAssetBrowserEntry::m_jobId)
->Field("m_assetId", &ProductAssetBrowserEntry::m_assetId)
->Field("m_assetType", &ProductAssetBrowserEntry::m_assetType)
->Version(1);
}
}
AssetBrowserEntry::AssetEntryType ProductAssetBrowserEntry::GetEntryType() const
{
return AssetEntryType::Product;
}
AZ::s64 ProductAssetBrowserEntry::GetProductID() const
{
return m_productId;
}
AZ::s64 ProductAssetBrowserEntry::GetJobID() const
{
return m_jobId;
}
const AZ::Data::AssetId& ProductAssetBrowserEntry::GetAssetId() const
{
return m_assetId;
}
const AZ::Data::AssetType& ProductAssetBrowserEntry::GetAssetType() const
{
return m_assetType;
}
const AZStd::string& ProductAssetBrowserEntry::GetAssetTypeString() const
{
return m_assetTypeString;
}
ProductAssetBrowserEntry* ProductAssetBrowserEntry::GetProductByAssetId(const AZ::Data::AssetId& assetId)
{
if (EntryCache* cache = EntryCache::GetInstance())
{
// use find to avoid automatically inserting non-found products into the map as nullptr.
auto found = cache->m_productAssetIdMap.find(assetId);
if (found != cache->m_productAssetIdMap.end())
{
return found->second;
}
}
return nullptr;
}
void ProductAssetBrowserEntry::ThumbnailUpdated()
{
// if source is displaying product's thumbnail, then it needs to also listen to its ThumbnailUpdated
if (m_parentAssetEntry)
{
if (EntryCache* cache = EntryCache::GetInstance())
{
cache->m_dirtyThumbnailsSet.insert(m_parentAssetEntry);
}
}
}
SharedThumbnailKey AssetBrowser::ProductAssetBrowserEntry::GetThumbnailKey() const
{
return AssetBrowserEntry::GetThumbnailKey();
}
SharedThumbnailKey ProductAssetBrowserEntry::CreateThumbnailKey()
{
return MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, m_assetId);
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,66 @@
/*
* 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/std/string/string.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Math/Uuid.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <QObject>
#include <QModelIndex>
namespace AzToolsFramework
{
namespace AssetBrowser
{
//! ProductAssetBrowserEntry represents product entry.
class ProductAssetBrowserEntry
: public AssetBrowserEntry
{
friend class RootAssetBrowserEntry;
public:
AZ_RTTI(ProductAssetBrowserEntry, "{52C02087-D68B-4E9D-BB8A-01E43CE51BA2}", AssetBrowserEntry);
AZ_CLASS_ALLOCATOR(ProductAssetBrowserEntry, AZ::SystemAllocator, 0);
ProductAssetBrowserEntry() = default;
~ProductAssetBrowserEntry() override;
QVariant data(int column) const override;
static void Reflect(AZ::ReflectContext* context);
AssetEntryType GetEntryType() const override;
AZ::s64 GetProductID() const;
AZ::s64 GetJobID() const;
const AZ::Data::AssetId& GetAssetId() const;
const AZ::Data::AssetType& GetAssetType() const;
const AZStd::string& GetAssetTypeString() const;
SharedThumbnailKey GetThumbnailKey() const override;
SharedThumbnailKey CreateThumbnailKey() override;
static ProductAssetBrowserEntry* GetProductByAssetId(const AZ::Data::AssetId& assetId);
void ThumbnailUpdated() override;
private:
AZ::s64 m_productId = -1;
AZ::s64 m_jobId = -1;
AZ::Data::AssetId m_assetId;
AZ::Data::AssetType m_assetType;
AZStd::string m_assetTypeString;
AZ_DISABLE_COPY_MOVE(ProductAssetBrowserEntry);
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,472 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/std/containers/vector.h>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/IO/FileIO.h>
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <QVariant>
namespace AzToolsFramework
{
namespace AssetBrowser
{
const char* GEMS_FOLDER_NAME = "Gems";
RootAssetBrowserEntry::RootAssetBrowserEntry()
: AssetBrowserEntry()
{
EntryCache::CreateInstance();
}
void RootAssetBrowserEntry::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<RootAssetBrowserEntry>()
->Version(1);
}
}
AssetBrowserEntry::AssetEntryType RootAssetBrowserEntry::GetEntryType() const
{
return AssetEntryType::Root;
}
void RootAssetBrowserEntry::Update(const char* devPath)
{
RemoveChildren();
EntryCache::GetInstance()->Clear();
m_scanFolderOutputPrefixMap.clear();
m_devPath = devPath;
// there is no "Gems" scan folder registered in db, create one manually
auto gemFolder = aznew FolderAssetBrowserEntry();
gemFolder->m_name = m_devPath + AZ_CORRECT_DATABASE_SEPARATOR + GEMS_FOLDER_NAME;
gemFolder->m_displayName = GEMS_FOLDER_NAME;
gemFolder->m_isGemsFolder = true;
AddChild(gemFolder);
}
bool RootAssetBrowserEntry::IsInitialUpdate() const
{
return m_isInitialUpdate;
}
void RootAssetBrowserEntry::SetInitialUpdate(bool newValue)
{
m_isInitialUpdate = newValue;
}
void RootAssetBrowserEntry::AddScanFolder(const AssetDatabase::ScanFolderDatabaseEntry& scanFolderDatabaseEntry)
{
// if it doesn't exist on disk yet, don't create it on the gui, yet. We cache its info for later.
EntryCache::GetInstance()->m_knownScanFolders[scanFolderDatabaseEntry.m_scanFolderID] = scanFolderDatabaseEntry.m_scanFolder;
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(scanFolderDatabaseEntry.m_scanFolder.c_str()))
{
const auto scanFolder = CreateFolders(scanFolderDatabaseEntry.m_scanFolder.c_str(), this);
scanFolder->m_displayName = QString::fromUtf8(scanFolderDatabaseEntry.m_displayName.c_str());
EntryCache::GetInstance()->m_scanFolderIdMap[scanFolderDatabaseEntry.m_scanFolderID] = scanFolder;
}
if (!scanFolderDatabaseEntry.m_outputPrefix.empty())
{
m_scanFolderOutputPrefixMap[scanFolderDatabaseEntry.m_scanFolderID] = scanFolderDatabaseEntry.m_outputPrefix;
}
}
void RootAssetBrowserEntry::AddFile(const AssetDatabase::FileDatabaseEntry& fileDatabaseEntry)
{
using namespace AzFramework;
AssetBrowserEntry* scanFolder = nullptr;
auto itScanFolder = EntryCache::GetInstance()->m_scanFolderIdMap.find(fileDatabaseEntry.m_scanFolderPK);
if (itScanFolder == EntryCache::GetInstance()->m_scanFolderIdMap.end())
{
// this scanfolder hasn't been created it, it probably just now popped into existence, so create the element now:
// the only thing we need to know is what the path to the scanfolder is.
auto scanFolderDetailsIt = EntryCache::GetInstance()->m_knownScanFolders.find(fileDatabaseEntry.m_scanFolderPK);
if (scanFolderDetailsIt == EntryCache::GetInstance()->m_knownScanFolders.end())
{
// we can't even find the details.
AZ_Assert(false, "No scan folder with id %d", fileDatabaseEntry.m_scanFolderPK);
return;
}
scanFolder = CreateFolders(scanFolderDetailsIt->second.c_str(), this);
EntryCache::GetInstance()->m_scanFolderIdMap[fileDatabaseEntry.m_scanFolderPK] = scanFolder;
}
else
{
scanFolder = itScanFolder->second;
}
// verify that file does not already exist
const auto itFile = EntryCache::GetInstance()->m_fileIdMap.find(fileDatabaseEntry.m_fileID);
if (itFile != EntryCache::GetInstance()->m_fileIdMap.end())
{
AZ_Assert(false, "File %d already exists", fileDatabaseEntry.m_fileID);
return;
}
const char* filePath = GetScanFolderOutputAdjustedPath(fileDatabaseEntry, scanFolder);
AssetBrowserEntry* file;
// file can be either folder or actual file
if (fileDatabaseEntry.m_isFolder)
{
file = CreateFolders(filePath, scanFolder);
}
else
{
AZStd::string sourcePath;
AZStd::string sourceName;
AZStd::string sourceExtension;
StringFunc::Path::Split(filePath, nullptr, &sourcePath, &sourceName, &sourceExtension);
// if missing create folders leading to file's location and get immediate parent
// (we don't need to have fileIds for any folders created yet, they will be added later)
auto parent = CreateFolders(sourcePath.c_str(), scanFolder);
// for simplicity in AB, files are represented as sources, but they are missing SourceDatabaseEntry-specific information such as SourceUuid
auto source = aznew SourceAssetBrowserEntry();
source->m_name = (sourceName + sourceExtension).c_str();
source->m_fileId = fileDatabaseEntry.m_fileID;
source->m_displayName = QString::fromUtf8(source->m_name.c_str());
source->m_scanFolderId = fileDatabaseEntry.m_scanFolderPK;
source->m_extension = sourceExtension.c_str();
parent->AddChild(source);
file = source;
}
EntryCache::GetInstance()->m_fileIdMap[fileDatabaseEntry.m_fileID] = file;
AZStd::string fullPath = file->m_fullPath;
AzFramework::StringFunc::Path::Normalize(fullPath);
EntryCache::GetInstance()->m_absolutePathToFileId[fullPath] = fileDatabaseEntry.m_fileID;
}
bool RootAssetBrowserEntry::RemoveFile(const AZ::s64& fileId) const
{
auto fileIdNodeHandle = EntryCache::GetInstance()->m_fileIdMap.extract(fileId);
if (fileIdNodeHandle.empty())
{
// file may have previously been removed if its parent folder was deleted
// the order of messages received from AP is not always guaranteed,
// so if we receive "remove folder" before "remove file" then this file would no longer exist in cache
return true;
}
AssetBrowserEntry* entryToRemove = fileIdNodeHandle.mapped();
AZStd::string fullPath = entryToRemove->GetFullPath();
auto* source = azrtti_cast<SourceAssetBrowserEntry*>(entryToRemove);
if (source && source->m_sourceId != -1)
{
RemoveSource(source->m_sourceUuid);
}
if (auto parent = entryToRemove->GetParent())
{
parent->RemoveChild(entryToRemove);
}
AzFramework::StringFunc::Path::Normalize(fullPath);
EntryCache::GetInstance()->m_absolutePathToFileId.erase(fullPath);
return true;
}
bool RootAssetBrowserEntry::AddSource(const SourceWithFileID& sourceWithFileIdEntry) const
{
const auto itFile = EntryCache::GetInstance()->m_fileIdMap.find(sourceWithFileIdEntry.first);
if (itFile == EntryCache::GetInstance()->m_fileIdMap.end())
{
AZ_Warning("Asset Browser", false, "Add source failed: file %d not found, retrying later", sourceWithFileIdEntry.first);
return false;
}
auto source = azrtti_cast<SourceAssetBrowserEntry*>(itFile->second);
source->m_sourceId = sourceWithFileIdEntry.second.m_sourceID;
source->m_sourceUuid = sourceWithFileIdEntry.second.m_sourceGuid;
EntryCache::GetInstance()->m_sourceUuidMap[source->m_sourceUuid] = source;
EntryCache::GetInstance()->m_sourceIdMap[source->m_sourceId] = source;
return true;
}
void RootAssetBrowserEntry::RemoveSource(const AZ::Uuid& sourceUuid) const
{
const auto itSource = EntryCache::GetInstance()->m_sourceUuidMap.find(sourceUuid);
if (itSource == EntryCache::GetInstance()->m_sourceUuidMap.end())
{
return;
}
AZStd::vector<const ProductAssetBrowserEntry*> products;
itSource->second->GetChildren<ProductAssetBrowserEntry>(products);
for (const ProductAssetBrowserEntry* product : products)
{
RemoveProduct(product->m_assetId);
}
EntryCache::GetInstance()->m_sourceIdMap.erase(itSource->second->m_sourceId);
itSource->second->m_sourceId = -1;
itSource->second->m_sourceUuid = AZ::Uuid::CreateNull();
EntryCache::GetInstance()->m_sourceUuidMap.erase(itSource);
}
bool RootAssetBrowserEntry::AddProduct(const ProductWithUuid& productWithUuidDatabaseEntry)
{
auto itSource = EntryCache::GetInstance()->m_sourceUuidMap.find(productWithUuidDatabaseEntry.first);
if (itSource == EntryCache::GetInstance()->m_sourceUuidMap.end())
{
return false;
}
auto source = itSource->second;
if (!source)
{
AZ_Assert(false, "Source is invalid");
return false;
}
const AZ::Data::AssetId assetId(AZ::Data::AssetId(productWithUuidDatabaseEntry.first, productWithUuidDatabaseEntry.second.m_subID));
ProductAssetBrowserEntry* product;
const auto itProduct = EntryCache::GetInstance()->m_productAssetIdMap.find(assetId);
bool needsAdd = false;
if (itProduct != EntryCache::GetInstance()->m_productAssetIdMap.end())
{
product = itProduct->second;
}
else
{
product = aznew ProductAssetBrowserEntry();
needsAdd = true;
}
AZStd::string productPath;
AZStd::string productName;
AZStd::string productExtension;
AzFramework::StringFunc::Path::Split(productWithUuidDatabaseEntry.second.m_productName.c_str(),
nullptr, &productPath, &productName, &productExtension);
AZStd::string assetTypeName;
AZ::AssetTypeInfoBus::EventResult(assetTypeName, productWithUuidDatabaseEntry.second.m_assetType, &AZ::AssetTypeInfo::GetAssetTypeDisplayName);
AZStd::string displayName;
if (!assetTypeName.empty())
{
displayName = AZStd::string::format("%s (%s)", productName.c_str(), assetTypeName.c_str());
}
else
{
displayName = productName;
}
productName += productExtension;
product->m_name = productName;
product->m_displayName = QString::fromUtf8(displayName.c_str());
product->m_productId = productWithUuidDatabaseEntry.second.m_productID;
product->m_jobId = productWithUuidDatabaseEntry.second.m_jobPK;
product->m_assetId = assetId;
product->m_assetType = productWithUuidDatabaseEntry.second.m_assetType;
product->m_assetType.ToString(product->m_assetTypeString);
AZ::Data::AssetCatalogRequestBus::BroadcastResult(product->m_relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, assetId);
EntryCache::GetInstance()->m_productAssetIdMap[assetId] = product;
if (needsAdd)
{
// save this for last since it actually causes other lookups (like thumbnail lookups) to occur.
source->AddChild(product);
}
return true;
}
void RootAssetBrowserEntry::RemoveProduct(const AZ::Data::AssetId& assetId) const
{
const auto itProduct = EntryCache::GetInstance()->m_productAssetIdMap.find(assetId);
if (itProduct == EntryCache::GetInstance()->m_productAssetIdMap.end())
{
return;
}
if (auto parent = itProduct->second->GetParent())
{
parent->RemoveChild(itProduct->second);
}
}
FolderAssetBrowserEntry* RootAssetBrowserEntry::CreateFolder(const char* folderName, AssetBrowserEntry* parent)
{
auto it = AZStd::find_if(parent->m_children.begin(), parent->m_children.end(), [folderName](AssetBrowserEntry* entry)
{
if (!azrtti_istypeof<FolderAssetBrowserEntry*>(entry))
{
return false;
}
return AzFramework::StringFunc::Equal(entry->m_name.c_str(), folderName);
});
if (it != parent->m_children.end())
{
return azrtti_cast<FolderAssetBrowserEntry*>(*it);
}
const auto folder = aznew FolderAssetBrowserEntry();
folder->m_name = folderName;
folder->m_displayName = folderName;
parent->AddChild(folder);
return folder;
}
AssetBrowserEntry* RootAssetBrowserEntry::CreateFolders(const char* relativePath, AssetBrowserEntry* parent)
{
auto children(parent->m_children);
int n = 0;
// check if folder with the same name already exists
// step through every character in relativePath and compare to each child's relative path of suggested parent
// if a character @n in child's rel path mismatches character at n in relativePath, remove that child from further search
while (!children.empty() && relativePath[n])
{
AZStd::vector<AssetBrowserEntry*> toRemove;
for (auto child : children)
{
auto& childPath = azrtti_istypeof<RootAssetBrowserEntry*>(parent) ? child->m_fullPath : child->m_relativePath;
// child's path mismatched, remove it from search candidates
if (childPath.length() == n || childPath[n] != relativePath[n])
{
toRemove.push_back(child);
// it is possible that child may be a closer parent, substitute it as new potential parent
// e.g. child->m_relativePath = 'Gems', relativePath = 'Gems/Assets', old parent = root, new parent = Gems
if (childPath.length() == n && relativePath[n] == AZ_CORRECT_DATABASE_SEPARATOR)
{
parent = child;
relativePath += n; // advance relative path n characters since the parent has changed
n = 0; // Once the relative path pointer is advanced, reset n
}
}
}
for (auto entry : toRemove)
{
children.erase(AZStd::remove(children.begin(), children.end(), entry), children.end());
}
n++;
}
// filter out the remaining children that don't end with '/' or '\0'
// for example if folderName = "foo", while children may still remain with names like "foo123",
// which is not the same folder
AZStd::vector<AssetBrowserEntry*> toRemove;
for (auto child : children)
{
auto& childPath = azrtti_istypeof<RootAssetBrowserEntry*>(parent) ? child->m_fullPath : child->m_relativePath;
// check if there are non-null characters remaining @n
if (childPath.length() > n)
{
toRemove.push_back(child);
}
}
for (auto entry : toRemove)
{
children.erase(AZStd::remove(children.begin(), children.end(), entry), children.end());
}
// at least one child remains, this means the folder with this name already exists, return it
if (!children.empty())
{
parent = children.front();
}
// if it's a scanfolder, then do not create folders leading to it
// e.g. instead of 'C:\dev\SampleProject' just create 'SampleProject'
else if (parent->GetEntryType() == AssetEntryType::Root)
{
AZStd::string folderName;
AzFramework::StringFunc::Path::Split(relativePath, nullptr, nullptr, &folderName);
parent = CreateFolder(folderName.c_str(), parent);
parent->m_fullPath = relativePath;
}
// otherwise create all missing folders
else
{
n = 0;
AZStd::string folderName(strlen(relativePath) + 1, '\0');
// iterate through relativePath until the first '/'
while (relativePath[n] && relativePath[n] != AZ_CORRECT_DATABASE_SEPARATOR)
{
folderName[n] = relativePath[n];
n++;
}
if (n > 0)
{
parent = CreateFolder(folderName.c_str(), parent);
}
// n+1 also skips the '/' character
if (relativePath[n] && relativePath[n + 1])
{
parent = CreateFolders(relativePath + n + 1, parent);
}
}
return parent;
}
void RootAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
{
child->m_relativePath = child->m_name;
child->m_fullPath = child->m_name;
AssetBrowserEntry::UpdateChildPaths(child);
}
SharedThumbnailKey RootAssetBrowserEntry::CreateThumbnailKey()
{
return MAKE_TKEY(ThumbnailKey);
}
const char* RootAssetBrowserEntry::GetScanFolderOutputAdjustedPath(const AssetDatabase::FileDatabaseEntry& fileDatabaseEntry, const AssetBrowserEntry* scanFolder)
{
Q_UNUSED(scanFolder);
const char* filePath = fileDatabaseEntry.m_fileName.c_str();
// adjust for output prefixes on scan folders (i.e. "editor")
auto itScanFolderOutputPrefix = m_scanFolderOutputPrefixMap.find(fileDatabaseEntry.m_scanFolderPK);
if (itScanFolderOutputPrefix != m_scanFolderOutputPrefixMap.end())
{
const AZStd::string& outputPrefix = itScanFolderOutputPrefix->second;
// Check if the input path starts with the output prefix.
// If it doesn't, something probably went seriously wrong,
// or someone is calling this function with an absolute path.
bool pathStartsWithPrefix = ((strncmp(filePath, outputPrefix.c_str(), outputPrefix.length()) == 0) && (fileDatabaseEntry.m_fileName.length() > (outputPrefix.length() + 1)));
AZ_Warning("Asset Browser", pathStartsWithPrefix, "Entry %s reported as under a ScanFolder (%s) with an 'output=%s', but the new entry does not begin with the output prefix! RootAssetBrowserEntry::GetScanFolderOutputAdjustedPath expects relative paths, not absolute; treating the input path as if it does not contain the ScanFolder output prefix.", filePath, scanFolder->m_name.c_str(), outputPrefix.c_str());
if (pathStartsWithPrefix)
{
// move the beginning ahead by the output prefix plus the separator
filePath += (outputPrefix.length() + 1);
}
}
return filePath;
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,98 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/string/string.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Math/Uuid.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <QObject>
#include <QModelIndex>
class QMimeData;
namespace AZ
{
class ReflectContext;
}
namespace AzToolsFramework
{
using namespace Thumbnailer;
namespace AssetDatabase
{
class ScanFolderDatabaseEntry;
class FileDatabaseEntry;
class SourceDatabaseEntry;
class ProductDatabaseEntry;
class CombinedDatabaseEntry;
}
namespace AssetBrowser
{
using ProductWithUuid = AZStd::pair<AZ::Uuid, AssetDatabase::ProductDatabaseEntry>;
using SourceWithFileID = AZStd::pair<AZ::s64, AssetDatabase::SourceDatabaseEntry>;
//! RootAssetBrowserEntry is a root node for Asset Browser tree view, it's not related to any asset path.
class RootAssetBrowserEntry
: public AssetBrowserEntry
{
public:
AZ_RTTI(RootAssetBrowserEntry, "{A35CA80E-E1EB-420B-8BFE-B7792E3CCEDB}");
AZ_CLASS_ALLOCATOR(RootAssetBrowserEntry, AZ::SystemAllocator, 0);
RootAssetBrowserEntry();
static void Reflect(AZ::ReflectContext* context);
AssetEntryType GetEntryType() const override;
//! Update root node to new dev location
void Update(const char* devPath);
void AddScanFolder(const AssetDatabase::ScanFolderDatabaseEntry& scanFolderDatabaseEntry);
void AddFile(const AssetDatabase::FileDatabaseEntry& fileDatabaseEntry);
bool RemoveFile(const AZ::s64& fileId) const;
bool AddSource(const SourceWithFileID& sourceWithFileIdEntry) const;
void RemoveSource(const AZ::Uuid& sourceUuid) const;
bool AddProduct(const ProductWithUuid& productWithUuidEntry);
void RemoveProduct(const AZ::Data::AssetId& assetId) const;
SharedThumbnailKey CreateThumbnailKey() override;
bool IsInitialUpdate() const;
void SetInitialUpdate(bool newValue);
protected:
void UpdateChildPaths(AssetBrowserEntry* child) const override;
private:
AZ_DISABLE_COPY_MOVE(RootAssetBrowserEntry);
AZStd::string m_devPath;
AZStd::unordered_map<AZ::s64, AZStd::string> m_scanFolderOutputPrefixMap;
//! Create folder entry child
FolderAssetBrowserEntry* CreateFolder(const char* folderName, AssetBrowserEntry* parent);
//! Recursively create folder structure leading to relative path from parent
AssetBrowserEntry* CreateFolders(const char* relativePath, AssetBrowserEntry* parent);
//! Get the path for the fileDatabaseEntry, offset by the output prefix for the scan folder ancestor, if it's been specified and if it's appropriate
const char* GetScanFolderOutputAdjustedPath(const AssetDatabase::FileDatabaseEntry& fileDatabaseEntry, const AssetBrowserEntry* scanFolder);
bool m_isInitialUpdate = false;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,181 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/containers/vector.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.h>
#include <AzToolsFramework/Thumbnails/SourceControlThumbnail.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <QVariant>
namespace AzToolsFramework
{
namespace AssetBrowser
{
SourceAssetBrowserEntry::~SourceAssetBrowserEntry()
{
if (EntryCache* cache = EntryCache::GetInstance())
{
cache->m_fileIdMap.erase(m_fileId);
AZStd::string fullPath = m_fullPath;
AzFramework::StringFunc::Path::Normalize(fullPath);
cache->m_absolutePathToFileId.erase(fullPath);
if (m_sourceId != -1)
{
cache->m_sourceUuidMap.erase(m_sourceUuid);
cache->m_sourceIdMap.erase(m_sourceId);
}
}
}
QVariant SourceAssetBrowserEntry::data(int column) const
{
switch (static_cast<Column>(column))
{
case Column::SourceID:
{
return QVariant(m_sourceId);
}
case Column::ScanFolderID:
{
return QVariant(m_scanFolderId);
}
default:
{
return AssetBrowserEntry::data(column);
}
}
}
void SourceAssetBrowserEntry::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<SourceAssetBrowserEntry, AssetBrowserEntry>()
->Version(2)
->Field("m_sourceId", &SourceAssetBrowserEntry::m_sourceId)
->Field("m_scanFolderId", &SourceAssetBrowserEntry::m_scanFolderId)
->Field("m_sourceUuid", &SourceAssetBrowserEntry::m_sourceUuid)
->Field("m_extension", &SourceAssetBrowserEntry::m_extension);
}
}
AssetBrowserEntry::AssetEntryType SourceAssetBrowserEntry::GetEntryType() const
{
return AssetEntryType::Source;
}
const AZStd::string& SourceAssetBrowserEntry::GetExtension() const
{
return m_extension;
}
AZ::s64 SourceAssetBrowserEntry::GetFileID() const
{
return m_fileId;
}
const AZ::Uuid& SourceAssetBrowserEntry::GetSourceUuid() const
{
return m_sourceUuid;
}
AZ::s64 SourceAssetBrowserEntry::GetSourceID() const
{
return m_sourceId;
}
AZ::s64 SourceAssetBrowserEntry::GetScanFolderID() const
{
return m_scanFolderId;
}
AZ::Data::AssetType SourceAssetBrowserEntry::GetPrimaryAssetType() const
{
AZStd::vector<const ProductAssetBrowserEntry*> products;
GetChildren<ProductAssetBrowserEntry>(products);
for (const ProductAssetBrowserEntry* product : products)
{
AZ::Data::AssetType productType = product->GetAssetType();
if (productType != AZ::Data::s_invalidAssetType)
{
return productType;
}
}
return AZ::Data::s_invalidAssetType;
}
bool SourceAssetBrowserEntry::HasProductType(const AZ::Data::AssetType& assetType) const
{
AZStd::vector<const ProductAssetBrowserEntry*> products;
GetChildren<ProductAssetBrowserEntry>(products);
for (const ProductAssetBrowserEntry* product : products)
{
AZ::Data::AssetType productType = product->GetAssetType();
if (productType == assetType)
{
return true;
}
}
return false;
}
const SourceAssetBrowserEntry* SourceAssetBrowserEntry::GetSourceByUuid(const AZ::Uuid& sourceUuid)
{
if (EntryCache* cache = EntryCache::GetInstance())
{
return cache->m_sourceUuidMap[sourceUuid];
}
return nullptr;
}
void SourceAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
{
child->m_fullPath = m_fullPath;
AssetBrowserEntry::UpdateChildPaths(child);
}
void SourceAssetBrowserEntry::PathsUpdated()
{
AssetBrowserEntry::PathsUpdated();
UpdateSourceControlThumbnail();
}
void SourceAssetBrowserEntry::UpdateSourceControlThumbnail()
{
if (m_sourceControlThumbnailKey)
{
disconnect(m_sourceControlThumbnailKey.data(), &ThumbnailKey::ThumbnailUpdatedSignal, this, &AssetBrowserEntry::ThumbnailUpdated);
}
m_sourceControlThumbnailKey = MAKE_TKEY(SourceControlThumbnailKey, m_fullPath.c_str());
connect(m_sourceControlThumbnailKey.data(), &ThumbnailKey::ThumbnailUpdatedSignal, this, &AssetBrowserEntry::ThumbnailUpdated);
}
SharedThumbnailKey SourceAssetBrowserEntry::CreateThumbnailKey()
{
return MAKE_TKEY(SourceThumbnailKey, m_fullPath.c_str());
}
SharedThumbnailKey SourceAssetBrowserEntry::GetSourceControlThumbnailKey() const
{
return m_sourceControlThumbnailKey;
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,83 @@
/*
* 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/std/string/string.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Math/Uuid.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <QObject>
#include <QModelIndex>
namespace AZ
{
class ReflectContext;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
//! SourceAssetBrowserEntry represents source entry.
class SourceAssetBrowserEntry
: public AssetBrowserEntry
{
friend class RootAssetBrowserEntry;
public:
AZ_RTTI(SourceAssetBrowserEntry, "{9FD4FF76-4CC3-4E96-953F-5BF63C2E1F1D}", AssetBrowserEntry);
AZ_CLASS_ALLOCATOR(SourceAssetBrowserEntry, AZ::SystemAllocator, 0);
SourceAssetBrowserEntry() = default;
~SourceAssetBrowserEntry() override;
QVariant data(int column) const override;
static void Reflect(AZ::ReflectContext* context);
AssetEntryType GetEntryType() const override;
const AZStd::string& GetExtension() const;
AZ::s64 GetFileID() const;
AZ::s64 GetSourceID() const;
AZ::s64 GetScanFolderID() const;
//! returns the asset type of the first child (product) that isn't an invalid type.
AZ::Data::AssetType GetPrimaryAssetType() const;
//! Returns true if any children (products) are the given asset type
bool HasProductType(const AZ::Data::AssetType& assetType) const;
SharedThumbnailKey CreateThumbnailKey() override;
SharedThumbnailKey GetSourceControlThumbnailKey() const;
const AZ::Uuid& GetSourceUuid() const;
static const SourceAssetBrowserEntry* GetSourceByUuid(const AZ::Uuid& sourceUuid);
protected:
void UpdateChildPaths(AssetBrowserEntry* child) const override;
void PathsUpdated() override;
private:
AZStd::string m_extension;
AZ::s64 m_fileId = -1;
AZ::s64 m_sourceId = -1;
AZ::s64 m_scanFolderId = -1;
AZ::Uuid m_sourceUuid;
SharedThumbnailKey m_sourceControlThumbnailKey;
void UpdateSourceControlThumbnail();
AZ_DISABLE_COPY_MOVE(SourceAssetBrowserEntry);
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,48 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/base.h>
#include <AzToolsFramework/AssetBrowser/Previewer/EmptyPreviewer.h>
// Disables warning messages triggered by the Qt library
// 4251: class needs to have dll-interface to be used by clients of class
// 4800: forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include "AssetBrowser/Previewer/ui_EmptyPreviewer.h"
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetBrowser
{
const QString EmptyPreviewer::Name{ QStringLiteral("EmptyPreviewer") };
EmptyPreviewer::EmptyPreviewer(QWidget* parent)
: Previewer(parent)
, m_ui(new Ui::EmptyPreviewerClass())
{
m_ui->setupUi(this);
}
void EmptyPreviewer::Display(const AssetBrowserEntry* entry)
{
AZ_UNUSED(entry);
}
const QString& EmptyPreviewer::GetName() const
{
return Name;
}
}
}
#include <AssetBrowser/Previewer/moc_EmptyPreviewer.cpp>
@@ -0,0 +1,50 @@
/*
* 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 <AzToolsFramework/AssetBrowser/Previewer/Previewer.h>
#endif
namespace Ui
{
class EmptyPreviewerClass;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserEntry;
//! Widget displaying "no preview available" text
class EmptyPreviewer
: public Previewer
{
Q_OBJECT
public:
EmptyPreviewer(QWidget* parent = nullptr);
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::AssetBrowser::Previewer
//////////////////////////////////////////////////////////////////////////
void Clear() const override {}
void Display(const AssetBrowserEntry* entry) override;
const QString& GetName() const override;
static const QString Name;
private:
QScopedPointer<Ui::EmptyPreviewerClass> m_ui;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EmptyPreviewerClass</class>
<widget class="QWidget" name="EmptyPreviewerClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>207</width>
<height>275</height>
</rect>
</property>
<property name="windowTitle">
<string>Preview</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="m_noPreviewWidget" native="true">
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QLabel" name="m_noPreviewText">
<property name="font">
<font>
<family>Open Sans</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string>No preview available</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,26 @@
/*
* 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/AssetBrowser/Previewer/Previewer.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
Previewer::Previewer(QWidget* parent)
: QWidget(parent)
{
}
}
}
#include <AssetBrowser/Previewer/moc_Previewer.cpp>
@@ -0,0 +1,52 @@
/*
* 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/PlatformDef.h>
#if !defined(Q_MOC_RUN)
// Disables warning messages triggered by the Qt library
// 4251: class needs to have dll-interface to be used by clients of class
// 4800: forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QWidget>
AZ_POP_DISABLE_WARNING
#endif
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserEntry;
//! A base class for Asset Browser previewer.
//! To implement your custom previewer:
//! 1. Derive your previewer widget from this class.
//! 2. Implement custom PreviewerFactory.
//! 3. Register PreviewerFactory with PreviewerRequestBus::RegisterFactory EBus.
//! Note: if there are multiple factories handling same entry type, last one registered will be selected.
class Previewer
: public QWidget
{
Q_OBJECT
public:
Previewer(QWidget* parent = nullptr);
//! Clear previewer
virtual void Clear() const = 0;
//! Display asset preview for specific asset browser entry
virtual void Display(const AssetBrowserEntry* entry) = 0;
//! Get name of the previewer (this should be unique to other previewers)
virtual const QString& GetName() const = 0;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,47 @@
/*
* 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/EBus/EBus.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserEntry;
class PreviewerFactory;
//! Public requests to previewer
class PreviewerRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::MultipleAndOrdered;
//! Get previewer factory that can handle provided entry if one exists
virtual const PreviewerFactory* GetPreviewerFactory(const AssetBrowserEntry* entry) const = 0;
//! Priority of current request handler when multiple handlers for the same entry instance exist
//! Higher priority takes precedence
virtual int GetPreviewerPriority() const { return 0; }
//! Called by ebus to sort request handlers by their priority
bool Compare(const PreviewerRequests* other) const
{
return GetPreviewerPriority() > other->GetPreviewerPriority(); // higher comes first!
}
};
using PreviewerRequestBus = AZ::EBus<PreviewerRequests>;
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,37 @@
/*
* 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
class QWidget;
class QString;
namespace AzToolsFramework
{
namespace AssetBrowser
{
class Previewer;
class AssetBrowserEntry;
//! Handles creating concrete instances of previewers.
class PreviewerFactory
{
public:
//! Create new instance of previewer.
//! Its lifecycle is managed by the parent widget, so you should not destroy it manually.
virtual Previewer* CreatePreviewer(QWidget* parent = nullptr) const = 0;
//! Checks if previewers created by this factory can display provided entry.
virtual bool IsEntrySupported(const AssetBrowserEntry* entry) const = 0;
//! Returns unique name for the factory (typically it's the name of previewer type it generates).
virtual const QString& GetName() const = 0;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,87 @@
/*
* 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 <QLayout>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h>
#include <AzToolsFramework/AssetBrowser/Previewer/PreviewerBus.h>
#include <AzToolsFramework/AssetBrowser/Previewer/PreviewerFactory.h>
#include <AzToolsFramework/AssetBrowser/Previewer/Previewer.h>
#include <AzToolsFramework/AssetBrowser/Previewer/EmptyPreviewer.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
PreviewerFrame::PreviewerFrame(QWidget* parent)
: QFrame(parent)
{
setLayout(new QVBoxLayout);
Clear();
}
void PreviewerFrame::Display(const AssetBrowserEntry* entry)
{
const auto factory = FindPreviewerFactory(entry);
if (factory)
{
if (!m_previewer || m_previewer->GetName() != factory->GetName())
{
InstallPreviewer(factory->CreatePreviewer(this));
}
m_previewer->Display(entry);
}
else
{
Clear();
}
}
void PreviewerFrame::Clear()
{
if (m_previewer && m_previewer->GetName() == EmptyPreviewer::Name)
{
return;
}
InstallPreviewer(new EmptyPreviewer());
}
const PreviewerFactory* PreviewerFrame::FindPreviewerFactory(const AssetBrowserEntry* entry) const
{
AZ::EBusAggregateResults<const PreviewerFactory*> results;
PreviewerRequestBus::BroadcastResult(results, &PreviewerRequests::GetPreviewerFactory, entry);
for (const auto factory : results.values)
{
if (factory)
{
return factory;
}
}
return nullptr;
}
void PreviewerFrame::InstallPreviewer(Previewer* previewer)
{
if (m_previewer)
{
delete m_previewer;
m_previewer = nullptr;
}
m_previewer = previewer;
layout()->addWidget(previewer);
}
} // AssetBrowser
} // AzToolsFramework
#include <AssetBrowser/Previewer/moc_PreviewerFrame.cpp>
@@ -0,0 +1,48 @@
/*
* 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 <QFrame>
#endif
namespace AzToolsFramework
{
namespace AssetBrowser
{
class PreviewerFactory;
class AssetBrowserEntry;
class AssetPreviewer;
class Previewer;
//! Widget managing previewers.
class PreviewerFrame
: public QFrame
{
Q_OBJECT
public:
PreviewerFrame(QWidget* parent = nullptr);
//! Preview an asset browser entry with a corresponding previewer.
//! If none are registered to preview this entry, empty previewer will be displayed.
void Display(const AssetBrowserEntry* entry);
//! Unload current previewer and show empty previewer.
void Clear();
private:
const PreviewerFactory* FindPreviewerFactory(const AssetBrowserEntry* entry) const;
void InstallPreviewer(Previewer* previewer);
Previewer* m_previewer = nullptr;
};
} // AssetBrowser
} // AzToolsFramework
@@ -0,0 +1,616 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/EBusFindAssetTypeByName.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
namespace
{
bool StringMatch(const QString& searched, const QString& text)
{
return text.contains(searched, Qt::CaseInsensitive);
}
//! Intersect operation between two sets which then overwrites result
void Intersect(AZStd::vector<const AssetBrowserEntry*>& result, AZStd::vector<const AssetBrowserEntry*>& set)
{
// inefficient, but sets are tiny so probably not worth the optimization effort
AZStd::vector<const AssetBrowserEntry*> intersection;
for (auto entry : result)
{
if (AZStd::find(set.begin(), set.end(), entry) != set.end())
{
intersection.push_back(entry);
}
}
result = intersection;
}
//! Insert an entry if it doesn't already exist
void Join(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry)
{
if (AZStd::find(result.begin(), result.end(), entry) == result.end())
{
result.push_back(entry);
}
}
//! Join operation between two sets which then overwrites result
void Join(AZStd::vector<const AssetBrowserEntry*>& result, AZStd::vector<const AssetBrowserEntry*>& set)
{
AZStd::vector<const AssetBrowserEntry*> unionResult;
for (auto entry : set)
{
Join(result, entry);
}
}
//! Expand all children recursively and write to result
void ExpandDown(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry)
{
Join(result, entry);
AZStd::vector<const AssetBrowserEntry*> children;
entry->GetChildren<AssetBrowserEntry>(children);
for (auto child : children)
{
ExpandDown(result, child);
}
}
//! Expand all entries that are either parent or child relationship to the entry and write to result
void Expand(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry)
{
auto parent = entry->GetParent();
while (parent && parent->GetEntryType() != AssetBrowserEntry::AssetEntryType::Root)
{
Join(result, parent);
parent = parent->GetParent();
}
ExpandDown(result, entry);
}
}
//////////////////////////////////////////////////////////////////////////
// AssetBrowserEntryFilter
//////////////////////////////////////////////////////////////////////////
AssetBrowserEntryFilter::AssetBrowserEntryFilter()
: m_direction(None)
{
}
bool AssetBrowserEntryFilter::Match(const AssetBrowserEntry* entry) const
{
if (MatchInternal(entry))
{
return true;
}
if (m_direction & Up)
{
auto parent = entry->GetParent();
while (parent && parent->GetEntryType() != AssetBrowserEntry::AssetEntryType::Root)
{
if (MatchInternal(parent))
{
return true;
}
parent = parent->GetParent();
}
}
if (m_direction & Down)
{
AZStd::vector<const AssetBrowserEntry*> children;
entry->GetChildren<AssetBrowserEntry>(children);
for (auto child : children)
{
if (MatchDown(child))
{
return true;
}
}
}
return false;
}
void AssetBrowserEntryFilter::Filter(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const
{
FilterInternal(result, entry);
if (m_direction & Up)
{
auto parent = entry->GetParent();
while (parent && parent->GetEntryType() != AssetBrowserEntry::AssetEntryType::Root)
{
FilterInternal(result, parent);
parent = parent->GetParent();
}
}
if (m_direction & Down)
{
AZStd::vector<const AssetBrowserEntry*> children;
entry->GetChildren<AssetBrowserEntry>(children);
for (auto child : children)
{
FilterDown(result, child);
}
}
}
QString AssetBrowserEntryFilter::GetName() const
{
return m_name.isEmpty() ? GetNameInternal() : m_name;
}
void AssetBrowserEntryFilter::SetName(const QString& name)
{
m_name = name;
}
const QString& AssetBrowserEntryFilter::GetTag() const
{
return m_tag;
}
void AssetBrowserEntryFilter::SetTag(const QString& tag)
{
m_tag = tag;
}
void AssetBrowserEntryFilter::SetFilterPropagation(int direction)
{
m_direction = direction;
}
void AssetBrowserEntryFilter::FilterInternal(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const
{
if (MatchInternal(entry))
{
Join(result, entry);
}
}
bool AssetBrowserEntryFilter::MatchDown(const AssetBrowserEntry* entry) const
{
if (MatchInternal(entry))
{
return true;
}
AZStd::vector<const AssetBrowserEntry*> children;
entry->GetChildren<AssetBrowserEntry>(children);
for (auto child : children)
{
if (MatchDown(child))
{
return true;
}
}
return false;
}
void AssetBrowserEntryFilter::FilterDown(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const
{
if (MatchInternal(entry))
{
Join(result, entry);
}
AZStd::vector<const AssetBrowserEntry*> children;
entry->GetChildren<AssetBrowserEntry>(children);
for (auto child : children)
{
FilterDown(result, child);
}
}
//////////////////////////////////////////////////////////////////////////
// StringFilter
//////////////////////////////////////////////////////////////////////////
StringFilter::StringFilter()
: m_filterString("") {}
void StringFilter::SetFilterString(const QString& filterString)
{
m_filterString = filterString;
Q_EMIT updatedSignal();
}
QString StringFilter::GetNameInternal() const
{
return m_filterString;
}
bool StringFilter::MatchInternal(const AssetBrowserEntry* entry) const
{
// no filter string matches any asset
if (m_filterString.isEmpty())
{
return true;
}
// entry's name matches search pattern
if (StringMatch(m_filterString, entry->GetDisplayName()))
{
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
// AssetTypeFilter
//////////////////////////////////////////////////////////////////////////
AssetTypeFilter::AssetTypeFilter()
: m_assetType(AZ::Data::AssetType::CreateNull()) {}
void AssetTypeFilter::SetAssetType(AZ::Data::AssetType assetType)
{
m_assetType = assetType;
Q_EMIT updatedSignal();
}
void AssetTypeFilter::SetAssetType(const char* assetTypeName)
{
EBusFindAssetTypeByName result(assetTypeName);
AZ::AssetTypeInfoBus::BroadcastResult(result, &AZ::AssetTypeInfo::GetAssetType);
SetAssetType(result.GetAssetType());
}
AZ::Data::AssetType AssetTypeFilter::GetAssetType() const
{
return m_assetType;
}
QString AssetTypeFilter::GetNameInternal() const
{
QString name;
AZ::AssetTypeInfoBus::EventResult(name, m_assetType, &AZ::AssetTypeInfo::GetAssetTypeDisplayName);
return name;
}
bool AssetTypeFilter::MatchInternal(const AssetBrowserEntry* entry) const
{
// this filter only works on products.
if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Product)
{
if (m_assetType.IsNull())
{
return true;
}
if (static_cast<const ProductAssetBrowserEntry*>(entry)->GetAssetType() == m_assetType)
{
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
// AssetGroupFilter
//////////////////////////////////////////////////////////////////////////
AssetGroupFilter::AssetGroupFilter()
: m_group("All")
{
}
void AssetGroupFilter::SetAssetGroup(const QString& group)
{
m_group = group;
}
const QString& AssetGroupFilter::GetAssetTypeGroup() const
{
return m_group;
}
QString AssetGroupFilter::GetNameInternal() const
{
return m_group;
}
bool AssetGroupFilter::MatchInternal(const AssetBrowserEntry* entry) const
{
// this filter only works on products.
if (entry->GetEntryType() != AssetBrowserEntry::AssetEntryType::Product)
{
return false;
}
if (m_group.compare("All", Qt::CaseInsensitive) == 0)
{
return true;
}
auto product = static_cast<const ProductAssetBrowserEntry*>(entry);
QString group;
AZ::AssetTypeInfoBus::EventResult(group, product->GetAssetType(), &AZ::AssetTypeInfo::GetGroup);
if (m_group.compare("Other", Qt::CaseInsensitive) == 0 && group.isEmpty())
{
return true;
}
return (m_group.compare(group, Qt::CaseInsensitive) == 0);
}
//////////////////////////////////////////////////////////////////////////
// EntryTypeFilter
//////////////////////////////////////////////////////////////////////////
EntryTypeFilter::EntryTypeFilter()
: m_entryType(AssetBrowserEntry::AssetEntryType::Product) {}
void EntryTypeFilter::SetEntryType(AssetBrowserEntry::AssetEntryType entryType)
{
m_entryType = entryType;
}
AssetBrowserEntry::AssetEntryType EntryTypeFilter::GetEntryType() const
{
return m_entryType;
}
QString EntryTypeFilter::GetNameInternal() const
{
return AssetBrowserEntry::AssetEntryTypeToString(m_entryType);
}
bool EntryTypeFilter::MatchInternal(const AssetBrowserEntry* entry) const
{
return entry->GetEntryType() == m_entryType;
}
//////////////////////////////////////////////////////////////////////////
// CompositeFilter
//////////////////////////////////////////////////////////////////////////
CompositeFilter::CompositeFilter(LogicOperatorType logicOperator)
: m_logicOperator(logicOperator)
, m_emptyResult(true) {}
void CompositeFilter::AddFilter(FilterConstType filter)
{
connect(filter.data(), &AssetBrowserEntryFilter::updatedSignal, this, &AssetBrowserEntryFilter::updatedSignal, Qt::UniqueConnection);
m_subFilters.append(filter);
Q_EMIT updatedSignal();
}
void CompositeFilter::RemoveFilter(FilterConstType filter)
{
if (m_subFilters.removeAll(filter))
{
Q_EMIT updatedSignal();
}
}
void CompositeFilter::RemoveAllFilters()
{
m_subFilters.clear();
Q_EMIT updatedSignal();
}
void CompositeFilter::SetLogicOperator(LogicOperatorType logicOperator)
{
m_logicOperator = logicOperator;
Q_EMIT updatedSignal();
}
const QList<FilterConstType>& CompositeFilter::GetSubFilters() const
{
return m_subFilters;
}
void CompositeFilter::SetEmptyResult(bool result)
{
if (m_emptyResult != result)
{
m_emptyResult = result;
Q_EMIT updatedSignal();
}
}
QString CompositeFilter::GetNameInternal() const
{
QString name = "";
for (auto it = m_subFilters.begin(); it != m_subFilters.end(); ++it)
{
name += (*it)->GetName();
if (AZStd::next(it) != m_subFilters.end())
{
name += ", ";
}
}
return name;
}
bool CompositeFilter::MatchInternal(const AssetBrowserEntry* entry) const
{
if (m_subFilters.count() == 0)
{
return m_emptyResult;
}
// AND
if (m_logicOperator == LogicOperatorType::AND)
{
for (auto filter : m_subFilters)
{
if (!filter->Match(entry))
{
return false;
}
}
return true;
}
// OR
for (auto filter : m_subFilters)
{
if (filter->Match(entry))
{
return true;
}
}
return false;
}
void CompositeFilter::FilterInternal(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const
{
// if no subfilters are present in this composite filter then all relating entries would match
if (m_subFilters.isEmpty())
{
// only if match on empty filter is success
if (m_emptyResult)
{
Expand(result, entry);
}
return;
}
// AND
if (m_logicOperator == LogicOperatorType::AND)
{
AZStd::vector<const AssetBrowserEntry*> andResult;
bool firstResult = true;
for (auto filter : m_subFilters)
{
if (firstResult)
{
firstResult = false;
filter->Filter(andResult, entry);
}
else
{
AZStd::vector<const AssetBrowserEntry*> set;
filter->Filter(set, entry);
Intersect(andResult, set);
}
if (andResult.empty())
{
break;
}
}
Join(result, andResult);
}
// OR
else
{
for (auto filter : m_subFilters)
{
AZStd::vector<const AssetBrowserEntry*> set;
filter->Filter(set, entry);
Join(result, set);
}
}
}
//////////////////////////////////////////////////////////////////////////
// InverseFilter
//////////////////////////////////////////////////////////////////////////
InverseFilter::InverseFilter() {}
void InverseFilter::SetFilter(FilterConstType filter)
{
if (m_filter == filter)
{
return;
}
m_filter = filter;
Q_EMIT updatedSignal();
}
QString InverseFilter::GetNameInternal() const
{
if (m_filter.isNull())
{
QString name = tr("NOT");
}
QString name = tr("NOT (%1)").arg(m_filter->GetName());
return name;
}
bool InverseFilter::MatchInternal(const AssetBrowserEntry* entry) const
{
if (m_filter.isNull())
{
return false;
}
return !m_filter->Match(entry);
}
void InverseFilter::FilterInternal(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const
{
if (MatchInternal(entry))
{
Expand(result, entry);
}
}
//////////////////////////////////////////////////////////////////////////
// CleanerProductsFilter
//////////////////////////////////////////////////////////////////////////
CleanerProductsFilter::CleanerProductsFilter() {}
QString CleanerProductsFilter::GetNameInternal() const
{
return QString();
}
bool CleanerProductsFilter::MatchInternal(const AssetBrowserEntry* entry) const
{
auto product = azrtti_cast<const ProductAssetBrowserEntry*>(entry);
if (!product)
{
return true;
}
auto source = product->GetParent();
if (!source)
{
return true;
}
if (source->GetChildCount() != 1)
{
return true;
}
AZStd::string assetTypeName;
AZ::AssetTypeInfoBus::EventResult(assetTypeName, product->GetAssetType(), &AZ::AssetTypeInfo::GetAssetTypeDisplayName);
if (!assetTypeName.empty())
{
return true;
}
return false;
}
void CleanerProductsFilter::FilterInternal(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const
{
if (MatchInternal(entry))
{
Expand(result, entry);
}
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Search/moc_Filter.cpp"
@@ -0,0 +1,323 @@
/*
* 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 <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
#include <QObject>
#include <QString>
#include <QSharedPointer>
#include <QString>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/algorithm.h>
#endif
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserEntryFilter;
typedef QSharedPointer<const AssetBrowserEntryFilter> FilterConstType;
//////////////////////////////////////////////////////////////////////////
// AssetBrowserEntryFilter
//////////////////////////////////////////////////////////////////////////
//! Filters are used to fascilitate searching asset browser for specific asset
//! They are also used for enforcing selection constraints for asset picking
class AssetBrowserEntryFilter
: public QObject
{
Q_OBJECT
public:
//! Propagate direction allows match satisfaction based on entry parents and/or children
/*
if PropagateDirection = Down, and entry does not satisfy filter, evaluation will propagate recursively to its children
until at least one child satisfies the filter, then the original entry would match
if PropagateDirection = Up, and entry does not satisfy filter, evaluation will propagate recursively upwards to its parents
until first parent matches the filter, then the original entry would match
if PropagateDirection = None, only entry itself is considered by the filter
*/
enum PropagateDirection : int
{
None = 0x00,
Up = 0x01,
Down = 0x02
};
AssetBrowserEntryFilter();
virtual ~AssetBrowserEntryFilter() = default;
//! Check if entry matches filter
bool Match(const AssetBrowserEntry* entry) const;
//! Retrieve all matching entries that are either entry itself or its parents or children
void Filter(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const;
//! Filter name is used to uniquely identify the filter
QString GetName() const;
void SetName(const QString& name);
//! Tags are used for identifying filter groups
const QString& GetTag() const;
void SetTag(const QString& tag);
void SetFilterPropagation(int direction);
Q_SIGNALS:
//! Emitted every time a filter is updated, in case of composite filter, the signal is propagated to the top level filter so only one listener needs to connected
void updatedSignal() const;
protected:
//! Internal name auto generated based on filter type and data
virtual QString GetNameInternal() const = 0;
//! Internal matching logic overrided by every filter type
virtual bool MatchInternal(const AssetBrowserEntry* entry) const = 0;
//! Internal filtering logic overrided by every filter type
virtual void FilterInternal(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const;
private:
QString m_name;
QString m_tag;
int m_direction;
bool MatchDown(const AssetBrowserEntry* entry) const;
void FilterDown(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const;
};
//////////////////////////////////////////////////////////////////////////
// StringFilter
//////////////////////////////////////////////////////////////////////////
//! StringFilter filters assets based on their name
class StringFilter
: public AssetBrowserEntryFilter
{
Q_OBJECT
public:
StringFilter();
~StringFilter() override = default;
void SetFilterString(const QString& filterString);
protected:
QString GetNameInternal() const override;
bool MatchInternal(const AssetBrowserEntry* entry) const override;
private:
QString m_filterString;
};
//////////////////////////////////////////////////////////////////////////
// AssetTypeFilter
//////////////////////////////////////////////////////////////////////////
//! AssetTypeFilter filters products based on their asset id
class AssetTypeFilter
: public AssetBrowserEntryFilter
{
Q_OBJECT
public:
AssetTypeFilter();
~AssetTypeFilter() override = default;
void SetAssetType(AZ::Data::AssetType assetType);
void SetAssetType(const char* assetTypeName);
AZ::Data::AssetType GetAssetType() const;
protected:
QString GetNameInternal() const override;
bool MatchInternal(const AssetBrowserEntry* entry) const override;
private:
AZ::Data::AssetType m_assetType;
};
//////////////////////////////////////////////////////////////////////////
// AssetGroupFilter
//////////////////////////////////////////////////////////////////////////
//! AssetGroupFilter filters products based on their asset group
class AssetGroupFilter
: public AssetBrowserEntryFilter
{
Q_OBJECT
public:
AssetGroupFilter();
~AssetGroupFilter() override = default;
void SetAssetGroup(const QString& group);
const QString& GetAssetTypeGroup() const;
protected:
QString GetNameInternal() const override;
bool MatchInternal(const AssetBrowserEntry* entry) const override;
private:
QString m_group;
};
//////////////////////////////////////////////////////////////////////////
// EntryTypeFilter
//////////////////////////////////////////////////////////////////////////
class EntryTypeFilter
: public AssetBrowserEntryFilter
{
Q_OBJECT
public:
EntryTypeFilter();
~EntryTypeFilter() override = default;
void SetEntryType(AssetBrowserEntry::AssetEntryType entryType);
AssetBrowserEntry::AssetEntryType GetEntryType() const;
protected:
QString GetNameInternal() const override;
bool MatchInternal(const AssetBrowserEntry* entry) const override;
private:
AssetBrowserEntry::AssetEntryType m_entryType;
};
//////////////////////////////////////////////////////////////////////////
// CompositeFilter
//////////////////////////////////////////////////////////////////////////
//! CompositeFilter performs an AND/OR operation between multiple subfilters
/*
If more complex logic operations required, CompositeFilters can be nested
with different logic operator types
*/
class CompositeFilter
: public AssetBrowserEntryFilter
{
Q_OBJECT
public:
enum class LogicOperatorType
{
OR,
AND
};
explicit CompositeFilter(LogicOperatorType logicOperator);
~CompositeFilter() override = default;
void AddFilter(FilterConstType filter);
void RemoveFilter(FilterConstType filter);
void RemoveAllFilters();
void SetLogicOperator(LogicOperatorType logicOperator);
const QList<FilterConstType>& GetSubFilters() const;
//! Return value if there are no subfilters present
void SetEmptyResult(bool result);
protected:
QString GetNameInternal() const override;
bool MatchInternal(const AssetBrowserEntry* entry) const override;
void FilterInternal(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const override;
private:
QList<FilterConstType> m_subFilters;
LogicOperatorType m_logicOperator;
bool m_emptyResult;
};
//////////////////////////////////////////////////////////////////////////
// InverseFilter
//////////////////////////////////////////////////////////////////////////
//! Inverse filter negates result of its child filter
class InverseFilter
: public AssetBrowserEntryFilter
{
Q_OBJECT
public:
InverseFilter();
~InverseFilter() override = default;
void SetFilter(FilterConstType filter);
protected:
QString GetNameInternal() const override;
bool MatchInternal(const AssetBrowserEntry* entry) const override;
void FilterInternal(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const override;
private:
FilterConstType m_filter;
};
//////////////////////////////////////////////////////////////////////////
// CleanerProductsFilter
//////////////////////////////////////////////////////////////////////////
//! Filters out products that shouldn't be shown
class CleanerProductsFilter
: public AssetBrowserEntryFilter
{
Q_OBJECT
public:
CleanerProductsFilter();
~CleanerProductsFilter() override = default;
protected:
QString GetNameInternal() const override;
bool MatchInternal(const AssetBrowserEntry* entry) const override;
void FilterInternal(AZStd::vector<const AssetBrowserEntry*>& result, const AssetBrowserEntry* entry) const override;
private:
FilterConstType m_filter;
};
template<class T>
struct EBusAggregateUniqueResults
{
AZStd::vector<T> values;
void operator=(const T& rhs)
{
if (AZStd::find(values.begin(), values.end(), rhs) == values.end())
{
values.push_back(rhs);
}
}
};
struct EBusAggregateAssetTypesIfBelongsToGroup
{
EBusAggregateAssetTypesIfBelongsToGroup(const QString& group)
: m_group(group)
{
}
EBusAggregateAssetTypesIfBelongsToGroup(const EBusAggregateAssetTypesIfBelongsToGroup&) = delete;
EBusAggregateAssetTypesIfBelongsToGroup& operator=(const EBusAggregateAssetTypesIfBelongsToGroup&) = delete;
AZStd::vector<AZ::Data::AssetType> values;
void operator=(const AZ::Data::AssetType& assetType)
{
if (BelongsToGroup(assetType))
{
values.push_back(assetType);
}
}
private:
const QString& m_group;
bool BelongsToGroup(const AZ::Data::AssetType& assetType)
{
QString group;
AZ::AssetTypeInfoBus::EventResult(group, assetType, &AZ::AssetTypeInfo::GetGroup);
return !group.compare(m_group, Qt::CaseInsensitive);
}
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
Q_DECLARE_METATYPE(AzToolsFramework::AssetBrowser::FilterConstType)
@@ -0,0 +1,42 @@
/*
* 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 <AzQtComponents/Components/ExtendedLabel.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include <AssetBrowser/Search/ui_FilterByWidget.h>
AZ_POP_DISABLE_WARNING
#include <AzToolsFramework/AssetBrowser/Search/FilterByWidget.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
FilterByWidget::FilterByWidget(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::FilterByWidgetClass)
{
m_ui->setupUi(this);
connect(m_ui->m_clearFiltersButton, &AzQtComponents::ExtendedLabel::clicked, this, &FilterByWidget::ClearSignal);
// hide clear button as filters are reset at the startup
ToggleClearButton(false);
}
FilterByWidget::~FilterByWidget() = default;
void FilterByWidget::ToggleClearButton(bool visible) const
{
m_ui->m_clearFiltersButton->setVisible(visible);
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Search/moc_FilterByWidget.cpp"
@@ -0,0 +1,48 @@
/*
* 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
/*********************************************************************************************
* FilterByWidget has been deprecated, use AzQtComponents::FilteredSearchWidget instead.
*********************************************************************************************/
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <QScopedPointer>
#endif
namespace Ui
{
class FilterByWidgetClass;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
class FilterByWidget
: public QWidget
{
Q_OBJECT
public:
explicit FilterByWidget(QWidget* parent = nullptr);
~FilterByWidget() override;
void ToggleClearButton(bool visible) const;
Q_SIGNALS:
void ClearSignal();
private:
QScopedPointer<Ui::FilterByWidgetClass> m_ui;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FilterByWidgetClass</class>
<widget class="QWidget" name="FilterByWidgetClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>150</width>
<height>25</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>25</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="m_filterByLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">color: white;</string>
</property>
<property name="text">
<string>Filter by:</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>59</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="AzQtComponents::ExtendedLabel" name="m_clearFiltersButton">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::ExtendedLabel</class>
<extends>QLabel</extends>
<header>AzQtComponents/Components/ExtendedLabel.h</header>
</customwidget>
</customwidgets>
<connections/>
</ui>
@@ -0,0 +1,157 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzCore/std/containers/vector.h>
#include <AzToolsFramework/AssetBrowser/Search/SearchAssetTypeSelectorWidget.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/AssetBrowser/Search/FilterByWidget.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include <AssetBrowser/Search/ui_SearchAssetTypeSelectorWidget.h>
AZ_POP_DISABLE_WARNING
#include <QPushButton>
#include <QMenu>
#include <QCheckBox>
#include <QWidgetAction>
#include <algorithm>
namespace AzToolsFramework
{
namespace AssetBrowser
{
SearchAssetTypeSelectorWidget::SearchAssetTypeSelectorWidget(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::SearchAssetTypeSelectorWidgetClass())
, m_filter(QSharedPointer<CompositeFilter>(new CompositeFilter(CompositeFilter::LogicOperatorType::OR)))
, m_locked(false)
{
m_ui->setupUi(this);
QMenu* menu = new QMenu(this);
AddAllAction(menu);
menu->addSeparator();
EBusAggregateUniqueResults<QString> results;
AZ::AssetTypeInfoBus::BroadcastResult(results, &AZ::AssetTypeInfo::GetGroup);
std::sort(results.values.begin(), results.values.end(),
[](const QString& a, const QString& b) { return QString::compare(a, b, Qt::CaseInsensitive) < 0; });
for (QString& group : results.values)
{
// Group "Other" should be in the end of the list, and "Hidden" should not be on the list at all
if (group == "Other" || group == "Hidden")
{
continue;
}
AddAssetTypeGroup(menu, group);
}
AddAssetTypeGroup(menu, "Other");
menu->setLayoutDirection(Qt::LeftToRight);
menu->setStyleSheet("border: none; background-color: #333333;");
m_ui->m_showSelectionButton->setMenu(menu);
m_filter->SetTag("AssetTypes");
m_filter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down);
}
SearchAssetTypeSelectorWidget::~SearchAssetTypeSelectorWidget()
{
}
void SearchAssetTypeSelectorWidget::UpdateFilterByWidget() const
{
for (auto assetTypeCheckbox : m_assetTypeCheckboxes)
{
if (assetTypeCheckbox->isChecked())
{
m_filterByWidget->ToggleClearButton(true);
return;
}
}
m_filterByWidget->ToggleClearButton(false);
}
void SearchAssetTypeSelectorWidget::ClearAll() const
{
// check all other asset types
for (auto assetTypeCheckbox : m_assetTypeCheckboxes)
{
if (assetTypeCheckbox->isChecked())
{
assetTypeCheckbox->setChecked(false);
}
}
m_filter->RemoveAllFilters();
m_filter->SetEmptyResult(true);
UpdateFilterByWidget();
}
FilterConstType SearchAssetTypeSelectorWidget::GetFilter() const
{
return m_filter;
}
bool SearchAssetTypeSelectorWidget::IsLocked() const
{
return m_locked;
}
void SearchAssetTypeSelectorWidget::AddAssetTypeGroup(QMenu* menu, const QString& group)
{
EBusAggregateAssetTypesIfBelongsToGroup results(group);
AZ::AssetTypeInfoBus::BroadcastResult(results, &AZ::AssetTypeInfo::GetAssetType);
if (!results.values.empty())
{
QCheckBox* checkbox = new QCheckBox(group, menu);
QWidgetAction* action = new QWidgetAction(menu);
action->setDefaultWidget(checkbox);
menu->addAction(action);
m_assetTypeCheckboxes.push_back(checkbox);
AssetGroupFilter* groupFilter = new AssetGroupFilter();
groupFilter->SetAssetGroup(group);
m_actionFiltersMapping[checkbox] = FilterConstType(groupFilter);
connect(checkbox, &QCheckBox::clicked, this,
[=](bool checked)
{
if (checked)
{
m_filter->AddFilter(m_actionFiltersMapping[checkbox]);
}
else
{
m_filter->RemoveFilter(m_actionFiltersMapping[checkbox]);
}
UpdateFilterByWidget();
});
}
}
void SearchAssetTypeSelectorWidget::AddAllAction(QMenu* menu)
{
m_filterByWidget = new FilterByWidget(menu);
auto action = new QWidgetAction(menu);
action->setDefaultWidget(m_filterByWidget);
menu->addAction(action);
connect(m_filterByWidget, &FilterByWidget::ClearSignal, this, &SearchAssetTypeSelectorWidget::ClearAll);
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Search/moc_SearchAssetTypeSelectorWidget.cpp"
@@ -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
/*********************************************************************************************
* SearchAssetTypeSelectorWidget has been deprecated, use AzQtComponents::FilteredSearchWidget instead.
*********************************************************************************************/
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/vector.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: 'QBrush::d': class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' needs to have dll-interface to be used by clients of class 'QBrush'
// 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
#include <QScopedPointer>
#include <QSharedPointer>
#include <QWidgetAction>
#include <QCheckBox>
#include <QString>
AZ_POP_DISABLE_WARNING
#endif
class QMenu;
class QAction;
namespace Ui
{
class SearchAssetTypeSelectorWidgetClass;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
class FilterByWidget;
class SearchAssetTypeSelectorWidget
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(SearchAssetTypeSelectorWidget, AZ::SystemAllocator, 0);
explicit SearchAssetTypeSelectorWidget(QWidget* parent = nullptr);
~SearchAssetTypeSelectorWidget() override;
void UpdateFilterByWidget() const;
FilterConstType GetFilter() const;
bool IsLocked() const;
public Q_SIGNAL:
void ClearAll() const;
private:
QScopedPointer<Ui::SearchAssetTypeSelectorWidgetClass> m_ui;
QSharedPointer<CompositeFilter> m_filter;
FilterByWidget* m_filterByWidget;
AZStd::vector<QCheckBox*> m_assetTypeCheckboxes;
AZStd::unordered_map<QCheckBox*, FilterConstType> m_actionFiltersMapping;
bool m_locked;
void AddAssetTypeGroup(QMenu* menu, const QString& group);
void AddAllAction(QMenu* menu);
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SearchAssetTypeSelectorWidgetClass</class>
<widget class="QWidget" name="SearchAssetTypeSelectorWidgetClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>94</width>
<height>25</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>25</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="m_showSelectionButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>25</height>
</size>
</property>
<property name="statusTip">
<string/>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="styleSheet">
<string notr="true">QPushButton::menu-indicator { image: none; }</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../../AzQtComponents/AzQtComponents/Components/resources.qrc">
<normaloff>:/stylesheet/img/filter.svg</normaloff>:/stylesheet/img/filter.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>25</width>
<height>21</height>
</size>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../../AzQtComponents/AzQtComponents/Components/resources.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,71 @@
/*
* 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 "SearchParametersWidget.h"
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include "AssetBrowser/Search/ui_SearchParametersWidget.h"
AZ_POP_DISABLE_WARNING
#include <AzQtComponents/Components/ExtendedLabel.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
SearchParametersWidget::SearchParametersWidget(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::SearchParametersWidgetClass())
, m_allowClear(true)
{
m_ui->setupUi(this);
hide();
connect(m_ui->m_clearFiltersButton, &AzQtComponents::ExtendedLabel::clicked, this, &SearchParametersWidget::ClearAllSignal);
}
SearchParametersWidget::~SearchParametersWidget() = default;
void SearchParametersWidget::FilterUpdatedSlot()
{
QString filterName = m_filter->GetName();
if (!filterName.isEmpty())
{
show();
m_ui->m_filtersLabel->setText("<b>Filtered by:</b> " + filterName);
if (m_allowClear)
{
m_ui->m_clearFiltersButton->show();
}
else
{
m_ui->m_clearFiltersButton->hide();
}
}
else
{
hide();
}
}
void SearchParametersWidget::SetFilter(FilterConstType filter)
{
m_filter = filter;
connect(m_filter.data(), &AssetBrowserEntryFilter::updatedSignal, this, &SearchParametersWidget::FilterUpdatedSlot);
}
void SearchParametersWidget::SetAllowClear(bool allowClear)
{
m_allowClear = allowClear;
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Search/moc_SearchParametersWidget.cpp"
@@ -0,0 +1,65 @@
/*
* 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
/*********************************************************************************************
* SearchParametersWidget has been deprecated, use AzQtComponents::FilteredSearchWidget instead.
*********************************************************************************************/
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzCore/Memory/SystemAllocator.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: 'QBrush::d': class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' needs to have dll-interface to be used by clients of class 'QBrush'
// 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
#include <QWidget>
#include <QScopedPointer>
AZ_POP_DISABLE_WARNING
#endif
namespace Ui
{
class SearchParametersWidgetClass;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
class SearchParametersWidget
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(SearchParametersWidget, AZ::SystemAllocator, 0);
explicit SearchParametersWidget(QWidget* parent = nullptr);
~SearchParametersWidget();
void SetFilter(FilterConstType filter);
void SetAllowClear(bool allowClear);
Q_SIGNALS:
void ClearAllSignal();
private:
QScopedPointer<Ui::SearchParametersWidgetClass> m_ui;
FilterConstType m_filter;
bool m_allowClear;
private Q_SLOTS:
void FilterUpdatedSlot();
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SearchParametersWidgetClass</class>
<widget class="QWidget" name="SearchParametersWidgetClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>96</width>
<height>28</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="m_filtersLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: white;</string>
</property>
<property name="text">
<string>Filtered by: None</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="AzQtComponents::ExtendedLabel" name="m_clearFiltersButton">
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::ExtendedLabel</class>
<extends>QLabel</extends>
<header>AzQtComponents/Components/ExtendedLabel.h</header>
</customwidget>
</customwidgets>
<connections/>
</ui>
@@ -0,0 +1,176 @@
/*
* 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/AssetBrowser/Search/SearchWidget.h>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzCore/std/containers/vector.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 'QTextFormat::d': class 'QSharedDataPointer<QTextFormatPrivate>' needs to have dll-interface to be used by clients of class 'QTextFormat'
#include <QLineEdit>
#include <QToolButton>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetBrowser
{
namespace
{
AzQtComponents::SearchTypeFilterList buildTypesFilterList()
{
AzQtComponents::SearchTypeFilterList filters;
EBusAggregateUniqueResults<QString> groups;
AZ::AssetTypeInfoBus::BroadcastResult(groups, &AZ::AssetTypeInfo::GetGroup);
// Group "Other" should be in the end of the list, and "Hidden" should not be on the list at all
for (const QString& group : groups.values)
{
if (group != "Hidden")
{
EBusAggregateAssetTypesIfBelongsToGroup types(group);
AZ::AssetTypeInfoBus::BroadcastResult(types, &AZ::AssetTypeInfo::GetAssetType);
if (!types.values.empty())
{
AssetGroupFilter* groupFilter = new AssetGroupFilter();
groupFilter->SetAssetGroup(group);
AzQtComponents::SearchTypeFilter stFilter;
stFilter.displayName = group;
stFilter.metadata = QVariant::fromValue(FilterConstType(groupFilter));
filters.push_back(stFilter);
}
}
}
std::sort(filters.begin(), filters.end(),
[](const AzQtComponents::SearchTypeFilter& a, const AzQtComponents::SearchTypeFilter& b)
{
const int categoryResult = QString::compare(a.category, b.category, Qt::CaseInsensitive);
if (categoryResult != 0)
{
return categoryResult < 0;
}
else if (a.displayName == QStringLiteral("Other"))
{
return false;
}
else if (b.displayName == QStringLiteral("Other"))
{
return true;
}
return QString::compare(a.displayName, b.displayName, Qt::CaseInsensitive) < 0;
});
return filters;
}
}
SearchWidget::SearchWidget(QWidget* parent)
: AzQtComponents::FilteredSearchWidget(parent)
, m_filter(new CompositeFilter(CompositeFilter::LogicOperatorType::AND))
, m_stringFilter(new CompositeFilter(CompositeFilter::LogicOperatorType::AND))
, m_typesFilter(new CompositeFilter(CompositeFilter::LogicOperatorType::OR))
{
m_filter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down);
m_stringFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Up);
m_stringFilter->SetTag("String");
m_typesFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down);
m_typesFilter->SetTag("AssetTypes");
connect(this, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, this,
[this](const QString& text)
{
if (!filterLineEdit()->isHidden())
{
m_stringFilter->RemoveAllFilters();
auto stringList = text.split(' ', Qt::SkipEmptyParts);
for (auto& str : stringList)
{
auto stringFilter = new StringFilter();
stringFilter->SetFilterString(str);
m_stringFilter->AddFilter(FilterConstType(stringFilter));
}
}
});
connect(this, &AzQtComponents::FilteredSearchWidget::TypeFilterChanged, this,
[this](const AzQtComponents::SearchTypeFilterList& filters)
{
if (!filterTypePushButton()->isHidden())
{
m_typesFilter->RemoveAllFilters();
if (filters.isEmpty())
{
m_typesFilter->SetEmptyResult(true);
}
else
{
for (auto it = filters.constBegin(), end = filters.constEnd(); it != end; ++it)
{
m_typesFilter->AddFilter((*it).metadata.value<FilterConstType>());
}
}
}
});
}
void SearchWidget::Setup(bool stringFilter, bool assetTypeFilter)
{
ClearTextFilter();
ClearTypeFilter();
m_filter->RemoveAllFilters();
SetTextFilterVisible(stringFilter);
SetTypeFilterVisible(assetTypeFilter);
if (stringFilter)
{
m_filter->AddFilter(m_stringFilter);
}
// do not show assets in Hidden group
auto hiddenGroupFilter = new AssetGroupFilter();
hiddenGroupFilter->SetAssetGroup("Hidden");
auto inverseFilter = new InverseFilter();
inverseFilter->SetFilter(FilterConstType(hiddenGroupFilter));
m_filter->AddFilter(FilterConstType(inverseFilter));
// hide irrelevant
auto cleanerProductsFilter = new CleanerProductsFilter();
m_filter->AddFilter(FilterConstType(cleanerProductsFilter));
if (assetTypeFilter)
{
m_filter->AddFilter(FilterConstType(m_typesFilter));
SetTypeFilters(buildTypesFilterList());
}
}
QSharedPointer<CompositeFilter> SearchWidget::GetFilter() const
{
return m_filter;
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Search/moc_SearchWidget.cpp"
@@ -0,0 +1,51 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/PlatformDef.h>
AZ_PUSH_DISABLE_WARNING(4127, "-Wunknown-warning-option") // conditional expression is constant
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
AZ_POP_DISABLE_WARNING
AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option")
#include <AzQtComponents/Components/FilteredSearchWidget.h>
#include <QSharedPointer>
AZ_POP_DISABLE_WARNING
#endif
namespace AzToolsFramework
{
namespace AssetBrowser
{
class SearchWidget
: public AzQtComponents::FilteredSearchWidget
{
Q_OBJECT
public:
explicit SearchWidget(QWidget* parent = nullptr);
void Setup(bool stringFilter, bool assetTypeFilter);
QSharedPointer<CompositeFilter> GetFilter() const;
QString GetFilterString() const { return textFilter(); }
void ClearStringFilter() { ClearTextFilter(); }
private:
QSharedPointer<CompositeFilter> m_filter;
QSharedPointer<CompositeFilter> m_stringFilter;
QSharedPointer<CompositeFilter> m_typesFilter;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SearchWidgetClass</class>
<widget class="QWidget" name="SearchWidgetClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>304</width>
<height>27</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="m_horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="m_textSearch">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">selection-background-color: rgb(233, 99, 0);
background: #6e7071;
background-image: url(:/stylesheet/img/search.svg);
background-repeat: no-repeat;
background-position: left;
padding: 2 2 2 24;
color: rgb(255, 255, 255);</string>
</property>
<property name="inputMask">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string>Search...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_buttonClearFilter">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>25</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="AzToolsFramework::AssetBrowser::SearchAssetTypeSelectorWidget" name="m_assetTypeSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>25</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzToolsFramework::AssetBrowser::SearchAssetTypeSelectorWidget</class>
<extends>QWidget</extends>
<header>AzToolsFramework/AssetBrowser/Search/SearchAssetTypeSelectorWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<connections/>
</ui>
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b39515aba246a0c4d7426d7ae1f25890b5bf3890567f1f9a3191d06bc67734a
size 17376
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="13px" viewBox="0 0 12 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 57.1 (83088) - https://sketch.com -->
<title>Icons / System / Window Controls / Close</title>
<desc>Created with Sketch.</desc>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Tab-/-Selected-hover" transform="translate(-142.000000, -8.000000)" fill="#FFFFFF">
<g id="tab">
<g id="Icons-/-System-/-Window-Controls-/-Close" transform="translate(140.000000, 6.500000)">
<path d="M13.0769231,2 L14,2.92307692 L8.923,8 L14,13.0769231 L13.0769231,14 L8,8.923 L2.92307692,14 L2,13.0769231 L7.076,8 L2,2.92307692 L2.92307692,2 L8,7.076 L13.0769231,2 Z" id="close"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 948 B

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:95801d5824584df03e9076295b2dc4609839fc5cd42a5ec5157d1b1e6d49c369
size 15655
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<polygon fill="#E9E9E9" fill-rule="evenodd" points="10 19 10 11.5 4 4 4 2 20 2 20 4 14 11.5 14 19 12 22 10 22"/>
</svg>

After

Width:  |  Height:  |  Size: 206 B

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:65b0a95d652f993f88f51f9270be5b48c9e496422fda03ba675202255ddde46e
size 15251
@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/AssetBrowser/Resources">
<file>search.svg</file>
<file>close.svg</file>
<file>filter.svg</file>
</qresource>
</RCC>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>Search</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Search" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<path d="M20.9694824,19.6604004 L16.3757324,15.0666504 C17.4069824,13.7541504 17.9694824,12.1604004 17.9694824,10.4729004 C17.9694824,6.34790039 14.5944824,2.97290039 10.4694824,2.97290039 C6.34448242,2.97290039 2.96948242,6.34790039 2.96948242,10.4729004 C2.96948242,14.5979004 6.34448242,17.9729004 10.4694824,17.9729004 C12.1569824,17.9729004 13.7507324,17.4104004 15.0632324,16.3791504 L19.6569824,20.9729004 L20.9694824,19.6604004 Z M10.4694824,16.0979004 C7.37573242,16.0979004 4.84448242,13.5666504 4.84448242,10.4729004 C4.84448242,7.37915039 7.37573242,4.84790039 10.4694824,4.84790039 C13.5632324,4.84790039 16.0944824,7.37915039 16.0944824,10.4729004 C16.0944824,13.5666504 13.5632324,16.0979004 10.4694824,16.0979004 Z" id="Shape" fill="#E9E9E9" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,190 @@
/*
* 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 "SortFilterProxyModel.hxx"
namespace AzToolsFramework
{
namespace AssetBrowser
{
//////////////////////////////////////////////////////////////////////////
//SortFilterProxyModel
SortFilterProxyModel::SortFilterProxyModel(QObject* parent)
: QSortFilterProxyModel(parent)
, m_assetMatchFiltersOperator(AzToolsFramework::FilterOperatorType::And)
{
//uncomment any column you want to see in the view
m_showColumn.insert(AssetBrowserEntry::Column::Name);
//m_showColumn.insert( Entry::Column_SourceID );
//m_showColumn.insert( Entry::Column_FingerprintValue );
//m_showColumn.insert( Entry::Colbumn_Guid );
//m_showColumn.insert( Entry::Column_ScanFolderID );
//m_showColumn.insert( Entry::Column_ProductID );
//m_showColumn.insert( Entry::Column_JobID );
//m_showColumn.insert( Entry::Column_JobKey );
//m_showColumn.insert( Entry::Column_SubID );
//m_showColumn.insert( Entry::Column_AssetType );
//m_showColumn.insert( Entry::Column_Platform );
//m_showColumn.insert( Entry::Column_ClassID );
}
void SortFilterProxyModel::OnSearchCriteriaChanged(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator)
{
removeAllAssetMatchFilters();
setAssetMatchFilterOperator(filterOperator);
for (QString criteria : criteriaList)
{
auto parts = criteria.split(": ", QString::SkipEmptyParts);
addAssetMatchFilter(parts.last().toUtf8().constData());
}
}
void SortFilterProxyModel::addAssetTypeFilter(AZ::Data::AssetType assetType)
{
m_assetTypeFilters.push_back(assetType);
invalidateFilter();
}
void SortFilterProxyModel::addAssetPathFilter(const char* assetPathFilter)
{
m_assetPathFilters.push_back(assetPathFilter);
invalidateFilter();
}
void SortFilterProxyModel::removeAllAssetPathFilters()
{
m_assetPathFilters.clear();
invalidateFilter();
}
void SortFilterProxyModel::setAssetMatchSubDirFilter(bool val)
{
m_includeSubdir = val;
invalidateFilter();
}
void SortFilterProxyModel::removeAllAssetMatchFilters()
{
m_assetMatchFilters.clear();
invalidateFilter();
}
void SortFilterProxyModel::addAssetMatchFilter(const char* assetMatchFilter)
{
m_assetMatchFilters.push_back(assetMatchFilter);
invalidateFilter();
}
void SortFilterProxyModel::setAssetMatchFilterOperator(AzToolsFramework::FilterOperatorType type)
{
m_assetMatchFiltersOperator = type;
invalidateFilter();
}
bool SortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
//get the source idx, if invalid early out
QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
if (!idx.isValid())
{
return false;
}
//the entry is the internal pointer of the index
auto entry = static_cast<AssetBrowserEntry*>(idx.internalPointer());
if (!entry->isValid())
{
return false;
}
////////////////////////////////////////////////////////////////////////
//we only want to see assets that have at least one child product that has a valid assetType
if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
{
//we have a asset with at least one valid child product assetType
//we only want to see assets that have at least one child product that matches the assetType filter
if (!m_assetTypeFilters.empty())
{
for (int i = 0; i < entry->GetChildCount(); ++i)
{
auto product = static_cast<ProductAssetBrowserEntry*>(entry->GetChild(i));
if (product->isValid())
{
if (AZStd::find(m_assetTypeFilters.begin(), m_assetTypeFilters.end(), product->GetAssetType()) == m_assetTypeFilters.end())
{
return false;
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
//we only want to see assets that match all the match filters
if (!m_assetMatchFilters.empty())
{
if (m_assetMatchFiltersOperator == AzToolsFramework::FilterOperatorType::And)
{
for (const auto& item : m_assetMatchFilters)
{
if (!entry->Match(item.c_str()))
{
return false;
}
}
}
else if (m_assetMatchFiltersOperator == AzToolsFramework::FilterOperatorType::Or)
{
for (const auto& item : m_assetMatchFilters)
{
if (entry->Match(item.c_str()))
{
return true;
}
}
return false;
}
}
////////////////////////////////////////////////////////////////////////
return true;
}
bool SortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const
{
(void)source_parent;
//if the column is in the set we want to show it
return m_showColumn.find(static_cast<AssetBrowserEntry::Column>(source_column)) != m_showColumn.end();
}
bool SortFilterProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const
{
if (source_left.column() == source_right.column())
{
QVariant leftData = sourceModel()->data(source_left);
QVariant rightData = sourceModel()->data(source_right);
if ((leftData.type() == QVariant::String) &&
(rightData.type() == QVariant::String))
{
QString leftString = leftData.toString();
QString rightString = rightData.toString();
return QString::compare(leftString, rightString, Qt::CaseInsensitive) > 0;
}
}
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
} // namespace AssetBrowser
} // namespace AzToolsFramework// namespace AssetBrowser
#include <AssetBrowser/moc_SortFilterProxyModel.cpp>

Some files were not shown because too many files have changed in this diff Show More