载入中...
搜索中...
未找到
VegetationPresetApply.cpp
浏览该文件的文档.
2
3#include <cerrno>
4#include <cmath>
5#include <cstdlib>
6
7namespace eve::asset_import {
8namespace {
9
10Result<double> number(std::string_view text, const VegetationPresetCommand& command) {
11 const std::string token(text);
12 char* end = nullptr;
13 errno = 0;
14 const double value = std::strtod(token.c_str(), &end);
15 if (errno != 0 || end != token.c_str() + token.size() || !std::isfinite(value))
17 "preset numeric argument is invalid", command.sourcePath, {},
18 "asset.import.vegetation-preset.apply"));
20}
21
22Result<std::array<double, 4>> vector(const VegetationPresetCommand& command) {
23 if (command.arguments.size() != 5)
24 return Result<std::array<double, 4>>::failure(
25 Diagnostic::error(DiagnosticCode::ParseError, "preset vector requires property and four values",
26 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
27 std::array<double, 4> result{};
28 for (std::size_t i = 0; i < 4; ++i) {
29 auto value = number(command.arguments[i + 1], command);
30 if (!value) return Result<std::array<double, 4>>::failure(value.status());
31 result[i] = value.value();
32 }
33 return Result<std::array<double, 4>>::success(result);
34}
35
36std::string join(const std::vector<std::string>& values) {
37 std::string result;
38 for (const auto& value : values) {
39 if (!result.empty()) result.push_back(' ');
40 result += value;
41 }
42 return result;
43}
44
45bool metadata(std::string_view domain) {
46 return domain == "InfoTitle" || domain == "InfoPreset" || domain == "InfoStatus" || domain == "InfoOnline" ||
47 domain == "InfoWarning" || domain == "InfoMessage" || domain == "Updated" || domain == "Detect" ||
48 domain == "Preset";
49}
50
51bool meshOperation(std::string_view operation) {
52 static const std::set<std::string_view> values{"SetVariation", "SetOcclusion", "SetDetailMask", "SetDetailCoord",
53 "SetHeight", "SetMotion2", "SetMotion3", "SetPivots",
54 "SetNormals", "SetBounds", "SetReadWrite"};
55 return values.contains(operation);
56}
57
58bool outputDomain(std::string_view domain) {
59 static const std::set<std::string_view> values{
60 "OutputOptions", "OutputTransforms", "OutputMeshes", "OutputMaterials", "OutputTextures", "OutputBase",
61 "OutputData", "OutputCollection", "OutputGUIDs", "OutputPipelines", "CollectBase", "CollectData"};
62 return values.contains(domain);
63}
64
65Result<void> applyMaterial(VegetationConversionCandidate& out, const VegetationPresetCommand& command) {
66 const auto& a = command.arguments;
67 if (command.operation == "SET_FLOAT") {
68 if (a.size() != 2)
70 "SET_FLOAT requires property and value", command.sourcePath,
71 {}, "asset.import.vegetation-preset.apply"));
72 auto value = number(a[1], command);
73 if (!value) return Result<void>::failure(value.status());
74 out.materialFloats[a[0]] = value.value();
75 } else if (command.operation == "SET_VECTOR" || command.operation == "SET_COLOR") {
76 auto value = vector(command);
77 if (!value) return Result<void>::failure(value.status());
78 if (command.operation == "SET_COLOR")
79 out.materialColors[a[0]] = value.value();
80 else
81 out.materialVectors[a[0]] = value.value();
82 } else if (command.operation == "COPY_FLOAT" || command.operation == "COPY_VECTOR" ||
83 command.operation == "COPY_COLOR" || command.operation == "COPY_TEX") {
84 const bool ignoredLegacyFloatArgument = command.operation == "COPY_FLOAT" && a.size() == 3;
85 if (a.size() != 2 && !ignoredLegacyFloatArgument)
87 Diagnostic::error(DiagnosticCode::ParseError, "COPY operation requires source and destination",
88 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
89 if (command.operation == "COPY_FLOAT") {
90 if (auto v = out.materialFloats.find(a[0]); v != out.materialFloats.end())
91 out.materialFloats[a[1]] = v->second;
92 } else if (command.operation == "COPY_VECTOR") {
93 if (auto v = out.materialVectors.find(a[0]); v != out.materialVectors.end())
94 out.materialVectors[a[1]] = v->second;
95 else if (auto v = out.materialColors.find(a[0]); v != out.materialColors.end())
96 out.materialVectors[a[1]] = v->second;
97 } else if (command.operation == "COPY_COLOR") {
98 if (auto v = out.materialColors.find(a[0]); v != out.materialColors.end())
99 out.materialColors[a[1]] = v->second;
100 else if (auto v = out.materialVectors.find(a[0]); v != out.materialVectors.end())
101 out.materialColors[a[1]] = v->second;
102 } else if (auto v = out.materialTextures.find(a[0]); v != out.materialTextures.end()) {
103 if (out.firstValidTextureDestinations.contains(a[1]))
104 out.materialTextures.try_emplace(a[1], v->second);
105 else
106 out.materialTextures[a[1]] = v->second;
107 }
108 } else if (command.operation == "COPY_ST_AS_VECTOR") {
109 if (a.size() != 2)
111 Diagnostic::error(DiagnosticCode::ParseError, "COPY_ST_AS_VECTOR requires source and destination",
112 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
113 if (auto v = out.materialTextureTransforms.find(a[0]); v != out.materialTextureTransforms.end())
114 out.materialVectors[a[1]] = v->second;
115 } else if (command.operation.starts_with("COPY_FLOAT_AS_VECTOR_")) {
116 if (a.size() != 2)
118 Diagnostic::error(DiagnosticCode::ParseError, "float-to-vector copy requires source and destination",
119 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
120 static constexpr std::string_view axes = "XYZW";
121 const auto axis = axes.find(command.operation.back());
122 if (axis == std::string_view::npos)
124 "float-to-vector axis is invalid", command.sourcePath, {},
125 "asset.import.vegetation-preset.apply"));
126 if (auto v = out.materialFloats.find(a[0]); v != out.materialFloats.end())
127 out.materialVectors[a[1]][axis] = v->second;
128 } else if (command.operation.starts_with("COPY_VECTOR_") && command.operation.ends_with("_AS_FLOAT")) {
129 if (a.size() != 2)
131 Diagnostic::error(DiagnosticCode::ParseError, "vector-to-float copy requires source and destination",
132 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
133 static constexpr std::string_view axes = "XYZW";
134 const auto axis = axes.find(command.operation[12]);
135 if (axis == std::string_view::npos)
137 "vector-to-float axis is invalid", command.sourcePath, {},
138 "asset.import.vegetation-preset.apply"));
139 if (auto v = out.materialVectors.find(a[0]); v != out.materialVectors.end())
140 out.materialFloats[a[1]] = v->second[axis];
141 } else if (command.operation == "SET_SHADER") {
142 if (a.size() != 1)
143 return Result<void>::failure(Diagnostic::error(DiagnosticCode::ParseError, "SET_SHADER requires one alias",
144 command.sourcePath, {},
145 "asset.import.vegetation-preset.apply"));
146 const auto found = out.shaderAliases.find(a[0]);
147 out.materialShader = found == out.shaderAliases.end() ? a[0] : found->second;
148 } else if (command.operation == "SET_SHADER_BY_NAME") {
149 if (a.empty())
151 "SET_SHADER_BY_NAME requires a name", command.sourcePath, {},
152 "asset.import.vegetation-preset.apply"));
153 out.materialShader = join(a);
154 } else if (command.operation == "SET_SHADER_BY_LIGHTING") {
155 if (a.size() != 1)
157 Diagnostic::error(DiagnosticCode::ParseError, "SET_SHADER_BY_LIGHTING requires one mode",
158 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
159 out.materialLighting = a[0];
160 } else if (command.operation == "ENABLE_KEYWORD" || command.operation == "LOCK_PROP") {
161 if (a.size() != 1)
163 Diagnostic::error(DiagnosticCode::ParseError, "keyword/property command requires one name",
164 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
165 (command.operation == "ENABLE_KEYWORD" ? out.materialKeywords : out.lockedMaterialProperties).insert(a[0]);
166 } else if (command.operation == "ENABLE_INSTANCING" || command.operation == "DISABLE_INSTANCING") {
167 if (!a.empty())
169 "instancing command takes no arguments", command.sourcePath,
170 {}, "asset.import.vegetation-preset.apply"));
171 out.materialInstancing = command.operation == "ENABLE_INSTANCING";
172 } else if (command.operation == "COPY_TEX_FIRST_VALID") {
173 if (a.size() != 1)
175 Diagnostic::error(DiagnosticCode::ParseError, "COPY_TEX_FIRST_VALID requires a destination",
176 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
177 out.firstValidTextureDestinations.insert(a[0]);
178 } else {
180 "unknown Material preset operation: " + command.operation,
181 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
182 }
183 return Result<void>::success();
184}
185} // namespace
186
188 const std::vector<VegetationPresetCommand>& commands) {
189 for (const auto& command : commands) {
190 if (command.domain == "Material") {
191 auto result = applyMaterial(out, command);
192 if (!result) return Result<VegetationConversionCandidate>::failure(result.status());
193 } else if (command.domain == "Mesh") {
194 if (!meshOperation(command.operation) || command.arguments.empty())
196 Diagnostic::error(DiagnosticCode::ParseError, "Mesh rule is malformed", command.sourcePath, {},
197 "asset.import.vegetation-preset.apply"));
198 out.meshRules[command.operation] = command.arguments;
199 } else if (command.domain == "Utility") {
200 static const std::set<std::string_view> utilities{"START_TEXTURE_PACKING", "USE_MATERIAL_POST_PROCESSING",
201 "USE_CURRENT_MATERIAL_AS_BASE",
202 "USE_CONVERTED_MATERIAL_AS_BASE"};
203 if (!utilities.contains(command.operation) || !command.arguments.empty())
205 Diagnostic::error(DiagnosticCode::ParseError, "Utility command takes no arguments",
206 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
207 out.utilityModes.push_back(command.operation);
208 if (command.operation == "START_TEXTURE_PACKING") out.texturePacks.emplace_back();
209 } else if (command.domain == "Texture") {
210 if (out.texturePacks.empty())
212 Diagnostic::error(DiagnosticCode::ParseError, "Texture command requires START_TEXTURE_PACKING",
213 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
214 auto& recipe = out.texturePacks.back();
215 if (command.operation == "PropName" || command.operation == "ImportType" ||
216 command.operation == "TransformSpace") {
217 if (command.arguments.size() != 1)
219 Diagnostic::error(DiagnosticCode::ParseError, "Texture property command requires one argument",
220 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
221 if (command.operation == "PropName")
222 recipe.targetProperty = command.arguments[0];
223 else if (command.operation == "ImportType")
224 recipe.importType = command.arguments[0];
225 else
226 recipe.transformSpace = command.arguments[0];
227 } else {
228 const std::map<std::string, std::size_t> channels{
229 {"SetRed", 0}, {"SetGreen", 1}, {"SetBlue", 2}, {"SetAlpha", 3}};
230 const auto found = channels.find(command.operation);
231 const bool none = command.arguments.size() == 1 && command.arguments[0] == "NONE";
232 if (found == channels.end() ||
233 (!none && (command.arguments.size() < 2 || command.arguments.size() > 3)))
235 Diagnostic::error(DiagnosticCode::ParseError, "Texture channel command is malformed",
236 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
237 recipe.channels[found->second] =
238 none ? VegetationTextureChannelRecipe{"NONE", {}, {}}
239 : VegetationTextureChannelRecipe{command.arguments[0], command.arguments[1],
240 command.arguments.size() == 3 ? command.arguments[2] : ""};
241 }
242 } else if (command.domain == "Shader") {
243 if (!command.operation.starts_with("SHADER_") || command.arguments.empty())
245 Diagnostic::error(DiagnosticCode::ParseError, "Shader alias is malformed", command.sourcePath, {},
246 "asset.import.vegetation-preset.apply"));
247 out.shaderAliases[command.operation] = join(command.arguments);
248 } else if (outputDomain(command.domain)) {
249 auto values = command.arguments;
250 values.insert(values.begin(), command.operation);
251 out.outputDirectives[command.domain] = std::move(values);
252 } else if (!metadata(command.domain)) {
254 Diagnostic::error(DiagnosticCode::ParseError, "unknown preset command domain: " + command.domain,
255 command.sourcePath, {}, "asset.import.vegetation-preset.apply"));
256 }
257 }
259}
260} // namespace eve::asset_import
ActionParameterOperation operation
double value
std::map< std::string, std::vector< Key >, std::less<> > channels
std::map< std::string, Var > values
float v
std::string text
MeleePoint3 a
Definition MeleeHit.cpp:40
uint64_t token
double number
bool found
std::vector< VegetationPresetCommand > commands
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 > applyVegetationPreset(VegetationConversionCandidate source, const std::vector< VegetationPresetCommand > &commands)
Type-check and atomically apply an evaluated TVE command sequence.
int axis(int64_t a, size_t rank)
Axis.
Owning unpublished material, mesh and texture candidate mutated transactionally by preset commands.
std::map< std::string, std::vector< std::string > > meshRules
std::vector< VegetationTexturePackRecipe > texturePacks
std::map< std::string, std::vector< std::string > > outputDirectives
std::map< std::string, std::string > shaderAliases
One channel instruction in a TVE packed-texture recipe.