Cogs.Core
ResourceReader.cpp
1#include "ResourceReader.h"
2
3#include "Foundation/Logging/Logger.h"
4
5#include "Context.h"
6#include "Scene.h"
7
8#include "Resources/MeshManager.h"
9#include "Resources/MaterialManager.h"
10#include "Resources/AssetManager.h"
11
12#include "Generators/MeshGenerator.h"
13
14#include "Services/Services.h"
15#include "Services/TaskManager.h"
16
17#include "Utilities/Parsing.h"
18
19#include "ReaderCommon.h"
20
21#include "Types.h"
22
23namespace
24{
25 std::unordered_map<size_t, Cogs::Core::ReaderExtension> extensions;
26
27 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("ResourceReader");
28}
29
30namespace Cogs::Core
31{
32 StringView readMesh(Context * context, ResourceDefinition & definition, const Value & jsonResource)
33 {
34 // Must set explicit name to enable lookup. Else clears and replaces existing unnamed mesh.
36
37 if (!definition.name.empty()) {
38 existing = context->meshManager->getMesh(definition.name);
39 }
40
41 Cogs::Core::MeshHandle mesh = existing ? existing : context->meshManager->create();
42
43 if (definition.name.empty()) {
44 static int seq = 0;
45 std::string generatedName = "GeneratedMesh" + std::to_string(seq++);
46 mesh->setName(generatedName);
47 } else {
48 mesh->setName(definition.name);
49 }
50
51 mesh->clear();
52
53 for (auto & m : jsonResource.GetObject()) {
54 auto key = toKey(m.name);
55
56 if (key == "positions") {
57 auto arr = m.value.GetArray();
58
59 std::vector<glm::vec3> v(arr.Size());
60
61 int i = 0;
62 for (auto & element : arr) {
63 readArray(element, v[i++], nullptr);
64 }
65
66 mesh->setPositions(v);
67 } else if (key == "instancePositions") {
68 auto arr = m.value.GetArray();
69
70 std::vector<glm::vec3> v(arr.Size());
71
72 int i = 0;
73 for (auto & element : arr) {
74 readArray(element, v[i++], nullptr);
75 }
76
77 mesh->setInstancePositions(v);
78 } else if (key == "instanceColors") {
79 auto arr = m.value.GetArray();
80
81 std::vector<glm::vec4> v(arr.Size());
82
83 int i = 0;
84 for (auto & element : arr) {
85 readArray(element, v[i++], nullptr);
86 }
87
88 mesh->setInstanceColors(v);
89 } else if (key == "normals") {
90 auto arr = m.value.GetArray();
91
92 std::vector<glm::vec3> v(arr.Size());
93
94 int i = 0;
95 for (auto & element : arr) {
96 readArray(element, v[i++], nullptr);
97 }
98
99 mesh->setNormals(v);
100 } else if (key == "bbox") {
101 auto arr = m.value.GetArray();
102
103 std::vector<glm::vec3> v(arr.Size());
104
105 int i = 0;
106 for (auto & element : arr) {
107 readArray(element, v[i++], nullptr);
108 }
109
110 if (v.size() == 2) {
111 mesh->setBounds({ v[0], v[1] });
112 }
113 } else if (key == "generator") {
114 ShapeDefinition definition2;
115 definition2.size = glm::vec3(1, 1, 1);
116
117 for (auto & g : m.value.GetObject()) {
118 auto gKey = toKey(g.name);
119
120 if (gKey == "shape") {
121 definition2.type = parseEnum(toKey(g.value), ShapeType::None);
122 } else if (gKey == "samples") {
123 if (g.value.IsInt()) {
124 definition2.refinement = g.value.GetInt();
125 } else {
126 definition2.refinement = parseInt(toKey(g.value), 16);
127 }
128 } else if (gKey == "size") {
129 readArray(g.value, definition2.size, nullptr);
130 }
131 }
132
133 context->services->getService<MeshGenerator>()->getMesh(mesh.resolve(), definition2);
134 }
135 }
136
137 return mesh->getName();
138 }
139
140 void readAssetDefinition(ReaderContext* /*context*/, const Value& jsonResource, ResourceDefinition& assetDefinition)
141 {
142 assetDefinition.type = ResourceTypes::Asset;
143 assetDefinition.source = toString(jsonResource["source"]);
144 }
145
146 void readModelDefinition(ReaderContext *, const Value & jsonResource, ResourceDefinition & definition)
147 {
148 definition.type = ResourceTypes::Model;
149 definition.source = toString(jsonResource["source"]);
150 }
151
152 void readTextureDefinition(ReaderContext * /*context*/, const Value & jsonResource, ResourceDefinition & definition)
153 {
154 definition.type = ResourceTypes::Texture;
155
156 auto & textureDescription = definition.textureDescription;
157
158 for (auto & m : jsonResource.GetObject()) {
159 auto key = toKey(m.name);
160
161 if (key == "source") {
162 definition.source = toString(m.value);
163
164 if (jsonResource.HasMember("flags")) {
165 auto flags = toKey(jsonResource["flags"]);
166
167 auto splits = split(flags, "|");
168
169 for (auto & s : splits) {
170 if (s == "LinearColorSpace") {
171 definition.loadFlags |= TextureLoadFlags::LinearColorSpace;
172 }
173 }
174 }
175 } else if (key == "generator") {
176 definition.textureGenerator = true;
177 ImageDefinition& imageDefinition = definition.imageDefinition;
178 for (auto & g : m.value.GetObject()) {
179 switch (toView(g.name).hash()) {
180 case Cogs::hash("image"):
181 imageDefinition.type = parseEnum(toKey(g.value), ImageType::None);
182 break;
183 case Cogs::hash("width"):
184 imageDefinition.width = readValue<int>(g.value, ImageDefinition().width, nullptr);
185 break;
186 case Cogs::hash("height"):
187 imageDefinition.height = readValue<int>(g.value, ImageDefinition().height, nullptr);
188 break;
189 case Cogs::hash("layers"):
190 imageDefinition.layers = readValue<int>(g.value, ImageDefinition().layers, nullptr);
191 break;
192 default:
193 break;
194 }
195 }
196 } else if (key == "width") {
197 textureDescription.width = readValue<int>(m.value, 1024, nullptr);
198 } else if (key == "height") {
199 textureDescription.height = readValue<int>(m.value, 1024, nullptr);
200 } else if (key == "renderTarget") {
201 textureDescription.flags |= TextureFlags::RenderTarget;
202 } else if (key == "format") {
203 textureDescription.format = parseTextureFormat(toKey(m.value), Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB);
204 } else if (key == "flags") {
205 TokenStream flags;
206 split(toKey(m.value), " |", flags);
207
208 for (auto & flag : flags) {
209 if (flag == "Texture") {
210 textureDescription.flags |= TextureFlags::Texture;
211 } else if (flag == "RenderTarget") {
212 textureDescription.flags |= TextureFlags::RenderTarget;
213 } else if (flag == "GenerateMipMaps") {
214 textureDescription.flags |= TextureFlags::GenerateMipMaps;
215 }
216 }
217 }
218 }
219 }
220
221 void parseMaterialInstanceProperty(const Value & value, const StringView & key, const Material * material, FieldValue & propertyValue, ExpressionContext * ec)
222 {
223 if (value.IsFloat()) {
224 propertyValue.type = DefaultValueType::Float;
225 propertyValue.floatValue = value.GetFloat();
226 } else if (value.IsBool()) {
227 propertyValue.type = DefaultValueType::Bool;
228 propertyValue.boolValue = value.GetBool();
229 } else if (value.IsInt()) {
230 propertyValue.type = DefaultValueType::Int;
231 propertyValue.intValue = value.GetInt();
232 } else if (value.IsArray()) {
233 auto arr = value.GetArray();
234
235 if (arr.Size() == 2) {
236 propertyValue.type = DefaultValueType::Vec2;
237 readArray(value, propertyValue.float2, ec);
238 } else if (arr.Size() == 3) {
239 propertyValue.type = DefaultValueType::Vec3;
240 readArray(value, propertyValue.float3, ec);
241 } else if (arr.Size() == 4) {
242 propertyValue.type = DefaultValueType::Vec4;
243 readArray(value, propertyValue.float4, ec);
244 } else {
245 LOG_ERROR(logger, "Unsupported array size for material property %.*s", StringViewFormat(key));
246 }
247 } else if (value.IsString()) {
248 ParsedValue parsedValue;
249 parseStringValue(toKey(value), parsedValue);
250
251 if (parsedValue.type == ParsedDataType::Unknown) parsedValue.type = ParsedDataType::String;
252
253 if (parsedValue.type == ParsedDataType::Float) {
254 propertyValue.type = DefaultValueType::Float;
255 propertyValue.floatValue = parsedValue.floatValue;
256 } else if (parsedValue.type == ParsedDataType::Float2) {
257 propertyValue.type = DefaultValueType::Vec2;
258 propertyValue.float2 = parsedValue.float2Value;
259 } else if (parsedValue.type == ParsedDataType::Float3) {
260 propertyValue.type = DefaultValueType::Vec3;
261 propertyValue.float3 = parsedValue.float3Value;
262 } else if (parsedValue.type == ParsedDataType::Float4) {
263 propertyValue.type = DefaultValueType::Vec4;
264 propertyValue.float4 = parsedValue.float4Value;
265 } else if (parsedValue.type == ParsedDataType::String) {
266 if (material) {
267 auto texKey = material->getTextureKey(key);
268 if (texKey != NoProperty) {
269 propertyValue.type = DefaultValueType::Texture;
270 propertyValue.value = parsedValue.value;
271 } else {
272 auto floatKey = material->getFloatKey(key);
273 if (floatKey != NoProperty) {
274 propertyValue.type = DefaultValueType::Float;
275 propertyValue.floatValue = ec->eval(parsedValue.value, 0.0f);
276 }
277 }
278 } else {
279 propertyValue.type = DefaultValueType::Texture;
280 propertyValue.value = parsedValue.value;
281 }
282 }
283 } else {
284 LOG_ERROR(logger, "Unsupported material property value for %.*s", StringViewFormat(key));
285 }
286 }
287
288 void readMaterialInstanceDefinition(ReaderContext * rc, const Value & value, ResourceDefinition & resourceDefinition)
289 {
290 resourceDefinition.type = ResourceTypes::MaterialInstance;
291
292 auto & materialInstance = resourceDefinition;
293 auto context = rc->context;
294
295 // Complex material instance defaults.
296 auto obj = value.GetObject();
297
298 MaterialHandle material;
299
300 for (auto & m : obj) {
301 auto matKey = toKey(m.name);
302
303 auto keyCode = StringView(matKey.data(), matKey.length()).hash();
304
305 switch (keyCode) {
306 case Cogs::hash("type"):
307 if (rc->ec) {
308 materialInstance.material = m.value.GetString();
309 material = context->materialManager->getMaterial(materialInstance.material);
310 }
311 break;
312 case Cogs::hash("material"):
313 materialInstance.material = m.value.GetString();
314 break;
315 case Cogs::hash("permutation"):
316 materialInstance.permutation = m.value.GetString();
317 break;
318 case Cogs::hash("options"):
319 {
320 auto optionsObject = m.value.GetObject();
321
322 for (auto & option : optionsObject) {
323 materialInstance.options[option.name.GetString()] = option.value.GetString();
324 }
325 break;
326 }
327 case Cogs::hash("variants"):
328 {
329 auto variantsObject = m.value.GetObject();
330
331 for (auto & variant : variantsObject) {
332 if (variant.value.IsNumber()) {
333 materialInstance.variants[variant.name.GetString()] = std::to_string(variant.value.GetInt());
334 } else if (variant.value.IsString()) {
335 materialInstance.variants[variant.name.GetString()] = variant.value.GetString();
336 } else if (variant.value.IsBool()) {
337 materialInstance.variants[variant.name.GetString()] = variant.value.GetBool() ? "true" : "false";
338 } else {
339 LOG_ERROR(logger, "Unknown variant type.");
340 }
341 }
342 break;
343 }
344 case Cogs::hash("properties"):
345 {
346 auto propsObject = m.value.GetObject();
347
348 for (auto & prop : propsObject) {
349 auto key = toKey(prop.name);
350 auto & propertyValue = materialInstance.properties[key.to_string()];
351
352 parseMaterialInstanceProperty(prop.value, key, material.resolve(), propertyValue, rc->ec);
353 }
354 break;
355 }
356 default:
357 auto & propertyValue = materialInstance.properties[matKey.to_string()];
358
359 parseMaterialInstanceProperty(m.value, matKey, material.resolve(), propertyValue, rc->ec);
360
361 break;
362 }
363 }
364 }
365
366 void readResource(Context * context, const StringView & name, const Value & jsonResource)
367 {
368 ResourceDefinition definition;
369
370 if (name.size()) definition.name = name.to_string();
371
372 if (jsonResource.IsString()) {
373 auto value = toKey(jsonResource);
374
375 if (value.find(".material") != StringView::NoPosition) {
376 context->materialManager->loadMaterial(value);
377 context->materialManager->processLoading();
378 return;
379 }
380 }
381
382 ReaderContext rc;
383 rc.context = context;
384
385 for (auto & m : jsonResource.GetObject()) {
386 auto key = toKey(m.name);
387
388 if (key == "name") {
389 definition.name = toString(m.value);
390 } else if (key == "type") {
391 auto type = toKey(m.value);
392
393 if (type == "Mesh") {
394 readMesh(context, definition, jsonResource);
395 } else if (type == "Asset") {
396 readAssetDefinition(&rc, jsonResource, definition);
397 context->assetManager->instantiateAsset(AssetHandle::NoHandle, definition);
398 } else if (type == "MaterialInstance") {
399 readMaterialInstanceDefinition(&rc, jsonResource, definition);
400 context->assetManager->instantiateMaterialInstance(AssetHandle::NoHandle, definition);
401 } else if (type == "Texture") {
402 readTextureDefinition(&rc, jsonResource, definition);
403 context->assetManager->instantiateTexture(AssetHandle::NoHandle, definition);
404 } else if (type == "Model") {
405 readModelDefinition(&rc, jsonResource, definition);
406 context->assetManager->instantiateModel(AssetHandle::NoHandle, definition);
407 } else if (type == "GuiDocument") {
408 auto * mgr = context->engine->getResourceManagerByValueType((int)DefaultValueType::Gui);
409 if (mgr) {
410 mgr->readSource(context, definition, &jsonResource);
411 }
412 else {
413 LOG_ERROR(logger, "No manager for DefaultValueType::Gui");
414 }
415 } else {
416 auto ext = extensions.find(type.hash());
417
418 if (ext != extensions.end()) {
419 ext->second.readCallback(definition.name, jsonResource);
420 }
421 }
422 }
423 }
424 }
425
426 void readResourceDefinition(Context * context, const StringView & name, const Value & jsonResource, ResourceDefinition & definition)
427 {
428 if (name.size()) definition.name = name.to_string();
429
430 if (jsonResource.IsString()) {
431 auto value = toKey(jsonResource);
432
433 if (value.find(".material") != StringView::NoPosition) {
434 context->materialManager->loadMaterial(value);
435 context->materialManager->processLoading();
436 return;
437 }
438 }
439
440 ReaderContext rc;
441 rc.context = context;
442
443 for (auto & m : jsonResource.GetObject()) {
444 auto key = toKey(m.name);
445
446 if (key == "name") {
447 definition.name = toString(m.value);
448 } else if (key == "type") {
449 auto type = toKey(m.value);
450
451 if (type == "Mesh") {
452 readMesh(context, definition, jsonResource);
453 } else if (type == "Asset") {
454 readAssetDefinition(&rc, jsonResource, definition);
455 } else if (type == "MaterialInstance") {
456 readMaterialInstanceDefinition(&rc, jsonResource, definition);
457 } else if (type == "Texture") {
458 readTextureDefinition(&rc, jsonResource, definition);
459 } else if (type == "Model") {
460 readModelDefinition(&rc, jsonResource, definition);
461 } else if (type == "GuiDocument") {
462 auto * mgr = context->engine->getResourceManagerByValueType((int)DefaultValueType::Gui);
463 if (mgr) {
464 mgr->readSource(context, definition, &jsonResource);
465 }
466 else {
467 LOG_ERROR(logger, "No manager for DefaultValueType::Gui");
468 }
469 } else {
470 auto ext = extensions.find(type.hash());
471
472 if (ext != extensions.end()) {
473 ext->second.readCallback(definition.name, jsonResource);
474 }
475 }
476 }
477 }
478 }
479}
480
481Cogs::Core::ReaderExtension * Cogs::Core::getExtensionReader(size_t code)
482{
483 for (auto & e : extensions) {
484 if (e.second.handleTypeId == code) {
485 return &e.second;
486 }
487 }
488
489 return nullptr;
490}
491
492void Cogs::Core::registerExtensionReader(Context * /*context*/, const StringView & resourceType, const Reflection::Type & handleType, ReaderExtension extension)
493{
494 extension.handleTypeId = handleType.getTypeId();
495
496 extensions[resourceType.hash()] = extension;
497}
498
499void Cogs::Core::readResources(Context * context, const Value & jsonResources, int /*flags*/)
500{
501 if (jsonResources.IsObject()) {
502 for (auto & m : jsonResources.GetObject()) {
503 readResource(context, toKey(m.name), m.value);
504 }
505 }
506 else {
507 LOG_ERROR(logger, "Expected resources section as JSON object");
508 }
509}
510
511void Cogs::Core::readResourceDefinitions(Context * context, const Value & jsonResources, int /*flags*/, AssetDefinition & asset)
512{
513 if (jsonResources.IsObject()) {
514 for (auto & m : jsonResources.GetObject()) {
515 auto & resourceDefinition = asset.resources.emplace_back();
516 readResourceDefinition(context, toKey(m.name), m.value, resourceDefinition);
517 }
518 }
519}
Log implementation class.
Definition: LogManager.h:139
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....
@ LinearColorSpace
For textures with RGBA format without color space information, mark the data as being in linear color...
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
void setBounds(Geometry::BoundingBox box)
Set custom bounds for the mesh.
Definition: Mesh.h:298
void setNormals(std::span< const glm::vec3 > normals)
Set the normal data of the Mesh.
Definition: Mesh.h:392
void clear()
Clear all data from the Mesh, returning it to its initial non-indexed state with no streams and no su...
Definition: Mesh.cpp:27
void setPositions(std::span< const glm::vec3 > positions)
Set the position data of the Mesh.
Definition: Mesh.h:315
StringView getName() const
Get the name of the resource.
Definition: ResourceBase.h:307
void setName(const StringView &name)
Set the user friendly name of the resource.
Definition: ResourceBase.h:298
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
@ RenderTarget
The texture can be used as a render target and drawn into.
Definition: Flags.h:120
@ GenerateMipMaps
The texture supports automatic mipmap generation performed by the graphics device.
Definition: Flags.h:124
@ Texture
Texture usage, see Default.
Definition: Flags.h:118