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,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