载入中...
搜索中...
未找到
VegetationPresetSource.cpp
浏览该文件的文档.
2
3#include <cctype>
4#include <cmath>
5#include <new>
6#include <regex>
7#include <sstream>
8
9namespace eve::asset_import {
10namespace {
11
12Result<double> number(std::string_view text, const std::string& path) {
13 std::string value(text);
14 char* end = nullptr;
15 errno = 0;
16 const auto parsed = std::strtod(value.c_str(), &end);
17 if (errno || end != value.c_str() + value.size() || !std::isfinite(parsed))
18 return Result<double>::failure(Diagnostic::error(DiagnosticCode::ParseError, "Unity material number is invalid",
19 path, {}, "asset.import.vegetation-preset.source"));
20 return Result<double>::success(parsed);
21}
22
23template <class Map, class ValueType>
24Result<void> insertUnique(Map& values, std::string name, ValueType value, const std::string& path) {
25 if (!values.emplace(std::move(name), std::move(value)).second)
27 "duplicate Unity material saved property", path, {},
28 "asset.import.vegetation-preset.source"));
29 return Result<void>::success();
30}
31} // namespace
32
34 std::string sourcePath,
35 std::uint64_t maximumBytes) {
36 if (yaml.empty() || yaml.size() > maximumBytes)
38 Diagnostic::error(DiagnosticCode::InvalidArgument, "Unity material source exceeds parsing budget",
39 sourcePath, {}, "asset.import.vegetation-preset.source"));
40 try {
41 const std::string text(reinterpret_cast<const char*>(yaml.data()), yaml.size());
42 if (text.find("\nMaterial:") == std::string::npos && !text.starts_with("Material:"))
44 Diagnostic::error(DiagnosticCode::TypeMismatch, "Unity source is not a text Material", sourcePath, {},
45 "asset.import.vegetation-preset.source"));
47 const std::regex scalarPattern(R"((?:^|\n)[ \t]*-[ \t]+([A-Za-z_][A-Za-z0-9_]*):[ \t]*([-+0-9.eE]+))");
48 for (auto i = std::sregex_iterator(text.begin(), text.end(), scalarPattern); i != std::sregex_iterator(); ++i) {
49 auto value = number((*i)[2].str(), sourcePath);
51 auto inserted = insertUnique(out.materialFloats, (*i)[1].str(), value.value(), sourcePath);
52 if (!inserted) return Result<VegetationConversionCandidate>::failure(inserted.status());
53 }
54 const std::regex vectorPattern(
55 R"((?:^|\n)[ \t]*-[ \t]+([A-Za-z_][A-Za-z0-9_]*):[ \t]*\{(?:r|x):[ \t]*([^,}]+),[ \t]*(?:g|y):[ \t]*([^,}]+),[ \t]*(?:b|z):[ \t]*([^,}]+),[ \t]*(?:a|w):[ \t]*([^}]+)\})");
56 for (auto i = std::sregex_iterator(text.begin(), text.end(), vectorPattern); i != std::sregex_iterator(); ++i) {
57 std::array<double, 4> value{};
58 for (std::size_t component = 0; component < 4; ++component) {
59 auto parsed = number((*i)[component + 2].str(), sourcePath);
60 if (!parsed) return Result<VegetationConversionCandidate>::failure(parsed.status());
61 value[component] = parsed.value();
62 }
63 const auto name = (*i)[1].str();
64 auto vectorInserted = insertUnique(out.materialVectors, name, value, sourcePath);
65 if (!vectorInserted) return Result<VegetationConversionCandidate>::failure(vectorInserted.status());
66 auto colorInserted = insertUnique(out.materialColors, name, value, sourcePath);
67 if (!colorInserted) return Result<VegetationConversionCandidate>::failure(colorInserted.status());
68 }
69 const std::regex texturePattern(
70 R"((?:^|\n)[ \t]*-[ \t]+([A-Za-z_][A-Za-z0-9_]*):[ \t]*(?:\r?\n)[ \t]+m_Texture:[ \t]*\{fileID:[ \t]*(-?[0-9]+)(?:,[ \t]*guid:[ \t]*([0-9a-fA-F]{32}),[ \t]*type:[ \t]*[0-9]+)?\}[ \t]*(?:\r?\n)[ \t]+m_Scale:[ \t]*\{x:[ \t]*([^,}]+),[ \t]*y:[ \t]*([^}]+)\}[ \t]*(?:\r?\n)[ \t]+m_Offset:[ \t]*\{x:[ \t]*([^,}]+),[ \t]*y:[ \t]*([^}]+)\})");
71 for (auto i = std::sregex_iterator(text.begin(), text.end(), texturePattern); i != std::sregex_iterator();
72 ++i) {
73 const auto name = (*i)[1].str();
74 if ((*i)[2].str() != "0") {
75 if (!(*i)[3].matched)
77 Diagnostic::error(DiagnosticCode::ParseError, "Unity texture reference lacks a GUID",
78 sourcePath, {}, "asset.import.vegetation-preset.source"));
79 auto guid = (*i)[3].str();
80 std::transform(guid.begin(), guid.end(), guid.begin(),
81 [](unsigned char value) { return static_cast<char>(std::tolower(value)); });
82 auto inserted = insertUnique(out.materialTextures, name, std::move(guid), sourcePath);
83 if (!inserted) return Result<VegetationConversionCandidate>::failure(inserted.status());
84 }
85 std::array<double, 4> transform{};
86 for (std::size_t component = 0; component < 4; ++component) {
87 auto parsed = number((*i)[component + 4].str(), sourcePath);
88 if (!parsed) return Result<VegetationConversionCandidate>::failure(parsed.status());
89 transform[component] = parsed.value();
90 }
91 auto inserted = insertUnique(out.materialTextureTransforms, name, transform, sourcePath);
92 if (!inserted) return Result<VegetationConversionCandidate>::failure(inserted.status());
93 }
94 const std::regex shaderPattern(
95 R"((?:^|\n)[ \t]*m_Shader:[ \t]*\{fileID:[ \t]*[0-9]+,[ \t]*guid:[ \t]*([0-9a-fA-F]{32}),[ \t]*type:[ \t]*[0-9]+\})");
96 std::smatch shader;
97 if (!std::regex_search(text, shader, shaderPattern))
99 Diagnostic::error(DiagnosticCode::ParseError, "Unity material shader GUID is missing", sourcePath, {},
100 "asset.import.vegetation-preset.source"));
101 out.materialShader = shader[1].str();
102 std::transform(out.materialShader.begin(), out.materialShader.end(), out.materialShader.begin(),
103 [](unsigned char value) { return static_cast<char>(std::tolower(value)); });
104 const std::regex instancingPattern(R"((?:^|\n)[ \t]*m_EnableInstancingVariants:[ \t]*([01])(?:\r?\n|$))");
105 std::smatch instancing;
106 if (std::regex_search(text, instancing, instancingPattern)) out.materialInstancing = instancing[1].str() == "1";
107 std::istringstream lines(text);
108 std::string line;
109 bool validKeywords = false;
110 while (std::getline(lines, line)) {
111 if (!line.empty() && line.back() == '\r') line.pop_back();
112 if (line == " m_ValidKeywords:") {
113 validKeywords = true;
114 continue;
115 }
116 if (validKeywords) {
117 if (line.starts_with(" - ")) {
118 if (line.size() > 4) out.materialKeywords.insert(line.substr(4));
119 continue;
120 }
121 validKeywords = false;
122 }
123 constexpr std::string_view legacy = " m_ShaderKeywords:";
124 if (line.starts_with(legacy)) {
125 std::istringstream words(line.substr(legacy.size()));
126 std::string keyword;
127 while (words >> keyword) out.materialKeywords.insert(std::move(keyword));
128 }
129 }
131 } catch (const std::regex_error&) {
133 Diagnostic::error(DiagnosticCode::Failed, "Unity material parser expression failed", sourcePath, {},
134 "asset.import.vegetation-preset.source"));
135 } catch (const std::bad_alloc&) {
137 Diagnostic::error(DiagnosticCode::Failed, "Unity material candidate allocation failed", sourcePath, {},
138 "asset.import.vegetation-preset.source"));
139 }
140}
141
143 std::string shaderName, std::string materialName,
144 std::string shaderPipeline, std::set<std::string> outputOptions) {
146 context.outputOptions = std::move(outputOptions);
147 context.shaderName = std::move(shaderName);
148 context.materialName = std::move(materialName);
149 context.shaderPipeline = std::move(shaderPipeline);
152 const auto addProperties = [&](const auto& values) {
153 for (const auto& [name, unused] : values) {
154 (void)unused;
156 }
157 };
158 addProperties(candidate.materialFloats);
159 addProperties(candidate.materialVectors);
160 addProperties(candidate.materialColors);
161 addProperties(candidate.materialTextureTransforms);
162 for (const auto& [name, unused] : candidate.materialTextures) {
163 (void)unused;
166 }
167 return context;
168}
169} // namespace eve::asset_import
double value
std::map< std::string, Var > values
std::string text
std::string name
std::string path
Definition PlayHost.cpp:110
Shader * shader
double number
AbilityInstancingPolicy instancing
std::string sourcePath
std::vector< std::string_view > lines
const VegetationPresetContext & context
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
EVENGINE_API_PLATFORM Result< VegetationConversionCandidate > decodeUnityVegetationConversionCandidate(std::span< const std::uint8_t > yaml, std::string sourcePath, std::uint64_t maximumBytes=16ull *1024ull *1024ull)
Decode Unity text Material saved properties into an owning vegetation conversion baseline.
EVENGINE_API_PLATFORM VegetationPresetContext makeVegetationPresetContext(const VegetationConversionCandidate &candidate, std::string shaderName, std::string materialName, std::string shaderPipeline, std::set< std::string > outputOptions={})
Project one conversion baseline into immutable preset predicate facts.
@ TypeMismatch
A stable reference resolved to a different canonical domain type.
Owning unpublished material, mesh and texture candidate mutated transactionally by preset commands.
std::map< std::string, std::array< double, 4 > > materialVectors
std::map< std::string, std::array< double, 4 > > materialColors
std::map< std::string, double > materialFloats
std::map< std::string, std::array< double, 4 > > materialTextureTransforms
std::map< std::string, std::string > materialTextures
Immutable source facts used by TVE preset predicates.
std::map< std::string, double > materialFloats