2026-04-07 13:00:30 +08:00
|
|
|
#include "ShaderAuthoringInternal.h"
|
|
|
|
|
|
|
|
|
|
namespace XCEngine {
|
|
|
|
|
namespace Resources {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2026-04-07 14:13:26 +08:00
|
|
|
MaterialRenderState BuildDefaultAuthoringFixedFunctionState() {
|
2026-04-07 13:00:30 +08:00
|
|
|
MaterialRenderState state = {};
|
|
|
|
|
state.blendEnable = false;
|
|
|
|
|
state.srcBlend = MaterialBlendFactor::One;
|
|
|
|
|
state.dstBlend = MaterialBlendFactor::Zero;
|
|
|
|
|
state.srcBlendAlpha = MaterialBlendFactor::One;
|
|
|
|
|
state.dstBlendAlpha = MaterialBlendFactor::Zero;
|
|
|
|
|
state.blendOp = MaterialBlendOp::Add;
|
|
|
|
|
state.blendOpAlpha = MaterialBlendOp::Add;
|
|
|
|
|
state.colorWriteMask = 0xF;
|
|
|
|
|
state.depthTestEnable = true;
|
|
|
|
|
state.depthWriteEnable = true;
|
|
|
|
|
state.depthFunc = MaterialComparisonFunc::LessEqual;
|
|
|
|
|
state.cullMode = MaterialCullMode::Back;
|
|
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EnsureAuthoringFixedFunctionStateInitialized(
|
|
|
|
|
bool& hasFixedFunctionState,
|
|
|
|
|
MaterialRenderState& fixedFunctionState) {
|
|
|
|
|
if (!hasFixedFunctionState) {
|
|
|
|
|
hasFixedFunctionState = true;
|
2026-04-07 14:13:26 +08:00
|
|
|
fixedFunctionState = BuildDefaultAuthoringFixedFunctionState();
|
2026-04-07 13:00:30 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 14:13:26 +08:00
|
|
|
bool TryParseAuthoringBoolDirectiveToken(const std::string& token, bool& outValue) {
|
2026-04-07 13:00:30 +08:00
|
|
|
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
|
|
|
|
|
if (normalized == "on") {
|
|
|
|
|
outValue = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "off") {
|
|
|
|
|
outValue = false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 14:13:26 +08:00
|
|
|
bool TryParseAuthoringCullMode(const std::string& token, MaterialCullMode& outMode) {
|
2026-04-07 13:00:30 +08:00
|
|
|
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
|
|
|
|
|
if (normalized == "back") {
|
|
|
|
|
outMode = MaterialCullMode::Back;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "front") {
|
|
|
|
|
outMode = MaterialCullMode::Front;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "off") {
|
|
|
|
|
outMode = MaterialCullMode::None;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 14:13:26 +08:00
|
|
|
bool TryParseAuthoringComparisonFunc(const std::string& token, MaterialComparisonFunc& outFunc) {
|
2026-04-07 13:00:30 +08:00
|
|
|
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
|
|
|
|
|
if (normalized == "never") {
|
|
|
|
|
outFunc = MaterialComparisonFunc::Never;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "less") {
|
|
|
|
|
outFunc = MaterialComparisonFunc::Less;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "equal") {
|
|
|
|
|
outFunc = MaterialComparisonFunc::Equal;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "lequal" || normalized == "lessequal" || normalized == "less_equal") {
|
|
|
|
|
outFunc = MaterialComparisonFunc::LessEqual;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "greater") {
|
|
|
|
|
outFunc = MaterialComparisonFunc::Greater;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "notequal" || normalized == "not_equal") {
|
|
|
|
|
outFunc = MaterialComparisonFunc::NotEqual;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "gequal" || normalized == "greaterequal" || normalized == "greater_equal") {
|
|
|
|
|
outFunc = MaterialComparisonFunc::GreaterEqual;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "always") {
|
|
|
|
|
outFunc = MaterialComparisonFunc::Always;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 14:13:26 +08:00
|
|
|
bool TryParseAuthoringBlendFactor(const std::string& token, MaterialBlendFactor& outFactor) {
|
2026-04-07 13:00:30 +08:00
|
|
|
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
|
|
|
|
|
if (normalized == "zero") {
|
|
|
|
|
outFactor = MaterialBlendFactor::Zero;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "one") {
|
|
|
|
|
outFactor = MaterialBlendFactor::One;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "srccolor" || normalized == "src_color") {
|
|
|
|
|
outFactor = MaterialBlendFactor::SrcColor;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "oneminussrccolor" || normalized == "one_minus_src_color" || normalized == "invsrccolor") {
|
|
|
|
|
outFactor = MaterialBlendFactor::InvSrcColor;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "srcalpha" || normalized == "src_alpha") {
|
|
|
|
|
outFactor = MaterialBlendFactor::SrcAlpha;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "oneminussrcalpha" || normalized == "one_minus_src_alpha" || normalized == "invsrcalpha") {
|
|
|
|
|
outFactor = MaterialBlendFactor::InvSrcAlpha;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "dstalpha" || normalized == "dst_alpha") {
|
|
|
|
|
outFactor = MaterialBlendFactor::DstAlpha;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "oneminusdstalpha" || normalized == "one_minus_dst_alpha" || normalized == "invdstalpha") {
|
|
|
|
|
outFactor = MaterialBlendFactor::InvDstAlpha;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "dstcolor" || normalized == "dst_color") {
|
|
|
|
|
outFactor = MaterialBlendFactor::DstColor;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "oneminusdstcolor" || normalized == "one_minus_dst_color" || normalized == "invdstcolor") {
|
|
|
|
|
outFactor = MaterialBlendFactor::InvDstColor;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (normalized == "srcalphasaturate" || normalized == "src_alpha_saturate" || normalized == "srcalphasat") {
|
|
|
|
|
outFactor = MaterialBlendFactor::SrcAlphaSat;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 14:13:26 +08:00
|
|
|
bool TryParseAuthoringColorMask(const std::string& token, Core::uint8& outMask) {
|
2026-04-07 13:00:30 +08:00
|
|
|
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToUpper();
|
|
|
|
|
if (normalized == "0") {
|
|
|
|
|
outMask = 0u;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Core::uint8 mask = 0u;
|
|
|
|
|
for (size_t index = 0; index < normalized.Length(); ++index) {
|
|
|
|
|
switch (normalized[index]) {
|
|
|
|
|
case 'R':
|
|
|
|
|
mask |= 0x1u;
|
|
|
|
|
break;
|
|
|
|
|
case 'G':
|
|
|
|
|
mask |= 0x2u;
|
|
|
|
|
break;
|
|
|
|
|
case 'B':
|
|
|
|
|
mask |= 0x4u;
|
|
|
|
|
break;
|
|
|
|
|
case 'A':
|
|
|
|
|
mask |= 0x8u;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outMask = mask;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 14:13:26 +08:00
|
|
|
bool TryParseAuthoringBlendDirective(
|
2026-04-07 13:00:30 +08:00
|
|
|
const std::vector<std::string>& tokens,
|
|
|
|
|
MaterialRenderState& outState) {
|
|
|
|
|
std::vector<std::string> normalizedTokens;
|
|
|
|
|
normalizedTokens.reserve(tokens.size());
|
|
|
|
|
for (const std::string& token : tokens) {
|
|
|
|
|
if (token == ",") {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string normalizedToken = token;
|
|
|
|
|
while (!normalizedToken.empty() && normalizedToken.back() == ',') {
|
|
|
|
|
normalizedToken.pop_back();
|
|
|
|
|
}
|
|
|
|
|
if (!normalizedToken.empty()) {
|
|
|
|
|
normalizedTokens.push_back(std::move(normalizedToken));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (normalizedTokens.size() != 2u &&
|
|
|
|
|
normalizedTokens.size() != 3u &&
|
|
|
|
|
normalizedTokens.size() != 5u) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (normalizedTokens.size() == 2u) {
|
|
|
|
|
bool enabled = false;
|
2026-04-07 14:13:26 +08:00
|
|
|
if (!TryParseAuthoringBoolDirectiveToken(normalizedTokens[1], enabled)) {
|
2026-04-07 13:00:30 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outState.blendEnable = enabled;
|
|
|
|
|
if (!enabled) {
|
|
|
|
|
outState.srcBlend = MaterialBlendFactor::One;
|
|
|
|
|
outState.dstBlend = MaterialBlendFactor::Zero;
|
|
|
|
|
outState.srcBlendAlpha = MaterialBlendFactor::One;
|
|
|
|
|
outState.dstBlendAlpha = MaterialBlendFactor::Zero;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MaterialBlendFactor srcBlend = MaterialBlendFactor::One;
|
|
|
|
|
MaterialBlendFactor dstBlend = MaterialBlendFactor::Zero;
|
2026-04-07 14:13:26 +08:00
|
|
|
if (!TryParseAuthoringBlendFactor(normalizedTokens[1], srcBlend) ||
|
|
|
|
|
!TryParseAuthoringBlendFactor(normalizedTokens[2], dstBlend)) {
|
2026-04-07 13:00:30 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outState.blendEnable = true;
|
|
|
|
|
outState.srcBlend = srcBlend;
|
|
|
|
|
outState.dstBlend = dstBlend;
|
|
|
|
|
|
|
|
|
|
if (normalizedTokens.size() == 5u) {
|
2026-04-07 14:13:26 +08:00
|
|
|
if (!TryParseAuthoringBlendFactor(normalizedTokens[3], outState.srcBlendAlpha) ||
|
|
|
|
|
!TryParseAuthoringBlendFactor(normalizedTokens[4], outState.dstBlendAlpha)) {
|
2026-04-07 13:00:30 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
outState.srcBlendAlpha = srcBlend;
|
|
|
|
|
outState.dstBlendAlpha = dstBlend;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetOrReplaceAuthoringTag(
|
|
|
|
|
std::vector<ShaderTagIR>& tags,
|
|
|
|
|
const Containers::String& name,
|
|
|
|
|
const Containers::String& value) {
|
|
|
|
|
for (ShaderTagIR& tag : tags) {
|
|
|
|
|
if (tag.name == name) {
|
|
|
|
|
tag.value = value;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tags.push_back({ name, value });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TryParseShaderKeywordDeclarationPragma(
|
|
|
|
|
const std::vector<std::string>& pragmaTokens,
|
|
|
|
|
ShaderKeywordDeclaration& outDeclaration) {
|
|
|
|
|
outDeclaration = {};
|
|
|
|
|
if (pragmaTokens.size() < 3u) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pragmaTokens[1] == "multi_compile") {
|
|
|
|
|
outDeclaration.type = ShaderKeywordDeclarationType::MultiCompile;
|
|
|
|
|
} else if (pragmaTokens[1] == "multi_compile_local") {
|
|
|
|
|
outDeclaration.type = ShaderKeywordDeclarationType::MultiCompileLocal;
|
|
|
|
|
} else if (pragmaTokens[1] == "shader_feature") {
|
|
|
|
|
outDeclaration.type = ShaderKeywordDeclarationType::ShaderFeature;
|
|
|
|
|
} else if (pragmaTokens[1] == "shader_feature_local") {
|
|
|
|
|
outDeclaration.type = ShaderKeywordDeclarationType::ShaderFeatureLocal;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t tokenIndex = 2; tokenIndex < pragmaTokens.size(); ++tokenIndex) {
|
|
|
|
|
outDeclaration.options.PushBack(pragmaTokens[tokenIndex].c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return !outDeclaration.options.Empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Resources
|
|
|
|
|
} // namespace XCEngine
|