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