Merge remote-tracking branch 'upstream/stabilization/2110' into Prism/show-gem-repos-update
Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com>
This commit is contained in:
@@ -76,6 +76,12 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC
|
||||
Legacy::CrySystem
|
||||
)
|
||||
|
||||
if(PAL_TRAIT_BUILD_SERVER_SUPPORTED)
|
||||
set(server_runtime_dependencies
|
||||
Legacy::CrySystem
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -1121,8 +1121,8 @@ inline ISystem* GetISystem()
|
||||
|
||||
// Description:
|
||||
// This function must be called once by each module at the beginning, to setup global pointers.
|
||||
extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, const char* moduleName);
|
||||
extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem(ISystem* pSystem);
|
||||
void ModuleInitISystem(ISystem* pSystem, const char* moduleName);
|
||||
void ModuleShutdownISystem(ISystem* pSystem);
|
||||
extern "C" AZ_DLL_EXPORT void InjectEnvironment(void* env);
|
||||
extern "C" AZ_DLL_EXPORT void DetachEnvironment();
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ void InitCRTHandlers() {}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// This is an entry to DLL initialization function that must be called for each loaded module
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName)
|
||||
void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName)
|
||||
{
|
||||
if (gEnv) // Already registered.
|
||||
{
|
||||
@@ -96,7 +96,7 @@ extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused
|
||||
} // if pSystem
|
||||
}
|
||||
|
||||
extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem)
|
||||
void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem)
|
||||
{
|
||||
// Unregister with AZ environment.
|
||||
AZ::Environment::Detach();
|
||||
|
||||
@@ -173,8 +173,6 @@ void CryEngineSignalHandler(int signal)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#if defined(WIN32) || defined(LINUX) || defined(APPLE)
|
||||
# define DLL_MODULE_INIT_ISYSTEM "ModuleInitISystem"
|
||||
# define DLL_MODULE_SHUTDOWN_ISYSTEM "ModuleShutdownISystem"
|
||||
# define DLL_INITFUNC_RENDERER "PackageRenderConstructor"
|
||||
# define DLL_INITFUNC_SOUND "CreateSoundSystem"
|
||||
# define DLL_INITFUNC_FONT "CreateCryFontInterface"
|
||||
@@ -188,8 +186,6 @@ void CryEngineSignalHandler(int signal)
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
# define DLL_MODULE_INIT_ISYSTEM (LPCSTR)2
|
||||
# define DLL_MODULE_SHUTDOWN_ISYSTEM (LPCSTR)3
|
||||
# define DLL_INITFUNC_RENDERER (LPCSTR)1
|
||||
# define DLL_INITFUNC_RENDERER (LPCSTR)1
|
||||
# define DLL_INITFUNC_SOUND (LPCSTR)1
|
||||
@@ -445,18 +441,6 @@ AZStd::unique_ptr<AZ::DynamicModuleHandle> CSystem::LoadDLL(const char* dllName)
|
||||
return handle;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// After loading DLL initialize it by calling ModuleInitISystem
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZStd::string moduleName = PathUtil::GetFileName(dllName);
|
||||
|
||||
typedef void*(*PtrFunc_ModuleInitISystem)(ISystem* pSystem, const char* moduleName);
|
||||
PtrFunc_ModuleInitISystem pfnModuleInitISystem = handle->GetFunction<PtrFunc_ModuleInitISystem>(DLL_MODULE_INIT_ISYSTEM);
|
||||
if (pfnModuleInitISystem)
|
||||
{
|
||||
pfnModuleInitISystem(this, moduleName.c_str());
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -497,13 +481,6 @@ void CSystem::ShutdownModuleLibraries()
|
||||
#if !defined(AZ_MONOLITHIC_BUILD)
|
||||
for (auto iterator = m_moduleDLLHandles.begin(); iterator != m_moduleDLLHandles.end(); ++iterator)
|
||||
{
|
||||
typedef void*( * PtrFunc_ModuleShutdownISystem )(ISystem* pSystem);
|
||||
|
||||
PtrFunc_ModuleShutdownISystem pfnModuleShutdownISystem = iterator->second->GetFunction<PtrFunc_ModuleShutdownISystem>(DLL_MODULE_SHUTDOWN_ISYSTEM);
|
||||
if (pfnModuleShutdownISystem)
|
||||
{
|
||||
pfnModuleShutdownISystem(this);
|
||||
}
|
||||
if (iterator->second->IsLoaded())
|
||||
{
|
||||
iterator->second->Unload();
|
||||
|
||||
@@ -333,11 +333,6 @@ void CXConsole::Init(ISystem* pSystem)
|
||||
|
||||
m_nLoadingBackTexID = -1;
|
||||
|
||||
if (gEnv->IsDedicated())
|
||||
{
|
||||
m_bConsoleActive = true;
|
||||
}
|
||||
|
||||
REGISTER_COMMAND("ConsoleShow", &ConsoleShow, VF_NULL, "Opens the console");
|
||||
REGISTER_COMMAND("ConsoleHide", &ConsoleHide, VF_NULL, "Closes the console");
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace AssetProcessor
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
UnitTestUtils::AssertAbsorber* m_errorAbsorber;
|
||||
AZStd::unique_ptr<UnitTestUtils::AssertAbsorber> m_errorAbsorber{};
|
||||
FileStatePassthrough m_fileStateCache;
|
||||
|
||||
void SetUp() override
|
||||
@@ -40,7 +40,7 @@ namespace AssetProcessor
|
||||
m_ownsSysAllocator = true;
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
|
||||
}
|
||||
m_errorAbsorber = new UnitTestUtils::AssertAbsorber();
|
||||
m_errorAbsorber = AZStd::make_unique<UnitTestUtils::AssertAbsorber>();
|
||||
|
||||
m_application = AZStd::make_unique<AzFramework::Application>();
|
||||
|
||||
@@ -62,8 +62,8 @@ namespace AssetProcessor
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
|
||||
m_application.reset();
|
||||
delete m_errorAbsorber;
|
||||
m_errorAbsorber = nullptr;
|
||||
m_errorAbsorber.reset();
|
||||
|
||||
if (m_ownsSysAllocator)
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
ProcessingOverlayWidget::ProcessingOverlayWidget(UI::OverlayWidget* overlay, Layout layout, Uuid traceTag)
|
||||
: QWidget()
|
||||
: QWidget(nullptr, Qt::Tool | Qt::WindowStaysOnTopHint)
|
||||
, m_traceTag(traceTag)
|
||||
, ui(new Ui::ProcessingOverlayWidget())
|
||||
, m_overlay(overlay)
|
||||
|
||||
@@ -100,13 +100,6 @@ namespace ImageProcessingAtom
|
||||
//compare whether two images are same. return true if they are same.
|
||||
virtual bool CompareImage(const IImageObjectPtr otherImage) const = 0;
|
||||
|
||||
// Writes this image to file used for runtime, overwrites any existing file.
|
||||
// It may write alpha image as attached image into the same file
|
||||
// outFilePaths will save filenames finally saved to since the image might be split and saved to multiple files
|
||||
virtual bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector<AZStd::string>& outFilePaths) const = 0;
|
||||
virtual bool SaveImage(AZ::IO::SystemFileStream& out) const = 0;
|
||||
virtual bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const = 0;
|
||||
|
||||
//get total image data size in memory of all mipmaps. Not includs header and flags.
|
||||
virtual AZ::u32 GetTextureMemory() const = 0;
|
||||
|
||||
@@ -135,9 +128,6 @@ namespace ImageProcessingAtom
|
||||
// The algorithm is based on the Frequency Domain Normal Mapping implementation presented by Neubelt and Pettineo at Siggraph 2013.
|
||||
virtual void GlossFromNormals(bool hasAuthoredGloss) = 0;
|
||||
|
||||
//convert gloss map from legacy distribution to new one. New World is still using legacy gloss map.
|
||||
virtual void ConvertLegacyGloss() = 0;
|
||||
|
||||
//clear image with color
|
||||
virtual void ClearColor(float r, float g, float b, float a) = 0;
|
||||
};
|
||||
|
||||
@@ -45,10 +45,7 @@ namespace ImageProcessingAtom
|
||||
->Field("MinTextureSize", &PresetSettings::m_minTextureSize)
|
||||
->Field("IsPowerOf2", &PresetSettings::m_isPowerOf2)
|
||||
->Field("SizeReduceLevel", &PresetSettings::m_sizeReduceLevel)
|
||||
->Field("IsColorChart", &PresetSettings::m_isColorChart)
|
||||
->Field("HighPassMip", &PresetSettings::m_highPassMip)
|
||||
->Field("GlossFromNormal", &PresetSettings::m_glossFromNormals)
|
||||
->Field("UseLegacyGloss", &PresetSettings::m_isLegacyGloss)
|
||||
->Field("MipRenormalize", &PresetSettings::m_isMipRenormalize)
|
||||
->Field("NumberResidentMips", &PresetSettings::m_numResidentMips)
|
||||
->Field("Swizzle", &PresetSettings::m_swizzle)
|
||||
@@ -200,10 +197,7 @@ namespace ImageProcessingAtom
|
||||
m_maxTextureSize == other.m_maxTextureSize &&
|
||||
m_isPowerOf2 == other.m_isPowerOf2 &&
|
||||
m_sizeReduceLevel == other.m_sizeReduceLevel &&
|
||||
m_isColorChart == other.m_isColorChart &&
|
||||
m_highPassMip == other.m_highPassMip &&
|
||||
m_glossFromNormals == other.m_glossFromNormals &&
|
||||
m_isLegacyGloss == other.m_isLegacyGloss &&
|
||||
m_swizzle == other.m_swizzle &&
|
||||
m_isMipRenormalize == other.m_isMipRenormalize &&
|
||||
m_numResidentMips == other.m_numResidentMips;
|
||||
@@ -239,10 +233,7 @@ namespace ImageProcessingAtom
|
||||
m_maxTextureSize = other.m_maxTextureSize;
|
||||
m_isPowerOf2 = other.m_isPowerOf2;
|
||||
m_sizeReduceLevel = other.m_sizeReduceLevel;
|
||||
m_isColorChart = other.m_isColorChart;
|
||||
m_highPassMip = other.m_highPassMip;
|
||||
m_glossFromNormals = other.m_glossFromNormals;
|
||||
m_isLegacyGloss = other.m_isLegacyGloss;
|
||||
m_swizzle = other.m_swizzle;
|
||||
m_isMipRenormalize = other.m_isMipRenormalize;
|
||||
m_numResidentMips = other.m_numResidentMips;
|
||||
|
||||
@@ -84,16 +84,7 @@ namespace ImageProcessingAtom
|
||||
|
||||
//settings for mipmap generation. it's null if this preset disable mipmap.
|
||||
AZStd::unique_ptr<MipmapSettings> m_mipmapSetting;
|
||||
|
||||
//some specific settings
|
||||
// "colorchart". This is to indicate if need to extract color chart from the image and output the color chart data.
|
||||
// This is very specific usage for cryEngine. Check ColorChart.cpp for better explanation.
|
||||
bool m_isColorChart = false;
|
||||
|
||||
//"highpass". Defines which mip level is subtracted when applying the high pass filter
|
||||
//this is only used for terrain asset. we might remove it later since it can be done with source image directly
|
||||
AZ::u32 m_highPassMip = 0;
|
||||
|
||||
|
||||
//"glossfromnormals". Bake normal variance into smoothness stored in alpha channel
|
||||
AZ::u32 m_glossFromNormals = 0;
|
||||
|
||||
@@ -109,10 +100,6 @@ namespace ImageProcessingAtom
|
||||
//that add up to 64K or lower
|
||||
AZ::u8 m_numResidentMips = 0;
|
||||
|
||||
//legacy options might be removed later
|
||||
//"glosslegacydist". If the gloss map use legacy distribution. NW is still using legacy dist
|
||||
bool m_isLegacyGloss = false;
|
||||
|
||||
//"swizzle". need to be 4 character and each character need to be one of "rgba01"
|
||||
AZStd::string m_swizzle;
|
||||
|
||||
|
||||
@@ -1,307 +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 <Processing/ImageObjectImpl.h>
|
||||
#include <Processing/ImageToProcess.h>
|
||||
|
||||
namespace ImageProcessingAtom
|
||||
{
|
||||
const int COLORCHART_IMAGE_WIDTH = 78;
|
||||
const int COLORCHART_IMAGE_HEIGHT = 66;
|
||||
|
||||
// color chart in cry engine is a special image data, with size 78x66, you may see in game screenshot which is defined by a rectangle
|
||||
// area with a yellow-black dash line boarder
|
||||
// Create color chart function is to read that block of image data and convert it to a color table then save it to another image
|
||||
// with size 256x16.
|
||||
|
||||
class C3dLutColorChart
|
||||
{
|
||||
public:
|
||||
C3dLutColorChart() {}
|
||||
~C3dLutColorChart() {};
|
||||
|
||||
//generate default color chart data
|
||||
void GenerateDefault();
|
||||
|
||||
//generate color chart data from input image
|
||||
bool GenerateFromInput(IImageObjectPtr image);
|
||||
|
||||
//ouput the color chart data to an image object
|
||||
IImageObjectPtr GenerateChartImage();
|
||||
|
||||
protected:
|
||||
//extract color chart data from specified location in an image
|
||||
void ExtractFromImageAt(IImageObjectPtr pImg, AZ::u32 x, AZ::u32 y);
|
||||
|
||||
//find color chart location in an image
|
||||
static bool FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY);
|
||||
|
||||
//if there is a color chart at specified location
|
||||
static bool IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch);
|
||||
|
||||
private:
|
||||
enum EPrimaryShades
|
||||
{
|
||||
ePS_Red = 16,
|
||||
ePS_Green = 16,
|
||||
ePS_Blue = 16,
|
||||
|
||||
ePS_NumColors = ePS_Red * ePS_Green * ePS_Blue
|
||||
};
|
||||
|
||||
struct SColor
|
||||
{
|
||||
unsigned char r, g, b, _padding;
|
||||
};
|
||||
|
||||
typedef AZStd::vector<SColor> ColorMapping;
|
||||
|
||||
ColorMapping m_mapping;
|
||||
};
|
||||
|
||||
void C3dLutColorChart::GenerateDefault()
|
||||
{
|
||||
m_mapping.reserve(ePS_NumColors);
|
||||
|
||||
for (int b = 0; b < ePS_Blue; ++b)
|
||||
{
|
||||
for (int g = 0; g < ePS_Green; ++g)
|
||||
{
|
||||
for (int r = 0; r < ePS_Red; ++r)
|
||||
{
|
||||
SColor col;
|
||||
col.r = static_cast<unsigned char>(255 * r / (ePS_Red));
|
||||
col.g = static_cast<unsigned char>(255 * g / (ePS_Green));
|
||||
col.b = static_cast<unsigned char>(255 * b / (ePS_Blue));
|
||||
int l = 255 - (col.r * 3 + col.g * 6 + col.b) / 10;
|
||||
col.r = col.g = col.b = (unsigned char)l;
|
||||
m_mapping.push_back(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//find color chart location in a image
|
||||
bool C3dLutColorChart::FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY)
|
||||
{
|
||||
const AZ::u32 width = pImg->GetWidth(0);
|
||||
const AZ::u32 height = pImg->GetHeight(0);
|
||||
|
||||
//the origin image is too small to have a color chart
|
||||
if (width < COLORCHART_IMAGE_WIDTH || height < COLORCHART_IMAGE_HEIGHT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::u8* pData;
|
||||
AZ::u32 pitch;
|
||||
pImg->GetImagePointer(0, pData, pitch);
|
||||
|
||||
//check all the posible start location on whether there might be a color chart
|
||||
for (AZ::u32 y = 0; y <= height - COLORCHART_IMAGE_HEIGHT; ++y)
|
||||
{
|
||||
for (AZ::u32 x = 0; x <= width - COLORCHART_IMAGE_WIDTH; ++x)
|
||||
{
|
||||
if (IsColorChartAt(x, y, pData, pitch))
|
||||
{
|
||||
outLocX = x;
|
||||
outLocY = y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool C3dLutColorChart::GenerateFromInput(IImageObjectPtr image)
|
||||
{
|
||||
AZ::u32 outLocX, outLocY;
|
||||
if (FindColorChart(image, outLocX, outLocY))
|
||||
{
|
||||
ExtractFromImageAt(image, outLocX, outLocY);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IImageObjectPtr C3dLutColorChart::GenerateChartImage()
|
||||
{
|
||||
IImageObjectPtr image(IImageObject::CreateImage(ePS_Red* ePS_Blue, ePS_Green, 1, ePixelFormat_R8G8B8A8));
|
||||
|
||||
{
|
||||
AZ::u8* pData;
|
||||
AZ::u32 pitch;
|
||||
image->GetImagePointer(0, pData, pitch);
|
||||
|
||||
size_t nSlicePitch = (pitch / ePS_Blue);
|
||||
AZ::u32 src = 0;
|
||||
for (int b = 0; b < ePS_Blue; ++b)
|
||||
{
|
||||
for (int g = 0; g < ePS_Green; ++g)
|
||||
{
|
||||
AZ::u8* p = pData + g * pitch + b * nSlicePitch;
|
||||
for (int r = 0; r < ePS_Red; ++r)
|
||||
{
|
||||
const SColor& c = m_mapping[src];
|
||||
p[0] = c.r;
|
||||
p[1] = c.g;
|
||||
p[2] = c.b;
|
||||
p[3] = 255;
|
||||
++src;
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void C3dLutColorChart::ExtractFromImageAt(IImageObjectPtr image, AZ::u32 x, AZ::u32 y)
|
||||
{
|
||||
int ox = x + 1;
|
||||
int oy = y + 1;
|
||||
|
||||
AZ::u8* pData;
|
||||
AZ::u32 pitch;
|
||||
image->GetImagePointer(0, pData, pitch);
|
||||
|
||||
m_mapping.reserve(ePS_NumColors);
|
||||
|
||||
for (int b = 0; b < ePS_Blue; ++b)
|
||||
{
|
||||
int px = ox + ePS_Red * (b % 4);
|
||||
int py = oy + ePS_Green * (b / 4);
|
||||
|
||||
for (int g = 0; g < ePS_Green; ++g)
|
||||
{
|
||||
for (int r = 0; r < ePS_Red; ++r)
|
||||
{
|
||||
AZ::u8* p = pData + pitch * (py + g) + (px + r) * 4;
|
||||
|
||||
SColor col;
|
||||
col.r = p[0];
|
||||
col.g = p[1];
|
||||
col.b = p[2];
|
||||
m_mapping.push_back(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check if image data at location x and y could be a color chart
|
||||
//based on if the boarder is dash lines with two pixel each segement
|
||||
//the idea and implementation are both coming from CryEngine.
|
||||
bool C3dLutColorChart::IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch)
|
||||
{
|
||||
struct Color
|
||||
{
|
||||
private:
|
||||
int c[3];
|
||||
|
||||
public:
|
||||
Color(AZ::u32 x, AZ::u32 y, void* pPixels, AZ::u32 pitch)
|
||||
{
|
||||
const uint8* p = (const uint8*)pPixels + pitch * y + x * 4;
|
||||
c[0] = p[0];
|
||||
c[1] = p[1];
|
||||
c[2] = p[2];
|
||||
}
|
||||
|
||||
bool isSimilar(const Color& a, int maxDiff) const
|
||||
{
|
||||
return
|
||||
abs(a.c[0] - c[0]) <= maxDiff &&
|
||||
abs(a.c[1] - c[1]) <= maxDiff &&
|
||||
abs(a.c[2] - c[2]) <= maxDiff;
|
||||
}
|
||||
};
|
||||
|
||||
const Color colorRef[2] =
|
||||
{
|
||||
Color(x, y, pData, pitch),
|
||||
Color(x + 2, y, pData, pitch)
|
||||
};
|
||||
|
||||
// We require two colors of the border to be at least a bit different
|
||||
if (colorRef[0].isSimilar(colorRef[1], 15))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static const int kMaxDiff = 3;
|
||||
|
||||
int refIdx = 0;
|
||||
//rectangle's top
|
||||
for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2)
|
||||
{
|
||||
if (!colorRef[refIdx].isSimilar(Color(x + i, y, pData, pitch), kMaxDiff) ||
|
||||
!colorRef[refIdx].isSimilar(Color(x + i + 1, y, pData, pitch), kMaxDiff))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
refIdx ^= 1;
|
||||
}
|
||||
|
||||
refIdx = 0;
|
||||
//left
|
||||
for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2)
|
||||
{
|
||||
if (!colorRef[refIdx].isSimilar(Color(x, y + i, pData, pitch), kMaxDiff) ||
|
||||
!colorRef[refIdx].isSimilar(Color(x, y + i + 1, pData, pitch), kMaxDiff))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
refIdx ^= 1;
|
||||
}
|
||||
|
||||
refIdx = 0;
|
||||
//right
|
||||
for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2)
|
||||
{
|
||||
if (!colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i, pData, pitch), kMaxDiff) ||
|
||||
!colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i + 1, pData, pitch), kMaxDiff))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
refIdx ^= 1;
|
||||
}
|
||||
|
||||
refIdx = 0;
|
||||
//bottom
|
||||
for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2)
|
||||
{
|
||||
if (!colorRef[refIdx].isSimilar(Color(x + i, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff) ||
|
||||
!colorRef[refIdx].isSimilar(Color(x + i + 1, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
refIdx ^= 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ImageToProcess::CreateColorChart()
|
||||
{
|
||||
C3dLutColorChart colorChart;
|
||||
|
||||
//get color chart data from source image.
|
||||
if (!colorChart.GenerateFromInput(m_img))
|
||||
{
|
||||
//if load from image failed then generate default color data
|
||||
colorChart.GenerateDefault();
|
||||
}
|
||||
|
||||
//save color chart data to an image and save as current
|
||||
m_img = colorChart.GenerateChartImage();
|
||||
}
|
||||
}
|
||||
@@ -547,7 +547,7 @@ namespace ImageProcessingAtom
|
||||
}
|
||||
|
||||
//generate box filtered source image mip chain
|
||||
IImageObjectPtr mippedSourceImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, ePixelFormat_R32G32B32A32F));
|
||||
IImageObjectPtr mippedSourceImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, srcPixelFormat));
|
||||
mippedSourceImage->CopyPropertiesFrom(m_image->Get());
|
||||
|
||||
for (int iSide = 0; iSide < 6; ++iSide)
|
||||
|
||||
@@ -1,100 +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 <Processing/ImageObjectImpl.h>
|
||||
#include <Processing/ImageToProcess.h>
|
||||
#include <Processing/ImageConvert.h>
|
||||
#include <Processing/PixelFormatInfo.h>
|
||||
#include <Converters/FIR-Windows.h>
|
||||
#include <Converters/PixelOperation.h>
|
||||
|
||||
namespace ImageProcessingAtom
|
||||
{
|
||||
// higher mip level is subtracted by lower mip level when applying the [cheap] high pass filter
|
||||
void ImageToProcess::CreateHighPass(AZ::u32 dwMipDown)
|
||||
{
|
||||
//no need to convert if mip go down 0
|
||||
if (dwMipDown == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const EPixelFormat ePixelFormat = m_img->GetPixelFormat();
|
||||
|
||||
if (ePixelFormat != ePixelFormat_R32G32B32A32F)
|
||||
{
|
||||
AZ_Assert(false, "You need convert the orginal image to ePixelFormat_R32G32B32A32F before call this function");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
AZ::u32 dwWidth, dwHeight, dwMips;
|
||||
dwWidth = m_img->GetWidth(0);
|
||||
dwHeight = m_img->GetHeight(0);
|
||||
dwMips = m_img->GetMipCount();
|
||||
|
||||
if (dwMipDown >= dwMips)
|
||||
{
|
||||
AZ_Warning("Image Processing", false, "CreateHighPass can't go down %i MIP levels for high pass as there are not\
|
||||
enough MIP levels available, going down by %i instead", dwMipDown, dwMips - 1);
|
||||
dwMipDown = dwMips - 1;
|
||||
}
|
||||
|
||||
IImageObjectPtr newImage(IImageObject::CreateImage(dwWidth, dwHeight, dwMips, ePixelFormat));
|
||||
newImage->CopyPropertiesFrom(m_img);
|
||||
|
||||
IPixelOperationPtr pixelOp = CreatePixelOperation(ePixelFormat);
|
||||
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(ePixelFormat)->bitsPerBlock / 8;
|
||||
|
||||
AZ::u32 dstMips = newImage->GetMipCount();
|
||||
for (AZ::u32 dstMip = 0; dstMip < dwMipDown; ++dstMip)
|
||||
{
|
||||
// linear interpolation
|
||||
FilterImage(MipGenType::triangle, MipGenEvalType::sum, 0.0f, 0.0f, m_img, dwMipDown, newImage, dstMip, NULL, NULL);
|
||||
|
||||
//substraction
|
||||
AZ::u8* srcPixelBuf;
|
||||
AZ::u32 srcPitch;
|
||||
m_img->GetImagePointer(dstMip, srcPixelBuf, srcPitch);
|
||||
AZ::u8* dstPixelBuf;
|
||||
AZ::u32 dstPitch;
|
||||
newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch);
|
||||
const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip);
|
||||
|
||||
for (AZ::u32 i = 0; i < pixelCount; ++i, srcPixelBuf += pixelBytes, dstPixelBuf += pixelBytes)
|
||||
{
|
||||
float r1, g1, b1, a1, r2, g2, b2, a2;
|
||||
pixelOp->GetRGBA(srcPixelBuf, r1, g1, b1, a1);
|
||||
pixelOp->GetRGBA(dstPixelBuf, r2, g2, b2, a2);
|
||||
|
||||
r2 = AZ::GetClamp<float>(r1 - r2 + 0.5f, 0.0f, 1.0f);
|
||||
g2 = AZ::GetClamp<float>(g1 - g2 + 0.5f, 0.0f, 1.0f);
|
||||
b2 = AZ::GetClamp<float>(b1 - b2 + 0.5f, 0.0f, 1.0f);
|
||||
a2 = AZ::GetClamp<float>(a1 - a2 + 0.5f, 0.0f, 1.0f);
|
||||
pixelOp->SetRGBA(dstPixelBuf, r2, g2, b2, a2);
|
||||
}
|
||||
}
|
||||
|
||||
// mips below the chosen highpass mip are grey
|
||||
for (AZ::u32 dstMip = dwMipDown; dstMip < dstMips; ++dstMip)
|
||||
{
|
||||
AZ::u8* dstPixelBuf;
|
||||
AZ::u32 dstPitch;
|
||||
newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch);
|
||||
const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip);
|
||||
|
||||
for (AZ::u32 i = 0; i < pixelCount; ++i, dstPixelBuf += pixelBytes)
|
||||
{
|
||||
pixelOp->SetRGBA(dstPixelBuf, 0.5f, 0.5f, 0.5f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
m_img = newImage;
|
||||
}
|
||||
} // namespace ImageProcessingAtom
|
||||
@@ -82,10 +82,7 @@ namespace ImageProcessingAtomEditor
|
||||
presetInfoText += "\n";
|
||||
presetInfoText += QString("Suppress Engine Reduce: %1\n").arg(presetSettings->m_suppressEngineReduce ? "True" : "False");
|
||||
presetInfoText += QString("Discard Alpha: %1\n").arg(presetSettings->m_discardAlpha ? "True" : "False");
|
||||
presetInfoText += QString("Is Color Chart: %1\n").arg(presetSettings->m_isColorChart ? "True" : "False");
|
||||
presetInfoText += QString("High Pass Mip: %1\n").arg(presetSettings->m_highPassMip);
|
||||
presetInfoText += QString("Gloss From Normal: %1\n").arg(presetSettings->m_glossFromNormals);
|
||||
presetInfoText += QString("Use Legacy Gloss: %1\n").arg(presetSettings->m_isLegacyGloss ? "True" : "False");
|
||||
presetInfoText += QString("Mip Re-normalize: %1\n").arg(presetSettings->m_isMipRenormalize ? "True" : "False");
|
||||
presetInfoText += QString("Resident Mips Number: %1\n").arg(presetSettings->m_numResidentMips);
|
||||
presetInfoText += QString("Swizzle: %1\n").arg(presetSettings->m_swizzle.c_str());
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace ImageProcessingAtom
|
||||
builderDescriptor.m_busId = azrtti_typeid<ImageBuilderWorker>();
|
||||
builderDescriptor.m_createJobFunction = AZStd::bind(&ImageBuilderWorker::CreateJobs, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
builderDescriptor.m_processJobFunction = AZStd::bind(&ImageBuilderWorker::ProcessJob, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
builderDescriptor.m_version = 25; // [ATOM-16575]
|
||||
builderDescriptor.m_version = 26; // [ATOM-15086]
|
||||
builderDescriptor.m_analysisFingerprint = ImageProcessingAtom::BuilderSettingManager::Instance()->GetAnalysisFingerprint();
|
||||
m_imageBuilder.BusConnect(builderDescriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor);
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace ImageProcessingAtom
|
||||
enum ConvertStep
|
||||
{
|
||||
StepValidateInput = 0,
|
||||
StepGenerateColorChart,
|
||||
StepConvertToLinear,
|
||||
StepSwizzle,
|
||||
StepCubemapLayout,
|
||||
@@ -55,9 +54,7 @@ namespace ImageProcessingAtom
|
||||
StepMipmap,
|
||||
StepGlossFromNormal,
|
||||
StepPostNormalize,
|
||||
StepCreateHighPass,
|
||||
StepConvertOutputColorSpace,
|
||||
StepAlphaImage,
|
||||
StepConvertPixelFormat,
|
||||
StepSaveToFile,
|
||||
StepAll
|
||||
@@ -66,7 +63,6 @@ namespace ImageProcessingAtom
|
||||
[[maybe_unused]] const char ProcessStepNames[StepAll][64] =
|
||||
{
|
||||
"ValidateInput",
|
||||
"GenerateColorChart",
|
||||
"ConvertToLinear",
|
||||
"Swizzle",
|
||||
"CubemapLayout",
|
||||
@@ -75,9 +71,7 @@ namespace ImageProcessingAtom
|
||||
"Mipmap",
|
||||
"GlossFromNormal",
|
||||
"PostNormalize",
|
||||
"CreateHighPass",
|
||||
"ConvertOutputColorSpace",
|
||||
"AlphaImage",
|
||||
"ConvertPixelFormat",
|
||||
"SaveToFile",
|
||||
};
|
||||
@@ -94,11 +88,6 @@ namespace ImageProcessingAtom
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IImageObjectPtr ImageConvertProcess::GetOutputAlphaImage()
|
||||
{
|
||||
return m_alphaImage;
|
||||
}
|
||||
|
||||
IImageObjectPtr ImageConvertProcess::GetOutputIBLSpecularCubemap()
|
||||
{
|
||||
return m_iblSpecularCubemapImage;
|
||||
@@ -180,6 +169,58 @@ namespace ImageProcessingAtom
|
||||
m_image = new ImageToProcess(IImageObjectPtr(m_input->m_inputImage->Clone(mipsToClone)));
|
||||
}
|
||||
|
||||
break;
|
||||
case StepConvertToLinear:
|
||||
// convert to linear space and the output image pixel format should be rgba32f
|
||||
ConvertToLinear();
|
||||
break;
|
||||
case StepSwizzle:
|
||||
{
|
||||
// swizzle if swizzle was set or decard alpha
|
||||
bool swizzleWasSet = m_input->m_presetSetting.m_swizzle.size() >= 4;
|
||||
if (swizzleWasSet || m_input->m_presetSetting.m_discardAlpha)
|
||||
{
|
||||
AZStd::string swizzle = "rgba";
|
||||
if (swizzleWasSet)
|
||||
{
|
||||
swizzle = m_input->m_presetSetting.m_swizzle.substr(0, 4);
|
||||
}
|
||||
|
||||
if (m_input->m_presetSetting.m_discardAlpha)
|
||||
{
|
||||
swizzle[3] = '1';
|
||||
}
|
||||
|
||||
m_image->Get()->Swizzle(swizzle.c_str());
|
||||
if (!m_input->m_presetSetting.m_discardAlpha)
|
||||
{
|
||||
m_alphaContent = EAlphaContent::eAlphaContent_Absent;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_alphaContent = m_image->Get()->GetAlphaContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StepCubemapLayout:
|
||||
// convert cubemap image's layout to vertical strip used in game.
|
||||
if (IsConvertToCubemap())
|
||||
{
|
||||
if (!m_image->ConvertCubemapLayout(CubemapLayoutVertical))
|
||||
{
|
||||
m_image->Set(nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StepPreNormalize:
|
||||
// normalize base image before mipmap generation if glossfromnormals is enabled and require normalize
|
||||
if (m_input->m_presetSetting.m_isMipRenormalize && m_input->m_presetSetting.m_glossFromNormals)
|
||||
{
|
||||
// Normalize the base mip map. This has to be done explicitly because we need to disable mip renormalization to
|
||||
// preserve the normal length when deriving the normal variance
|
||||
m_image->Get()->NormalizeVectors(0, 1);
|
||||
}
|
||||
break;
|
||||
case StepGenerateIBL:
|
||||
if (IsConvertToCubemap())
|
||||
@@ -204,56 +245,6 @@ namespace ImageProcessingAtom
|
||||
m_isFinished = true;
|
||||
}
|
||||
break;
|
||||
case StepGenerateColorChart:
|
||||
// GenerateColorChart.
|
||||
if (m_input->m_presetSetting.m_isColorChart)
|
||||
{
|
||||
// Convert to uncompressed format if it's compressed format. For example, loaded from DDS file.
|
||||
if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_image->Get()->GetPixelFormat()))
|
||||
{
|
||||
m_image->ConvertFormat(ePixelFormat_R32G32B32A32F);
|
||||
}
|
||||
|
||||
m_image->CreateColorChart();
|
||||
}
|
||||
break;
|
||||
case StepConvertToLinear:
|
||||
// convert to linear space and the output image pixel format should be rgba32f
|
||||
ConvertToLinear();
|
||||
break;
|
||||
case StepSwizzle:
|
||||
// convert texture format.
|
||||
if (m_input->m_presetSetting.m_swizzle.size() >= 4)
|
||||
{
|
||||
m_image->Get()->Swizzle(m_input->m_presetSetting.m_swizzle.substr(0, 4).c_str());
|
||||
m_alphaContent = m_image->Get()->GetAlphaContent();
|
||||
}
|
||||
|
||||
// convert gloss map (alhpa channel) from legacy distribution to new one
|
||||
if (m_input->m_presetSetting.m_isLegacyGloss)
|
||||
{
|
||||
m_image->Get()->ConvertLegacyGloss();
|
||||
}
|
||||
break;
|
||||
case StepCubemapLayout:
|
||||
// convert cubemap image's layout to vertical strip used in game.
|
||||
if (IsConvertToCubemap())
|
||||
{
|
||||
if (!m_image->ConvertCubemapLayout(CubemapLayoutVertical))
|
||||
{
|
||||
m_image->Set(nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StepPreNormalize:
|
||||
// normalize base image before mipmap generation if glossfromnormals is enabled and require normalize
|
||||
if (m_input->m_presetSetting.m_isMipRenormalize && m_input->m_presetSetting.m_glossFromNormals)
|
||||
{
|
||||
// Normalize the base mip map. This has to be done explicitly because we need to disable mip renormalization to
|
||||
// preserve the normal length when deriving the normal variance
|
||||
m_image->Get()->NormalizeVectors(0, 1);
|
||||
}
|
||||
break;
|
||||
case StepMipmap:
|
||||
// generate mipmaps
|
||||
if (IsConvertToCubemap())
|
||||
@@ -304,20 +295,10 @@ namespace ImageProcessingAtom
|
||||
m_image->Get()->AddImageFlags(EIF_RenormalizedTexture);
|
||||
}
|
||||
break;
|
||||
case StepCreateHighPass:
|
||||
if (m_input->m_presetSetting.m_highPassMip > 0)
|
||||
{
|
||||
m_image->CreateHighPass(m_input->m_presetSetting.m_highPassMip);
|
||||
}
|
||||
break;
|
||||
case StepConvertOutputColorSpace:
|
||||
// convert image from linear space to desired output color space
|
||||
ConvertToOuputColorSpace();
|
||||
break;
|
||||
case StepAlphaImage:
|
||||
// save alpha channel to separate image if it's needed
|
||||
CreateAlphaImage();
|
||||
break;
|
||||
case StepConvertPixelFormat:
|
||||
// convert pixel format
|
||||
ConvertPixelformat();
|
||||
@@ -411,12 +392,6 @@ namespace ImageProcessingAtom
|
||||
return;
|
||||
}
|
||||
|
||||
// don't do any reduce for color chart
|
||||
if (presetSettings->m_isColorChart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// get suitable size for dest pixel format
|
||||
CPixelFormats::GetInstance().GetSuitableImageSize(presetSettings->m_pixelFormat, inputWidth, inputHeight,
|
||||
outWidth, outHeight);
|
||||
@@ -510,52 +485,6 @@ namespace ImageProcessingAtom
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImageConvertProcess::CreateAlphaImage()
|
||||
{
|
||||
// if alpha content doesn't have alpha or we need to discard alpha, skip
|
||||
// we won't create alpha image for cubemap too
|
||||
if (m_alphaContent == EAlphaContent::eAlphaContent_Absent
|
||||
|| m_alphaContent == EAlphaContent::eAlphaContent_OnlyWhite
|
||||
|| m_input->m_presetSetting.m_discardAlpha || IsConvertToCubemap())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if dest format could save alpha, skip too
|
||||
if (!CPixelFormats::GetInstance().IsPixelFormatWithoutAlpha(m_input->m_presetSetting.m_pixelFormat))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// now create alpha image
|
||||
ImageToProcess alphaImage(m_image->Get());
|
||||
alphaImage.ConvertFormat(ePixelFormat_A8);
|
||||
|
||||
// validate pixelformatalpha
|
||||
if (CPixelFormats::GetInstance().IsFormatSingleChannel(m_input->m_presetSetting.m_pixelFormatAlpha))
|
||||
{
|
||||
alphaImage.ConvertFormat(m_input->m_presetSetting.m_pixelFormatAlpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
//For ASTC compression we need to clear out the alpha to get accurate rgb compression.
|
||||
if (IsASTCFormat(m_input->m_presetSetting.m_pixelFormat))
|
||||
{
|
||||
alphaImage.ConvertFormat(ePixelFormat_R8G8B8X8);
|
||||
alphaImage.ConvertFormat(m_input->m_presetSetting.m_pixelFormatAlpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "PixelFormatAlpha only supports single channel pixel formats or ASTC formats");
|
||||
}
|
||||
}
|
||||
|
||||
// get final result and save it to member variable for later use
|
||||
m_alphaImage = alphaImage.Get();
|
||||
|
||||
m_image->Get()->AddImageFlags(EIF_AttachedAlpha);
|
||||
}
|
||||
|
||||
// pixel format conversion
|
||||
bool ImageConvertProcess::ConvertPixelformat()
|
||||
{
|
||||
@@ -575,12 +504,6 @@ namespace ImageProcessingAtom
|
||||
m_image->GetCompressOption().rgbWeight = m_input->m_presetSetting.GetColorWeight();
|
||||
m_image->GetCompressOption().discardAlpha = m_input->m_presetSetting.m_discardAlpha;
|
||||
|
||||
//For ASTC compression we need to clear out the alpha to get accurate rgb compression.
|
||||
if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat))
|
||||
{
|
||||
m_image->GetCompressOption().discardAlpha = true;
|
||||
}
|
||||
|
||||
m_image->ConvertFormat(m_input->m_presetSetting.m_pixelFormat);
|
||||
|
||||
return true;
|
||||
@@ -762,7 +685,6 @@ namespace ImageProcessingAtom
|
||||
if (ImageProcess##PrivateName::DoesSupport(m_input->m_platform)) \
|
||||
{ \
|
||||
ImageProcess##PrivateName::PrepareImageForExport(m_image->Get()); \
|
||||
ImageProcess##PrivateName::PrepareImageForExport(m_alphaImage); \
|
||||
}
|
||||
AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS
|
||||
#undef AZ_RESTRICTED_PLATFORM_EXPANSION
|
||||
|
||||
@@ -115,7 +115,6 @@ namespace ImageProcessingAtom
|
||||
|
||||
//get output images
|
||||
IImageObjectPtr GetOutputImage();
|
||||
IImageObjectPtr GetOutputAlphaImage();
|
||||
IImageObjectPtr GetOutputIBLSpecularCubemap();
|
||||
IImageObjectPtr GetOutputIBLDiffuseCubemap();
|
||||
|
||||
@@ -131,8 +130,6 @@ namespace ImageProcessingAtom
|
||||
//for alpha
|
||||
//to indicate the current alpha channel content
|
||||
EAlphaContent m_alphaContent;
|
||||
//An image object to hold alpha channel in a separate image
|
||||
IImageObjectPtr m_alphaImage;
|
||||
|
||||
//output results of IBL cubemap generation, used in unit tests
|
||||
IImageObjectPtr m_iblSpecularCubemapImage;
|
||||
@@ -171,9 +168,6 @@ namespace ImageProcessingAtom
|
||||
//convert to output color space before compression
|
||||
bool ConvertToOuputColorSpace();
|
||||
|
||||
//create alpha image if it's needed
|
||||
void CreateAlphaImage();
|
||||
|
||||
//pixel format convertion/compression
|
||||
bool ConvertPixelformat();
|
||||
|
||||
|
||||
@@ -108,17 +108,14 @@ namespace ImageProcessingAtom
|
||||
}
|
||||
|
||||
IImageObjectPtr outputImage = m_process->GetOutputImage();
|
||||
IImageObjectPtr outputImageAlpha = m_process->GetOutputAlphaImage();
|
||||
|
||||
m_output->SetOutputImage(outputImage, ImageConvertOutput::Base);
|
||||
m_output->SetOutputImage(outputImageAlpha, ImageConvertOutput::Alpha);
|
||||
|
||||
if (!IsJobCancelled())
|
||||
{
|
||||
// For preview, combine image output with alpha if any
|
||||
m_output->SetProgress(1.0f / static_cast<float>(m_previewProcessStep));
|
||||
IImageObjectPtr combinedImage = MergeOutputImageForPreview(outputImage, outputImageAlpha);
|
||||
m_output->SetOutputImage(combinedImage, ImageConvertOutput::Preview);
|
||||
m_output->SetOutputImage(outputImage, ImageConvertOutput::Preview);
|
||||
}
|
||||
|
||||
m_output->SetReady(true);
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ImageProcessingAtom
|
||||
const static AZ::u32 EIF_Greyscale = 0x8; // hint for the engine (e.g. greyscale light beams can be applied to shadow mask), can be for DXT1 because compression artfacts don't count as color
|
||||
const static AZ::u32 EIF_SupressEngineReduce = 0x10; // info for the engine: don't reduce texture resolution on this texture
|
||||
const static AZ::u32 EIF_UNUSED_BIT = 0x40; // Free to use
|
||||
const static AZ::u32 EIF_AttachedAlpha = 0x400; // info for the engine: it's a texture with attached alpha channel
|
||||
const static AZ::u32 EIF_AttachedAlpha = 0x400; // deprecated: info for the engine: it's a texture with attached alpha channel
|
||||
const static AZ::u32 EIF_SRGBRead = 0x800; // info for the engine: if gamma corrected rendering is on, this texture requires SRGBRead (it's not stored in linear)
|
||||
const static AZ::u32 EIF_DontResize = 0x8000; // info for the engine: for dds textures that shouldn't be resized
|
||||
const static AZ::u32 EIF_RenormalizedTexture = 0x10000; // info for the engine: for dds textures that have renormalized color range
|
||||
|
||||
@@ -316,130 +316,6 @@ namespace ImageProcessingAtom
|
||||
m_mips.clear();
|
||||
}
|
||||
|
||||
//note: there are some unreasonable parts of the save files formats for cry textures. We might need to rethink about
|
||||
// it for new renderer
|
||||
bool CImageObject::SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector<AZStd::string>& outFilePaths) const
|
||||
{
|
||||
AZ::IO::SystemFile file;
|
||||
file.Open(filename, AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
|
||||
|
||||
AZ::IO::SystemFileStream fileSaveStream(&file, true);
|
||||
if (!fileSaveStream.IsOpen())
|
||||
{
|
||||
AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (alphaImage)
|
||||
{
|
||||
AZ_Assert(HasImageFlags(EIF_AttachedAlpha), "attached alpha image flag wasn't set");
|
||||
AZ_Assert(!alphaImage->HasImageFlags(EIF_AttachedAlpha), "alpha image shouldn't have attached alpha image flag");
|
||||
|
||||
// inherit cubemap and decal image flags to attached alpha image
|
||||
alphaImage->AddImageFlags(GetImageFlags() & (EIF_Cubemap
|
||||
| EIF_Decal | EIF_Splitted));
|
||||
alphaImage->SetNumPersistentMips(m_numPersistentMips);
|
||||
}
|
||||
|
||||
bool bOk = SaveImage(fileSaveStream);
|
||||
bool hasSplitFlag = HasImageFlags(EIF_Splitted);
|
||||
|
||||
//append alpha image data in the end if there is no split
|
||||
if (bOk && alphaImage && !hasSplitFlag)
|
||||
{
|
||||
//4 bytes extension tag, 4 bytes attached alpha tag, then 4 bytes of chunk size
|
||||
fileSaveStream.Write(sizeof(FOURCC_CExt), &FOURCC_CExt); // marker for the start of O3DE Extended data
|
||||
fileSaveStream.Write(sizeof(FOURCC_AttC), &FOURCC_AttC); // Attached Channel chunk
|
||||
|
||||
uint32_t size = 0;
|
||||
uint32_t sizeBytes = sizeof(size);
|
||||
fileSaveStream.Write(sizeBytes, &size); //size of attached chunk
|
||||
|
||||
//save alpha image and get the size
|
||||
AZ::IO::SizeType startPos = fileSaveStream.GetCurPos();
|
||||
bOk = alphaImage->SaveImage(fileSaveStream);
|
||||
AZ::IO::SizeType endPos = fileSaveStream.GetCurPos();
|
||||
size = static_cast<uint32_t>(endPos - startPos);
|
||||
|
||||
//move back to beginning of chunk and write chunk size then move back to end
|
||||
fileSaveStream.Seek(startPos - sizeBytes, AZ::IO::GenericStream::ST_SEEK_BEGIN);
|
||||
fileSaveStream.Write(sizeBytes, &size);
|
||||
fileSaveStream.Seek(endPos, AZ::IO::GenericStream::ST_SEEK_BEGIN);
|
||||
|
||||
// marker for the end of O3DE Extended data
|
||||
fileSaveStream.Write(sizeof(FOURCC_CEnd), &FOURCC_CEnd);
|
||||
}
|
||||
|
||||
if (!bOk)
|
||||
{
|
||||
AZ::IO::SystemFile::Delete(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's important to maintain the product output sequence. Asset Database/Browser will use the first product to determine the source type!
|
||||
outFilePaths.push_back(filename);
|
||||
|
||||
// save stand alone products
|
||||
if (hasSplitFlag)
|
||||
{
|
||||
// alpha
|
||||
if (alphaImage)
|
||||
{
|
||||
AZStd::string alphaFile = AZStd::string::format("%s.a", filename);
|
||||
|
||||
AZ::IO::SystemFile outAlphaFile;
|
||||
outAlphaFile.Open(alphaFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
|
||||
|
||||
AZ::IO::SystemFileStream alphaFileSaveStream(&outAlphaFile, true);
|
||||
|
||||
if (alphaFileSaveStream.IsOpen())
|
||||
{
|
||||
alphaImage->SaveImage(alphaFileSaveStream);
|
||||
outFilePaths.push_back(alphaFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, alphaFile.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// mips
|
||||
AZ::u32 numStreamable = GetMipCount() - m_numPersistentMips;
|
||||
for (AZ::u32 mip = 0; mip < numStreamable; mip++)
|
||||
{
|
||||
AZ::u32 nameIdx = numStreamable - mip;
|
||||
AZStd::string mipFileName = AZStd::string::format("%s.%d", filename, nameIdx);
|
||||
SaveMipToFile(mip, mipFileName);
|
||||
outFilePaths.push_back(mipFileName);
|
||||
if (alphaImage)
|
||||
{
|
||||
AZStd::string mipAlphaFileName = mipFileName + "a";
|
||||
alphaImage->SaveMipToFile(mip, mipAlphaFileName);
|
||||
outFilePaths.push_back(mipAlphaFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bOk;
|
||||
}
|
||||
|
||||
bool CImageObject::SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const
|
||||
{
|
||||
AZ::IO::SystemFile saveFile;
|
||||
saveFile.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
|
||||
|
||||
AZ::IO::SystemFileStream saveFileStream(&saveFile, true);
|
||||
|
||||
if (!saveFileStream.IsOpen())
|
||||
{
|
||||
AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
saveFileStream.Write(GetMipBufSize(mip), m_mips[mip]->m_pData);
|
||||
return true;
|
||||
}
|
||||
|
||||
float CImageObject::CalculateAverageBrightness() const
|
||||
{
|
||||
//if it's compressed format, return a default value
|
||||
@@ -642,63 +518,6 @@ namespace ImageProcessingAtom
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CImageObject::SaveImage(AZ::IO::SystemFileStream& saveFileStream) const
|
||||
{
|
||||
DDS_FILE_DESC_LEGACY desc;
|
||||
DDS_HEADER_DXT10 exthead;
|
||||
|
||||
desc.dwMagic = FOURCC_DDS;
|
||||
|
||||
if (!BuildSurfaceHeader(desc.header))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (desc.header.IsDX10Ext() && !BuildSurfaceExtendedHeader(exthead))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
saveFileStream.Write(sizeof(desc), &desc);
|
||||
|
||||
if (desc.header.IsDX10Ext())
|
||||
{
|
||||
saveFileStream.Write(sizeof(exthead), &exthead);
|
||||
}
|
||||
|
||||
AZ::u32 faces = 1;
|
||||
|
||||
//for cubemap. export each face and its mipmap
|
||||
if (HasImageFlags(EIF_Cubemap))
|
||||
{
|
||||
faces = 6;
|
||||
}
|
||||
|
||||
AZ::u32 mipStart = 0;
|
||||
if (HasImageFlags(EIF_Splitted))
|
||||
{
|
||||
if (m_numPersistentMips < m_mips.size())
|
||||
{
|
||||
mipStart = (AZ::u32)m_mips.size() - m_numPersistentMips;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "numPersistentMips wasn't setup correctly");
|
||||
}
|
||||
}
|
||||
|
||||
for (AZ::u32 face = 0; face < faces; face++)
|
||||
{
|
||||
for (AZ::u32 mip = mipStart; mip < m_mips.size(); ++mip)
|
||||
{
|
||||
const MipLevel& level = *m_mips[mip];
|
||||
AZ::u32 faceBufSize = level.m_pitch * level.m_rowCount / faces;
|
||||
saveFileStream.Write(faceBufSize, level.m_pData + faceBufSize * face);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CImageObject::GetExtent(AZ::u32& width, AZ::u32& height, AZ::u32& mipCount) const
|
||||
{
|
||||
mipCount = (AZ::u32)m_mips.size();
|
||||
@@ -953,35 +772,4 @@ namespace ImageProcessingAtom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CImageObject::ConvertLegacyGloss()
|
||||
{
|
||||
if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat)))
|
||||
{
|
||||
AZ_Assert(false, "%s function only works with uncompressed pixel format", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
//create pixel operation function
|
||||
IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
|
||||
|
||||
//get count of bytes per pixel
|
||||
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
|
||||
|
||||
const AZ::u32 mips = (AZ::u32)m_mips.size();
|
||||
float color[4];
|
||||
for (AZ::u32 mip = 0; mip < mips; ++mip)
|
||||
{
|
||||
AZ::u8* pixelBuf = m_mips[mip]->m_pData;
|
||||
const AZ::u32 pixelCount = GetPixelCount(mip);
|
||||
|
||||
for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
|
||||
{
|
||||
pixelOp->GetRGBA(pixelBuf, color[0], color[1], color[2], color[3]);
|
||||
// Convert from (1 - s * 0.7)^6 to (1 - s)^2
|
||||
color[3] = 1 - pow(1.0f - color[3] * 0.7f, 3.0f);
|
||||
pixelOp->SetRGBA(pixelBuf, color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ImageProcessingAtom
|
||||
|
||||
@@ -57,10 +57,6 @@ namespace ImageProcessingAtom
|
||||
|
||||
bool CompareImage(const IImageObjectPtr otherImage) const override;
|
||||
|
||||
bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector<AZStd::string>& outFilePaths) const override;
|
||||
bool SaveImage(AZ::IO::SystemFileStream& out) const override;
|
||||
bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const override;
|
||||
|
||||
uint32_t GetTextureMemory() const override;
|
||||
|
||||
EAlphaContent GetAlphaContent() const override;
|
||||
@@ -79,7 +75,6 @@ namespace ImageProcessingAtom
|
||||
void SetNumPersistentMips(AZ::u32 nMips) override;
|
||||
|
||||
void GlossFromNormals(bool hasAuthoredGloss) override;
|
||||
void ConvertLegacyGloss() override;
|
||||
void ClearColor(float r, float g, float b, float a) override;
|
||||
//end virtual functions from IImageObject
|
||||
|
||||
|
||||
@@ -66,13 +66,6 @@ namespace ImageProcessingAtom
|
||||
bool GammaToLinearRGBA32F(bool bDeGamma);
|
||||
void LinearToGamma();
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// Tools for A32B32G32R32F
|
||||
|
||||
void CreateHighPass(uint32 dwMipDown);
|
||||
|
||||
void CreateColorChart();
|
||||
|
||||
//convert various original cubemap layouts to new layout
|
||||
bool ConvertCubemapLayout(CubemapLayoutType newLayout);
|
||||
};
|
||||
|
||||
@@ -988,7 +988,6 @@ namespace UnitTest
|
||||
ASSERT_TRUE(process->IsSucceed());
|
||||
|
||||
SaveImageToFile(process->GetOutputImage(), "rgb", 10);
|
||||
SaveImageToFile(process->GetOutputAlphaImage(), "alpha", 10);
|
||||
|
||||
process->GetAppendOutputProducts(outProducts);
|
||||
|
||||
|
||||
@@ -103,8 +103,6 @@ set(FILES
|
||||
Source/Converters/ConvertPixelFormat.cpp
|
||||
Source/Converters/Cubemap.h
|
||||
Source/Converters/Cubemap.cpp
|
||||
Source/Converters/ColorChart.cpp
|
||||
Source/Converters/HighPass.cpp
|
||||
Source/Converters/Histogram.cpp
|
||||
Source/Converters/Histogram.h
|
||||
../External/CubeMapGen/CBBoxInt32.cpp
|
||||
|
||||
@@ -800,7 +800,8 @@ namespace AZ::AtomBridge
|
||||
const float startAngle = DegToRad(startAngleDegrees);
|
||||
const float stopAngle = DegToRad(sweepAngleDegrees) + startAngle;
|
||||
SingleColorDynamicSizeLineHelper lines(1+static_cast<int>(sweepAngleDegrees/angularStepDegrees));
|
||||
AZ::Vector3 radiusV3 = AZ::Vector3(radius);
|
||||
float aspectRadius = radius / GetAspectRatio();
|
||||
AZ::Vector3 radiusV3 = AZ::Vector3(aspectRadius, radius, radius);
|
||||
AZ::Vector3 pos = AZ::Vector3(center.GetX(), center.GetY(), z);
|
||||
CreateAxisAlignedArc(
|
||||
lines,
|
||||
|
||||
@@ -218,9 +218,10 @@ float4x4 GetObject_WorldMatrix()
|
||||
|
||||
float GetHeight(float2 origUv)
|
||||
{
|
||||
float2 uv = clamp(origUv + (ObjectSrg::m_terrainData.m_uvStep * 0.5f), 0.0f, 1.0f);
|
||||
float height = 0.0f;
|
||||
float2 halfStep = ObjectSrg::m_terrainData.m_uvStep * 0.5;
|
||||
float2 uv = origUv * (1.0 - ObjectSrg::m_terrainData.m_uvStep) + halfStep;
|
||||
|
||||
float height = 0.0f;
|
||||
if (o_useTerrainSmoothing)
|
||||
{
|
||||
float2 textureSize;
|
||||
|
||||
@@ -164,14 +164,17 @@ namespace Terrain
|
||||
// could just make this list a prioritized list from top to bottom for any points that overlap.
|
||||
for (auto& gradientId : m_configuration.m_gradientEntities)
|
||||
{
|
||||
// If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain
|
||||
// to *not* exist at a specific point.
|
||||
terrainExists = true;
|
||||
if (gradientId.IsValid())
|
||||
{
|
||||
// If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain
|
||||
// to *not* exist at a specific point.
|
||||
terrainExists = true;
|
||||
|
||||
float sample = 0.0f;
|
||||
GradientSignal::GradientRequestBus::EventResult(
|
||||
sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params);
|
||||
maxSample = AZ::GetMax(maxSample, sample);
|
||||
float sample = 0.0f;
|
||||
GradientSignal::GradientRequestBus::EventResult(
|
||||
sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params);
|
||||
maxSample = AZ::GetMax(maxSample, sample);
|
||||
}
|
||||
}
|
||||
m_isRequestInProgress = false;
|
||||
}
|
||||
|
||||
@@ -218,6 +218,12 @@ namespace Terrain
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
|
||||
|
||||
// Take the dirty region and adjust the Z values to the world min/max so that even if the dirty region falls outside the current
|
||||
// world bounds, we still update the wireframe accordingly.
|
||||
AZ::Aabb dirtyRegion2D = AZ::Aabb::CreateFromMinMaxValues(
|
||||
dirtyRegion.GetMin().GetX(), dirtyRegion.GetMin().GetY(), worldBounds.GetMin().GetZ(),
|
||||
dirtyRegion.GetMax().GetX(), dirtyRegion.GetMax().GetY(), worldBounds.GetMax().GetZ());
|
||||
|
||||
// Calculate the world size of each sector. Note that this size actually ends at the last point, not the last square.
|
||||
// So for example, the sector size for 3 points will go from (*--*--*) even though it will be used to draw (*--*--*--).
|
||||
const float xSectorSize = (queryResolution.GetX() * SectorSizeInGridPoints);
|
||||
@@ -230,7 +236,7 @@ namespace Terrain
|
||||
|
||||
// If we haven't cached anything before, or if the world bounds has changed, clear our cache structure and repopulate it
|
||||
// with WireframeSector entries with the proper AABB sizes.
|
||||
if (!m_wireframeBounds.IsValid() || !dirtyRegion.IsValid() || !m_wireframeBounds.IsClose(worldBounds))
|
||||
if (!m_wireframeBounds.IsValid() || !dirtyRegion2D.IsValid() || !m_wireframeBounds.IsClose(worldBounds))
|
||||
{
|
||||
m_wireframeBounds = worldBounds;
|
||||
|
||||
@@ -266,7 +272,7 @@ namespace Terrain
|
||||
// For each sector, if it overlaps with the dirty region, clear it out and recache the wireframe line data.
|
||||
for (auto& sector : m_wireframeSectors)
|
||||
{
|
||||
if (dirtyRegion.IsValid() && !dirtyRegion.Overlaps(sector.m_aabb))
|
||||
if (dirtyRegion2D.IsValid() && !dirtyRegion2D.Overlaps(sector.m_aabb))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Terrain
|
||||
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_4096Meters, "4 Kilometers")
|
||||
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_8192Meters, "8 Kilometers")
|
||||
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_16384Meters, "16 Kilometers")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, false) // Keeping invisible until it's hooked up under the hood
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,10 +417,10 @@ namespace Terrain
|
||||
m_areaData.m_rebuildSectors = false;
|
||||
|
||||
m_sectorData.clear();
|
||||
const float xFirstPatchStart = terrainBounds.GetMin().GetX() - fmod(terrainBounds.GetMin().GetX(), GridMeters);
|
||||
const float xLastPatchStart = terrainBounds.GetMax().GetX() - fmod(terrainBounds.GetMax().GetX(), GridMeters);
|
||||
const float yFirstPatchStart = terrainBounds.GetMin().GetY() - fmod(terrainBounds.GetMin().GetY(), GridMeters);
|
||||
const float yLastPatchStart = terrainBounds.GetMax().GetY() - fmod(terrainBounds.GetMax().GetY(), GridMeters);
|
||||
const float xFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetX() / GridMeters) * GridMeters;
|
||||
const float xLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetX() / GridMeters) * GridMeters;
|
||||
const float yFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetY() / GridMeters) * GridMeters;
|
||||
const float yLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetY() / GridMeters) * GridMeters;
|
||||
|
||||
const auto& materialAsset = m_materialInstance->GetAsset();
|
||||
const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg();
|
||||
@@ -603,7 +603,7 @@ namespace Terrain
|
||||
|
||||
// For every distance doubling beyond a minDistanceForLod0, we only need half the mesh density. Each LOD
|
||||
// is exactly half the resolution of the last.
|
||||
const float lodForCamera = floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0)));
|
||||
const float lodForCamera = AZStd::floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0)));
|
||||
|
||||
// All cameras should render the same LOD so effects like shadows are consistent.
|
||||
lodChoice = AZ::GetMin(lodChoice, aznumeric_cast<uint8_t>(lodForCamera));
|
||||
|
||||
@@ -222,20 +222,31 @@ float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, boo
|
||||
|
||||
float TerrainSystem::GetTerrainAreaHeight(float x, float y, bool& terrainExists) const
|
||||
{
|
||||
AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ());
|
||||
float height = m_currentSettings.m_worldBounds.GetMin().GetZ();
|
||||
const float worldMin = m_currentSettings.m_worldBounds.GetMin().GetZ();
|
||||
AZ::Vector3 inPosition(x, y, worldMin);
|
||||
float height = worldMin;
|
||||
terrainExists = false;
|
||||
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
|
||||
for (auto& [areaId, areaBounds] : m_registeredAreas)
|
||||
for (auto& [areaId, areaData] : m_registeredAreas)
|
||||
{
|
||||
inPosition.SetZ(areaBounds.GetMin().GetZ());
|
||||
if (areaBounds.Contains(inPosition))
|
||||
const float areaMin = areaData.m_areaBounds.GetMin().GetZ();
|
||||
inPosition.SetZ(areaMin);
|
||||
if (areaData.m_areaBounds.Contains(inPosition))
|
||||
{
|
||||
AZ::Vector3 outPosition;
|
||||
Terrain::TerrainAreaHeightRequestBus::Event(
|
||||
areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists);
|
||||
height = outPosition.GetZ();
|
||||
if (!terrainExists)
|
||||
{
|
||||
// If the terrain height provider doesn't have any data, then check the area's "use ground plane" setting.
|
||||
// If it's set, then create a default ground plane by saying terrain exists at the minimum height for the area.
|
||||
// Otherwise, we'll set the height at the terrain world minimum and say it doesn't exist.
|
||||
terrainExists = areaData.m_useGroundPlane;
|
||||
height = areaData.m_useGroundPlane ? areaMin : worldMin;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -395,12 +406,12 @@ AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::A
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
|
||||
// The areas are sorted into priority order: the first area that contains inPosition is the most suitable.
|
||||
for (const auto& [areaId, areaBounds] : m_registeredAreas)
|
||||
for (const auto& [areaId, areaData] : m_registeredAreas)
|
||||
{
|
||||
inPosition.SetZ(areaBounds.GetMin().GetZ());
|
||||
if (areaBounds.Contains(inPosition))
|
||||
inPosition.SetZ(areaData.m_areaBounds.GetMin().GetZ());
|
||||
if (areaData.m_areaBounds.Contains(inPosition))
|
||||
{
|
||||
bounds = areaBounds;
|
||||
bounds = areaData.m_areaBounds;
|
||||
return areaId;
|
||||
}
|
||||
}
|
||||
@@ -548,7 +559,12 @@ void TerrainSystem::RegisterArea(AZ::EntityId areaId)
|
||||
AZStd::unique_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
AZ::Aabb aabb = AZ::Aabb::CreateNull();
|
||||
LmbrCentral::ShapeComponentRequestsBus::EventResult(aabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
|
||||
m_registeredAreas[areaId] = aabb;
|
||||
|
||||
// Cache off whether or not this layer spawner should have a default ground plane when no other terrain height data exists.
|
||||
bool useGroundPlane = false;
|
||||
Terrain::TerrainSpawnerRequestBus::EventResult(useGroundPlane, areaId, &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane);
|
||||
|
||||
m_registeredAreas[areaId] = { aabb, useGroundPlane };
|
||||
m_dirtyRegion.AddAabb(aabb);
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
@@ -565,10 +581,10 @@ void TerrainSystem::UnregisterArea(AZ::EntityId areaId)
|
||||
m_registeredAreas,
|
||||
[areaId, this](const auto& item)
|
||||
{
|
||||
auto const& [entityId, aabb] = item;
|
||||
auto const& [entityId, areaData] = item;
|
||||
if (areaId == entityId)
|
||||
{
|
||||
m_dirtyRegion.AddAabb(aabb);
|
||||
m_dirtyRegion.AddAabb(areaData.m_areaBounds);
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
return true;
|
||||
@@ -585,10 +601,10 @@ void TerrainSystem::RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::Terra
|
||||
|
||||
auto areaAabb = m_registeredAreas.find(areaId);
|
||||
|
||||
AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second : AZ::Aabb::CreateNull();
|
||||
AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second.m_areaBounds : AZ::Aabb::CreateNull();
|
||||
AZ::Aabb newAabb = AZ::Aabb::CreateNull();
|
||||
LmbrCentral::ShapeComponentRequestsBus::EventResult(newAabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
|
||||
m_registeredAreas[areaId] = newAabb;
|
||||
m_registeredAreas[areaId].m_areaBounds = newAabb;
|
||||
|
||||
AZ::Aabb expandedAabb = oldAabb;
|
||||
expandedAabb.AddAabb(newAabb);
|
||||
|
||||
@@ -168,7 +168,14 @@ namespace Terrain
|
||||
bool m_terrainSurfacesDirty = false;
|
||||
AZ::Aabb m_dirtyRegion;
|
||||
|
||||
// Cached data for each terrain area to use when looking up terrain data.
|
||||
struct TerrainAreaData
|
||||
{
|
||||
AZ::Aabb m_areaBounds{ AZ::Aabb::CreateNull() };
|
||||
bool m_useGroundPlane{ false };
|
||||
};
|
||||
|
||||
mutable AZStd::shared_mutex m_areaMutex;
|
||||
AZStd::map<AZ::EntityId, AZ::Aabb, TerrainLayerPriorityComparator> m_registeredAreas;
|
||||
AZStd::map<AZ::EntityId, TerrainAreaData, TerrainLayerPriorityComparator> m_registeredAreas;
|
||||
};
|
||||
} // namespace Terrain
|
||||
|
||||
Reference in New Issue
Block a user