editor: preserve material authoring overrides
This commit is contained in:
@@ -481,3 +481,44 @@ Material 面板不应再次演化成随意堆字段的临时入口。所有 rend
|
|||||||
- 针对 Inspector 材质链路的专门自动化测试
|
- 针对 Inspector 材质链路的专门自动化测试
|
||||||
|
|
||||||
因此,Phase 3 的完成标准是“Shader schema 驱动的属性面板已经建立起来”,但还不是最终形态。
|
因此,Phase 3 的完成标准是“Shader schema 驱动的属性面板已经建立起来”,但还不是最终形态。
|
||||||
|
|
||||||
|
## 14. Phase 4 执行结果
|
||||||
|
|
||||||
|
状态:已完成
|
||||||
|
|
||||||
|
本阶段重点处理的是 authoring-state 与 runtime-state 的一致性问题,避免 Inspector 因为单纯从运行时对象反推而破坏材质源文件的语义。
|
||||||
|
|
||||||
|
### 14.1 已完成内容
|
||||||
|
|
||||||
|
- Inspector 现在会回读材质源文件中实际 authored 的:
|
||||||
|
- `properties`
|
||||||
|
- `textures`
|
||||||
|
- `keywords`
|
||||||
|
- `renderState`
|
||||||
|
- 材质状态中已加入“是否需要序列化回源文件”的 authored 标记。
|
||||||
|
- 保存材质时,不再无条件把所有运行时属性都写回源文件。
|
||||||
|
- 对于未在源文件中显式 authored 的属性,当前会继续保持“继承 Shader 默认值”的语义。
|
||||||
|
- 当用户在 Inspector 中修改属性或贴图后,对应项才会被标记为显式 authored 并写回源文件。
|
||||||
|
|
||||||
|
### 14.2 本阶段解决的核心问题
|
||||||
|
|
||||||
|
本阶段解决的是一个架构层面的隐患:
|
||||||
|
|
||||||
|
- 如果只从运行时 `Material` 反推回材质源文件,打开并保存一次材质,就会把 Shader 默认值全部固化进 `.mat`。
|
||||||
|
- 一旦默认值被固化,后续 Shader 默认值再调整,材质就不再继承新的默认值。
|
||||||
|
|
||||||
|
当前这条链路已经收口到更合理的状态:
|
||||||
|
|
||||||
|
- 只有显式 authored 的 override 才会写回
|
||||||
|
- 默认值仍然可以继续作为 Shader 侧的基线被继承
|
||||||
|
|
||||||
|
### 14.3 本阶段仍未完成的部分
|
||||||
|
|
||||||
|
以下内容仍然需要下一阶段继续完成:
|
||||||
|
|
||||||
|
- 针对 Inspector 材质链路的专门自动化测试
|
||||||
|
- 属性“重置到默认值”的正式交互
|
||||||
|
- 关键词的可视化编辑 UI
|
||||||
|
- 更完整的属性类型/显示策略覆盖
|
||||||
|
|
||||||
|
因此,Phase 4 的性质是“先把 authoring 语义做正确”,为最后的测试与收口创造条件。
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cctype>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
@@ -105,6 +107,254 @@ std::string EscapeJsonString(const std::string& value) {
|
|||||||
return escaped;
|
return escaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t SkipWhitespace(const std::string& text, size_t position) {
|
||||||
|
while (position < text.size() && std::isspace(static_cast<unsigned char>(text[position])) != 0) {
|
||||||
|
++position;
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseQuotedString(
|
||||||
|
const std::string& text,
|
||||||
|
size_t quotePosition,
|
||||||
|
std::string& outValue,
|
||||||
|
size_t* nextPosition = nullptr) {
|
||||||
|
if (quotePosition >= text.size() || text[quotePosition] != '"') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string parsed;
|
||||||
|
++quotePosition;
|
||||||
|
while (quotePosition < text.size()) {
|
||||||
|
const char ch = text[quotePosition];
|
||||||
|
if (ch == '\\') {
|
||||||
|
if (quotePosition + 1 >= text.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parsed.push_back(text[quotePosition + 1]);
|
||||||
|
quotePosition += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '"') {
|
||||||
|
outValue = parsed;
|
||||||
|
if (nextPosition != nullptr) {
|
||||||
|
*nextPosition = quotePosition + 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
parsed.push_back(ch);
|
||||||
|
++quotePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindValueStart(const std::string& text, const char* key, size_t& outValuePosition) {
|
||||||
|
const std::string token = std::string("\"") + key + "\"";
|
||||||
|
const size_t keyPosition = text.find(token);
|
||||||
|
if (keyPosition == std::string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t colonPosition = text.find(':', keyPosition + token.length());
|
||||||
|
if (colonPosition == std::string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outValuePosition = SkipWhitespace(text, colonPosition + 1);
|
||||||
|
return outValuePosition < text.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryExtractDelimitedValue(
|
||||||
|
const std::string& text,
|
||||||
|
const char* key,
|
||||||
|
char openChar,
|
||||||
|
char closeChar,
|
||||||
|
std::string& outValue) {
|
||||||
|
size_t valuePosition = 0;
|
||||||
|
if (!FindValueStart(text, key, valuePosition) || text[valuePosition] != openChar) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int depth = 0;
|
||||||
|
bool inString = false;
|
||||||
|
size_t current = valuePosition;
|
||||||
|
while (current < text.size()) {
|
||||||
|
const char ch = text[current];
|
||||||
|
if (ch == '"' && (current == 0 || text[current - 1] != '\\')) {
|
||||||
|
inString = !inString;
|
||||||
|
}
|
||||||
|
if (!inString) {
|
||||||
|
if (ch == openChar) {
|
||||||
|
++depth;
|
||||||
|
} else if (ch == closeChar) {
|
||||||
|
--depth;
|
||||||
|
if (depth == 0) {
|
||||||
|
outValue = text.substr(valuePosition, current - valuePosition + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryExtractObject(const std::string& text, const char* key, std::string& outValue) {
|
||||||
|
return TryExtractDelimitedValue(text, key, '{', '}', outValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryExtractArray(const std::string& text, const char* key, std::string& outValue) {
|
||||||
|
return TryExtractDelimitedValue(text, key, '[', ']', outValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectObjectKeys(const std::string& objectText, std::unordered_set<std::string>& outKeys) {
|
||||||
|
if (objectText.empty() || objectText.front() != '{' || objectText.back() != '}') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t position = 1;
|
||||||
|
while (position < objectText.size()) {
|
||||||
|
position = SkipWhitespace(objectText, position);
|
||||||
|
if (position >= objectText.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (objectText[position] == '}') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key;
|
||||||
|
if (!ParseQuotedString(objectText, position, key, &position)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outKeys.insert(key);
|
||||||
|
|
||||||
|
position = SkipWhitespace(objectText, position);
|
||||||
|
if (position >= objectText.size() || objectText[position] != ':') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
position = SkipWhitespace(objectText, position + 1);
|
||||||
|
if (position >= objectText.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectText[position] == '"') {
|
||||||
|
std::string ignoredValue;
|
||||||
|
if (!ParseQuotedString(objectText, position, ignoredValue, &position)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (objectText[position] == '[') {
|
||||||
|
int depth = 0;
|
||||||
|
bool inString = false;
|
||||||
|
do {
|
||||||
|
const char ch = objectText[position];
|
||||||
|
if (ch == '"' && (position == 0 || objectText[position - 1] != '\\')) {
|
||||||
|
inString = !inString;
|
||||||
|
}
|
||||||
|
if (!inString) {
|
||||||
|
if (ch == '[') {
|
||||||
|
++depth;
|
||||||
|
} else if (ch == ']') {
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++position;
|
||||||
|
} while (position < objectText.size() && depth > 0);
|
||||||
|
} else {
|
||||||
|
while (position < objectText.size() &&
|
||||||
|
objectText[position] != ',' &&
|
||||||
|
objectText[position] != '}') {
|
||||||
|
++position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
position = SkipWhitespace(objectText, position);
|
||||||
|
if (position < objectText.size() && objectText[position] == ',') {
|
||||||
|
++position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollectStringArrayValues(const std::string& arrayText, std::unordered_set<std::string>& outValues) {
|
||||||
|
if (arrayText.empty() || arrayText.front() != '[' || arrayText.back() != ']') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t position = 1;
|
||||||
|
while (position < arrayText.size()) {
|
||||||
|
position = SkipWhitespace(arrayText, position);
|
||||||
|
if (position >= arrayText.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (arrayText[position] == ']') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string value;
|
||||||
|
if (!ParseQuotedString(arrayText, position, value, &position)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!TrimCopy(value).empty()) {
|
||||||
|
outValues.insert(TrimCopy(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
position = SkipWhitespace(arrayText, position);
|
||||||
|
if (position < arrayText.size() && arrayText[position] == ',') {
|
||||||
|
++position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MaterialAuthoringPresence {
|
||||||
|
bool hasRenderStateOverride = false;
|
||||||
|
std::unordered_set<std::string> keywordValues;
|
||||||
|
std::unordered_set<std::string> propertyKeys;
|
||||||
|
std::unordered_set<std::string> textureKeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
MaterialAuthoringPresence ParseMaterialAuthoringPresence(const std::string& text) {
|
||||||
|
MaterialAuthoringPresence presence;
|
||||||
|
presence.hasRenderStateOverride = text.find("\"renderState\"") != std::string::npos;
|
||||||
|
|
||||||
|
std::string propertiesObject;
|
||||||
|
if (TryExtractObject(text, "properties", propertiesObject)) {
|
||||||
|
CollectObjectKeys(propertiesObject, presence.propertyKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string texturesObject;
|
||||||
|
if (TryExtractObject(text, "textures", texturesObject)) {
|
||||||
|
CollectObjectKeys(texturesObject, presence.textureKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string keywordsArray;
|
||||||
|
if (TryExtractArray(text, "keywords", keywordsArray)) {
|
||||||
|
CollectStringArrayValues(keywordsArray, presence.keywordValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
return presence;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReadTextFileOrEmpty(const std::string& path) {
|
||||||
|
if (path.empty()) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream input(std::filesystem::path(Platform::Utf8ToWide(path)), std::ios::in | std::ios::binary);
|
||||||
|
if (!input.is_open()) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(
|
||||||
|
(std::istreambuf_iterator<char>(input)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
}
|
||||||
|
|
||||||
struct InspectorAssetReferenceInteraction {
|
struct InspectorAssetReferenceInteraction {
|
||||||
std::string assignedPath;
|
std::string assignedPath;
|
||||||
bool clearRequested = false;
|
bool clearRequested = false;
|
||||||
@@ -750,6 +1000,7 @@ void CopyMaterialPropertyValue(
|
|||||||
destination.intValue = source.intValue;
|
destination.intValue = source.intValue;
|
||||||
destination.boolValue = source.boolValue;
|
destination.boolValue = source.boolValue;
|
||||||
destination.texturePath = source.texturePath;
|
destination.texturePath = source.texturePath;
|
||||||
|
destination.serialized = source.serialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<InspectorPanel::MaterialPropertyState> BuildShaderDefaultPropertyStates(
|
std::vector<InspectorPanel::MaterialPropertyState> BuildShaderDefaultPropertyStates(
|
||||||
@@ -807,6 +1058,24 @@ void SyncMaterialAssetStateWithShader(
|
|||||||
state.keywords = std::move(nextKeywords);
|
state.keywords = std::move(nextKeywords);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApplyMaterialAuthoringPresenceToState(
|
||||||
|
const MaterialAuthoringPresence& presence,
|
||||||
|
InspectorPanel::MaterialAssetState& state) {
|
||||||
|
state.hasRenderStateOverride = presence.hasRenderStateOverride;
|
||||||
|
|
||||||
|
for (InspectorPanel::MaterialKeywordState& keyword : state.keywords) {
|
||||||
|
keyword.serialized = presence.keywordValues.find(TrimCopy(keyword.value)) != presence.keywordValues.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (InspectorPanel::MaterialPropertyState& property : state.properties) {
|
||||||
|
if (IsTextureMaterialPropertyType(property.type)) {
|
||||||
|
property.serialized = presence.textureKeys.find(property.name) != presence.textureKeys.end();
|
||||||
|
} else {
|
||||||
|
property.serialized = presence.propertyKeys.find(property.name) != presence.propertyKeys.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InspectorPanel::MaterialPropertyState* FindMaterialPropertyState(
|
InspectorPanel::MaterialPropertyState* FindMaterialPropertyState(
|
||||||
InspectorPanel::MaterialAssetState& state,
|
InspectorPanel::MaterialAssetState& state,
|
||||||
const Containers::String& propertyName) {
|
const Containers::String& propertyName) {
|
||||||
@@ -846,7 +1115,11 @@ bool DrawShaderDrivenMaterialProperty(
|
|||||||
switch (shaderProperty.type) {
|
switch (shaderProperty.type) {
|
||||||
case ::XCEngine::Resources::ShaderPropertyType::Float:
|
case ::XCEngine::Resources::ShaderPropertyType::Float:
|
||||||
case ::XCEngine::Resources::ShaderPropertyType::Range:
|
case ::XCEngine::Resources::ShaderPropertyType::Range:
|
||||||
return UI::DrawPropertyFloat(label, propertyState->floatValue[0], 0.01f);
|
if (!UI::DrawPropertyFloat(label, propertyState->floatValue[0], 0.01f)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
propertyState->serialized = true;
|
||||||
|
return true;
|
||||||
|
|
||||||
case ::XCEngine::Resources::ShaderPropertyType::Int: {
|
case ::XCEngine::Resources::ShaderPropertyType::Int: {
|
||||||
int value = propertyState->intValue[0];
|
int value = propertyState->intValue[0];
|
||||||
@@ -854,11 +1127,16 @@ bool DrawShaderDrivenMaterialProperty(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
propertyState->intValue[0] = value;
|
propertyState->intValue[0] = value;
|
||||||
|
propertyState->serialized = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ::XCEngine::Resources::ShaderPropertyType::Color:
|
case ::XCEngine::Resources::ShaderPropertyType::Color:
|
||||||
return UI::DrawPropertyColor4(label, propertyState->floatValue.data());
|
if (!UI::DrawPropertyColor4(label, propertyState->floatValue.data())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
propertyState->serialized = true;
|
||||||
|
return true;
|
||||||
|
|
||||||
case ::XCEngine::Resources::ShaderPropertyType::Vector: {
|
case ::XCEngine::Resources::ShaderPropertyType::Vector: {
|
||||||
if (propertyState->type == ::XCEngine::Resources::MaterialPropertyType::Float2) {
|
if (propertyState->type == ::XCEngine::Resources::MaterialPropertyType::Float2) {
|
||||||
@@ -868,6 +1146,7 @@ bool DrawShaderDrivenMaterialProperty(
|
|||||||
}
|
}
|
||||||
propertyState->floatValue[0] = value.x;
|
propertyState->floatValue[0] = value.x;
|
||||||
propertyState->floatValue[1] = value.y;
|
propertyState->floatValue[1] = value.y;
|
||||||
|
propertyState->serialized = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (propertyState->type == ::XCEngine::Resources::MaterialPropertyType::Float3) {
|
if (propertyState->type == ::XCEngine::Resources::MaterialPropertyType::Float3) {
|
||||||
@@ -881,9 +1160,14 @@ bool DrawShaderDrivenMaterialProperty(
|
|||||||
propertyState->floatValue[0] = value.x;
|
propertyState->floatValue[0] = value.x;
|
||||||
propertyState->floatValue[1] = value.y;
|
propertyState->floatValue[1] = value.y;
|
||||||
propertyState->floatValue[2] = value.z;
|
propertyState->floatValue[2] = value.z;
|
||||||
|
propertyState->serialized = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return DrawFloat4Property(label, propertyState->floatValue.data());
|
if (!DrawFloat4Property(label, propertyState->floatValue.data())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
propertyState->serialized = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ::XCEngine::Resources::ShaderPropertyType::Texture2D:
|
case ::XCEngine::Resources::ShaderPropertyType::Texture2D:
|
||||||
@@ -899,11 +1183,13 @@ bool DrawShaderDrivenMaterialProperty(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
propertyState->texturePath.clear();
|
propertyState->texturePath.clear();
|
||||||
|
propertyState->serialized = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!textureInteraction.assignedPath.empty() &&
|
if (!textureInteraction.assignedPath.empty() &&
|
||||||
textureInteraction.assignedPath != propertyState->texturePath) {
|
textureInteraction.assignedPath != propertyState->texturePath) {
|
||||||
propertyState->texturePath = textureInteraction.assignedPath;
|
propertyState->texturePath = textureInteraction.assignedPath;
|
||||||
|
propertyState->serialized = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -980,6 +1266,9 @@ std::string BuildMaterialAssetFileText(const InspectorPanel::MaterialAssetState&
|
|||||||
std::string keywordsArray = " \"keywords\": [";
|
std::string keywordsArray = " \"keywords\": [";
|
||||||
bool firstKeyword = true;
|
bool firstKeyword = true;
|
||||||
for (const InspectorPanel::MaterialKeywordState& keyword : state.keywords) {
|
for (const InspectorPanel::MaterialKeywordState& keyword : state.keywords) {
|
||||||
|
if (!keyword.serialized) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const std::string keywordValue = TrimCopy(keyword.value);
|
const std::string keywordValue = TrimCopy(keyword.value);
|
||||||
if (keywordValue.empty()) {
|
if (keywordValue.empty()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1004,6 +1293,9 @@ std::string BuildMaterialAssetFileText(const InspectorPanel::MaterialAssetState&
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (IsTextureMaterialPropertyType(property.type)) {
|
if (IsTextureMaterialPropertyType(property.type)) {
|
||||||
|
if (!property.serialized) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const std::string texturePath = TrimCopy(property.texturePath);
|
const std::string texturePath = TrimCopy(property.texturePath);
|
||||||
if (!texturePath.empty()) {
|
if (!texturePath.empty()) {
|
||||||
textureEntries.push_back(
|
textureEntries.push_back(
|
||||||
@@ -1013,6 +1305,9 @@ std::string BuildMaterialAssetFileText(const InspectorPanel::MaterialAssetState&
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!property.serialized) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const std::string propertyValueText = BuildMaterialPropertyValueText(property);
|
const std::string propertyValueText = BuildMaterialPropertyValueText(property);
|
||||||
if (!propertyValueText.empty()) {
|
if (!propertyValueText.empty()) {
|
||||||
propertyEntries.push_back(
|
propertyEntries.push_back(
|
||||||
@@ -1228,6 +1523,13 @@ void InspectorPanel::PopulateMaterialAssetStateFromResource(::XCEngine::Resource
|
|||||||
|
|
||||||
m_materialAssetState.properties = CollectMaterialPropertyStates(material);
|
m_materialAssetState.properties = CollectMaterialPropertyStates(material);
|
||||||
|
|
||||||
|
const std::string sourceText = ReadTextFileOrEmpty(m_materialAssetState.assetFullPath);
|
||||||
|
if (!sourceText.empty()) {
|
||||||
|
ApplyMaterialAuthoringPresenceToState(
|
||||||
|
ParseMaterialAuthoringPresence(sourceText),
|
||||||
|
m_materialAssetState);
|
||||||
|
}
|
||||||
|
|
||||||
m_materialAssetState.loaded = true;
|
m_materialAssetState.loaded = true;
|
||||||
m_materialAssetState.dirty = false;
|
m_materialAssetState.dirty = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public:
|
|||||||
|
|
||||||
struct MaterialKeywordState {
|
struct MaterialKeywordState {
|
||||||
std::string value;
|
std::string value;
|
||||||
|
bool serialized = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MaterialPropertyState {
|
struct MaterialPropertyState {
|
||||||
@@ -55,6 +56,7 @@ public:
|
|||||||
std::array<::XCEngine::Core::int32, 4> intValue{};
|
std::array<::XCEngine::Core::int32, 4> intValue{};
|
||||||
bool boolValue = false;
|
bool boolValue = false;
|
||||||
std::string texturePath;
|
std::string texturePath;
|
||||||
|
bool serialized = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MaterialAssetState {
|
struct MaterialAssetState {
|
||||||
|
|||||||
Reference in New Issue
Block a user