548 lines
20 KiB
C++
548 lines
20 KiB
C++
// Copyright 2020-2024 CesiumGS, Inc. and Contributors
|
|
|
|
#include "CesiumTextureUtility.h"
|
|
#include "CesiumAsync/AsyncSystem.h"
|
|
#include "ExtensionImageAssetUnreal.h"
|
|
#include "Misc/AutomationTest.h"
|
|
#include "RenderingThread.h"
|
|
#include <CesiumGltfReader/GltfReader.h>
|
|
#include <UnrealTaskProcessor.h>
|
|
#include <memory>
|
|
|
|
using namespace CesiumTextureUtility;
|
|
using namespace CesiumUtility;
|
|
|
|
BEGIN_DEFINE_SPEC(
|
|
CesiumTextureUtilitySpec,
|
|
"Cesium.Unit.CesiumTextureUtility",
|
|
EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext |
|
|
EAutomationTestFlags::ProductFilter | EAutomationTestFlags::NonNullRHI)
|
|
std::vector<uint8_t> originalPixels;
|
|
std::vector<uint8_t> originalMipPixels;
|
|
std::vector<uint8_t> expectedMipPixelsIfGenerated;
|
|
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> pImageAsset;
|
|
|
|
void RunTests();
|
|
|
|
void CheckPixels(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
bool requireMips = false);
|
|
void CheckSRGB(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
bool expectedSRGB);
|
|
void CheckAddress(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
TextureAddress expectedAddressX,
|
|
TextureAddress expectedAddressY);
|
|
void CheckFilter(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
TextureFilter expectedFilter);
|
|
void CheckGroup(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
TextureGroup expectedGroup);
|
|
END_DEFINE_SPEC(CesiumTextureUtilitySpec)
|
|
|
|
void CesiumTextureUtilitySpec::Define() {
|
|
Describe("Without Mips", [this]() {
|
|
BeforeEach([this]() {
|
|
originalPixels = {0x20, 0x40, 0x80, 0xF0, 0x21, 0x41, 0x81, 0xF1,
|
|
0x22, 0x42, 0x82, 0xF2, 0x23, 0x43, 0x83, 0xF3,
|
|
0x24, 0x44, 0x84, 0xF4, 0x25, 0x45, 0x85, 0xF5};
|
|
originalMipPixels.clear();
|
|
|
|
pImageAsset.emplace();
|
|
pImageAsset->width = 3;
|
|
pImageAsset->height = 2;
|
|
TestEqual(
|
|
"image buffer size is correct",
|
|
originalPixels.size(),
|
|
pImageAsset->width * pImageAsset->height *
|
|
pImageAsset->bytesPerChannel * pImageAsset->channels);
|
|
pImageAsset->pixelData.resize(originalPixels.size());
|
|
|
|
std::memcpy(
|
|
pImageAsset->pixelData.data(),
|
|
originalPixels.data(),
|
|
originalPixels.size());
|
|
|
|
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> pCopy =
|
|
new CesiumGltf::ImageAsset(*pImageAsset);
|
|
CesiumGltfReader::ImageDecoder::generateMipMaps(*pCopy);
|
|
|
|
expectedMipPixelsIfGenerated.clear();
|
|
|
|
if (pCopy->mipPositions.size() >= 2) {
|
|
expectedMipPixelsIfGenerated.resize(pCopy->mipPositions[1].byteSize);
|
|
for (size_t iSrc = pCopy->mipPositions[1].byteOffset, iDest = 0;
|
|
iDest < pCopy->mipPositions[1].byteSize;
|
|
++iSrc, ++iDest) {
|
|
expectedMipPixelsIfGenerated[iDest] = uint8_t(pCopy->pixelData[iSrc]);
|
|
}
|
|
}
|
|
});
|
|
|
|
RunTests();
|
|
});
|
|
|
|
Describe("With Mips", [this]() {
|
|
BeforeEach([this]() {
|
|
pImageAsset.emplace();
|
|
pImageAsset->width = 3;
|
|
pImageAsset->height = 2;
|
|
|
|
// Original image (3x2)
|
|
originalPixels = {0x20, 0x40, 0x80, 0xF0, 0x21, 0x41, 0x81, 0xF1,
|
|
0x22, 0x42, 0x82, 0xF2, 0x23, 0x43, 0x83, 0xF3,
|
|
0x24, 0x44, 0x84, 0xF4, 0x25, 0x45, 0x85, 0xF5};
|
|
pImageAsset->mipPositions.emplace_back(
|
|
CesiumGltf::ImageAssetMipPosition{0, originalPixels.size()});
|
|
|
|
// Mip 1 (1x1)
|
|
originalMipPixels = {0x26, 0x46, 0x86, 0xF6};
|
|
pImageAsset->mipPositions.emplace_back(CesiumGltf::ImageAssetMipPosition{
|
|
pImageAsset->mipPositions[0].byteSize,
|
|
originalMipPixels.size()});
|
|
|
|
pImageAsset->pixelData.resize(
|
|
originalPixels.size() + originalMipPixels.size());
|
|
std::memcpy(
|
|
pImageAsset->pixelData.data(),
|
|
originalPixels.data(),
|
|
originalPixels.size());
|
|
std::memcpy(
|
|
pImageAsset->pixelData.data() + originalPixels.size(),
|
|
originalMipPixels.data(),
|
|
originalMipPixels.size());
|
|
});
|
|
|
|
RunTests();
|
|
});
|
|
}
|
|
|
|
void CesiumTextureUtilitySpec::RunTests() {
|
|
It("ImageAsset non-sRGB", [this]() {
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded = loadTextureAnyThreadPart(
|
|
*pImageAsset,
|
|
TextureAddress::TA_Mirror,
|
|
TextureAddress::TA_Wrap,
|
|
TextureFilter::TF_Bilinear,
|
|
true,
|
|
TextureGroup::TEXTUREGROUP_Cinematic,
|
|
false,
|
|
std::nullopt);
|
|
TestNotNull("pHalfLoaded", pHalfLoaded.Get());
|
|
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture =
|
|
loadTextureGameThreadPart(pHalfLoaded.Get());
|
|
CheckPixels(pRefCountedTexture, true);
|
|
CheckSRGB(pRefCountedTexture, false);
|
|
CheckAddress(
|
|
pRefCountedTexture,
|
|
TextureAddress::TA_Mirror,
|
|
TextureAddress::TA_Wrap);
|
|
CheckFilter(pRefCountedTexture, TextureFilter::TF_Bilinear);
|
|
CheckGroup(pRefCountedTexture, TextureGroup::TEXTUREGROUP_Cinematic);
|
|
});
|
|
|
|
It("ImageAsset sRGB", [this]() {
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded = loadTextureAnyThreadPart(
|
|
*pImageAsset,
|
|
TextureAddress::TA_Clamp,
|
|
TextureAddress::TA_Mirror,
|
|
TextureFilter::TF_Trilinear,
|
|
true,
|
|
TextureGroup::TEXTUREGROUP_Bokeh,
|
|
true,
|
|
std::nullopt);
|
|
TestNotNull("pHalfLoaded", pHalfLoaded.Get());
|
|
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture =
|
|
loadTextureGameThreadPart(pHalfLoaded.Get());
|
|
CheckPixels(pRefCountedTexture, true);
|
|
CheckSRGB(pRefCountedTexture, true);
|
|
CheckAddress(
|
|
pRefCountedTexture,
|
|
TextureAddress::TA_Clamp,
|
|
TextureAddress::TA_Mirror);
|
|
CheckFilter(pRefCountedTexture, TextureFilter::TF_Trilinear);
|
|
CheckGroup(pRefCountedTexture, TextureGroup::TEXTUREGROUP_Bokeh);
|
|
});
|
|
|
|
It("Image and Sampler", [this]() {
|
|
CesiumGltf::Sampler sampler;
|
|
sampler.minFilter = CesiumGltf::Sampler::MinFilter::NEAREST;
|
|
sampler.magFilter = CesiumGltf::Sampler::MagFilter::NEAREST;
|
|
sampler.wrapS = CesiumGltf::Sampler::WrapS::MIRRORED_REPEAT;
|
|
sampler.wrapT = CesiumGltf::Sampler::WrapT::CLAMP_TO_EDGE;
|
|
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded =
|
|
loadTextureFromImageAndSamplerAnyThreadPart(
|
|
*pImageAsset,
|
|
sampler,
|
|
false);
|
|
TestNotNull("pHalfLoaded", pHalfLoaded.Get());
|
|
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture =
|
|
loadTextureGameThreadPart(pHalfLoaded.Get());
|
|
CheckPixels(pRefCountedTexture, false);
|
|
CheckSRGB(pRefCountedTexture, false);
|
|
CheckAddress(
|
|
pRefCountedTexture,
|
|
TextureAddress::TA_Mirror,
|
|
TextureAddress::TA_Clamp);
|
|
CheckFilter(pRefCountedTexture, TextureFilter::TF_Nearest);
|
|
CheckGroup(pRefCountedTexture, TextureGroup::TEXTUREGROUP_World);
|
|
});
|
|
|
|
It("Model", [this]() {
|
|
CesiumGltf::Model model;
|
|
|
|
CesiumGltf::Image& image = model.images.emplace_back();
|
|
image.pAsset = pImageAsset;
|
|
|
|
CesiumGltf::Sampler& sampler = model.samplers.emplace_back();
|
|
sampler.minFilter = CesiumGltf::Sampler::MinFilter::LINEAR_MIPMAP_LINEAR;
|
|
sampler.magFilter = CesiumGltf::Sampler::MagFilter::LINEAR;
|
|
sampler.wrapS = CesiumGltf::Sampler::WrapS::REPEAT;
|
|
sampler.wrapT = CesiumGltf::Sampler::WrapT::MIRRORED_REPEAT;
|
|
|
|
CesiumGltf::Texture& texture = model.textures.emplace_back();
|
|
texture.source = 0;
|
|
texture.sampler = 0;
|
|
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded =
|
|
loadTextureFromModelAnyThreadPart(model, texture, true);
|
|
TestNotNull("pHalfLoaded", pHalfLoaded.Get());
|
|
TestNotNull("pHalfLoaded->pTexture", pHalfLoaded->pTexture.get());
|
|
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture =
|
|
loadTextureGameThreadPart(model, pHalfLoaded.Get());
|
|
CheckPixels(pRefCountedTexture, true);
|
|
CheckSRGB(pRefCountedTexture, true);
|
|
CheckAddress(
|
|
pRefCountedTexture,
|
|
TextureAddress::TA_Wrap,
|
|
TextureAddress::TA_Mirror);
|
|
CheckFilter(pRefCountedTexture, TextureFilter::TF_Default);
|
|
CheckGroup(pRefCountedTexture, TextureGroup::TEXTUREGROUP_World);
|
|
});
|
|
|
|
It("Two textures referencing one image", [this]() {
|
|
CesiumGltf::Model model;
|
|
|
|
CesiumGltf::Image& image = model.images.emplace_back();
|
|
image.pAsset = pImageAsset;
|
|
|
|
CesiumGltf::Sampler& sampler1 = model.samplers.emplace_back();
|
|
sampler1.minFilter = CesiumGltf::Sampler::MinFilter::LINEAR_MIPMAP_LINEAR;
|
|
sampler1.magFilter = CesiumGltf::Sampler::MagFilter::LINEAR;
|
|
sampler1.wrapS = CesiumGltf::Sampler::WrapS::REPEAT;
|
|
sampler1.wrapT = CesiumGltf::Sampler::WrapT::MIRRORED_REPEAT;
|
|
|
|
CesiumGltf::Texture& texture1 = model.textures.emplace_back();
|
|
texture1.source = 0;
|
|
texture1.sampler = 0;
|
|
|
|
CesiumGltf::Sampler& sampler2 = model.samplers.emplace_back();
|
|
sampler2.minFilter = CesiumGltf::Sampler::MinFilter::NEAREST;
|
|
sampler2.magFilter = CesiumGltf::Sampler::MagFilter::NEAREST;
|
|
sampler2.wrapS = CesiumGltf::Sampler::WrapS::MIRRORED_REPEAT;
|
|
sampler2.wrapT = CesiumGltf::Sampler::WrapT::REPEAT;
|
|
|
|
CesiumGltf::Texture& texture2 = model.textures.emplace_back();
|
|
texture2.source = 0;
|
|
texture2.sampler = 1;
|
|
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded1 =
|
|
loadTextureFromModelAnyThreadPart(model, model.textures[0], true);
|
|
TestNotNull("pHalfLoaded1", pHalfLoaded1.Get());
|
|
TestNotNull("pHalfLoaded1->pTexture", pHalfLoaded1->pTexture.get());
|
|
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded2 =
|
|
loadTextureFromModelAnyThreadPart(model, model.textures[1], false);
|
|
TestNotNull("pHalfLoaded2", pHalfLoaded2.Get());
|
|
TestNotNull("pHalfLoaded2->pTexture", pHalfLoaded2->pTexture.get());
|
|
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture1 =
|
|
loadTextureGameThreadPart(model, pHalfLoaded1.Get());
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture2 =
|
|
loadTextureGameThreadPart(model, pHalfLoaded2.Get());
|
|
|
|
CheckPixels(pRefCountedTexture1, true);
|
|
CheckSRGB(pRefCountedTexture1, true);
|
|
CheckAddress(
|
|
pRefCountedTexture1,
|
|
TextureAddress::TA_Wrap,
|
|
TextureAddress::TA_Mirror);
|
|
CheckFilter(pRefCountedTexture1, TextureFilter::TF_Default);
|
|
CheckGroup(pRefCountedTexture1, TextureGroup::TEXTUREGROUP_World);
|
|
|
|
CheckPixels(pRefCountedTexture2, false);
|
|
CheckSRGB(pRefCountedTexture2, false);
|
|
CheckAddress(
|
|
pRefCountedTexture2,
|
|
TextureAddress::TA_Mirror,
|
|
TextureAddress::TA_Wrap);
|
|
CheckFilter(pRefCountedTexture2, TextureFilter::TF_Nearest);
|
|
CheckGroup(pRefCountedTexture2, TextureGroup::TEXTUREGROUP_World);
|
|
|
|
TestEqual(
|
|
"Textures share RHI resource",
|
|
pRefCountedTexture1->getUnrealTexture()->GetResource()->GetTextureRHI(),
|
|
pRefCountedTexture2->getUnrealTexture()
|
|
->GetResource()
|
|
->GetTextureRHI());
|
|
});
|
|
|
|
It("Loading the same texture twice", [this]() {
|
|
CesiumGltf::Model model;
|
|
|
|
CesiumGltf::Image& image = model.images.emplace_back();
|
|
image.pAsset = pImageAsset;
|
|
|
|
CesiumGltf::Sampler& sampler = model.samplers.emplace_back();
|
|
sampler.minFilter = CesiumGltf::Sampler::MinFilter::LINEAR_MIPMAP_LINEAR;
|
|
sampler.magFilter = CesiumGltf::Sampler::MagFilter::LINEAR;
|
|
sampler.wrapS = CesiumGltf::Sampler::WrapS::REPEAT;
|
|
sampler.wrapT = CesiumGltf::Sampler::WrapT::MIRRORED_REPEAT;
|
|
|
|
CesiumGltf::Texture& texture = model.textures.emplace_back();
|
|
texture.source = 0;
|
|
texture.sampler = 0;
|
|
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded =
|
|
loadTextureFromModelAnyThreadPart(model, texture, true);
|
|
TestNotNull("pHalfLoaded", pHalfLoaded.Get());
|
|
TestNotNull("pHalfLoaded->pTexture", pHalfLoaded->pTexture.get());
|
|
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture =
|
|
loadTextureGameThreadPart(model, pHalfLoaded.Get());
|
|
CheckPixels(pRefCountedTexture, true);
|
|
CheckSRGB(pRefCountedTexture, true);
|
|
CheckAddress(
|
|
pRefCountedTexture,
|
|
TextureAddress::TA_Wrap,
|
|
TextureAddress::TA_Mirror);
|
|
CheckFilter(pRefCountedTexture, TextureFilter::TF_Default);
|
|
CheckGroup(pRefCountedTexture, TextureGroup::TEXTUREGROUP_World);
|
|
|
|
// Copy the model and load the same texture again.
|
|
// This time there's no more pixel data, so it's necessary to use the
|
|
// previously-created texture.
|
|
CesiumGltf::Model model2 = model;
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded2 =
|
|
loadTextureFromModelAnyThreadPart(model2, model.textures[0], true);
|
|
TestNotNull("pHalfLoaded2", pHalfLoaded2.Get());
|
|
TestNotNull("pHalfLoaded2->pTexture", pHalfLoaded2->pTexture.get());
|
|
TestNull(
|
|
"pHalfLoaded2->pTexture->getTextureResource()",
|
|
pHalfLoaded2->pTexture->getTextureResource().Get());
|
|
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture2 =
|
|
loadTextureGameThreadPart(model, pHalfLoaded.Get());
|
|
TestEqual("Same textures", pRefCountedTexture2, pRefCountedTexture);
|
|
});
|
|
|
|
It("Loading the same texture twice from one model", [this]() {
|
|
CesiumGltf::Model model;
|
|
|
|
CesiumGltf::Image& image = model.images.emplace_back();
|
|
image.pAsset = pImageAsset;
|
|
|
|
CesiumGltf::Sampler& sampler = model.samplers.emplace_back();
|
|
sampler.minFilter = CesiumGltf::Sampler::MinFilter::LINEAR_MIPMAP_LINEAR;
|
|
sampler.magFilter = CesiumGltf::Sampler::MagFilter::LINEAR;
|
|
sampler.wrapS = CesiumGltf::Sampler::WrapS::REPEAT;
|
|
sampler.wrapT = CesiumGltf::Sampler::WrapT::MIRRORED_REPEAT;
|
|
|
|
CesiumGltf::Texture& texture = model.textures.emplace_back();
|
|
texture.source = 0;
|
|
texture.sampler = 0;
|
|
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded =
|
|
loadTextureFromModelAnyThreadPart(model, texture, true);
|
|
TestNotNull("pHalfLoaded", pHalfLoaded.Get());
|
|
TestNotNull("pHalfLoaded->pTexture", pHalfLoaded->pTexture.get());
|
|
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture =
|
|
loadTextureGameThreadPart(model, pHalfLoaded.Get());
|
|
CheckPixels(pRefCountedTexture, true);
|
|
CheckSRGB(pRefCountedTexture, true);
|
|
CheckAddress(
|
|
pRefCountedTexture,
|
|
TextureAddress::TA_Wrap,
|
|
TextureAddress::TA_Mirror);
|
|
CheckFilter(pRefCountedTexture, TextureFilter::TF_Default);
|
|
CheckGroup(pRefCountedTexture, TextureGroup::TEXTUREGROUP_World);
|
|
|
|
// Load the same texture again.
|
|
// This time there's no more pixel data, so it's necessary to use the
|
|
// previously-created texture.
|
|
TUniquePtr<LoadedTextureResult> pHalfLoaded2 =
|
|
loadTextureFromModelAnyThreadPart(model, model.textures[0], true);
|
|
TestNotNull("pHalfLoaded2", pHalfLoaded2.Get());
|
|
TestNotNull("pHalfLoaded2->pTexture", pHalfLoaded2->pTexture.get());
|
|
TestNull(
|
|
"pHalfLoaded2->pTexture->getTextureResource()",
|
|
pHalfLoaded2->pTexture->getTextureResource().Get());
|
|
|
|
IntrusivePointer<ReferenceCountedUnrealTexture> pRefCountedTexture2 =
|
|
loadTextureGameThreadPart(model, pHalfLoaded.Get());
|
|
TestEqual("Same textures", pRefCountedTexture2, pRefCountedTexture);
|
|
});
|
|
}
|
|
|
|
void CesiumTextureUtilitySpec::CheckPixels(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
bool requireMips) {
|
|
TestNotNull("pRefCountedTexture", pRefCountedTexture.get());
|
|
TestNotNull(
|
|
"pRefCountedTexture->getUnrealTexture()",
|
|
pRefCountedTexture->getUnrealTexture().Get());
|
|
|
|
UTexture2D* pTexture = pRefCountedTexture->getUnrealTexture();
|
|
TestNotNull("pTexture", pTexture);
|
|
if (pTexture == nullptr)
|
|
return;
|
|
|
|
FTextureResource* pResource = pTexture->GetResource();
|
|
TestNotNull("pResource", pResource);
|
|
if (pResource == nullptr)
|
|
return;
|
|
|
|
TArray<FColor> readPixels;
|
|
TArray<FColor> readPixelsMip1;
|
|
|
|
ENQUEUE_RENDER_COMMAND(ReadSurfaceCommand)
|
|
([pResource, &readPixels, &readPixelsMip1](
|
|
FRHICommandListImmediate& RHICmdList) {
|
|
FRHITexture* pRHITexture = pResource->GetTextureRHI();
|
|
if (pRHITexture == nullptr)
|
|
return;
|
|
|
|
FReadSurfaceDataFlags flags{};
|
|
flags.SetLinearToGamma(false);
|
|
RHICmdList
|
|
.ReadSurfaceData(pRHITexture, FIntRect(0, 0, 3, 2), readPixels, flags);
|
|
|
|
if (pRHITexture->GetNumMips() > 1) {
|
|
flags.SetMip(1);
|
|
RHICmdList.ReadSurfaceData(
|
|
pRHITexture,
|
|
FIntRect(0, 0, 1, 1),
|
|
readPixelsMip1,
|
|
flags);
|
|
}
|
|
});
|
|
FlushRenderingCommands();
|
|
|
|
TestEqual("read buffer size", readPixels.Num() * 4, originalPixels.size());
|
|
for (size_t i = 0; i < readPixels.Num(); ++i) {
|
|
TestEqual("pixel-red", readPixels[i].R, originalPixels[i * 4]);
|
|
TestEqual("pixel-green", readPixels[i].G, originalPixels[i * 4 + 1]);
|
|
TestEqual("pixel-blue", readPixels[i].B, originalPixels[i * 4 + 2]);
|
|
TestEqual("pixel-alpha", readPixels[i].A, originalPixels[i * 4 + 3]);
|
|
}
|
|
|
|
if (requireMips) {
|
|
TestTrue("Has Mips", !readPixelsMip1.IsEmpty());
|
|
}
|
|
|
|
if (!readPixelsMip1.IsEmpty()) {
|
|
std::vector<uint8_t>& pixelsToMatch = originalMipPixels.empty()
|
|
? expectedMipPixelsIfGenerated
|
|
: originalMipPixels;
|
|
|
|
TestEqual(
|
|
"read buffer size",
|
|
readPixelsMip1.Num() * 4,
|
|
pixelsToMatch.size());
|
|
for (size_t i = 0;
|
|
i < readPixelsMip1.Num() && (i * 4 + 3) < pixelsToMatch.size();
|
|
++i) {
|
|
TestEqual("mip pixel-red", readPixelsMip1[i].R, pixelsToMatch[i * 4]);
|
|
TestEqual(
|
|
"mip pixel-green",
|
|
readPixelsMip1[i].G,
|
|
pixelsToMatch[i * 4 + 1]);
|
|
TestEqual(
|
|
"mip pixel-blue",
|
|
readPixelsMip1[i].B,
|
|
pixelsToMatch[i * 4 + 2]);
|
|
TestEqual(
|
|
"mip pixel-alpha",
|
|
readPixelsMip1[i].A,
|
|
pixelsToMatch[i * 4 + 3]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CesiumTextureUtilitySpec::CheckSRGB(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
bool expectedSRGB) {
|
|
TestNotNull("pRefCountedTexture", pRefCountedTexture.get());
|
|
if (!pRefCountedTexture)
|
|
return;
|
|
|
|
UTexture2D* pTexture = pRefCountedTexture->getUnrealTexture();
|
|
TestNotNull("pTexture", pTexture);
|
|
if (!pTexture)
|
|
return;
|
|
|
|
TestEqual("SRGB", pTexture->SRGB, expectedSRGB);
|
|
|
|
FTextureResource* pResource = pTexture->GetResource();
|
|
TestNotNull("pResource", pResource);
|
|
if (!pResource)
|
|
return;
|
|
|
|
TestEqual("RHI sRGB", pResource->bSRGB, expectedSRGB);
|
|
}
|
|
|
|
void CesiumTextureUtilitySpec::CheckAddress(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
TextureAddress expectedAddressX,
|
|
TextureAddress expectedAddressY) {
|
|
TestNotNull("pRefCountedTexture", pRefCountedTexture.get());
|
|
if (!pRefCountedTexture)
|
|
return;
|
|
|
|
UTexture2D* pTexture = pRefCountedTexture->getUnrealTexture();
|
|
TestNotNull("pTexture", pTexture);
|
|
if (!pTexture)
|
|
return;
|
|
|
|
TestEqual("AddressX", pTexture->AddressX, expectedAddressX);
|
|
TestEqual("AddressY", pTexture->AddressY, expectedAddressY);
|
|
}
|
|
|
|
void CesiumTextureUtilitySpec::CheckFilter(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
TextureFilter expectedFilter) {
|
|
TestNotNull("pRefCountedTexture", pRefCountedTexture.get());
|
|
if (!pRefCountedTexture)
|
|
return;
|
|
|
|
UTexture2D* pTexture = pRefCountedTexture->getUnrealTexture();
|
|
TestNotNull("pTexture", pTexture);
|
|
if (!pTexture)
|
|
return;
|
|
|
|
TestEqual("Filter", pTexture->Filter, expectedFilter);
|
|
}
|
|
|
|
void CesiumTextureUtilitySpec::CheckGroup(
|
|
const IntrusivePointer<ReferenceCountedUnrealTexture>& pRefCountedTexture,
|
|
TextureGroup expectedGroup) {
|
|
TestNotNull("pRefCountedTexture", pRefCountedTexture.get());
|
|
if (!pRefCountedTexture)
|
|
return;
|
|
|
|
UTexture2D* pTexture = pRefCountedTexture->getUnrealTexture();
|
|
TestNotNull("pTexture", pTexture);
|
|
if (!pTexture)
|
|
return;
|
|
|
|
TestEqual("LODGroup", pTexture->LODGroup, expectedGroup);
|
|
}
|