Tighten material loader schema parsing
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
@@ -572,6 +571,21 @@ bool TryParseFloatText(const std::string& text, float& outValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParseFloatValue(const std::string& json, const char* key, float& outValue) {
|
||||
size_t valuePos = 0;
|
||||
if (!FindValueStart(json, key, valuePos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string rawValue;
|
||||
JsonRawValueType rawType = JsonRawValueType::Invalid;
|
||||
if (!TryExtractRawValue(json, valuePos, rawValue, rawType) || rawType != JsonRawValueType::Number) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryParseFloatText(rawValue, outValue);
|
||||
}
|
||||
|
||||
bool TryParseIntText(const std::string& text, Core::int32& outValue) {
|
||||
const std::string trimmed = TrimCopy(text);
|
||||
if (trimmed.empty()) {
|
||||
@@ -641,91 +655,14 @@ bool TryParseFloatListText(const std::string& text,
|
||||
return outCount > 0;
|
||||
}
|
||||
|
||||
Containers::String NormalizeMaterialLookupToken(const Containers::String& value) {
|
||||
std::string normalized;
|
||||
normalized.reserve(value.Length());
|
||||
for (size_t index = 0; index < value.Length(); ++index) {
|
||||
const unsigned char ch = static_cast<unsigned char>(value[index]);
|
||||
if (std::isalnum(ch) != 0) {
|
||||
normalized.push_back(static_cast<char>(std::tolower(ch)));
|
||||
}
|
||||
}
|
||||
|
||||
return Containers::String(normalized.c_str());
|
||||
}
|
||||
|
||||
const ShaderPropertyDesc* FindShaderPropertyBySemantic(
|
||||
const Shader* shader,
|
||||
const Containers::String& semantic) {
|
||||
if (shader == nullptr || semantic.Empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Containers::String normalizedSemantic = NormalizeMaterialLookupToken(semantic);
|
||||
for (const ShaderPropertyDesc& property : shader->GetProperties()) {
|
||||
if (NormalizeMaterialLookupToken(property.semantic) == normalizedSemantic) {
|
||||
return &property;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Containers::String ResolveLegacyMaterialSemanticAlias(
|
||||
const Containers::String& propertyName,
|
||||
JsonRawValueType rawType) {
|
||||
const Containers::String normalizedName = NormalizeMaterialLookupToken(propertyName);
|
||||
|
||||
if (rawType == JsonRawValueType::Array ||
|
||||
rawType == JsonRawValueType::Number) {
|
||||
if (normalizedName == "basecolor" ||
|
||||
normalizedName == "color") {
|
||||
return Containers::String("BaseColor");
|
||||
}
|
||||
}
|
||||
|
||||
if (rawType == JsonRawValueType::String) {
|
||||
if (normalizedName == "basecolortexture" ||
|
||||
normalizedName == "maintex" ||
|
||||
normalizedName == "maintexture" ||
|
||||
normalizedName == "albedotexture" ||
|
||||
normalizedName == "texture") {
|
||||
return Containers::String("BaseColorTexture");
|
||||
}
|
||||
}
|
||||
|
||||
return Containers::String();
|
||||
}
|
||||
|
||||
const ShaderPropertyDesc* ResolveShaderPropertyForMaterialKey(
|
||||
const Shader* shader,
|
||||
const Containers::String& propertyName,
|
||||
JsonRawValueType rawType) {
|
||||
const Containers::String& propertyName) {
|
||||
if (shader == nullptr || propertyName.Empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (const ShaderPropertyDesc* property = shader->FindProperty(propertyName)) {
|
||||
return property;
|
||||
}
|
||||
|
||||
const Containers::String normalizedName = NormalizeMaterialLookupToken(propertyName);
|
||||
for (const ShaderPropertyDesc& property : shader->GetProperties()) {
|
||||
if (NormalizeMaterialLookupToken(property.name) == normalizedName) {
|
||||
return &property;
|
||||
}
|
||||
}
|
||||
|
||||
if (const ShaderPropertyDesc* property = FindShaderPropertyBySemantic(shader, propertyName)) {
|
||||
return property;
|
||||
}
|
||||
|
||||
const Containers::String semanticAlias = ResolveLegacyMaterialSemanticAlias(propertyName, rawType);
|
||||
if (!semanticAlias.Empty()) {
|
||||
return FindShaderPropertyBySemantic(shader, semanticAlias);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return shader->FindProperty(propertyName);
|
||||
}
|
||||
|
||||
bool TryApplySchemaMaterialProperty(Material* material,
|
||||
@@ -875,7 +812,7 @@ bool TryParseMaterialPropertiesObject(const std::string& objectText, Material* m
|
||||
|
||||
const Shader* shader = material->GetShader();
|
||||
const ShaderPropertyDesc* shaderProperty =
|
||||
ResolveShaderPropertyForMaterialKey(shader, propertyName, rawType);
|
||||
ResolveShaderPropertyForMaterialKey(shader, propertyName);
|
||||
if (shader != nullptr && shaderProperty == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -1105,7 +1042,7 @@ bool TryApplyTexturePath(Material* material,
|
||||
|
||||
const Shader* shader = material->GetShader();
|
||||
const ShaderPropertyDesc* shaderProperty =
|
||||
ResolveShaderPropertyForMaterialKey(shader, textureName, JsonRawValueType::String);
|
||||
ResolveShaderPropertyForMaterialKey(shader, textureName);
|
||||
if (shader != nullptr && shaderProperty == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -1332,6 +1269,123 @@ bool TryParseBlendOp(const Containers::String& value, MaterialBlendOp& outOp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryParseStencilOp(const Containers::String& value, MaterialStencilOp& outOp) {
|
||||
const Containers::String normalized = value.Trim().ToLower();
|
||||
if (normalized == "keep") {
|
||||
outOp = MaterialStencilOp::Keep;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "zero") {
|
||||
outOp = MaterialStencilOp::Zero;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "replace") {
|
||||
outOp = MaterialStencilOp::Replace;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "incrsat" || normalized == "incrementclamp" || normalized == "increment_clamp") {
|
||||
outOp = MaterialStencilOp::IncrSat;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "decrsat" || normalized == "decrementclamp" || normalized == "decrement_clamp") {
|
||||
outOp = MaterialStencilOp::DecrSat;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "invert") {
|
||||
outOp = MaterialStencilOp::Invert;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "incrwrap" || normalized == "incr" || normalized == "incrementwrap" || normalized == "increment_wrap") {
|
||||
outOp = MaterialStencilOp::IncrWrap;
|
||||
return true;
|
||||
}
|
||||
if (normalized == "decrwrap" || normalized == "decr" || normalized == "decrementwrap" || normalized == "decrement_wrap") {
|
||||
outOp = MaterialStencilOp::DecrWrap;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryParseStencilStateObject(const std::string& objectText, MaterialStencilState& outState) {
|
||||
if (objectText.empty() || objectText.front() != '{' || objectText.back() != '}') {
|
||||
return false;
|
||||
}
|
||||
|
||||
MaterialStencilState stencilState = outState;
|
||||
stencilState.enabled = true;
|
||||
|
||||
bool boolValue = false;
|
||||
if (HasKey(objectText, "enabled")) {
|
||||
if (!TryParseBoolValue(objectText, "enabled", boolValue)) {
|
||||
return false;
|
||||
}
|
||||
stencilState.enabled = boolValue;
|
||||
}
|
||||
|
||||
Core::int32 intValue = 0;
|
||||
auto parseMaskValue = [&](const char* key, Core::uint8& outValue) -> bool {
|
||||
if (!HasKey(objectText, key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryParseIntValue(objectText, key, intValue) || intValue < 0 || intValue > 0xFF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outValue = static_cast<Core::uint8>(intValue);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!parseMaskValue("readMask", stencilState.readMask) ||
|
||||
!parseMaskValue("writeMask", stencilState.writeMask) ||
|
||||
!parseMaskValue("ref", stencilState.reference) ||
|
||||
!parseMaskValue("reference", stencilState.reference)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Containers::String enumValue;
|
||||
auto parseFaceCompare = [&](const char* key, MaterialComparisonFunc& outFunc) -> bool {
|
||||
if (!HasKey(objectText, key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryParseStringValue(objectText, key, enumValue) &&
|
||||
TryParseComparisonFunc(enumValue, outFunc);
|
||||
};
|
||||
|
||||
auto parseFaceOp = [&](const char* key, MaterialStencilOp& outOp) -> bool {
|
||||
if (!HasKey(objectText, key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryParseStringValue(objectText, key, enumValue) &&
|
||||
TryParseStencilOp(enumValue, outOp);
|
||||
};
|
||||
|
||||
if (!parseFaceCompare("comp", stencilState.front.func) ||
|
||||
!parseFaceCompare("comp", stencilState.back.func) ||
|
||||
!parseFaceOp("pass", stencilState.front.passOp) ||
|
||||
!parseFaceOp("pass", stencilState.back.passOp) ||
|
||||
!parseFaceOp("fail", stencilState.front.failOp) ||
|
||||
!parseFaceOp("fail", stencilState.back.failOp) ||
|
||||
!parseFaceOp("zFail", stencilState.front.depthFailOp) ||
|
||||
!parseFaceOp("zFail", stencilState.back.depthFailOp) ||
|
||||
!parseFaceCompare("compFront", stencilState.front.func) ||
|
||||
!parseFaceOp("passFront", stencilState.front.passOp) ||
|
||||
!parseFaceOp("failFront", stencilState.front.failOp) ||
|
||||
!parseFaceOp("zFailFront", stencilState.front.depthFailOp) ||
|
||||
!parseFaceCompare("compBack", stencilState.back.func) ||
|
||||
!parseFaceOp("passBack", stencilState.back.passOp) ||
|
||||
!parseFaceOp("failBack", stencilState.back.failOp) ||
|
||||
!parseFaceOp("zFailBack", stencilState.back.depthFailOp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outState = stencilState;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParseRenderStateObject(const std::string& objectText, Material* material) {
|
||||
if (material == nullptr || objectText.empty() || objectText.front() != '{' || objectText.back() != '}') {
|
||||
return false;
|
||||
@@ -1435,6 +1489,42 @@ bool TryParseRenderStateObject(const std::string& objectText, Material* material
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (HasKey(objectText, "offset")) {
|
||||
std::string offsetArray;
|
||||
if (!TryExtractArray(objectText, "offset", offsetArray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float offsetValues[2] = {};
|
||||
size_t offsetCount = 0;
|
||||
if (!TryParseFloatListText(offsetArray, offsetValues, 2, offsetCount) || offsetCount != 2u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
renderState.depthBiasFactor = offsetValues[0];
|
||||
renderState.depthBiasUnits = static_cast<Core::int32>(offsetValues[1]);
|
||||
}
|
||||
if (HasKey(objectText, "offsetFactor")) {
|
||||
float floatValue = 0.0f;
|
||||
if (!TryParseFloatValue(objectText, "offsetFactor", floatValue)) {
|
||||
return false;
|
||||
}
|
||||
renderState.depthBiasFactor = floatValue;
|
||||
}
|
||||
if (HasKey(objectText, "offsetUnits")) {
|
||||
Core::int32 offsetUnits = 0;
|
||||
if (!TryParseIntValue(objectText, "offsetUnits", offsetUnits)) {
|
||||
return false;
|
||||
}
|
||||
renderState.depthBiasUnits = offsetUnits;
|
||||
}
|
||||
if (HasKey(objectText, "stencil")) {
|
||||
std::string stencilObject;
|
||||
if (!TryExtractObject(objectText, "stencil", stencilObject) ||
|
||||
!TryParseStencilStateObject(stencilObject, renderState.stencil)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
material->SetRenderState(renderState);
|
||||
return true;
|
||||
@@ -1473,18 +1563,6 @@ bool MaterialFileExists(const Containers::String& path) {
|
||||
return std::filesystem::exists(std::filesystem::path(resourceRoot.CStr()) / inputPath);
|
||||
}
|
||||
|
||||
void ApplyLegacyMaterialShaderPassHint(Material* material, const Containers::String& shaderPass) {
|
||||
if (material == nullptr || shaderPass.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Rendering::IsRedundantLegacyMaterialShaderPassHint(material->GetShader(), shaderPass)) {
|
||||
return;
|
||||
}
|
||||
|
||||
material->SetLegacyShaderPassHint(shaderPass);
|
||||
}
|
||||
|
||||
ResourceHandle<Shader> LoadShaderHandle(const Containers::String& shaderPath);
|
||||
|
||||
template<typename T>
|
||||
@@ -1572,11 +1650,9 @@ LoadResult LoadMaterialArtifact(const Containers::String& path) {
|
||||
}
|
||||
|
||||
const std::string magic(fileHeader.magic, fileHeader.magic + 7);
|
||||
const bool isLegacySchema = magic == "XCMAT02" && fileHeader.schemaVersion == 2u;
|
||||
const bool isSchemaV3 = magic == "XCMAT03" && fileHeader.schemaVersion == 3u;
|
||||
const bool isCurrentSchema =
|
||||
magic == "XCMAT04" && fileHeader.schemaVersion == kMaterialArtifactSchemaVersion;
|
||||
if (!isLegacySchema && !isSchemaV3 && !isCurrentSchema) {
|
||||
magic == "XCMAT06" && fileHeader.schemaVersion == kMaterialArtifactSchemaVersion;
|
||||
if (!isCurrentSchema) {
|
||||
return LoadResult("Invalid material artifact magic: " + path);
|
||||
}
|
||||
|
||||
@@ -1588,11 +1664,9 @@ LoadResult LoadMaterialArtifact(const Containers::String& path) {
|
||||
Containers::String materialName;
|
||||
Containers::String materialSourcePath;
|
||||
Containers::String shaderPath;
|
||||
Containers::String shaderPass;
|
||||
if (!ReadMaterialArtifactString(data, offset, materialName) ||
|
||||
!ReadMaterialArtifactString(data, offset, materialSourcePath) ||
|
||||
!ReadMaterialArtifactString(data, offset, shaderPath) ||
|
||||
!ReadMaterialArtifactString(data, offset, shaderPass)) {
|
||||
!ReadMaterialArtifactString(data, offset, shaderPath)) {
|
||||
return LoadResult("Failed to parse material artifact strings: " + path);
|
||||
}
|
||||
|
||||
@@ -1608,38 +1682,10 @@ LoadResult LoadMaterialArtifact(const Containers::String& path) {
|
||||
material->SetShader(shaderHandle);
|
||||
}
|
||||
}
|
||||
ApplyLegacyMaterialShaderPassHint(material.get(), shaderPass);
|
||||
|
||||
MaterialArtifactHeader header = {};
|
||||
if (isLegacySchema) {
|
||||
MaterialArtifactHeaderV2 legacyHeader = {};
|
||||
if (!ReadMaterialArtifactValue(data, offset, legacyHeader)) {
|
||||
return LoadResult("Failed to parse material artifact body: " + path);
|
||||
}
|
||||
|
||||
header.renderQueue = legacyHeader.renderQueue;
|
||||
header.renderState = legacyHeader.renderState;
|
||||
header.tagCount = legacyHeader.tagCount;
|
||||
header.hasRenderStateOverride = 1u;
|
||||
header.propertyCount = legacyHeader.propertyCount;
|
||||
header.textureBindingCount = legacyHeader.textureBindingCount;
|
||||
} else if (isSchemaV3) {
|
||||
MaterialArtifactHeaderV3 legacyHeader = {};
|
||||
if (!ReadMaterialArtifactValue(data, offset, legacyHeader)) {
|
||||
return LoadResult("Failed to parse material artifact body: " + path);
|
||||
}
|
||||
|
||||
header.renderQueue = legacyHeader.renderQueue;
|
||||
header.renderState = legacyHeader.renderState;
|
||||
header.tagCount = legacyHeader.tagCount;
|
||||
header.hasRenderStateOverride = 1u;
|
||||
header.keywordCount = legacyHeader.keywordCount;
|
||||
header.propertyCount = legacyHeader.propertyCount;
|
||||
header.textureBindingCount = legacyHeader.textureBindingCount;
|
||||
} else {
|
||||
if (!ReadMaterialArtifactValue(data, offset, header)) {
|
||||
return LoadResult("Failed to parse material artifact body: " + path);
|
||||
}
|
||||
if (!ReadMaterialArtifactValue(data, offset, header)) {
|
||||
return LoadResult("Failed to parse material artifact body: " + path);
|
||||
}
|
||||
|
||||
material->SetRenderQueue(header.renderQueue);
|
||||
@@ -1794,19 +1840,6 @@ bool MaterialLoader::ParseMaterialData(const Containers::Array<Core::uint8>& dat
|
||||
}
|
||||
}
|
||||
|
||||
Containers::String shaderPass;
|
||||
if (HasKey(jsonText, "shaderPass")) {
|
||||
if (!TryParseStringValue(jsonText, "shaderPass", shaderPass)) {
|
||||
return false;
|
||||
}
|
||||
ApplyLegacyMaterialShaderPassHint(material, shaderPass);
|
||||
} else if (HasKey(jsonText, "pass")) {
|
||||
if (!TryParseStringValue(jsonText, "pass", shaderPass)) {
|
||||
return false;
|
||||
}
|
||||
ApplyLegacyMaterialShaderPassHint(material, shaderPass);
|
||||
}
|
||||
|
||||
if (HasKey(jsonText, "renderQueue")) {
|
||||
Core::int32 renderQueue = 0;
|
||||
if (TryParseIntValue(jsonText, "renderQueue", renderQueue)) {
|
||||
|
||||
@@ -217,7 +217,6 @@ TEST(MaterialLoader, LoadValidMaterialParsesRenderMetadata) {
|
||||
materialFile << "{\n";
|
||||
materialFile << " \"shader\": \"" << shaderPath.generic_string() << "\",\n";
|
||||
materialFile << " \"renderQueue\": \"Transparent\",\n";
|
||||
materialFile << " \"shaderPass\": \"ForwardLit\",\n";
|
||||
materialFile << " \"tags\": {\n";
|
||||
materialFile << " \"LightMode\": \"ForwardBase\",\n";
|
||||
materialFile << " \"RenderType\": \"Transparent\"\n";
|
||||
@@ -243,7 +242,6 @@ TEST(MaterialLoader, LoadValidMaterialParsesRenderMetadata) {
|
||||
EXPECT_TRUE(material->IsValid());
|
||||
EXPECT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_EQ(material->GetRenderQueue(), static_cast<XCEngine::Core::int32>(MaterialRenderQueue::Transparent));
|
||||
EXPECT_TRUE(material->GetLegacyShaderPassHint().Empty());
|
||||
EXPECT_EQ(material->GetTag("LightMode"), "ForwardBase");
|
||||
EXPECT_EQ(material->GetTag("RenderType"), "Transparent");
|
||||
EXPECT_EQ(material->GetRenderState().cullMode, MaterialCullMode::Back);
|
||||
@@ -380,7 +378,6 @@ TEST(MaterialLoader, LoadMaterialWithAuthoringShaderResolvesShaderPass) {
|
||||
ASSERT_TRUE(materialFile.is_open());
|
||||
materialFile << "{\n";
|
||||
materialFile << " \"shader\": \"" << shaderPath.generic_string() << "\",\n";
|
||||
materialFile << " \"shaderPass\": \"ForwardLit\",\n";
|
||||
materialFile << " \"renderQueue\": \"Geometry\"\n";
|
||||
materialFile << "}\n";
|
||||
}
|
||||
@@ -393,7 +390,6 @@ TEST(MaterialLoader, LoadMaterialWithAuthoringShaderResolvesShaderPass) {
|
||||
Material* material = static_cast<Material*>(result.resource);
|
||||
ASSERT_NE(material, nullptr);
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_TRUE(material->GetLegacyShaderPassHint().Empty());
|
||||
ASSERT_NE(material->GetShader()->FindPass("ForwardLit"), nullptr);
|
||||
const ShaderStageVariant* vertexVariant =
|
||||
material->GetShader()->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::OpenGL);
|
||||
@@ -419,7 +415,6 @@ TEST(MaterialLoader, LoadMaterialWithPropertiesObjectAppliesTypedOverrides) {
|
||||
materialPath,
|
||||
"{\n"
|
||||
" \"shader\": \"" + shaderPath.generic_string() + "\",\n"
|
||||
" \"shaderPass\": \"ForwardLit\",\n"
|
||||
" \"properties\": {\n"
|
||||
" \"_BaseColor\": [0.2, 0.4, 0.6, 0.8],\n"
|
||||
" \"_Metallic\": 0.15,\n"
|
||||
@@ -435,7 +430,6 @@ TEST(MaterialLoader, LoadMaterialWithPropertiesObjectAppliesTypedOverrides) {
|
||||
auto* material = static_cast<Material*>(result.resource);
|
||||
ASSERT_NE(material, nullptr);
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_TRUE(material->GetLegacyShaderPassHint().Empty());
|
||||
EXPECT_EQ(material->GetFloat4("_BaseColor"), XCEngine::Math::Vector4(0.2f, 0.4f, 0.6f, 0.8f));
|
||||
EXPECT_FLOAT_EQ(material->GetFloat("_Metallic"), 0.15f);
|
||||
EXPECT_EQ(material->GetInt("_Mode"), 5);
|
||||
@@ -446,7 +440,7 @@ TEST(MaterialLoader, LoadMaterialWithPropertiesObjectAppliesTypedOverrides) {
|
||||
fs::remove_all(rootPath);
|
||||
}
|
||||
|
||||
TEST(MaterialLoader, LoadBuiltinShaderMaterialDropsRedundantBuiltinShaderPassHint) {
|
||||
TEST(MaterialLoader, LoadBuiltinShaderMaterialUsesShaderMetadataWithoutMaterialPassHint) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
ResourceManager& manager = ResourceManager::Get();
|
||||
@@ -460,8 +454,7 @@ TEST(MaterialLoader, LoadBuiltinShaderMaterialDropsRedundantBuiltinShaderPassHin
|
||||
WriteTextFile(
|
||||
materialPath,
|
||||
"{\n"
|
||||
" \"shader\": \"" + std::string(GetBuiltinUnlitShaderPath().CStr()) + "\",\n"
|
||||
" \"shaderPass\": \"Unlit\"\n"
|
||||
" \"shader\": \"" + std::string(GetBuiltinUnlitShaderPath().CStr()) + "\"\n"
|
||||
"}\n");
|
||||
|
||||
MaterialLoader loader;
|
||||
@@ -472,7 +465,6 @@ TEST(MaterialLoader, LoadBuiltinShaderMaterialDropsRedundantBuiltinShaderPassHin
|
||||
auto* material = static_cast<Material*>(result.resource);
|
||||
ASSERT_NE(material, nullptr);
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_TRUE(material->GetLegacyShaderPassHint().Empty());
|
||||
EXPECT_NE(material->GetShader()->FindPass("Unlit"), nullptr);
|
||||
|
||||
delete material;
|
||||
@@ -568,7 +560,7 @@ TEST(MaterialLoader, LoadMaterialWithPropertiesObjectPreservesShaderDefaultsForO
|
||||
fs::remove_all(rootPath);
|
||||
}
|
||||
|
||||
TEST(MaterialLoader, LoadMaterialMapsLegacySemanticKeysIntoShaderSchemaProperties) {
|
||||
TEST(MaterialLoader, RejectsSemanticKeysWhenShaderSchemaRequiresExactPropertyNames) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path rootPath = fs::temp_directory_path() / "xc_material_loader_semantic_alias_test";
|
||||
@@ -593,21 +585,7 @@ TEST(MaterialLoader, LoadMaterialMapsLegacySemanticKeysIntoShaderSchemaPropertie
|
||||
|
||||
MaterialLoader loader;
|
||||
LoadResult result = loader.Load(materialPath.generic_string().c_str());
|
||||
ASSERT_TRUE(result);
|
||||
ASSERT_NE(result.resource, nullptr);
|
||||
|
||||
auto* material = static_cast<Material*>(result.resource);
|
||||
ASSERT_NE(material, nullptr);
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_FALSE(material->HasProperty("baseColor"));
|
||||
EXPECT_EQ(material->GetFloat4("_BaseColor"), XCEngine::Math::Vector4(0.2f, 0.4f, 0.6f, 0.8f));
|
||||
ASSERT_EQ(material->GetTextureBindingCount(), 1u);
|
||||
EXPECT_EQ(material->GetTextureBindingName(0), "_MainTex");
|
||||
EXPECT_EQ(
|
||||
fs::path(material->GetTextureBindingPath(0).CStr()).lexically_normal().generic_string(),
|
||||
(rootPath / "checker.bmp").lexically_normal().generic_string());
|
||||
|
||||
delete material;
|
||||
EXPECT_FALSE(result);
|
||||
fs::remove_all(rootPath);
|
||||
}
|
||||
|
||||
@@ -624,7 +602,6 @@ TEST(MaterialLoader, LoadMaterialParsesKeywordArrayAgainstShaderSchema) {
|
||||
materialPath,
|
||||
"{\n"
|
||||
" \"shader\": \"" + shaderPath.generic_string() + "\",\n"
|
||||
" \"shaderPass\": \"ForwardLit\",\n"
|
||||
" \"keywords\": [\"XC_MAIN_LIGHT_SHADOWS\", \"XC_ALPHA_TEST\", \"XC_ALPHA_TEST\", \"_\"]\n"
|
||||
"}\n");
|
||||
|
||||
@@ -839,7 +816,6 @@ TEST(MaterialLoader, AssetDatabaseMaterialArtifactRoundTripsKeywords) {
|
||||
materialPath,
|
||||
"{\n"
|
||||
" \"shader\": \"Assets/Shaders/keyword.shader\",\n"
|
||||
" \"shaderPass\": \"ForwardLit\",\n"
|
||||
" \"keywords\": [\"XC_MAIN_LIGHT_SHADOWS\", \"XC_ALPHA_TEST\"]\n"
|
||||
"}\n");
|
||||
|
||||
@@ -873,7 +849,7 @@ TEST(MaterialLoader, AssetDatabaseMaterialArtifactRoundTripsKeywords) {
|
||||
fs::remove_all(projectRoot);
|
||||
}
|
||||
|
||||
TEST(MaterialLoader, AssetDatabaseMaterialArtifactStripsRedundantBuiltinShaderPassHint) {
|
||||
TEST(MaterialLoader, AssetDatabaseMaterialArtifactRoundTripsBuiltinShaderWithoutMaterialPassHint) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
ResourceManager& manager = ResourceManager::Get();
|
||||
@@ -890,7 +866,6 @@ TEST(MaterialLoader, AssetDatabaseMaterialArtifactStripsRedundantBuiltinShaderPa
|
||||
materialPath,
|
||||
"{\n"
|
||||
" \"shader\": \"" + std::string(GetBuiltinUnlitShaderPath().CStr()) + "\",\n"
|
||||
" \"shaderPass\": \"Unlit\",\n"
|
||||
" \"properties\": {\n"
|
||||
" \"_BaseColor\": [1.0, 1.0, 1.0, 1.0]\n"
|
||||
" }\n"
|
||||
@@ -914,7 +889,6 @@ TEST(MaterialLoader, AssetDatabaseMaterialArtifactStripsRedundantBuiltinShaderPa
|
||||
auto* material = static_cast<Material*>(result.resource);
|
||||
ASSERT_NE(material, nullptr);
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_TRUE(material->GetLegacyShaderPassHint().Empty());
|
||||
EXPECT_EQ(material->GetFloat4("_BaseColor"), XCEngine::Math::Vector4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
delete material;
|
||||
@@ -1065,7 +1039,6 @@ TEST(MaterialLoader, AssetDatabaseReimportsMaterialWhenShaderDependencyChanges)
|
||||
ASSERT_TRUE(materialFile.is_open());
|
||||
materialFile << "{\n";
|
||||
materialFile << " \"shader\": \"Assets/Shaders/lit.shader\",\n";
|
||||
materialFile << " \"shaderPass\": \"ForwardLit\",\n";
|
||||
materialFile << " \"renderQueue\": \"geometry\"\n";
|
||||
materialFile << "}\n";
|
||||
}
|
||||
@@ -1133,7 +1106,6 @@ TEST(MaterialLoader, LoadMaterialArtifactDefersTexturePayloadUntilRequested) {
|
||||
WriteArtifactString(output, "LazyMaterial");
|
||||
WriteArtifactString(output, "Assets/lazy.material");
|
||||
WriteArtifactString(output, "");
|
||||
WriteArtifactString(output, "");
|
||||
|
||||
MaterialArtifactHeader header;
|
||||
header.renderQueue = static_cast<XCEngine::Core::int32>(MaterialRenderQueue::Geometry);
|
||||
|
||||
Reference in New Issue
Block a user