Merge pull request #7406 from aws-lumberyard-dev/cgalvan/ImageGradientStreamingImageSupport

Updated the image gradient component to use a streaming image asset
monroegm-disable-blank-issue-2
Chris Galvan 4 years ago committed by GitHub
commit 39777e7afb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -49,8 +49,7 @@ namespace ImageProcessingAtom
static const unsigned int s_MinReduceLevel = 0; static const unsigned int s_MinReduceLevel = 0;
static const unsigned int s_MaxReduceLevel = 5; static const unsigned int s_MaxReduceLevel = 5;
static const int s_TotalSupportedImageExtensions = 10; static const char* s_SupportedImageExtensions[] = {
static const char* s_SupportedImageExtensions[s_TotalSupportedImageExtensions] = {
"*.tif", "*.tif",
"*.tiff", "*.tiff",
"*.png", "*.png",
@ -62,6 +61,7 @@ namespace ImageProcessingAtom
"*.dds", "*.dds",
"*.exr" "*.exr"
}; };
static constexpr int s_TotalSupportedImageExtensions = AZ_ARRAY_SIZE(s_SupportedImageExtensions);
enum class RGBWeight : AZ::u32 enum class RGBWeight : AZ::u32
{ {

@ -8,12 +8,12 @@
#pragma once #pragma once
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/BuilderSettings.h> #include <BuilderSettings/BuilderSettings.h>
#include <AzCore/base.h> #include <AzCore/base.h>
#include <AzCore/IO/Path/Path.h> #include <AzCore/IO/Path/Path.h>
#include <AzCore/std/containers/set.h> #include <AzCore/std/containers/set.h>
#include <Atom/ImageProcessing/ImageObject.h> #include <Atom/ImageProcessing/ImageObject.h>
#include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <QDateTime> #include <QDateTime>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>

@ -11,7 +11,7 @@
#include <AzCore/RTTI/TypeInfo.h> #include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/RTTI/ReflectContext.h> #include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Memory/Memory.h> #include <AzCore/Memory/Memory.h>
#include <BuilderSettings/ImageProcessingDefines.h> #include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h> #include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
namespace ImageProcessingAtom namespace ImageProcessingAtom

@ -8,7 +8,7 @@
#pragma once #pragma once
#include <BuilderSettings/ImageProcessingDefines.h> #include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <AzCore/RTTI/TypeInfo.h> #include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/RTTI/ReflectContext.h> #include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Memory/Memory.h> #include <AzCore/Memory/Memory.h>

@ -10,7 +10,7 @@
#include <AzCore/RTTI/TypeInfo.h> #include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Memory/SystemAllocator.h> #include <AzCore/Memory/SystemAllocator.h>
#include <BuilderSettings/ImageProcessingDefines.h> #include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <Atom/ImageProcessing/PixelFormats.h> #include <Atom/ImageProcessing/PixelFormats.h>
namespace ImageProcessingAtom namespace ImageProcessingAtom

@ -11,9 +11,9 @@
#include <AzCore/RTTI/TypeInfo.h> #include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Asset/AssetCommon.h> #include <AzCore/Asset/AssetCommon.h>
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/CubemapSettings.h> #include <BuilderSettings/CubemapSettings.h>
#include <BuilderSettings/MipmapSettings.h> #include <BuilderSettings/MipmapSettings.h>
#include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <Atom/ImageProcessing/PixelFormats.h> #include <Atom/ImageProcessing/PixelFormats.h>
namespace ImageProcessingAtom namespace ImageProcessingAtom

@ -8,7 +8,7 @@
#pragma once #pragma once
#include <BuilderSettings/ImageProcessingDefines.h> #include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/RTTI/ReflectContext.h> #include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/DataPatch.h> #include <AzCore/Serialization/DataPatch.h>

@ -8,7 +8,7 @@
#pragma once #pragma once
#include <BuilderSettings/ImageProcessingDefines.h> #include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <Atom/ImageProcessing/PixelFormats.h> #include <Atom/ImageProcessing/PixelFormats.h>
#include <Atom/ImageProcessing/ImageObject.h> #include <Atom/ImageProcessing/ImageObject.h>

@ -10,10 +10,10 @@
#include <QRect> #include <QRect>
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/PresetSettings.h> #include <BuilderSettings/PresetSettings.h>
#include <BuilderSettings/TextureSettings.h> #include <BuilderSettings/TextureSettings.h>
#include <Atom/ImageProcessing/ImageObject.h> #include <Atom/ImageProcessing/ImageObject.h>
#include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <Compressors/Compressor.h> #include <Compressors/Compressor.h>
#include <AzCore/Jobs/Job.h> #include <AzCore/Jobs/Job.h>

@ -8,10 +8,10 @@
#pragma once #pragma once
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/PresetSettings.h> #include <BuilderSettings/PresetSettings.h>
#include <BuilderSettings/TextureSettings.h> #include <BuilderSettings/TextureSettings.h>
#include <Atom/ImageProcessing/ImageObject.h> #include <Atom/ImageProcessing/ImageObject.h>
#include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <AzCore/Jobs/Job.h> #include <AzCore/Jobs/Job.h>
namespace ImageProcessingAtom namespace ImageProcessingAtom

@ -32,6 +32,7 @@
#include <AzFramework/IO/LocalFileIO.h> #include <AzFramework/IO/LocalFileIO.h>
#include <Atom/ImageProcessing/ImageObject.h> #include <Atom/ImageProcessing/ImageObject.h>
#include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <Processing/PixelFormatInfo.h> #include <Processing/PixelFormatInfo.h>
#include <Processing/ImageConvert.h> #include <Processing/ImageConvert.h>
#include <Processing/ImageToProcess.h> #include <Processing/ImageToProcess.h>
@ -45,7 +46,6 @@
#include <BuilderSettings/BuilderSettingManager.h> #include <BuilderSettings/BuilderSettingManager.h>
#include <BuilderSettings/CubemapSettings.h> #include <BuilderSettings/CubemapSettings.h>
#include <BuilderSettings/ImageProcessingDefines.h>
#include <BuilderSettings/PresetSettings.h> #include <BuilderSettings/PresetSettings.h>
#include <Editor/EditorCommon.h> #include <Editor/EditorCommon.h>

@ -13,6 +13,7 @@ set(FILES
Include/Atom/ImageProcessing/ImageProcessingEditorBus.h Include/Atom/ImageProcessing/ImageProcessingEditorBus.h
Include/Atom/ImageProcessing/PixelFormats.h Include/Atom/ImageProcessing/PixelFormats.h
Include/Atom/ImageProcessing/ImageObject.h Include/Atom/ImageProcessing/ImageObject.h
Include/Atom/ImageProcessing/ImageProcessingDefines.h
../Assets/Editor/Resources.qrc ../Assets/Editor/Resources.qrc
../Assets/Editor/Backward.png ../Assets/Editor/Backward.png
../Assets/Editor/Forward.png ../Assets/Editor/Forward.png
@ -28,7 +29,6 @@ set(FILES
Source/BuilderSettings/BuilderSettings.h Source/BuilderSettings/BuilderSettings.h
Source/BuilderSettings/CubemapSettings.cpp Source/BuilderSettings/CubemapSettings.cpp
Source/BuilderSettings/CubemapSettings.h Source/BuilderSettings/CubemapSettings.h
Source/BuilderSettings/ImageProcessingDefines.h
Source/BuilderSettings/MipmapSettings.cpp Source/BuilderSettings/MipmapSettings.cpp
Source/BuilderSettings/MipmapSettings.h Source/BuilderSettings/MipmapSettings.h
Source/BuilderSettings/PlatformSettings.h Source/BuilderSettings/PlatformSettings.h

@ -7,4 +7,5 @@
# #
set(FILES set(FILES
Include/Atom/ImageProcessing/ImageProcessingDefines.h
) )

@ -133,7 +133,8 @@ namespace AZ
case AZ::RHI::Format::R16G16_UNORM: case AZ::RHI::Format::R16G16_UNORM:
case AZ::RHI::Format::R16G16B16A16_UNORM: case AZ::RHI::Format::R16G16B16A16_UNORM:
{ {
return mem[index] / static_cast<float>(std::numeric_limits<AZ::u16>::max()); auto actualMem = reinterpret_cast<const AZ::u16*>(mem);
return actualMem[index] / static_cast<float>(std::numeric_limits<AZ::u16>::max());
} }
case AZ::RHI::Format::R16_SNORM: case AZ::RHI::Format::R16_SNORM:
case AZ::RHI::Format::R16G16_SNORM: case AZ::RHI::Format::R16G16_SNORM:
@ -480,14 +481,13 @@ namespace AZ
const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor(); const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor();
auto width = imageDescriptor.m_size.m_width; auto width = imageDescriptor.m_size.m_width;
const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format); const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format);
const uint32_t pixelSize = AZ::RHI::GetFormatSize(imageDescriptor.m_format) / numComponents;
size_t outValuesIndex = 0; size_t outValuesIndex = 0;
for (uint32_t y = topLeft.second; y < bottomRight.second; ++y) for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
{ {
for (uint32_t x = topLeft.first; x < bottomRight.first; ++x) for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
{ {
size_t imageDataIndex = (y * width + x) * pixelSize + componentIndex; size_t imageDataIndex = (y * width + x) * numComponents + componentIndex;
auto& outValue = outValues[outValuesIndex++]; auto& outValue = outValues[outValuesIndex++];
outValue = Internal::RetrieveFloatValue(imageData.data(), imageDataIndex, imageDescriptor.m_format); outValue = Internal::RetrieveFloatValue(imageData.data(), imageDataIndex, imageDescriptor.m_format);
@ -513,14 +513,13 @@ namespace AZ
const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor(); const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor();
auto width = imageDescriptor.m_size.m_width; auto width = imageDescriptor.m_size.m_width;
const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format); const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format);
const uint32_t pixelSize = AZ::RHI::GetFormatSize(imageDescriptor.m_format) / numComponents;
size_t outValuesIndex = 0; size_t outValuesIndex = 0;
for (uint32_t y = topLeft.second; y < bottomRight.second; ++y) for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
{ {
for (uint32_t x = topLeft.first; x < bottomRight.first; ++x) for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
{ {
size_t imageDataIndex = (y * width + x) * pixelSize + componentIndex; size_t imageDataIndex = (y * width + x) * numComponents + componentIndex;
auto& outValue = outValues[outValuesIndex++]; auto& outValue = outValues[outValuesIndex++];
outValue = Internal::RetrieveUintValue(imageData.data(), imageDataIndex, imageDescriptor.m_format); outValue = Internal::RetrieveUintValue(imageData.data(), imageDataIndex, imageDescriptor.m_format);
@ -546,14 +545,13 @@ namespace AZ
const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor(); const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor();
auto width = imageDescriptor.m_size.m_width; auto width = imageDescriptor.m_size.m_width;
const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format); const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format);
const uint32_t pixelSize = AZ::RHI::GetFormatSize(imageDescriptor.m_format) / numComponents;
size_t outValuesIndex = 0; size_t outValuesIndex = 0;
for (uint32_t y = topLeft.second; y < bottomRight.second; ++y) for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
{ {
for (uint32_t x = topLeft.first; x < bottomRight.first; ++x) for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
{ {
size_t imageDataIndex = (y * width + x) * pixelSize + componentIndex; size_t imageDataIndex = (y * width + x) * numComponents + componentIndex;
auto& outValue = outValues[outValuesIndex++]; auto& outValue = outValues[outValuesIndex++];
outValue = Internal::RetrieveIntValue(imageData.data(), imageDataIndex, imageDescriptor.m_format); outValue = Internal::RetrieveIntValue(imageData.data(), imageDataIndex, imageDescriptor.m_format);

@ -17,9 +17,12 @@ ly_add_target(
PUBLIC PUBLIC
Include Include
BUILD_DEPENDENCIES BUILD_DEPENDENCIES
PRIVATE
AZ::AtomCore
PUBLIC PUBLIC
AZ::AzCore AZ::AzCore
AZ::AzFramework AZ::AzFramework
Gem::Atom_RPI.Public
Gem::SurfaceData Gem::SurfaceData
Gem::ImageProcessingAtom.Headers Gem::ImageProcessingAtom.Headers
Gem::LmbrCentral Gem::LmbrCentral

@ -8,8 +8,11 @@
#pragma once #pragma once
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
#include <AzCore/Asset/AssetCommon.h> #include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h> #include <AzCore/Component/Component.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <GradientSignal/Ebuses/GradientRequestBus.h> #include <GradientSignal/Ebuses/GradientRequestBus.h>
#include <GradientSignal/Ebuses/GradientTransformRequestBus.h> #include <GradientSignal/Ebuses/GradientTransformRequestBus.h>
#include <GradientSignal/Ebuses/ImageGradientRequestBus.h> #include <GradientSignal/Ebuses/ImageGradientRequestBus.h>
@ -25,6 +28,19 @@ namespace LmbrCentral
namespace GradientSignal namespace GradientSignal
{ {
// Custom JSON serializer for ImageGradientConfig to handle version conversion
class JsonImageGradientConfigSerializer
: public AZ::BaseJsonSerializer
{
public:
AZ_RTTI(GradientSignal::JsonImageGradientConfigSerializer, "{C5B982C8-2E81-45C3-8932-B6F54B28F493}", AZ::BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
AZ::JsonSerializationResult::Result Load(
void* outputValue, const AZ::Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
AZ::JsonDeserializerContext& context) override;
};
class ImageGradientConfig class ImageGradientConfig
: public AZ::ComponentConfig : public AZ::ComponentConfig
{ {
@ -32,7 +48,7 @@ namespace GradientSignal
AZ_CLASS_ALLOCATOR(ImageGradientConfig, AZ::SystemAllocator, 0); AZ_CLASS_ALLOCATOR(ImageGradientConfig, AZ::SystemAllocator, 0);
AZ_RTTI(ImageGradientConfig, "{1BDB5DA4-A4A8-452B-BE6D-6BD451D4E7CD}", AZ::ComponentConfig); AZ_RTTI(ImageGradientConfig, "{1BDB5DA4-A4A8-452B-BE6D-6BD451D4E7CD}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context); static void Reflect(AZ::ReflectContext* context);
AZ::Data::Asset<ImageAsset> m_imageAsset = { AZ::Data::AssetLoadBehavior::QueueLoad }; AZ::Data::Asset<AZ::RPI::StreamingImageAsset> m_imageAsset = { AZ::Data::AssetLoadBehavior::QueueLoad };
float m_tilingX = 1.0f; float m_tilingX = 1.0f;
float m_tilingY = 1.0f; float m_tilingY = 1.0f;
}; };

@ -12,6 +12,7 @@
#include <AzCore/RTTI/ReflectContext.h> #include <AzCore/RTTI/ReflectContext.h>
#include <AzFramework/Asset/GenericAssetHandler.h> #include <AzFramework/Asset/GenericAssetHandler.h>
#include <Atom/ImageProcessing/PixelFormats.h> #include <Atom/ImageProcessing/PixelFormats.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
namespace AZ namespace AZ
{ {
@ -61,6 +62,6 @@ namespace GradientSignal
} }
}; };
float GetValueFromImageAsset(const AZ::Data::Asset<ImageAsset>& imageAsset, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue); float GetValueFromImageAsset(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue);
} // namespace GradientSignal } // namespace GradientSignal

@ -7,6 +7,7 @@
*/ */
#include <GradientSignal/Components/ImageGradientComponent.h> #include <GradientSignal/Components/ImageGradientComponent.h>
#include <Atom/ImageProcessing/ImageProcessingDefines.h>
#include <AzCore/Asset/AssetManager.h> #include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetSerializer.h> #include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Debug/Profiler.h> #include <AzCore/Debug/Profiler.h>
@ -18,16 +19,103 @@
namespace GradientSignal namespace GradientSignal
{ {
AZ::JsonSerializationResult::Result JsonImageGradientConfigSerializer::Load(
void* outputValue, [[maybe_unused]] const AZ::Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, AZ::JsonDeserializerContext& context)
{
// We can distinguish between version 1 and 2 by the presence of the "ImageAsset" field,
// which is only in version 1.
// For version 2, we don't need to do any special processing, so just let the base class
// load the JSON if we don't find the "ImageAsset" field.
rapidjson::Value::ConstMemberIterator itr = inputValue.FindMember("ImageAsset");
if (itr == inputValue.MemberEnd())
{
return AZ::BaseJsonSerializer::Load(outputValue, outputValueTypeId, inputValue, context);
}
namespace JSR = AZ::JsonSerializationResult;
auto configInstance = reinterpret_cast<ImageGradientConfig*>(outputValue);
AZ_Assert(configInstance, "Output value for JsonImageGradientConfigSerializer can't be null.");
JSR::ResultCode result(JSR::Tasks::ReadField);
result.Combine(ContinueLoadingFromJsonObjectField(
&configInstance->m_tilingX, azrtti_typeid<decltype(configInstance->m_tilingX)>(), inputValue, "TilingX", context));
result.Combine(ContinueLoadingFromJsonObjectField(
&configInstance->m_tilingY, azrtti_typeid<decltype(configInstance->m_tilingY)>(), inputValue, "TilingY", context));
// Version 1 stored a custom GradientSignal::ImageAsset as the image asset.
// In Version 2, we changed the image asset to use the generic AZ::RPI::StreamingImageAsset,
// so they are both AZ::Data::Asset but reference different types.
// Using the assetHint, which will be something like "my_test_image.gradimage",
// we need to find the valid streaming image asset product from the same source,
// which will be something like "my_test_image.png.streamingimage"
AZStd::string assetHint;
AZ::Data::AssetId fixedAssetId;
auto it = itr->value.FindMember("assetHint");
if (it != itr->value.MemberEnd())
{
AZ::ScopedContextPath subPath(context, "assetHint");
result.Combine(ContinueLoading(&assetHint, azrtti_typeid<AZStd::string>(), it->value, context));
if (assetHint.ends_with(".gradimage"))
{
// We don't know what image format the original source was, so we need to loop through
// all the supported image extensions to check if they have a valid corresponding
// streaming image asset
for (auto& supportedImageExtension : ImageProcessingAtom::s_SupportedImageExtensions)
{
AZStd::string imageExtension(supportedImageExtension);
// The image extensions are stored with a wildcard (e.g. *.png) so we need to strip that off first
AZ::StringFunc::Replace(imageExtension, "*", "");
// Form potential streaming image path (e.g. my_test_image.png.streamingimage)
AZStd::string potentialStreamingImagePath(assetHint);
AZ::StringFunc::Replace(potentialStreamingImagePath, ".gradimage", "");
potentialStreamingImagePath += imageExtension + ".streamingimage";
// Check if there is a valid streaming image asset for this path
AZ::Data::AssetCatalogRequestBus::BroadcastResult(fixedAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, potentialStreamingImagePath.c_str(), azrtti_typeid<AZ::Data::Asset<AZ::RPI::StreamingImageAsset>>(), false);
if (fixedAssetId.IsValid())
{
break;
}
}
}
}
// Replace the old gradimage with new AssetId for streaming image asset
if (fixedAssetId.IsValid())
{
configInstance->m_imageAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::StreamingImageAsset>(fixedAssetId, AZ::Data::AssetLoadBehavior::QueueLoad);
}
return context.Report(result,
result.GetProcessing() != JSR::Processing::Halted ?
"Successfully loaded ImageGradientConfig information." :
"Failed to load ImageGradientConfig information.");
}
AZ_CLASS_ALLOCATOR_IMPL(JsonImageGradientConfigSerializer, AZ::SystemAllocator, 0);
void ImageGradientConfig::Reflect(AZ::ReflectContext* context) void ImageGradientConfig::Reflect(AZ::ReflectContext* context)
{ {
if (auto jsonContext = azrtti_cast<AZ::JsonRegistrationContext*>(context))
{
jsonContext->Serializer<JsonImageGradientConfigSerializer>()->HandlesType<ImageGradientConfig>();
}
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context); AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize) if (serialize)
{ {
serialize->Class<ImageGradientConfig, AZ::ComponentConfig>() serialize->Class<ImageGradientConfig, AZ::ComponentConfig>()
->Version(1) ->Version(2)
->Field("ImageAsset", &ImageGradientConfig::m_imageAsset)
->Field("TilingX", &ImageGradientConfig::m_tilingX) ->Field("TilingX", &ImageGradientConfig::m_tilingX)
->Field("TilingY", &ImageGradientConfig::m_tilingY) ->Field("TilingY", &ImageGradientConfig::m_tilingY)
->Field("StreamingImageAsset", &ImageGradientConfig::m_imageAsset)
; ;
AZ::EditContext* edit = serialize->GetEditContext(); AZ::EditContext* edit = serialize->GetEditContext();
@ -265,7 +353,7 @@ namespace GradientSignal
{ {
AZStd::unique_lock<decltype(m_imageMutex)> imageLock(m_imageMutex); AZStd::unique_lock<decltype(m_imageMutex)> imageLock(m_imageMutex);
m_configuration.m_imageAsset = AZ::Data::AssetManager::Instance().FindOrCreateAsset(assetId, azrtti_typeid<ImageAsset>(), m_configuration.m_imageAsset.GetAutoLoadBehavior()); m_configuration.m_imageAsset = AZ::Data::AssetManager::Instance().FindOrCreateAsset(assetId, azrtti_typeid<AZ::RPI::StreamingImageAsset>(), m_configuration.m_imageAsset.GetAutoLoadBehavior());
} }
SetupDependencies(); SetupDependencies();

@ -15,84 +15,10 @@
#include <AzCore/Debug/Profiler.h> #include <AzCore/Debug/Profiler.h>
#include <Atom/ImageProcessing/PixelFormats.h> #include <Atom/ImageProcessing/PixelFormats.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <GradientSignal/Util.h> #include <GradientSignal/Util.h>
#include <numeric> #include <numeric>
namespace
{
template <ImageProcessingAtom::EPixelFormat>
float RetrieveValue(const AZ::u8* mem, size_t index)
{
AZ_Assert(false, "Unimplemented!");
return 0.0f;
}
template <>
float RetrieveValue<ImageProcessingAtom::EPixelFormat::ePixelFormat_Unknown>([[maybe_unused]] const AZ::u8* mem, [[maybe_unused]] size_t index)
{
return 0.0f;
}
template <>
float RetrieveValue<ImageProcessingAtom::EPixelFormat::ePixelFormat_R8>(const AZ::u8* mem, size_t index)
{
return mem[index] / static_cast<float>(std::numeric_limits<AZ::u8>::max());
}
template <>
float RetrieveValue<ImageProcessingAtom::EPixelFormat::ePixelFormat_R16>(const AZ::u8* mem, size_t index)
{
// 16 bits per channel
auto actualMem = reinterpret_cast<const AZ::u16*>(mem);
actualMem += index;
return *actualMem / static_cast<float>(std::numeric_limits<AZ::u16>::max());
}
template <>
float RetrieveValue<ImageProcessingAtom::EPixelFormat::ePixelFormat_R32>(const AZ::u8* mem, size_t index)
{
// 32 bits per channel
auto actualMem = reinterpret_cast<const AZ::u32*>(mem);
actualMem += index;
return *actualMem / static_cast<float>(std::numeric_limits<AZ::u32>::max());
}
template <>
float RetrieveValue<ImageProcessingAtom::EPixelFormat::ePixelFormat_R32F>(const AZ::u8* mem, size_t index)
{
// 32 bits per channel
auto actualMem = reinterpret_cast<const float*>(mem);
actualMem += index;
return *actualMem;
}
float RetrieveValue(const AZ::u8* mem, size_t index, ImageProcessingAtom::EPixelFormat format)
{
using namespace ImageProcessingAtom;
switch (format)
{
case ePixelFormat_R8:
return RetrieveValue<ePixelFormat_R8>(mem, index);
case ePixelFormat_R16:
return RetrieveValue<ePixelFormat_R16>(mem, index);
case ePixelFormat_R32:
return RetrieveValue<ePixelFormat_R32>(mem, index);
case ePixelFormat_R32F:
return RetrieveValue<ePixelFormat_R32F>(mem, index);
default:
return RetrieveValue<ePixelFormat_Unknown>(mem, index);
}
}
}
namespace GradientSignal namespace GradientSignal
{ {
void ImageAsset::Reflect(AZ::ReflectContext* context) void ImageAsset::Reflect(AZ::ReflectContext* context)
@ -152,17 +78,15 @@ namespace GradientSignal
return true; return true;
} }
float GetValueFromImageAsset(const AZ::Data::Asset<ImageAsset>& imageAsset, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue) float GetValueFromImageAsset(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue)
{ {
if (imageAsset.IsReady()) if (imageAsset.IsReady())
{ {
const auto& image = imageAsset.Get(); auto imageDescriptor = imageAsset->GetImageDescriptor();
AZStd::size_t imageSize = image->m_imageWidth * image->m_imageHeight * auto width = imageDescriptor.m_size.m_width;
static_cast<AZ::u32>(image->m_bytesPerPixel); auto height = imageDescriptor.m_size.m_height;
if (image->m_imageWidth > 0 && if (width > 0 && height > 0)
image->m_imageHeight > 0 &&
image->m_imageData.size() == imageSize)
{ {
// When "rasterizing" from uvs, a range of 0-1 has slightly different meanings depending on the sampler state. // When "rasterizing" from uvs, a range of 0-1 has slightly different meanings depending on the sampler state.
// For repeating states (Unbounded/None, Repeat), a uv value of 1 should wrap around back to our 0th pixel. // For repeating states (Unbounded/None, Repeat), a uv value of 1 should wrap around back to our 0th pixel.
@ -185,8 +109,8 @@ namespace GradientSignal
// A 16x16 pixel image and tilingX = tilingY = 1 maps the uv range of 0-1 to 0-16 pixels. // A 16x16 pixel image and tilingX = tilingY = 1 maps the uv range of 0-1 to 0-16 pixels.
// A 16x16 pixel image and tilingX = tilingY = 1.5 maps the uv range of 0-1 to 0-24 pixels. // A 16x16 pixel image and tilingX = tilingY = 1.5 maps the uv range of 0-1 to 0-24 pixels.
const AZ::Vector3 tiledDimensions((image->m_imageWidth * tilingX), const AZ::Vector3 tiledDimensions((width * tilingX),
(image->m_imageHeight * tilingY), (height * tilingY),
0.0f); 0.0f);
// Convert from uv space back to pixel space // Convert from uv space back to pixel space
@ -195,13 +119,13 @@ namespace GradientSignal
// UVs outside the 0-1 range are treated as infinitely tiling, so that we behave the same as the // UVs outside the 0-1 range are treated as infinitely tiling, so that we behave the same as the
// other gradient generators. As mentioned above, if clamping is desired, we expect it to be applied // other gradient generators. As mentioned above, if clamping is desired, we expect it to be applied
// outside of this function. // outside of this function.
size_t x = static_cast<size_t>(pixelLookup.GetX()) % image->m_imageWidth; auto x = aznumeric_cast<AZ::u32>(pixelLookup.GetX()) % width;
size_t y = static_cast<size_t>(pixelLookup.GetY()) % image->m_imageHeight; auto y = aznumeric_cast<AZ::u32>(pixelLookup.GetY()) % height;
// Flip the y because images are stored in reverse of our world axes // Flip the y because images are stored in reverse of our world axes
size_t index = ((image->m_imageHeight - 1) - y) * image->m_imageWidth + x; y = (height - 1) - y;
return RetrieveValue(image->m_imageData.data(), index, image->m_imageFormat); return AZ::RPI::GetSubImagePixelValue<float>(imageAsset, x, y);
} }
} }

@ -8,6 +8,7 @@
#include <Tests/GradientSignalTestFixtures.h> #include <Tests/GradientSignalTestFixtures.h>
#include <Tests/GradientSignalTestHelpers.h>
#include <AzTest/AzTest.h> #include <AzTest/AzTest.h>
#include <AzCore/Asset/AssetManager.h> #include <AzCore/Asset/AssetManager.h>
@ -88,7 +89,7 @@ namespace UnitTest
// Create the Image Gradient Component. // Create the Image Gradient Component.
GradientSignal::ImageGradientConfig config; GradientSignal::ImageGradientConfig config;
config.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset( config.m_imageAsset = UnitTest::CreateSpecificPixelImageAsset(
test.m_imageSize, test.m_imageSize, static_cast<AZ::u32>(test.m_pixel.GetX()), static_cast<AZ::u32>(test.m_pixel.GetY())); test.m_imageSize, test.m_imageSize, static_cast<AZ::u32>(test.m_pixel.GetX()), static_cast<AZ::u32>(test.m_pixel.GetY()));
config.m_tilingX = test.m_tiling; config.m_tilingX = test.m_tiling;
config.m_tilingY = test.m_tiling; config.m_tilingY = test.m_tiling;
@ -379,7 +380,7 @@ namespace UnitTest
// Create an ImageGradient with a 3x3 asset with the center pixel set. // Create an ImageGradient with a 3x3 asset with the center pixel set.
GradientSignal::ImageGradientConfig gradientConfig; GradientSignal::ImageGradientConfig gradientConfig;
gradientConfig.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(3, 3, 1, 1); gradientConfig.m_imageAsset = UnitTest::CreateSpecificPixelImageAsset(3, 3, 1, 1);
entity->CreateComponent<GradientSignal::ImageGradientComponent>(gradientConfig); entity->CreateComponent<GradientSignal::ImageGradientComponent>(gradientConfig);
// Create the test GradientTransform // Create the test GradientTransform

@ -8,7 +8,10 @@
#include <Tests/GradientSignalTestFixtures.h> #include <Tests/GradientSignalTestFixtures.h>
#include <Tests/GradientSignalTestHelpers.h>
#include <Atom/RPI.Reflect/Image/ImageMipChainAsset.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
#include <AzFramework/Components/TransformComponent.h> #include <AzFramework/Components/TransformComponent.h>
#include <GradientSignal/Components/GradientSurfaceDataComponent.h> #include <GradientSignal/Components/GradientSurfaceDataComponent.h>
#include <GradientSignal/Components/GradientTransformComponent.h> #include <GradientSignal/Components/GradientTransformComponent.h>
@ -74,14 +77,16 @@ namespace UnitTest
void GradientSignalBaseFixture::SetupCoreSystems() void GradientSignalBaseFixture::SetupCoreSystems()
{ {
m_mockHandler = new UnitTest::ImageAssetMockAssetHandler(); // Using the AZ::RPI::MakeAssetHandler will both create the asset handlers,
AZ::Data::AssetManager::Instance().RegisterHandler(m_mockHandler, azrtti_typeid<GradientSignal::ImageAsset>()); // and register them with the AssetManager
m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::ImageMipChainAssetHandler>());
m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::StreamingImageAssetHandler>());
} }
void GradientSignalBaseFixture::TearDownCoreSystems() void GradientSignalBaseFixture::TearDownCoreSystems()
{ {
AZ::Data::AssetManager::Instance().UnregisterHandler(m_mockHandler); // This will delete the asset handlers, which will unregister themselves on deletion
delete m_mockHandler; // delete after removing from the asset manager m_assetHandlers.clear();
AzFramework::LegacyAssetEventBus::ClearQueuedEvents(); AzFramework::LegacyAssetEventBus::ClearQueuedEvents();
} }
@ -139,7 +144,7 @@ namespace UnitTest
GradientSignal::ImageGradientConfig config; GradientSignal::ImageGradientConfig config;
const uint32_t imageSize = 4096; const uint32_t imageSize = 4096;
const int32_t imageSeed = 12345; const int32_t imageSeed = 12345;
config.m_imageAsset = ImageAssetMockAssetHandler::CreateImageAsset(imageSize, imageSize, imageSeed); config.m_imageAsset = UnitTest::CreateImageAsset(imageSize, imageSize, imageSeed);
config.m_tilingX = 1.0f; config.m_tilingX = 1.0f;
config.m_tilingY = 1.0f; config.m_tilingY = 1.0f;
entity->CreateComponent<GradientSignal::ImageGradientComponent>(config); entity->CreateComponent<GradientSignal::ImageGradientComponent>(config);

@ -9,6 +9,7 @@
#include <Tests/GradientSignalTestMocks.h> #include <Tests/GradientSignalTestMocks.h>
#include <LmbrCentral/Shape/MockShapes.h> #include <LmbrCentral/Shape/MockShapes.h>
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
#include <AzTest/GemTestEnvironment.h> #include <AzTest/GemTestEnvironment.h>
namespace UnitTest namespace UnitTest
@ -89,7 +90,7 @@ namespace UnitTest
AZStd::unique_ptr<AZ::Entity> BuildTestSurfaceMaskGradient(float shapeHalfBounds); AZStd::unique_ptr<AZ::Entity> BuildTestSurfaceMaskGradient(float shapeHalfBounds);
AZStd::unique_ptr<AZ::Entity> BuildTestSurfaceSlopeGradient(float shapeHalfBounds); AZStd::unique_ptr<AZ::Entity> BuildTestSurfaceSlopeGradient(float shapeHalfBounds);
UnitTest::ImageAssetMockAssetHandler* m_mockHandler = nullptr; AZ::RPI::AssetHandlerPtrList m_assetHandlers;
}; };
struct GradientSignalTest struct GradientSignalTest

@ -8,11 +8,189 @@
#include <Tests/GradientSignalTestHelpers.h> #include <Tests/GradientSignalTestHelpers.h>
#include <Atom/RPI.Reflect/Image/ImageMipChainAssetCreator.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAssetCreator.h>
#include <AzCore/Math/Aabb.h> #include <AzCore/Math/Aabb.h>
#include <GradientSignal/GradientSampler.h> #include <GradientSignal/GradientSampler.h>
namespace UnitTest namespace UnitTest
{ {
AZ::RHI::ImageSubresourceLayout BuildSubImageLayout(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize)
{
AZ::RHI::ImageSubresourceLayout layout;
layout.m_size = AZ::RHI::Size{ width, height, 1 };
layout.m_rowCount = width;
layout.m_bytesPerRow = width * pixelSize;
layout.m_bytesPerImage = width * height * pixelSize;
return layout;
}
AZStd::vector<uint8_t> BuildBasicImageData(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::s32 seed)
{
const size_t imageSize = width * height * pixelSize;
AZStd::vector<uint8_t> image;
image.reserve(imageSize);
size_t value = 0;
AZStd::hash_combine(value, seed);
for (AZ::u32 x = 0; x < width; ++x)
{
for (AZ::u32 y = 0; y < height; ++y)
{
AZStd::hash_combine(value, x);
AZStd::hash_combine(value, y);
image.push_back(static_cast<AZ::u8>(value));
}
}
EXPECT_EQ(image.size(), imageSize);
return image;
}
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> BuildBasicMipChainAsset(AZ::u16 mipLevels, AZ::u16 arraySize, AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::s32 seed)
{
using namespace AZ;
RPI::ImageMipChainAssetCreator assetCreator;
assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize);
RHI::ImageSubresourceLayout layout = BuildSubImageLayout(width, height, pixelSize);
assetCreator.BeginMip(layout);
for (AZ::u32 arrayIndex = 0; arrayIndex < arraySize; ++arrayIndex)
{
AZStd::vector<uint8_t> data = BuildBasicImageData(width, height, pixelSize, seed);
assetCreator.AddSubImage(data.data(), data.size());
}
assetCreator.EndMip();
Data::Asset<RPI::ImageMipChainAsset> asset;
EXPECT_TRUE(assetCreator.End(asset));
EXPECT_TRUE(asset.IsReady());
EXPECT_NE(asset.Get(), nullptr);
return asset;
}
AZStd::vector<uint8_t> BuildSpecificPixelImageData(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::u32 pixelX, AZ::u32 pixelY)
{
const size_t imageSize = width * height * pixelSize;
AZStd::vector<uint8_t> image;
image.reserve(imageSize);
const AZ::u8 pixelValue = 255;
// Image data should be stored inverted on the y axis relative to our engine, so loop backwards through y.
for (int y = static_cast<int>(height) - 1; y >= 0; --y)
{
for (AZ::u32 x = 0; x < width; ++x)
{
if ((x == static_cast<int>(pixelX)) && (y == static_cast<int>(pixelY)))
{
image.push_back(pixelValue);
}
else
{
image.push_back(0);
}
}
}
EXPECT_EQ(image.size(), imageSize);
return image;
}
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> BuildSpecificPixelMipChainAsset(AZ::u16 mipLevels, AZ::u16 arraySize, AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::u32 pixelX, AZ::u32 pixelY)
{
using namespace AZ;
RPI::ImageMipChainAssetCreator assetCreator;
assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), mipLevels, arraySize);
RHI::ImageSubresourceLayout layout = BuildSubImageLayout(width, height, pixelSize);
assetCreator.BeginMip(layout);
for (AZ::u32 arrayIndex = 0; arrayIndex < arraySize; ++arrayIndex)
{
AZStd::vector<uint8_t> data = BuildSpecificPixelImageData(width, height, pixelSize, pixelX, pixelY);
assetCreator.AddSubImage(data.data(), data.size());
}
assetCreator.EndMip();
Data::Asset<RPI::ImageMipChainAsset> asset;
EXPECT_TRUE(assetCreator.End(asset));
EXPECT_TRUE(asset.IsReady());
EXPECT_NE(asset.Get(), nullptr);
return asset;
}
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed)
{
auto randomAssetId = AZ::Data::AssetId(AZ::Uuid::CreateRandom());
auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset<AZ::RPI::StreamingImageAsset>(
randomAssetId, AZ::Data::AssetLoadBehavior::Default);
const AZ::u32 arraySize = 1;
const AZ::u32 mipCountTotal = 1;
const auto format = AZ::RHI::Format::R8_UNORM;
const AZ::u32 pixelSize = AZ::RHI::GetFormatComponentCount(format);
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> mipChain = BuildBasicMipChainAsset(mipCountTotal, arraySize, width, height, pixelSize, seed);
AZ::RPI::StreamingImageAssetCreator assetCreator;
assetCreator.Begin(randomAssetId);
AZ::RHI::ImageDescriptor imageDesc = AZ::RHI::ImageDescriptor::Create2DArray(AZ::RHI::ImageBindFlags::ShaderRead, width, height, arraySize, format);
imageDesc.m_mipLevels = static_cast<AZ::u16>(mipCountTotal);
assetCreator.SetImageDescriptor(imageDesc);
assetCreator.AddMipChainAsset(*mipChain.Get());
EXPECT_TRUE(assetCreator.End(imageAsset));
EXPECT_TRUE(imageAsset.IsReady());
EXPECT_NE(imageAsset.Get(), nullptr);
return imageAsset;
}
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> CreateSpecificPixelImageAsset(AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY)
{
auto randomAssetId = AZ::Data::AssetId(AZ::Uuid::CreateRandom());
auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset<AZ::RPI::StreamingImageAsset>(
randomAssetId, AZ::Data::AssetLoadBehavior::Default);
const AZ::u32 arraySize = 1;
const AZ::u32 mipCountTotal = 1;
const auto format = AZ::RHI::Format::R8_UNORM;
const AZ::u32 pixelSize = AZ::RHI::GetFormatComponentCount(format);
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> mipChain = BuildSpecificPixelMipChainAsset(mipCountTotal, arraySize, width, height, pixelSize, pixelX, pixelY);
AZ::RPI::StreamingImageAssetCreator assetCreator;
assetCreator.Begin(randomAssetId);
AZ::RHI::ImageDescriptor imageDesc = AZ::RHI::ImageDescriptor::Create2DArray(AZ::RHI::ImageBindFlags::ShaderRead, width, height, arraySize, format);
imageDesc.m_mipLevels = static_cast<AZ::u16>(mipCountTotal);
assetCreator.SetImageDescriptor(imageDesc);
assetCreator.AddMipChainAsset(*mipChain.Get());
EXPECT_TRUE(assetCreator.End(imageAsset));
EXPECT_TRUE(imageAsset.IsReady());
EXPECT_NE(imageAsset.Get(), nullptr);
return imageAsset;
}
void GradientSignalTestHelpers::CompareGetValueAndGetValues(AZ::EntityId gradientEntityId, float queryMin, float queryMax) void GradientSignalTestHelpers::CompareGetValueAndGetValues(AZ::EntityId gradientEntityId, float queryMin, float queryMax)
{ {
// Create a gradient sampler and run through a series of points to see if they match expectations. // Create a gradient sampler and run through a series of points to see if they match expectations.

@ -12,8 +12,72 @@
#include <AzCore/std/containers/vector.h> #include <AzCore/std/containers/vector.h>
#include <AzTest/AzTest.h> #include <AzTest/AzTest.h>
#include <Atom/RHI.Reflect/ImageSubresource.h>
#include <Atom/RPI.Reflect/Image/ImageMipChainAsset.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
namespace UnitTest namespace UnitTest
{ {
//! Helper method to build a AZ::RHI::ImageSubresourceLayout
//! @param width The width of the image
//! @param height The height of the image
//! @param pixelSize Number of bytes per pixel
//! @return The AZ::RHI::ImageSubresourceLayout that has been filled out appropriately
AZ::RHI::ImageSubresourceLayout BuildSubImageLayout(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize);
//! Build a deterministic random set of image pixel data
//! @param width Width of the image
//! @param height Height of the image
//! @param pixelSize Number of bytes per pixel
//! @param seed The random seed for generating the data
//! @return A vector of bytes for the image data
AZStd::vector<uint8_t> BuildBasicImageData(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::s32 seed);
//! Build a mip chain asset that contains the basic image data from BuildBasicImageData
//! @param mipLevels Number of mip levels in the chain
//! @param arraySize Number of sub images within a mip level
//! @param width The width of the image
//! @param height The height of the image
//! @param pixelSize The number of bytes per pixel
//! @param seed The random seed for generating the data
//! @return A mip chain asset with the specified basic image data
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> BuildBasicMipChainAsset(AZ::u16 mipLevels, AZ::u16 arraySize, AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::s32 seed);
//! Construct an array of image data where all the pixels are 0 except for one at the given coordinate
//! @param width Width of the image
//! @param height Height of the image
//! @param pixelSize Number of bytes per pixel
//! @param pixelX The X coordinate of the pixel to set to 1
//! @param pixelY The Y coordinate of the pixel to set to 1
//! @return A vector of bytes for the image data
AZStd::vector<uint8_t> BuildSpecificPixelImageData(AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::u32 pixelX, AZ::u32 pixelY);
//! Build a mip chain asset that contains the specific image data from BuildSpecificPixelImageData
//! @param mipLevels Number of mip levels in the chain
//! @param arraySize Number of sub images within a mip level
//! @param width The width of the image
//! @param height The height of the image
//! @param pixelSize The number of bytes per pixel
//! @param pixelX The X coordinate of the pixel to set to 1
//! @param pixelY The Y coordinate of the pixel to set to 1
//! @return A mip chain asset with the specific pixel image data
AZ::Data::Asset<AZ::RPI::ImageMipChainAsset> BuildSpecificPixelMipChainAsset(AZ::u16 mipLevels, AZ::u16 arraySize, AZ::u32 width, AZ::u32 height, AZ::u32 pixelSize, AZ::u32 pixelX, AZ::u32 pixelY);
//! Creates a deterministically random set of pixel data as an AZ::RPI::StreamingImageAsset.
//! \param width The width of the AZ::RPI::StreamingImageAsset
//! \param height The height of the AZ::RPI::StreamingImageAsset
//! \param seed The random seed to use for generating the random data
//! \return The AZ::RPI::StreamingImageAsset in a loaded ready state
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed);
//! Creates an AZ::RPI::StreamingImageAsset where all the pixels are 0 except for the one pixel at the given coordinates, which is set to 1.
//! \param width The width of the AZ::RPI::StreamingImageAsset
//! \param height The height of the AZ::RPI::StreamingImageAsset
//! \param pixelX The X coordinate of the pixel to set to 1
//! \param pixelY The Y coordinate of the pixel to set to 1
//! \return The AZ::RPI::StreamingImageAsset in a loaded ready state
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> CreateSpecificPixelImageAsset(AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY);
class GradientSignalTestHelpers class GradientSignalTestHelpers
{ {
public: public:

@ -1,74 +0,0 @@
/*
* 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
*
*/
#include <Tests/GradientSignalTestMocks.h>
namespace UnitTest
{
AZ::Data::Asset<GradientSignal::ImageAsset> ImageAssetMockAssetHandler::CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed)
{
auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset<GradientSignal::ImageAsset>(
AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetLoadBehavior::Default);
imageAsset->m_imageWidth = width;
imageAsset->m_imageHeight = height;
imageAsset->m_bytesPerPixel = 1;
imageAsset->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
imageAsset->m_imageData.reserve(width * height);
size_t value = 0;
AZStd::hash_combine(value, seed);
for (AZ::u32 x = 0; x < width; ++x)
{
for (AZ::u32 y = 0; y < height; ++y)
{
AZStd::hash_combine(value, x);
AZStd::hash_combine(value, y);
imageAsset->m_imageData.push_back(static_cast<AZ::u8>(value));
}
}
return imageAsset;
}
AZ::Data::Asset<GradientSignal::ImageAsset> ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(
AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY)
{
auto imageAsset = AZ::Data::AssetManager::Instance().CreateAsset<GradientSignal::ImageAsset>(
AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetLoadBehavior::Default);
imageAsset->m_imageWidth = width;
imageAsset->m_imageHeight = height;
imageAsset->m_bytesPerPixel = 1;
imageAsset->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
imageAsset->m_imageData.reserve(width * height);
const AZ::u8 pixelValue = 255;
// Image data should be stored inverted on the y axis relative to our engine, so loop backwards through y.
for (int y = static_cast<int>(height) - 1; y >= 0; --y)
{
for (AZ::u32 x = 0; x < width; ++x)
{
if ((x == static_cast<int>(pixelX)) && (y == static_cast<int>(pixelY)))
{
imageAsset->m_imageData.push_back(pixelValue);
}
else
{
imageAsset->m_imageData.push_back(0);
}
}
}
return imageAsset;
}
}

@ -28,53 +28,6 @@
namespace UnitTest namespace UnitTest
{ {
// Mock asset handler for GradientSignal::ImageAsset that we can use in unit tests to pretend to load an image asset with.
// Also includes utility functions for creating image assets with specific testable patterns.
struct ImageAssetMockAssetHandler : public AZ::Data::AssetHandler
{
//! Creates a deterministically random set of pixel data as an ImageAsset.
//! \param width The width of the ImageAsset
//! \param height The height of the ImageAsset
//! \param seed The random seed to use for generating the random data
//! \return The ImageAsset in a loaded ready state
static AZ::Data::Asset<GradientSignal::ImageAsset> CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed);
//! Creates an ImageAsset where all the pixels are 0 except for the one pixel at the given coordinates, which is set to 1.
//! \param width The width of the ImageAsset
//! \param height The height of the ImageAsset
//! \param pixelX The X coordinate of the pixel to set to 1
//! \param pixelY The Y coordinate of the pixel to set to 1
//! \return The ImageAsset in a loaded ready state
static AZ::Data::Asset<GradientSignal::ImageAsset> CreateSpecificPixelImageAsset(
AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY);
AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override
{
// For our mock handler, always mark our assets as immediately ready.
return aznew GradientSignal::ImageAsset(id, AZ::Data::AssetData::AssetStatus::Ready);
}
void DestroyAsset(AZ::Data::AssetPtr ptr) override
{
if (ptr)
{
delete ptr;
}
}
void GetHandledAssetTypes([[maybe_unused]] AZStd::vector<AZ::Data::AssetType>& assetTypes) override
{
}
AZ::Data::AssetHandler::LoadResult LoadAssetData(
[[maybe_unused]] const AZ::Data::Asset<AZ::Data::AssetData>& asset,
[[maybe_unused]] AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
[[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) override
{
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
};
struct MockGradientRequestsBus struct MockGradientRequestsBus
: public GradientSignal::GradientRequestBus::Handler : public GradientSignal::GradientRequestBus::Handler
{ {

@ -11,6 +11,5 @@ set(FILES
Tests/GradientSignalTestHelpers.h Tests/GradientSignalTestHelpers.h
Tests/GradientSignalTestFixtures.cpp Tests/GradientSignalTestFixtures.cpp
Tests/GradientSignalTestFixtures.h Tests/GradientSignalTestFixtures.h
Tests/GradientSignalTestMocks.cpp
Tests/GradientSignalTestMocks.h Tests/GradientSignalTestMocks.h
) )

Loading…
Cancel
Save