载入中...
搜索中...
未找到
CookedMaterial.cpp
浏览该文件的文档.
2#include <cmath>
3#include <limits>
6namespace {
7struct Invalid {};
8const Value* field(const Value::Object& object, const std::string& key) {
9 auto it = object.find(key);
10 return it == object.end() ? nullptr : &it->second;
11}
12float number(const Value::Object& object, const std::string& key, float fallback) {
13 auto* v = field(object, key);
14 if (!v) return fallback;
15 if (!v->isNumeric()) throw Invalid{};
16 double n = v->isInt64() ? double(v->asInt()) : v->asDouble();
17 if (!std::isfinite(n) || std::abs(n) > std::numeric_limits<float>::max()) throw Invalid{};
18 return float(n);
19}
20uint32_t integer(const Value::Object& object, const std::string& key, uint32_t fallback) {
21 auto* v = field(object, key);
22 if (!v) return fallback;
23 if (!v->isInt64() || v->asInt() < 0 || uint64_t(v->asInt()) > UINT32_MAX) throw Invalid{};
24 return uint32_t(v->asInt());
25}
26bool boolean(const Value::Object& o, const char* key, bool fallback) {
27 auto* v = field(o, key);
28 if (!v) return fallback;
29 if (!v->isBool()) throw Invalid{};
30 return v->asBool();
31}
32std::string string(const Value::Object& o, const char* key, std::string fallback) {
33 auto* v = field(o, key);
34 if (!v) return fallback;
35 if (!v->isString()) throw Invalid{};
36 return v->asString();
37}
38template <size_t N>
39void vector(const Value::Object& o, const char* key, std::array<float, N>& out) {
40 auto* v = field(o, key);
41 if (!v) return;
42 auto* a = v->getIf<Value::Array>();
43 if (!a || a->size() != N) throw Invalid{};
44 for (size_t i = 0; i < N; i++) {
45 Value::Object scalar{{"v", (*a)[i]}};
46 out[i] = number(scalar, "v", 0);
47 }
48}
49} // namespace
51 const asset::EvpackCapabilities& caps) {
52 auto payload = reader.read(ref, "eve.material/15", caps, 1024 * 1024);
53 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
54 payload = reader.read(ref, "eve.material/14", caps, 1024 * 1024);
55 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
56 payload = reader.read(ref, "eve.material/13", caps, 1024 * 1024);
57 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
58 payload = reader.read(ref, "eve.material/12", caps, 1024 * 1024);
59 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
60 payload = reader.read(ref, "eve.material/11", caps, 1024 * 1024);
61 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
62 payload = reader.read(ref, "eve.material/10", caps, 1024 * 1024);
63 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
64 payload = reader.read(ref, "eve.material/9", caps, 1024 * 1024);
65 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
66 payload = reader.read(ref, "eve.material/8", caps, 1024 * 1024);
67 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
68 payload = reader.read(ref, "eve.material/7", caps, 1024 * 1024);
69 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
70 payload = reader.read(ref, "eve.material/6", caps, 1024 * 1024);
71 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
72 payload = reader.read(ref, "eve.material/5", caps, 1024 * 1024);
73 if (!payload) return Result<CookedMaterial>::failure(payload.status());
74 try {
75 if (payload.value().chunks.size() != 1) throw Invalid{};
76 auto decoded = asset::decodeRuntimeDefinition(payload.value().chunks.front().bytes);
77 if (!decoded) return Result<CookedMaterial>::failure(decoded.status());
78 auto* object = decoded.value().getIf<Value::Object>();
79 if (!object) throw Invalid{};
80 auto& o = *object;
82 auto& s = m.surface;
83 auto version = integer(o, "schemaVersion", 0);
84 if (string(o, "schema", "") != "eve.material" ||
85 (version != 5 && version != 6 && version != 7 && version != 8 && version != 9 && version != 10 &&
86 version != 11 && version != 12 && version != 13 && version != 14 && version != 15))
87 throw Invalid{};
88 if (version != payload.value().schemaVersion.value()) throw Invalid{};
89 if (version >= 15 && !o.contains("alphaToCoverage")) throw Invalid{};
90 if (version == 5 && o.contains("vegetationSurface")) throw Invalid{};
91 vector(o, "motionHighlightColor", s.motionHighlightColor);
92 if (auto* encoded = field(o, "vegetationSurface")) {
93 const auto* vegetation = encoded->getIf<Value::Object>();
94 const auto expectedVegetationFields = version == 6 ? 7u : (version >= 14 ? 16u : 14u);
95 if (!vegetation || vegetation->size() != expectedVegetationFields) throw Invalid{};
96 for (const auto& [key, value] : *vegetation)
97 if (key != "overlay" && key != "wetness" && key != "overlayVariation" && key != "overlayProjection" &&
98 key != "vertexOcclusionAlpha" && key != "invertVertexOcclusion" && key != "sourceFamily" &&
99 key != "colors" && key != "colorsIntensity" && key != "colorsMask" && key != "colorsVariation" &&
100 key != "vertexOcclusionMinimum" && key != "vertexOcclusionMaximum" &&
101 key != "invertVertexOcclusionColors" && key != "vertexOcclusionColor" &&
102 key != "backfaceNormalMode")
103 throw Invalid{};
104 if (string(*vegetation, "sourceFamily", "") != "tve-12") throw Invalid{};
106 result.overlayCoverage = number(*vegetation, "overlay", 1);
107 result.wetnessCoverage = number(*vegetation, "wetness", 1);
108 result.overlayVariation = number(*vegetation, "overlayVariation", .5f);
109 result.overlayProjection = number(*vegetation, "overlayProjection", .5f);
110 result.vertexOcclusionAlpha = number(*vegetation, "vertexOcclusionAlpha", .5019608f);
111 if (version >= 14) vector(*vegetation, "vertexOcclusionColor", result.vertexOcclusionColor);
112 if (version >= 14)
113 result.backfaceNormalMode =
114 graphics::PbrVegetationBackfaceNormalMode(integer(*vegetation, "backfaceNormalMode", 0));
115 result.invertVertexOcclusion = boolean(*vegetation, "invertVertexOcclusion", false);
116 result.colorsCoverage = number(*vegetation, "colors", 1);
117 result.colorsIntensity = number(*vegetation, "colorsIntensity", 1);
118 result.colorsMask = number(*vegetation, "colorsMask", 1);
119 result.colorsVariation = number(*vegetation, "colorsVariation", .5f);
120 result.vertexOcclusionMinimum = number(*vegetation, "vertexOcclusionMinimum", 0);
121 result.vertexOcclusionMaximum = number(*vegetation, "vertexOcclusionMaximum", 1);
122 result.invertVertexOcclusionColors = boolean(*vegetation, "invertVertexOcclusionColors", false);
123 if (result.overlayCoverage < 0 || result.overlayCoverage > 1 || result.wetnessCoverage < 0 ||
124 result.wetnessCoverage > 1 || result.overlayVariation < 0 || result.overlayVariation > 1 ||
125 result.overlayProjection < 0 || result.overlayProjection > 1 || result.vertexOcclusionAlpha < 0 ||
126 result.vertexOcclusionAlpha > 1 || result.colorsCoverage < 0 || result.colorsCoverage > 1 ||
127 result.colorsIntensity < 0 || result.colorsIntensity > 2 || result.colorsMask < 0 ||
128 result.colorsMask > 1 || result.colorsVariation < 0 || result.colorsVariation > 1 ||
129 result.vertexOcclusionMinimum < 0 || result.vertexOcclusionMinimum > 1 ||
130 result.vertexOcclusionMaximum < 0 || result.vertexOcclusionMaximum > 1 ||
131 result.vertexOcclusionMaximum - result.vertexOcclusionMinimum + .0001f == 0)
132 throw Invalid{};
133 for (const auto value : result.vertexOcclusionColor)
134 if (!std::isfinite(value) || value < 0) throw Invalid{};
136 throw Invalid{};
137 m.vegetationSurface = result;
138 }
139 if (auto* encoded = field(o, "vegetationDetail")) {
140 if (version < 8) throw Invalid{};
141 const auto* detail = encoded->getIf<Value::Object>();
142 if (!detail || detail->size() != 24) throw Invalid{};
143 auto& d = s.vegetationDetail;
144 d.value = number(*detail, "value", 0);
145 d.uvMode = integer(*detail, "uvMode", 0);
146 d.inverseUvScale = boolean(*detail, "inverseUvScale", false);
147 vector(*detail, "uvScale", d.uvScale);
148 vector(*detail, "uvOffset", d.uvOffset);
149 vector(*detail, "color", d.color);
150 vector(*detail, "colorTwo", d.colorTwo);
151 d.colorMode = integer(*detail, "colorMode", 0);
152 d.albedoValue = number(*detail, "albedoValue", 1);
153 d.normalValue = number(*detail, "normalValue", 1);
154 d.normalBlendValue = number(*detail, "normalBlendValue", 1);
155 d.metallicValue = number(*detail, "metallicValue", 0);
156 d.occlusionValue = number(*detail, "occlusionValue", 1);
157 d.smoothnessValue = number(*detail, "smoothnessValue", 1);
158 d.blendMode = integer(*detail, "blendMode", 0);
159 d.alphaMode = integer(*detail, "alphaMode", 1);
160 d.maskMode = integer(*detail, "maskMode", 0);
161 d.meshMode = integer(*detail, "meshMode", 0);
162 d.blendMinimum = number(*detail, "blendMinimum", 0);
163 d.blendMaximum = number(*detail, "blendMaximum", 1);
164 d.maskMinimum = number(*detail, "maskMinimum", 0);
165 d.maskMaximum = number(*detail, "maskMaximum", 1);
166 d.meshMinimum = number(*detail, "meshMinimum", 0);
167 d.meshMaximum = number(*detail, "meshMaximum", 1);
168 }
169 if (auto* encoded = field(o, "vegetationAlpha")) {
170 if (version < 9 || version > 15) throw Invalid{};
171 const auto* alpha = encoded->getIf<Value::Object>();
172 if (!alpha || alpha->size() != (version < 12 ? 3u : 6u)) throw Invalid{};
173 auto& a = s.vegetationAlpha;
174 a.enabled = true;
175 a.global = number(*alpha, "global", 1);
176 a.variation = number(*alpha, "variation", .5f);
177 a.detailFade = boolean(*alpha, "detailFade", false);
178 if (version >= 12) {
179 a.glancing = number(*alpha, "glancing", 0);
180 a.camera = number(*alpha, "camera", 1);
181 a.constant = number(*alpha, "constant", 0);
182 }
183 }
184 if (auto* encoded = field(o, "vegetationFields")) {
185 if (version < 9 || version > 15) throw Invalid{};
186 const auto* fields = encoded->getIf<Value::Object>();
187 if (!fields || fields->size() != (version == 9 ? 4u : 9u)) throw Invalid{};
188 s.vegetationColors.layer = integer(*fields, "colorsLayer", 0);
189 s.vegetationColors.usePivotPosition = boolean(*fields, "colorsUsePivotPosition", false);
190 s.vegetationExtras.layer = integer(*fields, "extrasLayer", 0);
191 s.vegetationExtras.usePivotPosition = boolean(*fields, "extrasUsePivotPosition", false);
192 if (version >= 10) {
193 s.vegetationMotion.layer = integer(*fields, "motionLayer", 0);
194 s.vegetationVertex.layer = integer(*fields, "vertexLayer", 0);
195 s.vegetationVertex.globalSize = number(*fields, "globalSize", 1);
196 s.vegetationVertex.sizeFadeStart = number(*fields, "sizeFadeStart", 0);
197 s.vegetationVertex.sizeFadeEnd = number(*fields, "sizeFadeEnd", 100);
198 }
199 }
200 if (auto* encoded = field(o, "vegetationMotion")) {
201 if (version < 11 || version > 15) throw Invalid{};
202 const auto* motion = encoded->getIf<Value::Object>();
203 if (!motion || motion->size() != 21) throw Invalid{};
204 for (const auto& [key, value] : *motion)
205 if (key != "dynamicMode" && key != "rigidity" && key != "facing" && key != "bending" &&
206 key != "bendingSpeed" && key != "bendingScale" && key != "bendingVariation" && key != "branch" &&
207 key != "rolling" && key != "branchSpeed" && key != "branchScale" && key != "branchVariation" &&
208 key != "flutter" && key != "flutterSpeed" && key != "flutterScale" && key != "flutterVariation" &&
209 key != "interaction" && key != "interactionMask" && key != "perspectivePush" &&
210 key != "perspectiveNoise" && key != "perspectiveAngle")
211 throw Invalid{};
212 auto& m = s.vegetationMotion;
213 m.dynamicMode = number(*motion, "dynamicMode", 0);
214 m.rigidity = number(*motion, "rigidity", .5f);
215 m.facing = number(*motion, "facing", .5f);
216 m.bending = number(*motion, "bending", .2f);
217 m.bendingSpeed = number(*motion, "bendingSpeed", 2);
218 m.bendingScale = number(*motion, "bendingScale", 1);
219 m.bendingVariation = number(*motion, "bendingVariation", 0);
220 m.branch = number(*motion, "branch", .2f);
221 m.rolling = number(*motion, "rolling", .2f);
222 m.branchSpeed = number(*motion, "branchSpeed", 6);
223 m.branchScale = number(*motion, "branchScale", 3);
224 m.branchVariation = number(*motion, "branchVariation", 0);
225 m.flutter = number(*motion, "flutter", .2f);
226 m.flutterSpeed = number(*motion, "flutterSpeed", 20);
227 m.flutterScale = number(*motion, "flutterScale", 10);
228 m.flutterVariation = number(*motion, "flutterVariation", 0);
229 m.interaction = number(*motion, "interaction", 1);
230 m.interactionMask = number(*motion, "interactionMask", 1);
231 m.perspectivePush = number(*motion, "perspectivePush", 0);
232 m.perspectiveNoise = number(*motion, "perspectiveNoise", 0);
233 m.perspectiveAngle = number(*motion, "perspectiveAngle", 1);
234 if (m.dynamicMode < 0 || m.dynamicMode > 1 || m.rigidity < 0 || m.rigidity > 1 || m.facing < 0 ||
235 m.facing > 1 || m.interactionMask < 0 || m.interactionMask > 1)
236 throw Invalid{};
237 for (const float value :
238 {m.bending, m.bendingSpeed, m.bendingScale, m.bendingVariation, m.branch, m.rolling, m.branchSpeed,
239 m.branchScale, m.branchVariation, m.flutter, m.flutterSpeed, m.flutterScale, m.flutterVariation,
240 m.interaction, m.perspectivePush, m.perspectiveNoise, m.perspectiveAngle})
241 if (value < 0 || value > 1000000) throw Invalid{};
242 }
243 if (auto* encoded = field(o, "vegetationEmission")) {
244 if (version < 13) throw Invalid{};
245 const auto* emission = encoded->getIf<Value::Object>();
246 if (!emission || emission->size() != 4) throw Invalid{};
247 for (const auto& [key, value] : *emission)
248 if (key != "minimum" && key != "maximum" && key != "phase" && key != "global") throw Invalid{};
249 auto& e = s.vegetationEmission;
250 e.minimum = number(*emission, "minimum", 0);
251 e.maximum = number(*emission, "maximum", 1);
252 e.phase = number(*emission, "phase", 1);
253 e.global = number(*emission, "global", 1);
254 e.enabled = true;
255 }
256 if (auto* encoded = field(o, "vegetationGradient")) {
257 if (version < 14) throw Invalid{};
258 const auto* gradient = encoded->getIf<Value::Object>();
259 if (!gradient || gradient->size() != 4) throw Invalid{};
260 for (const auto& [key, value] : *gradient)
261 if (key != "colorOne" && key != "colorTwo" && key != "minimum" && key != "maximum") throw Invalid{};
262 auto& g = s.vegetationGradient;
263 vector(*gradient, "colorOne", g.colorOne);
264 vector(*gradient, "colorTwo", g.colorTwo);
265 g.minimum = number(*gradient, "minimum", 0);
266 g.maximum = number(*gradient, "maximum", 1);
267 g.enabled = true;
268 }
269 const auto shading = string(o, "shadingModel", "");
270 if (shading != "pbr" && shading != "unlit") throw Invalid{};
271 s.unlit = shading == "unlit";
272 auto mode = string(o, "surfaceMode", "opaque");
273 if (mode != "opaque" && mode != "masked" && mode != "transparent") throw Invalid{};
274 m.transparent = mode == "transparent";
275 m.masked = mode == "masked";
276 auto blend = string(o, "blendMode", "alpha");
277 if (blend != "alpha" && blend != "premultiplied") throw Invalid{};
278 m.blend = blend == "alpha" ? graphics::BlendMode::Alpha : graphics::BlendMode::Premultiplied;
279 m.doubleSided = boolean(o, "doubleSided", false);
280 const auto cullMode = string(o, "cullMode", m.doubleSided ? "none" : "back");
281 if (cullMode != "none" && cullMode != "back" && cullMode != "front") throw Invalid{};
282 if ((cullMode == "none") != m.doubleSided) throw Invalid{};
283 s.cullMode = cullMode == "none" ? graphics::PbrCullMode::None
284 : cullMode == "front" ? graphics::PbrCullMode::Front
286 s.alphaToCoverage = version >= 15 && boolean(o, "alphaToCoverage", false);
287 m.alphaCutoff = number(o, "alphaCutoff", .5f);
288 std::array<float, 4> color{1, 1, 1, 1};
289 vector(o, "baseColor", color);
290 for (std::size_t i = 0; i < color.size(); ++i)
291 if (color[i] < 0 || (i == 3 && color[i] > 1)) throw Invalid{};
292 m.color = {color[0], color[1], color[2], color[3]};
293 m.metallic = number(o, "metallic", 0);
294 m.roughness = number(o, "roughness", 1);
295 if (m.metallic < 0 || m.metallic > 1 || m.roughness < 0 || m.roughness > 1 || m.alphaCutoff < 0 ||
296 m.alphaCutoff > 1)
297 throw Invalid{};
298 vector(o, "emissive", s.emissive);
299 vector(o, "specularColorFactor", s.specularColor);
300 s.normalScale = number(o, "normalScale", 1);
301 s.occlusionStrength = number(o, "occlusionStrength", 1);
302 s.emissiveStrength = number(o, "emissiveStrength", 1);
303 s.specularFactor = number(o, "specularFactor", 1);
304 s.ior = number(o, "ior", 1.5f);
305 s.anisotropyStrength = number(o, "anisotropyStrength", 0);
306 s.anisotropyRotation = number(o, "anisotropyRotation", 0);
307 s.clearcoatFactor = number(o, "clearcoatFactor", 0);
308 s.clearcoatRoughness = number(o, "clearcoatRoughnessFactor", 0);
309 s.clearcoatNormalScale = number(o, "clearcoatNormalScale", 1);
310 s.albedoTextureStrength = number(o, "albedoTextureStrength", 1);
311 if (auto* encoded = field(o, "translucency")) {
312 const auto* trans = encoded->getIf<Value::Object>();
313 if (!trans) throw Invalid{};
314 for (const auto& [key, value] : *trans)
315 if (key != "color" && key != "intensity" && key != "strength" && key != "normalDistortion" &&
316 key != "scattering" && key != "direct" && key != "ambient" && key != "shadow" &&
317 key != "maskAmount" && key != "maskMinimum" && key != "maskMaximum")
318 throw Invalid{};
319 auto& t = s.translucency;
320 vector(*trans, "color", t.color);
321 t.intensity = number(*trans, "intensity", 0);
322 t.strength = number(*trans, "strength", 1);
323 t.normalDistortion = number(*trans, "normalDistortion", .5f);
324 t.scattering = number(*trans, "scattering", 2);
325 t.direct = number(*trans, "direct", .9f);
326 t.ambient = number(*trans, "ambient", .1f);
327 t.shadow = number(*trans, "shadow", .5f);
328 t.maskAmount = number(*trans, "maskAmount", 0);
329 t.maskMinimum = number(*trans, "maskMinimum", 0);
330 t.maskMaximum = number(*trans, "maskMaximum", 0);
331 }
332 auto normalMode = graphics::PbrNormalMode::TangentXYZ;
333 if (auto* encoded = field(o, "normalEncoding")) {
334 if (!encoded->isString()) throw Invalid{};
335 const auto& name = encoded->asString();
336 if (name == "tangent-xyz")
338 else if (name == "tve-rg")
340 else if (name == "tve-rag")
342 else if (name == "tve-ag")
344 else
345 throw Invalid{};
346 }
347 const bool colorMaskEnabled = o.contains("colorMask");
348 if (colorMaskEnabled) {
349 const auto* mask = o.at("colorMask").getIf<Value::Object>();
350 if (!mask || mask->size() != 3 || !mask->contains("secondary") || !mask->contains("minimum") ||
351 !mask->contains("maximum"))
352 throw Invalid{};
353 vector(*mask, "secondary", s.colorMaskSecondary);
354 s.colorMaskMin = number(*mask, "minimum", 0);
355 s.colorMaskMax = number(*mask, "maximum", 0);
356 }
357 constexpr const char* roles[] = {"baseColorTexture", "metallicRoughnessTexture", "normalTexture",
358 "occlusionTexture", "emissiveTexture", "specularTexture",
359 "specularColorTexture", "anisotropyTexture", "clearcoatTexture",
360 "clearcoatRoughnessTexture", "clearcoatNormalTexture"};
361 for (size_t i = 0; i < 11; i++) {
362 std::string role = roles[i];
363 auto* image = field(o, role);
364 if (!image) continue;
365 if (!image->isString()) throw Invalid{};
366 auto ref = AssetRef::parse(image->asString());
367 if (!ref) return Result<CookedMaterial>::failure(ref.status());
368 m.images[i] = std::move(ref).takeValue();
369 auto& b = s.textures[i];
370 b.srgbDecode = false;
371 b.texcoord = integer(o, role + "TexCoord", 0);
372 if (auto* transform = field(o, role + "Transform")) {
373 auto* t = transform->getIf<Value::Object>();
374 if (!t) throw Invalid{};
375 vector(*t, "offset", b.offset);
376 vector(*t, "scale", b.scale);
377 b.rotation = number(*t, "rotation", 0);
378 b.texcoord = integer(*t, "texCoord", b.texcoord);
379 }
380 if (auto* sampler = field(o, role + "Sampler")) {
381 auto* t = sampler->getIf<Value::Object>();
382 if (!t) throw Invalid{};
383 b.wrapS = integer(*t, "wrapS", 10497);
384 b.wrapT = integer(*t, "wrapT", 10497);
385 b.minFilter = integer(*t, "minFilter", 9987);
386 b.magFilter = integer(*t, "magFilter", 9729);
387 }
388 }
389 constexpr const char* detailRoles[] = {"detailAlbedoTexture", "detailNormalTexture", "detailMaskTexture"};
390 for (size_t i = 0; i < 3; ++i) {
391 const std::string role = detailRoles[i];
392 auto* image = field(o, role);
393 if (!image) continue;
394 if (version < 8 || !image->isString()) throw Invalid{};
395 auto ref = AssetRef::parse(image->asString());
396 if (!ref) return Result<CookedMaterial>::failure(ref.status());
397 m.detailImages[i] = std::move(ref).takeValue();
398 auto& b = s.vegetationDetail.textures[i];
399 b.srgbDecode = i == 0;
400 if (auto* sampler = field(o, role + "Sampler")) {
401 auto* t = sampler->getIf<Value::Object>();
402 if (!t) throw Invalid{};
403 b.wrapS = integer(*t, "wrapS", 10497);
404 b.wrapT = integer(*t, "wrapT", 10497);
405 b.minFilter = integer(*t, "minFilter", 9987);
406 b.magFilter = integer(*t, "magFilter", 9729);
407 }
408 }
409 const auto transMask = s.translucency.maskAmount;
410 if (!std::isfinite(transMask) || transMask < 0 || transMask > 1) throw Invalid{};
411 s.translucency.maskAmount = 0;
412 const auto detailValue = s.vegetationDetail.value;
413 s.vegetationDetail.value = 0;
414 auto validated = graphics::validatePbrSurface(s);
415 if (!validated) return Result<CookedMaterial>::failure(validated.status());
416 // This owning CPU definition has no GPU resources yet. Validate logical identity
417 // here; setMesh3DPbrSurface validates the resolved texture after upload.
418 if (colorMaskEnabled && !m.images[1]) throw Invalid{};
419 if (s.translucency.intensity > 0 && transMask > 0 && !m.images[1]) throw Invalid{};
420 s.translucency.maskAmount = transMask;
421 if (detailValue > 0 && (!m.detailImages[0] || !m.detailImages[1])) throw Invalid{};
422 s.vegetationDetail.value = detailValue;
423 if (normalMode != graphics::PbrNormalMode::TangentXYZ &&
424 (!m.images[2] || s.normalScale < -8.f || s.normalScale > 8.f))
425 throw Invalid{};
426 if (s.vegetationEmission.enabled && !m.images[4]) throw Invalid{};
427 s.normalMode = normalMode;
428 s.colorMaskEnabled = colorMaskEnabled;
429 if (m.vegetationSurface) {
430 auto& vegetation = *m.vegetationSurface;
431 vegetation.albedo = {m.color.r, m.color.g, m.color.b, m.color.a};
432 vegetation.emission = s.emissive;
433 vegetation.roughness = m.roughness;
434 vegetation.alphaCutoff = m.alphaCutoff;
435 }
436 return Result<CookedMaterial>::success(std::move(m));
437 } catch (const Invalid&) {
439 DiagnosticCode::InvalidArgument, "invalid cooked PBR material", {}, {}, "asset.graphics.material"));
440 }
441}
442} // namespace eve::asset_graphics::detail
double value
Value::Object payload
const std::string & s
int mask
tensor::Graph g
Definition GpuGraph.cpp:7
std::uint32_t key
vk::UniqueSampler sampler
vk::UniqueImage image
glm::vec3 n
Definition Grass.cpp:63
float v
float blend
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::vector< std::string > fields
Definition PlayHost.cpp:111
float d
int detail
float t
double number
JSON-free versioned runtime metadata codec.
bool boolean
std::string string
Json object
int caps
Definition TreeMesh.cpp:162
float m[16]
Stable asset identity backed by PersistentId.
Definition ResourceRef.h:53
static Result< AssetRef > parse(std::string_view text)
Parse an asset://<canonical UUID> reference.
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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
Immutable reader retaining an admitted pack by shared ownership.
Result< RuntimeAssetPayload > read(const AssetRef &asset, std::string_view expectedType, const EvpackCapabilities &capabilities, std::uint64_t maximumDecodedBytes) const
Resolve, type-check, variant-select and decode a canonical runtime asset.
Result< CookedMaterial > readCookedMaterial(const asset::EvpackResourceReader &reader, const AssetRef &ref, const asset::EvpackCapabilities &caps)
Decode material schemas 5 through 9 without allocating graphics resources. Unknown additive fields ar...
Result< Value > decodeRuntimeDefinition(std::span< const std::uint8_t > bytes, const RuntimeDefinitionLimits &limits)
Decode a bounded EVDEF\0\1 metadata tree without a JSON parser.
PbrVegetationBackfaceNormalMode
TVE tangent-space normal treatment for back-facing fragments.
Definition PbrSurface.h:18
Result< void > validatePbrSurface(const PbrSurface &s)
Validate finite material factors and sampler enums without changing the input.
Definition PbrSurface.cpp:4
@ TypeMismatch
A stable reference resolved to a different canonical domain type.
Actual runtime device capabilities used for variant selection.
Definition Evpack.h:92
Validated material value; image references own identity, GPU bindings initially empty.
Owning surface inputs, supplied afresh to avoid accumulating tint/wetness across frames....
std::array< float, 3 > vertexOcclusionColor
PbrVegetationBackfaceNormalMode backfaceNormalMode
glm::vec4 color