Cogs.Core
GltfWriter.cpp
1#include "GltfWriter.h"
2#include "Context.h"
3#include "EntityStore.h"
4#include "rapidjson/document.h"
5#include "rapidjson/pointer.h"
6#include "rapidjson/filewritestream.h"
7#include "rapidjson/prettywriter.h"
8
9#include "Components/Core/TransformComponent.h"
10#include "Components/Core/MeshRenderComponent.h"
11#include "Components/Core/MeshComponent.h"
12#include "Components/Core/StaticModelComponent.h"
13#include "Components/Core/SubMeshRenderComponent.h"
14#include "Components/Appearance/MaterialComponent.h"
15#include "Systems/Core/TransformSystem.h"
16#include "Systems/Core/RenderSystem.h"
17
18#include "Resources/Buffer.h"
19#include "Resources/Mesh.h"
20#include "Resources/Material.h"
21#include "Resources/MaterialInstance.h"
22#include "Resources/Texture.h"
23#include "Resources/Model.h"
24#include "Components/Core/SceneComponent.h"
25
26#include "Foundation/Logging/Logger.h"
27#include "Foundation/Platform/IO.h"
28
29#include <glm/glm.hpp>
30#include <glm/gtx/quaternion.hpp>
31#include <glm/gtc/matrix_transform.hpp>
32#include "stb_image_write.h"
33
34#include <fstream>
35#include <sstream>
36#include <filesystem>
37#include <vector>
38#include <type_traits>
39
40#ifndef EMSCRIPTEN
41namespace {
42 Cogs::Logging::Log logger = Cogs::Logging::getLogger("GLTFWriter");
43
44 constexpr float floatMax = std::numeric_limits<float>::max();
45 constexpr float floatMin = -std::numeric_limits<float>::max();
46 constexpr int32_t intMax = std::numeric_limits<int32_t>::max();
47 constexpr int32_t intMin = std::numeric_limits<int32_t>::min();
48
49 enum EPrimitiveMode { Points, Lines, LineLoop, LineStrip, Triangles, TriangleStrip, TriangleFan };
50
51 enum EAccessorType { BYTE = 5120, UNSIGNED_BYTE = 5121, SHORT = 5122, UNSIGNED_SHORT = 5123, UNSIGNED_INT = 5125, FLOAT = 5126 };
52
53 enum EBufferType { Image = 0, ArrayBuffer = 34962, ElementArrayBuffer = 34963 };
54
55 enum EAccessorContainer { SCALAR, VEC2, VEC3, VEC4, MAT2, MAT3, MAT4 };
56
57 struct Buffer {
59 bool active;
60 Buffer(bool enable = false) : buffer(Cogs::MemBlockType::IOResource), active(enable) {}
61 };
62
63 typedef struct {
64 int last_pos;
65 void* context;
66 } stbi_mem_context;
67
68 void stbi_write_mem(void* context, void* data, int size) {
69 stbi_mem_context* c = (stbi_mem_context*)context;
70 char* dst = (char*)c->context;
71 char* src = (char*)data;
72 int cur_pos = c->last_pos;
73 for (int i = 0; i < size; i++) {
74 dst[cur_pos++] = src[i];
75 }
76
77 c->last_pos = cur_pos;
78 }
79
80 void* ptrOffset(void* ptr, unsigned int offset) {
81 return static_cast<char*>(ptr) + offset;
82 }
83
84 std::pair<glm::vec3, glm::vec3> getMinMaxVec3(Cogs::Core::DataStream& stream, unsigned int offset) {
85 glm::vec3 min { floatMax, floatMax, floatMax };
86 glm::vec3 max { floatMin, floatMin, floatMin };
87 glm::vec3* vecs = static_cast<glm::vec3*>(ptrOffset(stream.data(), offset));
88 for (size_t i = 0; i < stream.size() / 12; i++) {
89 min = glm::min(vecs[i], min);
90 max = glm::max(vecs[i], max);
91 }
92
93 return std::make_pair(min, max);
94 }
95
96 std::pair<glm::vec2, glm::vec2> getMinMaxVec2(Cogs::Core::DataStream& stream, unsigned int offset) {
97 glm::vec2 min { floatMax, floatMax };
98 glm::vec2 max { floatMin, floatMin };
99 glm::vec2* vecs = static_cast<glm::vec2*>(ptrOffset(stream.data(), offset));
100 for (size_t i = 0; i < stream.size() / 8; i++) {
101 min = glm::min(vecs[i], min);
102 max = glm::max(vecs[i], max);
103 }
104
105 return std::make_pair(min, max);
106 }
107
108 std::pair<int32_t, int32_t> getMinMaxIndex(Cogs::Core::DataStream& stream) {
109
110 int32_t min = std::numeric_limits<int32_t>::max();
111 int32_t max = std::numeric_limits<int32_t>::min();
112 int32_t* indices = static_cast<int32_t*>(stream.data());
113 for (size_t i = 0; i < stream.size() / 4; i++) {
114 min = std::min(indices[i], min);
115 max = std::max(indices[i], max);
116 }
117
118 return std::make_pair(min, max);
119 }
120
121 void extractMeshes(Cogs::ComponentModel::Entity* currentEntity, glm::mat4& modelMatrix, std::vector<std::pair<Cogs::ComponentModel::Entity*, glm::mat4>>& entities) {
122 if (currentEntity == nullptr) return;
123
125 if (mesh) {
126 if (std::find_if(entities.begin(), entities.end(), [ourEntity = currentEntity](std::pair<Cogs::ComponentModel::Entity*, glm::mat4> e) { return e.first->getId() == ourEntity->getId(); }) == entities.end()) {
128 if (transform) {
129 glm::mat4 translate = glm::translate(glm::mat4(1.0), glm::vec3(transform->position.x, transform->position.y, transform->position.z));
130 glm::mat4 rotate = glm::mat4_cast(transform->rotation);
131 glm::mat4 scale = glm::scale(glm::mat4(1.0), transform->scale);
132
133 glm::mat4 matrix = translate * rotate * scale;
134 modelMatrix = modelMatrix * matrix;
135 }
136 entities.push_back(std::make_pair(currentEntity, modelMatrix));
137 }
138 }
139
141 if (scene) {
142 for (const auto& c : scene->children) {
143 glm::mat4 childModelMatrix{ modelMatrix };
145 if (transform) {
146 glm::mat4 translate = glm::translate(glm::mat4(1.0), glm::vec3(transform->position.x, transform->position.y, transform->position.z));
147 glm::mat4 rotate = glm::mat4_cast(transform->rotation);
148 glm::mat4 scale = glm::scale(glm::mat4(1.0), transform->scale);
149
150 glm::mat4 matrix = translate * rotate * scale;
151 childModelMatrix = modelMatrix * matrix;
152 }
153 extractMeshes(c.get(), childModelMatrix, entities);
154 }
155 }
156 else return;
157 }
158}
159
160namespace Cogs::Core::GltfWriter {
161 static EPrimitiveMode toGltfPrimitiveType(Cogs::PrimitiveType::EPrimitiveType type)
162 {
163 switch (type) {
164 case Cogs::PrimitiveType::PointList: return EPrimitiveMode::Points;
165 case Cogs::PrimitiveType::LineList: return EPrimitiveMode::Lines;
166 case Cogs::PrimitiveType::LineStrip: return EPrimitiveMode::LineStrip;
167 case Cogs::PrimitiveType::TriangleList: return EPrimitiveMode::Triangles;
168 case Cogs::PrimitiveType::TriangleStrip: return EPrimitiveMode::TriangleStrip;
169 default: return EPrimitiveMode::Triangles;
170 }
171 }
172
173 rapidjson::Value generateBuffer(rapidjson::Document& doc, const std::string& name)
174 {
175 rapidjson::Value GLTFbuffers(rapidjson::kArrayType);
176 rapidjson::Value GLTFbuffer(rapidjson::kObjectType);
177 rapidjson::Value GLTFuri(rapidjson::kStringType);
178
179 GLTFuri.SetString(name.c_str(), doc.GetAllocator());
180
181 GLTFbuffer.AddMember("byteLength", 0, doc.GetAllocator());
182 if (!name.empty()) GLTFbuffer.AddMember("uri", GLTFuri, doc.GetAllocator());
183 GLTFbuffers.PushBack(GLTFbuffer, doc.GetAllocator());
184
185 return GLTFbuffers;
186 }
187
188 unsigned int generateBufferView(rapidjson::Document& doc,
189 const std::string& path,
190 void* data,
191 size_t dataSize,
192 uint32_t byteStride,
193 EBufferType target,
194 Buffer& dataBuffer)
195 {
196 rapidjson::Value GLTFbufferView(rapidjson::kObjectType);
197 if (!dataBuffer.active) {
198 std::fstream bufferFile(path, std::ios::binary | std::ios::out | std::ios::app | std::ios::ate);
199 if (!bufferFile.is_open()) {
200 LOG_ERROR(logger, "GLTF binary data file could not be opened!");
201 return 0;
202 }
203
204 GLTFbufferView.AddMember("buffer", 0, doc.GetAllocator());
205 GLTFbufferView.AddMember("byteOffset", static_cast<int>(bufferFile.tellg()), doc.GetAllocator());
206 GLTFbufferView.AddMember("byteLength", static_cast<uint64_t>(dataSize), doc.GetAllocator());
207 if (byteStride > 4) GLTFbufferView.AddMember("byteStride", byteStride, doc.GetAllocator());
208 if (target != EBufferType::Image) GLTFbufferView.AddMember("target", target, doc.GetAllocator());
209
210 bufferFile.write(static_cast<const char*>(data), dataSize);
211
212 // We need to write some empty data if the buffer is misaligned
213 while (static_cast<size_t>(bufferFile.tellg()) % 4) {
214 char t = 0x00;
215 bufferFile.write(&t, sizeof(char));
216 }
217
218 doc["buffers"].GetArray()[0]["byteLength"].SetInt(static_cast<int>(bufferFile.tellg()));
219 bufferFile.close();
220 }
221 else {
222
223 GLTFbufferView.AddMember("buffer", 0, doc.GetAllocator());
224 GLTFbufferView.AddMember("byteOffset", static_cast<int>(dataBuffer.buffer.size()), doc.GetAllocator());
225 GLTFbufferView.AddMember("byteLength", static_cast<uint64_t>(dataSize), doc.GetAllocator());
226 if (byteStride > 0) GLTFbufferView.AddMember("byteStride", byteStride, doc.GetAllocator());
227 if (target != EBufferType::Image) GLTFbufferView.AddMember("target", target, doc.GetAllocator());
228
229 dataBuffer.buffer.write(static_cast<const char*>(data), dataSize);
230
231 // We need to write some empty data if the buffer is misaligned
232 while (static_cast<size_t>(dataBuffer.buffer.size()) % 4) {
233 char t = 0x00;
234 dataBuffer.buffer.write(&t, sizeof(char));
235 }
236
237 doc["buffers"].GetArray()[0]["byteLength"].SetInt(static_cast<int>(dataBuffer.buffer.size()));
238 }
239
240 if (doc.HasMember("bufferViews") && doc["bufferViews"].IsArray()) {
241 const auto bufferviews = doc["bufferViews"].GetArray();
242 bufferviews.PushBack(GLTFbufferView, doc.GetAllocator());
243
244 return bufferviews.Size() - 1;
245 }
246 else {
247 rapidjson::Value GLTFbufferviews(rapidjson::kArrayType);
248 GLTFbufferviews.PushBack(GLTFbufferView, doc.GetAllocator());
249 doc.AddMember("bufferViews", GLTFbufferviews, doc.GetAllocator());
250
251 return 0;
252 }
253 }
254
255 template <typename MinMax>
256 unsigned int generateAccessor(int id,
257 rapidjson::Document& doc,
258 uint32_t offset,
259 EAccessorType componentType,
260 uint32_t count,
261 const std::string& type,
262 const MinMax& min,
263 const MinMax& max)
264 {
265 rapidjson::Value GLTFaccessor(rapidjson::kObjectType);
266 rapidjson::Value GLTFtype(rapidjson::kStringType);
267 rapidjson::Value GLTFmin(rapidjson::kArrayType);
268 rapidjson::Value GLTFmax(rapidjson::kArrayType);
269
270 if constexpr (std::is_same_v<MinMax, glm::vec3>) {
271 glm::vec3 minVec = static_cast<glm::vec3>(min);
272 glm::vec3 maxVec = static_cast<glm::vec3>(max);
273 if (minVec.x != floatMax) {
274 GLTFmin.PushBack(minVec.x, doc.GetAllocator());
275 GLTFmin.PushBack(minVec.y, doc.GetAllocator());
276 GLTFmin.PushBack(minVec.z, doc.GetAllocator());
277 }
278 if (maxVec.x != floatMin) {
279 GLTFmax.PushBack(maxVec.x, doc.GetAllocator());
280 GLTFmax.PushBack(maxVec.y, doc.GetAllocator());
281 GLTFmax.PushBack(maxVec.z, doc.GetAllocator());
282 }
283 } else if constexpr (std::is_same_v<MinMax, glm::vec2>) {
284 glm::vec2 minVec = static_cast<glm::vec2>(min);
285 glm::vec2 maxVec = static_cast<glm::vec2>(max);
286 if (minVec.x != floatMax) {
287 GLTFmin.PushBack(minVec.x, doc.GetAllocator());
288 GLTFmin.PushBack(minVec.y, doc.GetAllocator());
289 }
290 if (maxVec.x != floatMin) {
291 GLTFmax.PushBack(maxVec.x, doc.GetAllocator());
292 GLTFmax.PushBack(maxVec.y, doc.GetAllocator());
293 }
294 } else {
295 if (min != intMax) {
296 GLTFmin.PushBack(min, doc.GetAllocator());
297 }
298 if (max != intMin) {
299 GLTFmax.PushBack(max, doc.GetAllocator());
300 }
301 }
302
303 GLTFtype.SetString(type.c_str(), doc.GetAllocator());
304
305 GLTFaccessor.AddMember("bufferView", id, doc.GetAllocator());
306 GLTFaccessor.AddMember("byteOffset", offset, doc.GetAllocator());
307 GLTFaccessor.AddMember("componentType", componentType, doc.GetAllocator());
308 GLTFaccessor.AddMember("count", count, doc.GetAllocator());
309 GLTFaccessor.AddMember("type", GLTFtype, doc.GetAllocator());
310 if (GLTFmin.Size()) GLTFaccessor.AddMember("min", GLTFmin, doc.GetAllocator());
311 if (GLTFmax.Size()) GLTFaccessor.AddMember("max", GLTFmax, doc.GetAllocator());
312
313 if (doc.HasMember("accessors") && doc["accessors"].IsArray()) {
314 const auto accessors = doc["accessors"].GetArray();
315 accessors.PushBack(GLTFaccessor, doc.GetAllocator());
316
317 return accessors.Size() - 1;
318 }
319 else {
320 rapidjson::Value accessors(rapidjson::kArrayType);
321 accessors.PushBack(GLTFaccessor, doc.GetAllocator());
322 doc.AddMember("accessors", accessors, doc.GetAllocator());
323
324 return 0;
325 }
326 }
327
328 unsigned int generateImage(rapidjson::Document& doc,
329 const std::string& filePath,
330 void* buffer,
331 size_t bufferSize,
332 const std::string& extension,
333 Buffer& dataBuffer)
334 {
335 rapidjson::Value GLTFimage(rapidjson::kObjectType);
336 unsigned int bufferViewIndex = generateBufferView(doc, filePath + extension, buffer, bufferSize, 0, EBufferType::Image, dataBuffer);
337
338 GLTFimage.AddMember("mimeType", "image/png", doc.GetAllocator());
339 GLTFimage.AddMember("bufferView", bufferViewIndex, doc.GetAllocator());
340
341 if (doc.HasMember("images") && doc["images"].IsArray()) {
342 auto images = doc["images"].GetArray();
343 images.PushBack(GLTFimage, doc.GetAllocator());
344
345 return images.Size() - 1;
346 }
347 else {
348 rapidjson::Value GLTFimages(rapidjson::kArrayType);
349 GLTFimages.PushBack(GLTFimage, doc.GetAllocator());
350 doc.AddMember("images", GLTFimages, doc.GetAllocator());
351
352 return 0;
353 }
354 }
355
356 unsigned int generateTexture(rapidjson::Document& doc,
357 const std::string& filePath,
358 void* buffer,
359 size_t bufferSize,
360 const std::string& extension,
361 Buffer& dataBuffer)
362 {
363 rapidjson::Value GLTFtexture(rapidjson::kObjectType);
364 unsigned int imageIndex = generateImage(doc, filePath, buffer, bufferSize, extension, dataBuffer);
365 GLTFtexture.AddMember("source", imageIndex, doc.GetAllocator());
366
367 if (doc.HasMember("textures") && doc["textures"].IsArray()) {
368 auto textures = doc["textures"].GetArray();
369 textures.PushBack(GLTFtexture, doc.GetAllocator());
370
371 return textures.Size() - 1;
372 }
373 else {
374 rapidjson::Value GLTFtextures(rapidjson::kArrayType);
375 GLTFtextures.PushBack(GLTFtexture, doc.GetAllocator());
376 doc.AddMember("textures", GLTFtextures, doc.GetAllocator());
377
378 return 0;
379 }
380 }
381
382 rapidjson::Value generateMaterialProperty(rapidjson::Document& doc,
383 const std::string& filePath,
384 const Cogs::Core::TextureHandle& handle,
385 const std::string& extension,
386 Buffer& dataBuffer)
387 {
388 rapidjson::Value GLTFmaterialProperty(rapidjson::kObjectType);
389
390 size_t size = handle->storage.getSize();
391 unsigned char* buffer = new unsigned char[size];
392 stbi_mem_context context{};
393 context.last_pos = 0;
394 context.context = (void*)buffer;
395 int result = stbi_write_png_to_func(stbi_write_mem, &context, handle->description.width, handle->description.height, static_cast<int>(handle->storage.blockSize), handle->storage.getData(), handle->description.width * 4);
396 if (result < 1) {
397 LOG_ERROR(logger, "stbi failed to write image data to memory.");
398 return GLTFmaterialProperty;
399 }
400
401 unsigned int index = generateTexture(doc, filePath, buffer, context.last_pos, extension, dataBuffer);
402 GLTFmaterialProperty.AddMember("index", index, doc.GetAllocator());
403 delete[] buffer;
404
405 return GLTFmaterialProperty;
406 }
407
408 unsigned int generateMaterial(rapidjson::Document& doc,
409 const std::string& filePath,
410 const Cogs::Core::MaterialInstanceHandle& materialIns,
412 const std::string& extension,
413 Buffer& dataBuffer)
414 {
415 rapidjson::Value GLTFmaterial(rapidjson::kObjectType);
416 rapidjson::Value GLTFpbrMetallicRoughness(rapidjson::kObjectType);
417 rapidjson::Value GLTFbaseColorFactor(rapidjson::kArrayType);
418 rapidjson::Value GLTFmaterialName(rapidjson::kStringType);
419
420 if (matComp != nullptr) {
421 for (int i = 0; i < 4; i++) {
422 GLTFbaseColorFactor.PushBack(matComp->diffuseColor[i], doc.GetAllocator());
423 }
424 GLTFpbrMetallicRoughness.AddMember("roughnessFactor", matComp->specularPower / 100.f, doc.GetAllocator());
425
426 if (matComp->diffuseMap != nullptr) {
427 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, matComp->diffuseMap, extension, dataBuffer);
428 GLTFpbrMetallicRoughness.AddMember("baseColorTexture", GLTFmaterialProperty, doc.GetAllocator());
429 }
430 if (matComp->normalMap != nullptr) {
431 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, matComp->normalMap, extension, dataBuffer);
432 GLTFmaterial.AddMember("normalTexture", GLTFmaterialProperty, doc.GetAllocator());
433 }
434 if (matComp->specularMap != nullptr) {
435 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, matComp->specularMap, extension, dataBuffer);
436 GLTFmaterial.AddMember("specularTexture", GLTFmaterialProperty, doc.GetAllocator());
437 }
438 }
439 else if (materialIns != nullptr) {
441 Cogs::Core::TextureValue albedoMap = materialIns->getTextureProperty(materialIns->material->getTextureKey("albedoMap"));
442 Cogs::Core::TextureValue diffuseMap = materialIns->getTextureProperty(materialIns->material->getTextureKey("diffuseMap"));
443
444 if (albedoMap.property != nullptr && albedoMap.texture.handle && (albedoMap.texture.handle->description.height > 4 || albedoMap.texture.handle->description.width > 4)) {
445 const Cogs::Core::TextureHandle& handle = albedoMap.texture.handle;
446 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
447 GLTFpbrMetallicRoughness.AddMember("baseColorTexture", GLTFmaterialProperty, doc.GetAllocator());
448 }
449 else if (diffuseMap.property != nullptr && diffuseMap.texture.handle && (diffuseMap.texture.handle->description.height > 4 || diffuseMap.texture.handle->description.width > 4)) {
450 const Cogs::Core::TextureHandle& handle = diffuseMap.texture.handle;
451 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
452 GLTFpbrMetallicRoughness.AddMember("baseColorTexture", GLTFmaterialProperty, doc.GetAllocator());
453 }
454 else {
455 glm::vec4 albedo = materialIns->getVec4Property(materialIns->material->getVec4Key("albedo"));
456 glm::vec4 diffuse = materialIns->getVec4Property(materialIns->material->getVec4Key("diffuseColor"));
457
458 glm::vec4 color = albedo == glm::vec4{ 0 } ? diffuse : albedo;
459
460 GLTFbaseColorFactor.PushBack(color.x, doc.GetAllocator());
461 GLTFbaseColorFactor.PushBack(color.y, doc.GetAllocator());
462 GLTFbaseColorFactor.PushBack(color.z, doc.GetAllocator());
463 GLTFbaseColorFactor.PushBack(color.w, doc.GetAllocator());
464 GLTFpbrMetallicRoughness.AddMember("baseColorFactor", GLTFbaseColorFactor, doc.GetAllocator());
465 }
466
467 texture = materialIns->getTextureProperty(materialIns->material->getTextureKey("metallicRoughnessMap"));
468 if (texture.property != nullptr && texture.texture.handle) {
469 if (texture.texture.handle->description.height > 4 || texture.texture.handle->description.width > 4) {
470 const Cogs::Core::TextureHandle& handle = texture.texture.handle;
471 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
472 GLTFpbrMetallicRoughness.AddMember("metallicRoughnessTexture", GLTFmaterialProperty, doc.GetAllocator());
473 }
474 }
475 texture = materialIns->getTextureProperty(materialIns->material->getTextureKey("normalMap"));
476 if (texture.property != nullptr && texture.texture.handle) {
477 if (texture.texture.handle->description.height > 4 || texture.texture.handle->description.width > 4) {
478 const Cogs::Core::TextureHandle& handle = texture.texture.handle;
479 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
480 GLTFmaterial.AddMember("normalTexture", GLTFmaterialProperty, doc.GetAllocator());
481 }
482 }
483 texture = materialIns->getTextureProperty(materialIns->material->getTextureKey("emissiveMap"));
484 if (texture.property != nullptr && texture.texture.handle) {
485 if (texture.texture.handle->description.height > 4 || texture.texture.handle->description.width > 4) {
486 const Cogs::Core::TextureHandle& handle = texture.texture.handle;
487 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
488 GLTFmaterial.AddMember("emissiveTexture", GLTFmaterialProperty, doc.GetAllocator());
489 }
490 }
491 texture = materialIns->getTextureProperty(materialIns->material->getTextureKey("occlusionMap"));
492 if (texture.property != nullptr && texture.texture.handle) {
493 if (texture.texture.handle->description.height > 4 || texture.texture.handle->description.width > 4) {
494 const Cogs::Core::TextureHandle& handle = texture.texture.handle;
495 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
496 GLTFmaterial.AddMember("occlusionTexture", GLTFmaterialProperty, doc.GetAllocator());
497 }
498 }
499 }
500
501 GLTFpbrMetallicRoughness.AddMember("metallicFactor", 0, doc.GetAllocator());
502
503 GLTFmaterial.AddMember("pbrMetallicRoughness", GLTFpbrMetallicRoughness, doc.GetAllocator());
504
505 if (doc.HasMember("materials") && doc["materials"].IsArray()) {
506 auto materials = doc["materials"].GetArray();
507 GLTFmaterialName.SetString(std::string("Material" + std::to_string(materials.Size())).c_str(), doc.GetAllocator());
508 GLTFmaterial.AddMember("name", GLTFmaterialName, doc.GetAllocator());
509 materials.PushBack(GLTFmaterial, doc.GetAllocator());
510
511 return materials.Size() - 1;
512 }
513 else {
514 rapidjson::Value GLTFmaterials(rapidjson::kArrayType);
515 GLTFmaterialName.SetString("Material0", doc.GetAllocator());
516 GLTFmaterial.AddMember("name", GLTFmaterialName, doc.GetAllocator());
517 GLTFmaterials.PushBack(GLTFmaterial, doc.GetAllocator());
518 doc.AddMember("materials", GLTFmaterials, doc.GetAllocator());
519
520 return 0;
521 }
522 }
523
524 template <typename MinMax>
525 void generateAttribute(rapidjson::Document& doc,
526 rapidjson::Value& attr,
528 EAccessorType accessorCompType,
529 const std::string& accessorType,
530 const std::string& filePath,
531 EBufferType bufferType,
532 const rapidjson::GenericStringRef<char>& attributeName,
533 const std::string& extension,
534 Buffer& dataBuffer,
535 const MinMax& min,
536 const MinMax& max)
537 {
538 unsigned int bufferViewIndex = generateBufferView(doc, filePath + extension, stream.data(), stream.size(), std::string("indices").compare(attributeName) ? 0 : stream.stride, bufferType, dataBuffer);
539 unsigned int accesorIndex = generateAccessor<MinMax>(bufferViewIndex, doc, stream.offset, accessorCompType, stream.numElements, accessorType, min, max);
540 attr.AddMember(attributeName, accesorIndex, doc.GetAllocator());
541 }
542
543 rapidjson::Value generatePrimitives(rapidjson::Document& doc,
547 const std::vector<Cogs::Core::MaterialInstanceHandle>& subMeshMaterials,
548 const std::string& filePath,
549 const std::string& extension,
550 Buffer& dataBuffer)
551 {
552 rapidjson::Value GLTFprimitives(rapidjson::kArrayType);
553 rapidjson::Value GLTFattributes(rapidjson::kObjectType);
554 unsigned int indicesBufferViewIndex = 0;
555 std::pair<int32_t, int32_t> indicesMinMax;
556
557 for (size_t i = 0; i < mesh->streams.size(); i++) {
558 if (mesh->streams[i].size() != 0) {
559 if (mesh->streams[i].type != Cogs::Core::VertexDataType::Interleaved0 &&
560 mesh->streams[i].type != Cogs::Core::VertexDataType::Interleaved1) {
561 if (mesh->streams[i].type == Cogs::Core::VertexDataType::Indexes) {
562 indicesMinMax = getMinMaxIndex(mesh->streams[i]);
565 buffer.reserve(mesh->getIndexes().size_bytes());
566 for (auto x = mesh->getIndexes().rbegin(); x != mesh->getIndexes().rend(); ++x) {
567 buffer.write32(*x);
568 }
569 indicesBufferViewIndex = generateBufferView(doc, filePath + extension, buffer.data(), buffer.size(), 0, EBufferType::ElementArrayBuffer, dataBuffer);
570 }
571 else {
572 indicesBufferViewIndex = generateBufferView(doc, filePath + extension, mesh->streams[i].data(), mesh->streams[i].size(), 0, EBufferType::ElementArrayBuffer, dataBuffer);
573 }
574 }
575 else if (mesh->streams[i].type == Cogs::Core::VertexDataType::Positions) {
576 std::pair<glm::vec3, glm::vec3> minMax = getMinMaxVec3(mesh->streams[i], 0);
577 generateAttribute<glm::vec3>(doc, GLTFattributes, mesh->streams[i], EAccessorType::FLOAT,
578 std::string("VEC3"), filePath, EBufferType::ArrayBuffer, rapidjson::GenericStringRef<char>("POSITION"), extension, dataBuffer, minMax.first, minMax.second);
579 }
580 else if (mesh->streams[i].type == Cogs::Core::VertexDataType::Normals) {
581 std::pair<glm::vec3, glm::vec3> minMax = getMinMaxVec3(mesh->streams[i], 0);
582 generateAttribute<glm::vec3>(doc, GLTFattributes, mesh->streams[i], EAccessorType::FLOAT,
583 std::string("VEC3"), filePath, EBufferType::ArrayBuffer, rapidjson::GenericStringRef<char>("NORMAL"), extension, dataBuffer, minMax.first, minMax.second);
584 }
585 else if (mesh->streams[i].type == Cogs::Core::VertexDataType::TexCoords0) {
586 std::pair<glm::vec2, glm::vec2> minMax = getMinMaxVec2(mesh->streams[i], 0);
587 generateAttribute<glm::vec2>(doc, GLTFattributes, mesh->streams[i], EAccessorType::FLOAT,
588 std::string("VEC2"), filePath, EBufferType::ArrayBuffer, rapidjson::GenericStringRef<char>("TEXCOORD_0"), extension, dataBuffer, minMax.first, minMax.second);
589 }
590 else if (mesh->streams[i].type == Cogs::Core::VertexDataType::TexCoords1) {
591 std::pair<glm::vec2, glm::vec2> minMax = getMinMaxVec2(mesh->streams[i], 0);
592 generateAttribute<glm::vec2>(doc, GLTFattributes, mesh->streams[i], EAccessorType::FLOAT,
593 std::string("VEC2"), filePath, EBufferType::ArrayBuffer, rapidjson::GenericStringRef<char>("TEXCOORD_1"), extension, dataBuffer, minMax.first, minMax.second);
594 }
595 else if (mesh->streams[i].type == Cogs::Core::VertexDataType::TexCoords2) {
596 std::pair<glm::vec2, glm::vec2> minMax = getMinMaxVec2(mesh->streams[i], 0);
597 generateAttribute<glm::vec2>(doc, GLTFattributes, mesh->streams[i], EAccessorType::FLOAT,
598 std::string("VEC2"), filePath, EBufferType::ArrayBuffer, rapidjson::GenericStringRef<char>("TEXCOORD_2"), extension, dataBuffer, minMax.first, minMax.second);
599 }
600 }
601 else {
602 static const Cogs::VertexFormat* vertexPrimitive = Cogs::VertexFormats::getVertexFormat(mesh->streams[i].format);
603 unsigned int bufferViewIndex = generateBufferView(doc, filePath + extension, mesh->streams[i].data(), mesh->streams[i].size(), getSize(*vertexPrimitive), EBufferType::ArrayBuffer, dataBuffer);
604 for (auto& data : vertexPrimitive->elements) {
605 if (data.semantic == Cogs::ElementSemantic::Position) {
606 std::pair<glm::vec3, glm::vec3> minMax = getMinMaxVec3(mesh->streams[i], data.offset);
607 unsigned int accesorIndex = generateAccessor<glm::vec3>(bufferViewIndex, doc, data.offset, EAccessorType::FLOAT, mesh->streams[i].numElements, std::string("VEC3"), minMax.first, minMax.second);
608 GLTFattributes.AddMember("POSITION", accesorIndex, doc.GetAllocator());
609 }
610 else if (data.semantic == Cogs::ElementSemantic::Normal) {
611 std::pair<glm::vec3, glm::vec3> minMax = getMinMaxVec3(mesh->streams[i], data.offset);
612 unsigned int accesorIndex = generateAccessor<glm::vec3>(bufferViewIndex, doc, data.offset, EAccessorType::FLOAT, mesh->streams[i].numElements, std::string("VEC3"), minMax.first, minMax.second);
613 GLTFattributes.AddMember("NORMAL", accesorIndex, doc.GetAllocator());
614 }
615 else if (data.semantic == Cogs::ElementSemantic::TextureCoordinate) {
616 std::pair<glm::vec2, glm::vec2> minMax = getMinMaxVec2(mesh->streams[i], data.offset);
617 unsigned int accesorIndex = generateAccessor<glm::vec2>(bufferViewIndex, doc, data.offset, EAccessorType::FLOAT, mesh->streams[i].numElements, std::string("VEC2"), minMax.first, minMax.second);
618 GLTFattributes.AddMember("TEXCOORD_0", accesorIndex, doc.GetAllocator());
619 }
620 }
621 }
622 }
623 }
624
625 std::span<Cogs::Core::SubMesh> subMeshes = mesh->getSubMeshes();
626 if (subMeshes.size()) {
627 size_t subMeshMaterialIndex = 0;
628 for (const auto &subMesh : subMeshes) {
629 rapidjson::Value GLTFprimitive(rapidjson::kObjectType);
630 rapidjson::Value GLTFattributesCopy(rapidjson::kObjectType);
631
632 GLTFattributesCopy.CopyFrom(GLTFattributes, doc.GetAllocator());
633
634 unsigned int accesorIndex = generateAccessor<int32_t>(indicesBufferViewIndex, doc, sizeof(unsigned int) * subMesh.startIndex, EAccessorType::UNSIGNED_INT, subMesh.numIndexes, std::string("SCALAR"), indicesMinMax.first, indicesMinMax.second);
635 GLTFprimitive.AddMember("indices", accesorIndex, doc.GetAllocator());
636
637 GLTFprimitive.AddMember("attributes", GLTFattributesCopy, doc.GetAllocator());
638 GLTFprimitive.AddMember("mode", toGltfPrimitiveType(mesh->primitiveType), doc.GetAllocator());
639
640 unsigned int materialIndex;
641 if (subMeshMaterialIndex < subMeshMaterials.size()) {
642 materialIndex = generateMaterial(doc, filePath, subMeshMaterials[subMeshMaterialIndex], matComp, extension, dataBuffer);
643 subMeshMaterialIndex++;
644 }
645 else {
646 materialIndex = generateMaterial(doc, filePath, materialIns, matComp, extension, dataBuffer);
647 }
648 GLTFprimitive.AddMember("material", materialIndex, doc.GetAllocator());
649
650 GLTFprimitives.PushBack(GLTFprimitive, doc.GetAllocator());
651 }
652 }
653 else {
654 rapidjson::Value GLTFprimitive(rapidjson::kObjectType);
655
656 unsigned int accesorIndex = generateAccessor<int32_t>(indicesBufferViewIndex, doc, 0, EAccessorType::UNSIGNED_INT, mesh->getCount(), std::string("SCALAR"), indicesMinMax.first, indicesMinMax.second);
657 GLTFprimitive.AddMember("indices", accesorIndex, doc.GetAllocator());
658
659 GLTFprimitive.AddMember("attributes", GLTFattributes, doc.GetAllocator());
660 GLTFprimitive.AddMember("mode", toGltfPrimitiveType(mesh->primitiveType), doc.GetAllocator());
661
662 unsigned int materialIndex = generateMaterial(doc, filePath, materialIns, matComp, extension, dataBuffer);
663 GLTFprimitive.AddMember("material", materialIndex, doc.GetAllocator());
664
665 GLTFprimitives.PushBack(GLTFprimitive, doc.GetAllocator());
666 }
667
668 return GLTFprimitives;
669 }
670
671 unsigned int generateMesh(rapidjson::Document& doc,
675 const std::vector<Cogs::Core::MaterialInstanceHandle>& subMeshMaterials,
676 const std::string& filePath,
677 const std::string& extension,
678 Buffer& dataBuffer)
679 {
680 rapidjson::Value GLTFmesh(rapidjson::kObjectType);
681
682 rapidjson::Value GLTFprimitives = generatePrimitives(doc, mesh, materialIns, matComp, subMeshMaterials, filePath, extension, dataBuffer);
683
684 GLTFmesh.AddMember("primitives", GLTFprimitives, doc.GetAllocator());
685
686 rapidjson::Value GLTFname(rapidjson::kStringType);
687
688 if (doc.HasMember("meshes") && doc["meshes"].IsArray()) {
689 auto meshes = doc["meshes"].GetArray();
690 GLTFname.SetString(std::string("Mesh" + std::to_string(meshes.Size())).c_str(), doc.GetAllocator());
691 GLTFmesh.AddMember("name", GLTFname, doc.GetAllocator());
692 meshes.PushBack(GLTFmesh, doc.GetAllocator());
693
694 return meshes.Size() - 1;
695 }
696 else {
697 rapidjson::Value meshes(rapidjson::kArrayType);
698 GLTFname.SetString("Mesh0", doc.GetAllocator());
699 GLTFmesh.AddMember("name", GLTFname, doc.GetAllocator());
700 meshes.PushBack(GLTFmesh, doc.GetAllocator());
701 doc.AddMember("meshes", meshes, doc.GetAllocator());
702
703 return 0;
704 }
705 }
706
707 rapidjson::Value generateNode(rapidjson::Document& doc,
711 std::vector<Cogs::Core::MaterialInstanceHandle>& subMeshMaterials,
712 glm::mat4& modelMatrix,
713 const std::string& filePath,
714 const std::string& extension,
715 Buffer& dataBuffer)
716 {
717 rapidjson::Value GLTFnode(rapidjson::kObjectType);
718
719 unsigned int meshIndex = generateMesh(doc, mesh, materialIns, matComp, subMeshMaterials, filePath, extension, dataBuffer);
720 GLTFnode.AddMember("mesh", meshIndex, doc.GetAllocator());
721
722 rapidjson::Value transformation(rapidjson::kArrayType);
723 for (int c = 0; c < 4; c++) {
724 for (int r = 0; r < 4; r++) {
725 transformation.PushBack(modelMatrix[c][r], doc.GetAllocator());
726 }
727 }
728 GLTFnode.AddMember("matrix", transformation, doc.GetAllocator());
729
730 return GLTFnode;
731 }
732
733 rapidjson::Value generateNodes(rapidjson::Document& doc,
734 std::vector<ComponentModel::Entity*>& _entities,
735 const std::string& filePath,
736 const std::string& extension,
737 Buffer& dataBuffer)
738 {
739 rapidjson::Value GLTFnodes(rapidjson::kArrayType);
740 std::vector<std::pair<Cogs::ComponentModel::Entity*, glm::mat4>> meshEntities;
741
742 for (unsigned int i = 0; i < _entities.size(); i++) {
743
744 glm::mat4 modelMatrix{ 1 };
745 extractMeshes(_entities[i], modelMatrix, meshEntities);
746
747 }
748
749 for (const auto& entity : meshEntities) {
750 Cogs::Core::MeshComponent* mesh = entity.first->getComponent<Cogs::Core::MeshComponent>();
751 if (mesh && mesh->meshHandle) {
754 std::vector<Cogs::Core::MaterialInstanceHandle> subMeshMaterials;
755 if (subMeshComp != nullptr) {
756 subMeshMaterials = subMeshComp->materials;
757 }
758
759 glm::mat4 modelMatrix = entity.second;
760
761 if (material) {
763 rapidjson::Value GLTFnode = generateNode(doc, mesh->meshHandle, emptyInstance, material, subMeshMaterials, modelMatrix, filePath, extension, dataBuffer);
764 GLTFnodes.PushBack(GLTFnode, doc.GetAllocator());
765 }
766 else {
767 Cogs::Core::MeshRenderComponent* render = entity.first->getComponent<Cogs::Core::MeshRenderComponent>();
768 rapidjson::Value GLTFnode = generateNode(doc, mesh->meshHandle, render->material, nullptr, subMeshMaterials, modelMatrix, filePath, extension, dataBuffer);
769 GLTFnodes.PushBack(GLTFnode, doc.GetAllocator());
770 }
771 }
772 }
773
774 return GLTFnodes;
775 }
776
777 rapidjson::Value generateAsset(rapidjson::Document& doc)
778 {
779 rapidjson::Value GLTFasset(rapidjson::kObjectType);
780 GLTFasset.AddMember("generator", "COGS2GLTF", doc.GetAllocator());
781 GLTFasset.AddMember("version", "2.0", doc.GetAllocator());
782
783 return GLTFasset;
784 }
785
786 void generateSceneNodes(rapidjson::Document& doc, rapidjson::Value& GLTFfirstSceneNodes, uint32_t& nodeCount, ComponentModel::Entity* currentEntity)
787 {
788 if (!currentEntity) return;
789
791 if (mesh && mesh->meshHandle) {
792 GLTFfirstSceneNodes.PushBack(nodeCount, doc.GetAllocator());
793 nodeCount++;
794 }
795
797 if (scene) {
798 for (const auto& c : scene->children) {
799 generateSceneNodes(doc, GLTFfirstSceneNodes, nodeCount, c.get());
800 }
801 }
802 }
803
804 rapidjson::Value generateScenes(rapidjson::Document& doc, std::vector<ComponentModel::Entity*>& _entities)
805 {
806 rapidjson::Value GLTFscenes(rapidjson::kArrayType);
807 rapidjson::Value GLTFfirstScene(rapidjson::kObjectType);
808 rapidjson::Value GLTFfirstSceneNodes(rapidjson::kArrayType);
809
810 uint32_t nodeCount = 0;
811 for (unsigned int i = 0; i < _entities.size(); i++) {
812 rapidjson::Value GLTFnode(rapidjson::kObjectType);
813 generateSceneNodes(doc, GLTFfirstSceneNodes, nodeCount, _entities[i]);
814 }
815
816 GLTFfirstScene.AddMember("nodes", GLTFfirstSceneNodes, doc.GetAllocator());
817 GLTFscenes.PushBack(GLTFfirstScene, doc.GetAllocator());
818
819 return GLTFscenes;
820 }
821
822 bool writeGltf(std::vector<ComponentModel::Entity*>& entities, const std::string& filePath, const std::string& fileName)
823 {
824 if (entities.size() == 0 || fileName.empty() || filePath.empty()) return false;
825
826 Buffer empty;
827 rapidjson::Document doc;
828 doc.SetObject();
829
830 rapidjson::Value GLTFbuffers = generateBuffer(doc, fileName + ".bin");
831 doc.AddMember("buffers", GLTFbuffers, doc.GetAllocator());
832
833 rapidjson::Value GLTFasset = generateAsset(doc);
834 doc.AddMember("asset", GLTFasset, doc.GetAllocator());
835
836 rapidjson::Value GLTFscenes = generateScenes(doc, entities);
837 doc.AddMember("scene", 0, doc.GetAllocator());
838 doc.AddMember("scenes", GLTFscenes, doc.GetAllocator());
839
840 rapidjson::Value GLTFnodes = generateNodes(doc, entities, Cogs::IO::combine(filePath, fileName), std::string(".bin"), empty);
841 doc.AddMember("nodes", GLTFnodes, doc.GetAllocator());
842
843 FILE* jsonFile = fopen(Cogs::IO::combine(filePath, fileName + std::string(".gltf")).c_str(), "wb");
844 if (jsonFile == NULL) {
845 LOG_ERROR(logger, "Could not create GLTF file.");
846 return false;
847 }
848
849 char writeBuffer[65536];
850 rapidjson::FileWriteStream os(jsonFile, writeBuffer, sizeof(writeBuffer));
851 rapidjson::PrettyWriter<rapidjson::FileWriteStream> writer(os);
852 if (!doc.Accept(writer)) {
853 LOG_ERROR(logger, "GLTF - JSON serialization failed");
854 return false;
855 }
856
857 os.Flush();
858 fclose(jsonFile);
859 return true;
860 }
861
862 bool writeGlb(std::vector<ComponentModel::Entity*>& entities, const std::string& filePath, const std::string& fileName)
863 {
864 if (entities.size() == 0 || fileName.empty() || filePath.empty()) return false;
865
866 Buffer dataBuffer{ true };
867 rapidjson::Document doc;
868 doc.SetObject();
869
870 rapidjson::Value GLTFbuffers = generateBuffer(doc, "");
871 doc.AddMember("buffers", GLTFbuffers, doc.GetAllocator());
872
873 rapidjson::Value GLTFasset = generateAsset(doc);
874 doc.AddMember("asset", GLTFasset, doc.GetAllocator());
875
876 rapidjson::Value GLTFscenes = generateScenes(doc, entities);
877 doc.AddMember("scene", 0, doc.GetAllocator());
878 doc.AddMember("scenes", GLTFscenes, doc.GetAllocator());
879
880 rapidjson::Value GLTFnodes = generateNodes(doc, entities, Cogs::IO::combine(filePath, fileName), "", dataBuffer);
881 doc.AddMember("nodes", GLTFnodes, doc.GetAllocator());
882
883 rapidjson::StringBuffer jsonStringBuffer;
884 rapidjson::Writer<rapidjson::StringBuffer> writer(jsonStringBuffer);
885 if (!doc.Accept(writer)) {
886 LOG_ERROR(logger, "GLTF - JSON serialization failed");
887 return false;
888 }
889
890 // Create GLB file
891 std::fstream glbFile(Cogs::IO::combine(filePath, fileName + std::string(".glb")).c_str(), std::ios::binary | std::ios::out | std::ios::app);
892 if (!glbFile.is_open()) {
893 LOG_ERROR(logger, "GLB file could not be created");
894 return false;
895 }
896
897 // JSON chunk
898 const char* jsonChunkData = jsonStringBuffer.GetString();
899 uint32_t jsonChunkSize = static_cast<uint32_t>(jsonStringBuffer.GetSize());
900 uint32_t jsonChunkType = 0x4E4F534A;
901 uint32_t jsonChunkPadding = jsonChunkSize % 4;
902 jsonChunkSize += jsonChunkPadding; // Add the total length of the json with padding
903
904 // Data chunk
905 uint32_t dataChunkSize = static_cast<uint32_t>(dataBuffer.buffer.size());
906 uint32_t dataChunkType = 0x004E4942;
907 char* dataChunkData = (char*)dataBuffer.buffer.data();
908
909 // Main header
910 uint32_t magic = 0x46546C67;
911 uint32_t version = 2;
912 uint32_t length = 28 + dataChunkSize + jsonChunkSize; // Adding 28 bytes on top to accomodate for headers and such.
913
914 // Write header
915 glbFile.write((char*)&magic, sizeof(uint32_t));
916 glbFile.write((char*)&version, sizeof(uint32_t));
917 glbFile.write((char*)&length, sizeof(uint32_t));
918
919 // Write JSON chunk
920 glbFile.write((char*)&jsonChunkSize, sizeof(uint32_t));
921 glbFile.write((char*)&jsonChunkType, sizeof(uint32_t));
922 glbFile.write(jsonChunkData, static_cast<std::streamsize>(jsonChunkSize) - jsonChunkPadding); // Substract the padding so we don't write data that's not part of the JSON
923
924 // Fix JSON alignment
925 while (jsonChunkPadding) {
926 char space = static_cast<char>(0x20);
927 glbFile.write(&space, sizeof(char));
928 jsonChunkPadding--;
929 }
930
931 // Write data chunk
932 glbFile.write((char*)&dataChunkSize, sizeof(uint32_t));
933 glbFile.write((char*)&dataChunkType, sizeof(uint32_t));
934 glbFile.write(dataChunkData, dataChunkSize); // Write the raw data. Padding not needed because buffer data was already padded when it was generated
935
936 // Close file
937 glbFile.close();
938
939 return true;
940 }
941}
942
943#else
944bool Cogs::Core::GltfWriter::writeGltf(std::vector<ComponentModel::Entity*>& /*entities*/, const std::string& /*filePath*/, const std::string& /*fileName*/)
945{
946 return false;
947}
948bool Cogs::Core::GltfWriter::writeGlb(std::vector<ComponentModel::Entity*>& /*entities*/, const std::string& /*filePath*/, const std::string& /*fileName*/)
949{
950 return false;
951}
952#endif
ComponentType * getComponent() const
Definition: Component.h:159
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Definition: Entity.h:35
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
Definition: MeshComponent.h:15
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
Definition: MeshComponent.h:29
Renders the contents of a MeshComponent using the given materials.
Contains information on how the entity behaves in the scene.
std::vector< EntityPtr > children
Contains all child entities owned by this component.
Renders a mesh with flexible submesh usage.
std::vector< MaterialInstanceHandle > materials
Materials used to render individual sub-meshes.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
glm::vec3 scale
Scale factor to apply to each of the axes.
glm::quat rotation
Rotation given as a quaternion.
glm::vec3 position
Local position relative to the global coordinates, or the parent coordinate system if the parent fiel...
Log implementation class.
Definition: LogManager.h:139
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
@ Position
Position semantic.
@ Normal
Normal semantic.
@ TextureCoordinate
Texture coordinate semantic.
Contains a stream of data used by Mesh resources.
Definition: Mesh.h:80
uint32_t numElements
Number of elements of the type given by format contained in data.
Definition: Mesh.h:108
uint32_t offset
Byte offset from the start of the buffer.
Definition: Mesh.h:102
uint32_t stride
Element stride.
Definition: Mesh.h:105
Exposes material properties for legacy entities and code.
glm::vec4 getVec4Property(const VariableKey key) const
Get the value of the property with the given key.
TextureValue getTextureProperty(const VariableKey key) const
Get the value of the property with the given key.
Material * material
Material resource this MaterialInstance is created from.
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
Definition: Mesh.h:63
constexpr bool isMeshFlagSet(MeshFlags::EMeshFlags flag) const
Check if the given mesh flag(s) is set.
Definition: Mesh.h:683
uint32_t getCount() const
Get the vertex count of the mesh.
Definition: Mesh.h:1012
TextureWithSampler texture
Value of the property for the material instance this belongs to.
const TextureProperty * property
Pointer to the property this value is for.
TextureHandle handle
Handle to a texture resource, or TextureHandle::NoHandle if texture should be disabled.
EPrimitiveType
Primitive type enumeration.
Definition: Common.h:114
@ LineStrip
Line strip.
Definition: Common.h:122
@ LineList
List of lines.
Definition: Common.h:120
@ TriangleStrip
Triangle strip.
Definition: Common.h:118
@ PointList
List of points.
Definition: Common.h:124
@ TriangleList
List of triangles.
Definition: Common.h:116
Vertex format structure used to describe a single vertex for the input assembler.
Definition: VertexFormat.h:60
std::vector< VertexElement > elements
Vector containing all vertex elements of this format.
Definition: VertexFormat.h:62