MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ decode()

bool MayaFlux::Nexus::StateDecoder::decode ( Fabric fabric,
const std::string &  base_path 
)

Decode and apply to fabric.

Parameters
fabricTarget fabric. Must already contain entities with ids matching the schema.
base_pathPath stem without extension, same value passed to StateEncoder::encode.
Returns
True on success. Partial patches (some ids missing) still return true; failures are logged.

Definition at line 233 of file StateDecoder.cpp.

234{
235 m_last_error.clear();
236 m_patched_count = 0;
237 m_missing_count = 0;
238
239 const std::string json_path = base_path + ".json";
240 const std::string exr_path = base_path + ".exr";
241
242 IO::JSONSerializer ser;
243 auto schema_opt = ser.read<FabricSchema>(json_path);
244 if (!schema_opt) {
245 m_last_error = "Failed to load schema: " + ser.last_error();
247 return false;
248 }
249 const auto& schema = *schema_opt;
250
251 if (schema.version < 2 || schema.version > 4) {
252 m_last_error = "Unsupported schema version: " + std::to_string(schema.version);
254 return false;
255 }
256
257 if (schema.entities.empty()) {
258 m_last_error = "Schema contains no entities: " + json_path;
260 return false;
261 }
262
263 const uint32_t expected_rows = schema.version >= 4 ? 5 : k_exr_rows;
264 auto pv_opt = load_exr(exr_path, static_cast<uint32_t>(schema.entities.size()), expected_rows, m_last_error);
265 if (!pv_opt) {
267 return false;
268 }
269 auto& pv = *pv_opt;
270 const auto* pixels = pv.image.as_float();
271 const uint32_t width = pv.width;
272 const auto& r = schema.ranges;
273
274 for (size_t i = 0; i < schema.entities.size(); ++i) {
275 const auto& entry = schema.entities[i];
276
277 if (!kind_known(entry.kind)) {
279 "StateDecoder: unknown kind '{}' for id {}, skipping", entry.kind, entry.id);
281 continue;
282 }
283
284 const size_t row0 = (static_cast<size_t>(0) * width + i) * k_channels;
285 const size_t row1 = (static_cast<size_t>(1) * width + i) * k_channels;
286 const size_t row2 = (static_cast<size_t>(2) * width + i) * k_channels;
287
288 const glm::vec3 position {
289 denormalize((*pixels)[row0 + 0], r.pos_x),
290 denormalize((*pixels)[row0 + 1], r.pos_y),
291 denormalize((*pixels)[row0 + 2], r.pos_z),
292 };
293
294 switch (parse_kind(entry.kind)) {
296 auto e = fabric.get_emitter(entry.id);
297 if (!e) {
299 "StateDecoder: id {} not found as Emitter, skipping", entry.id);
301 continue;
302 }
303 if (!entry.influence_fn_name.empty() && e->fn_name() != entry.influence_fn_name) {
305 "StateDecoder: Emitter {} fn_name mismatch: schema='{}' live='{}'",
306 entry.id, entry.influence_fn_name, e->fn_name());
307 }
308 e->set_position(position);
309 e->set_intensity(denormalize((*pixels)[row0 + 3], r.intensity));
310 if (entry.color) {
311 e->set_color(glm::vec3 {
312 denormalize((*pixels)[row1 + 0], r.color_r),
313 denormalize((*pixels)[row1 + 1], r.color_g),
314 denormalize((*pixels)[row1 + 2], r.color_b),
315 });
316 }
317 if (entry.size) {
318 e->set_size(denormalize((*pixels)[row1 + 3], r.size));
319 }
320 e->set_radius(denormalize((*pixels)[row2 + 0], r.radius));
321 break;
322 }
324 auto s = fabric.get_sensor(entry.id);
325 if (!s) {
327 "StateDecoder: id {} not found as Sensor, skipping", entry.id);
329 continue;
330 }
331 if (!entry.perception_fn_name.empty() && s->fn_name() != entry.perception_fn_name) {
333 "StateDecoder: Sensor {} fn_name mismatch: schema='{}' live='{}'",
334 entry.id, entry.perception_fn_name, s->fn_name());
335 }
336 s->set_position(position);
337 s->set_query_radius(denormalize((*pixels)[row2 + 1], r.query_radius));
338 break;
339 }
340 case Fabric::Kind::Agent: {
341 auto a = fabric.get_agent(entry.id);
342 if (!a) {
344 "StateDecoder: id {} not found as Agent, skipping", entry.id);
346 continue;
347 }
348 if (!entry.perception_fn_name.empty() && a->perception_fn_name() != entry.perception_fn_name) {
350 "StateDecoder: Agent {} perception_fn_name mismatch: schema='{}' live='{}'",
351 entry.id, entry.perception_fn_name, a->perception_fn_name());
352 }
353 if (!entry.influence_fn_name.empty() && a->influence_fn_name() != entry.influence_fn_name) {
355 "StateDecoder: Agent {} influence_fn_name mismatch: schema='{}' live='{}'",
356 entry.id, entry.influence_fn_name, a->influence_fn_name());
357 }
358 a->set_position(position);
359 a->set_intensity(denormalize((*pixels)[row0 + 3], r.intensity));
360 if (entry.color) {
361 a->set_color(glm::vec3 {
362 denormalize((*pixels)[row1 + 0], r.color_r),
363 denormalize((*pixels)[row1 + 1], r.color_g),
364 denormalize((*pixels)[row1 + 2], r.color_b),
365 });
366 }
367 if (entry.size) {
368 a->set_size(denormalize((*pixels)[row1 + 3], r.size));
369 }
370 a->set_radius(denormalize((*pixels)[row2 + 0], r.radius));
371 a->set_query_radius(denormalize((*pixels)[row2 + 1], r.query_radius));
372 break;
373 }
374 }
375
377 }
378
380 "StateDecoder: patched {} entities ({} missing) from {} + {}",
381 m_patched_count, m_missing_count, exr_path, json_path);
382
383 return true;
384}
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
size_t a
uint32_t width
const std::vector< float > * pixels
glm::vec3 position
@ FileIO
Filesystem I/O operations.
@ Runtime
General runtime operations (default fallback)
@ Nexus
Spatial indexing and scheduling for user-defined behaviour.

References a, MayaFlux::Nexus::Fabric::Agent, MayaFlux::Nexus::Fabric::Emitter, MayaFlux::Journal::FileIO, MayaFlux::Nexus::Fabric::get_agent(), MayaFlux::Nexus::Fabric::get_emitter(), MayaFlux::Nexus::Fabric::get_sensor(), MayaFlux::IO::JSONSerializer::last_error(), m_last_error, m_missing_count, m_patched_count, MF_ERROR, MF_INFO, MF_WARN, MayaFlux::Journal::Nexus, pixels, position, MayaFlux::IO::JSONSerializer::read(), MayaFlux::Journal::Runtime, MayaFlux::Nexus::Fabric::Sensor, and width.

+ Here is the call graph for this function: