/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #ifndef AZFRAMEWORK_ASSET_GENERICASSETHANDLER_H #define AZFRAMEWORK_ASSET_GENERICASSETHANDLER_H #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace AzFramework { /** * Generic implementation of an asset handler for any arbitrary type that is reflected for serialization. * * Simple or game specific assets that wish to make use of automated loading and editing facilities * can use this handler with any asset type that's reflected for editing. * * Example: * * class MyAsset : public AZ::Data::AssetData * { * public: * AZ_CLASS_ALLOCATOR(MyAsset, AZ::SystemAllocator, 0); * AZ_RTTI(MyAsset, "{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}", AZ::Data::AssetData); * * static void Reflect(AZ::ReflectContext* context) * { * AZ::SerializeContext* serialize = azrtti_cast(context); * if (serialize) * { * serialize->Class() * ->Field("SomeField", &MyAsset::m_someField) * ; * * AZ::EditContext* edit = serialize->GetEditContext(); * if (edit) * { * edit->Class("My Asset", "Asset for representing X, Y, and Z") * ->DataElement(0, &MyAsset::m_someField, "Some data field", "It's a float") * ; * } * } * } * * float m_someField; * }; * * * using MyAssetHandler = GenericAssetHandler; * */ /** * Just a base class to assign concrete RTTI to these classes - Don't derive from this - use GenericAssetHandler instead. * This being in the heirarchy allows you to easily ask whether a particular handler derives from this type and thus is a * GenericAssetHandler. */ class GenericAssetHandlerBase : public AZ::Data::AssetHandler { public: AZ_RTTI(GenericAssetHandlerBase, "{B153B8B5-25CC-4BB7-A2BD-9A47ECF4123C}", AZ::Data::AssetHandler); virtual ~GenericAssetHandlerBase() = default; }; template class GenericAssetHandler : public GenericAssetHandlerBase , private AZ::AssetTypeInfoBus::Handler { public: AZ_CLASS_ALLOCATOR(GenericAssetHandler, AZ::SystemAllocator, 0); AZ_RTTI(GenericAssetHandler, "{8B36B3E8-8C0B-4297-BDA2-1648C155C78E}", GenericAssetHandlerBase); GenericAssetHandler(const char* displayName, const char* group, const char* extension, const AZ::Uuid& componentTypeId = AZ::Uuid::CreateNull(), AZ::SerializeContext* serializeContext = nullptr) : m_displayName(displayName) , m_group(group) , m_extension(extension) , m_componentTypeId(componentTypeId) , m_serializeContext(serializeContext) { AZ_Assert(extension, "Extension is required."); if (extension[0] == '.') { ++extension; } m_extension = extension; AZ_Assert(!m_extension.empty(), "Invalid extension provided."); if (!m_serializeContext) { EBUS_EVENT_RESULT(m_serializeContext, AZ::ComponentApplicationBus, GetSerializeContext); } AZ::AssetTypeInfoBus::Handler::BusConnect(AZ::AzTypeInfo::Uuid()); } ~GenericAssetHandler() { AZ::AssetTypeInfoBus::Handler::BusDisconnect(); } AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& /*id*/, const AZ::Data::AssetType& /*type*/) override { return aznew AssetType(); } AZ::Data::AssetHandler::LoadResult LoadAssetData( const AZ::Data::Asset& asset, AZStd::shared_ptr stream, const AZ::Data::AssetFilterCB& assetLoadFilterCB) override { AssetType* assetData = asset.GetAs(); AZ_Assert(assetData, "Asset is of the wrong type."); AZ_Assert(m_serializeContext, "Unable to retrieve serialize context."); if (assetData) { return AZ::Utils::LoadObjectFromStreamInPlace(*stream, *assetData, m_serializeContext, AZ::ObjectStream::FilterDescriptor(assetLoadFilterCB)) ? AZ::Data::AssetHandler::LoadResult::LoadComplete : AZ::Data::AssetHandler::LoadResult::Error; } return AZ::Data::AssetHandler::LoadResult::Error; } bool SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) override { AssetType* assetData = asset.GetAs(); AZ_Assert(assetData, "Asset is of the wrong type."); if (assetData && m_serializeContext) { return AZ::Utils::SaveObjectToStream(*stream, AZ::ObjectStream::ST_XML, assetData, m_serializeContext); } return false; } void DestroyAsset(AZ::Data::AssetPtr ptr) override { delete ptr; } void GetHandledAssetTypes(AZStd::vector& assetTypes) override { assetTypes.push_back(AZ::AzTypeInfo::Uuid()); } void Register() { EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, EnableCatalogForAsset, AZ::AzTypeInfo::Uuid()); EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, AddExtension, m_extension.c_str()); AZ_Assert(AZ::Data::AssetManager::IsReady(), "AssetManager isn't ready!"); AZ::Data::AssetManager::Instance().RegisterHandler(this, AZ::AzTypeInfo::Uuid()); } void Unregister() { if (AZ::Data::AssetManager::IsReady()) { AZ::Data::AssetManager::Instance().UnregisterHandler(this); } } bool CanHandleAsset(const AZ::Data::AssetId& id) const override { AZStd::string assetPath; EBUS_EVENT_RESULT(assetPath, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, id); if (!assetPath.empty()) { AZStd::string assetExtension; if (AzFramework::StringFunc::Path::GetExtension(assetPath.c_str(), assetExtension, false)) { return assetExtension == m_extension; } } return false; } ////////////////////////////////////////////////////////////////////////////////////////////// // AZ::AssetTypeInfoBus::Handler AZ::Data::AssetType GetAssetType() const override { return AZ::AzTypeInfo::Uuid(); } const char* GetAssetTypeDisplayName() const override { return m_displayName.c_str(); } const char* GetGroup() const override { return m_group.c_str(); } AZ::Uuid GetComponentTypeId() const override { return m_componentTypeId; } void GetAssetTypeExtensions(AZStd::vector& extensions) override { extensions.push_back(m_extension); } ////////////////////////////////////////////////////////////////////////////////////////////// AZStd::string m_displayName; AZStd::string m_group; AZStd::string m_extension; AZ::Uuid m_componentTypeId = AZ::Uuid::CreateNull(); AZ::SerializeContext* m_serializeContext; GenericAssetHandler(const GenericAssetHandler&) = delete; }; } // namespace AzFramework #endif // AZFRAMEWORK_ASSET_GENERICASSETHANDLER_H