/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
Code/Engine/RendererCore/Textures/TextureLoader.cpp
290 строк
9 KB
Jan Krassnigg
Use UV clamping for gradient textures
28 ноя 2025, 21:07
28 ноя 2025, 21:07
18db25d
Код
Авторство
О чём код?
#include <RendererCore/RendererCorePCH.h> #include <Core/Curves/ColorGradientResource.h> #include <Foundation/Configuration/CVar.h> #include <Foundation/Configuration/Startup.h> #include <Foundation/IO/FileSystem/FileReader.h> #include <Foundation/IO/OSFile.h> #include <Foundation/Math/Float16.h> #include <Foundation/Utilities/AssetFileHeader.h> #include <RendererCore/Textures/Texture2DResource.h> #include <RendererCore/Textures/Texture3DResource.h> #include <RendererCore/Textures/TextureCubeResource.h> #include <RendererCore/Textures/TextureLoader.h> #include <RendererCore/Textures/TextureUtils.h> #include <Texture/Image/Formats/DdsFileFormat.h> #include <Texture/Image/ImageConversion.h> #include <Texture/ezTexFormat/ezTexFormat.h> static ezTextureResourceLoader s_TextureResourceLoader; ezCVarFloat cvar_StreamingTextureLoadDelay("Streaming.TextureLoadDelay", 0.0f, ezCVarFlags::Save, "Artificial texture loading slowdown"); // clang-format off EZ_BEGIN_SUBSYSTEM_DECLARATION(RendererCore, TextureResource) BEGIN_SUBSYSTEM_DEPENDENCIES "Foundation", "Core" END_SUBSYSTEM_DEPENDENCIES ON_CORESYSTEMS_STARTUP { ezResourceManager::SetResourceTypeLoader<ezTexture2DResource>(&s_TextureResourceLoader); ezResourceManager::SetResourceTypeLoader<ezTexture3DResource>(&s_TextureResourceLoader); ezResourceManager::SetResourceTypeLoader<ezTextureCubeResource>(&s_TextureResourceLoader); ezResourceManager::SetResourceTypeLoader<ezRenderToTexture2DResource>(&s_TextureResourceLoader); } ON_CORESYSTEMS_SHUTDOWN { ezResourceManager::SetResourceTypeLoader<ezTexture2DResource>(nullptr); ezResourceManager::SetResourceTypeLoader<ezTexture3DResource>(nullptr); ezResourceManager::SetResourceTypeLoader<ezTextureCubeResource>(nullptr); ezResourceManager::SetResourceTypeLoader<ezRenderToTexture2DResource>(nullptr); } ON_HIGHLEVELSYSTEMS_STARTUP { } ON_HIGHLEVELSYSTEMS_SHUTDOWN { } EZ_END_SUBSYSTEM_DECLARATION; // clang-format on ezResourceLoadData ezTextureResourceLoader::OpenDataStream(const ezResource* pResource) { LoadedData* pData = EZ_DEFAULT_NEW(LoadedData); ezResourceLoadData res; ezStringView sResourceID = pResource->GetResourceID(); // Solid Color Textures if (sResourceID.HasExtension("color") || sResourceID.StartsWith("#") || (!sResourceID.HasAnyExtension() && !ezConversionUtils::IsStringUuid(sResourceID))) { ezStringBuilder sName = pResource->GetResourceID(); sName.RemoveFileExtension(); bool bValidColor = false; const ezColorGammaUB color = ezConversionUtils::GetColorByName(sName, &bValidColor); if (!bValidColor) { ezLog::Error("'{0}' is not a valid color name. Using 'RebeccaPurple' as fallback.", sName); } pData->m_TexFormat.m_bSRGB = true; ezImageHeader header; header.SetWidth(4); header.SetHeight(4); header.SetDepth(1); header.SetImageFormat(ezImageFormat::R8G8B8A8_UNORM_SRGB); header.SetNumMipLevels(1); header.SetNumFaces(1); pData->m_Image.ResetAndAlloc(header); ezUInt8* pPixels = pData->m_Image.GetPixelPointer<ezUInt8>(); for (ezUInt32 px = 0; px < 4 * 4 * 4; px += 4) { pPixels[px + 0] = color.r; pPixels[px + 1] = color.g; pPixels[px + 2] = color.b; pPixels[px + 3] = color.a; } } else { ezFileReader File; if (File.Open(pResource->GetResourceID()).Failed()) return res; if (File.GetFilePathAbsolute().HasExtension("ezBinColorGradient")) { res.m_sResourceDescription = File.GetFilePathRelative().GetView(); #if EZ_ENABLED(EZ_SUPPORTS_FILE_STATS) { ezFileStats stat; if (ezFileSystem::GetFileStats(pResource->GetResourceID(), stat).Succeeded()) { res.m_LoadedFileModificationDate = stat.m_LastModificationTime; } } #endif // skip the asset file header at the start of the file ezAssetFileHeader AssetHash; if (AssetHash.Read(File).Failed()) return res; ezColorGradientResourceDescriptor desc; desc.Load(File); constexpr ezUInt32 uiWidth = 512; constexpr ezUInt32 uiHeight = 1; ezImageHeader header; header.SetWidth(uiWidth); header.SetHeight(uiHeight); header.SetDepth(1); header.SetImageFormat(ezImageFormat::R16G16B16A16_FLOAT); header.SetNumMipLevels(1); header.SetNumFaces(1); pData->m_TexFormat.m_AddressModeU = ezImageAddressMode::Clamp; pData->m_TexFormat.m_AddressModeV = ezImageAddressMode::Clamp; pData->m_TexFormat.m_TextureFilter = ezTextureFilterSetting::FixedBilinear; pData->m_Image.ResetAndAlloc(header); ezFloat16* pPixels = pData->m_Image.GetPixelPointer<ezFloat16>(); // fill each pixel with the color gradient from left to right, duplicate along the vertical pixels for (ezUInt32 x = 0; x < uiWidth; ++x) { const double fGradientPos = (double)x / (double)(uiWidth - 1); // normalize to [0, 1] ezColor color; desc.m_Gradient.Evaluate(fGradientPos, color); // duplicate along the vertical pixels for (ezUInt32 y = 0; y < uiHeight; ++y) { const ezUInt32 pixelIndex = (y * 1024 + x) * 4; pPixels[pixelIndex + 0] = color.r; pPixels[pixelIndex + 1] = color.g; pPixels[pixelIndex + 2] = color.b; pPixels[pixelIndex + 3] = color.a; } } } else { const ezStringBuilder sAbsolutePath = File.GetFilePathAbsolute(); res.m_sResourceDescription = File.GetFilePathRelative().GetView(); #if EZ_ENABLED(EZ_SUPPORTS_FILE_STATS) { ezFileStats stat; if (ezFileSystem::GetFileStats(pResource->GetResourceID(), stat).Succeeded()) { res.m_LoadedFileModificationDate = stat.m_LastModificationTime; } } #endif /// In case this is not a proper asset (ezTextureXX format), this is a hack to get the SRGB information for the texture const ezStringBuilder sName = ezPathUtils::GetFileName(sAbsolutePath); pData->m_TexFormat.m_bSRGB = (sName.EndsWith_NoCase("_D") || sName.EndsWith_NoCase("_SRGB") || sName.EndsWith_NoCase("_diff")); if (sAbsolutePath.HasExtension("ezBinTexture2D") || sAbsolutePath.HasExtension("ezBinTexture3D") || sAbsolutePath.HasExtension("ezBinTextureCube") || sAbsolutePath.HasExtension("ezBinRenderTarget") || sAbsolutePath.HasExtension("ezBinLUT")) { if (LoadTexFile(File, *pData).Failed()) return res; } else { // read whatever format, as long as ezImage supports it File.Close(); if (pData->m_Image.LoadFrom(pResource->GetResourceID()).Failed()) return res; if (pData->m_Image.GetImageFormat() == ezImageFormat::B8G8R8_UNORM) { /// \todo A conversion to B8G8R8X8_UNORM currently fails ezLog::Warning("Texture resource uses inefficient BGR format, converting to BGRX: '{0}'", sAbsolutePath); if (ezImageConversion::Convert(pData->m_Image, pData->m_Image, ezImageFormat::B8G8R8A8_UNORM).Failed()) return res; } } } } ezMemoryStreamWriter w(&pData->m_Storage); WriteTextureLoadStream(w, *pData); res.m_pDataStream = &pData->m_Reader; res.m_pCustomLoaderData = pData; if (cvar_StreamingTextureLoadDelay > 0) { ezThreadUtils::Sleep(ezTime::MakeFromSeconds(cvar_StreamingTextureLoadDelay)); } return res; } void ezTextureResourceLoader::CloseDataStream(const ezResource* pResource, const ezResourceLoadData& loaderData) { LoadedData* pData = (LoadedData*)loaderData.m_pCustomLoaderData; EZ_DEFAULT_DELETE(pData); } bool ezTextureResourceLoader::IsResourceOutdated(const ezResource* pResource) const { // solid color textures are never outdated if (ezPathUtils::HasExtension(pResource->GetResourceID(), "color")) return false; ezStringView sResourceID = pResource->GetResourceID(); // procedurally generated textures (color names, hex codes) are never outdated if (sResourceID.StartsWith("#") || (!sResourceID.HasAnyExtension() && !ezConversionUtils::IsStringUuid(sResourceID))) return false; // don't try to reload a file that cannot be found ezStringBuilder sAbs; if (ezFileSystem::ResolvePath(pResource->GetResourceID(), &sAbs, nullptr).Failed()) return false; #if EZ_ENABLED(EZ_SUPPORTS_FILE_STATS) if (pResource->GetLoadedFileModificationTime().IsValid()) { ezFileStats stat; if (ezFileSystem::GetFileStats(pResource->GetResourceID(), stat).Failed()) return false; return !stat.m_LastModificationTime.Compare(pResource->GetLoadedFileModificationTime(), ezTimestamp::CompareMode::FileTimeEqual); } #endif return true; } ezResult ezTextureResourceLoader::LoadTexFile(ezStreamReader& inout_stream, LoadedData& ref_data) { // read the hash, ignore it ezAssetFileHeader AssetHash; EZ_SUCCEED_OR_RETURN(AssetHash.Read(inout_stream)); ref_data.m_TexFormat.ReadHeader(inout_stream); if (ref_data.m_TexFormat.m_iRenderTargetResolutionX == 0) { ezDdsFileFormat fmt; return fmt.ReadImage(inout_stream, ref_data.m_Image, "dds"); } else { return EZ_SUCCESS; } } void ezTextureResourceLoader::WriteTextureLoadStream(ezStreamWriter& w, const LoadedData& data) { const ezImage* pImage = &data.m_Image; w.WriteBytes(&pImage, sizeof(ezImage*)).IgnoreResult(); w << data.m_bIsFallback; data.m_TexFormat.WriteRenderTargetHeader(w); } EZ_STATICLINK_FILE(RendererCore, RendererCore_Textures_TextureLoader);