Cogs.Core
SceneReader.cpp
1#include "SceneReader.h"
2
3#include <vector>
4
5#include "Context.h"
6
7#include "EntityPtr.h"
8#include "EntityStore.h"
9
10#include "Foundation/Logging/Logger.h"
11
12#include "Utilities/Expressions.h"
13#include "Utilities/Parsing.h"
14
15#include "Components/Core/PropertiesComponent.h"
16#include "Components/Core/AssetComponent.h"
17
18#include "Resources/AssetManager.h"
19
20#include "EntityReader.h"
21#include "EntityCreator.h"
22#include "ResourceReader.h"
23
24#include <algorithm>
25
26namespace
27{
28 using namespace Cogs::Core;
29
31 constexpr size_t PreAllocSize = 64ULL;
32
34 constexpr uint32_t NoIndex = static_cast<uint32_t>(-1);
35
36 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("SceneReader");
37
38 bool isTypeSomethingDifferent(const AssetDefinition& /*asset*/, const SceneEntityDefinition& entity, Cogs::StringView newTypeName)
39 {
40 if (entity.isAssetModelOrLodGroup() || ((entity.type != NoString) && (Strings::get(entity.type) != newTypeName))) {
41 LOG_WARNING(logger, "Type already set to '%.*s', cannot set type to '%.*s'",
42 StringViewFormat(entity.getTypeName()),
43 StringViewFormat(newTypeName));
44 return true;
45 }
46 return false;
47 }
48
49
50 void loopValues(Context* context, const Value& v, AssetDefinition& asset, SceneDefinition& sceneDef, uint32_t entityId, ExpressionContext* ev, size_t i)
51 {
52 auto& val = ev->getValues()[i];
53
54 const uint32_t index = sceneDef.entities[entityId].index;
55
56 val.value = val.min;
57 while (val.value <= val.max) {
58 if (i == 0) {
59 for (auto& c : v.GetArray()) {
60 uint32_t child = asset.scene.createEntity(index);
61 readSceneEntityDefinition(context, c, asset, asset.scene, child, ev);
62 asset.scene[index].numChildren++;
63 }
64 }
65 else {
66 loopValues(context, v, asset, asset.scene, index, ev, i - 1);
67 }
68
69 val.value += val.step;
70 ++val.sequenceNumber;
71 }
72 }
73
74 void replace(std::string& str, const std::string& from, const std::string& to)
75 {
76 size_t start_pos = str.find(from);
77 if (start_pos != std::string::npos) {
78 str.replace(start_pos, from.length(), to);
79 }
80 }
81
82 void parseModelItem(Context* /*context*/, AssetDefinition& asset, SceneEntityDefinition& entity, const Value& modelValue)
83 {
84 SceneDefinition& scene = asset.scene;
85
86 if (isTypeSomethingDifferent(asset, entity, "Model")) {
87 entity.setError();
88 return;
89 }
90
91 entity.setModel();
92 entity.model.part = NoIndex;
93 entity.model.size = 0;
94
95 uint32_t lodDepth = 0;
96 uint32_t pIndex = entity.parentIndex;
97 while (pIndex != NoIndex) {
98 const SceneEntityDefinition& e = scene[pIndex];
99
100 if (e.isLodGroup()) ++lodDepth;
101
102 pIndex = e.parentIndex;
103 }
104 entity.model.depth = (uint16_t)lodDepth;
105
106 if (modelValue.IsObject()) {
107 for (auto& mm : modelValue.GetObject()) {
108
109 Cogs::StringView key = toView(mm.name);
110 switch (Cogs::hash(key)) {
111
112 case Cogs::hash("source"):
113 if (mm.value.IsString()) {
114 Cogs::StringView source = toView(mm.value);
115 if (!source.empty()) {
116 entity.model.index = scene.properties.addProperty("model", source);
117 }
118 else {
119 LOG_ERROR(logger, "In 'model', value of 'source' is empty string");
120 entity.setError();
121 }
122 }
123 else if (mm.value.IsUint()) {
124 // Compact notation, number gets interpreted as the filename xxxx/xxxx.cogsbin
125 entity.model.index = scene.properties.addProperty("model", mm.value.GetUint());
126 }
127 else {
128 LOG_ERROR(logger, "In 'model', value of 'source' is neither string nor uint");
129 entity.setError();
130 }
131 break;
132
133 case Cogs::hash("offset"):
134 case Cogs::hash("part"):
135 if (mm.value.IsUint()) {
136 entity.model.part = mm.value.GetUint();
137 }
138 else if (mm.value.IsInt() && mm.value.GetInt() == -1) {
139 entity.model.part = NoIndex;
140 }
141 else {
142 LOG_ERROR(logger, "In 'model', value of '%.*s' is not an uint", StringViewFormat(key));
143 entity.setError();
144 }
145 break;
146
147 case Cogs::hash("kb"):
148 if (mm.value.IsUint()) {
149 entity.model.size = mm.value.GetUint();
150 }
151 else {
152 LOG_ERROR(logger, "In 'model', value of kb is not an uint");
153 entity.setError();
154 }
155 break;
156
157 default:
158 LOG_WARNING(logger, "In 'model', unrecognized key '%.*s'", StringViewFormat(key));
159 break;
160 }
161 }
162 }
163
164 else if (modelValue.IsString()) {
165 Cogs::StringView source = toView(modelValue);
166 if (!source.empty()) {
167 entity.model.index = scene.properties.addProperty("asset", source);
168 }
169 else {
170 entity.setError();
171 }
172 }
173
174 else if (modelValue.IsUint()) {
175 entity.model.index = scene.properties.addProperty("model", modelValue.GetUint());
176 }
177
178 else {
179 entity.setError();
180 LOG_ERROR(logger, "Invalid model value.");
181 }
182
183 }
184
185 void parseAssetItem(Context* context, AssetDefinition& asset, SceneEntityDefinition& entity, ExpressionContext* ec, const Value& assetValue)
186 {
187 SceneDefinition& scene = asset.scene;
188
189 if (isTypeSomethingDifferent(asset, entity, "Asset")) {
190 entity.setError();
191 return;
192 }
193
194 entity.setAsset();
195 entity.asset.material = NoIndex;
196
197 if (assetValue.IsObject()) {
198 for (auto& mm : assetValue.GetObject()) {
199
200 Cogs::StringView key = toView(mm.name);
201 switch (Cogs::hash(key)) {
202
203 case Cogs::hash("source"):
204 if (mm.value.IsString()) {
205 entity.asset.index = scene.properties.addProperty("asset", toKey(mm.value));
206 }
207 else if (mm.value.IsUint()) {
208 entity.asset.index = scene.properties.addProperty("asset", mm.value.GetUint());
209 }
210 else {
211 LOG_ERROR(logger, "In 'asset', value of 'source' is neither a string nor an uint");
212 }
213 break;
214
215 case Cogs::hash("flags"):
216 if (mm.value.IsString()) {
217 entity.asset.flags = uint32_t(parseEnumFlags(toView(mm.value), AssetFlags::None));
218 }
219 else if (mm.value.IsUint()) {
220 // Compact notation, store numeric value directly
221 entity.asset.flags = mm.value.GetUint();
222 }
223 else {
224 LOG_ERROR(logger, "In 'asset', value of 'flags' is neither a string nor an uint");
225 }
226 break;
227
228 case Cogs::hash("material"):
229 if (mm.value.IsString()) {
230 entity.asset.material = scene.properties.addProperty("material", toView(mm.value));
231 }
232 else if (mm.value.IsObject()) {
233 ResourceDefinition& resource = asset.resources.emplace_back();
234 ReaderContext rc;
235 rc.context = context;
236 rc.asset = nullptr;
237 rc.ec = ec;
238 readMaterialInstanceDefinition(&rc, mm.value, resource);
239 // FIXME: doesn't look like entity.asset.material gets set?
240 }
241 else {
242 LOG_ERROR(logger, "In 'asset', value of 'material' is neither a string nor an object");
243 }
244 break;
245
246 default:
247 LOG_WARNING(logger, "In 'asset', unrecognized key '%.*s'", StringViewFormat(key));
248 break;
249 }
250 }
251 }
252
253 else if (assetValue.IsString()) {
254 Cogs::StringView source = toView(assetValue);
255 if (!source.empty()) {
256 entity.asset.index = scene.properties.addProperty("asset", source);
257 }
258 else {
259 entity.setError();
260 }
261 }
262
263 else if (assetValue.IsUint()) {
264 entity.asset.index = scene.properties.addProperty("asset", assetValue.GetUint());
265 }
266
267 else {
268 entity.setError();
269 LOG_ERROR(logger, "Invalid asset value.");
270 }
271 }
272
273}
274
275
276void Cogs::Core::readSceneEntityDefinition(Context* context, const Value& jsonEntity, AssetDefinition& asset, SceneDefinition& sceneDef, const uint32_t entityId, ExpressionContext* ec)
277{
278 if (!jsonEntity.IsObject()) {
279 sceneDef[entityId].setError();
280 LOG_ERROR(logger, "JSON parse Error in scene entity definition.");
281 return;
282 }
283
284 SceneDefinition& scene = asset.scene;
285
286 if (jsonEntity.GetObject().ObjectEmpty()) {
287 sceneDef[entityId].setEmpty();
288 return;
289 }
290
291 sceneDef[entityId].firstField = uint32_t(scene.fieldValues.size());
292 sceneDef[entityId].firstChild = uint32_t(scene.entities.size());
293
294 auto o = jsonEntity.GetObject();
295
296 for (auto& m : o) {
297 StringView key = toKey(m.name);
298
299 if (key == "name") {
300 std::string nameStr = m.value.GetString();
301
302 if (ec && nameStr.find('%') != std::string::npos) {
303
304 ExpressionContext* tec = ec;
305 do {
306 for (const ExpressionVariable& value : tec->getValues()) {
307 replace(nameStr, "%" + value.name, std::to_string(value.sequenceNumber));
308 }
309 tec = tec->getParent();
310 } while (tec != nullptr);
311
312 }
313
314 sceneDef[entityId].nameIndex = scene.properties.addProperty("name", nameStr);
315 }
316 else if (key == "objectId") {
317 sceneDef[entityId].objectId = m.value.GetUint();
318 }
319 else if (key == "type") {
320 StringView type = StringView(m.value.GetString());
321 if (isTypeSomethingDifferent(asset, sceneDef[entityId], type)) {
322 continue;
323 }
324 sceneDef[entityId].type = Strings::add(type);
325 }
326 else if (key == "model") {
327 parseModelItem(context, asset, sceneDef[entityId], m.value);
328 }
329 else if (key == "asset") {
330 parseAssetItem(context, asset, sceneDef[entityId], ec, m.value);
331 }
332 else if (key == "parent") {
333 uint32_t parentPropertyIndex = scene.properties.addProperty("parent", toKey(m.value));
334 sceneDef[entityId].parentIndex = parentPropertyIndex;
335 sceneDef[entityId].flags |= SceneEntityFlags::ParentIsName;
336 }
337 else if (key == "properties") {
338 PropertyStore& properties = scene.properties;
339
340 SceneEntityDefinition& entity = sceneDef[entityId];
341 entity.firstProperty = uint32_t(scene.properties.size());
342
343 for (auto& c : m.value.GetObject()) {
344 StringView propName = toKey(c.name);
345 ++entity.numProperties;
346
347 if (c.value.IsBool()) {
348 properties.addProperty(propName, c.value.GetBool());
349 }
350 else if (c.value.IsString()) {
351 properties.addProperty(propName, toKey(c.value));
352 }
353 else if (c.value.IsInt()) {
354 properties.addProperty(propName, c.value.GetInt());
355 }
356 else if (c.value.IsFloat()) {
357 properties.addProperty(propName, c.value.GetFloat());
358 }
359 else if (c.value.IsArray()) {
360 auto valueArray = c.value.GetArray();
361
362 if (valueArray.Empty()) {
363 LOG_WARNING(logger, "Ignoring empty array in property \"%.*s\"", StringViewFormat(propName));
364 }
365 else {
366
367 // Convert known array properties to expected type.
368 switch (toKey(c.name).hash()) {
369 case hash("ids"):
370 {
371 std::vector<uint32_t> uints(valueArray.Size());
372 for (uint32_t i = 0; i < valueArray.Size(); ++i) {
373 uints[i] = valueArray[i].GetUint();
374 }
375 properties.addProperty(propName, uints);
376 break;
377 }
378
379 default:
380 {
381 std::vector<float> floats(valueArray.Size());
382 for (uint32_t i = 0; i < valueArray.Size(); ++i) {
383 floats[i] = valueArray[i].GetFloat();
384 }
385 properties.addProperty(propName, floats);
386 break;
387 }
388 }
389
390 }
391
392
393
394
395 }
396 else {
397 StringView name = toKey(c.name);
398 LOG_WARNING(logger, "Unsupported property type for key \"%.*s\".", StringViewFormat(name));
399 --entity.numProperties;
400 }
401 }
402 }
403 else if (key == "children" || key == "lods") {
404 // Skip
405 }
406 else if (key == "sequence") {
407 sceneDef[entityId].setSequence();
408
409 ExpressionContext expressionContext;
410
411 expressionContext.inherit(ec);
412
413 for (auto& v : m.value.GetObject()) {
414 if (v.name == "variables") {
415 for (auto& vv : v.value.GetObject()) {
416
417 ExpressionVariable& var = expressionContext.add(toKey(vv.name), 0);
418
419 if (vv.value.IsObject()) {
420 if (!vv.value.HasMember("min") || !vv.value.HasMember("max") || !vv.value.HasMember("step")) {
421 LOG_ERROR(logger, "Key variables: Invalid range specification.");
422 continue;
423 }
424 var.min = vv.value["min"].GetDouble();
425 var.max = vv.value["max"].GetDouble();
426 var.step = vv.value["step"].GetDouble();
427 }
428 else if (vv.value.IsArray()) {
429 if (vv.value.Size() != 3) {
430 LOG_ERROR(logger, "Key variables: Invalid number of elements in array.");
431 continue;
432 }
433 var.min = vv.value[0].GetDouble();
434 var.max = vv.value[1].GetDouble();
435 var.step = vv.value[2].GetDouble();
436 }
437 }
438 }
439 }
440
441 for (auto& v : m.value.GetObject()) {
442 if (v.name == "entities") {
443 loopValues(context, v.value, asset, sceneDef, entityId, &expressionContext, expressionContext.getValues().size() - 1);
444 }
445 }
446 }
447 else {
448 if (scene.fieldValues.empty()) {
449 scene.fieldValues.reserve(PreAllocSize);
450 }
451
452 TokenStream tokens;
453 split(key, ".", tokens);
454
455 if (tokens.size() == 2) {
456 // (Component . field) pair
457 const Reflection::Type& type = Reflection::TypeDatabase::getType(tokens[0]);
458
459 if (readFieldValue(context, m.value, type, tokens[1], scene.fieldValues, ec)) {
460 ++sceneDef[entityId].numFields;
461 }
462 }
463 else if (tokens.size() == 1) {
464 size_t before = scene.fieldValues.size();
465 readFieldValues(context, m.value, key, scene.fieldValues, ec);
466 size_t after = scene.fieldValues.size();
467
468 sceneDef[entityId].numFields += uint32_t(after - before);
469 }
470 else {
471 LOG_ERROR(logger, "Unrecognized property name %.*s.", StringViewFormat(key));
472 }
473 }
474 }
475
476 uint32_t index = sceneDef[entityId].index;
477
478 for (auto& m : o) {
479 StringView key = toKey(m.name);
480
481 if (key == "children") {
482 if (!m.value.IsArray()) {
483 LOG_ERROR(logger, "children property must be array type.");
484 sceneDef[entityId].setError();
485 continue;
486 }
487 uint32_t prevChild = NoIndex;
488 if (m.value.GetArray().Empty()) {
489 continue;
490 }
491 for (auto& c : m.value.GetArray()) {
492 uint32_t child = scene.createEntity(index);
493 if (prevChild != NoIndex) {
494 scene[prevChild].nextSibling = child;
495 }
496 prevChild = child;
497 ++scene[index].numChildren;
498 readSceneEntityDefinition(context, c, asset, scene, child, ec);
499 }
500
501 if (scene[index].numChildren) scene[index].setChildren();
502 }
503 else if (key == "lods") {
504 if (!m.value.IsArray()) {
505 LOG_ERROR(logger, "lods property must be array type.");
506 sceneDef[entityId].setError();
507 continue;
508 }
509
510 if (isTypeSomethingDifferent(asset, scene[index], "LodGroup")) {
511 scene[index].setError();
512 continue;
513 }
514
515 ++scene.numLodGroups;
516 scene[index].setLodGroup();
517 scene[index].lod.numLods = 0;
518 uint32_t prevChild = NoIndex;
519
520 for (auto& c : m.value.GetArray()) {
521
522 if (c.IsObject() || (c.IsArray() && c.Empty())) {
523 uint32_t child = scene.createEntity(index);
524 if (prevChild != NoIndex) {
525 scene[prevChild].nextSibling = child;
526 }
527 prevChild = child;
528 ++scene[index].numChildren;
529 ++scene[index].lod.numLods;
530 if (c.IsObject()) {
531 readSceneEntityDefinition(context, c, asset, scene, child, ec);
532 }
533 else {
534 // Note: If a lod-level is empty, we create an empty child to represent
535 // this, making ordering and count wrt error values correct.
536 scene[child].setEmpty();
537 }
538 }
539
540 else if (c.IsArray()) {
541 uint32_t prevLod = NoIndex;
542 for (auto& cc : c.GetArray()) {
543 if (cc.IsObject()) {
544 uint32_t child = scene.createEntity(index);
545 if (prevChild != NoIndex) {
546 scene[prevChild].nextSibling = child;
547 }
548
549 if (prevLod != NoIndex) {
550 scene[prevLod].nextLodSibling = child;
551 }
552 prevLod = child;
553 prevChild = child;
554 ++scene[index].numChildren;
555 readSceneEntityDefinition(context, cc, asset, scene, child, ec);
556 }
557 else {
558 LOG_ERROR(logger, "Invalid lod child - must be object.");
559 }
560 }
561 ++scene[index].lod.numLods;
562 }
563 }
564
565 if (scene[index].numChildren) scene[index].setChildren();
566 }
567 }
568}
569
570void Cogs::Core::readScene(Context* context, const Value& jsonScene, AssetLoadFlags flags, ComponentModel::Entity* root)
571{
572 AssetDefinition asset;
573 SceneDefinition& scene = asset.scene;
574
575 readSceneDefinition(context, jsonScene, flags, asset);
576
577 std::vector<ComponentModel::Entity*> entities(scene.entities.size());
578
579 for (const SceneEntityDefinition& definition : scene.entities) {
580 Entity* parent = nullptr;
581 if (definition.isParentedByName()) {
582 parent = root;
583 }
584 else if (definition.parentIndex == NoIndex) {
585 parent = root;
586 }
587 else {
588 const SceneEntityDefinition& parentDef = scene.entities[definition.parentIndex];
589 if (parentDef.isSequence()) {
590 // Sequence has no enity. Children shall be parented by optional root.
591 parent = root;
592 }
593 else {
594 parent = entities[definition.parentIndex];
595 }
596 }
597
598 entities[definition.index] = createEntity(context, scene, definition, parent);
599 }
600}
601
602void Cogs::Core::readSceneDefinition(Context* context, const Value& jsonScene, AssetLoadFlags /*flags*/, AssetDefinition& asset)
603{
604 ExpressionContext evaluationContext;
605
606 if (!jsonScene.IsArray()) {
607 LOG_ERROR(logger, "JSON Scene must be specified as an array.");
608 return;
609 }
610
611 auto entityArray = jsonScene.GetArray();
612 SceneDefinition& scene = asset.scene;
613
614 scene.numTopLevelEntities = entityArray.Size();
615 // Probably more entities in children.
616 if (scene.entities.empty()) {
617 scene.entities.reserve(std::max(size_t(entityArray.Size()), PreAllocSize));
618 }
619 scene.properties.addProperty(StringView(), StringView());
620
621 for (auto& e : entityArray) {
622 uint32_t index = scene.createEntity(NoParentEntity);
623 readSceneEntityDefinition(context, e, asset, scene, index, &evaluationContext);
624 }
625
626 if (scene.entities.empty()) {
627 LOG_WARNING(logger, "Read empty scene.");
628 }
629}
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
Log implementation class.
Definition: LogManager.h:139
Represents a discrete type definition, describing a native type class.
Definition: Type.h:89
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr size_t hash() const noexcept
Get the hash code of the string.
Definition: StringView.h:200
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
void COGSCORE_DLL_API readSceneDefinition(class Context *context, const Value &jsonScene, AssetLoadFlags flags, AssetDefinition &asset)
Parse JSON description of Entities in Cogs Scene. Store contents in asset param.
void COGSCORE_DLL_API readScene(class Context *context, const Value &jsonScene, AssetLoadFlags flags, ComponentModel::Entity *root)
Parse JSON description of Entities in Cogs Scene file and create Entities defined in scene.
AssetLoadFlags
Asset and Scene loading flags. May be combined with resource loading flags.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
Provides a context for evaluation of expressions.
Definition: Expressions.h:54
Defines a single named expression variable.
Definition: Expressions.h:19
struct Cogs::Core::SceneEntityDefinition::@28::@31 model
SceneEntityFlags::Model is set.
uint32_t flags
Really enum of SceneEntityFlags.
StringRef type
Neither SceneEntityFlags::Asset, SceneEntityFlags::Model, nor SceneEntityFlags::LodGroup is set.
struct Cogs::Core::SceneEntityDefinition::@28::@30 asset
SceneEntityFlags::Asset is set.