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 127 of file Decoder.cpp.

128{
129 m_last_error.clear();
130 m_patched_count = 0;
131 m_missing_count = 0;
132
133 const std::string json_path = base_path + ".json";
134 const std::string exr_path = base_path + ".exr";
135
136 IO::JSONSerializer ser;
137 auto schema_opt = ser.read<State::FabricSchema>(json_path);
138 if (!schema_opt) {
139 m_last_error = "Failed to load schema: " + ser.last_error();
141 return false;
142 }
143 const auto& schema = *schema_opt;
144
145 if (schema.version != State::k_schema_version) {
146 m_last_error = "Unsupported schema version: " + std::to_string(schema.version)
147 + " (expected " + std::to_string(State::k_schema_version) + ")";
149 return false;
150 }
151
152 if (schema.entities.empty()) {
153 m_last_error = "Schema contains no entities: " + json_path;
155 return false;
156 }
157
158 const uint32_t expected_rows = State::k_exr_rows;
159 auto pv_opt = load_exr(exr_path, static_cast<uint32_t>(schema.entities.size()), expected_rows, m_last_error);
160 if (!pv_opt) {
162 return false;
163 }
164 auto& pv = *pv_opt;
165 const auto* pixels = pv.image.as_float();
166 const uint32_t width = pv.width;
167 const auto& r = schema.ranges;
168
169 for (size_t i = 0; i < schema.entities.size(); ++i) {
170 const auto& entry = schema.entities[i];
171
172 if (!State::kind_known(entry.kind)) {
174 "StateDecoder: unknown kind '{}' for id {}, skipping", entry.kind, entry.id);
176 continue;
177 }
178
179 const size_t row0 = (static_cast<size_t>(0) * width + i) * State::k_channels;
180 const size_t row1 = (static_cast<size_t>(1) * width + i) * State::k_channels;
181 const size_t row2 = (static_cast<size_t>(2) * width + i) * State::k_channels;
182
183 const glm::vec3 position {
184 denormalize((*pixels)[row0 + 0], r.pos_x),
185 denormalize((*pixels)[row0 + 1], r.pos_y),
186 denormalize((*pixels)[row0 + 2], r.pos_z),
187 };
188
189 switch (State::parse_kind(entry.kind)) {
191 auto e = fabric.get_emitter(entry.id);
192 if (!e) {
194 "StateDecoder: id {} not found as Emitter, skipping", entry.id);
196 continue;
197 }
198 if (!entry.influence_fn_name.empty() && e->fn_name() != entry.influence_fn_name) {
200 "StateDecoder: Emitter {} fn_name mismatch: schema='{}' live='{}'",
201 entry.id, entry.influence_fn_name, e->fn_name());
202 }
203 e->set_position(position);
204 e->set_intensity(denormalize((*pixels)[row0 + 3], r.intensity));
205 if (entry.color) {
206 e->set_color(glm::vec3 {
207 denormalize((*pixels)[row1 + 0], r.color_r),
208 denormalize((*pixels)[row1 + 1], r.color_g),
209 denormalize((*pixels)[row1 + 2], r.color_b),
210 });
211 }
212 if (entry.size) {
213 e->set_size(denormalize((*pixels)[row1 + 3], r.size));
214 }
215 e->set_radius(denormalize((*pixels)[row2 + 0], r.radius));
216 break;
217 }
219 auto s = fabric.get_sensor(entry.id);
220 if (!s) {
222 "StateDecoder: id {} not found as Sensor, skipping", entry.id);
224 continue;
225 }
226 if (!entry.perception_fn_name.empty() && s->fn_name() != entry.perception_fn_name) {
228 "StateDecoder: Sensor {} fn_name mismatch: schema='{}' live='{}'",
229 entry.id, entry.perception_fn_name, s->fn_name());
230 }
231 s->set_position(position);
232 s->set_query_radius(denormalize((*pixels)[row2 + 1], r.query_radius));
233 break;
234 }
235 case Fabric::Kind::Agent: {
236 auto a = fabric.get_agent(entry.id);
237 if (!a) {
239 "StateDecoder: id {} not found as Agent, skipping", entry.id);
241 continue;
242 }
243 if (!entry.perception_fn_name.empty() && a->perception_fn_name() != entry.perception_fn_name) {
245 "StateDecoder: Agent {} perception_fn_name mismatch: schema='{}' live='{}'",
246 entry.id, entry.perception_fn_name, a->perception_fn_name());
247 }
248 if (!entry.influence_fn_name.empty() && a->influence_fn_name() != entry.influence_fn_name) {
250 "StateDecoder: Agent {} influence_fn_name mismatch: schema='{}' live='{}'",
251 entry.id, entry.influence_fn_name, a->influence_fn_name());
252 }
253 a->set_position(position);
254 a->set_intensity(denormalize((*pixels)[row0 + 3], r.intensity));
255 if (entry.color) {
256 a->set_color(glm::vec3 {
257 denormalize((*pixels)[row1 + 0], r.color_r),
258 denormalize((*pixels)[row1 + 1], r.color_g),
259 denormalize((*pixels)[row1 + 2], r.color_b),
260 });
261 }
262 if (entry.size) {
263 a->set_size(denormalize((*pixels)[row1 + 3], r.size));
264 }
265 a->set_radius(denormalize((*pixels)[row2 + 0], r.radius));
266 a->set_query_radius(denormalize((*pixels)[row2 + 1], r.query_radius));
267
268 if (entry.locus_nav) {
269 if (!apply_locus_nav(a, *entry.locus_nav)) {
271 "StateDecoder: Agent {} has locus_nav in schema but is not a Locus at runtime",
272 entry.id);
273 }
274 }
275
276 if (auto presence = std::dynamic_pointer_cast<Presence>(a)) {
277 if (!entry.falloff_curve_name.empty()) {
278 if (auto fc = Reflect::string_to_enum_case_insensitive<Presence::FalloffCurve>(entry.falloff_curve_name))
279 presence->set_falloff_curve(*fc);
280 }
281 if (entry.falloff_radius)
282 presence->set_falloff_radius(*entry.falloff_radius);
283 }
284 break;
285 }
286 }
287
289 }
290
291 for (const auto& xrec : schema.expanses) {
292 if (xrec.fn_name.empty()) {
294 "StateDecoder: Expanse {} has no fn_name, skipping", xrec.id);
295 continue;
296 }
297 auto contains_fn = fabric.resolve_expanse_fn(xrec.fn_name);
298 if (!contains_fn || !*contains_fn) {
300 "StateDecoder: Expanse {} fn '{}' not in registry, skipping",
301 xrec.id, xrec.fn_name);
302 continue;
303 }
304 auto on_enter_fn = xrec.on_enter_fn_name.empty()
306 : [ptr = fabric.resolve_crossing_fn(xrec.on_enter_fn_name)](uint32_t id) {
307 if (ptr && *ptr)
308 (*ptr)(id);
309 };
310 auto on_exit_fn = xrec.on_exit_fn_name.empty()
312 : [ptr = fabric.resolve_crossing_fn(xrec.on_exit_fn_name)](uint32_t id) {
313 if (ptr && *ptr)
314 (*ptr)(id);
315 };
316 auto expanse = std::make_shared<Expanse>(
317 xrec.fn_name,
318 xrec.on_enter_fn_name,
319 xrec.on_exit_fn_name,
320 *contains_fn,
321 std::move(on_enter_fn),
322 std::move(on_exit_fn));
323 fabric.add_expanse(std::move(expanse));
324 }
325
327 "StateDecoder: patched {} entities ({} missing) from {} + {}",
328 m_patched_count, m_missing_count, exr_path, json_path);
329
330 return true;
331}
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
uint32_t width
Definition Decoder.cpp:59
const std::vector< float > * pixels
Definition Decoder.cpp:58
size_t a
const uint8_t * ptr
std::function< void(uint32_t)> CrossingFn
Definition Expanse.hpp:34
@ FileIO
Filesystem I/O operations.
@ Runtime
General runtime operations (default fallback)
@ Nexus
Spatial indexing and scheduling for user-defined behaviour.
constexpr T denormalize(T t, T lo, T hi) noexcept
Denormalize t from [0, 1] to [lo, hi].
Definition Scalar.hpp:61
constexpr uint32_t k_schema_version
Current schema version written by StateEncoder and accepted by StateDecoder.
Definition Schema.hpp:20
bool kind_known(std::string_view s)
Return true if s maps to a known Fabric::Kind token.
Definition Schema.hpp:367
Fabric::Kind parse_kind(std::string_view s)
Parse a JSON kind token to Fabric::Kind (case-insensitive).
Definition Schema.hpp:378
constexpr uint32_t k_exr_rows
RGBA32F EXR layout constants shared between encoder and decoder.
Definition Schema.hpp:31
constexpr uint32_t k_channels
Definition Schema.hpp:32

References a, MayaFlux::Nexus::Fabric::add_expanse(), 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::Nexus::State::k_channels, MayaFlux::Nexus::State::k_exr_rows, MayaFlux::Nexus::State::k_schema_version, MayaFlux::Nexus::State::kind_known(), MayaFlux::IO::JSONSerializer::last_error(), m_last_error, m_missing_count, m_patched_count, MF_ERROR, MF_INFO, MF_WARN, MayaFlux::Journal::Nexus, MayaFlux::Nexus::State::parse_kind(), pixels, ptr, MayaFlux::IO::JSONSerializer::read(), MayaFlux::Nexus::Fabric::resolve_crossing_fn(), MayaFlux::Nexus::Fabric::resolve_expanse_fn(), MayaFlux::Journal::Runtime, MayaFlux::Nexus::Fabric::Sensor, and width.

+ Here is the call graph for this function: