1#include "AssetWriter.h"
5#pragma warning(disable:4996)
7#include "rapidjson/document.h"
14#include "rapidjson/stringbuffer.h"
15#include "rapidjson/filewritestream.h"
16#include "rapidjson/prettywriter.h"
17#include "rapidjson/error/en.h"
19#include "Foundation/Logging/Logger.h"
23#include "EntityStore.h"
24#include "EntityDefinition.h"
26#include "Editor/IEditor.h"
28#include "Components/Core/AssetComponent.h"
29#include "Components/Core/TransformComponent.h"
31#include "Resources/Asset.h"
32#include "Resources/ResourceBase.h"
33#include "Resources/Texture.h"
34#include "Resources/MaterialInstance.h"
36#include "Services/Variables.h"
38#include "JsonMemoryBufferStream.h"
40#include "EntityWriter.h"
43using namespace rapidjson;
55 constexpr uint32_t NoValue =
static_cast<uint32_t
>(-1);
61 const char* currentErrnoString()
63 static char buff[256] =
"unknown";
65 strerror_s(buff,
sizeof(buff), errno);
67 strerror_r(errno, buff,
sizeof(buff));
75 MemoryPoolAllocator<CrtAllocator> alloc;
80 [[nodiscard]] Value encodeString(
const std::string& str)
82 return Value(str.data(), uint32_t(str.length()), alloc);
89 return Value(view.
data(), uint32_t(view.
length()), alloc);
93 template<
class ValueType>
94 [[nodiscard]] Value encodeSpan(std::span<ValueType> values)
96 Value vec(kArrayType);
97 for (
size_t i = 0; i < values.size(); i++) {
98 vec.PushBack(values[i], alloc);
106 Cogs::StringView string = definition.scene.properties.getString(propertyInfo);
107 return ctx.encodeString(
string);
110 [[nodiscard]]
bool writeDoc(WriterCtx& ctx, Value& entities, Value& resources,
const char* path)
112 Document doc(&ctx.alloc);
115 if (resources.MemberCount()) {
116 doc.AddMember(
"resources", resources, ctx.alloc);
119 if (!entities.Empty()) {
120 doc.AddMember(
"entities", entities, ctx.alloc);
123 Cogs::IO::createDirectories(path);
126 if ((ctx.writeFlags & AssetWriteFlags::PrettyPrint) == AssetWriteFlags::PrettyPrint) {
128 PrettyWriter<JsonMemoryBufferStream> writer(stream);
129 writer.SetIndent(
' ', 2);
130 writer.SetMaxDecimalPlaces(4);
135 Writer<JsonMemoryBufferStream> writer(stream);
136 writer.SetMaxDecimalPlaces(4);
140 if ((ctx.writeFlags & AssetWriteFlags::Compress) == AssetWriteFlags::Compress) {
141 int compressionLevel = ctx.context->variables->get(
"resources.compressionLevel", 0);
142 size_t uncompressedSize = buffer.size();
143 size_t maxSize = ZSTD_compressBound(uncompressedSize);
146 ZSTD_CCtx* zstd_ctx = ZSTD_createCCtx();
147 size_t result = ZSTD_compressCCtx(zstd_ctx, compressed.data(), maxSize, buffer.data(), uncompressedSize,compressionLevel);
148 if (ZSTD_isError(result)) {
149 LOG_ERROR(logger,
"%s: zstd compression failed: %s", path, ZSTD_getErrorName(result));
152 else if (uncompressedSize <= result) {
153 LOG_WARNING(logger,
"%s: compressed size greater or equal to uncompressed size", path);
156 compressed.resize(result);
157 buffer.swap(compressed);
159 ZSTD_freeCCtx(zstd_ctx);
162 FILE* file = std::fopen(path,
"wb");
164 LOG_ERROR(logger,
"Failed to open '%s' for writing: %s", path, currentErrnoString());
167 size_t n = fwrite(buffer.data(), 1, buffer.size(), file);
169 if (n != buffer.size()) {
170 LOG_ERROR(logger,
"Error writing to %s, wrote %zu of %zu: %s", path, n, buffer.size(), currentErrnoString());
176 [[nodiscard]]
bool writeEntityItem(WriterCtx& ctx, Value& entity,
const AssetDefinition& definition,
size_t entityIx);
178 [[nodiscard]]
bool writeBBoxProperty(WriterCtx& ctx, Value& entityPoperties,
const PropertyRange& propertyRange,
const char* what,
bool required)
181 std::span<const float> bboxValues = propertyRange.store->getFloatArray(ix);
182 if (bboxValues.size() == 6) {
183 Value bboxValue(kArrayType);
184 for (
size_t k = 0; k < 6; k++) { bboxValue.PushBack(Value(bboxValues[k]), ctx.alloc); }
185 entityPoperties.AddMember(
"bbox", bboxValue, ctx.alloc);
188 LOG_ERROR(logger,
"%s item bounding box has wrong number of elements (%zu != 6)", what, bboxValues.size());
191 else if (!required) {
194 LOG_ERROR(logger,
"%s item is missing bounding box property", what);
198 [[nodiscard]]
bool writeLodGroupItem(WriterCtx& ctx, Value& entity, Value& entityPoperties,
const AssetDefinition& definition,
size_t entityIx)
201 PropertyRange propertyRange = definition.scene.getProperties(entityDef);
203 entity.AddMember(
"type",
"LodGroup", ctx.alloc);
205 if (std::span<const uint32_t> ids = propertyRange.getProperty(idsString, std::span<const uint32_t>()); !ids.empty()) {
206 Value idsValue(kArrayType);
207 for (
const uint32_t
id : ids) {
208 idsValue.PushBack(
id, ctx.alloc);
210 entityPoperties.AddMember(
"ids", idsValue, ctx.alloc);
213 if (!writeBBoxProperty(ctx, entityPoperties, propertyRange,
"LodGroup",
true)) {
217 if (entityDef.
lod.numLods) {
219 std::span<const float> errorValues = propertyRange.getProperty(errorsString, std::span<const float>());
220 if (errorValues.size() == entityDef.
lod.numLods) {
221 Value value(kArrayType);
222 for (
size_t k = 0; k < errorValues.size(); k++) { value.PushBack(Value(errorValues[k]), ctx.alloc); }
223 entityPoperties.AddMember(
"errors", value, ctx.alloc);
226 LOG_ERROR(logger,
"Lod level item count (=%u) does not match number of error values (=%zu)", entityDef.
lod.numLods, errorValues.size());
230 Value lods(kArrayType);
232 uint32_t childIx = entityDef.firstChild;
233 for (
size_t k = 0; k < entityDef.
lod.numLods; k++) {
236 if (childIx == ::NoValue) {
237 lods.PushBack(Value(kObjectType), ctx.alloc);
243 if (definition.scene.entities.size() <= childIx) {
244 LOG_ERROR(logger,
"Illegal entity first child %u", childIx);
249 if (definition.scene[childIx].nextLodSibling == ::NoValue) {
250 Value child(kObjectType);
251 if (!writeEntityItem(ctx, child, definition, childIx))
return false;
252 lods.PushBack(child, ctx.alloc);
257 Value children(kArrayType);
260 Value child(kObjectType);
261 if (!writeEntityItem(ctx, child, definition, childIx))
return false;
262 children.PushBack(child, ctx.alloc);
264 if (definition.scene[childIx].nextLodSibling == ::NoValue)
break;
265 childIx = definition.scene[childIx].nextLodSibling;
268 lods.PushBack(children, ctx.alloc);
271 childIx = definition.scene[childIx].nextSibling;
274 entity.AddMember(
"lods", lods, ctx.alloc);
279 [[nodiscard]]
bool writeModelItem(WriterCtx& ctx, Value& entity, Value& entityPoperties,
const AssetDefinition& definition,
size_t entityIx)
282 if (entityDef.
model.index == ::NoValue) {
283 LOG_ERROR(logger,
"Model without unset source");
287 PropertyRange propertyRange = definition.scene.getProperties(entityDef);
288 if (propertyRange.numProperties) {
289 if(!writeBBoxProperty(ctx, entityPoperties, propertyRange,
"Model",
false)) {
296 const PropertyInfo& pathInfo = definition.scene.properties.getPropertyByIndex(entityDef.
model.index);
297 switch (pathInfo.type) {
298 case PropertyType::UnsignedInteger:
299 path.SetUint(pathInfo.uintValue);
301 case PropertyType::String:
302 path = getStringProperty(ctx, definition, pathInfo);
305 LOG_ERROR(logger,
"Model source is neither uint nor string");
310 if (entityDef.
model.part == ::NoValue) {
311 entity.AddMember(
"model", path, ctx.alloc);
316 Value modelRef(kObjectType);
317 modelRef.AddMember(
"source", path, ctx.alloc);
318 if (entityDef.
model.part != ::NoValue) {
319 modelRef.AddMember(
"part", entityDef.
model.part, ctx.alloc);
321 entity.AddMember(
"model", modelRef, ctx.alloc);
327 [[nodiscard]]
bool writeAssetItem(WriterCtx& ctx, Value& entity, Value& ,
const AssetDefinition& definition,
size_t entityIx)
331 if (entityDef.
asset.index == ::NoValue) {
332 LOG_ERROR(logger,
"Asset without source");
336 const PropertyInfo& sourceInfo = definition.scene.properties.getPropertyByIndex(entityDef.
asset.index);
337 switch (sourceInfo.type) {
338 case PropertyType::UnsignedInteger:
339 sourceValue.SetUint(sourceInfo.uintValue);
341 case PropertyType::String: {
342 sourceValue = getStringProperty(ctx, definition, sourceInfo);
346 LOG_ERROR(logger,
"Source property is neither uint nor string");
351 if (entityDef.
asset.
flags == 0 && entityDef.
asset.material == ::NoValue) {
352 entity.AddMember(
"asset", sourceValue, ctx.alloc);
357 Value assetValue(kObjectType);
359 std::string flagsString;
361 if ((flags & AssetFlags::InstantiateOnDemand) == AssetFlags::InstantiateOnDemand) {
362 flagsString.append(
"InstantiateOnDemand | ");
365 if ((flags & AssetFlags::RelativePaths) == AssetFlags::RelativePaths) {
366 flagsString.append(
"RelativePaths | ");
369 if ((flags & AssetFlags::OverrideMaterial) == AssetFlags::OverrideMaterial) {
370 flagsString.append(
"OverrideMaterial | ");
373 if (flags != AssetFlags::None) {
374 LOG_ERROR(logger,
"Unhandled asset flags");
378 assert(3 < flagsString.length());
379 const Cogs::StringView writeFlagsString(flagsString.c_str(), flagsString.size() - 3);
380 assetValue.AddMember(
"flags", ctx.encodeString(writeFlagsString), ctx.alloc);
383 assetValue.AddMember(
"source", sourceValue, ctx.alloc);
385 if (entityDef.
asset.material != ::NoValue) {
386 const PropertyInfo& materialInfo = definition.scene.properties.getPropertyByIndex(entityDef.
asset.material);
387 if (materialInfo.type != PropertyType::String) {
388 LOG_ERROR(logger,
"Material property type isn't a string");
391 assetValue.AddMember(
"material", getStringProperty(ctx, definition, materialInfo), ctx.alloc);
394 entity.AddMember(
"asset", assetValue, ctx.alloc);
398 [[nodiscard]] Value encodeFieldValue(WriterCtx& ctx,
const FieldValue& fieldValue)
400 switch (fieldValue.
type) {
401 case DefaultValueType::Bool:
return Value(fieldValue.boolValue);
402 case DefaultValueType::Float:
return Value(
double(fieldValue.floatValue));
403 case DefaultValueType::Int:
return Value(fieldValue.intValue);
404 case DefaultValueType::Enum:
return Value(fieldValue.intValue);
405 case DefaultValueType::Vec2:
return ctx.encodeSpan(std::span<const float>(glm::value_ptr(fieldValue.float2), 2));
406 case DefaultValueType::Vec3:
return ctx.encodeSpan(std::span<const float>(glm::value_ptr(fieldValue.float3), 3));
407 case DefaultValueType::Vec4:
return ctx.encodeSpan(std::span<const float>(glm::value_ptr(fieldValue.float4), 4));
408 case DefaultValueType::Mat4:
return ctx.encodeSpan(std::span<const float>(glm::value_ptr(fieldValue.mat4), 16));
409 case DefaultValueType::DVec2:
return ctx.encodeSpan(std::span<const double>(glm::value_ptr(fieldValue.double2), 2));
410 case DefaultValueType::DVec3:
return ctx.encodeSpan(std::span<const double>(glm::value_ptr(fieldValue.double3), 3));
411 case DefaultValueType::DVec4:
return ctx.encodeSpan(std::span<const double>(glm::value_ptr(fieldValue.double4), 4));
412 case DefaultValueType::Quaternion:
return ctx.encodeSpan(std::span<const float>(glm::value_ptr(fieldValue.quat), 4));
413 case DefaultValueType::String:
414 case DefaultValueType::Model:
415 case DefaultValueType::Asset:
416 case DefaultValueType::Texture:
417 case DefaultValueType::Gui:
return ctx.encodeString(fieldValue.value);
418 case DefaultValueType::MaterialInstance: {
420 if (matDef && !matDef->reference.empty()) {
421 return ctx.encodeString(std::string(
"$") + matDef->reference);
425 case DefaultValueType::Unknown:
426 case DefaultValueType::Mesh:
427 case DefaultValueType::MultiString:
428 case DefaultValueType::IntArray:
429 case DefaultValueType::FloatArray:
430 case DefaultValueType::Vec2Array:
431 case DefaultValueType::Vec3Array:
432 case DefaultValueType::Vec4Array:
433 case DefaultValueType::Entity:
434 case DefaultValueType::EntityArray:
435 case DefaultValueType::Extension:
436 LOG_ERROR(logger,
"Unhandled value type %u", uint32_t(fieldValue.
type));
439 assert(
false &&
"Illegal enum value");
444 [[nodiscard]]
bool writeEntityItem(WriterCtx& ctx, Value& entity,
const AssetDefinition& definition,
size_t entityIx)
447 PropertyRange propertyRange = definition.scene.getProperties(entityDef);
449 if ((ctx.writeFlags & AssetWriteFlags::Strip) != AssetWriteFlags::Strip) {
450 if (entityDef.nameIndex != ::NoValue) {
451 Cogs::StringView name = definition.scene.properties.getString(definition.scene.properties.getPropertyByIndex(entityDef.nameIndex));
453 entity.AddMember(
"name", ctx.encodeString(name), ctx.alloc);
458 Value entityPoperties(kObjectType);
459 if (entityDef.isLodGroup()) {
460 if (!writeLodGroupItem(ctx, entity, entityPoperties, definition, entityIx))
return false;
462 else if (entityDef.isModel()) {
463 if (!writeModelItem(ctx, entity, entityPoperties, definition, entityIx))
return false;
465 else if (entityDef.isAsset()) {
466 if (!writeAssetItem(ctx, entity, entityPoperties, definition, entityIx))
return false;
468 else if(!entityDef.isEmpty()) {
471 if (!entityType.
empty()) {
472 entity.AddMember(
"type", ctx.encodeString(entityType), ctx.alloc);
475 LOG_WARNING(logger,
"Entity without type");
479 if (entityDef.numFields) {
480 for (
const FieldValue& entry : std::span(definition.scene.fieldValues).subspan(entityDef.firstField, entityDef.numFields)) {
483 LOG_WARNING(logger,
"Missing field component type, ignoring");
488 LOG_WARNING(logger,
"Missing field id, ignoring");
495 LOG_ERROR(logger,
"Field %u not found on type %s.", uint32_t(entry.fieldId), componentType.
getName().
c_str());
503 Value keyValue = ctx.encodeString(key);
505 entity.AddMember(keyValue, encodeFieldValue(ctx, entry), ctx.alloc);
509 if (entityDef.numProperties) {
510 for (uint32_t i = 0; i < entityDef.numProperties; i++) {
511 const PropertyInfo& propInfo = definition.scene.properties.getPropertyByIndex(entityDef.firstProperty + i);
513 Value keyValue = ctx.encodeString(Strings::get(propInfo.key));
515 switch (propInfo.type) {
516 case PropertyType::Unknown:
517 LOG_ERROR(logger,
"Unhandled property type %u", uint32_t(propInfo.type));
519 case PropertyType::Bool:
520 entityPoperties.AddMember(keyValue, propInfo.boolValue, ctx.alloc);
522 case PropertyType::Integer:
523 entityPoperties.AddMember(keyValue, propInfo.intValue, ctx.alloc);
525 case PropertyType::Int2:
526 entityPoperties.AddMember(keyValue, ctx.encodeSpan(std::span<const int32_t>(propInfo.int2Value, 2)), ctx.alloc);
528 case PropertyType::UnsignedInteger:
529 entityPoperties.AddMember(keyValue, propInfo.uintValue, ctx.alloc);
531 case PropertyType::UInt2:
532 entityPoperties.AddMember(keyValue, ctx.encodeSpan(std::span<const uint32_t>(propInfo.uint2Value, 2)), ctx.alloc);
534 case PropertyType::Float:
535 entityPoperties.AddMember(keyValue, propInfo.floatValue, ctx.alloc);
537 case PropertyType::Float2:
538 entityPoperties.AddMember(keyValue, ctx.encodeSpan(std::span<const float>(propInfo.float2Value, 2)), ctx.alloc);
540 case PropertyType::Double:
541 entityPoperties.AddMember(keyValue, propInfo.doubleValue, ctx.alloc);
543 case PropertyType::StringRef:
544 entityPoperties.AddMember(keyValue, ctx.encodeString(Strings::get(propInfo.stringRefValue)), ctx.alloc);
546 case PropertyType::String:
547 entityPoperties.AddMember(keyValue, ctx.encodeString(definition.scene.properties.getString(propInfo)), ctx.alloc);
549 case PropertyType::FloatArray:
550 entityPoperties.AddMember(keyValue, ctx.encodeSpan(definition.scene.properties.getFloatArray(propInfo)), ctx.alloc);
552 case PropertyType::IntArray:
553 entityPoperties.AddMember(keyValue, ctx.encodeSpan(definition.scene.properties.getIntArray(propInfo)), ctx.alloc);
555 case PropertyType::UIntArray:
556 entityPoperties.AddMember(keyValue, ctx.encodeSpan(definition.scene.properties.getUIntArray(propInfo)), ctx.alloc);
558 case PropertyType::DoubleArray:
559 entityPoperties.AddMember(keyValue, ctx.encodeSpan(definition.scene.properties.getDoubleArray(propInfo)), ctx.alloc);
562 assert(
false &&
"Illegal enum value");
569 if (entityPoperties.MemberCount()) {
570 entity.AddMember(
"properties", entityPoperties, ctx.alloc);
580 std::string saveFileName(fileName);
582 auto file = fopen(saveFileName.c_str(),
"w");
583 if (file ==
nullptr) {
584 LOG_ERROR(logger,
"Failed to open output file: %s", saveFileName.c_str());
588 std::vector<char> buffer(16384);
590 FileWriteStream stream(file, buffer.data(), buffer.size());
591 PrettyWriter<FileWriteStream> out(stream);
594 ctx.context = context;
595 ctx.writeFlags = flags;
598 auto & a = d.GetAllocator();
602 Value metaValue(kObjectType);
604 metaValue.AddMember(
"flags",
"true", a);
606 d.AddMember(
"meta", metaValue, a);
610 Value resourcesValue(kObjectType);
611 Value entitiesValue(kArrayType);
613 auto store = context->
store;
618 for (
auto & e : entities) {
619 auto entity = e.second;
624 if (transformComponent && transformComponent->parent)
continue;
626 Value entityValue(kObjectType);
628 writeEntity(context, entity.get(), entityValue, d, &sc, flags);
630 entitiesValue.PushBack(entityValue, a);
634 for (
auto& r : sc.assets) {
635 Value resourceValue(kObjectType);
636 resourceValue.AddMember(
"type",
"Asset", a);
637 resourceValue.AddMember(
"source", ctx.encodeString(r->getSource()), a);
639 resourcesValue.AddMember(ctx.encodeString(r->getName()), resourceValue, a);
642 for (
auto & r : sc.models) {
643 Value resourceValue(kObjectType);
644 resourceValue.AddMember(
"type",
"Model", a);
645 resourceValue.AddMember(
"source", ctx.encodeString(r->getSource()), a);
647 resourcesValue.AddMember(ctx.encodeString(r->getName()), resourceValue, a);
650 Value materialInstances(kObjectType);
652 for (
auto & r : sc.materialInstances) {
654 auto material = instance->material;
656 Value resourceValue(kObjectType);
657 resourceValue.AddMember(
"type",
"MaterialInstance", a);
658 resourceValue.AddMember(
"material", ctx.encodeString(material->getName()), a);
659 resourceValue.AddMember(
"permutation", ctx.encodeString(material->permutationKeys[instance->permutationIndex]), a);
661 for (
auto & tProp : material->textureProperties) {
662 auto instanceValue = instance->getTextureProperty(tProp.key);
663 if (
HandleIsValid(instanceValue.texture.handle) && instanceValue.texture.handle != tProp.texture.handle) {
664 auto texName = instanceValue.texture.handle->getName();
665 texName =
"$" + texName.to_string();
667 resourceValue.AddMember(ctx.encodeString(tProp.name), ctx.encodeString(texName), a);
669 sc.textures.insert(instanceValue.texture.handle);
673 for (
auto & prop : material->constantBuffers.variables) {
675 case MaterialDataType::Float:
677 auto instanceValue = instance->getFloatProperty(prop.key);
679 if (instanceValue != prop.defaultFloat()) {
680 resourceValue.AddMember(ctx.encodeString(prop.name), Value((
double)instanceValue), a);
685 case MaterialDataType::Float4:
687 auto instanceValue = instance->getVec4Property(prop.key);
689 if (instanceValue != prop.defaultVec4()) {
693 for (
int i = 0; i < 4; ++i) {
694 v.PushBack(instanceValue[i], a);
697 resourceValue.AddMember(ctx.encodeString(prop.name), v, a);
702 case MaterialDataType::Float3:
704 auto instanceValue = instance->getVec3Property(prop.key);
706 if (instanceValue != prop.defaultVec3()) {
710 for (
int i = 0; i < 3; ++i) {
711 v.PushBack(instanceValue[i], a);
714 resourceValue.AddMember(ctx.encodeString(prop.name), v, a);
719 case MaterialDataType::Float2:
721 auto instanceValue = instance->getVec2Property(prop.key);
723 if (instanceValue != prop.defaultVec2()) {
727 for (
int i = 0; i < 2; ++i) {
728 v.PushBack(instanceValue[i], a);
731 resourceValue.AddMember(ctx.encodeString(prop.name), v, a);
736 case MaterialDataType::UInt:
738 uint32_t instanceValue = instance->getProperty<uint32_t>(prop.key);
739 if (instanceValue != prop.defaultUInt()) {
740 resourceValue.AddMember(ctx.encodeString(prop.name), Value(instanceValue), a);
745 case MaterialDataType::Bool:
747 bool instanceValue = instance->getProperty<
bool>(prop.key);
748 if (instanceValue != prop.defaultBool()) {
749 resourceValue.AddMember(ctx.encodeString(prop.name), Value(instanceValue ?
"true" :
"false", a), a);
755 LOG_WARNING(logger,
"Skipped serialization of unsupported material property %s. Typeid:%s", prop.name.c_str(), Cogs::Core::DataTypeNames[
int(prop.type)]);
760 Value variantsValue(kObjectType);
762 const ShaderVariants& variants = material->definition.variants;
766 if (variant.isShared)
continue;
768 if (selector.value != variant.defaultValue) {
769 switch (variant.type)
771 case ShaderVariantType::Bool:
774 b.SetBool(selector.value != 0);
776 variantsValue.AddMember(ctx.encodeString(variant.name), b, a);
779 case ShaderVariantType::Enum:
782 b.SetString(variant.values[selector.value].key.c_str(), a);
784 variantsValue.AddMember(ctx.encodeString(variant.name), b, a);
787 case ShaderVariantType::Int:
790 i.SetInt((
int)selector.value);
792 variantsValue.AddMember(ctx.encodeString(variant.name), i, a);
796 LOG_WARNING(logger,
"Skipped serialization of unsupported material variant %s", variant.name.c_str());
802 if (variantsValue.MemberCount()) {
803 resourceValue.AddMember(
"variants", variantsValue, a);
806 materialInstances.AddMember(ctx.encodeString(r->getName()), resourceValue, a);
809 for (
auto & r : sc.textures) {
810 auto texture = (
Texture *)r.get();
812 Value resourceValue(kObjectType);
813 resourceValue.AddMember(
"type",
"Texture", a);
814 resourceValue.AddMember(
"source", ctx.encodeString(r->getSource()), a);
816 if (texture->description.format != TextureFormat::R8G8B8A8_UNORM_SRGB) {
817 resourceValue.AddMember(
"flags",
"LinearColorSpace", a);
820 resourcesValue.AddMember(ctx.encodeString(r->getName()), resourceValue, a);
823 for (
auto & m : materialInstances.GetObject()) {
824 resourcesValue.AddMember(m.name, m.value, a);
827 if (resourcesValue.MemberCount()) {
828 d.AddMember(
"resources", resourcesValue, a);
831 d.AddMember(
"entities", entitiesValue, a);
834 bool ok = d.Accept(out);
836 LOG_ERROR(logger,
"JSon serialization failed.");
845 LOG_ERROR(logger,
"Write specific entity not supported (only null allowed).");
852 LOG_ERROR(logger,
"Undefined asset");
857 if (!asset->isLoaded()) {
858 LOG_ERROR(logger,
"Asset is not loaded");
864 ctx.context = context;
865 ctx.writeFlags = writeFlags;
867 Value resources(kObjectType);
870 if (resDef.name.empty()) {
871 LOG_ERROR(logger,
"Resource without name, ignoring");
875 Value resource(kObjectType);
876 switch (resDef.type) {
877 case ResourceTypes::Unknown:
878 LOG_ERROR(logger,
"Unknown resource type encountered, ignoring");
880 case ResourceTypes::Asset: resource.AddMember(
"type",
"Asset", ctx.alloc);
break;
881 case ResourceTypes::Model: resource.AddMember(
"type",
"Model", ctx.alloc);
break;
882 case ResourceTypes::Texture: resource.AddMember(
"type",
"Texture", ctx.alloc);
break;
883 case ResourceTypes::Effect: resource.AddMember(
"type",
"Effect", ctx.alloc);
break;
884 case ResourceTypes::Material: resource.AddMember(
"type",
"Material", ctx.alloc);
break;
885 case ResourceTypes::MaterialInstance: resource.AddMember(
"type",
"MaterialInstance", ctx.alloc);
break;
886 case ResourceTypes::Mesh: resource.AddMember(
"type",
"Mesh", ctx.alloc);
break;
887 case ResourceTypes::Buffer: resource.AddMember(
"type",
"Buffer", ctx.alloc);
break;
888 case ResourceTypes::Font: resource.AddMember(
"type",
"Font", ctx.alloc);
890 case ResourceTypes::Gui:
891 resource.AddMember(
"type",
"Gui", ctx.alloc);
894 assert(
false &&
"Invalid enum");
897 if (!resDef.source.empty()) {
898 resource.AddMember(
"source", ctx.encodeString(resDef.source), ctx.alloc);
901 switch (resDef.type) {
902 case ResourceTypes::Material:
903 case ResourceTypes::MaterialInstance:
904 if (!resDef.material.empty()) resource.AddMember(
"material", ctx.encodeString(resDef.material), ctx.alloc);
905 if (!resDef.permutation.empty()) resource.AddMember(
"permutation", ctx.encodeString(resDef.material), ctx.alloc);
906 if (!resDef.options.empty()) {
907 Value options(kObjectType);
908 for (
const auto& item : resDef.options) {
909 options.AddMember(ctx.encodeString(item.first), ctx.encodeString(item.second), ctx.alloc);
911 resource.AddMember(
"options", options, ctx.alloc);
913 if (!resDef.variants.empty()) {
914 Value options(kObjectType);
915 for (
const auto& item : resDef.variants) {
916 options.AddMember(ctx.encodeString(item.first), ctx.encodeString(item.second), ctx.alloc);
918 resource.AddMember(
"variants", options, ctx.alloc);
920 if (!resDef.properties.empty()) {
921 for (
auto& item : resDef.properties) {
922 resource.AddMember(ctx.encodeString(item.first), encodeFieldValue(ctx, item.second), ctx.alloc);
932 resources.AddMember(ctx.encodeString(resDef.name), resource, ctx.alloc);
935 Value roots(kArrayType);
936 for (
size_t i = 0; i < definition.scene.entities.size(); i++) {
938 if (entityDef.parentIndex == ::NoValue) {
940 Value entity(kObjectType);
941 if (!writeEntityItem(ctx, entity, definition, i))
return false;
942 roots.PushBack(entity, ctx.alloc);
946 return writeDoc(ctx, roots, resources, path);
Container for components, providing composition of dynamic entities.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
class EntityStore * store
Entity store.
const std::unordered_map< EntityId, EntityPtr > & getEntities() const
Return map of entities with global ownership.
Log implementation class.
Field definition describing a single data member of a data structure.
const Name & getName() const
Get the name of the field.
static const Type & getType()
Get the Type of the given template argument.
Represents a discrete type definition, describing a native type class.
constexpr const Name & getName() const
Get the unique name of the type.
const Field * getField(const Name &name) const
Get a pointer to the field info of the field with the given name.
Provides a weakly referenced view over the contents of a string.
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
constexpr size_t length() const noexcept
Get the length of the string.
constexpr bool empty() const noexcept
Check if the string is empty.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
AssetWriteFlags
Flags that control serialization of assets.
void COGSCORE_DLL_API writeEntity(Context *context, ComponentModel::Entity *entity, rapidjson::Value &object, rapidjson::Document &d, SerializationContext *sc=nullptr, const AssetWriteFlags flags=AssetWriteFlags::None)
Serialize entity adding entity to the given parent 'object'.
AssetFlags
Controls asset system's model instance behaviour.
@ InstantiateOnDemand
Dynamically calculate lod levels and only instantiate what is visible.
@ RelativePaths
Paths in asset file is relative to file's path.
@ OverrideMaterial
Override any model's material with material member of asset component.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains reflection support.
constexpr FieldId NoField
No field id.
constexpr TypeId NoType
Definition of no type.
Defines a value to apply to a field.
DefaultValueType type
Type of the field.
Material instances represent a specialized Material combined with state for all its buffers and prope...
static constexpr uint32_t NoProperty
Return from findProperty if key not found.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
struct Cogs::Core::SceneEntityDefinition::@28::@31 model
SceneEntityFlags::Model is set.
uint32_t flags
Really enum of SceneEntityFlags.
struct Cogs::Core::SceneEntityDefinition::@28::@32 lod
SceneEntityFlags::LodGroup is set.
StringRef type
Neither SceneEntityFlags::Asset, SceneEntityFlags::Model, nor SceneEntityFlags::LodGroup is set.
struct Cogs::Core::SceneEntityDefinition::@28::@30 asset
SceneEntityFlags::Asset is set.
Texture resources contain raster bitmap data to use for texturing.
const char * c_str() const
Gets the name as a null-terminated string.