3#include "EntityStore.h"
4#include "rapidjson/document.h"
5#include "rapidjson/pointer.h"
6#include "rapidjson/filewritestream.h"
7#include "rapidjson/prettywriter.h"
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"
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"
26#include "Foundation/Logging/Logger.h"
27#include "Foundation/Platform/IO.h"
30#include <glm/gtx/quaternion.hpp>
31#include <glm/gtc/matrix_transform.hpp>
32#include "stb_image_write.h"
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();
49 enum EPrimitiveMode { Points, Lines, LineLoop, LineStrip, Triangles, TriangleStrip, TriangleFan };
51 enum EAccessorType { BYTE = 5120, UNSIGNED_BYTE = 5121, SHORT = 5122, UNSIGNED_SHORT = 5123, UNSIGNED_INT = 5125, FLOAT = 5126 };
53 enum EBufferType { Image = 0, ArrayBuffer = 34962, ElementArrayBuffer = 34963 };
55 enum EAccessorContainer { SCALAR, VEC2, VEC3, VEC4, MAT2, MAT3, MAT4 };
60 Buffer(
bool enable =
false) : buffer(
Cogs::MemBlockType::IOResource), active(enable) {}
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];
77 c->last_pos = cur_pos;
80 void* ptrOffset(
void* ptr,
unsigned int offset) {
81 return static_cast<char*
>(ptr) + 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);
93 return std::make_pair(min, max);
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);
105 return std::make_pair(min, max);
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);
118 return std::make_pair(min, max);
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;
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()) {
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);
133 glm::mat4 matrix = translate * rotate * scale;
134 modelMatrix = modelMatrix * matrix;
136 entities.push_back(std::make_pair(currentEntity, modelMatrix));
142 for (
const auto& c : scene->
children) {
143 glm::mat4 childModelMatrix{ modelMatrix };
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);
150 glm::mat4 matrix = translate * rotate * scale;
151 childModelMatrix = modelMatrix * matrix;
153 extractMeshes(c.get(), childModelMatrix, entities);
160namespace Cogs::Core::GltfWriter {
169 default:
return EPrimitiveMode::Triangles;
173 rapidjson::Value generateBuffer(rapidjson::Document& doc,
const std::string& name)
175 rapidjson::Value GLTFbuffers(rapidjson::kArrayType);
176 rapidjson::Value GLTFbuffer(rapidjson::kObjectType);
177 rapidjson::Value GLTFuri(rapidjson::kStringType);
179 GLTFuri.SetString(name.c_str(), doc.GetAllocator());
181 GLTFbuffer.AddMember(
"byteLength", 0, doc.GetAllocator());
182 if (!name.empty()) GLTFbuffer.AddMember(
"uri", GLTFuri, doc.GetAllocator());
183 GLTFbuffers.PushBack(GLTFbuffer, doc.GetAllocator());
188 unsigned int generateBufferView(rapidjson::Document& doc,
189 const std::string& path,
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!");
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());
210 bufferFile.write(
static_cast<const char*
>(data), dataSize);
213 while (
static_cast<size_t>(bufferFile.tellg()) % 4) {
215 bufferFile.write(&t,
sizeof(
char));
218 doc[
"buffers"].GetArray()[0][
"byteLength"].SetInt(
static_cast<int>(bufferFile.tellg()));
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());
229 dataBuffer.buffer.write(
static_cast<const char*
>(data), dataSize);
232 while (
static_cast<size_t>(dataBuffer.buffer.size()) % 4) {
234 dataBuffer.buffer.write(&t,
sizeof(
char));
237 doc[
"buffers"].GetArray()[0][
"byteLength"].SetInt(
static_cast<int>(dataBuffer.buffer.size()));
240 if (doc.HasMember(
"bufferViews") && doc[
"bufferViews"].IsArray()) {
241 const auto bufferviews = doc[
"bufferViews"].GetArray();
242 bufferviews.PushBack(GLTFbufferView, doc.GetAllocator());
244 return bufferviews.Size() - 1;
247 rapidjson::Value GLTFbufferviews(rapidjson::kArrayType);
248 GLTFbufferviews.PushBack(GLTFbufferView, doc.GetAllocator());
249 doc.AddMember(
"bufferViews", GLTFbufferviews, doc.GetAllocator());
255 template <
typename MinMax>
256 unsigned int generateAccessor(
int id,
257 rapidjson::Document& doc,
259 EAccessorType componentType,
261 const std::string& type,
265 rapidjson::Value GLTFaccessor(rapidjson::kObjectType);
266 rapidjson::Value GLTFtype(rapidjson::kStringType);
267 rapidjson::Value GLTFmin(rapidjson::kArrayType);
268 rapidjson::Value GLTFmax(rapidjson::kArrayType);
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());
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());
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());
290 if (maxVec.x != floatMin) {
291 GLTFmax.PushBack(maxVec.x, doc.GetAllocator());
292 GLTFmax.PushBack(maxVec.y, doc.GetAllocator());
296 GLTFmin.PushBack(min, doc.GetAllocator());
299 GLTFmax.PushBack(max, doc.GetAllocator());
303 GLTFtype.SetString(type.c_str(), doc.GetAllocator());
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());
313 if (doc.HasMember(
"accessors") && doc[
"accessors"].IsArray()) {
314 const auto accessors = doc[
"accessors"].GetArray();
315 accessors.PushBack(GLTFaccessor, doc.GetAllocator());
317 return accessors.Size() - 1;
320 rapidjson::Value accessors(rapidjson::kArrayType);
321 accessors.PushBack(GLTFaccessor, doc.GetAllocator());
322 doc.AddMember(
"accessors", accessors, doc.GetAllocator());
328 unsigned int generateImage(rapidjson::Document& doc,
329 const std::string& filePath,
332 const std::string& extension,
335 rapidjson::Value GLTFimage(rapidjson::kObjectType);
336 unsigned int bufferViewIndex = generateBufferView(doc, filePath + extension, buffer, bufferSize, 0, EBufferType::Image, dataBuffer);
338 GLTFimage.AddMember(
"mimeType",
"image/png", doc.GetAllocator());
339 GLTFimage.AddMember(
"bufferView", bufferViewIndex, doc.GetAllocator());
341 if (doc.HasMember(
"images") && doc[
"images"].IsArray()) {
342 auto images = doc[
"images"].GetArray();
343 images.PushBack(GLTFimage, doc.GetAllocator());
345 return images.Size() - 1;
348 rapidjson::Value GLTFimages(rapidjson::kArrayType);
349 GLTFimages.PushBack(GLTFimage, doc.GetAllocator());
350 doc.AddMember(
"images", GLTFimages, doc.GetAllocator());
356 unsigned int generateTexture(rapidjson::Document& doc,
357 const std::string& filePath,
360 const std::string& extension,
363 rapidjson::Value GLTFtexture(rapidjson::kObjectType);
364 unsigned int imageIndex = generateImage(doc, filePath, buffer, bufferSize, extension, dataBuffer);
365 GLTFtexture.AddMember(
"source", imageIndex, doc.GetAllocator());
367 if (doc.HasMember(
"textures") && doc[
"textures"].IsArray()) {
368 auto textures = doc[
"textures"].GetArray();
369 textures.PushBack(GLTFtexture, doc.GetAllocator());
371 return textures.Size() - 1;
374 rapidjson::Value GLTFtextures(rapidjson::kArrayType);
375 GLTFtextures.PushBack(GLTFtexture, doc.GetAllocator());
376 doc.AddMember(
"textures", GLTFtextures, doc.GetAllocator());
382 rapidjson::Value generateMaterialProperty(rapidjson::Document& doc,
383 const std::string& filePath,
385 const std::string& extension,
388 rapidjson::Value GLTFmaterialProperty(rapidjson::kObjectType);
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);
397 LOG_ERROR(logger,
"stbi failed to write image data to memory.");
398 return GLTFmaterialProperty;
401 unsigned int index = generateTexture(doc, filePath, buffer, context.last_pos, extension, dataBuffer);
402 GLTFmaterialProperty.AddMember(
"index", index, doc.GetAllocator());
405 return GLTFmaterialProperty;
408 unsigned int generateMaterial(rapidjson::Document& doc,
409 const std::string& filePath,
412 const std::string& extension,
415 rapidjson::Value GLTFmaterial(rapidjson::kObjectType);
416 rapidjson::Value GLTFpbrMetallicRoughness(rapidjson::kObjectType);
417 rapidjson::Value GLTFbaseColorFactor(rapidjson::kArrayType);
418 rapidjson::Value GLTFmaterialName(rapidjson::kStringType);
420 if (matComp !=
nullptr) {
421 for (
int i = 0; i < 4; i++) {
422 GLTFbaseColorFactor.PushBack(matComp->diffuseColor[i], doc.GetAllocator());
424 GLTFpbrMetallicRoughness.AddMember(
"roughnessFactor", matComp->specularPower / 100.f, doc.GetAllocator());
426 if (matComp->diffuseMap !=
nullptr) {
427 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, matComp->diffuseMap, extension, dataBuffer);
428 GLTFpbrMetallicRoughness.AddMember(
"baseColorTexture", GLTFmaterialProperty, doc.GetAllocator());
430 if (matComp->normalMap !=
nullptr) {
431 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, matComp->normalMap, extension, dataBuffer);
432 GLTFmaterial.AddMember(
"normalTexture", GLTFmaterialProperty, doc.GetAllocator());
434 if (matComp->specularMap !=
nullptr) {
435 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, matComp->specularMap, extension, dataBuffer);
436 GLTFmaterial.AddMember(
"specularTexture", GLTFmaterialProperty, doc.GetAllocator());
439 else if (materialIns !=
nullptr) {
446 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
447 GLTFpbrMetallicRoughness.AddMember(
"baseColorTexture", GLTFmaterialProperty, doc.GetAllocator());
451 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
452 GLTFpbrMetallicRoughness.AddMember(
"baseColorTexture", GLTFmaterialProperty, doc.GetAllocator());
458 glm::vec4 color = albedo == glm::vec4{ 0 } ? diffuse : albedo;
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());
471 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
472 GLTFpbrMetallicRoughness.AddMember(
"metallicRoughnessTexture", GLTFmaterialProperty, doc.GetAllocator());
479 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
480 GLTFmaterial.AddMember(
"normalTexture", GLTFmaterialProperty, doc.GetAllocator());
487 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
488 GLTFmaterial.AddMember(
"emissiveTexture", GLTFmaterialProperty, doc.GetAllocator());
495 rapidjson::Value GLTFmaterialProperty = generateMaterialProperty(doc, filePath, handle, extension, dataBuffer);
496 GLTFmaterial.AddMember(
"occlusionTexture", GLTFmaterialProperty, doc.GetAllocator());
501 GLTFpbrMetallicRoughness.AddMember(
"metallicFactor", 0, doc.GetAllocator());
503 GLTFmaterial.AddMember(
"pbrMetallicRoughness", GLTFpbrMetallicRoughness, doc.GetAllocator());
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());
511 return materials.Size() - 1;
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());
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,
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());
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,
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;
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) {
569 indicesBufferViewIndex = generateBufferView(doc, filePath + extension, buffer.data(), buffer.size(), 0, EBufferType::ElementArrayBuffer, dataBuffer);
572 indicesBufferViewIndex = generateBufferView(doc, filePath + extension, mesh->streams[i].data(), mesh->streams[i].size(), 0, EBufferType::ElementArrayBuffer, dataBuffer);
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);
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);
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);
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);
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);
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) {
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());
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());
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());
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);
632 GLTFattributesCopy.CopyFrom(GLTFattributes, doc.GetAllocator());
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());
637 GLTFprimitive.AddMember(
"attributes", GLTFattributesCopy, doc.GetAllocator());
638 GLTFprimitive.AddMember(
"mode", toGltfPrimitiveType(mesh->primitiveType), doc.GetAllocator());
640 unsigned int materialIndex;
641 if (subMeshMaterialIndex < subMeshMaterials.size()) {
642 materialIndex = generateMaterial(doc, filePath, subMeshMaterials[subMeshMaterialIndex], matComp, extension, dataBuffer);
643 subMeshMaterialIndex++;
646 materialIndex = generateMaterial(doc, filePath, materialIns, matComp, extension, dataBuffer);
648 GLTFprimitive.AddMember(
"material", materialIndex, doc.GetAllocator());
650 GLTFprimitives.PushBack(GLTFprimitive, doc.GetAllocator());
654 rapidjson::Value GLTFprimitive(rapidjson::kObjectType);
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());
659 GLTFprimitive.AddMember(
"attributes", GLTFattributes, doc.GetAllocator());
660 GLTFprimitive.AddMember(
"mode", toGltfPrimitiveType(mesh->primitiveType), doc.GetAllocator());
662 unsigned int materialIndex = generateMaterial(doc, filePath, materialIns, matComp, extension, dataBuffer);
663 GLTFprimitive.AddMember(
"material", materialIndex, doc.GetAllocator());
665 GLTFprimitives.PushBack(GLTFprimitive, doc.GetAllocator());
668 return GLTFprimitives;
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,
680 rapidjson::Value GLTFmesh(rapidjson::kObjectType);
682 rapidjson::Value GLTFprimitives = generatePrimitives(doc, mesh, materialIns, matComp, subMeshMaterials, filePath, extension, dataBuffer);
684 GLTFmesh.AddMember(
"primitives", GLTFprimitives, doc.GetAllocator());
686 rapidjson::Value GLTFname(rapidjson::kStringType);
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());
694 return meshes.Size() - 1;
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());
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,
717 rapidjson::Value GLTFnode(rapidjson::kObjectType);
719 unsigned int meshIndex = generateMesh(doc, mesh, materialIns, matComp, subMeshMaterials, filePath, extension, dataBuffer);
720 GLTFnode.AddMember(
"mesh", meshIndex, doc.GetAllocator());
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());
728 GLTFnode.AddMember(
"matrix", transformation, doc.GetAllocator());
733 rapidjson::Value generateNodes(rapidjson::Document& doc,
734 std::vector<ComponentModel::Entity*>& _entities,
735 const std::string& filePath,
736 const std::string& extension,
739 rapidjson::Value GLTFnodes(rapidjson::kArrayType);
740 std::vector<std::pair<Cogs::ComponentModel::Entity*, glm::mat4>> meshEntities;
742 for (
unsigned int i = 0; i < _entities.size(); i++) {
744 glm::mat4 modelMatrix{ 1 };
745 extractMeshes(_entities[i], modelMatrix, meshEntities);
749 for (
const auto& entity : meshEntities) {
754 std::vector<Cogs::Core::MaterialInstanceHandle> subMeshMaterials;
755 if (subMeshComp !=
nullptr) {
756 subMeshMaterials = subMeshComp->
materials;
759 glm::mat4 modelMatrix = entity.second;
763 rapidjson::Value GLTFnode = generateNode(doc, mesh->
meshHandle, emptyInstance, material, subMeshMaterials, modelMatrix, filePath, extension, dataBuffer);
764 GLTFnodes.PushBack(GLTFnode, doc.GetAllocator());
768 rapidjson::Value GLTFnode = generateNode(doc, mesh->
meshHandle, render->material,
nullptr, subMeshMaterials, modelMatrix, filePath, extension, dataBuffer);
769 GLTFnodes.PushBack(GLTFnode, doc.GetAllocator());
777 rapidjson::Value generateAsset(rapidjson::Document& doc)
779 rapidjson::Value GLTFasset(rapidjson::kObjectType);
780 GLTFasset.AddMember(
"generator",
"COGS2GLTF", doc.GetAllocator());
781 GLTFasset.AddMember(
"version",
"2.0", doc.GetAllocator());
786 void generateSceneNodes(rapidjson::Document& doc, rapidjson::Value& GLTFfirstSceneNodes, uint32_t& nodeCount, ComponentModel::Entity* currentEntity)
788 if (!currentEntity)
return;
792 GLTFfirstSceneNodes.PushBack(nodeCount, doc.GetAllocator());
798 for (
const auto& c : scene->
children) {
799 generateSceneNodes(doc, GLTFfirstSceneNodes, nodeCount, c.get());
804 rapidjson::Value generateScenes(rapidjson::Document& doc, std::vector<ComponentModel::Entity*>& _entities)
806 rapidjson::Value GLTFscenes(rapidjson::kArrayType);
807 rapidjson::Value GLTFfirstScene(rapidjson::kObjectType);
808 rapidjson::Value GLTFfirstSceneNodes(rapidjson::kArrayType);
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]);
816 GLTFfirstScene.AddMember(
"nodes", GLTFfirstSceneNodes, doc.GetAllocator());
817 GLTFscenes.PushBack(GLTFfirstScene, doc.GetAllocator());
822 bool writeGltf(std::vector<ComponentModel::Entity*>& entities,
const std::string& filePath,
const std::string& fileName)
824 if (entities.size() == 0 || fileName.empty() || filePath.empty())
return false;
827 rapidjson::Document doc;
830 rapidjson::Value GLTFbuffers = generateBuffer(doc, fileName +
".bin");
831 doc.AddMember(
"buffers", GLTFbuffers, doc.GetAllocator());
833 rapidjson::Value GLTFasset = generateAsset(doc);
834 doc.AddMember(
"asset", GLTFasset, doc.GetAllocator());
836 rapidjson::Value GLTFscenes = generateScenes(doc, entities);
837 doc.AddMember(
"scene", 0, doc.GetAllocator());
838 doc.AddMember(
"scenes", GLTFscenes, doc.GetAllocator());
840 rapidjson::Value GLTFnodes = generateNodes(doc, entities, Cogs::IO::combine(filePath, fileName), std::string(
".bin"), empty);
841 doc.AddMember(
"nodes", GLTFnodes, doc.GetAllocator());
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.");
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");
862 bool writeGlb(std::vector<ComponentModel::Entity*>& entities,
const std::string& filePath,
const std::string& fileName)
864 if (entities.size() == 0 || fileName.empty() || filePath.empty())
return false;
866 Buffer dataBuffer{
true };
867 rapidjson::Document doc;
870 rapidjson::Value GLTFbuffers = generateBuffer(doc,
"");
871 doc.AddMember(
"buffers", GLTFbuffers, doc.GetAllocator());
873 rapidjson::Value GLTFasset = generateAsset(doc);
874 doc.AddMember(
"asset", GLTFasset, doc.GetAllocator());
876 rapidjson::Value GLTFscenes = generateScenes(doc, entities);
877 doc.AddMember(
"scene", 0, doc.GetAllocator());
878 doc.AddMember(
"scenes", GLTFscenes, doc.GetAllocator());
880 rapidjson::Value GLTFnodes = generateNodes(doc, entities, Cogs::IO::combine(filePath, fileName),
"", dataBuffer);
881 doc.AddMember(
"nodes", GLTFnodes, doc.GetAllocator());
883 rapidjson::StringBuffer jsonStringBuffer;
884 rapidjson::Writer<rapidjson::StringBuffer> writer(jsonStringBuffer);
885 if (!doc.Accept(writer)) {
886 LOG_ERROR(logger,
"GLTF - JSON serialization failed");
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");
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;
905 uint32_t dataChunkSize =
static_cast<uint32_t
>(dataBuffer.buffer.size());
906 uint32_t dataChunkType = 0x004E4942;
907 char* dataChunkData = (
char*)dataBuffer.buffer.data();
910 uint32_t magic = 0x46546C67;
911 uint32_t version = 2;
912 uint32_t length = 28 + dataChunkSize + jsonChunkSize;
915 glbFile.write((
char*)&magic,
sizeof(uint32_t));
916 glbFile.write((
char*)&version,
sizeof(uint32_t));
917 glbFile.write((
char*)&length,
sizeof(uint32_t));
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);
925 while (jsonChunkPadding) {
926 char space =
static_cast<char>(0x20);
927 glbFile.write(&space,
sizeof(
char));
932 glbFile.write((
char*)&dataChunkSize,
sizeof(uint32_t));
933 glbFile.write((
char*)&dataChunkType,
sizeof(uint32_t));
934 glbFile.write(dataChunkData, dataChunkSize);
944bool Cogs::Core::GltfWriter::writeGltf(std::vector<ComponentModel::Entity*>& ,
const std::string& ,
const std::string& )
948bool Cogs::Core::GltfWriter::writeGlb(std::vector<ComponentModel::Entity*>& ,
const std::string& ,
const std::string& )
ComponentType * getComponent() const
Container for components, providing composition of dynamic entities.
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
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.
Log implementation class.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
@ Position
Position semantic.
@ TextureCoordinate
Texture coordinate semantic.
Contains a stream of data used by Mesh resources.
uint32_t numElements
Number of elements of the type given by format contained in data.
uint32_t offset
Byte offset from the start of the buffer.
uint32_t stride
Element stride.
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.
constexpr bool isMeshFlagSet(MeshFlags::EMeshFlags flag) const
Check if the given mesh flag(s) is set.
uint32_t getCount() const
Get the vertex count of the mesh.
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.
@ TriangleStrip
Triangle strip.
@ PointList
List of points.
@ TriangleList
List of triangles.