1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 // This is bit unfortunate construct, clean up once there is one agreed implementation of JSON somewhere
17 #include <PropertyTools/property_data.h>
18 #define JSON_IMPL // have template specialisation defined once
19 #include <algorithm>
20 #include <charconv>
21 #include <cinttypes>
22
23 #include <3d/ecs/components/name_component.h>
24 #include <3d/ecs/components/render_handle_component.h>
25 #include <base/util/base64_decode.h>
26 #include <base/util/base64_encode.h>
27 #include <base/util/uid_util.h>
28 #include <core/ecs/intf_component_manager.h>
29 #include <core/ecs/intf_entity_manager.h>
30 #include <core/image/intf_image_loader_manager.h>
31 #include <core/intf_engine.h>
32 #include <core/property/intf_property_handle.h>
33 #include <render/device/intf_gpu_resource_manager.h>
34 #include <render/device/intf_shader_manager.h>
35 #include <render/util/intf_render_util.h>
36
37 #include "asset_loader.h"
38 #include "ecs_serializer.h"
39 #include "ecs_util.h"
40 #include "entity_collection.h"
41 #include "json.h"
42 #include "json_util.h"
43
44 using namespace BASE_NS;
45 using namespace CORE_NS;
46 using namespace RENDER_NS;
47 using namespace CORE3D_NS;
48
49 // #define VERBOSE_LOGGING
50 SCENE_BEGIN_NAMESPACE()
51
52 namespace {
53
54 // Helper function that makes sure that any dynamic arrays referenced in a property path are large enough to contain the
55 // referenced indices.
EnsureDynamicArraySize(IPropertyHandle * propertyHandle,string_view propertyPath)56 void EnsureDynamicArraySize(IPropertyHandle* propertyHandle, string_view propertyPath)
57 {
58 const auto separatorPosition = propertyPath.find('[');
59 if (separatorPosition == BASE_NS::string::npos) {
60 return;
61 }
62 const auto separatorEndPosition = propertyPath.find(']', separatorPosition);
63 if (separatorEndPosition == BASE_NS::string::npos) {
64 return;
65 }
66
67 string arrayPath, arrayIndex;
68 arrayPath = propertyPath.substr(0, separatorPosition);
69 arrayIndex = propertyPath.substr(separatorPosition + 1, separatorEndPosition - separatorPosition - 1);
70
71 char* end = nullptr;
72 const unsigned long index = std::strtoul(arrayIndex.c_str(), &end, 10); // 10: base
73 // Check that conversion stopped at the end of the string.
74 if (!end || *end != '\0') {
75 return;
76 }
77
78 PropertyData propertyData;
79 PropertyData::PropertyOffset propertyOffset = propertyData.WLock(*propertyHandle, arrayPath);
80 if (propertyOffset) {
81 auto* containerMethods = propertyOffset.property->metaData.containerMethods;
82 if (containerMethods && containerMethods->resize) {
83 if (containerMethods->size(propertyOffset.offset) <= index) {
84 containerMethods->resize(propertyOffset.offset, index + 1);
85 }
86 }
87 }
88
89 const auto restOfThePath = propertyPath.substr(separatorEndPosition);
90 EnsureDynamicArraySize(&propertyData, restOfThePath);
91 }
92
93 template<typename Type>
GetPropertyValue(uintptr_t ptr)94 Type& GetPropertyValue(uintptr_t ptr)
95 {
96 return *reinterpret_cast<Type*>(ptr);
97 }
98
99 struct IoUtil {
100 struct CompatibilityInfo {
101 uint32_t versionMajor { 0 };
102 uint32_t versionMinor { 0 };
103 BASE_NS::string type;
104 };
105
106 struct CompatibilityRange {
107 static const uint32_t IGNORE_VERSION = { ~0u };
108
109 uint32_t versionMajorMin { IGNORE_VERSION };
110 uint32_t versionMajorMax { IGNORE_VERSION };
111 uint32_t versionMinorMin { IGNORE_VERSION };
112 uint32_t versionMinorMax { IGNORE_VERSION };
113 BASE_NS::string type {};
114 };
115
WriteCompatibilityInfo__anon92e275030110::IoUtil116 static bool WriteCompatibilityInfo(json::standalone_value& jsonOut, const CompatibilityInfo& info)
117 {
118 jsonOut["compatibility_info"] = json::standalone_value::object();
119 jsonOut["compatibility_info"]["version"] =
120 string(to_string(info.versionMajor) + "." + to_string(info.versionMinor));
121 jsonOut["compatibility_info"]["type"] = string(info.type);
122 return true;
123 }
124
CheckCompatibility__anon92e275030110::IoUtil125 static bool CheckCompatibility(const json::value& json, array_view<CompatibilityRange const> validVersions)
126 {
127 string type;
128 string version;
129 if (const json::value* iter = json.find("compatibility_info"); iter) {
130 string parseError;
131 SafeGetJsonValue(*iter, "type", parseError, type);
132 SafeGetJsonValue(*iter, "version", parseError, version);
133
134 uint32_t versionMajor { 0 };
135 uint32_t versionMinor { 0 };
136 if (const auto delim = version.find('.'); delim != string::npos) {
137 std::from_chars(version.data(), version.data() + delim, versionMajor);
138 const size_t minorStart = delim + 1;
139 std::from_chars(version.data() + minorStart, version.data() + version.size(), versionMinor);
140 } else {
141 std::from_chars(version.data(), version.data() + version.size(), versionMajor);
142 }
143
144 for (const auto& range : validVersions) {
145 if (type != range.type) {
146 continue;
147 }
148 if ((range.versionMajorMin != CompatibilityRange::IGNORE_VERSION) &&
149 (versionMajor < range.versionMajorMin)) {
150 continue;
151 }
152 if ((range.versionMajorMax != CompatibilityRange::IGNORE_VERSION) &&
153 (versionMajor > range.versionMajorMax)) {
154 continue;
155 }
156 if ((range.versionMinorMin != CompatibilityRange::IGNORE_VERSION) &&
157 (versionMinor < range.versionMinorMin)) {
158 continue;
159 }
160 if ((range.versionMinorMax != CompatibilityRange::IGNORE_VERSION) &&
161 (versionMinor > range.versionMinorMax)) {
162 continue;
163 }
164
165 // A compatible version was found from the list of valid versions.
166 return true;
167 }
168 }
169
170 // Not a compatible version.
171 return false;
172 }
173 };
174 } // namespace
175
176 //
177 // EcsSerializer::SimpleJsonSerializer
178 //
ToJson(const IEntityCollection & ec,const Property & property,uintptr_t offset,json::standalone_value & jsonOut) const179 bool EcsSerializer::SimpleJsonSerializer::ToJson(
180 const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const
181 {
182 return PropertyToJson(ec, property, offset, jsonOut);
183 }
184
FromJson(const IEntityCollection & ec,const json::value & jsonIn,const Property & property,uintptr_t offset) const185 bool EcsSerializer::SimpleJsonSerializer::FromJson(
186 const IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const
187 {
188 return PropertyFromJson(ec, jsonIn, property, offset);
189 }
190
SimpleJsonSerializer(PropertyToJsonFunc toJson,PropertyFromJsonFunc fromJson)191 EcsSerializer::SimpleJsonSerializer::SimpleJsonSerializer(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)
192 : PropertyToJson(toJson), PropertyFromJson(fromJson)
193 {}
194
195 //
196 // EcsSerializer
197 //
EcsSerializer(RENDER_NS::IRenderContext & renderContext)198 EcsSerializer::EcsSerializer(RENDER_NS::IRenderContext& renderContext) : renderContext_(renderContext) {}
199
Add(PropertyToJsonFunc toJson,PropertyFromJsonFunc fromJson)200 EcsSerializer::IPropertySerializer& EcsSerializer::Add(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)
201 {
202 auto serializer = new SimpleJsonSerializer(toJson, fromJson);
203 auto ptr = unique_ptr<SimpleJsonSerializer> { serializer };
204 ownedSerializers_.emplace_back(move(ptr));
205 return *ownedSerializers_.back();
206 }
207
208 namespace {
209 template<class Type>
PropertyToJson(const IEntityCollection &,const Property &,uintptr_t offset,json::standalone_value & jsonOut)210 bool PropertyToJson(
211 const IEntityCollection& /*ec*/, const Property& /*property*/, uintptr_t offset, json::standalone_value& jsonOut)
212 {
213 jsonOut = ToJson(GetPropertyValue<Type>(offset));
214 return true;
215 }
216
217 template<class Type>
PropertyFromJson(const IEntityCollection &,const json::value & jsonIn,const Property &,uintptr_t offset)218 bool PropertyFromJson(
219 const IEntityCollection& /*ec*/, const json::value& jsonIn, const Property& /*property*/, uintptr_t offset)
220 {
221 auto& value = GetPropertyValue<Type>(offset);
222 return FromJson(jsonIn, value);
223 }
224
RenderHandleReferenceFromJson(const IEcsSerializer & ecsSerializer,IRenderContext & renderContext,const IEntityCollection & ec,const json::value & jsonIn,RenderHandleReference & handleRefOut)225 bool RenderHandleReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext,
226 const IEntityCollection& ec, const json::value& jsonIn, RenderHandleReference& handleRefOut)
227 {
228 CORE_UNUSED(ec);
229
230 if (!jsonIn.is_object()) {
231 return false;
232 }
233
234 string error;
235 RenderHandleDesc desc;
236
237 // Casting to enum directly from numeric value is a bit fishy.
238 uint32_t type = 0;
239 if (!SafeGetJsonValue(jsonIn, "type", error, type)) {
240 return false;
241 }
242 desc.type = static_cast<RenderHandleType>(type);
243
244 if (!SafeGetJsonValue(jsonIn, "name", error, desc.name)) {
245 return false;
246 }
247
248 SafeGetJsonValue(jsonIn, "additionalName", error, desc.additionalName);
249
250 auto& renderUtil = renderContext.GetRenderUtil();
251 handleRefOut = renderUtil.GetRenderHandle(desc);
252
253 if (!handleRefOut && desc.type == RenderHandleType::GPU_IMAGE && !desc.name.empty()) {
254 // Special handling for images: Load the image if it was not already loaded.
255 // Note: assuming that the name is the image uri.
256 handleRefOut = ecsSerializer.LoadImageResource(desc.name);
257 }
258
259 return true;
260 }
261
RenderHandleReferenceToJson(IRenderContext & renderContext,const IEntityCollection & ec,const RenderHandleReference & handleRefIn,json::standalone_value & jsonOut)262 bool RenderHandleReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec,
263 const RenderHandleReference& handleRefIn, json::standalone_value& jsonOut)
264 {
265 CORE_UNUSED(ec);
266
267 auto& renderUtil = renderContext.GetRenderUtil();
268 const auto desc = renderUtil.GetRenderHandleDesc(handleRefIn);
269
270 jsonOut = json::standalone_value::object {};
271 jsonOut["type"] = static_cast<unsigned int>(desc.type);
272 jsonOut["name"] = string(desc.name);
273 if (!desc.additionalName.empty()) {
274 jsonOut["additionalName"] = string(desc.additionalName);
275 }
276
277 return true;
278 }
279
EntityFromJson(const IEntityCollection & ec,const json::value & jsonIn,Entity & entityOut)280 bool EntityFromJson(const IEntityCollection& ec, const json::value& jsonIn, Entity& entityOut)
281 {
282 // Figure out to what entity id a piece of json refers to and handle error cases.
283 Entity entity {};
284 if (jsonIn.is_unsigned_int()) {
285 const auto entityReference = static_cast<uint32_t>(jsonIn.unsigned_);
286 entity = ec.GetEntity(entityReference);
287 if (entity == Entity {}) {
288 CORE_LOG_W("Component entity not found for index: %u", entityReference);
289 return false;
290 }
291 } else if (jsonIn.is_string()) {
292 string entityReferenceString;
293 if (FromJson(jsonIn, entityReferenceString)) {
294 entity = ec.GetEntity(entityReferenceString);
295 }
296 if (entity == Entity {}) {
297 CORE_LOG_W("Component entity not found for id: '%s'", entityReferenceString.c_str());
298 return false;
299 }
300 } else if (jsonIn.is_object()) {
301 // Empty object means "null ref".
302 if (jsonIn.empty()) {
303 entityOut = Entity {};
304 return true;
305 }
306
307 // Figure out the correct collection (Recursive).
308 const IEntityCollection* collection { nullptr };
309 const auto* collectionJson = jsonIn.find("collection");
310 if (collectionJson) {
311 if (collectionJson->is_string()) {
312 string collectionName;
313 if (FromJson(*collectionJson, collectionName)) {
314 if (auto index = ec.GetSubCollectionIndex(collectionName); index >= 0) {
315 collection = ec.GetSubCollection(static_cast<size_t>(index));
316 }
317 }
318 if (!collection) {
319 CORE_LOG_W("Collection not found: '%s'", collectionName.c_str());
320 return false;
321 }
322
323 } else if (collectionJson->is_unsigned_int()) {
324 const auto collectionIndex = collectionJson->unsigned_;
325 if (collectionIndex < ec.GetSubCollectionCount()) {
326 collection = ec.GetSubCollection(collectionIndex);
327 } else {
328 CORE_LOG_W("Collection not found: %" PRIu64, collectionIndex);
329 return false;
330 }
331
332 } else {
333 CORE_LOG_W("Invalid collection for a component.");
334 return false;
335 }
336
337 if (collection) {
338 const auto* entityJson = jsonIn.find("entity");
339 if (entityJson) {
340 return EntityFromJson(*collection, *entityJson, entityOut);
341 }
342 }
343 return false;
344 }
345
346 } else {
347 CORE_LOG_W("Component entity property must be an index to the entities array, an string id, or an object");
348 return false;
349 }
350
351 entityOut = entity;
352 return true;
353 }
354
EntityToJson(const IEntityCollection & ec,const Entity & entityIn,json::standalone_value & jsonOut)355 bool EntityToJson(const IEntityCollection& ec, const Entity& entityIn, json::standalone_value& jsonOut)
356 {
357 // Write entity index/name if part of this collection.
358 const auto entityCount = ec.GetEntityCount();
359 for (size_t i = 0; i < entityCount; ++i) {
360 if (entityIn == ec.GetEntity(i)) {
361 auto id = ec.GetId(entityIn);
362 if (!id.empty()) {
363 jsonOut = string(id);
364 } else {
365 jsonOut = i;
366 }
367 return true;
368 }
369 }
370
371 // Otherwise check sub-collections recursively.
372 const auto collectionCount = ec.GetSubCollectionCount();
373 size_t collectionId = 0;
374 for (size_t i = 0; i < collectionCount; ++i) {
375 auto* collection = ec.GetSubCollection(i);
376 BASE_ASSERT(collection);
377 // NOTE: Skipping over destroyed collections (same needs to be done when writing the actual collections).
378 if (collection->IsMarkedDestroyed() || !collection->IsSerialized()) {
379 continue;
380 }
381 json::standalone_value entityJson;
382 if (EntityToJson(*collection, entityIn, entityJson)) {
383 jsonOut = json::standalone_value::object {};
384 jsonOut[string_view { "collection" }] = collectionId;
385 jsonOut[string_view { "entity" }] = move(entityJson);
386 return true;
387 }
388 collectionId++;
389 }
390
391 return false;
392 }
393
EntityReferenceToJson(IRenderContext & renderContext,const IEntityCollection & ec,const EntityReference & entityIn,json::standalone_value & jsonOut)394 bool EntityReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec, const EntityReference& entityIn,
395 json::standalone_value& jsonOut)
396 {
397 if (EntityToJson(ec, entityIn, jsonOut)) {
398 return true;
399 }
400
401 // Write render handle reference as render handle desc.
402 auto* rhm = GetManager<IRenderHandleComponentManager>(ec.GetEcs());
403 if (rhm) {
404 if (auto handle = rhm->Read(entityIn); handle) {
405 json::standalone_value renderHandleJson = json::standalone_value::object {};
406 if (RenderHandleReferenceToJson(renderContext, ec, handle->reference, renderHandleJson["renderHandle"])) {
407 jsonOut = move(renderHandleJson);
408 return true;
409 }
410 }
411 }
412
413 return false;
414 }
415
EntityReferenceFromJson(const IEcsSerializer & ecsSerializer,IRenderContext & renderContext,const IEntityCollection & ec,const json::value & jsonIn,EntityReference & entityOut)416 bool EntityReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext,
417 const IEntityCollection& ec, const json::value& jsonIn, EntityReference& entityOut)
418 {
419 // A generic handler for any uri to a render handle.
420 const auto* renderHandleJson = jsonIn.find("renderHandle");
421 if (renderHandleJson) {
422 // Find a shader render handle by desc.
423 RenderHandleReference renderHandle;
424 if (RenderHandleReferenceFromJson(ecsSerializer, renderContext, ec, *renderHandleJson, renderHandle)) {
425 auto* rhm = GetManager<IRenderHandleComponentManager>(ec.GetEcs());
426 if (rhm && renderHandle) {
427 entityOut = GetOrCreateEntityReference(ec.GetEcs().GetEntityManager(), *rhm, renderHandle);
428 return true;
429 }
430 }
431 }
432
433 Entity temp;
434 if (!EntityFromJson(ec, jsonIn, temp)) {
435 return false;
436 }
437 entityOut = ec.GetEcs().GetEntityManager().GetReferenceCounted(temp);
438 return true;
439 }
440 } // namespace
441
SetDefaultSerializers()442 void EcsSerializer::SetDefaultSerializers()
443 {
444 //
445 // Basic types (for types that nlohman knows how to serialize).
446 //
447
448 SetSerializer(PropertyType::FLOAT_T, Add(PropertyToJson<float>, PropertyFromJson<float>));
449 SetSerializer(PropertyType::DOUBLE_T, Add(PropertyToJson<double>, PropertyFromJson<double>));
450
451 SetSerializer(PropertyType::UINT8_T, Add(PropertyToJson<uint8_t>, PropertyFromJson<uint8_t>));
452 SetSerializer(PropertyType::UINT16_T, Add(PropertyToJson<uint16_t>, PropertyFromJson<uint16_t>));
453 SetSerializer(PropertyType::UINT32_T, Add(PropertyToJson<uint32_t>, PropertyFromJson<uint32_t>));
454 SetSerializer(PropertyType::UINT64_T, Add(PropertyToJson<uint64_t>, PropertyFromJson<uint64_t>));
455
456 SetSerializer(PropertyType::INT8_T, Add(PropertyToJson<int8_t>, PropertyFromJson<int8_t>));
457 SetSerializer(PropertyType::INT16_T, Add(PropertyToJson<int16_t>, PropertyFromJson<int16_t>));
458 SetSerializer(PropertyType::INT32_T, Add(PropertyToJson<int32_t>, PropertyFromJson<int32_t>));
459 SetSerializer(PropertyType::INT64_T, Add(PropertyToJson<int64_t>, PropertyFromJson<int64_t>));
460
461 SetSerializer(PropertyType::VEC2_T, Add(PropertyToJson<Math::Vec2>, PropertyFromJson<Math::Vec2>));
462 SetSerializer(PropertyType::VEC3_T, Add(PropertyToJson<Math::Vec3>, PropertyFromJson<Math::Vec3>));
463 SetSerializer(PropertyType::VEC4_T, Add(PropertyToJson<Math::Vec4>, PropertyFromJson<Math::Vec4>));
464
465 SetSerializer(PropertyType::UVEC2_T, Add(PropertyToJson<Math::UVec2>, PropertyFromJson<Math::UVec2>));
466 SetSerializer(PropertyType::UVEC3_T, Add(PropertyToJson<Math::UVec3>, PropertyFromJson<Math::UVec3>));
467 SetSerializer(PropertyType::UVEC4_T, Add(PropertyToJson<Math::UVec4>, PropertyFromJson<Math::UVec4>));
468
469 SetSerializer(PropertyType::BOOL_T, Add(PropertyToJson<bool>, PropertyFromJson<bool>));
470 SetSerializer(PropertyType::QUAT_T, Add(PropertyToJson<Math::Quat>, PropertyFromJson<Math::Quat>));
471 SetSerializer(PropertyType::UID_T, Add(PropertyToJson<Uid>, PropertyFromJson<Uid>));
472
473 SetSerializer(PropertyType::STRING_T, Add(PropertyToJson<string>, PropertyFromJson<string>));
474
475 //
476 // Others
477 //
478
479 SetSerializer(PropertyType::CHAR_ARRAY_T,
480 Add(
481 [](const IEntityCollection& /*ec*/, const Property& property, uintptr_t offset,
482 json::standalone_value& jsonOut) {
483 const auto* value = &GetPropertyValue<char>(offset);
484 // NOTE: a hacky way to calculate cstring size.
485 string_view view(value);
486 const size_t size = view.size() < property.size ? view.size() : property.size;
487 jsonOut = ToJson(string(value, size));
488 return true;
489 },
490 [](const IEntityCollection& /*ec*/, const json::value& jsonIn, const Property& property, uintptr_t offset) {
491 string value;
492 if (FromJson(jsonIn, value)) {
493 char* charArray = &GetPropertyValue<char>(offset);
494 charArray[value.copy(charArray, property.size - 1)] = '\0';
495 return true;
496 }
497 return false;
498 }));
499
500 SetSerializer(PropertyType::ENTITY_T,
501 Add(
502 [](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
503 json::standalone_value& jsonOut) {
504 return EntityToJson(ec, GetPropertyValue<const Entity>(offset), jsonOut);
505 },
506 [](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/, uintptr_t offset) {
507 return EntityFromJson(ec, jsonIn, GetPropertyValue<Entity>(offset));
508 }));
509
510 SetSerializer(PropertyType::ENTITY_REFERENCE_T,
511 Add(
512 [this](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
513 json::standalone_value& jsonOut) {
514 return EntityReferenceToJson(
515 renderContext_, ec, GetPropertyValue<const EntityReference>(offset), jsonOut);
516 },
517 [this](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/,
518 uintptr_t offset) {
519 return EntityReferenceFromJson(
520 *this, renderContext_, ec, jsonIn, GetPropertyValue<EntityReference>(offset));
521 }));
522
523 SetSerializer(PROPERTYTYPE(RenderHandleReference),
524 Add(
525 [this](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
526 json::standalone_value& jsonOut) {
527 return RenderHandleReferenceToJson(
528 renderContext_, ec, GetPropertyValue<const RenderHandleReference>(offset), jsonOut);
529 },
530 [this](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/,
531 uintptr_t offset) {
532 return RenderHandleReferenceFromJson(
533 *this, renderContext_, ec, jsonIn, GetPropertyValue<RenderHandleReference>(offset));
534 }));
535 }
536
SetListener(IListener * listener)537 void EcsSerializer::SetListener(IListener* listener)
538 {
539 listener_ = listener;
540 }
541
SetSerializer(const PropertyTypeDecl & type,IPropertySerializer & serializer)542 void EcsSerializer::SetSerializer(const PropertyTypeDecl& type, IPropertySerializer& serializer)
543 {
544 typetoSerializerMap_[type] = &serializer;
545 }
546
WriteEntityCollection(const IEntityCollection & ec,json::standalone_value & jsonOut) const547 bool EcsSerializer::WriteEntityCollection(const IEntityCollection& ec, json::standalone_value& jsonOut) const
548 {
549 // NOTE: We make sure collections and entities are written in the output before entity-components to make it
550 // possible to parse the file in one go.
551
552 jsonOut = json::standalone_value::object();
553
554 string type = ec.GetType();
555 if (type.empty()) {
556 type = "entitycollection";
557 }
558
559 const IoUtil::CompatibilityInfo info { VERSION_MAJOR, VERSION_MINOR, type };
560 if (!IoUtil::WriteCompatibilityInfo(jsonOut, info)) {
561 return false;
562 }
563
564 if (string rootSrc = ec.GetSrc(); !rootSrc.empty()) {
565 jsonOut["src"] = move(rootSrc);
566 }
567
568 // Write external collections instanced in this collection.
569 const auto collectionCount = ec.GetSubCollectionCount();
570 if (collectionCount > 0) {
571 json::standalone_value collectionsJson = json::standalone_value::array();
572 collectionsJson.array_.reserve(collectionCount);
573
574 for (size_t i = 0; i < collectionCount; ++i) {
575 json::standalone_value collectionJson = json::standalone_value::object();
576 auto* collection = ec.GetSubCollection(i);
577
578 if (!collection || collection->IsMarkedDestroyed() || !collection->IsSerialized()) {
579 // NOTE: Skipping over destroyed collections (same needs to be done when writing entity references).
580 continue;
581 }
582
583 if (string src = collection->GetSrc(); !src.empty()) {
584 collectionJson["src"] = move(src);
585 }
586 if (string id = collection->GetUri(); !id.empty()) {
587 collectionJson["id"] = move(id);
588 }
589
590 collectionsJson.array_.emplace_back(move(collectionJson));
591 }
592
593 if (!collectionsJson.array_.empty()) {
594 jsonOut["collections"] = move(collectionsJson);
595 }
596 }
597
598 // Write bare entities without components.
599 const auto entityCount = ec.GetEntityCount();
600 if (entityCount > 0) {
601 auto& entitiesJson = (jsonOut["entities"] = json::standalone_value::array());
602 entitiesJson.array_.reserve(entityCount);
603 for (size_t i = 0; i < entityCount; ++i) {
604 json::standalone_value entityJson = json::standalone_value::object();
605
606 // Write id if one is defined.
607 auto entity = ec.GetEntity(i);
608 const auto& id = ec.GetId(entity);
609 if (!id.empty()) {
610 entityJson["id"] = string(id);
611 }
612
613 entitiesJson.array_.emplace_back(move(entityJson));
614 }
615 }
616
617 vector<EntityReference> allEntities;
618 ec.GetEntitiesRecursive(false, allEntities, false);
619 if (allEntities.size() > 0) {
620 auto& entityComponentsJson = (jsonOut["entity-components"] = json::standalone_value::array());
621 for (Entity entity : allEntities) {
622 json::standalone_value componentJson = json::standalone_value::object();
623 if (WriteComponents(ec, entity, componentJson["components"])) {
624 json::standalone_value entityRefJson;
625 if (EntityToJson(ec, entity, entityRefJson)) {
626 componentJson["entity"] = move(entityRefJson);
627 }
628 entityComponentsJson.array_.emplace_back(move(componentJson));
629 }
630 }
631 }
632
633 // NOTE: Always returns true even if parts of the writing failed.
634 return true;
635 }
636
WriteComponents(const IEntityCollection & ec,Entity entity,json::standalone_value & jsonOut) const637 bool EcsSerializer::WriteComponents(const IEntityCollection& ec, Entity entity, json::standalone_value& jsonOut) const
638 {
639 // Write all entity components to json (Sorting by uid to keep the order more stable).
640 vector<string> managerUids;
641 jsonOut = json::standalone_value::object();
642 auto cms = ec.GetEcs().GetComponentManagers();
643 for (auto cm : cms) {
644 const auto componentId = cm->GetComponentId(entity);
645 if (componentId != IComponentManager::INVALID_COMPONENT_ID) {
646 auto uidString = to_string(cm->GetUid());
647 managerUids.emplace_back(string(uidString));
648 }
649 }
650 std::sort(managerUids.begin(), managerUids.end());
651
652 for (auto& uidString : managerUids) {
653 const auto* cm = ec.GetEcs().GetComponentManager(StringToUid(uidString));
654 const auto componentId = cm->GetComponentId(entity);
655
656 json::standalone_value componentJson = json::standalone_value::object();
657 if (WriteComponent(ec, entity, *cm, componentId, componentJson)) {
658 jsonOut[uidString] = move(componentJson);
659 }
660 }
661
662 return (!jsonOut.empty());
663 }
664
WriteComponent(const IEntityCollection & ec,Entity entity,const IComponentManager & cm,IComponentManager::ComponentId id,json::standalone_value & jsonOut) const665 bool EcsSerializer::WriteComponent(const IEntityCollection& ec, Entity entity, const IComponentManager& cm,
666 IComponentManager::ComponentId id, json::standalone_value& jsonOut) const
667 {
668 // Write all properties to json.
669 json::standalone_value propertiesJson = json::standalone_value::object();
670 const auto* props = ec.GetSerializedProperties(entity, cm.GetUid());
671 if (props) {
672 const auto* propertyHandle = cm.GetData(id);
673 if (!propertyHandle) {
674 return false;
675 }
676 for (auto& propertyPath : *props) {
677 const IPropertyHandle* handle = propertyHandle;
678
679 PropertyData propertyData;
680 PropertyData::PropertyOffset propertyOffset;
681
682 // Check if this is property container.
683 string path, name;
684 auto containerHandle = ResolveContainerProperty(*handle, propertyPath, path, name);
685 if (containerHandle) {
686 propertyOffset = propertyData.RLock(*containerHandle, name);
687 } else {
688 propertyOffset = propertyData.RLock(*propertyHandle, propertyPath);
689 }
690
691 if (propertyOffset) {
692 if ((propertyOffset.property->flags & static_cast<uint32_t>(PropertyFlags::NO_SERIALIZE)) != 0) {
693 continue;
694 }
695
696 json::standalone_value propertyJson;
697 if (WriteProperty(ec, *propertyOffset.property, propertyOffset.offset, propertyJson)) {
698 if (!propertyJson.is_null()) {
699 propertiesJson[propertyPath] = move(propertyJson);
700 }
701 }
702 }
703 }
704
705 // NOTE: optional name to make reading files easier.
706 jsonOut["name"] = string(cm.GetName());
707
708 if (!propertiesJson.empty()) {
709 jsonOut["properties"] = move(propertiesJson);
710 }
711
712 // NOTE: Maybe return false if any property write fails?
713 return true;
714 }
715 return false;
716 }
717
WriteProperty(const IEntityCollection & ec,const Property & property,uintptr_t offset,json::standalone_value & jsonOut) const718 bool EcsSerializer::WriteProperty(
719 const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const
720 {
721 auto serializer = typetoSerializerMap_.find(property.type);
722 if (serializer != typetoSerializerMap_.end()) {
723 return serializer->second->ToJson(ec, property, offset, jsonOut);
724
725 } else if (!property.metaData.enumMetaData.empty()) {
726 // Enum type property.
727 switch (property.size) {
728 case sizeof(uint8_t):
729 jsonOut = GetPropertyValue<uint8_t>(offset);
730 return true;
731 case sizeof(uint16_t):
732 jsonOut = GetPropertyValue<uint16_t>(offset);
733 return true;
734 case sizeof(uint32_t):
735 jsonOut = GetPropertyValue<uint32_t>(offset);
736 return true;
737 case sizeof(uint64_t):
738 jsonOut = GetPropertyValue<uint64_t>(offset);
739 return true;
740 }
741
742 } else if (property.metaData.containerMethods) {
743 const auto& container = *property.metaData.containerMethods;
744
745 // Container type property.
746 if (property.type.isArray) {
747 // Special handling for byte arrays. Save as a base64 encoded string.
748 if (property.type == PropertyType::UINT8_ARRAY_T) {
749 array_view<const uint8_t> bytes { (const uint8_t*)offset, property.size };
750 jsonOut = BASE_NS::Base64Encode(bytes);
751 return true;
752 }
753
754 // C style array.
755 json::standalone_value array = json::standalone_value::array();
756 array.array_.reserve(property.count);
757 for (size_t i = 0; i < property.count; i++) {
758 uintptr_t ptr = offset + i * container.property.size;
759 // TODO: return false if any recurseive call fails?
760 json::standalone_value elementJson;
761 WriteProperty(ec, container.property, ptr, elementJson);
762 array.array_.push_back(move(elementJson));
763 }
764 jsonOut = move(array);
765 return true;
766
767 } else {
768 // This is a "non trivial container".
769 const auto count = container.size(offset);
770
771 // Special handling for byte arrays. Save as a base64 encoded string.
772 // NOTE: Only specifically vector so we can assume that the memory usage is linear.
773 if (property.type == PROPERTYTYPE(vector<uint8_t>)) {
774 if (count == 0) {
775 jsonOut = "";
776 } else {
777 auto data = container.get(offset, 0);
778 array_view<const uint8_t> bytes { reinterpret_cast<const uint8_t*>(data), count };
779 jsonOut = BASE_NS::Base64Encode(bytes);
780 }
781 return true;
782 }
783
784 json::standalone_value array = json::standalone_value::array();
785 array.array_.reserve(count);
786
787 for (size_t i = 0; i < count; i++) {
788 uintptr_t ptr = container.get(offset, i);
789 // TODO: return false if any recurseive call fails?
790 json::standalone_value elementJson;
791 WriteProperty(ec, container.property, ptr, elementJson);
792 array.array_.push_back(move(elementJson));
793 }
794 jsonOut = move(array);
795 return true;
796 }
797
798 } else if (!property.metaData.memberProperties.empty()) {
799 // Struct type property (ie. has sub properties).
800 json::standalone_value object = json::standalone_value::object();
801 for (const auto& subProperty : property.metaData.memberProperties) {
802 json::standalone_value subPropertyJson;
803 if (WriteProperty(ec, subProperty, offset + subProperty.offset, subPropertyJson)) {
804 object[subProperty.name] = move(subPropertyJson);
805 }
806 }
807 // TODO: return false if any recurseive call fails?
808 jsonOut = move(object);
809 return true;
810
811 } else {
812 CORE_LOG_V("Ecs serializer not found for '%s'", string(property.type.name).c_str());
813 }
814 return false;
815 }
816
GatherExternalCollections(const json::value & jsonIn,string_view contextUri,vector<ExternalCollection> & externalCollectionsOut) const817 bool EcsSerializer::GatherExternalCollections(
818 const json::value& jsonIn, string_view contextUri, vector<ExternalCollection>& externalCollectionsOut) const
819 {
820 const auto* srcJson = jsonIn.find("src");
821 string srcUri;
822 if (srcJson && FromJson(*srcJson, srcUri)) {
823 #ifdef VERBOSE_LOGGING
824 SCENE_PLUGIN_VERBOSE_LOG(
825 "External Collection: uri='%s' context='%s'", srcUri.c_str(), string(contextUri).c_str());
826 #endif
827 externalCollectionsOut.emplace_back(ExternalCollection { srcUri, string { contextUri } });
828 }
829
830 const auto* collectionsJson = jsonIn.find("collections");
831 if (collectionsJson && collectionsJson->is_array()) {
832 for (const auto& collectionJson : collectionsJson->array_) {
833 if (collectionJson.is_object()) {
834 const auto* collectionSrcJson = collectionJson.find("src");
835 if (collectionSrcJson && collectionSrcJson->is_string()) {
836 string collectionSrcUri;
837 FromJson(*collectionSrcJson, collectionSrcUri);
838 externalCollectionsOut.emplace_back(ExternalCollection { collectionSrcUri, string { contextUri } });
839 }
840 }
841 }
842 }
843 return true;
844 }
845
ReadEntityCollection(IEntityCollection & ec,const json::value & jsonIn,string_view contextUri) const846 /*IIoUtil::SerializationResult*/ int EcsSerializer::ReadEntityCollection(
847 IEntityCollection& ec, const json::value& jsonIn, string_view contextUri) const
848 {
849 // TODO: Move version check to be separately so it can be done before gathering the dependencies.
850 // NOTE: Only comparing the major version.
851 const auto minor = IoUtil::CompatibilityRange::IGNORE_VERSION;
852 // TODO: Type name was changed to be in line with engine naming. Allow the old type name for a while.
853 const IoUtil::CompatibilityRange validVersions[] {
854 { VERSION_MAJOR, VERSION_MAJOR, minor, minor, ec.GetType() },
855 { VERSION_MAJOR, VERSION_MAJOR, minor, minor, "entity_collection" },
856 };
857
858 auto result = IoUtil::CheckCompatibility(jsonIn, validVersions);
859 if (!result) {
860 CORE_LOG_W("Deprecated compatibility info found \"entity_collection\": '%s'", string(contextUri).c_str());
861 }
862
863 const auto* srcJson = jsonIn.find("src");
864 string srcUri;
865 if (srcJson && FromJson(*srcJson, srcUri)) {
866 #ifdef VERBOSE_LOGGING
867 SCENE_PLUGIN_VERBOSE_LOG(
868 "External Collection: uri='%s' context='%s'", srcUri.c_str(), string(contextUri).c_str());
869 #endif
870 auto* externalCollection = listener_->GetExternalCollection(ec.GetEcs(), srcUri, contextUri);
871 if (externalCollection) {
872 ec.CopyContents(*externalCollection);
873 }
874 }
875
876 const auto* collectionsJson = jsonIn.find("collections");
877 if (collectionsJson && collectionsJson->is_array()) {
878 for (const auto& collectionJson : collectionsJson->array_) {
879 if (collectionJson.is_object()) {
880 string id;
881 const auto* idJson = collectionJson.find("id");
882 if (idJson) {
883 FromJson(*idJson, id);
884 }
885
886 const auto* collectionSrcJson = collectionJson.find("src");
887 if (collectionSrcJson && collectionSrcJson->is_string()) {
888 string collectionSrcUri;
889 FromJson(*collectionSrcJson, collectionSrcUri);
890
891 // Instantiate the collection pointed by src.
892 #ifdef VERBOSE_LOGGING
893 SCENE_PLUGIN_VERBOSE_LOG("External Collection: uri='%s' context='%s'", collectionSrcUri.c_str(),
894 string(contextUri).c_str());
895 #endif
896 auto* externalCollection =
897 listener_->GetExternalCollection(ec.GetEcs(), collectionSrcUri, contextUri);
898 if (externalCollection) {
899 ec.AddSubCollectionClone(*externalCollection, id).SetSrc(collectionSrcUri);
900 ;
901 } else {
902 CORE_LOG_E("Loading collection failed: '%s'", collectionSrcUri.c_str());
903 // Just adding an empty collection as a placeholder.
904 ec.AddSubCollection(id, collectionSrcUri).SetSrc(collectionSrcUri);
905 }
906 } else {
907 ec.AddSubCollection(id, {});
908 }
909 }
910 }
911 }
912
913 const auto* entitiesJson = jsonIn.find("entities");
914 bool idSet { false };
915 if (entitiesJson && entitiesJson->is_array()) {
916 auto& em = ec.GetEcs().GetEntityManager();
917
918 // First create all entities so they can be referenced by components.
919 for (const auto& entityJson : entitiesJson->array_) {
920 // Create a new entity.
921 EntityReference entity = em.CreateReferenceCounted();
922 ec.AddEntity(entity);
923
924 // Add with an id if one is defined.
925 const auto* idJson = entityJson.find("id");
926 string id;
927 if (idJson && FromJson(*idJson, id)) {
928 ec.SetId(id, entity);
929 idSet = true;
930 }
931 }
932 }
933
934 const auto* ecJson = jsonIn.find("entity-components");
935 if (ecJson && ecJson->is_array()) {
936 // Then load entity contents (i.e. components).
937 for (size_t i = 0; i < ecJson->array_.size(); ++i) {
938 ReadComponents(ec, ecJson->array_.at(i), !idSet);
939 }
940 }
941
942 if (!ec.IsActive()) {
943 // If the ec is not active also make the newly loaded entities not active.
944 ec.SetActive(false);
945 }
946
947 // NOTE: Always returns success, even if parts of the load failed.
948 return 1; // result;
949 }
950
ReadComponents(IEntityCollection & ec,const json::value & jsonIn,bool setId) const951 bool EcsSerializer::ReadComponents(IEntityCollection& ec, const json::value& jsonIn, bool setId) const
952 {
953 // Figure out to which entity these components belong to.
954 const auto* entityJson = jsonIn.find("entity");
955 if (!entityJson) {
956 CORE_LOG_W("No entity defined for a component.");
957 return false;
958 }
959
960 Entity entity {};
961
962 if (!EntityFromJson(ec, *entityJson, entity)) {
963 return false;
964 }
965
966 auto& ecs = ec.GetEcs();
967 const auto* componentsJson = jsonIn.find("components");
968 if (componentsJson) {
969 // Read all entity components from json.
970 for (auto& component : componentsJson->object_) {
971 auto& key = component.key;
972 const auto componentUid = StringToUid(key);
973
974 auto& componentJson = component.value;
975 auto* cm = ecs.GetComponentManager(componentUid);
976 if (cm) {
977 ReadComponent(ec, componentJson, entity, *cm);
978 if (setId && cm->GetName() == "NameComponent") {
979 if (auto handle = reinterpret_cast<CORE3D_NS::INameComponentManager*>(cm)->Read(entity)) {
980 for (auto& ref : ec.GetEntities()) {
981 if (ref == entity) {
982 BASE_NS::string compound = handle->name;
983 compound.append(":");
984 compound.append(BASE_NS::to_hex(entity.id));
985
986 ec.SetUniqueIdentifier(compound, ref);
987 break;
988 }
989 }
990 }
991 }
992 } else {
993 // TODO: Maybe we should try to find a matching component by name as a fallback
994 CORE_LOG_W("Unrecognized component found: '%s'", string(key).c_str());
995 }
996 }
997 }
998
999 return true;
1000 }
1001
ReadComponent(IEntityCollection & ec,const json::value & jsonIn,Entity entity,IComponentManager & component) const1002 bool EcsSerializer::ReadComponent(
1003 IEntityCollection& ec, const json::value& jsonIn, Entity entity, IComponentManager& component) const
1004 {
1005 // Create the component if it does not exist yet.
1006 auto componentId = component.GetComponentId(entity);
1007 if (componentId == IComponentManager::INVALID_COMPONENT_ID) {
1008 component.Create(entity);
1009 componentId = component.GetComponentId(entity);
1010 }
1011
1012 ec.MarkComponentSerialized(entity, component.GetUid(), true);
1013
1014 const auto* propertiesJson = jsonIn.find("properties");
1015 if (!propertiesJson || propertiesJson->type != json::type::object) {
1016 // No properties.
1017 return true;
1018 }
1019
1020 auto* propertyHandle = component.GetData(componentId);
1021 if (!propertyHandle) {
1022 return false;
1023 }
1024
1025 for (auto& propertyJson : propertiesJson->object_) {
1026 const auto& propertyPath = propertyJson.key;
1027 const auto& propertyValueJson = propertyJson.value;
1028 auto pathView = string_view(propertyPath.data(), propertyPath.size());
1029
1030 // Find the property using the propertyName
1031 {
1032 const IPropertyHandle* handle = propertyHandle;
1033 PropertyData propertyData;
1034 PropertyData::PropertyOffset propertyOffset;
1035
1036 // Check if this is property container.
1037 string path, name;
1038 auto containerHandle = ResolveContainerProperty(*handle, string(pathView), path, name);
1039 if (containerHandle) {
1040 propertyOffset = propertyData.WLock(*containerHandle, name);
1041 } else {
1042 // We can only ask for the property if we first make sure it exists (we may be referencing a dynamic
1043 // array).
1044 EnsureDynamicArraySize(propertyHandle, pathView);
1045
1046 propertyOffset = propertyData.WLock(*propertyHandle, pathView);
1047 }
1048
1049 if (propertyOffset) {
1050 if (ReadProperty(ec, propertyValueJson, *propertyOffset.property, propertyOffset.offset)) {
1051 // Mark this property value as serialized (instead of being a cloned from a prototype entity).
1052 ec.MarkPropertySerialized(entity, component.GetUid(), pathView, true);
1053 } else {
1054 CORE_LOG_W("Unrecognized property: Component: '%s' Property: '%s'", component.GetName().data(),
1055 string(pathView).c_str());
1056 }
1057 }
1058 }
1059 }
1060 return true;
1061 }
1062
ReadProperty(IEntityCollection & ec,const json::value & jsonIn,const Property & property,uintptr_t offset) const1063 bool EcsSerializer::ReadProperty(
1064 IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const
1065 {
1066 // See if there is a serializer for this type. Otherwise recurse further.
1067 auto serializer = typetoSerializerMap_.find(property.type);
1068 if (serializer != typetoSerializerMap_.end()) {
1069 return serializer->second->FromJson(ec, jsonIn, property, offset);
1070
1071 } else if (!property.metaData.enumMetaData.empty()) {
1072 // Enum type property.
1073 if (jsonIn.is_unsigned_int()) {
1074 switch (property.size) {
1075 case sizeof(uint8_t):
1076 GetPropertyValue<uint8_t>(offset) = static_cast<uint8_t>(jsonIn.unsigned_);
1077 return true;
1078 case sizeof(uint16_t):
1079 GetPropertyValue<uint16_t>(offset) = static_cast<uint16_t>(jsonIn.unsigned_);
1080 return true;
1081 case sizeof(uint32_t):
1082 GetPropertyValue<uint32_t>(offset) = static_cast<uint32_t>(jsonIn.unsigned_);
1083 return true;
1084 case sizeof(uint64_t):
1085 GetPropertyValue<uint64_t>(offset) = static_cast<uint64_t>(jsonIn.unsigned_);
1086 return true;
1087 }
1088 }
1089
1090 } else if (property.metaData.containerMethods) {
1091 // Special handling for byte data encoded as base64.
1092 if (jsonIn.is_string()) {
1093 // A base64 encoded string containing raw array data
1094 auto bytes = BASE_NS::Base64Decode(jsonIn.string_);
1095
1096 // Only valid for byte data.
1097 if (property.type == PropertyType::UINT8_ARRAY_T) {
1098 if (property.size != bytes.size()) {
1099 CORE_LOG_W("Invalid base64 data size in: %s", string(property.name).c_str());
1100 return false;
1101 }
1102 CloneData(GetPropertyValue<uint8_t*>(offset), property.size, &bytes[0], bytes.size());
1103 return true;
1104
1105 } else if (property.type == PROPERTYTYPE(vector<uint8_t>)) {
1106 GetPropertyValue<vector<uint8_t>>(offset).swap(bytes);
1107 return true;
1108 }
1109 return false;
1110 }
1111
1112 // Container type property.
1113 if (property.type.isArray) {
1114 // C style array.
1115 if (jsonIn.is_array()) {
1116 if (jsonIn.array_.size() != property.count) {
1117 CORE_LOG_W("Expecting a json array of size %zu", property.count);
1118 return false;
1119 }
1120 for (size_t i = 0; i < property.count; i++) {
1121 uintptr_t ptr = offset + i * property.metaData.containerMethods->property.size;
1122 // TODO: return false if any recurseive call fails?
1123 ReadProperty(ec, jsonIn.array_.at(i), property.metaData.containerMethods->property, ptr);
1124 }
1125 return true;
1126
1127 } else if (jsonIn.is_object()) {
1128 // Allow "sparse arrays" by using objects with the array index as the key.
1129 for (auto& element : jsonIn.object_) {
1130 const auto& key = element.key;
1131 const auto index = static_cast<size_t>(strtol(string(key).c_str(), nullptr, 10));
1132 if ((index == 0 && key != "0") || index >= property.count) {
1133 CORE_LOG_W("Invalid array Index: %s", string(key).c_str());
1134 continue;
1135 }
1136 uintptr_t ptr = offset + index * property.metaData.containerMethods->property.size;
1137 ReadProperty(ec, element.value, property.metaData.containerMethods->property, ptr);
1138 }
1139 return true;
1140 }
1141 return false;
1142
1143 } else {
1144 // This is a "non trivial container".
1145 if (jsonIn.is_array()) {
1146 const auto count = jsonIn.array_.size();
1147 property.metaData.containerMethods->resize(offset, count);
1148 for (size_t i = 0; i < count; i++) {
1149 uintptr_t ptr = property.metaData.containerMethods->get(offset, i);
1150 ReadProperty(ec, jsonIn.array_.at(i), property.metaData.containerMethods->property, ptr);
1151 }
1152 return true;
1153
1154 } else if (jsonIn.is_object()) {
1155 // Allow "sparse arrays" by using objects with the array index as the key.
1156 for (auto& element : jsonIn.object_) {
1157 const auto& key = element.key;
1158 const auto index = static_cast<size_t>(strtol(string(key).c_str(), nullptr, 10));
1159 if ((index == 0 && key != "0")) {
1160 CORE_LOG_W("Invalid array Index: %s", string(key).c_str());
1161 continue;
1162 }
1163
1164 const auto count = property.metaData.containerMethods->size(offset);
1165 if (count <= index) {
1166 property.metaData.containerMethods->resize(offset, index + 1);
1167 }
1168 uintptr_t ptr = property.metaData.containerMethods->get(offset, index);
1169 ReadProperty(ec, element.value, property.metaData.containerMethods->property, ptr);
1170 }
1171 return true;
1172 }
1173 return false;
1174 }
1175
1176 } else if (!property.metaData.memberProperties.empty()) {
1177 // Struct type property (ie. has sub properties).
1178 if (jsonIn.is_object()) {
1179 for (const auto& subProperty : property.metaData.memberProperties) {
1180 // TODO: is there a way to not create the string
1181 const string name(subProperty.name);
1182 const auto* subJson = jsonIn.find(name);
1183 if (subJson) {
1184 ReadProperty(ec, *subJson, subProperty, offset + subProperty.offset);
1185 }
1186 }
1187 // TODO: return false if any recurseive call fails?
1188 return true;
1189 }
1190 }
1191
1192 return false;
1193 }
1194
LoadImageResource(BASE_NS::string_view uri) const1195 RENDER_NS::RenderHandleReference EcsSerializer::LoadImageResource(BASE_NS::string_view uri) const
1196 {
1197 // Get rid of a possible query string in the uri.
1198 const auto fileUri = PathUtil::ResolvePath({}, uri, false);
1199
1200 auto params = PathUtil::GetUriParameters(uri);
1201
1202 // Image loading flags can be passed in the uri query string.
1203 uint64_t imageLoaderFlags {};
1204 auto loaderFlags = params.find("loaderFlags");
1205 if (loaderFlags != params.end()) {
1206 const char* start = loaderFlags->second.data();
1207 const char* end = start + loaderFlags->second.size();
1208 std::from_chars(start, end, imageLoaderFlags);
1209 }
1210
1211 auto imageLoadResult = renderContext_.GetEngine().GetImageLoaderManager().LoadImage(fileUri, imageLoaderFlags);
1212
1213 RenderHandleReference imageHandle {};
1214 if (!imageLoadResult.success) {
1215 CORE_LOG_E("Could not load image asset: %s", imageLoadResult.error);
1216
1217 } else {
1218 auto& gpuResourceMgr = renderContext_.GetDevice().GetGpuResourceManager();
1219 GpuImageDesc gpuDesc = gpuResourceMgr.CreateGpuImageDesc(imageLoadResult.image->GetImageDesc());
1220 gpuDesc.usageFlags = CORE_IMAGE_USAGE_SAMPLED_BIT | CORE_IMAGE_USAGE_TRANSFER_DST_BIT;
1221 if (gpuDesc.engineCreationFlags & EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_GENERATE_MIPS) {
1222 gpuDesc.usageFlags |= CORE_IMAGE_USAGE_TRANSFER_SRC_BIT;
1223 }
1224 gpuDesc.memoryPropertyFlags = CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
1225
1226 imageHandle = gpuResourceMgr.Create(uri, gpuDesc, std::move(imageLoadResult.image));
1227 }
1228
1229 return imageHandle;
1230 }
1231
Destroy()1232 void EcsSerializer::Destroy()
1233 {
1234 delete this;
1235 }
1236
1237 SCENE_END_NAMESPACE()
1238