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