205 lines
9.4 KiB
C++
205 lines
9.4 KiB
C++
/*
|
|
* 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 <AssetBuilderSDK/SerializationDependencies.h>
|
|
|
|
namespace AssetBuilderSDK
|
|
{
|
|
bool UpdateDependenciesFromClassData(
|
|
const AZ::SerializeContext& serializeContext,
|
|
void* instancePointer,
|
|
const AZ::SerializeContext::ClassData* classData,
|
|
const AZ::SerializeContext::ClassElement* classElement,
|
|
UniqueDependencyList& productDependencySet,
|
|
ProductPathDependencySet& productPathDependencySet,
|
|
bool enumerateChildren)
|
|
{
|
|
if(classData == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
if (classData->m_typeId == AZ::GetAssetClassId())
|
|
{
|
|
auto* asset = reinterpret_cast<AZ::Data::Asset<AZ::Data::AssetData>*>(instancePointer);
|
|
|
|
if (asset->GetId().IsValid())
|
|
{
|
|
productDependencySet[asset->GetId()] = AZ::Data::ProductDependencyInfo::CreateFlags(asset->GetAutoLoadBehavior());
|
|
}
|
|
}
|
|
else if (classData->m_typeId == azrtti_typeid<AZ::Data::AssetId>())
|
|
{
|
|
auto* assetId = reinterpret_cast<AZ::Data::AssetId*>(instancePointer);
|
|
|
|
if (assetId->IsValid())
|
|
{
|
|
// For asset ID dependencies, set the behavior to "NoLoad" so that loading the parent asset doesn't trigger a load
|
|
// of the dependent asset.
|
|
productDependencySet[*assetId] = AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::NoLoad);
|
|
}
|
|
}
|
|
else if (classData->m_azRtti && classData->m_azRtti->IsTypeOf(azrtti_typeid<AzFramework::SimpleAssetReferenceBase>()))
|
|
{
|
|
auto* asset = reinterpret_cast<AzFramework::SimpleAssetReferenceBase*>(instancePointer);
|
|
|
|
if (!asset->GetAssetPath().empty())
|
|
{
|
|
AZStd::string filePath = asset->GetAssetPath();
|
|
AZStd::string fileExtension;
|
|
if (!AzFramework::StringFunc::Path::GetExtension(filePath.c_str(), fileExtension))
|
|
{
|
|
// GetFileFilter can return either
|
|
// 1) one file extension like "*.fileExtension"
|
|
// 2) one file extension like "fileExtension"
|
|
// 3) a semi colon separated list of file extensions like "*.fileExtension1; *.fileExtension2"
|
|
// Please note that if file extension is missing from the path and we get a list of semicolon separated file extensions
|
|
// we will extract the first file extension and use that.
|
|
fileExtension = asset->GetFileFilter();
|
|
AZStd::regex fileExtensionRegex("^(?:\\*\\.)?(\\w+);?");
|
|
AZStd::smatch match;
|
|
if (AZStd::regex_search(fileExtension, match, fileExtensionRegex))
|
|
{
|
|
fileExtension = match[1];
|
|
AzFramework::StringFunc::Path::ReplaceExtension(filePath, fileExtension.c_str());
|
|
}
|
|
}
|
|
|
|
productPathDependencySet.emplace(filePath, ProductPathDependencyType::ProductFile);
|
|
}
|
|
}
|
|
else if(enumerateChildren)
|
|
{
|
|
|
|
auto beginCallback = [&serializeContext, &productDependencySet, &productPathDependencySet](void* instancePointer, const AZ::SerializeContext::ClassData* classData, const AZ::SerializeContext::ClassElement* classElement)
|
|
{
|
|
// EnumerateInstance calls are already recursive, so no need to keep going, set enumerateChildren to false.
|
|
return UpdateDependenciesFromClassData(serializeContext, instancePointer, classData, classElement, productDependencySet, productPathDependencySet, false);
|
|
};
|
|
AZ::SerializeContext::EnumerateInstanceCallContext callContext(
|
|
beginCallback,
|
|
{},
|
|
&serializeContext,
|
|
AZ::SerializeContext::ENUM_ACCESS_FOR_READ,
|
|
nullptr
|
|
);
|
|
|
|
return serializeContext.EnumerateInstance(&callContext, instancePointer, classData->m_typeId, classData, classElement);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FillDependencyVectorFromSet(
|
|
AZStd::vector<ProductDependency>& productDependencies,
|
|
UniqueDependencyList& productDependencySet)
|
|
{
|
|
productDependencies.reserve(productDependencySet.size());
|
|
|
|
for (const auto& thisEntry : productDependencySet)
|
|
{
|
|
constexpr int flags = 0;
|
|
productDependencies.emplace_back(thisEntry.first, thisEntry.second);
|
|
}
|
|
}
|
|
|
|
bool GatherProductDependenciesForFile(
|
|
AZ::SerializeContext& serializeContext,
|
|
const AZStd::string& filePath,
|
|
AZStd::vector<ProductDependency>& productDependencies,
|
|
ProductPathDependencySet& productPathDependencySet)
|
|
{
|
|
AZ::IO::FileIOStream fileStream;
|
|
if (!fileStream.Open(filePath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary))
|
|
{
|
|
return false;
|
|
}
|
|
UniqueDependencyList productDependencySet;
|
|
|
|
// UpdateDependenciesFromClassData is also looking for assets. In some cases, the assets may not be ready to use
|
|
// in UpdateDependenciesFromClassData, and have an invalid asset ID. This asset filter will be called with valid, ready to use assets,
|
|
// but it's only called on assets and not other supported types, and it's only available when loading the file, and not on an in-memory stream.
|
|
AZ::ObjectStream::FilterDescriptor assetReadyFilterDescriptor([&productDependencySet](const AZ::Data::AssetFilterInfo& filterInfo)
|
|
{
|
|
if (filterInfo.m_assetId.IsValid())
|
|
{
|
|
productDependencySet[filterInfo.m_assetId] = AZ::Data::ProductDependencyInfo::CreateFlags(filterInfo.m_loadBehavior);
|
|
}
|
|
return false;
|
|
});
|
|
|
|
if (!AZ::ObjectStream::LoadBlocking(&fileStream, serializeContext, [&productDependencySet, &productPathDependencySet](void* instancePointer, const AZ::Uuid& classId, const AZ::SerializeContext* callbackSerializeContext)
|
|
{
|
|
auto classData = callbackSerializeContext->FindClassData(classId);
|
|
// LoadBlocking only enumerates the topmost level objects, so call UpdateDependenciesFromClassData with enumerateChildren set.
|
|
UpdateDependenciesFromClassData(*callbackSerializeContext, instancePointer, classData, nullptr, productDependencySet, productPathDependencySet, true);
|
|
return true;
|
|
}, assetReadyFilterDescriptor))
|
|
{
|
|
return false;
|
|
}
|
|
FillDependencyVectorFromSet(productDependencies, productDependencySet);
|
|
return true;
|
|
}
|
|
|
|
bool GatherProductDependencies(
|
|
AZ::SerializeContext& serializeContext,
|
|
const void* obj,
|
|
AZ::TypeId typeId,
|
|
AZStd::vector<ProductDependency>& productDependencies,
|
|
ProductPathDependencySet& productPathDependencySet,
|
|
const DependencyHandler& handler)
|
|
{
|
|
if (obj == nullptr)
|
|
{
|
|
AZ_Error("AssetBuilderSDK", false, "Cannot gather product dependencies for null data.");
|
|
return false;
|
|
}
|
|
|
|
// start with a set to make it easy to avoid duplicate entries.
|
|
UniqueDependencyList productDependencySet;
|
|
auto beginCallback = [&serializeContext, &productDependencySet, &productPathDependencySet, handler](void* instancePointer, const AZ::SerializeContext::ClassData* classData, const AZ::SerializeContext::ClassElement* classElement)
|
|
{
|
|
// EnumerateObject already visits every element, so no need to enumerate farther, set enumerateChildren to false.
|
|
return handler(serializeContext, instancePointer, classData, classElement, productDependencySet, productPathDependencySet, false);
|
|
};
|
|
bool enumerateResult = serializeContext.EnumerateInstanceConst(obj, typeId, beginCallback, {}, AZ::SerializeContext::ENUM_ACCESS_FOR_READ, nullptr, nullptr);
|
|
|
|
FillDependencyVectorFromSet(productDependencies, productDependencySet);
|
|
return enumerateResult;
|
|
}
|
|
|
|
bool OutputObject(const void* obj, AZ::TypeId typeId, AZStd::string_view outputPath, AZ::Data::AssetType assetType, AZ::u32 subId, JobProduct& jobProduct, AZ::SerializeContext* serializeContext, const DependencyHandler& handler)
|
|
{
|
|
if (!serializeContext)
|
|
{
|
|
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
|
}
|
|
|
|
if(!serializeContext)
|
|
{
|
|
AZ_Error("AssetBuilderSDK", false, "Failed to retrieve serialization context.");
|
|
return false;
|
|
}
|
|
|
|
jobProduct = JobProduct(outputPath, assetType, subId);
|
|
|
|
if (GatherProductDependencies(*serializeContext, obj, typeId, jobProduct.m_dependencies, jobProduct.m_pathDependencies, handler))
|
|
{
|
|
jobProduct.m_dependenciesHandled = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
jobProduct = {};
|
|
return false;
|
|
}
|
|
}
|