14 snapshot =
target.snapshotValue();
16 if (!
root)
return nullptr;
17 const auto found =
root->find(
"properties");
27template <
class T,
class Resolver>
28Result<T*> resolveAsset(
const std::string& asset, Resolver&& resolver) {
29 if (asset.empty())
return eve::editing::applied<T*>(
nullptr);
30 return resolver(asset);
37 const IMaterialRuntimeAssetResolver*
assets =
nullptr;
40Renderable3DMaterialRuntimeSink::Renderable3DMaterialRuntimeSink(
42 : impl_(
std::make_unique<
Impl>()) {
43 impl_->handle = ecs::handle_of(renderable);
48 if (
const auto* real =
value.getIf<
double>())
return *real;
49 if (
const auto* integer =
value.getIf<std::int64_t>())
return static_cast<double>(*integer);
53Renderable3DMaterialRuntimeSink::~Renderable3DMaterialRuntimeSink() =
default;
56 const MaterialDocumentTarget& candidate) {
58 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId(
"editor.material.runtime-input"),
59 "Material asset resolver is required");
62 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId(
"editor.material.runtime-stale"),
63 "Renderable3D handle is missing or stale");
64 const auto diagnostics = candidate.validate();
66 if (diagnostic.severity() == DiagnosticSeverity::Error)
72 return eve::editing::failed<void>(EditorStatus::Failed, RuleId(
"editor.material.runtime-properties"),
73 "Material properties are unavailable");
74 const bool vegetationAlpha = *value<bool>(*
values,
"vegetation.alpha.enabled");
75 const bool vegetationEmission = *value<bool>(*
values,
"vegetation.emission.enabled");
76 const bool vegetationGradient = *value<bool>(*
values,
"vegetation.gradient.enabled");
77 const bool vegetationTranslucency = *value<bool>(*
values,
"vegetation.translucency.enabled");
78 const bool vegetationColor = *value<bool>(*
values,
"vegetation.color.enabled");
79 const bool vegetationDetail = *value<bool>(*
values,
"vegetation.detail.enabled");
80 const bool vegetationExtras = *value<bool>(*
values,
"vegetation.extras.enabled");
81 const bool vegetationColors = *value<bool>(*
values,
"vegetation.colors.enabled");
82 const bool vegetationVertex = *value<bool>(*
values,
"vegetation.vertex.enabled");
83 const bool vegetationMotion = *value<std::string>(*
values,
"vegetation.motion.mode") ==
"object";
84 const bool extendedTextures = !value<std::string>(*
values,
"textures.orm")->empty() ||
85 !value<std::string>(*
values,
"textures.emissive")->empty();
86 const bool vegetationExtended =
87 vegetationAlpha || vegetationEmission || vegetationGradient || vegetationTranslucency || vegetationColor ||
88 vegetationDetail || vegetationExtras || vegetationColors || vegetationVertex || vegetationMotion ||
91 if (*value<std::string>(*
values,
"shading.model") !=
"pbr" ||
93 *value<std::string>(*
values,
"surface.blend") !=
"alpha" ||
94 *value<bool>(*
values,
"surface.double-sided"))
95 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId(
"editor.material.runtime-legacy-surface"),
96 "Legacy Renderable3D supports PBR opaque single-sided materials only");
98 std::array<graphics::Texture*, 14> textures{};
99 constexpr const char* texturePaths[] = {
100 "textures.albedo",
"textures.normal",
"textures.height",
101 "textures.orm",
"textures.emissive",
"vegetation.alpha.noise",
102 "vegetation.detail.albedo",
"vegetation.detail.normal",
"vegetation.detail.mask",
103 "vegetation.extras.texture",
"vegetation.colors.texture",
"vegetation.vertex.texture",
104 "vegetation.motion.texture",
"vegetation.motion.noise"};
105 const std::array<bool, 14> textureEnabled = {
true,
119 for (std::size_t i = 0; i < textures.size(); ++i) {
120 if (!textureEnabled[i])
continue;
121 auto resolved = resolveAsset<graphics::Texture>(
122 *value<std::string>(*
values, texturePaths[i]),
123 [&](
const std::string& asset) {
return impl_->assets->resolveTexture(asset); });
125 textures[i] = resolved.value();
127 auto shader = resolveAsset<graphics::Shader>(
128 *value<std::string>(*
values,
"textures.shader"),
129 [&](
const std::string& asset) {
return impl_->assets->resolveShader(asset); });
132 graphics::Material* runtimeMaterial = renderable->getMaterial();
133 if (vegetationExtended && !runtimeMaterial)
134 return eve::editing::failed<void>(EditorStatus::Unsupported,
135 RuleId(
"editor.material.runtime-vegetation-material"),
136 "Extended PBR vegetation editing requires a Material bound to Renderable3D");
137 if (runtimeMaterial && (vegetationExtended || runtimeMaterial->hasPbrSurface())) {
138 graphics::PbrSurface surface = runtimeMaterial->pbrSurface();
139 surface.vegetationAlpha.enabled = vegetationAlpha;
140 surface.vegetationAlpha.noise = textures[5];
141 surface.vegetationAlpha.global = float(*value<double>(*
values,
"vegetation.alpha.global"));
142 surface.vegetationAlpha.variation = float(*value<double>(*
values,
"vegetation.alpha.variation"));
143 surface.vegetationAlpha.glancing = float(*value<double>(*
values,
"vegetation.alpha.glancing"));
144 surface.vegetationAlpha.camera = float(*value<double>(*
values,
"vegetation.alpha.camera"));
145 surface.vegetationAlpha.constant = float(*value<double>(*
values,
"vegetation.alpha.constant"));
146 surface.vegetationAlpha.cameraFadeMin = float(*value<double>(*
values,
"vegetation.alpha.camera-min"));
147 surface.vegetationAlpha.cameraFadeMax = float(*value<double>(*
values,
"vegetation.alpha.camera-max"));
148 surface.vegetationAlpha.noiseTiling = float(*value<double>(*
values,
"vegetation.alpha.noise-tiling"));
149 surface.vegetationAlpha.detailFade = *value<bool>(*
values,
"vegetation.alpha.detail-fade");
150 surface.vegetationEmission.enabled = vegetationEmission;
151 surface.vegetationEmission.minimum = float(*value<double>(*
values,
"vegetation.emission.minimum"));
152 surface.vegetationEmission.maximum = float(*value<double>(*
values,
"vegetation.emission.maximum"));
153 surface.vegetationEmission.phase = float(*value<double>(*
values,
"vegetation.emission.phase"));
154 surface.vegetationEmission.global = float(*value<double>(*
values,
"vegetation.emission.global"));
155 surface.vegetationGradient.enabled = vegetationGradient;
156 const auto& gradientOne = *value<EditorValue::Array>(*
values,
"vegetation.gradient.color-one");
157 const auto& gradientTwo = *value<EditorValue::Array>(*
values,
"vegetation.gradient.color-two");
158 for (std::size_t i = 0; i < 3; ++i) {
159 surface.vegetationGradient.colorOne[i] = float(
number(gradientOne[i]));
160 surface.vegetationGradient.colorTwo[i] = float(
number(gradientTwo[i]));
162 surface.vegetationGradient.minimum = float(*value<double>(*
values,
"vegetation.gradient.minimum"));
163 surface.vegetationGradient.maximum = float(*value<double>(*
values,
"vegetation.gradient.maximum"));
165 ormBinding.texture = textures[3];
166 ormBinding.srgbDecode =
false;
168 emissiveBinding.texture = textures[4];
169 emissiveBinding.srgbDecode =
true;
170 surface.translucency.intensity =
171 vegetationTranslucency ? float(*value<double>(*
values,
"vegetation.translucency.intensity")) : 0.f;
172 const auto& transColor = *value<EditorValue::Array>(*
values,
"vegetation.translucency.color");
173 for (std::size_t i = 0; i < 3; ++i) surface.translucency.color[i] =
float(
number(transColor[i]));
174 surface.translucency.strength = float(*value<double>(*
values,
"vegetation.translucency.strength"));
175 surface.translucency.normalDistortion =
176 float(*value<double>(*
values,
"vegetation.translucency.normal-distortion"));
177 surface.translucency.scattering = float(*value<double>(*
values,
"vegetation.translucency.scattering"));
178 surface.translucency.direct = float(*value<double>(*
values,
"vegetation.translucency.direct"));
179 surface.translucency.ambient = float(*value<double>(*
values,
"vegetation.translucency.ambient"));
180 surface.translucency.shadow = float(*value<double>(*
values,
"vegetation.translucency.shadow"));
181 surface.translucency.maskAmount =
182 vegetationTranslucency ? float(*value<double>(*
values,
"vegetation.translucency.mask-amount")) : 0.f;
183 surface.translucency.globalIntensity = float(*value<double>(*
values,
"vegetation.translucency.global"));
184 surface.translucency.overlay = float(*value<double>(*
values,
"vegetation.translucency.overlay"));
185 surface.translucency.maskMinimum =
186 float(*value<double>(*
values,
"vegetation.translucency.mask-minimum"));
187 surface.translucency.maskMaximum =
188 float(*value<double>(*
values,
"vegetation.translucency.mask-maximum"));
189 surface.vegetationColor = {};
190 if (vegetationColor) {
191 const auto&
field = *value<EditorValue::Array>(*
values,
"vegetation.color.field");
192 const auto& overlay = *value<EditorValue::Array>(*
values,
"vegetation.color.overlay-color");
193 const auto& occlusion =
194 *value<EditorValue::Array>(*
values,
"vegetation.color.vertex-occlusion-color");
195 for (std::size_t i = 0; i < 4; ++i) surface.vegetationColor.fieldColor[i] =
float(
number(field[i]));
196 for (std::size_t i = 0; i < 3; ++i) {
197 surface.vegetationColor.overlayColor[i] = float(
number(overlay[i]));
198 surface.vegetationColor.vertexOcclusionColor[i] = float(
number(occlusion[i]));
200 const auto scalar = [&](
const char*
name) {
201 return float(*value<double>(*
values, (std::string(
"vegetation.color.") +
name).c_str()));
203 surface.vegetationColor.overlay = scalar(
"overlay");
204 surface.vegetationColor.wetness = scalar(
"wetness");
205 surface.vegetationColor.overlayVariation = scalar(
"overlay-variation");
206 surface.vegetationColor.overlayProjection = scalar(
"overlay-projection");
207 surface.vegetationColor.vertexOcclusionAlpha = scalar(
"vertex-occlusion-alpha");
208 surface.vegetationColor.overlayNormalScale = scalar(
"overlay-normal");
209 surface.vegetationColor.wetnessNormalScale = scalar(
"wetness-normal");
210 surface.vegetationColor.overlaySmoothness = scalar(
"overlay-smoothness");
211 surface.vegetationColor.wetnessContrast = scalar(
"wetness-contrast");
212 surface.vegetationColor.overlaySubsurface = scalar(
"overlay-subsurface");
213 surface.vegetationColor.colorsCoverage = scalar(
"colors-coverage");
214 surface.vegetationColor.colorsIntensity = scalar(
"colors-intensity");
215 surface.vegetationColor.colorsMask = scalar(
"colors-mask");
216 surface.vegetationColor.colorsVariation = scalar(
"colors-variation");
217 surface.vegetationColor.globalColorMaskMinimum = scalar(
"color-mask-minimum");
218 surface.vegetationColor.globalColorMaskMaximum = scalar(
"color-mask-maximum");
219 surface.vegetationColor.globalOverlayMaskMinimum = scalar(
"overlay-mask-minimum");
220 surface.vegetationColor.globalOverlayMaskMaximum = scalar(
"overlay-mask-maximum");
221 surface.vegetationColor.globalAlphaThresholdOffset = scalar(
"alpha-threshold-offset");
222 surface.vegetationColor.vertexOcclusionMinimum = scalar(
"vertex-occlusion-minimum");
223 surface.vegetationColor.vertexOcclusionMaximum = scalar(
"vertex-occlusion-maximum");
224 surface.vegetationColor.invertVertexOcclusion =
225 *value<bool>(*
values,
"vegetation.color.invert-vertex-occlusion");
226 surface.vegetationColor.invertVertexOcclusionColors =
227 *value<bool>(*
values,
"vegetation.color.invert-vertex-occlusion-colors");
228 const auto& backface = *value<std::string>(*
values,
"vegetation.color.backface-normal");
229 surface.vegetationColor.backfaceNormalMode =
234 surface.vegetationDetail = {};
235 if (vegetationDetail) {
236 auto&
detail = surface.vegetationDetail;
237 detail.textures[0].texture = textures[6];
238 detail.textures[0].srgbDecode =
true;
239 detail.textures[1].texture = textures[7];
240 detail.textures[1].srgbDecode =
false;
241 detail.textures[2].texture = textures[8];
242 detail.textures[2].srgbDecode =
false;
243 const auto&
color = *value<EditorValue::Array>(*
values,
"vegetation.detail.color");
244 const auto& colorTwo = *value<EditorValue::Array>(*
values,
"vegetation.detail.color-two");
245 const auto& uvScale = *value<EditorValue::Array>(*
values,
"vegetation.detail.uv-scale");
246 const auto& uvOffset = *value<EditorValue::Array>(*
values,
"vegetation.detail.uv-offset");
247 for (std::size_t i = 0; i < 4; ++i) {
251 for (std::size_t i = 0; i < 2; ++i) {
255 const auto detailScalar = [&](
const char*
name) {
256 return float(*value<double>(*
values, (std::string(
"vegetation.detail.") +
name).c_str()));
258 detail.value = detailScalar(
"value");
259 detail.normalValue = detailScalar(
"normal-value");
260 detail.normalBlendValue = detailScalar(
"normal-blend");
261 detail.albedoValue = detailScalar(
"albedo-value");
262 detail.metallicValue = detailScalar(
"metallic");
263 detail.occlusionValue = detailScalar(
"occlusion");
264 detail.smoothnessValue = detailScalar(
"smoothness");
265 detail.blendMinimum = detailScalar(
"blend-minimum");
266 detail.blendMaximum = detailScalar(
"blend-maximum");
267 detail.maskMinimum = detailScalar(
"mask-minimum");
268 detail.maskMaximum = detailScalar(
"mask-maximum");
269 detail.meshMinimum = detailScalar(
"mesh-minimum");
270 detail.meshMaximum = detailScalar(
"mesh-maximum");
271 detail.inverseUvScale = *value<bool>(*
values,
"vegetation.detail.inverse-uv-scale");
272 const auto& uvMode = *value<std::string>(*
values,
"vegetation.detail.uv-mode");
273 detail.uvMode = uvMode ==
"world" ? 2u : uvMode ==
"detail" ? 1u : 0
u;
274 detail.colorMode = *value<std::string>(*
values,
"vegetation.detail.color-mode") ==
"variation";
275 detail.blendMode = *value<std::string>(*
values,
"vegetation.detail.blend-mode") ==
"replace";
276 detail.alphaMode = *value<std::string>(*
values,
"vegetation.detail.alpha-mode") ==
"detail";
277 detail.maskMode = *value<std::string>(*
values,
"vegetation.detail.mask-mode") ==
"inverse";
278 detail.meshMode = *value<std::string>(*
values,
"vegetation.detail.mesh-mode") ==
"inverse";
280 const auto publishField = [&](
const char*
group,
auto&
field, graphics::Texture* texture) {
281 const std::string prefix = std::string(
"vegetation.") +
group;
282 field.texture = texture;
283 field.layer = uint32_t(*value<std::int64_t>(*
values, (prefix +
".layer").c_str()));
284 field.usePivotPosition = *value<bool>(*
values, (prefix +
".use-pivot-position").c_str());
285 const auto& fallback = *value<EditorValue::Array>(*
values, (prefix +
".fallback").c_str());
286 const auto& coords = *value<EditorValue::Array>(*
values, (prefix +
".coords").c_str());
287 for (std::size_t i = 0; i < 4; ++i) {
291 for (std::size_t i = 0; i <
field.usage.size(); ++i) {
292 const std::string
path = prefix +
".usage-" + std::to_string(i);
296 surface.vegetationExtras = {};
297 if (vegetationExtras) publishField(
"extras", surface.vegetationExtras, textures[9]);
298 surface.vegetationColors = {};
299 if (vegetationColors) publishField(
"colors", surface.vegetationColors, textures[10]);
300 surface.vegetationVertex = {};
301 if (vegetationVertex) {
302 auto& vertex = surface.vegetationVertex;
303 vertex.texture = textures[11];
304 vertex.layer = uint32_t(*value<std::int64_t>(*
values,
"vegetation.vertex.layer"));
305 const auto& fallback = *value<EditorValue::Array>(*
values,
"vegetation.vertex.fallback");
306 const auto& coords = *value<EditorValue::Array>(*
values,
"vegetation.vertex.coords");
307 for (std::size_t i = 0; i < 4; ++i) {
308 vertex.fallback[i] = float(
number(fallback[i]));
309 vertex.coords[i] = float(
number(coords[i]));
311 for (std::size_t i = 0; i < vertex.usage.size(); ++i) {
312 const std::string
path =
"vegetation.vertex.usage-" + std::to_string(i);
313 vertex.usage[i] = float(*value<double>(*
values,
path.c_str()));
315 vertex.globalSize = float(*value<double>(*
values,
"vegetation.vertex.global-size"));
316 vertex.sizeFadeStart = float(*value<double>(*
values,
"vegetation.vertex.size-fade-start"));
317 vertex.sizeFadeEnd = float(*value<double>(*
values,
"vegetation.vertex.size-fade-end"));
318 vertex.distanceFadeBias = float(*value<double>(*
values,
"vegetation.vertex.distance-fade-bias"));
319 const auto&
source = *value<std::string>(*
values,
"vegetation.vertex.source");
320 vertex.source =
source ==
"gpu-fields"
322 :
source ==
"cpu-deformed"
326 surface.vegetationMotion = {};
327 if (vegetationMotion) {
328 auto& motion = surface.vegetationMotion;
329 motion.texture = textures[12];
330 motion.noise = textures[13];
332 motion.layer = uint32_t(*value<std::int64_t>(*
values,
"vegetation.motion.layer"));
333 const auto& fallback = *value<EditorValue::Array>(*
values,
"vegetation.motion.fallback");
334 const auto& coords = *value<EditorValue::Array>(*
values,
"vegetation.motion.coords");
335 const auto&
direction = *value<EditorValue::Array>(*
values,
"vegetation.motion.global-direction");
336 const auto&
origin = *value<EditorValue::Array>(*
values,
"vegetation.motion.world-origin");
337 for (std::size_t i = 0; i < 4; ++i) {
338 motion.fallback[i] = float(
number(fallback[i]));
339 motion.coords[i] = float(
number(coords[i]));
341 for (std::size_t i = 0; i < 2; ++i) motion.globalDirection[i] =
float(
number(
direction[i]));
342 for (std::size_t i = 0; i < 3; ++i) motion.worldOrigin[i] =
float(
number(
origin[i]));
343 for (std::size_t i = 0; i < motion.usage.size(); ++i) {
344 const std::string
path =
"vegetation.motion.usage-" + std::to_string(i);
345 motion.usage[i] = float(*value<double>(*
values,
path.c_str()));
347 const auto scalar = [&](
const char*
name) {
348 const std::string
path = std::string(
"vegetation.motion.") +
name;
349 return float(*value<double>(*
values,
path.c_str()));
351 motion.time = *value<double>(*
values,
"vegetation.motion.time");
352 motion.dynamicMode = scalar(
"dynamic-mode");
353 motion.rigidity = scalar(
"rigidity");
354 motion.facing = scalar(
"facing");
355 motion.bending = scalar(
"bending");
356 motion.bendingSpeed = scalar(
"bending-speed");
357 motion.bendingScale = scalar(
"bending-scale");
358 motion.bendingVariation = scalar(
"bending-variation");
359 motion.branch = scalar(
"branch");
360 motion.rolling = scalar(
"rolling");
361 motion.branchSpeed = scalar(
"branch-speed");
362 motion.branchScale = scalar(
"branch-scale");
363 motion.branchVariation = scalar(
"branch-variation");
364 motion.flutter = scalar(
"flutter");
365 motion.flutterSpeed = scalar(
"flutter-speed");
366 motion.flutterScale = scalar(
"flutter-scale");
367 motion.flutterVariation = scalar(
"flutter-variation");
368 motion.globalBending = scalar(
"global-bending");
369 motion.globalBranch = scalar(
"global-branch");
370 motion.globalFlutter = scalar(
"global-flutter");
371 motion.noiseTiling = scalar(
"noise-tiling");
372 motion.interaction = scalar(
"interaction");
373 motion.interactionMask = scalar(
"interaction-mask");
374 motion.fadeDistance = scalar(
"fade-distance");
375 motion.perspectivePush = scalar(
"perspective-push");
376 motion.perspectiveNoise = scalar(
"perspective-noise");
377 motion.perspectiveAngle = scalar(
"perspective-angle");
379 auto published = runtimeMaterial->setPbrSurface(surface);
381 return eve::editing::failed<void>(EditorStatus::Rejected,
382 RuleId(
"editor.material.runtime-vegetation-invalid"),
383 published.error()->message());
386 const auto&
tint = *value<EditorValue::Array>(*
values,
"shading.tint");
387 if (runtimeMaterial) {
388 runtimeMaterial->setAlbedoTexture(textures[0]);
389 runtimeMaterial->setNormalTexture(textures[1]);
390 runtimeMaterial->setHeightTexture(textures[2]);
391 runtimeMaterial->setShader(
shader.value());
392 runtimeMaterial->setTint(
static_cast<float>(
number(
tint[0])),
static_cast<float>(
number(
tint[1])),
394 runtimeMaterial->setMetallic(
static_cast<float>(*value<double>(*
values,
"shading.metallic")));
395 runtimeMaterial->setRoughness(
static_cast<float>(*value<double>(*
values,
"shading.roughness")));
396 runtimeMaterial->setParallax(
static_cast<float>(*value<double>(*
values,
"parallax.scale")));
397 runtimeMaterial->setReceiveLight(*value<bool>(*
values,
"lighting.receive"));
398 runtimeMaterial->setCastShadow(*value<bool>(*
values,
"shadow.cast"));
399 runtimeMaterial->setReceiveShadow(*value<bool>(*
values,
"shadow.receive"));
401 runtimeMaterial->setAlphaCutoff(
static_cast<float>(*value<double>(*
values,
"surface.alpha-cutoff")));
402 runtimeMaterial->setDepthWrite(*value<bool>(*
values,
"surface.depth-write"));
403 runtimeMaterial->setDoubleSided(*value<bool>(*
values,
"surface.double-sided"));
404 return eve::editing::applied<void>();
406 renderable->setTexture(textures[0]);
407 renderable->setNormalTexture(textures[1]);
408 renderable->setHeightTexture(textures[2]);
409 renderable->setShader(
shader.value());
410 renderable->setTint(
static_cast<float>(
number(
tint[0])),
414 renderable->setMetallic(
static_cast<float>(*value<double>(*
values,
"shading.metallic")));
415 renderable->setRoughness(
static_cast<float>(*value<double>(*
values,
"shading.roughness")));
416 renderable->setParallax(
static_cast<float>(*value<double>(*
values,
"parallax.scale")));
417 renderable->setReceiveLight(*value<bool>(*
values,
"lighting.receive"));
418 renderable->setCastShadow(*value<bool>(*
values,
"shadow.cast"));
419 renderable->setReceiveShadow(*value<bool>(*
values,
"shadow.receive"));
420 return eve::editing::applied<void>();
building::EdgeCurveGroup group
std::map< std::string, Var > values
std::map< std::string, PropertyRule > properties
RoadLaneDirection direction
const UnitySourceAsset & source
Move-only operation result carrying either a value or Status.
static Result failure(Status status)
Construct a failed result from a structured status.
Structured status and zero or more diagnostics for an operation.
Deterministic owning value tree shared by authoring hosts.
std::map< std::string, Value > Object
EVENGINE_API_BACKENDS public API.
editing::Diagnostic EditorDiagnostic
editing::Value EditorValue
const EditorValue * field(const EditorValue &value, const char *name)
const IMaterialRuntimeAssetResolver * assets