20#include "DracoMeshDecompressor.h"
21#include "MeshoptDecompressor.h"
23#include "ExtensionRegistry.h"
25#include "Services/Variables.h"
26#include "Platform/ResourceBufferBackedFileContents.h"
27#include "Resources/AnimationManager.h"
28#include "Resources/DefaultMaterial.h"
29#include "Resources/IModelLoader.h"
30#include "Resources/MaterialManager.h"
31#include "Resources/ResourceStore.h"
32#include "Resources/Skeleton.h"
33#include "Resources/Animation.h"
34#include "Foundation/HashSequence.h"
35#include "Foundation/Logging/Logger.h"
36#include "Foundation/Platform/IO.h"
37#include "Rendering/DataFormat.h"
39#if defined ( __clang__ )
40#pragma clang diagnostic push
41#pragma clang diagnostic ignored "-Wsign-compare"
46#if defined ( __clang__ )
47#pragma clang diagnostic pop
56 std::string getScheme(
const std::string& uri)
58 std::string scheme =
"file";
59 std::string::size_type spos = uri.find(
':');
61 if (spos != std::string::npos) {
62 std::string s = uri.substr(0, spos);
64 for (
const char &c : s) {
65 if (!(std::isupper(c) || std::islower(c))) {
83 va_start(argptr, format);
84 int n = std::vsnprintf(buffer,
sizeof(buffer), format, argptr);
86 if (0 < n && n <
static_cast<int>(
sizeof(buffer))) {
87 loadData.debugMessages[buffer] += 1;
92 void stridedCopy(uint8_t* dst,
size_t dstStride,
const uint8_t* src,
size_t srcStride,
size_t count)
94 for (
size_t i = 0; i < count; i++) {
95 for (
size_t k = 0; k < M; k++) {
96 dst[dstStride * i + k] = src[srcStride * i + k];
101 [[nodiscard]]
bool checkIsUint(
const GltfModelDefinition& loadData,
const char* name,
const Value& value)
103 if (!value.IsUint()) {
104 LOG_ERROR(logger,
"%.*s: %s is not an uint", StringViewFormat(loadData.path), name);
112 return checkIsUint(loadData, member.name.GetString(), member.value);
115 [[nodiscard]]
bool getUint(
const GltfModelDefinition& loadData, uint32_t& result,
const Member& member)
117 if (!checkIsUint(loadData, member.name.GetString(), member.value)) {
120 result = member.value.GetUint();
124 [[nodiscard]]
bool checkIsBool(
const GltfModelDefinition& loadData,
const char* name,
const Value& value)
126 if (!value.IsBool()) {
127 LOG_ERROR(logger,
"%.*s: %s is not a bool", StringViewFormat(loadData.path), name);
135 return checkIsBool(loadData, member.name.GetString(), member.value);
138 [[nodiscard]]
bool getBool(
const GltfModelDefinition& loadData,
bool& result,
const Member& member)
140 if (!checkIsBool(loadData, member.name.GetString(), member.value))
return false;
141 result = member.value.GetBool();
145 [[nodiscard]]
bool checkIsString(
const GltfModelDefinition& loadData,
const char* name,
const Value& value)
147 if (!value.IsString()) {
148 LOG_ERROR(logger,
"%.*s: %s is not a string", StringViewFormat(loadData.path), name);
154 [[nodiscard]]
bool checkIsString(
const GltfModelDefinition& loadData,
const Member& member)
156 return checkIsString(loadData, member.name.GetString(), member.value);
159 [[nodiscard]]
bool checkIsObject(
const GltfModelDefinition& loadData,
const char* name,
const Value& value)
161 if (!value.IsObject()) {
162 LOG_ERROR(logger,
"%.*s: %s is not an object", StringViewFormat(loadData.path), name);
168 [[nodiscard]]
bool checkIsObject(
const GltfModelDefinition& loadData,
const Member& member)
170 return checkIsObject(loadData, member.name.GetString(), member.value);
175 if (!member.value.IsArray()) {
176 LOG_ERROR(logger,
"%.*s: %s is not an array", StringViewFormat(loadData.path), member.name.GetString());
182 template<
typename Vec>
183 [[nodiscard]]
bool tryGetVecN(Vec& result,
const GltfModelDefinition& loadData,
const char* key,
const Object& parent)
185 size_t N = result.length();
187 if (
auto it = parent.FindMember(key); it != parent.MemberEnd()) {
188 if (!it->value.IsArray()) {
189 LOG_ERROR(logger,
"%.*s: %s not an array", StringViewFormat(loadData.path), it->name.GetString());
192 const Array& arr = it->value.GetArray();
193 if (arr.Size() != N) {
194 LOG_ERROR(logger,
"%.*s: %s not an array with length %zu", StringViewFormat(loadData.path), it->name.GetString(), N);
197 for (uint32_t i = 0; i < N; i++) {
198 if (!arr[i].IsNumber()) {
199 LOG_ERROR(logger,
"%.*s: %s array element %u is not a float", StringViewFormat(loadData.path), it->name.GetString(), i);
202 result[i] = arr[i].GetFloat();
208 [[nodiscard]]
bool tryGetBool(
bool& result,
const GltfModelDefinition& loadData,
const char* key,
const Object& parent)
210 if (
auto it = parent.FindMember(key); it != parent.MemberEnd()) {
211 if (!it->value.IsBool()) {
212 LOG_ERROR(logger,
"%.*s: %s not a bool", StringViewFormat(loadData.path), it->name.GetString());
215 result = it->value.GetBool();
222 if (
auto it = parent.FindMember(key); it != parent.MemberEnd()) {
223 if (!it->value.IsString()) {
224 LOG_ERROR(logger,
"%.*s: %s not a string", StringViewFormat(loadData.path), it->name.GetString());
227 result = toView(it->value);
232 [[nodiscard]]
bool tryGetFloat(
float& result,
const GltfModelDefinition& loadData,
const char* key,
const Object& parent)
234 if (
auto it = parent.FindMember(key); it != parent.MemberEnd()) {
235 if (!it->value.IsNumber()) {
236 LOG_ERROR(logger,
"%.*s: %s not a float", StringViewFormat(loadData.path), it->name.GetString());
239 result = it->value.GetFloat();
244 [[nodiscard]]
bool tryGetUint(uint32_t& result,
const GltfModelDefinition& loadData,
const char* key,
const Object& parent)
246 if (
auto it = parent.FindMember(key); it != parent.MemberEnd()) {
247 if (!it->value.IsUint()) {
248 LOG_ERROR(logger,
"%.*s: %s not an uint", StringViewFormat(loadData.path), it->name.GetString());
251 result = it->value.GetUint();
256 [[nodiscard]]
bool getString(std::string& result,
const Cogs::StringView& path,
const Member& member)
258 if (!member.value.IsString()) {
259 LOG_ERROR(logger,
"%.*s: %s s not a string", StringViewFormat(path), member.name.GetString());
262 result = std::string(member.value.GetString());
267 size_t pos = result.find(
"%20");
268 if (pos == std::string::npos) {
271 result.replace(pos, 3,
" ");
279 if (!assetValue.IsObject()) {
280 LOG_ERROR(logger,
"%.*s: JSON asset is not an object value", StringViewFormat(path));
284 for (
const auto& field : assetValue.GetObject()) {
285 if (field.name ==
"version") {
286 std::string v = field.value.GetString();
287 size_t pos = v.find(
'.');
288 std::string v_maj = v.substr(0, pos);
289 std::string v_min = v.substr(pos + 1, v.size() - pos);
290 int32_t major_version = atoi(v_maj.c_str());
291 int32_t minor_version = atoi(v_min.c_str());
292 loadData.version = major_version * 100 + minor_version;
294 else if (field.name ==
"minVersion") {
295 std::string v = field.value.GetString();
296 size_t pos = v.find(
'.');
297 std::string v_maj = v.substr(0, pos);
298 std::string v_min = v.substr(pos + 1, v.size() - pos);
299 int32_t major_version = atoi(v_maj.c_str());
300 int32_t minor_version = atoi(v_min.c_str());
301 loadData.min_version = major_version * 100 + minor_version;
303 else if (field.name ==
"generator") {
306 else if (field.name ==
"copyright") {
309 else if (field.name ==
"extras") {
312 else if(field.name ==
"extensions"){
315 else if(field.name ==
"extensionsRequired"){
319 LOG_WARNING_ONCE(logger,
"Unknown asset field: %s", field.name.GetString());
323 if (loadData.min_version == -1) loadData.min_version = loadData.version;
324 if (loadData.min_version >= 300) {
325 LOG_ERROR(logger,
"Could not load asset file %.*s. (Unsuported min version %d.)", StringViewFormat(path), loadData.min_version);
333 if (!buffersValue.IsArray()) {
334 LOG_ERROR(logger,
"%.*s: JSON buffers member is not an array value", StringViewFormat(path));
338 for (
const auto& bufferValue : buffersValue.GetArray()) {
342 for (
const auto& field : bufferValue.GetObject()) {
343 if (field.name ==
"uri") {
344 uri = field.value.GetString();
345 if (uri.find(
"%") != std::string::npos) {
346 LOG_WARNING_ONCE(logger,
"Unknown buffer field: %s", uri.c_str());
348 else if (uri.find(
"&") != std::string::npos) {
349 LOG_WARNING_ONCE(logger,
"Unknown buffer field: %s", uri.c_str());
353 else if (field.name ==
"byteLength") {
356 else if (field.name ==
"extensions") {
357 auto exts = field.value.GetObject();
358 for (
auto& ext : exts) {
359 if (std::string(ext.name.GetString()) ==
"EXT_meshopt_compression") {
366 LOG_WARNING_ONCE(logger,
"Unknown buffer field: %s", field.name.GetString());
370 std::string scheme = getScheme(uri);
373 loadData.buffer_data.push_back(glbData);
375 else if (scheme ==
"data") {
376 std::string::size_type spos = uri.find(
':');
377 std::string::size_type mpos = uri.find(
';');
378 std::string::size_type dpos = uri.find(
',');
379 std::string media_type = uri.substr(spos + 1, mpos - (spos + 1));
380 std::string base_type = uri.substr(mpos + 1, dpos - (mpos + 1));
381 std::string base64 = uri.substr(dpos + 1);
382 LOG_TRACE(logger,
"Loading data: media type \"%s\", base_type \"%s\"", media_type.c_str(), base_type.c_str());
383 std::string data = base64_decode(base64);
387 std::memcpy(buffer.data(), &data[0], data.size());
389 auto& stored = loadData.buffer_data_store.emplace_back(
ResourceBuffer{ std::make_shared<Cogs::Memory::MemoryBuffer>(std::move(buffer)) });
391 loadData.buffer_data.push_back(std::span<const uint8_t>(stored.data(), stored.data() + stored.size()));
393 else if (scheme ==
"file") {
394 std::string::size_type spos = uri.find(
':');
395 if (spos != std::string::npos) {
396 uri = Cogs::IO::combine(Cogs::IO::parentPath(path), uri.substr(spos + 1));
399 uri = Cogs::IO::combine(Cogs::IO::parentPath(path), uri);
401 auto& stored = loadData.buffer_data_store.emplace_back(context->
resourceStore->getResourceContents(uri));
402 if (stored.empty()) {
403 LOG_ERROR(logger,
"Failed or empty linked uri: %s files.", uri.c_str());
406 loadData.buffer_data.push_back(std::span<const uint8_t>(stored.data(), stored.data() + stored.size()));
409 LOG_ERROR(logger,
"%.*s: Loader cannot handle uri %s files.", StringViewFormat(path), uri.c_str());
418 if (!bufferViewsValue.IsArray()) {
419 LOG_ERROR(logger,
"%.*s: JSON bufferViews is not an array", StringViewFormat(path));
423 for (
const auto& bufferViewValue : bufferViewsValue.GetArray()) {
426 if (!bufferViewValue.IsObject()) {
427 LOG_ERROR(logger,
"%.*s: JSON bufferViews element is not an object", StringViewFormat(path));
431 for (
const auto& field : bufferViewValue.GetObject()) {
432 switch (toView(field.name).
hash()) {
434 if (!field.value.IsUint()) {
435 LOG_ERROR(logger,
"%.*s: JSON bufferView buffer is not an uint", StringViewFormat(path));
438 tmp.buffer = field.value.GetUint();
442 if (!field.value.IsUint()) {
443 LOG_ERROR(logger,
"%.*s: JSON bufferView byteLength is not an uint", StringViewFormat(path));
446 tmp.byteLength = field.value.GetUint();
450 if (!field.value.IsUint()) {
451 LOG_ERROR(logger,
"%.*s: JSON bufferView byteOffset is not an uint", StringViewFormat(path));
454 tmp.byteOffset = field.value.GetUint();
458 if (!field.value.IsUint()) {
459 LOG_ERROR(logger,
"%.*s: JSON bufferView target is not an uint", StringViewFormat(path));
462 tmp.target = field.value.GetUint();
466 if (!field.value.IsUint()) {
467 LOG_ERROR(logger,
"%.*s: JSON bufferView byteStride is not an uint", StringViewFormat(path));
470 tmp.byteStride = field.value.GetUint();
476 for (
const auto& ext : field.value.GetObject()) {
477 if (std::string(ext.name.GetString()) ==
"EXT_meshopt_compression") {
483 LOG_ERROR(logger,
"%.*s: JSON unknown bufferView field: %s", StringViewFormat(path), field.name.GetString());
494 const uint32_t img[2 * 2] = {
498 void* mem = malloc(
sizeof(img));
499 std::memcpy(mem, img,
sizeof(img));
500 gltfImage.decoded.data =
reinterpret_cast<stbi_uc*
>(mem);
501 gltfImage.decoded.width = 2;
502 gltfImage.decoded.height = 2;
503 gltfImage.decoded.comp = 4;
507 switch (mimeType.
hash()) {
510 stbi_set_flip_vertically_on_load_thread(0);
522 LOG_ERROR(logger,
"%.*s: Unsupported mimetype %.*s", StringViewFormat(path), StringViewFormat(mimeType));
523 gltfImage.decoded.failed =
true;
530 const uint32_t img[2 * 2] = {
534 void* mem = malloc(
sizeof(img));
535 std::memcpy(mem, img,
sizeof(img));
536 gltfImage.decoded.data =
reinterpret_cast<stbi_uc*
>(mem);
537 gltfImage.decoded.width = 2;
538 gltfImage.decoded.height = 2;
539 gltfImage.decoded.comp = 4;
543 switch (mimeType.
hash()) {
546 gltfImage.handle = context->textureManager->loadTextureFromMemory(data, size,
"unnamed.extension", NoResourceId, loadFlags);
556 LOG_ERROR(logger,
"%.*s: Unsupported mimetype %.*s", StringViewFormat(path), StringViewFormat(mimeType));
557 gltfImage.decoded.failed =
true;
563 if (!imagesValue.IsArray()) {
564 LOG_ERROR(logger,
"%.*s: Images is not an array", StringViewFormat(loadData.path));
568 Array images = imagesValue.GetArray();
569 loadData.images.resize(images.Size());
571 for(uint32_t i=0; i<images.Size(); i++) {
572 const Value& imageValue = images[i];
573 if (!imageValue.IsObject()) {
574 LOG_ERROR(logger,
"%.*s: Images array item is not an object", StringViewFormat(loadData.path));
577 auto imageObject = imageValue.GetObject();
581 if (
auto uriMember = imageObject.FindMember(
"uri"); uriMember != imageValue.MemberEnd()) {
582 if (!getString(image.uri, loadData.path, *uriMember)) {
587 if (imageUri.
substr(0, 5) ==
"data:") {
594 LOG_TRACE(logger,
"%.*s: Loading embedded data: media type \"%.*s\", base_type \"%.*s\"",
595 StringViewFormat(loadData.path), StringViewFormat(mediaType), StringViewFormat(baseType));
597 image.kind = GltfImage::LoadFromMemory;
598 image.decoded.task = loadData.context->
taskManager->enqueueChild(loadData.rootTask,
599 [&loadData, &image, mediaType, base64, context]() {
600 std::string data = base64_decode(base64.to_string());
601 decodeImageData(context, image, loadData.path, mediaType, data.c_str(), data.size());
605 image.kind = GltfImage::LoadFromUri;
609 else if (
auto bufferViewMember = imageObject.FindMember(
"bufferView"); bufferViewMember != imageValue.MemberEnd()) {
610 auto mimeTypeMember = imageObject.FindMember(
"mimeType");
611 if (mimeTypeMember == imageObject.MemberEnd()) {
612 LOG_ERROR(logger,
"%.*s: Images array item has an bufferView item but no mimeType member", StringViewFormat(loadData.path));
616 uint32_t bufferViewIndex = 0;
617 if (!getUint(loadData, bufferViewIndex, *bufferViewMember)) {
621 std::string mimeType;
622 if (!getString(mimeType, loadData.path, *mimeTypeMember)) {
626 image.kind = GltfImage::LoadFromMemory;
627 image.from_memory_data.bufferViewIndex = bufferViewIndex;
628 image.from_memory_data.mimeType = mimeType;
631 LOG_ERROR(logger,
"%.*s: Images array item has neither an uri nor a bufferView member", StringViewFormat(loadData.path));
641 if (loadData.images.size() <= imageIndex) {
642 LOG_ERROR(logger,
"%.*s: Illegal image index %zu", StringViewFormat(loadData.path), imageIndex);
646 GltfImage& image = loadData.images[imageIndex];
647 const size_t cacheIndex = (linearColor ? 2 : 0) + (mipmapped ? 1 : 0);
649 if (!image.cache[cacheIndex].handle) {
650 switch (image.kind) {
651 case GltfImage::Unset:
652 LOG_WARNING_ONCE(logger,
"%.*s: Image index %zu has no valid data", StringViewFormat(loadData.path), imageIndex);
653 image.cache[cacheIndex].handle = loadData.context->textureManager->white;
655 case GltfImage::LoadFromUri:
656 image.cache[cacheIndex].handle = loadData.context->textureManager->loadTexture(Cogs::IO::combine(Cogs::IO::parentPath(loadData.path), image.uri),
661 case GltfImage::LoadFromMemory:
662 assert(!image.cache[cacheIndex].proxy);
664 const GltfBufferView& bufferView = loadData.buffer_views[image.from_memory_data.bufferViewIndex];
665 std::span<const uint8_t> buffer = loadData.buffer_data[bufferView.buffer];
667 const uint8_t* ptr = &buffer[bufferView.byteOffset];
668 uint32_t size = bufferView.byteLength;
672 image.cache[cacheIndex].handle = image.handle;
675 assert(
false &&
"Invalid image kind");
679 return image.cache[cacheIndex].handle;
684 if (!samplersValue.IsArray()) {
685 LOG_ERROR(logger,
"%.*s: Samplers is not an array", StringViewFormat(path));
688 for (
auto& samplerValue : samplersValue.GetArray()) {
689 if (!samplerValue.IsObject()) {
690 LOG_ERROR(logger,
"%.*s: Sampler is not an object", StringViewFormat(path));
695 bool linearFilter =
true;
698 for (
const auto& field : samplerValue.GetObject()) {
699 switch (toView(field.name).
hash()) {
702 if (!getUint(loadData, magFilter, field)) {
706 case GLTF_NEAREST:
break;
707 case GLTF_LINEAR:
break;
709 LOG_ERROR(logger,
"%.*s: Unrecognized magFilter %u", StringViewFormat(path), magFilter);
717 if (!getUint(loadData, minFilter, field))
return false;
719 case GLTF_NEAREST: sampler.mipmapped =
false; linearFilter =
false;
break;
720 case GLTF_LINEAR: sampler.mipmapped =
false;
break;
721 case GLTF_NEAREST_MIPMAP_NEAREST: linearFilter =
false;
break;
722 case GLTF_LINEAR_MIPMAP_NEAREST:
break;
723 case GLTF_NEAREST_MIPMAP_LINEAR:
break;
724 case GLTF_LINEAR_MIPMAP_LINEAR:
break;
726 LOG_ERROR(logger,
"%.*s: Unrecognized minFilter %u", StringViewFormat(path), minFilter);
734 if (!getUint(loadData, wrapS, field))
return false;
740 LOG_ERROR(logger,
"%.*s: Unrecognized wrapS %u", StringViewFormat(path), wrapS);
748 if (!getUint(loadData, wrapT, field))
return false;
754 LOG_ERROR(logger,
"%.*s: Unrecognized wrapT %u", StringViewFormat(path), wrapT);
761 LOG_WARNING(logger,
"%.*s: Unrecognized sampler field %s", StringViewFormat(path), field.name.GetString());
772 if (!texturesValue.IsArray()) {
773 LOG_ERROR(logger,
"%.*s: Textures is not an array", StringViewFormat(path));
776 for (
auto& texture : texturesValue.GetArray()) {
777 auto& texture_value = loadData.textures.emplace_back(
GltfTexture{ -1, -1 });
779 for (
const auto& field : texture.GetObject()) {
780 if (field.name ==
"sampler") {
781 texture_value.sampler = field.value.GetUint();
782 if (loadData.texture_samplers.size() <= (
size_t)texture_value.sampler) {
783 LOG_ERROR(logger,
"Illegal texture sampler index %u", texture_value.sampler);
787 else if (field.name ==
"source") {
788 texture_value.source = field.value.GetUint();
789 if (loadData.images.size() <= uint32_t(texture_value.source)) {
790 LOG_ERROR(logger,
"Illegal texture source index %u", texture_value.source);
794 else if (field.name ==
"extensions") {
795 auto ext = field.value.GetObject();
796 if (ext.HasMember(
"KHR_texture_basisu")) {
797 auto data = ext[
"KHR_texture_basisu"].GetObject();
798 int altSource = data[
"source"].GetInt();
799 if (!texture.HasMember(
"source")) {
800 texture_value.source = altSource;
805 LOG_WARNING_ONCE(logger,
"Unknown texture field: %s", field.name.GetString());
817 const Object& parent,
818 const char* textureName,
821 const char* extraKey =
nullptr,
822 float* extraValue =
nullptr)
824 if (
auto texInfoIt = parent.FindMember(key); texInfoIt != parent.MemberEnd()) {
825 if (!checkIsObject(loadData, *texInfoIt)) {
828 const Object& texInfoObject = texInfoIt->value.GetObject();
830 if (
auto indexIt = texInfoObject.FindMember(
"index"); indexIt != texInfoObject.MemberEnd()) {
832 if (!getUint(loadData, index, *indexIt)) {
833 LOG_ERROR(logger,
"No 'index' uint element in texInfoObject");
837 uint32_t texcoord = 0;
838 if (!tryGetUint(texcoord, loadData,
"texCoord", texInfoObject)) {
839 LOG_ERROR(logger,
"No 'texCoord' uint element in texInfoObject");
842 cachedModelMaterial.texCoordSets = std::max(cachedModelMaterial.texCoordSets, texcoord + 1);
844 if (extraKey && !tryGetFloat(*extraValue, loadData, extraKey, texInfoObject)) {
845 LOG_ERROR(logger,
"No '%s' float element in texInfoObject", extraKey);
849 if (loadData.textures.size() <= index) {
850 LOG_ERROR(logger,
"%.*s: %s has illegal texture index %u", StringViewFormat(loadData.path), key, index);
854 const GltfTexture& textureDefinition = loadData.textures[index];
855 if (loadData.images.size() <= (
size_t)textureDefinition.source) {
856 LOG_ERROR(logger,
"%.*s: %s has illegal texture index source %u", StringViewFormat(loadData.path), key, textureDefinition.source);
861 if (textureDefinition.sampler != -1) {
862 if (loadData.texture_samplers.size() <= (
size_t)textureDefinition.sampler) {
863 LOG_ERROR(logger,
"%.*s: %s has illegal sampler index %u", StringViewFormat(loadData.path), key, textureDefinition.sampler);
866 sampler = loadData.texture_samplers[textureDefinition.sampler];
869 linearColor, sampler.mipmapped);
871 const std::string texmapName = std::string(textureName) +
"Map";
872 if (instance->
material->getVariantIndex(texmapName) != Material::NoVariantIndex) {
873 instance->setVariant(texmapName,
true);
876 const std::string texUVChannelName = std::string(textureName) +
"UVChannel";
877 if (instance->
material->getVariantIndex(texUVChannelName) != Material::NoVariantIndex) {
878 instance->setVariant(texUVChannelName,
int(texcoord));
881 const std::string texCoordName = std::string(
"TexCoord") + std::to_string(texcoord);
882 if (instance->
material->getVariantIndex(texCoordName) != Material::NoVariantIndex) {
883 instance->setVariant(texCoordName,
true);
886 std::string texKeyName = std::string(textureName) +
"Map";
887 texKeyName[0] = char(std::tolower(texKeyName[0]));
889 const VariableKey texKey = material->getTextureKey(texKeyName);
897 if (
auto extIt = texInfoObject.FindMember(
"extensions"); extIt != texInfoObject.MemberEnd()) {
898 const Object& extObject = extIt->value.GetObject();
899 if (
auto texTrIt = extObject.FindMember(
"KHR_texture_transform"); texTrIt != extObject.MemberEnd()) {
900 const Object& texTrObject = texTrIt->value.GetObject();
902 if (texTrObject.HasMember(
"offset")) {
903 if (!texTrObject[
"offset"].IsArray() || texTrObject[
"offset"].Size() != 2) {
904 LOG_ERROR(logger,
"The 'KHR_texture_transform' member 'offset' is not an array of size 2");
return false;
907 textureTransform.offset = glm::vec2(texTrObject[
"offset"][0].GetFloat(), texTrObject[
"offset"][1].GetFloat());
909 if (texTrObject.HasMember(
"scale")) {
910 if (!texTrObject[
"scale"].IsArray() || texTrObject[
"scale"].Size() != 2) {
911 LOG_ERROR(logger,
"The 'KHR_texture_transform' member 'scale' is not an array of size 2");
return false;
914 textureTransform.scale = glm::vec2(texTrObject[
"scale"][0].GetFloat(), texTrObject[
"scale"][1].GetFloat());
916 if (texTrObject.HasMember(
"rotation")) {
917 textureTransform.rotation = texTrObject[
"rotation"].GetFloat();
920 loadData.textureTransforms.push_back(textureTransform);
929 LOG_ERROR(logger,
"%.*s: %s is missing required index property", StringViewFormat(loadData.path), texInfoIt->name.GetString());
943 glm::vec4 baseColorFactor(1.f, 1.f, 1.f, 1.f);
944 float metallicFactor = 1.f;
945 float roughnessFactor = 1.f;
947 if (
auto pbrMetallicValue = materialObject.FindMember(
"pbrMetallicRoughness"); pbrMetallicValue != materialObject.MemberEnd()) {
948 if (!checkIsObject(loadData, *pbrMetallicValue))
return false;
949 const Object& pbrMetallicObject = pbrMetallicValue->value.GetObject();
951 if (!tryGetVecN(baseColorFactor, loadData,
"baseColorFactor", pbrMetallicObject))
return false;
952 if (!tryGetFloat(metallicFactor, loadData,
"metallicFactor", pbrMetallicObject))
return false;
953 if (!tryGetFloat(roughnessFactor, loadData,
"roughnessFactor", pbrMetallicObject))
return false;
955 if (!tryHandleTextureInfo(material, instance, loadData, cachedMaterial,
"baseColorTexture", pbrMetallicObject,
"Albedo",
false,
nullptr))
return false;
956 if (!tryHandleTextureInfo(material, instance, loadData, cachedMaterial,
"metallicRoughnessTexture", pbrMetallicObject,
"MetallicRoughness",
true,
nullptr))
return false;
960 for (
int i = 0; i < 3; i++) {
961 baseColorFactor[i] = std::pow(std::abs(baseColorFactor[i]), 1.0f / 2.2f);
964 instance->
setVec4Property(material->getVec4Key(
"albedo"), baseColorFactor);
965 instance->
setFloatProperty(material->getFloatKey(
"metallic"), metallicFactor);
966 instance->
setFloatProperty(material->getFloatKey(
"roughness"), roughnessFactor);
974 if (!tryGetString(name, loadData,
"name", materialObject))
return false;
980 if (!tryGetString(alphaMode, loadData,
"alphaMode", materialObject))
return false;
981 switch (alphaMode.
hash()) {
985 instance->setVariant(
"AlphaTest",
"Discard");
991 LOG_ERROR(logger,
"%.*s: Unknown alphaMode '%.*s'", StringViewFormat(loadData.path), StringViewFormat(alphaMode));
995 float alphaCutoff = 0.5f;
996 if (!tryGetFloat(alphaCutoff, loadData,
"alphaCutoff", materialObject))
return false;
997 instance->
setFloatProperty(material->getFloatKey(
"alphaThreshold"), alphaCutoff);
999 bool doubleSided =
false;
1000 if (!tryGetBool(doubleSided, loadData,
"doubleSided", materialObject))
return false;
1003 float normalMapFactor = 1.0f;
1004 if (!tryHandleTextureInfo(material, instance, loadData, cachedMaterial,
"normalTexture", materialObject,
"Normal",
true, &cachedMaterial.needsTangents,
"scale", &normalMapFactor))
return false;
1005 instance->
setFloatProperty(material->getFloatKey(
"normalMapFactor"), normalMapFactor);
1007 float occlusionFactor = 1.0f;
1008 if (!tryGetFloat(occlusionFactor, loadData,
"occlusionFactor", materialObject))
return false;
1009 if (!tryHandleTextureInfo(material, instance, loadData, cachedMaterial,
"occlusionTexture", materialObject,
"Occlusion",
true,
nullptr,
"strength", &occlusionFactor))
return false;
1010 instance->
setFloatProperty(material->getFloatKey(
"occlusion"), occlusionFactor);
1012 glm::vec3 emissiveFactor = glm::vec3(0.0f, 0.0f, 0.0f);
1013 if (!tryGetVecN(emissiveFactor, loadData,
"emissiveFactor", materialObject))
return false;
1014 if (!tryHandleTextureInfo(material, instance, loadData, cachedMaterial,
"emissiveTexture", materialObject,
"Emissive",
false,
nullptr))
return false;
1015 instance->
setVec3Property(material->getVec3Key(
"emissive"), emissiveFactor);
1022 glm::vec4 diffuseFactor = glm::vec4(1.f, 1.f, 1.f, 1.f);
1023 if (!tryGetVecN(diffuseFactor, loadData,
"diffuseFactor", specGlossObject))
return false;
1025 for (
int i = 0; i < 3; i++) {
1026 diffuseFactor[i] = std::pow(std::abs(diffuseFactor[i]), 1.0f / 2.2f);
1028 instance->
setVec4Property(material->getVec4Key(
"albedo"), diffuseFactor);
1030 if (!tryHandleTextureInfo(material, instance, loadData, cachedMaterial,
"diffuseTexture", specGlossObject,
"Albedo",
false,
nullptr))
return false;
1032 glm::vec3 specularFactor = glm::vec3(1.f, 1.f, 1.f);
1033 if (!tryGetVecN(specularFactor, loadData,
"specularFactor", specGlossObject))
return false;
1034 instance->
setVec3Property(material->getVec3Key(
"specular"), specularFactor);
1036 float glossinessFactor = 1.f;
1037 if (!tryGetFloat(glossinessFactor, loadData,
"glossinessFactor", specGlossObject))
return false;
1038 instance->
setFloatProperty(material->getFloatKey(
"gloss"), glossinessFactor);
1040 if (!tryHandleTextureInfo(material, instance, loadData, cachedMaterial,
"specularGlossinessTexture", specGlossObject,
"SpecularGloss",
false,
nullptr))
return false;
1046 float specularFactor = 1.f;
1047 if (!tryGetFloat(specularFactor, loadData,
"specularFactor", specularObject))
return false;
1048 instance->
setFloatProperty(material->getFloatKey(
"specularFactor"), specularFactor);
1050 glm::vec3 specularColorFactor = glm::vec3(1.f, 1.f, 1.f);
1051 if (!tryGetVecN(specularColorFactor, loadData,
"specularColorFactor", specularObject))
return false;
1052 instance->
setVec3Property(material->getVec3Key(
"specularColor"), specularColorFactor);
1054 if (!tryHandleTextureInfo(material, instance, loadData, cachedMaterial,
"specularTexture", specularObject,
"SpecularFactor",
false,
nullptr))
return false;
1056 if (!tryHandleTextureInfo(material, instance, loadData, cachedMaterial,
"specularColorTexture", specularObject,
"SpecularColor",
false,
nullptr))
return false;
1063 float idxOfRefraction = 1.5f;
1064 if (!tryGetFloat(idxOfRefraction, loadData,
"ior", iorObject))
return false;
1066 instance->setVariant(
"Ior",
true);
1073 if (
auto extensionsIt = materialObject.FindMember(
"extensions"); extensionsIt != materialObject.MemberEnd()) {
1074 if (!checkIsObject(loadData, *extensionsIt)) {
1078 const Object& extensionsObject = extensionsIt->value.GetObject();
1080 if (
auto specGlossIt = extensionsObject.FindMember(
"KHR_materials_pbrSpecularGlossiness"); specGlossIt != extensionsObject.MemberEnd()) {
1081 if (!checkIsObject(loadData, *specGlossIt)) {
1084 const Object& specGlossObject = specGlossIt->value.GetObject();
1086 if (!handleMaterialPbrSpecularGlossiness(loadData, cachedMaterial, material, instance, specGlossObject)) {
1094 if (
auto specularIt = extensionsObject.FindMember(
"KHR_materials_specular"); specularIt != extensionsObject.MemberEnd()) {
1095 if (!checkIsObject(loadData, *specularIt)) {
1098 const Object& specObject = specularIt->value.GetObject();
1099 if (!handleMaterialSpecular(loadData, cachedMaterial, material, instance, specObject)) {
1104 if (
auto iorIt = extensionsObject.FindMember(
"KHR_materials_ior"); iorIt != extensionsObject.MemberEnd()) {
1105 if (!checkIsObject(loadData, *iorIt)) {
1108 const Object& iorObject = iorIt->value.GetObject();
1109 if (!handleMaterialIdxOfRefraction(loadData, cachedMaterial, material, instance, iorObject)) {
1120 const int32_t gltfMaterialIx,
1121 const bool isSkinned,
1122 const bool albedoPerVertex,
1123 const bool hasBitangents)
1125 if (loadData.materialsArray.size() <
size_t(gltfMaterialIx + 1)) {
1126 LOG_ERROR(logger,
"%.*s: Illegal gltf material index %d", StringViewFormat(loadData.path), gltfMaterialIx);
1129 if (loadData.modelMaterialsCache.empty()) {
1130 loadData.modelMaterialsCache.resize(8 * (loadData.materialsArray.size() + 1));
1134 size_t cacheIx = 8 * size_t(gltfMaterialIx + 1) + (isSkinned ? 4 : 0) + (albedoPerVertex ? 2 : 0) + (hasBitangents ? 1 : 0);
1135 assert(cacheIx < loadData.modelMaterialsCache.size());
1137 cachedMaterial_ = &cachedMaterial;
1140 if (cachedMaterial.modelMaterialIx != -1) {
1147 if (gltfMaterialIx >= 0) {
1148 const Object& materialObject = loadData.materialsArray[gltfMaterialIx];
1151 if (materialObject.HasMember(
"extensions") && materialObject[
"extensions"].HasMember(
"KHR_materials_unlit")) {
1152 MaterialHandle material = loadData.context->materialManager->getMaterial(
"DefaultMaterial");
1153 materialInstance = loadData.context->materialInstanceManager->createMaterialInstance(material);
1154 materialInstance->setPermutation(
"Default");
1155 materialInstance->setVariant(
"LightModel",
"BaseColor");
1156 materialInstance->setVariant(
"EnableLighting",
false);
1157 materialInstance->setVariant(
"NoTonemap",
true);
1159 if (
auto pbrMetallicValue = materialObject.FindMember(
"pbrMetallicRoughness"); pbrMetallicValue != materialObject.MemberEnd()) {
1160 if (!checkIsObject(loadData, *pbrMetallicValue)) {
1161 LOG_ERROR(logger,
"The 'KHR_materials_unlit' material does not have a 'pbrMetallicRoughness' section.");
1164 const Object& pbrMetallicObject = pbrMetallicValue->value.GetObject();
1165 glm::vec4 baseColorFactor(1.0, 1.0, 1.0, 1.0);
1166 if (!tryGetVecN(baseColorFactor, loadData,
"baseColorFactor", pbrMetallicObject)) {
1167 LOG_ERROR(logger,
"The 'pbrMetallicRoughness' section does not have a 'baseColorFactor' value.");
1170 materialInstance->
setVec4Property(DefaultMaterial::DiffuseColor, baseColorFactor);
1172 if (!tryHandleTextureInfo(material, materialInstance, loadData, cachedMaterial,
"baseColorTexture", pbrMetallicObject,
"Diffuse",
false,
nullptr)) {
1174 LOG_ERROR(logger,
"The 'pbrMetallicRoughness' section does not have a 'baseColorTexture' value.");
1178 if (cachedMaterial.texCoordSets > 1) {
1179 LOG_WARNING_ONCE(logger,
"Unlit materials with more than one texture coordinate set not yet supported.");
1182 if (cachedMaterial.texCoordSets > 0) {
1183 materialInstance->setVariant(
"DefaultHasTexCoord",
true);
1184 materialInstance->setVariant(
"Textured",
true);
1189 cachedMaterial_->needsNormals =
false;
1192 MaterialHandle material = loadData.context->materialManager->getMaterial(
"StandardMaterial");
1193 materialInstance = loadData.context->materialInstanceManager->createMaterialInstance(material);
1196 materialInstance->setPermutation(
"Metallic");
1197 if (
auto extensionsIt = materialObject.FindMember(
"extensions"); extensionsIt != materialObject.MemberEnd()) {
1198 if (!checkIsObject(loadData, *extensionsIt))
return false;
1199 const Object& extensionsObject = extensionsIt->value.GetObject();
1200 if (
auto specGlossIt = extensionsObject.FindMember(
"KHR_materials_pbrSpecularGlossiness"); specGlossIt != extensionsObject.MemberEnd()) {
1201 materialInstance->setPermutation(
"Specular");
1205 if (
auto extensionsIt = materialObject.FindMember(
"extensions"); extensionsIt != materialObject.MemberEnd()) {
1206 if (!checkIsObject(loadData, *extensionsIt))
return false;
1207 const Object& extensionsObject = extensionsIt->value.GetObject();
1208 if (
auto iorIt = extensionsObject.FindMember(
"KHR_materials_ior"); iorIt != extensionsObject.MemberEnd()) {
1209 materialInstance->setVariant(
"Ior",
true);
1211 if (
auto specularIt = extensionsObject.FindMember(
"KHR_materials_specular"); specularIt != extensionsObject.MemberEnd()) {
1212 materialInstance->setVariant(
"Specular",
true);
1216 materialInstance->setVariant(
"LightModel",
"PBR");
1217 if (isSkinned) materialInstance->setVariant(
"Skinned",
true);
1218 if (albedoPerVertex) materialInstance->setVariant(
"AlbedoPerVertex",
true);
1219 if (hasBitangents) materialInstance->setVariant(
"Bitangents",
true);
1221 if (!handleMaterialPbrMetallicRoughness(loadData, cachedMaterial, material, materialInstance, materialObject))
return false;
1222 if (!handleMaterialGenericFields(loadData, cachedMaterial, material, materialInstance, materialObject))
return false;
1223 if (!handleMaterialExtensions(loadData, cachedMaterial, material, materialInstance, materialObject))
return false;
1225 cachedMaterial_->needsNormals =
true;
1230 MaterialHandle material = loadData.context->materialManager->getMaterial(
"StandardMaterial");
1231 materialInstance = loadData.context->materialInstanceManager->createMaterialInstance(material);
1233 materialInstance->setPermutation(
"Metallic");
1234 if (isSkinned) materialInstance->setVariant(
"Skinned",
true);
1235 if (albedoPerVertex) materialInstance->setVariant(
"AlbedoPerVertex",
true);
1236 if (hasBitangents) materialInstance->setVariant(
"Bitangents",
true);
1237 materialInstance->
setName(
"GLTF default material");
1238 materialInstance->
setOption(
"CullMode",
"Back");
1240 materialInstance->
setVec4Property(material->getVec4Key(
"albedo"), glm::vec4(1.0, 1.0, 1.0, 1.0));
1241 materialInstance->
setFloatProperty(material->getFloatKey(
"metallic"), 1.0f);
1242 materialInstance->
setFloatProperty(material->getFloatKey(
"roughness"), 1.0f);
1243 materialInstance->
setVec3Property(material->getVec3Key(
"emissive"), glm::vec3(0.0, 0.0, 0.0));
1244 materialInstance->
setFloatProperty(material->getFloatKey(
"alphaThreshold"), 0.5f);
1246 cachedMaterial_->needsNormals =
true;
1249 assert(materialInstance &&
"Internal error");
1251 cachedMaterial.modelMaterialIx = int32_t(model.materials.size());
1252 model.materials.push_back(materialInstance);
1258 if (!scenesValue.IsArray()) {
1259 LOG_ERROR(logger,
"%.*s: JSON scenes is not an array value", StringViewFormat(path));
1263 for (
const auto& sceneValue : scenesValue.GetArray()) {
1264 if (!sceneValue.IsObject()) {
1265 LOG_ERROR(logger,
"%.*s: JSON scenes item is not an object", StringViewFormat(path));
1269 GltfScene& scene = loadData.scenes.emplace_back();
1270 for (
const auto& field : sceneValue.GetObject()) {
1271 switch (toView(field.name).
hash()) {
1273 if (!field.value.IsString()) {
1274 LOG_ERROR(logger,
"%.*s: JSON scene name value is not a string", StringViewFormat(path));
1277 scene.name = field.value.GetString();
1282 if (!field.value.IsArray()) {
1283 LOG_ERROR(logger,
"%.*s: JSON scene nodes value is not an array", StringViewFormat(path));
1286 for (
const auto& node : field.value.GetArray()) {
1287 if (!node.IsUint()) {
1288 LOG_ERROR(logger,
"%.*s: JSON scene nodes array item is not an uint", StringViewFormat(path));
1291 scene.nodes.push_back(node.GetUint());
1297 if (!field.value.IsObject()) {
1298 LOG_ERROR(logger,
"%.*s: JSON scene extensions item is not an object", StringViewFormat(path));
1311 auto type = field.value.GetType();
1313 LOG_WARNING_ONCE(logger,
"Unhandled application-specific node under scenes node: Extras[rapidjson::Type: %u]", type );
1317 LOG_WARNING_ONCE(logger,
"%.*s: JSON unknown scene field: %s", StringViewFormat(path), field.name.GetString());
1327 if (!nodesValue.IsArray()) {
1328 LOG_ERROR(logger,
"%.*s: JSON nodes member is not an array value", StringViewFormat(path));
1332 const auto nodeValues = nodesValue.GetArray();
1333 const uint32_t numNodes = nodeValues.Size();
1334 loadData.nodes.resize(numNodes);
1336 for (uint32_t i = 0; i < numNodes; ++i) {
1337 GltfNode& node = loadData.nodes[i];
1338 node.node_index = i;
1340 bool use_matrix_transform =
false;
1342 const Value& nodeValue = nodeValues[i];
1343 if (!nodeValue.IsObject()) {
1344 LOG_ERROR(logger,
"%.*s: JSON nodes array item not an object", StringViewFormat(path));
1348 for (
const auto& field : nodeValue.GetObject()) {
1349 switch (toView(field.name).
hash()) {
1351 if (!field.value.IsString()) {
1352 LOG_ERROR(logger,
"%.*s: JSON node name is not a string", StringViewFormat(path));
1355 node.name = field.value.GetString();
1359 if (!field.value.IsArray()) {
1360 LOG_ERROR(logger,
"%.*s: JSON node children is not an array", StringViewFormat(path));
1363 for (
const auto& child : field.value.GetArray()) {
1364 if (!child.IsUint()) {
1365 LOG_ERROR(logger,
"%.*s: JSON children array element is not an uint", StringViewFormat(path));
1368 uint32_t childIndex = child.GetUint();
1369 node.children.push_back(childIndex);
1370 loadData.nodes[childIndex].parent_node = i;
1375 if (field.value.IsArray() && field.value.GetArray().Size() == 4) {
1376 auto arr = field.value.GetArray();
1377 node.rot = glm::quat(arr[3].GetFloat(), arr[0].GetFloat(), arr[1].GetFloat(), arr[2].GetFloat());
1380 LOG_ERROR(logger,
"%.*s: JSON node rotation value is not an array with 4 elements", StringViewFormat(path));
1386 if (field.value.IsArray() && field.value.GetArray().Size() == 3) {
1387 auto arr = field.value.GetArray();
1388 node.pos = glm::vec3(arr[0].GetFloat(), arr[1].GetFloat(), arr[2].GetFloat());
1391 LOG_ERROR(logger,
"%.*s: JSON node translation value is not an array with 3 elements", StringViewFormat(path));
1397 if (field.value.IsArray() && field.value.GetArray().Size() == 3) {
1398 auto arr = field.value.GetArray();
1399 node.scale = glm::vec3(arr[0].GetFloat(), arr[1].GetFloat(), arr[2].GetFloat());
1402 LOG_ERROR(logger,
"%.*s: JSON node scale value is not an array with 3 elements", StringViewFormat(path));
1408 if (field.value.IsArray() && field.value.GetArray().Size() == 16) {
1409 auto arr = field.value.GetArray();
1410 node.mat = glm::mat4(arr[0].GetFloat(), arr[1].GetFloat(), arr[2].GetFloat(), arr[3].GetFloat(),
1411 arr[4].GetFloat(), arr[5].GetFloat(), arr[6].GetFloat(), arr[7].GetFloat(),
1412 arr[8].GetFloat(), arr[9].GetFloat(), arr[10].GetFloat(), arr[11].GetFloat(),
1413 arr[12].GetFloat(), arr[13].GetFloat(), arr[14].GetFloat(), arr[15].GetFloat());
1415 glm::vec4 perspective;
1416 glm::decompose(node.mat, node.scale, node.rot, node.pos, skew, perspective);
1417 use_matrix_transform =
true;
1420 LOG_ERROR(logger,
"%.*s: JSON node matrix value is not an array with 16 elements", StringViewFormat(path));
1426 if (field.value.IsUint()) {
1427 node.mesh = field.value.GetUint();
1430 LOG_ERROR(logger,
"%.*s: JSON node mesh value is not an uint", StringViewFormat(path));
1436 if (field.value.IsUint()) {
1437 node.skin = field.value.GetUint();
1440 LOG_ERROR(logger,
"%.*s: JSON node skin value is not an uint", StringViewFormat(path));
1449 LOG_DEBUG(logger,
"Unknown node field: %s", field.name.GetString());
1454 if (!use_matrix_transform) {
1456 node.mat = glm::translate(ii, node.pos) * glm::mat4_cast(node.rot) * glm::scale(ii, node.scale);
1466 void parseAccessorMinArray(
GltfAccessor & accessor, Array arr)
1468 accessor.minCount = std::min(16u, arr.Size());
1469 for (uint32_t i = 0; i < accessor.minCount; i++) {
1470 if (arr[i].IsUint() || arr[i].IsInt()) {
1471 if (accessor.componentType == AccessorComponentType::Float) {
1472 accessor.min.s_float[i] = float(arr[i].GetInt());
1475 if (accessor.componentType == AccessorComponentType::UnsignedByte ||
1476 accessor.componentType == AccessorComponentType::UnsignedShort ||
1477 accessor.componentType == AccessorComponentType::UnsignedInt) {
1478 accessor.min.s_uint[i] = arr[i].GetUint();
1481 accessor.min.s_int[i] = arr[i].GetInt();
1486 if (accessor.componentType == AccessorComponentType::Float) {
1487 accessor.min.s_float[i] = arr[i].GetFloat();
1490 if (accessor.componentType == AccessorComponentType::UnsignedByte ||
1491 accessor.componentType == AccessorComponentType::UnsignedShort ||
1492 accessor.componentType == AccessorComponentType::UnsignedInt) {
1493 accessor.min.s_uint[i] = uint32_t(std::floor(arr[i].GetFloat()));
1496 accessor.min.s_int[i] = int32_t(std::floor(arr[i].GetFloat()));
1504 void parseAccessorMaxArray(
GltfAccessor& accessor, Array arr)
1506 accessor.maxCount = std::min(16u, arr.Size());
1507 for (uint32_t i = 0; i < accessor.maxCount; i++) {
1508 if (arr[i].IsInt() || arr[i].IsUint()) {
1509 if (accessor.componentType == AccessorComponentType::Float) {
1510 accessor.max.s_float[i] = float(arr[i].GetInt());
1513 if (accessor.componentType == AccessorComponentType::UnsignedByte ||
1514 accessor.componentType == AccessorComponentType::UnsignedShort ||
1515 accessor.componentType == AccessorComponentType::UnsignedInt) {
1516 accessor.max.s_uint[i] = arr[i].GetUint();
1519 accessor.max.s_int[i] = arr[i].GetInt();
1524 if (accessor.componentType == AccessorComponentType::Float) {
1525 accessor.max.s_float[i] = arr[i].GetFloat();
1528 if (accessor.componentType == AccessorComponentType::UnsignedByte ||
1529 accessor.componentType == AccessorComponentType::UnsignedShort ||
1530 accessor.componentType == AccessorComponentType::UnsignedInt) {
1531 accessor.max.s_uint[i] = uint32_t(std::ceil(arr[i].GetFloat()));
1534 accessor.max.s_int[i] = int32_t(std::ceil(arr[i].GetFloat()));
1543 assert(accessorsValue.IsArray());
1544 for (
const auto& accessorValue : accessorsValue.GetArray()) {
1545 if (!checkIsObject(loadData,
"accessor array item", accessorValue))
return false;
1547 GltfAccessor& accessor = loadData.accessors.emplace_back();
1548 for (
const auto& field : accessorValue.GetObject()) {
1549 switch (toView(field.name).
hash()) {
1551 if (!getUint(loadData, accessor.bufferView, field))
return false;
1555 if (!getUint(loadData, accessor.byteOffset, field))
return false;
1559 if (checkIsUint(loadData, field)) {
1560 switch (field.value.GetUint()) {
1561 case GLTF_BYTE: accessor.componentType = AccessorComponentType::Byte;
break;
1562 case GLTF_UNSIGNED_BYTE: accessor.componentType = AccessorComponentType::UnsignedByte;
break;
1563 case GLTF_SHORT: accessor.componentType = AccessorComponentType::Short;
break;
1564 case GLTF_UNSIGNED_SHORT: accessor.componentType = AccessorComponentType::UnsignedShort;
break;
1565 case GLTF_UNSIGNED_INT: accessor.componentType = AccessorComponentType::UnsignedInt;
break;
1566 case GLTF_FLOAT: accessor.componentType = AccessorComponentType::Float;
break;
1568 LOG_ERROR(logger,
"%.*s:n Unrecognized accessor componentType %u", StringViewFormat(path), field.value.GetUint());
1576 if (!getUint(loadData, accessor.count, field))
return false;
1580 if (checkIsString(loadData, field)) {
1581 switch (toView(field.value).
hash()) {
1582 case Cogs::hash(
"SCALAR"): accessor.type = AccessorType::Scalar;
break;
1583 case Cogs::hash(
"VEC2"): accessor.type = AccessorType::Vec2;
break;
1584 case Cogs::hash(
"VEC3"): accessor.type = AccessorType::Vec3;
break;
1585 case Cogs::hash(
"VEC4"): accessor.type = AccessorType::Vec4;
break;
1586 case Cogs::hash(
"MAT2"): accessor.type = AccessorType::Mat2;
break;
1587 case Cogs::hash(
"MAT3"): accessor.type = AccessorType::Mat3;
break;
1588 case Cogs::hash(
"MAT4"): accessor.type = AccessorType::Mat4;
break;
1590 LOG_ERROR(logger,
"%.*s: Unrecognized accessor type '%s'", StringViewFormat(path), field.value.GetString());
1598 if (checkIsArray(loadData, field)) {
1599 assert(field.value.IsArray());
1600 parseAccessorMinArray(accessor, field.value.GetArray());
1606 if (checkIsArray(loadData, field)) {
1607 assert(field.value.IsArray());
1608 parseAccessorMaxArray(accessor, field.value.GetArray());
1616 if (!getBool(loadData, accessor.normalized, field))
return false;
1624 LOG_WARNING_ONCE(logger,
"%.*s: Unknown accessor key '%s'", StringViewFormat(path), field.name.GetString());
1634 if (!skinsValue.IsArray()) {
1635 LOG_ERROR(logger,
"%.*s: Skins is not an array", StringViewFormat(path));
1638 for (
auto& skin_value : skinsValue.GetArray()) {
1639 auto& skin = loadData.skins.emplace_back();
1641 for (
const auto& field : skin_value.GetObject()) {
1642 if (field.name ==
"inverseBindMatrices")
1643 skin.inverseBindMatrices = field.value.GetUint();
1644 else if (field.name ==
"joints") {
1645 for (
auto& joint : field.value.GetArray()) skin.joints.push_back(joint.GetUint());
1647 else if (field.name ==
"skeleton")
1648 skin.skeleton = field.value.GetUint();
1649 else if (field.name ==
"name")
1650 skin.name = field.value.GetString();
1652 LOG_WARNING_ONCE(logger,
"Unknown skin field: %s", field.name.GetString());
1661 if (!animationsValue.IsArray()) {
1662 LOG_ERROR(logger,
"%.*s: Animations is not an array", StringViewFormat(path));
1665 for (
auto& animation_value : animationsValue.GetArray()) {
1666 auto& animation = loadData.animations.emplace_back();
1668 for (
const auto& field : animation_value.GetObject()) {
1669 if (field.name ==
"channels") {
1670 for (
auto& channel_value : field.value.GetArray()) {
1671 auto& channel = animation.channels.emplace_back();
1673 for (
auto& channel_field : channel_value.GetObject()) {
1674 if (channel_field.name ==
"sampler")
1675 channel.sampler = channel_field.value.GetUint();
1676 else if (channel_field.name ==
"target") {
1677 for (
auto& target : channel_field.value.GetObject()) {
1678 if (target.name ==
"node")
1679 channel.node = target.value.GetUint();
1680 else if (target.name ==
"path")
1681 channel.path = target.value.GetString();
1683 LOG_WARNING_ONCE(logger,
"Unknown animation channel target field: %s", target.name.GetString());
1688 LOG_WARNING_ONCE(logger,
"Unknown animation channel field: %s", channel_field.name.GetString());
1693 else if (field.name ==
"samplers") {
1694 for (
auto& sampler_value : field.value.GetArray()) {
1695 auto& sampler = animation.samplers.emplace_back();
1697 for (
auto& sampler_field : sampler_value.GetObject()) {
1698 if (sampler_field.name ==
"input")
1699 sampler.input = sampler_field.value.GetUint();
1700 else if (sampler_field.name ==
"interpolation")
1701 sampler.interpolation = sampler_field.value.GetString();
1702 else if (sampler_field.name ==
"output")
1703 sampler.output = sampler_field.value.GetUint();
1705 LOG_WARNING_ONCE(logger,
"Unknown animation sampler field: %s", sampler_field.name.GetString());
1710 else if (field.name ==
"name") {
1711 animation.name = field.value.GetString();
1714 LOG_WARNING_ONCE(logger,
"Unknown animation field: %s", field.name.GetString());
1722 uint32_t getAccessorTypeSize(
const GltfAccessor& accessor)
1724 assert(
size_t(accessor.componentType) <
size_t(AccessorComponentType::Count));
1725 assert(
size_t(accessor.type) <
size_t(AccessorType::Count));
1726 return accessorTypeSize[size_t(accessor.type)] * accessorComponentTypeSize[size_t(accessor.componentType)];
1731 if (loadData.buffer_views.size() <= accessor.bufferView) {
1732 LOG_ERROR(logger,
"%.*s: Illegal accessor buffer view index %u", StringViewFormat(loadData.path), accessor.bufferView);
1735 const GltfBufferView& bufferView = loadData.buffer_views[accessor.bufferView];
1737 if (loadData.buffer_data.size() <= bufferView.buffer) {
1738 LOG_ERROR(logger,
"%.*s: Illegal buffer view buffer index %u", StringViewFormat(loadData.path), bufferView.buffer);
1741 std::span<const uint8_t> buffer = loadData.buffer_data[bufferView.buffer];
1743 size_t byteOffset = size_t(accessor.byteOffset) + bufferView.byteOffset;
1744 uint32_t stride = bufferView.byteStride != 0u ? bufferView.byteStride : getAccessorTypeSize(accessor);
1746 if (buffer.size() < byteOffset +
size_t(stride) * accessor.count) {
1747 LOG_ERROR(logger,
"%.*s: Accessor references outside of buffer (bufferSize=%zd, idx=%zd)",
1748 StringViewFormat(loadData.path), buffer.size(), byteOffset +
size_t(stride) * accessor.count);
1753 .data = buffer.data() + byteOffset,
1755 .count = accessor.count
1765 const glm::mat4& parent_transform)
1767 const glm::mat4& localTransform = node.mat;
1768 const glm::mat4 globalTransform = parent_transform * localTransform;
1770 uint32_t boneIndex = (uint32_t)skeleton.bones.size();
1771 node.bone_index = boneIndex;
1773 auto& bone = skeleton.bones.emplace_back();
1774 bone.name = std::to_string(node.node_index) +
"_" + node.name;
1775 bone.parentBone = parent_bone;
1776 bone.pos = node.pos;
1777 bone.scale = node.scale;
1778 bone.rot = node.rot;
1779 bone.relative = localTransform;
1780 bone.absolute = globalTransform;
1781 bone.inverseBindPose = glm::mat4(1.0);
1782 bone.hasOffset =
false;
1784 bone.animated =
false;
1786 skeleton.bonesByName[bone.name] = boneIndex;
1788 for (uint32_t childIndex : node.children) {
1789 if (!loadBone(definition, definition.nodes[childIndex], skeleton, boneIndex, globalTransform)) {
1802 for (uint32_t childIndex : node.children) {
1803 if (!loadJoints(definition, definition.nodes[childIndex], skeleton, meshoptDecomp)) {
1808 if (node.skin != (uint32_t)-1) {
1809 skeleton.bindPose = glm::inverse(skeleton.bones[node.bone_index].absolute);
1810 GltfSkin& skin = definition.skins[node.skin];
1812 if (definition.accessors.size() <=
size_t(skin.inverseBindMatrices)) {
1816 size_t accessorIdx = size_t(skin.inverseBindMatrices);
1817 GltfAccessor accessor = definition.accessors[accessorIdx];
1820 if (meshoptDecomp.isCompressed(definition,
int(accessorIdx))) {
1821 bool ok = meshoptDecomp.decompress(definition,
int(accessorIdx));
1828 if (!getBufferViewData(definition, dataView, accessor)) {
1833 for (uint32_t jointIndex : skin.joints) {
1834 auto& joint = skeleton.bones[definition.nodes[jointIndex].bone_index];
1835 joint.inverseBindPose = *
reinterpret_cast<const glm::mat4*
>(dataView.data + dataView.stride * i++);
1836 joint.hasOffset =
true;
1846 if (loadData.accessors.size() <= accessorIx) {
1847 LOG_ERROR(logger,
"%.*s: Illegal access index %u", StringViewFormat(loadData.path), accessorIx);
1850 const GltfAccessor& accessor = loadData.accessors[accessorIx];
1852 if (!getBufferViewData(loadData, vertexStream.dataView, accessor)) {
1859 switch (key.hash()) {
1861 if (accessor.type == AccessorType::Vec3 &&
1862 (accessor.componentType == AccessorComponentType::Float ||
1863 accessor.componentType == AccessorComponentType::Byte ||
1864 accessor.componentType == AccessorComponentType::Short ||
1865 accessor.componentType == AccessorComponentType::UnsignedShort)) {
1866 streamsState.positionData = vertexStream.dataView;
1867 streamsState.positionPresent =
true;
1868 streamsState.positionCount = accessor.count;
1870 vertexStream.format = Cogs::DataFormat::R32G32B32_FLOAT;
1872 if (accessor.componentType != AccessorComponentType::Float) {
1875 vertexStream.dataView = convertVecBufferToFloats<glm::vec3>(vertexStream.dataView, accessor.componentType, &loadData.positionsScratch.back());
1877 streamsState.positionData = vertexStream.dataView;
1879 if (accessor.minCount == 3 && accessor.maxCount == 3) {
1880 if (accessor.componentType == AccessorComponentType::Float) {
1881 streamsState.bbox.min = glm::make_vec3(accessor.min.s_float);
1882 streamsState.bbox.max = glm::make_vec3(accessor.max.s_float);
1884 else if (accessor.componentType == AccessorComponentType::UnsignedShort) {
1885 streamsState.bbox.min = glm::make_vec3(accessor.min.s_uint);
1886 streamsState.bbox.max = glm::make_vec3(accessor.max.s_uint);
1889 streamsState.bbox.min = glm::make_vec3(accessor.min.s_int);
1890 streamsState.bbox.max = glm::make_vec3(accessor.max.s_int);
1894 LOG_WARNING_ONCE(logger,
"%.*s: POSITION attribute has missing or malformed min and max values", StringViewFormat(loadData.path));
1895 streamsState.bbox.min = glm::vec3(std::numeric_limits<float>::max());
1896 streamsState.bbox.max = -streamsState.bbox.min;
1897 for (
size_t i = 0; i < streamsState.positionCount; i++) {
1898 const glm::vec3& p = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * i);
1899 if (std::isfinite(p.x) && std::isfinite(p.y) && std::isfinite(p.z)) {
1900 streamsState.bbox.min = min(streamsState.bbox.min, p);
1901 streamsState.bbox.max = max(streamsState.bbox.max, p);
1908 LOG_ERROR(logger,
"%.*s: Illegal POSITION accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
1912 if (accessor.type == AccessorType::Vec3 &&
1913 (accessor.componentType == AccessorComponentType::Float ||
1914 accessor.componentType == AccessorComponentType::Byte ||
1915 accessor.componentType == AccessorComponentType::Short)) {
1916 if (accessor.componentType != AccessorComponentType::Float) {
1919 vertexStream.dataView = convertVecBufferToFloats<glm::vec3>(vertexStream.dataView, accessor.componentType, &loadData.normalsScratch.back());
1922 streamsState.normalData = vertexStream.dataView;
1923 streamsState.normalPresent =
true;
1925 vertexStream.format = Cogs::DataFormat::R32G32B32_FLOAT;
1929 LOG_ERROR(logger,
"%.*s: Illegal NORMAL accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
1933 if (accessor.type == AccessorType::Vec4 &&
1934 (accessor.componentType == AccessorComponentType::Float ||
1935 accessor.componentType == AccessorComponentType::Byte ||
1936 accessor.componentType == AccessorComponentType::Short)) {
1937 streamsState.tangentPresent =
true;
1939 if (accessor.componentType != AccessorComponentType::Float) {
1942 vertexStream.dataView = convertVecBufferToFloats<glm::vec4>(vertexStream.dataView, accessor.componentType, &loadData.tangentsScratch.back());
1944 vertexStream.format = Cogs::DataFormat::R32G32B32_FLOAT;
1948 LOG_ERROR(logger,
"%.*s: Illegal TANGENT accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
1956 if (
size_t underscore = key.find_first_of(
'_');
1958 underscore + 1 < key.length() &&
1959 '0' <= key[underscore + 1] &&
1960 key[underscore + 1] <=
'9')
1962 vertexStream.semanticIndex = 0;
1963 size_t digit = underscore + 1;
1964 for (; digit < key.size() &&
'0' <= key[digit] && key[digit] <=
'9'; digit++) {
1965 vertexStream.semanticIndex = 10 * vertexStream.semanticIndex + uint32_t(key[digit] -
'0');
1968 if (digit == key.length()) {
1969 switch (key.substr(0, underscore).hash()) {
1971 if (vertexStream.semanticIndex == 0) {
1972 if (accessor.componentType != AccessorComponentType::Float) {
1975 vertexStream.dataView = convertVecBufferToFloats<glm::vec2>(vertexStream.dataView, accessor.componentType, &loadData.texCoordsScratch.back());
1980 if (loadData.textureTransforms.size()) {
1981 const TextureTransform& textureTransform = loadData.textureTransforms.back();
1982 float* buf =
reinterpret_cast<float*
>(
const_cast<uint8_t*
>(vertexStream.dataView.data));
1983 size_t num = vertexStream.dataView.count;
1984 const glm::mat3 translation = glm::mat3(1.0, 0, 0, 0, 1.0, 0, textureTransform.offset.x, textureTransform.offset.y, 1.0);
1985 const glm::mat3 rotation = glm::mat3(cos(textureTransform.rotation), sin(textureTransform.rotation), 0,
1986 -sin(textureTransform.rotation), cos(textureTransform.rotation), 0,
1988 const glm::mat3 scale = glm::mat3(textureTransform.scale.x, 0, 0, 0, textureTransform.scale.y, 0, 0, 0, 1.0);
1989 const glm::mat3 xform = translation * rotation * scale;
1991 for (
size_t i = 0; i < num; ++i) {
1992 const glm::vec3 result = xform * glm::vec3(buf[i * 2], buf[i * 2 + 1], 1.0);
1993 buf[i * 2] = result.x;
1994 buf[i * 2 + 1] = result.y;
1998 streamsState.texcoordData = vertexStream.dataView;
1999 streamsState.texcoordStreamCount++;
2001 vertexStream.format = Cogs::DataFormat::R32G32_FLOAT;
2005 streamsState.colorStreamCount++;
2007 if (accessor.type == AccessorType::Vec3) {
2008 switch (accessor.componentType) {
2009 case AccessorComponentType::Float: vertexStream.format = Cogs::DataFormat::R32G32B32_FLOAT;
return true;
2010 case AccessorComponentType::UnsignedByte: vertexStream.format = Cogs::DataFormat::R8G8B8_UNORM;
return true;
2011 case AccessorComponentType::UnsignedShort: vertexStream.format = Cogs::DataFormat::R16G16B16_UNORM;
return true;
2015 else if (accessor.type == AccessorType::Vec4) {
2016 switch (accessor.componentType) {
2017 case AccessorComponentType::Float: vertexStream.format = Cogs::DataFormat::R32G32B32A32_FLOAT;
return true;
2018 case AccessorComponentType::UnsignedByte: vertexStream.format = Cogs::DataFormat::R8G8B8A8_UNORM;
return true;
2019 case AccessorComponentType::UnsignedShort: vertexStream.format = Cogs::DataFormat::R16G16B16A16_UNORM;
return true;
2023 LOG_ERROR(logger,
"%.*s: Illegal COLOR accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
2027 if (vertexStream.semanticIndex == 0) {
2028 streamsState.jointsStreamCount = 1;
2030 vertexStream.semanticIndex = 4;
2031 if (accessor.type == AccessorType::Vec4) {
2032 switch (accessor.componentType) {
2033 case AccessorComponentType::UnsignedByte: vertexStream.format = Cogs::DataFormat::R8G8B8A8_UINT;
return true;
2034 case AccessorComponentType::UnsignedShort: vertexStream.format = Cogs::DataFormat::R16G16B16A16_UINT;
return true;
2035 case AccessorComponentType::Float: vertexStream.format = Cogs::DataFormat::R32G32B32A32_FLOAT;
return true;
2039 LOG_ERROR(logger,
"%.*s: Illegal JOINTS accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
2045 if (vertexStream.semanticIndex == 0) {
2047 vertexStream.semanticIndex = 5;
2048 if (accessor.type == AccessorType::Vec4) {
2049 switch (accessor.componentType) {
2050 case AccessorComponentType::Float: vertexStream.format = Cogs::DataFormat::R32G32B32A32_FLOAT;
return true;
2051 case AccessorComponentType::UnsignedByte: vertexStream.format = Cogs::DataFormat::R8G8B8A8_UNORM;
return true;
2052 case AccessorComponentType::UnsignedShort: vertexStream.format = Cogs::DataFormat::R16G16B16A16_UNORM;
return true;
2056 LOG_ERROR(logger,
"%.*s: Illegal WEIGHTS accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
2069 LOG_WARNING_ONCE(logger,
"Unrecognized attribute semantic %.*s", StringViewFormat(key));
2075 for (
const auto& attributeIt : attributeObject) {
2077 uint32_t accessorIx = 0;
2078 if (!getUint(loadData, accessorIx, attributeIt)) {
2083 if (meshoptDecomp.isCompressed(loadData, accessorIx)) {
2084 meshoptDecomp.decompress(loadData, accessorIx);
2089 if (getVertexElement(loadData, streamsState, vertexStream, toView(attributeIt.name), accessorIx)) {
2090 vertexStreams.push_back(vertexStream);
2094 if (!streamsState.positionPresent) {
2095 LOG_ERROR(logger,
"%.*s: Primitive has no POSITION attribute", StringViewFormat(loadData.path));
2099 for (
const auto& stream : vertexStreams) {
2100 if (stream.dataView.count != streamsState.positionCount) {
2101 LOG_ERROR(logger,
"%.*s: Primitive has attributes with variable counts", StringViewFormat(loadData.path));
2112 LOG_ERROR(logger,
"%.*s: Vertex tangent generator only supports triangle lists (type is %d)", StringViewFormat(loadData.path), primitiveType);
2115 assert(streamsState.positionPresent);
2116 assert(streamsState.normalPresent);
2117 assert(streamsState.texcoordStreamCount);
2119 const size_t byteSize =
sizeof(glm::vec3) * streamsState.positionCount;
2123 memBuf->resize(byteSize);
2124 glm::vec3* tangents = (glm::vec3*) memBuf->data();
2125 std::memset(
static_cast<void*
>(tangents), 0, byteSize);
2127 size_t count = streamsState.indexType != AccessorComponentType::None ? streamsState.indexData.count : streamsState.positionCount;
2128 for (
size_t i = 0; i + 2 < count; i += 3) {
2129 size_t ix[3] = { 0,0,0 };
2130 switch (streamsState.indexType) {
2131 case AccessorComponentType::None:
for (
size_t k = 0; k < 3; k++) { ix[k] = i + k; }
break;
2132 case AccessorComponentType::UnsignedByte:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2133 case AccessorComponentType::UnsignedShort:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(
const uint16_t*)(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2134 case AccessorComponentType::UnsignedInt:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(
const uint32_t*)(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2135 default: assert(
false);
return false;
2138 glm::vec3 a = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[0]);
2139 glm::vec3 b = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[1]);
2140 glm::vec3 c = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[2]);
2141 glm::vec2 ta = *(
const glm::vec2*)(streamsState.texcoordData.data + streamsState.texcoordData.stride * ix[0]);
2142 glm::vec2 tb = *(
const glm::vec2*)(streamsState.texcoordData.data + streamsState.texcoordData.stride * ix[1]);
2143 glm::vec2 tc = *(
const glm::vec2*)(streamsState.texcoordData.data + streamsState.texcoordData.stride * ix[2]);
2160 float den = 1.f / (u0.x * u1.y - u0.y * u1.x);
2161 glm::vec3 t0 = u1.y * den * v0 - u0.y * den * v1;
2163 tangents[ix[0]] += t0;
2164 tangents[ix[1]] += t0;
2165 tangents[ix[2]] += t0;
2168 for (
size_t i = 0; i < streamsState.positionCount; i++) {
2169 const glm::vec3 normal = *(
const glm::vec3*)(streamsState.normalData.data + streamsState.normalData.stride * i);
2170 tangents[i] = glm::normalize(tangents[i] - glm::dot(normal, tangents[i]) * normal);
2175 .data = (
const uint8_t*) memBuf->data(),
2176 .stride =
sizeof(glm::vec3),
2177 .count = streamsState.positionCount
2179 .format = Cogs::DataFormat::R32G32B32_FLOAT,
2190 LOG_ERROR(logger,
"%.*s: Vertex normal generator only supports triangle lists", StringViewFormat(loadData.path));
2193 assert(streamsState.positionPresent);
2195 const size_t byteSize =
sizeof(glm::vec3) * streamsState.positionCount;
2199 memBuf->resize(byteSize);
2200 glm::vec3* normals = (glm::vec3*) memBuf->data();
2201 std::memset(
static_cast<void*
>(normals), 0, byteSize);
2203 size_t count = streamsState.indexSize ? streamsState.indexData.count : streamsState.positionCount;
2204 for (
size_t i = 0; i + 2 < count; i += 3) {
2205 size_t ix[3] = { 0,0,0 };
2206 switch (streamsState.indexSize) {
2207 case 0:
for (
size_t k = 0; k < 3; k++) { ix[k] = i + k; }
break;
2208 case 1:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2209 case 2:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(
const uint16_t*)(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2210 case 4:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(
const uint32_t*)(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2211 default: assert(
false);
return false;
2213 glm::vec3 a = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[0]);
2214 glm::vec3 b = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[1]);
2215 glm::vec3 c = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[2]);
2217 const glm::vec3 l1 = clockwise ? a - b : b - a;
2218 const glm::vec3 l2 = clockwise ? a - c : c - a;
2219 glm::vec3 n = glm::cross(l1, l2);
2221 if (glm::length(n) <= 0.0) {
2222 LOG_WARNING_ONCE(logger,
"Calculated normal is not valid (normalIdx=%zu)", i);
2223 n = glm::vec3(1, 0, 0);
2226 normals[ix[0]] += n;
2227 normals[ix[1]] += n;
2228 normals[ix[2]] += n;
2231 for (
size_t i = 0; i < streamsState.positionCount; i++) {
2232 normals[i] = glm::normalize(normals[i]);
2237 .data = (
const uint8_t*) normals,
2238 .stride =
sizeof(glm::vec3),
2239 .count = streamsState.positionCount
2241 .format = Cogs::DataFormat::R32G32B32_FLOAT,
2251 uint32_t dstStride = 0;
2252 switch (streamsState.indexType) {
2254 case AccessorComponentType::UnsignedByte: dstStride = 2; streamsState.indexSize = 1;
break;
2255 case AccessorComponentType::UnsignedShort: dstStride = 2; streamsState.indexSize = dstStride;
break;
2256 case AccessorComponentType::UnsignedInt: dstStride = 4; streamsState.indexSize = dstStride;
break;
2258 LOG_ERROR(logger,
"%.*s: Illegal index data component type %s", StringViewFormat(loadData.path), accessorComponentTypeName[
size_t(streamsState.indexType)]);
2262 if (uint8_t* dst = mesh->mapStream(VertexDataType::Indexes, 0, streamsState.indexData.count, dstStride,
true); dst) {
2263 switch (streamsState.indexType) {
2264 case AccessorComponentType::UnsignedByte: stridedCopy<1>(dst, 2, streamsState.indexData.data, streamsState.indexData.stride, streamsState.indexData.count);
break;
2265 case AccessorComponentType::UnsignedShort: stridedCopy<2>(dst, 2, streamsState.indexData.data, streamsState.indexData.stride, streamsState.indexData.count);
break;
2266 case AccessorComponentType::UnsignedInt: stridedCopy<4>(dst, 4, streamsState.indexData.data, streamsState.indexData.stride, streamsState.indexData.count);
break;
2267 default: assert(
false);
break;
2269 mesh->unmap(VertexDataType::Indexes);
2273 mesh->setCount(streamsState.indexData.count);
2283 const Cogs::FormatInfo* formatInfo = Cogs::getFormatInfo(vertexStream.format);
2286 vertexStream.formatSize = formatInfo->
blockSize;
2290 std::vector<Cogs::VertexElement> vertexElements;
2292 offset = (offset + 3) & ~3;
2293 vertexStream.offset = offset;
2295 .
offset = uint16_t(offset),
2296 .format = vertexStream.format,
2297 .semantic = vertexStream.semantic,
2298 .semanticIndex = uint16_t(vertexStream.semanticIndex),
2302 offset += vertexStream.formatSize;
2305 vertexFormat = Cogs::VertexFormats::createVertexFormat(vertexElements.data(), vertexElements.size());
2307 LOG_ERROR(logger,
"%.*s: Failed to create vertex format with %zu elements", StringViewFormat(loadData.path), vertexElements.size());
2312 size_t stride = Cogs::getSize(vertexFormat);
2313 if (uint8_t* base = mesh->mapStream(VertexDataType::Interleaved0, vertexFormat, 0, streamsState.positionCount, stride,
true)) {
2314 for (
const VertexStream& vertexStream : vertexStreams) {
2315 uint8_t* dst = base + vertexStream.offset;
2316 switch (vertexStream.format) {
2317 case Cogs::DataFormat::R32G32B32A32_FLOAT:
2318 assert(vertexStream.formatSize == 16);
2319 stridedCopy<16>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2322 case Cogs::DataFormat::R32G32B32_FLOAT:
2323 assert(vertexStream.formatSize == 12);
2324 stridedCopy<12>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2327 case Cogs::DataFormat::R32G32_FLOAT:
2328 case Cogs::DataFormat::R16G16B16A16_UNORM:
2329 case Cogs::DataFormat::R16G16B16A16_UINT:
2330 assert(vertexStream.formatSize == 8);
2331 stridedCopy<8>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2334 case Cogs::DataFormat::R16G16B16_UNORM:
2335 case Cogs::DataFormat::R16G16B16_UINT:
2336 case Cogs::DataFormat::R16G16B16_SINT:
2337 case Cogs::DataFormat::R16G16B16_SNORM:
2338 assert(vertexStream.formatSize == 6);
2339 stridedCopy<6>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2342 case Cogs::DataFormat::R16G16_UNORM:
2343 case Cogs::DataFormat::R16G16_UINT:
2344 case Cogs::DataFormat::R16G16_SNORM:
2345 case Cogs::DataFormat::R16G16_SINT:
2346 case Cogs::DataFormat::R8G8B8A8_UNORM:
2347 case Cogs::DataFormat::R8G8B8A8_UINT:
2348 assert(vertexStream.formatSize == 4);
2349 stridedCopy<4>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2352 case Cogs::DataFormat::R8G8B8_UNORM:
2353 case Cogs::DataFormat::R8G8B8_UINT:
2354 case Cogs::DataFormat::R8G8B8_SINT:
2355 case Cogs::DataFormat::R8G8B8_SNORM:
2356 assert(vertexStream.formatSize == 3);
2357 stridedCopy<3>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2360 case Cogs::DataFormat::R8G8_UNORM:
2361 assert(vertexStream.formatSize == 3);
2362 stridedCopy<2>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2366 assert(
false &&
"Unhandled DataFormat");
2370 mesh->unmap(VertexDataType::Interleaved0);
2378 std::vector<VertexStream> vertexStreams;
2381 bool needsNormals =
true;
2382 if (
auto materialIt = primitiveObject.FindMember(
"material"); materialIt != primitiveObject.MemberEnd()) {
2383 uint32_t materialIx = 0;
2384 if (!getUint(loadData, materialIx, *materialIt)) {
2385 LOG_ERROR(logger,
"The 'meshes.primitives.material' is not an integer");
2388 if (materialIx >= 0) {
2389 const Object& materialObject = loadData.materialsArray[materialIx];
2390 if (materialObject.HasMember(
"extensions") && materialObject[
"extensions"].HasMember(
"KHR_materials_unlit")) {
2391 needsNormals =
false;
2397 bool useDracoDecompression =
false;
2401 if (primitiveObject.HasMember(
"extensions") && primitiveObject[
"extensions"].HasMember(
"KHR_draco_mesh_compression")) {
2402 auto extSection = primitiveObject[
"extensions"].GetObject();
2403 auto dracoSection = extSection[
"KHR_draco_mesh_compression"].GetObject();
2404 draco.initAttributes(dracoSection);
2405 useDracoDecompression =
true;
2408 if (
const auto& attributesIt = primitiveObject.FindMember(
"attributes"); attributesIt != primitiveObject.MemberEnd()) {
2409 if (!checkIsObject(loadData, *attributesIt)) {
2413 if (useDracoDecompression) {
2414 if (!draco.decompress(loadData, streamsState, vertexStreams, attributesIt->value.GetObject(), !needsNormals)) {
2419 if (!getVertexAttributes(loadData, streamsState, vertexStreams, attributesIt->value.GetObject(), meshoptDecomp)) {
2425 LOG_ERROR(logger,
"%.*s: Primitive has no attribute property", StringViewFormat(loadData.path));
2430 bool isIndexed =
false;
2431 uint32_t indicesAccessorIx = 0;
2433 if (useDracoDecompression) {
2437 if (
auto indicesIt = primitiveObject.FindMember(
"indices"); indicesIt != primitiveObject.MemberEnd()) {
2438 if (!getUint(loadData, indicesAccessorIx, *indicesIt)) {
2439 LOG_ERROR(logger,
"The 'meshes.primitives.indices' is not an integer");
2443 if (loadData.accessors.size() <= indicesAccessorIx) {
2444 LOG_ERROR(logger,
"%.*s: Illegal accessor index %u", StringViewFormat(loadData.path), indicesAccessorIx);
2448 const GltfAccessor& accessor = loadData.accessors[indicesAccessorIx];
2449 streamsState.indexType = accessor.componentType;
2451 if (meshoptDecomp.isCompressed(loadData, indicesAccessorIx)) {
2452 bool ok = meshoptDecomp.decompress(loadData, indicesAccessorIx);
2458 if (!getBufferViewData(loadData, streamsState.indexData, accessor)) {
2467 if (
auto modeIt = primitiveObject.FindMember(
"mode"); modeIt != primitiveObject.MemberEnd()) {
2469 if (!getUint(loadData, mode, *modeIt)) {
2479 case 2: LOG_ERROR(logger,
"%.*s: Unsupported primitive mode 2: LINE_LOOP", StringViewFormat(loadData.path));
return false;
2480 case 6: LOG_ERROR(logger,
"%.*s: Unsupported primitive mode 6: TRIANGLE_FAN", StringViewFormat(loadData.path));
return false;
2481 default: LOG_ERROR(logger,
"%.*s: Illegal primitive mode %u", StringViewFormat(loadData.path), mode);
return false;
2487 if (
auto targetsIt = primitiveObject.FindMember(
"targets"); targetsIt != primitiveObject.MemberEnd()) {
2488 if (!checkIsArray(loadData, *targetsIt)) {
2491 if (!targetsIt->value.GetArray().Empty()) {
2493 LOG_WARNING_ONCE(logger,
"%.*s: Morph targets not implemented yet, ignoring.", StringViewFormat(loadData.path));
2498 bool isSkinned = streamsState.jointsStreamCount != 0;
2499 bool albedoPerVertex = streamsState.colorStreamCount != 0;
2501 if (
auto materialIt = primitiveObject.FindMember(
"material"); materialIt != primitiveObject.MemberEnd()) {
2502 uint32_t materialIx = 0;
2503 if (!getUint(loadData, materialIx, *materialIt)) {
2504 LOG_ERROR(logger,
"The 'meshes.primitives.material' is not an integer");
2507 if (!getMaterialHandle(loadData, model, modelMaterial, materialIx, isSkinned, albedoPerVertex,
false)) {
2512 if (!getMaterialHandle(loadData, model, modelMaterial, -1, isSkinned, albedoPerVertex,
false)) {
2516 assert(modelMaterial);
2517 modelMeshInstance.materialIx = modelMaterial->modelMaterialIx;
2520 if (streamsState.texcoordStreamCount < modelMaterial->texCoordSets) {
2521 LOG_ERROR(logger,
"%.*s: Material requires texture coordinates, something which the mesh doesn't have", StringViewFormat(loadData.path));
2526 if (modelMaterial->needsNormals && !streamsState.normalPresent) {
2527 if (!generateVertexNormals(loadData, streamsState, vertexStreams, primitiveType, clockwise)) {
2533 if (modelMaterial->needsTangents && !streamsState.tangentPresent) {
2534 if (!generateVertexTangents(loadData, streamsState, vertexStreams, primitiveType, clockwise)) {
2540 if (loadData.optimizationLevel >= 1) {
2541 std::vector<VertexStream> culledVertexStreams;
2543 for (
auto& stream : vertexStreams) {
2545 switch (stream.semantic) {
2547 keep = stream.semanticIndex == 0;
2550 keep = modelMaterial->needsNormals ? stream.semanticIndex == 0 :
false;
2553 if (stream.semanticIndex == 4 || stream.semanticIndex == 5) {
2557 keep = albedoPerVertex && stream.semanticIndex == 0;
2561 keep = stream.semanticIndex < modelMaterial->texCoordSets;
2564 keep = stream.semanticIndex == 0;
2570 culledVertexStreams.emplace_back(stream);
2573 debugMessageOnce(loadData,
"Culled unused vertex stream with semantic=%u:%u",
unsigned(stream.semantic), stream.semanticIndex);
2576 vertexStreams = std::move(culledVertexStreams);
2581 size_t N = vertexStreams.size();
2582 std::vector<VertexStream> sortedStreams(N);
2583 for (
const auto& stream : vertexStreams) {
2586 (
unsigned(stream.semantic) <
unsigned(sortedStreams[i - 1].semantic)) ||
2587 ((
unsigned(stream.semantic) ==
unsigned(sortedStreams[i - 1].semantic) && (stream.semanticIndex < sortedStreams[i - 1].semanticIndex)))))
2589 sortedStreams[i] = sortedStreams[i - 1];
2592 sortedStreams[i] = stream;
2594 vertexStreams = std::move(sortedStreams);
2602 clockwise ? uint32_t(1) : uint32_t(0),
2603 isIndexed ? uint32_t(~0) : indicesAccessorIx);
2604 for (
const VertexStream& vertexStream : vertexStreams) {
2605 meshHash =
hash(vertexStream, meshHash);
2608 if (
auto it = loadData.modelMeshCache.find(meshHash); it != loadData.modelMeshCache.end()) {
2609 debugMessageOnce(loadData,
"Found duplicate primitive description, recycling mesh");
2610 modelMeshInstance.mesh = it->second;
2615 modelMeshInstance.mesh.meshIx = uint32_t(model.meshes.size());
2616 model.meshes.push_back(mesh.getHandle());
2617 if (!setVertexAttributes(loadData, mesh, streamsState, vertexStreams)) {
2626 mesh->setMeshFlag(MeshFlags::Skinned);
2629 mesh->primitiveType = primitiveType;
2630 mesh->setBounds(streamsState.bbox);
2631 modelMeshInstance.mesh.bboxIx = uint32_t(model.bounds.size());
2632 model.bounds.push_back(streamsState.bbox);
2635 if (!setMeshIndices(loadData, mesh, streamsState)) {
2640 mesh->setCount(streamsState.positionCount);
2642 modelMeshInstance.mesh.vertexCount = mesh->getCount();
2647 assert(
size_t(skinIx) < loadData.skins.size());
2648 const GltfSkin& skin = loadData.skins[skinIx];
2649 size_t count = skin.joints.size();
2650 if (kMaxBones < count) {
2651 LOG_WARNING_ONCE(logger,
"%.*s: Unable to handle bone count %zu over %zu", StringViewFormat(loadData.path), count, kMaxBones);
2655 std::span<uint32_t> poseIndexes = mesh->mapPoseIndexes(
static_cast<uint32_t
>(count));
2656 for (
size_t i = 0; i < count; i++) {
2657 uint32_t jointIx = skin.joints[i];
2658 poseIndexes[i] = loadData.nodes[jointIx].bone_index;
2662 loadData.modelMeshCache[meshHash] = modelMeshInstance.mesh;
2668 [[nodiscard]]
bool buildMeshes(
GltfModelDefinition& loadData, std::vector<ModelMeshInstance>& modelMeshInstances,
Model& model, int32_t skinIx, int32_t gltfMeshIx,
bool clockwise,
MeshoptDecompressor & meshoptDecomp)
2670 if (loadData.meshesArray.size() <
size_t(gltfMeshIx)) {
2671 LOG_ERROR(logger,
"%.*s: Illegal gltf mesh index %u", StringViewFormat(loadData.path), gltfMeshIx);
2675 const Object& meshObject = loadData.meshesArray[gltfMeshIx];
2677 if (
auto primitivesIt = meshObject.FindMember(
"primitives"); primitivesIt != meshObject.MemberEnd()) {
2678 if (!checkIsArray(loadData, *primitivesIt)) {
2682 const Array& primitivesArray = primitivesIt->value.GetArray();
2683 for (
auto& primitiveItem : primitivesArray) {
2684 if (!checkIsObject(loadData,
"mesh primitive item", primitiveItem)) {
2687 if (!buildPrimitiveMesh(loadData, modelMeshInstances.emplace_back(), model, primitiveItem.GetObject(), skinIx, clockwise, meshoptDecomp)) {
2693 LOG_ERROR(logger,
"%.*s: Mesh %u has no primitives property", StringViewFormat(loadData.path), gltfMeshIx);
2700 bool loadNode(
Context* context,
2704 uint32_t parent_part,
2705 const glm::mat4& parent_transform,
2708 const bool clockwise = glm::determinant(node.mat) < 0.0f;
2709 const glm::mat4& localTransform = node.mat;
2710 const glm::mat4 globalTransform = parent_transform * localTransform;
2712 std::string nodeName = std::to_string(node.node_index) +
"_" + node.name;
2713 uint32_t partIndex = (uint32_t)model.parts.size();
2714 node.part_index = partIndex;
2716 ModelPart& nodePart = model.parts.emplace_back();
2717 nodePart.parentIndex = parent_part;
2718 model.setPartTransform(nodePart, localTransform);
2719 model.setPartName(nodePart, nodeName);
2721 if (node.mesh != (uint32_t)-1) {
2722 std::vector<ModelMeshInstance> meshes;
2723 if (!buildMeshes(definition, meshes, model, node.skin, node.mesh, clockwise, meshoptDecomp)) {
2728 if (meshes.size() == 1) {
2729 nodePart.meshIndex = meshes[0].mesh.meshIx;
2730 nodePart.materialIndex = meshes[0].materialIx;
2731 nodePart.boundsIndex = meshes[0].mesh.bboxIx;
2732 nodePart.vertexCount = meshes[0].mesh.vertexCount;
2736 for (
auto& instance : meshes) {
2737 ModelPart& part = model.parts.emplace_back();
2738 part.parentIndex = partIndex;
2739 part.meshIndex = instance.mesh.meshIx;
2740 part.materialIndex = instance.materialIx;
2741 part.boundsIndex = instance.mesh.bboxIx;
2742 part.vertexCount = instance.mesh.vertexCount;
2747 for (uint32_t child_index : node.children) {
2748 if (!loadNode(context, definition, definition.nodes[child_index], model, partIndex, globalTransform, meshoptDecomp)) {
2758 if (definition.animations.size()) model.animation = context->animationManager->create();
2760 auto& animation = *model.animation.
resolve();
2761 auto& skeleton = model.skeleton;
2763 for (
auto& animationValue : definition.animations) {
2764 auto& clip = animation.clips.emplace_back();
2766 clip.name = animationValue.name;
2767 clip.duration = 0.0f;
2768 clip.resolution = 1.0f;
2770 uint32_t prev_node = (
decltype(prev_node))-1;
2771 for (uint32_t i = 0; i < animationValue.channels.size(); i++) {
2772 GltfChannel& channel = animationValue.channels[i];
2773 GltfSampler& sampler = animationValue.samplers[channel.sampler];
2774 uint32_t nodeIndex = channel.node;
2775 GltfNode& node = definition.nodes[nodeIndex];
2776 uint32_t boneIndex = node.bone_index;
2778 if (sampler.interpolation !=
"" && sampler.interpolation !=
"LINEAR")
2779 LOG_ERROR(logger,
"Unable to handle animation interpolation %s", sampler.interpolation.c_str());
2781 auto& inputAccessor = definition.accessors[sampler.input];
2784 if (meshoptDecomp.isCompressed(definition, sampler.input)) {
2785 bool ok = meshoptDecomp.decompress(definition, sampler.input);
2792 if (!getBufferViewData(definition, dataView, inputAccessor)) {
2796 if (!dataView.data || !dataView.count) {
2800 float t_max = inputAccessor.max.s_float[0];
2802 clip.duration = std::max(t_max, clip.duration);
2804 if (prev_node != nodeIndex) {
2805 clip.tracks.push_back({});
2807 prev_node = nodeIndex;
2808 auto& track = clip.tracks.back();
2809 track.boneIndex = boneIndex;
2811 skeleton.bones[boneIndex].animated =
true;
2813 auto getTime = [](uint32_t i,
const uint8_t* iData,
const size_t iStride) {
2814 return *
reinterpret_cast<const float*
>(iData + iStride * i);
2817 if (definition.accessors.size() <= sampler.output) {
2821 const GltfAccessor& outputAccessor = definition.accessors[sampler.output];
2824 if (meshoptDecomp.isCompressed(definition, sampler.output)) {
2825 bool ok = meshoptDecomp.decompress(definition, sampler.output);
2832 if (!getBufferViewData(definition, oDataView, outputAccessor)) {
2836 if (channel.path ==
"translation") {
2837 assert(outputAccessor.type == AccessorType::Vec3);
2838 track.translations.resize(dataView.count);
2840 for (uint32_t j = 0; j < dataView.count; j++) {
2841 const float time = getTime(j, dataView.data, dataView.stride);
2842 glm::vec3 translation = *
reinterpret_cast<const glm::vec3*
>(oDataView.data + oDataView.stride * j);
2846 else if (channel.path ==
"scale") {
2847 assert(outputAccessor.type == AccessorType::Vec3);
2848 track.scales.resize(dataView.count);
2850 for (uint32_t j = 0; j < dataView.count; j++) {
2851 const float time = getTime(j, dataView.data, dataView.stride);
2852 glm::vec3 scale = *
reinterpret_cast<const glm::vec3*
>(oDataView.data + oDataView.stride * j);
2853 track.scales[j] =
ScaleKey{ time, scale };
2856 else if (channel.path ==
"rotation") {
2857 assert(outputAccessor.type == AccessorType::Vec4);
2858 track.rotations.resize(dataView.count);
2860 if (outputAccessor.componentType == AccessorComponentType::Float) {
2861 for (uint32_t j = 0; j < dataView.count; j++) {
2862 const float time = getTime(j, dataView.data, dataView.stride);
2863 glm::vec4 rot = *
reinterpret_cast<const glm::vec4*
>(oDataView.data + oDataView.stride * j);
2864 track.rotations[j] =
RotationKey{ time, glm::quat(rot.w, rot.x, rot.y, rot.z) };
2869 LOG_ERROR(logger,
"Unknown animation channel path: %s", channel.path.c_str());
2873 if (model.animation) {
2874 model.animation->skeleton = model.skeleton;
2885 if (
Variable* enable = context->
variables->get(enableVariableName); !enable->isEmpty() && enable->getBool() ==
false) {
2892 if (resourcePath.starts_with(
"http")) {
2893 size_t paramPos = resourcePath.find(
'?');
2894 if (paramPos != std::string::npos) {
2895 resourcePath = resourcePath.substr(0, paramPos);
2899 auto extension = Cogs::IO::extension(resourcePath);
2900 if (extension.starts_with(
".gltf"))
return true;
2901 if (extension.starts_with(
".glb"))
return true;
2902 if (extension.starts_with(
".b3dm"))
return true;
2919 uint32_t featureTableJSONLength;
2920 uint32_t featureTableBinaryLength;
2921 uint32_t batchTableJSONLength;
2922 uint32_t batchTableBinaryLength;
2925 const uint32_t b3dmMagic = uint32_t(
'b') + uint32_t(
'3' << 8) + uint32_t(
'd' << 16) + uint32_t(
'm' << 24);
2926 B3DMHeader* header =
reinterpret_cast<B3DMHeader*
>(content);
2928 if (header->magic != b3dmMagic) {
2932 assert(header->length <= size &&
"Invalid length value in header");
2934 if (header->version != 1) {
2935 LOG_ERROR(logger,
"The B3DM file is not version 1.0");
2939 uint32_t featureTableStart =
sizeof(B3DMHeader);
2940 uint32_t batchTableStart = featureTableStart + header->featureTableJSONLength + header->featureTableBinaryLength;
2941 uint32_t glbStart = batchTableStart + header->batchTableJSONLength + header->batchTableBinaryLength;
2943 assert(glbStart < size &&
"Offset out of bounds");
2947 bool GltfLoader::load(
Context* context,
const ModelLoadInfo& loadInfo, std::unique_ptr<Cogs::FileContents> contents)
2949 LOG_TRACE(logger,
"Loading model: '%s'", loadInfo.
resourceName.c_str());
2953 if (!content.size()) {
2954 LOG_ERROR(logger,
"Could not open file '%s'.", loadInfo.
resourcePath.c_str());
2961 const uint8_t* dataPtr = (uint8_t*)(content.data()) + fileOffset;
2970 struct GlbChunkHeader
2978 GlbChunkHeader header;
2982 JsonParseFlags flags = JsonParseFlags::NoCachedContent;
2986 const GlbHeader* header = (
const GlbHeader*) dataPtr;
2987 GltfModelDefinition loadData(context, loadInfo.
resourcePath);
2988 loadData.default_material = -1;
2989 if (
auto var = context->
variables->get(optimizeVariableName); !var->isEmpty()) {
2990 loadData.optimizationLevel = unsigned(std::max(0, var->getInt()));
2993 std::span<const uint8_t> glb_data;
2994 if (
sizeof(GlbHeader) <= content.size() && header->magic == 0x46546C67) {
2995 loadData.version = header->version * 100;
2997 if (header->version != 2) {
2998 LOG_ERROR(logger,
"Unable to load glb format %d", loadData.version);
3002 size_t offset =
sizeof(GlbHeader);
3004 while (offset < (content.size() - fileOffset)) {
3005 const GlbChunk* chunk = (GlbChunk*)(
reinterpret_cast<const char*
>(dataPtr) + offset);
3006 if (chunk->header.type == 0x4E4F534A)
3007 document = parseJson(
Cogs::StringView(chunk->data, chunk->header.length), flags);
3008 else if (chunk->header.type == 0x004E4942)
3009 glb_data = std::span((
const uint8_t*)chunk->data, (
const uint8_t*)chunk->data + chunk->header.length);
3011 LOG_ERROR(logger,
"Unknown glb chunk type 0x%x (index %d)", chunk->header.type, i);
3013 offset += chunk->header.length +
sizeof(GlbChunkHeader);
3018 document = parseJson(
Cogs::StringView(
reinterpret_cast<const char*
>(dataPtr), content.size() - fileOffset), flags);
3021 if (!document.IsObject()) {
3022 LOG_ERROR(logger,
"Could not load asset file %s: JSON root is not an object.", loadInfo.
resourceName.c_str());
3026 auto& modelManager = *context->modelManager;
3027 auto model = modelManager.lock(loadInfo.
handle);
3029 MeshoptDecompressor meshoptDecomp;
3031 auto root = document.GetObject();
3034 if (
const auto& assetItem = root.FindMember(
"asset"); assetItem != root.MemberEnd()) {
3035 if (!parseAsset(loadData, path, assetItem->value))
return false;
3038 LOG_ERROR(logger,
"%.*s: No required JSON asset member", StringViewFormat(path));
3042 if (
const auto& buffersItem = root.FindMember(
"buffers"); buffersItem != root.MemberEnd()) {
3043 if (!parseBuffers(context, loadData, path, buffersItem->value, glb_data)) {
3048 if (
const auto& bufferViewsItem = root.FindMember(
"bufferViews"); bufferViewsItem != root.MemberEnd()) {
3049 if (!parseBufferViews(loadData, path, bufferViewsItem->value, meshoptDecomp)) {
3054 if (
const auto& imagesItem = root.FindMember(
"images"); imagesItem != root.MemberEnd()) {
3055 if (!parseImages(context, loadData, imagesItem->value)) {
3060 if (
const auto& samplersItem = root.FindMember(
"samplers"); samplersItem != root.MemberEnd()) {
3061 if (!parseSamplers(loadData, path, samplersItem->value)) {
3066 if (
const auto& texturesItem = root.FindMember(
"textures"); texturesItem != root.MemberEnd()) {
3067 if (!parseTextures(loadData, path, texturesItem->value)) {
3072 if (
auto materialsItem = root.FindMember(
"materials"); materialsItem != root.MemberEnd()) {
3073 if (!checkIsArray(loadData, *materialsItem))
return false;
3074 for (
const Value& materialsElement : materialsItem->value.GetArray()) {
3075 if (!checkIsObject(loadData,
"materials element", materialsElement)) {
3078 loadData.materialsArray.emplace_back(materialsElement.GetObject());
3082 if (
auto meshesItem = root.FindMember(
"meshes"); meshesItem != root.MemberEnd()) {
3083 if (!checkIsArray(loadData, *meshesItem))
return false;
3084 for (
const Value& meshesElement : meshesItem->value.GetArray()) {
3085 if (!checkIsObject(loadData,
"meshes element", meshesElement)) {
3088 loadData.meshesArray.emplace_back(meshesElement.GetObject());
3092 for (
const auto& section : root) {
3093 switch (toView(section.name).
hash()) {
3106 if (!parseNodes(loadData, path, section.value)) return false;
3110 if (!parseScenes(loadData, path, section.value)) return false;
3114 loadData.load_scene = section.value.GetUint();
3118 if (!checkIsArray(loadData, section)) return false;
3119 if (!parseAccessors(loadData, path, section.value))
return false;
3123 if (!parseSkins(loadData, path, section.value)) return false;
3127 if (!parseAnimations(loadData, path, section.value)) return false;
3131 for (auto& extension : section.value.GetArray()) {
3132 if (extension.GetString() ==
Cogs::StringView(
"KHR_materials_pbrSpecularGlossiness") ||
3147 LOG_ERROR(logger,
"Unknown required extension: %s", extension.GetString());
3155 if (section.value.IsArray()) {
3156 for (
auto& extension : section.value.GetArray()) {
3157 if (extension.GetString() ==
Cogs::StringView(
"KHR_materials_pbrSpecularGlossiness") ||
3169 LOG_WARNING(logger,
"Unknown extension: %s", extension.GetString());
3173 else if (section.value.IsObject()) {
3174 for (
const auto& extension : section.value.GetObject()) {
3175 LOG_WARNING(logger,
"Unknown extension: %s", extension.name.GetString());
3179 LOG_WARNING(logger,
"Unknown extension: %s", section.value.GetString());
3184 LOG_WARNING(logger,
"%.*s: Unrecognized root key %s", StringViewFormat(path), section.name.GetString());
3189 uint32_t sceneIndex = 0;
3190 for (
auto& scene : loadData.scenes) {
3191 if (sceneIndex > 1) {
3192 LOG_ERROR(logger,
"Loader unable to handle more than one scene.");
3196 model->setName(scene.name);
3200 auto& root_ = model->parts.emplace_back();
3201 model->setPartTransform(root_, glm::rotate(glm::mat4(1.0f), glm::half_pi<float>(), glm::vec3(1, 0, 0)));
3202 model->setPartName(root_,
"CogsRoot");
3205 glm::mat4 rootTransform(1.0f);
3207 for (uint32_t nodeIndex : scene.nodes) {
3208 assert(nodeIndex < loadData.nodes.size());
3210 if (!loadBone(loadData, loadData.nodes[nodeIndex], model->skeleton, (
size_t)-1, rootTransform)) {
3214 if (!loadJoints(loadData, loadData.nodes[nodeIndex], model->skeleton, meshoptDecomp)) {
3219 if (!loadNode(context, loadData, loadData.nodes[nodeIndex], *model, 0, rootTransform, meshoptDecomp)) {
3224 if (!loadAnimation(context, loadData, *model, meshoptDecomp)) {
3229 for (
auto const & it : loadData.debugMessages) {
3230 LOG_TRACE(logger,
"%s (x %u)", it.first.c_str(), it.second);
3235 bool GltfLoader::load(Context* context,
const ModelLoadInfo& loadInfo)
3237 JsonParseFlags flags = JsonParseFlags::NoCachedContent;
3238#ifdef __EMSCRIPTEN__
3239 const auto resourceFlags = ResourceStoreFlags::None;
3241 const auto resourceFlags = (flags & JsonParseFlags::NoCachedContent) == JsonParseFlags::NoCachedContent
3243 : ResourceStoreFlags::None;
3246 std::unique_ptr<Cogs::FileContents> contents;
3248 if (loadInfo.protocol == ResourceProtocol::Archive) {
3250 ResourceBuffer buffer = context->resourceStore->getResourceContents(loadInfo.resourcePath);
3251 if (!buffer.buffer->data()) {
3252 LOG_ERROR(logger,
"Could not load resource data for model %s.", loadInfo.resourcePath.c_str());
3255 contents = std::make_unique<Cogs::Platform::ResourceBufferBackedFileContents>(buffer, loadInfo.resourcePath, Cogs::FileContentsHints::None);
3259 Cogs::FileHandle::Ptr file = Cogs::IO::openFile(loadInfo.resourcePath, Cogs::FileHandle::OpenMode::OpenAlways, Cogs::FileHandle::AccessMode::Read);
3261 LOG_ERROR(logger,
"Could not open file %s for mapping.", loadInfo.resourcePath.c_str());
3265 size_t size = Cogs::IO::fileSize(file);
3267 LOG_ERROR(logger,
"File size invalid: %zd.", size);
3271 const uint8_t* ptr =
static_cast<const uint8_t*
>(Cogs::IO::mapFile(file, Cogs::FileHandle::ProtectionMode::ReadOnly, 0, size));
3273 LOG_ERROR(logger,
"Could not map file %s for reading model data.", loadInfo.resourcePath.c_str());
3277 contents = std::make_unique<Cogs::MMapBackedFileContents>(ptr, size, file, loadInfo.resourcePath, Cogs::FileContentsHints::None);
3282 const ResourceBuffer content = context->resourceStore->getResourceContents(path, resourceFlags);
3284 return load(context, loadInfo, std::move(contents));
3287 inline GltfModelDefinition::GltfModelDefinition(Context* context,
Cogs::StringView path) :
3291 timer = Cogs::Timer::startNew();
3292 rootTask = context->taskManager->createGroup(context->taskManager->ResourceQueue);
3295 inline GltfModelDefinition::~GltfModelDefinition()
3297 context->taskManager->wait(rootTask);
3298 for (
auto& image : images) {
3299 if (image.decoded.data) {
3300 stbi_image_free(image.decoded.data);
3301 image.decoded.data =
nullptr;
3304 LOG_TRACE(logger,
"Gltf elapsed: %f.", timer.elapsedSeconds());
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< struct MemoryContext > memory
Memory and allocation info.
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
std::unique_ptr< class Variables > variables
Variables service instance.
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
size_t getPotentialB3DMOffset(uint8_t *content, size_t size)
bool registerBufferViewCompression(uint32_t bufferViewIdx, const GltfLoader::Object &properties)
Returns false if something failed.
Log implementation class.
Provides a weakly referenced view over the contents of a string.
size_t find_first_of(char character, size_t pos=0) const noexcept
Find the first occurance of the given character from the specified starting position.
constexpr StringView substr(size_t offset, size_t count=NoPosition) const noexcept
Get the given sub string.
static constexpr size_t NoPosition
No position.
constexpr size_t hash() const noexcept
Get the hash code of the string.
@ NoCachedContent
Never use cached data.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
TextureLoadFlags
Texture loading flags. May be combined with resource load flags.
@ LinearColorSpace
For textures with RGBA format without color space information, mark the data as being in linear color...
@ ColorSpaceFromLoadInfo
by default we want to retrieve colorspace info from the texture data, not from the format specified i...
@ NoMipMaps
Do not generate mipmaps.
@ Back
Back face of primitives discarded before rasterization.
@ None
No primitive culling performed.
uint16_t VariableKey
Used to lookup material properties.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
constexpr size_t hashSequence(const T &t, const U &u)
Hash the last two items in a sequence of objects.
@ VertexData
Per vertex data.
PrimitiveType
Primitive types for interpreting vertex data sent to the graphics pipeline.
@ PointList
List of points.
@ TriangleStrip
Triangle strip.
@ TriangleList
List of triangles.
@ Position
Position semantic.
@ Tangent
Tangent semantic.
@ TextureCoordinate
Texture coordinate semantic.
void setFloatProperty(const VariableKey key, float value)
Set the float property with the given key to value.
void setTransparent()
Set the material instance to transparent, indicating to the renderer that blending should be enabled ...
void setVec3Property(const VariableKey key, glm::vec3 value)
Set the vec3 property with the given key to value.
void setOption(const StringView &key, const StringView &value)
Sets the option with the given key to a value parsed from the value string.
void setTextureAddressMode(const StringView &key, const StringView &addressMode)
Set texture address mode with textual name.
void setTextureProperty(const StringView &key, TextureHandle value)
Set the texture property with the given key to the texture resource held by value.
MaterialOptions options
Material rendering options used by this instance.
void setVec4Property(const VariableKey key, glm::vec4 value)
Set the vec4 property with the given key to value.
Material * material
Material resource this MaterialInstance is created from.
CullMode cullMode
Primitive culling mode to use.
@ IndexesChanged
The index data of the mesh changed.
@ Indexed
The mesh should be drawn indexed, using index data to order the triangle vertexes.
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
Model resources define a template for a set of connected entities, with resources such as meshes,...
void setName(const StringView &name)
Set the user friendly name of the resource.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
std::string resourcePath
Resource path. Used to locate resource.
std::string resourceName
Desired resource name. If no name is given, a default name will be chosen.
ResourceHandleBase handle
Handle to resource structure for holding actual resource data.
Runtime control variable.
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
@ Mirror
Texture coordinates are mirrored when outside [0, 1] range.
@ MinMagMipPoint
Point sampling for both minification and magnification.
@ MinMagMipLinear
Linear sampling for both minification and magnification.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
uint16_t offset
Offset in bytes from the vertex position in memory.