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(
"DefaultHasTexCoord",
true);
1156 materialInstance->setVariant(
"Textured",
true);
1157 materialInstance->setVariant(
"LightModel",
"BaseColor");
1158 materialInstance->setVariant(
"EnableLighting",
false);
1159 materialInstance->setVariant(
"NoTonemap",
true);
1161 if (
auto pbrMetallicValue = materialObject.FindMember(
"pbrMetallicRoughness"); pbrMetallicValue != materialObject.MemberEnd()) {
1162 if (!checkIsObject(loadData, *pbrMetallicValue)) {
1163 LOG_ERROR(logger,
"The 'KHR_materials_unlit' material does not have a 'pbrMetallicRoughness' section.");
1166 const Object& pbrMetallicObject = pbrMetallicValue->value.GetObject();
1167 glm::vec4 baseColorFactor(1.0, 1.0, 1.0, 1.0);
1168 if (!tryGetVecN(baseColorFactor, loadData,
"baseColorFactor", pbrMetallicObject)) {
1169 LOG_ERROR(logger,
"The 'pbrMetallicRoughness' section does not have a 'baseColorFactor' value.");
1172 materialInstance->
setVec4Property(DefaultMaterial::DiffuseColor, baseColorFactor);
1174 if (!tryHandleTextureInfo(material, materialInstance, loadData, cachedMaterial,
"baseColorTexture", pbrMetallicObject,
"Diffuse",
false,
nullptr)) {
1176 LOG_ERROR(logger,
"The 'pbrMetallicRoughness' section does not have a 'baseColorTexture' value.");
1182 cachedMaterial_->needsNormals =
false;
1185 MaterialHandle material = loadData.context->materialManager->getMaterial(
"StandardMaterial");
1186 materialInstance = loadData.context->materialInstanceManager->createMaterialInstance(material);
1189 materialInstance->setPermutation(
"Metallic");
1190 if (
auto extensionsIt = materialObject.FindMember(
"extensions"); extensionsIt != materialObject.MemberEnd()) {
1191 if (!checkIsObject(loadData, *extensionsIt))
return false;
1192 const Object& extensionsObject = extensionsIt->value.GetObject();
1193 if (
auto specGlossIt = extensionsObject.FindMember(
"KHR_materials_pbrSpecularGlossiness"); specGlossIt != extensionsObject.MemberEnd()) {
1194 materialInstance->setPermutation(
"Specular");
1198 if (
auto extensionsIt = materialObject.FindMember(
"extensions"); extensionsIt != materialObject.MemberEnd()) {
1199 if (!checkIsObject(loadData, *extensionsIt))
return false;
1200 const Object& extensionsObject = extensionsIt->value.GetObject();
1201 if (
auto iorIt = extensionsObject.FindMember(
"KHR_materials_ior"); iorIt != extensionsObject.MemberEnd()) {
1202 materialInstance->setVariant(
"Ior",
true);
1204 if (
auto specularIt = extensionsObject.FindMember(
"KHR_materials_specular"); specularIt != extensionsObject.MemberEnd()) {
1205 materialInstance->setVariant(
"Specular",
true);
1209 materialInstance->setVariant(
"LightModel",
"PBR");
1210 materialInstance->setVariant(
"GltfMaterialModel",
true);
1211 if (isSkinned) materialInstance->setVariant(
"Skinned",
true);
1212 if (albedoPerVertex) materialInstance->setVariant(
"AlbedoPerVertex",
true);
1213 if (hasBitangents) materialInstance->setVariant(
"Bitangents",
true);
1215 if (!handleMaterialPbrMetallicRoughness(loadData, cachedMaterial, material, materialInstance, materialObject))
return false;
1216 if (!handleMaterialGenericFields(loadData, cachedMaterial, material, materialInstance, materialObject))
return false;
1217 if (!handleMaterialExtensions(loadData, cachedMaterial, material, materialInstance, materialObject))
return false;
1219 cachedMaterial_->needsNormals =
true;
1224 MaterialHandle material = loadData.context->materialManager->getMaterial(
"StandardMaterial");
1225 materialInstance = loadData.context->materialInstanceManager->createMaterialInstance(material);
1227 materialInstance->setPermutation(
"Metallic");
1228 materialInstance->setVariant(
"GltfMaterialModel",
true);
1229 if (isSkinned) materialInstance->setVariant(
"Skinned",
true);
1230 if (albedoPerVertex) materialInstance->setVariant(
"AlbedoPerVertex",
true);
1231 if (hasBitangents) materialInstance->setVariant(
"Bitangents",
true);
1232 materialInstance->
setName(
"GLTF default material");
1233 materialInstance->
setOption(
"CullMode",
"Back");
1234 materialInstance->setVariant(
"GltfMaterialModel",
true);
1236 materialInstance->
setVec4Property(material->getVec4Key(
"albedo"), glm::vec4(1.0, 1.0, 1.0, 1.0));
1237 materialInstance->
setFloatProperty(material->getFloatKey(
"metallic"), 1.0f);
1238 materialInstance->
setFloatProperty(material->getFloatKey(
"roughness"), 1.0f);
1239 materialInstance->
setVec3Property(material->getVec3Key(
"emissive"), glm::vec3(0.0, 0.0, 0.0));
1240 materialInstance->
setFloatProperty(material->getFloatKey(
"alphaThreshold"), 0.5f);
1242 cachedMaterial_->needsNormals =
true;
1245 assert(materialInstance &&
"Internal error");
1247 cachedMaterial.modelMaterialIx = int32_t(model.materials.size());
1248 model.materials.push_back(materialInstance);
1254 if (!scenesValue.IsArray()) {
1255 LOG_ERROR(logger,
"%.*s: JSON scenes is not an array value", StringViewFormat(path));
1259 for (
const auto& sceneValue : scenesValue.GetArray()) {
1260 if (!sceneValue.IsObject()) {
1261 LOG_ERROR(logger,
"%.*s: JSON scenes item is not an object", StringViewFormat(path));
1265 GltfScene& scene = loadData.scenes.emplace_back();
1266 for (
const auto& field : sceneValue.GetObject()) {
1267 switch (toView(field.name).
hash()) {
1269 if (!field.value.IsString()) {
1270 LOG_ERROR(logger,
"%.*s: JSON scene name value is not a string", StringViewFormat(path));
1273 scene.name = field.value.GetString();
1278 if (!field.value.IsArray()) {
1279 LOG_ERROR(logger,
"%.*s: JSON scene nodes value is not an array", StringViewFormat(path));
1282 for (
const auto& node : field.value.GetArray()) {
1283 if (!node.IsUint()) {
1284 LOG_ERROR(logger,
"%.*s: JSON scene nodes array item is not an uint", StringViewFormat(path));
1287 scene.nodes.push_back(node.GetUint());
1293 if (!field.value.IsObject()) {
1294 LOG_ERROR(logger,
"%.*s: JSON scene extensions item is not an object", StringViewFormat(path));
1307 auto type = field.value.GetType();
1309 LOG_WARNING_ONCE(logger,
"Unhandled application-specific node under scenes node: Extras[rapidjson::Type: %u]", type );
1313 LOG_WARNING_ONCE(logger,
"%.*s: JSON unknown scene field: %s", StringViewFormat(path), field.name.GetString());
1323 if (!nodesValue.IsArray()) {
1324 LOG_ERROR(logger,
"%.*s: JSON nodes member is not an array value", StringViewFormat(path));
1328 const auto nodeValues = nodesValue.GetArray();
1329 const uint32_t numNodes = nodeValues.Size();
1330 loadData.nodes.resize(numNodes);
1332 for (uint32_t i = 0; i < numNodes; ++i) {
1333 GltfNode& node = loadData.nodes[i];
1334 node.node_index = i;
1336 bool use_matrix_transform =
false;
1338 const Value& nodeValue = nodeValues[i];
1339 if (!nodeValue.IsObject()) {
1340 LOG_ERROR(logger,
"%.*s: JSON nodes array item not an object", StringViewFormat(path));
1344 for (
const auto& field : nodeValue.GetObject()) {
1345 switch (toView(field.name).
hash()) {
1347 if (!field.value.IsString()) {
1348 LOG_ERROR(logger,
"%.*s: JSON node name is not a string", StringViewFormat(path));
1351 node.name = field.value.GetString();
1355 if (!field.value.IsArray()) {
1356 LOG_ERROR(logger,
"%.*s: JSON node children is not an array", StringViewFormat(path));
1359 for (
const auto& child : field.value.GetArray()) {
1360 if (!child.IsUint()) {
1361 LOG_ERROR(logger,
"%.*s: JSON children array element is not an uint", StringViewFormat(path));
1364 uint32_t childIndex = child.GetUint();
1365 node.children.push_back(childIndex);
1366 loadData.nodes[childIndex].parent_node = i;
1371 if (field.value.IsArray() && field.value.GetArray().Size() == 4) {
1372 auto arr = field.value.GetArray();
1373 node.rot = glm::quat(arr[3].GetFloat(), arr[0].GetFloat(), arr[1].GetFloat(), arr[2].GetFloat());
1376 LOG_ERROR(logger,
"%.*s: JSON node rotation value is not an array with 4 elements", StringViewFormat(path));
1382 if (field.value.IsArray() && field.value.GetArray().Size() == 3) {
1383 auto arr = field.value.GetArray();
1384 node.pos = glm::vec3(arr[0].GetFloat(), arr[1].GetFloat(), arr[2].GetFloat());
1387 LOG_ERROR(logger,
"%.*s: JSON node translation value is not an array with 3 elements", StringViewFormat(path));
1393 if (field.value.IsArray() && field.value.GetArray().Size() == 3) {
1394 auto arr = field.value.GetArray();
1395 node.scale = glm::vec3(arr[0].GetFloat(), arr[1].GetFloat(), arr[2].GetFloat());
1398 LOG_ERROR(logger,
"%.*s: JSON node scale value is not an array with 3 elements", StringViewFormat(path));
1404 if (field.value.IsArray() && field.value.GetArray().Size() == 16) {
1405 auto arr = field.value.GetArray();
1406 node.mat = glm::mat4(arr[0].GetFloat(), arr[1].GetFloat(), arr[2].GetFloat(), arr[3].GetFloat(),
1407 arr[4].GetFloat(), arr[5].GetFloat(), arr[6].GetFloat(), arr[7].GetFloat(),
1408 arr[8].GetFloat(), arr[9].GetFloat(), arr[10].GetFloat(), arr[11].GetFloat(),
1409 arr[12].GetFloat(), arr[13].GetFloat(), arr[14].GetFloat(), arr[15].GetFloat());
1411 glm::vec4 perspective;
1412 glm::decompose(node.mat, node.scale, node.rot, node.pos, skew, perspective);
1413 use_matrix_transform =
true;
1416 LOG_ERROR(logger,
"%.*s: JSON node matrix value is not an array with 16 elements", StringViewFormat(path));
1422 if (field.value.IsUint()) {
1423 node.mesh = field.value.GetUint();
1426 LOG_ERROR(logger,
"%.*s: JSON node mesh value is not an uint", StringViewFormat(path));
1432 if (field.value.IsUint()) {
1433 node.skin = field.value.GetUint();
1436 LOG_ERROR(logger,
"%.*s: JSON node skin value is not an uint", StringViewFormat(path));
1445 LOG_DEBUG(logger,
"Unknown node field: %s", field.name.GetString());
1450 if (!use_matrix_transform) {
1452 node.mat = glm::translate(ii, node.pos) * glm::mat4_cast(node.rot) * glm::scale(ii, node.scale);
1462 void parseAccessorMinArray(
GltfAccessor & accessor, Array arr)
1464 accessor.minCount = std::min(16u, arr.Size());
1465 for (uint32_t i = 0; i < accessor.minCount; i++) {
1466 if (arr[i].IsUint() || arr[i].IsInt()) {
1467 if (accessor.componentType == AccessorComponentType::Float) {
1468 accessor.min.s_float[i] = float(arr[i].GetInt());
1471 if (accessor.componentType == AccessorComponentType::UnsignedByte ||
1472 accessor.componentType == AccessorComponentType::UnsignedShort ||
1473 accessor.componentType == AccessorComponentType::UnsignedInt) {
1474 accessor.min.s_uint[i] = arr[i].GetUint();
1477 accessor.min.s_int[i] = arr[i].GetInt();
1482 if (accessor.componentType == AccessorComponentType::Float) {
1483 accessor.min.s_float[i] = arr[i].GetFloat();
1486 if (accessor.componentType == AccessorComponentType::UnsignedByte ||
1487 accessor.componentType == AccessorComponentType::UnsignedShort ||
1488 accessor.componentType == AccessorComponentType::UnsignedInt) {
1489 accessor.min.s_uint[i] = uint32_t(std::floor(arr[i].GetFloat()));
1492 accessor.min.s_int[i] = int32_t(std::floor(arr[i].GetFloat()));
1500 void parseAccessorMaxArray(
GltfAccessor& accessor, Array arr)
1502 accessor.maxCount = std::min(16u, arr.Size());
1503 for (uint32_t i = 0; i < accessor.maxCount; i++) {
1504 if (arr[i].IsInt() || arr[i].IsUint()) {
1505 if (accessor.componentType == AccessorComponentType::Float) {
1506 accessor.max.s_float[i] = float(arr[i].GetInt());
1509 if (accessor.componentType == AccessorComponentType::UnsignedByte ||
1510 accessor.componentType == AccessorComponentType::UnsignedShort ||
1511 accessor.componentType == AccessorComponentType::UnsignedInt) {
1512 accessor.max.s_uint[i] = arr[i].GetUint();
1515 accessor.max.s_int[i] = arr[i].GetInt();
1520 if (accessor.componentType == AccessorComponentType::Float) {
1521 accessor.max.s_float[i] = arr[i].GetFloat();
1524 if (accessor.componentType == AccessorComponentType::UnsignedByte ||
1525 accessor.componentType == AccessorComponentType::UnsignedShort ||
1526 accessor.componentType == AccessorComponentType::UnsignedInt) {
1527 accessor.max.s_uint[i] = uint32_t(std::ceil(arr[i].GetFloat()));
1530 accessor.max.s_int[i] = int32_t(std::ceil(arr[i].GetFloat()));
1539 assert(accessorsValue.IsArray());
1540 for (
const auto& accessorValue : accessorsValue.GetArray()) {
1541 if (!checkIsObject(loadData,
"accessor array item", accessorValue))
return false;
1543 GltfAccessor& accessor = loadData.accessors.emplace_back();
1544 for (
const auto& field : accessorValue.GetObject()) {
1545 switch (toView(field.name).
hash()) {
1547 if (!getUint(loadData, accessor.bufferView, field))
return false;
1551 if (!getUint(loadData, accessor.byteOffset, field))
return false;
1555 if (checkIsUint(loadData, field)) {
1556 switch (field.value.GetUint()) {
1557 case GLTF_BYTE: accessor.componentType = AccessorComponentType::Byte;
break;
1558 case GLTF_UNSIGNED_BYTE: accessor.componentType = AccessorComponentType::UnsignedByte;
break;
1559 case GLTF_SHORT: accessor.componentType = AccessorComponentType::Short;
break;
1560 case GLTF_UNSIGNED_SHORT: accessor.componentType = AccessorComponentType::UnsignedShort;
break;
1561 case GLTF_UNSIGNED_INT: accessor.componentType = AccessorComponentType::UnsignedInt;
break;
1562 case GLTF_FLOAT: accessor.componentType = AccessorComponentType::Float;
break;
1564 LOG_ERROR(logger,
"%.*s:n Unrecognized accessor componentType %u", StringViewFormat(path), field.value.GetUint());
1572 if (!getUint(loadData, accessor.count, field))
return false;
1576 if (checkIsString(loadData, field)) {
1577 switch (toView(field.value).
hash()) {
1578 case Cogs::hash(
"SCALAR"): accessor.type = AccessorType::Scalar;
break;
1579 case Cogs::hash(
"VEC2"): accessor.type = AccessorType::Vec2;
break;
1580 case Cogs::hash(
"VEC3"): accessor.type = AccessorType::Vec3;
break;
1581 case Cogs::hash(
"VEC4"): accessor.type = AccessorType::Vec4;
break;
1582 case Cogs::hash(
"MAT2"): accessor.type = AccessorType::Mat2;
break;
1583 case Cogs::hash(
"MAT3"): accessor.type = AccessorType::Mat3;
break;
1584 case Cogs::hash(
"MAT4"): accessor.type = AccessorType::Mat4;
break;
1586 LOG_ERROR(logger,
"%.*s: Unrecognized accessor type '%s'", StringViewFormat(path), field.value.GetString());
1594 if (checkIsArray(loadData, field)) {
1595 assert(field.value.IsArray());
1596 parseAccessorMinArray(accessor, field.value.GetArray());
1602 if (checkIsArray(loadData, field)) {
1603 assert(field.value.IsArray());
1604 parseAccessorMaxArray(accessor, field.value.GetArray());
1612 if (!getBool(loadData, accessor.normalized, field))
return false;
1620 LOG_WARNING_ONCE(logger,
"%.*s: Unknown accessor key '%s'", StringViewFormat(path), field.name.GetString());
1630 if (!skinsValue.IsArray()) {
1631 LOG_ERROR(logger,
"%.*s: Skins is not an array", StringViewFormat(path));
1634 for (
auto& skin_value : skinsValue.GetArray()) {
1635 auto& skin = loadData.skins.emplace_back();
1637 for (
const auto& field : skin_value.GetObject()) {
1638 if (field.name ==
"inverseBindMatrices")
1639 skin.inverseBindMatrices = field.value.GetUint();
1640 else if (field.name ==
"joints") {
1641 for (
auto& joint : field.value.GetArray()) skin.joints.push_back(joint.GetUint());
1643 else if (field.name ==
"skeleton")
1644 skin.skeleton = field.value.GetUint();
1645 else if (field.name ==
"name")
1646 skin.name = field.value.GetString();
1648 LOG_WARNING_ONCE(logger,
"Unknown skin field: %s", field.name.GetString());
1657 if (!animationsValue.IsArray()) {
1658 LOG_ERROR(logger,
"%.*s: Animations is not an array", StringViewFormat(path));
1661 for (
auto& animation_value : animationsValue.GetArray()) {
1662 auto& animation = loadData.animations.emplace_back();
1664 for (
const auto& field : animation_value.GetObject()) {
1665 if (field.name ==
"channels") {
1666 for (
auto& channel_value : field.value.GetArray()) {
1667 auto& channel = animation.channels.emplace_back();
1669 for (
auto& channel_field : channel_value.GetObject()) {
1670 if (channel_field.name ==
"sampler")
1671 channel.sampler = channel_field.value.GetUint();
1672 else if (channel_field.name ==
"target") {
1673 for (
auto& target : channel_field.value.GetObject()) {
1674 if (target.name ==
"node")
1675 channel.node = target.value.GetUint();
1676 else if (target.name ==
"path")
1677 channel.path = target.value.GetString();
1679 LOG_WARNING_ONCE(logger,
"Unknown animation channel target field: %s", target.name.GetString());
1684 LOG_WARNING_ONCE(logger,
"Unknown animation channel field: %s", channel_field.name.GetString());
1689 else if (field.name ==
"samplers") {
1690 for (
auto& sampler_value : field.value.GetArray()) {
1691 auto& sampler = animation.samplers.emplace_back();
1693 for (
auto& sampler_field : sampler_value.GetObject()) {
1694 if (sampler_field.name ==
"input")
1695 sampler.input = sampler_field.value.GetUint();
1696 else if (sampler_field.name ==
"interpolation")
1697 sampler.interpolation = sampler_field.value.GetString();
1698 else if (sampler_field.name ==
"output")
1699 sampler.output = sampler_field.value.GetUint();
1701 LOG_WARNING_ONCE(logger,
"Unknown animation sampler field: %s", sampler_field.name.GetString());
1706 else if (field.name ==
"name") {
1707 animation.name = field.value.GetString();
1710 LOG_WARNING_ONCE(logger,
"Unknown animation field: %s", field.name.GetString());
1718 uint32_t getAccessorTypeSize(
const GltfAccessor& accessor)
1720 assert(
size_t(accessor.componentType) <
size_t(AccessorComponentType::Count));
1721 assert(
size_t(accessor.type) <
size_t(AccessorType::Count));
1722 return accessorTypeSize[size_t(accessor.type)] * accessorComponentTypeSize[size_t(accessor.componentType)];
1727 if (loadData.buffer_views.size() <= accessor.bufferView) {
1728 LOG_ERROR(logger,
"%.*s: Illegal accessor buffer view index %u", StringViewFormat(loadData.path), accessor.bufferView);
1731 const GltfBufferView& bufferView = loadData.buffer_views[accessor.bufferView];
1733 if (loadData.buffer_data.size() <= bufferView.buffer) {
1734 LOG_ERROR(logger,
"%.*s: Illegal buffer view buffer index %u", StringViewFormat(loadData.path), bufferView.buffer);
1737 std::span<const uint8_t> buffer = loadData.buffer_data[bufferView.buffer];
1739 size_t byteOffset = size_t(accessor.byteOffset) + bufferView.byteOffset;
1740 uint32_t stride = bufferView.byteStride != 0u ? bufferView.byteStride : getAccessorTypeSize(accessor);
1742 if (buffer.size() < byteOffset +
size_t(stride) * accessor.count) {
1743 LOG_ERROR(logger,
"%.*s: Accessor references outside of buffer (bufferSize=%zd, idx=%zd)",
1744 StringViewFormat(loadData.path), buffer.size(), byteOffset +
size_t(stride) * accessor.count);
1749 .data = buffer.data() + byteOffset,
1751 .count = accessor.count
1761 const glm::mat4& parent_transform)
1763 const glm::mat4& localTransform = node.mat;
1764 const glm::mat4 globalTransform = parent_transform * localTransform;
1766 uint32_t boneIndex = (uint32_t)skeleton.bones.size();
1767 node.bone_index = boneIndex;
1769 auto& bone = skeleton.bones.emplace_back();
1770 bone.name = std::to_string(node.node_index) +
"_" + node.name;
1771 bone.parentBone = parent_bone;
1772 bone.pos = node.pos;
1773 bone.scale = node.scale;
1774 bone.rot = node.rot;
1775 bone.relative = localTransform;
1776 bone.absolute = globalTransform;
1777 bone.inverseBindPose = glm::mat4(1.0);
1778 bone.hasOffset =
false;
1780 bone.animated =
false;
1782 skeleton.bonesByName[bone.name] = boneIndex;
1784 for (uint32_t childIndex : node.children) {
1785 if (!loadBone(definition, definition.nodes[childIndex], skeleton, boneIndex, globalTransform)) {
1798 for (uint32_t childIndex : node.children) {
1799 if (!loadJoints(definition, definition.nodes[childIndex], skeleton, meshoptDecomp)) {
1804 if (node.skin != (uint32_t)-1) {
1805 skeleton.bindPose = glm::inverse(skeleton.bones[node.bone_index].absolute);
1806 GltfSkin& skin = definition.skins[node.skin];
1808 if (definition.accessors.size() <=
size_t(skin.inverseBindMatrices)) {
1812 size_t accessorIdx = size_t(skin.inverseBindMatrices);
1813 GltfAccessor accessor = definition.accessors[accessorIdx];
1816 if (meshoptDecomp.isCompressed(definition,
int(accessorIdx))) {
1817 bool ok = meshoptDecomp.decompress(definition,
int(accessorIdx));
1824 if (!getBufferViewData(definition, dataView, accessor)) {
1829 for (uint32_t jointIndex : skin.joints) {
1830 auto& joint = skeleton.bones[definition.nodes[jointIndex].bone_index];
1831 joint.inverseBindPose = *
reinterpret_cast<const glm::mat4*
>(dataView.data + dataView.stride * i++);
1832 joint.hasOffset =
true;
1842 if (loadData.accessors.size() <= accessorIx) {
1843 LOG_ERROR(logger,
"%.*s: Illegal access index %u", StringViewFormat(loadData.path), accessorIx);
1846 const GltfAccessor& accessor = loadData.accessors[accessorIx];
1848 if (!getBufferViewData(loadData, vertexStream.dataView, accessor)) {
1855 switch (key.hash()) {
1857 if (accessor.type == AccessorType::Vec3 &&
1858 (accessor.componentType == AccessorComponentType::Float ||
1859 accessor.componentType == AccessorComponentType::Byte ||
1860 accessor.componentType == AccessorComponentType::Short ||
1861 accessor.componentType == AccessorComponentType::UnsignedShort)) {
1862 streamsState.positionData = vertexStream.dataView;
1863 streamsState.positionPresent =
true;
1864 streamsState.positionCount = accessor.count;
1866 vertexStream.format = Cogs::DataFormat::R32G32B32_FLOAT;
1868 if (accessor.componentType != AccessorComponentType::Float) {
1871 vertexStream.dataView = convertVecBufferToFloats<glm::vec3>(vertexStream.dataView, accessor.componentType, &loadData.positionsScratch.back());
1873 streamsState.positionData = vertexStream.dataView;
1875 if (accessor.minCount == 3 && accessor.maxCount == 3) {
1876 if (accessor.componentType == AccessorComponentType::Float) {
1877 streamsState.bbox.min = glm::make_vec3(accessor.min.s_float);
1878 streamsState.bbox.max = glm::make_vec3(accessor.max.s_float);
1880 else if (accessor.componentType == AccessorComponentType::UnsignedShort) {
1881 streamsState.bbox.min = glm::make_vec3(accessor.min.s_uint);
1882 streamsState.bbox.max = glm::make_vec3(accessor.max.s_uint);
1885 streamsState.bbox.min = glm::make_vec3(accessor.min.s_int);
1886 streamsState.bbox.max = glm::make_vec3(accessor.max.s_int);
1890 LOG_WARNING_ONCE(logger,
"%.*s: POSITION attribute has missing or malformed min and max values", StringViewFormat(loadData.path));
1891 streamsState.bbox.min = glm::vec3(std::numeric_limits<float>::max());
1892 streamsState.bbox.max = -streamsState.bbox.min;
1893 for (
size_t i = 0; i < streamsState.positionCount; i++) {
1894 const glm::vec3& p = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * i);
1895 if (std::isfinite(p.x) && std::isfinite(p.y) && std::isfinite(p.z)) {
1896 streamsState.bbox.min = min(streamsState.bbox.min, p);
1897 streamsState.bbox.max = max(streamsState.bbox.max, p);
1904 LOG_ERROR(logger,
"%.*s: Illegal POSITION accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
1908 if (accessor.type == AccessorType::Vec3 &&
1909 (accessor.componentType == AccessorComponentType::Float ||
1910 accessor.componentType == AccessorComponentType::Byte ||
1911 accessor.componentType == AccessorComponentType::Short)) {
1912 if (accessor.componentType != AccessorComponentType::Float) {
1915 vertexStream.dataView = convertVecBufferToFloats<glm::vec3>(vertexStream.dataView, accessor.componentType, &loadData.normalsScratch.back());
1918 streamsState.normalData = vertexStream.dataView;
1919 streamsState.normalPresent =
true;
1921 vertexStream.format = Cogs::DataFormat::R32G32B32_FLOAT;
1925 LOG_ERROR(logger,
"%.*s: Illegal NORMAL accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
1929 if (accessor.type == AccessorType::Vec4 &&
1930 (accessor.componentType == AccessorComponentType::Float ||
1931 accessor.componentType == AccessorComponentType::Byte ||
1932 accessor.componentType == AccessorComponentType::Short)) {
1933 streamsState.tangentPresent =
true;
1935 if (accessor.componentType != AccessorComponentType::Float) {
1938 vertexStream.dataView = convertVecBufferToFloats<glm::vec4>(vertexStream.dataView, accessor.componentType, &loadData.tangentsScratch.back());
1940 vertexStream.format = Cogs::DataFormat::R32G32B32_FLOAT;
1944 LOG_ERROR(logger,
"%.*s: Illegal TANGENT accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
1952 if (
size_t underscore = key.find_first_of(
'_');
1954 underscore + 1 < key.length() &&
1955 '0' <= key[underscore + 1] &&
1956 key[underscore + 1] <=
'9')
1958 vertexStream.semanticIndex = 0;
1959 size_t digit = underscore + 1;
1960 for (; digit < key.size() &&
'0' <= key[digit] && key[digit] <=
'9'; digit++) {
1961 vertexStream.semanticIndex = 10 * vertexStream.semanticIndex + uint32_t(key[digit] -
'0');
1964 if (digit == key.length()) {
1965 switch (key.substr(0, underscore).hash()) {
1967 if (vertexStream.semanticIndex == 0) {
1968 if (accessor.componentType != AccessorComponentType::Float) {
1971 vertexStream.dataView = convertVecBufferToFloats<glm::vec2>(vertexStream.dataView, accessor.componentType, &loadData.texCoordsScratch.back());
1976 if (loadData.textureTransforms.size()) {
1977 const TextureTransform& textureTransform = loadData.textureTransforms.back();
1978 float* buf =
reinterpret_cast<float*
>(
const_cast<uint8_t*
>(vertexStream.dataView.data));
1979 size_t num = vertexStream.dataView.count;
1980 const glm::mat3 translation = glm::mat3(1.0, 0, 0, 0, 1.0, 0, textureTransform.offset.x, textureTransform.offset.y, 1.0);
1981 const glm::mat3 rotation = glm::mat3(cos(textureTransform.rotation), sin(textureTransform.rotation), 0,
1982 -sin(textureTransform.rotation), cos(textureTransform.rotation), 0,
1984 const glm::mat3 scale = glm::mat3(textureTransform.scale.x, 0, 0, 0, textureTransform.scale.y, 0, 0, 0, 1.0);
1985 const glm::mat3 xform = translation * rotation * scale;
1987 for (
size_t i = 0; i < num; ++i) {
1988 const glm::vec3 result = xform * glm::vec3(buf[i * 2], buf[i * 2 + 1], 1.0);
1989 buf[i * 2] = result.x;
1990 buf[i * 2 + 1] = result.y;
1994 streamsState.texcoordData = vertexStream.dataView;
1995 streamsState.texcoordStreamCount++;
1997 vertexStream.format = Cogs::DataFormat::R32G32_FLOAT;
2001 streamsState.colorStreamCount++;
2003 if (accessor.type == AccessorType::Vec3) {
2004 switch (accessor.componentType) {
2005 case AccessorComponentType::Float: vertexStream.format = Cogs::DataFormat::R32G32B32_FLOAT;
return true;
2006 case AccessorComponentType::UnsignedByte: vertexStream.format = Cogs::DataFormat::R8G8B8_UNORM;
return true;
2007 case AccessorComponentType::UnsignedShort: vertexStream.format = Cogs::DataFormat::R16G16B16_UNORM;
return true;
2011 else if (accessor.type == AccessorType::Vec4) {
2012 switch (accessor.componentType) {
2013 case AccessorComponentType::Float: vertexStream.format = Cogs::DataFormat::R32G32B32A32_FLOAT;
return true;
2014 case AccessorComponentType::UnsignedByte: vertexStream.format = Cogs::DataFormat::R8G8B8A8_UNORM;
return true;
2015 case AccessorComponentType::UnsignedShort: vertexStream.format = Cogs::DataFormat::R16G16B16A16_UNORM;
return true;
2019 LOG_ERROR(logger,
"%.*s: Illegal COLOR accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
2023 if (vertexStream.semanticIndex == 0) {
2024 streamsState.jointsStreamCount = 1;
2026 vertexStream.semanticIndex = 4;
2027 if (accessor.type == AccessorType::Vec4) {
2028 switch (accessor.componentType) {
2029 case AccessorComponentType::UnsignedByte: vertexStream.format = Cogs::DataFormat::R8G8B8A8_UINT;
return true;
2030 case AccessorComponentType::UnsignedShort: vertexStream.format = Cogs::DataFormat::R16G16B16A16_UINT;
return true;
2031 case AccessorComponentType::Float: vertexStream.format = Cogs::DataFormat::R32G32B32A32_FLOAT;
return true;
2035 LOG_ERROR(logger,
"%.*s: Illegal JOINTS accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
2041 if (vertexStream.semanticIndex == 0) {
2043 vertexStream.semanticIndex = 5;
2044 if (accessor.type == AccessorType::Vec4) {
2045 switch (accessor.componentType) {
2046 case AccessorComponentType::Float: vertexStream.format = Cogs::DataFormat::R32G32B32A32_FLOAT;
return true;
2047 case AccessorComponentType::UnsignedByte: vertexStream.format = Cogs::DataFormat::R8G8B8A8_UNORM;
return true;
2048 case AccessorComponentType::UnsignedShort: vertexStream.format = Cogs::DataFormat::R16G16B16A16_UNORM;
return true;
2052 LOG_ERROR(logger,
"%.*s: Illegal WEIGHTS accessor type %s of %s", StringViewFormat(loadData.path), accessorTypeName[
size_t(accessor.type)], accessorComponentTypeName[
size_t(accessor.componentType)]);
2065 LOG_WARNING_ONCE(logger,
"Unrecognized attribute semantic %.*s", StringViewFormat(key));
2071 for (
const auto& attributeIt : attributeObject) {
2073 uint32_t accessorIx = 0;
2074 if (!getUint(loadData, accessorIx, attributeIt)) {
2079 if (meshoptDecomp.isCompressed(loadData, accessorIx)) {
2080 meshoptDecomp.decompress(loadData, accessorIx);
2085 if (getVertexElement(loadData, streamsState, vertexStream, toView(attributeIt.name), accessorIx)) {
2086 vertexStreams.push_back(vertexStream);
2090 if (!streamsState.positionPresent) {
2091 LOG_ERROR(logger,
"%.*s: Primitive has no POSITION attribute", StringViewFormat(loadData.path));
2095 for (
const auto& stream : vertexStreams) {
2096 if (stream.dataView.count != streamsState.positionCount) {
2097 LOG_ERROR(logger,
"%.*s: Primitive has attributes with variable counts", StringViewFormat(loadData.path));
2108 LOG_ERROR(logger,
"%.*s: Vertex tangent generator only supports triangle lists (type is %d)", StringViewFormat(loadData.path), primitiveType);
2111 assert(streamsState.positionPresent);
2112 assert(streamsState.normalPresent);
2113 assert(streamsState.texcoordStreamCount);
2115 const size_t byteSize =
sizeof(glm::vec3) * streamsState.positionCount;
2119 memBuf->resize(byteSize);
2120 glm::vec3* tangents = (glm::vec3*) memBuf->data();
2121 std::memset(
static_cast<void*
>(tangents), 0, byteSize);
2123 size_t count = streamsState.indexType != AccessorComponentType::None ? streamsState.indexData.count : streamsState.positionCount;
2124 for (
size_t i = 0; i + 2 < count; i += 3) {
2125 size_t ix[3] = { 0,0,0 };
2126 switch (streamsState.indexType) {
2127 case AccessorComponentType::None:
for (
size_t k = 0; k < 3; k++) { ix[k] = i + k; }
break;
2128 case AccessorComponentType::UnsignedByte:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2129 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;
2130 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;
2131 default: assert(
false);
return false;
2134 glm::vec3 a = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[0]);
2135 glm::vec3 b = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[1]);
2136 glm::vec3 c = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[2]);
2137 glm::vec2 ta = *(
const glm::vec2*)(streamsState.texcoordData.data + streamsState.texcoordData.stride * ix[0]);
2138 glm::vec2 tb = *(
const glm::vec2*)(streamsState.texcoordData.data + streamsState.texcoordData.stride * ix[1]);
2139 glm::vec2 tc = *(
const glm::vec2*)(streamsState.texcoordData.data + streamsState.texcoordData.stride * ix[2]);
2156 float den = 1.f / (u0.x * u1.y - u0.y * u1.x);
2157 glm::vec3 t0 = u1.y * den * v0 - u0.y * den * v1;
2159 tangents[ix[0]] += t0;
2160 tangents[ix[1]] += t0;
2161 tangents[ix[2]] += t0;
2164 for (
size_t i = 0; i < streamsState.positionCount; i++) {
2165 const glm::vec3 normal = *(
const glm::vec3*)(streamsState.normalData.data + streamsState.normalData.stride * i);
2166 tangents[i] = glm::normalize(tangents[i] - glm::dot(normal, tangents[i]) * normal);
2171 .data = (
const uint8_t*) memBuf->data(),
2172 .stride =
sizeof(glm::vec3),
2173 .count = streamsState.positionCount
2175 .format = Cogs::DataFormat::R32G32B32_FLOAT,
2186 LOG_ERROR(logger,
"%.*s: Vertex normal generator only supports triangle lists", StringViewFormat(loadData.path));
2189 assert(streamsState.positionPresent);
2191 const size_t byteSize =
sizeof(glm::vec3) * streamsState.positionCount;
2195 memBuf->resize(byteSize);
2196 glm::vec3* normals = (glm::vec3*) memBuf->data();
2197 std::memset(
static_cast<void*
>(normals), 0, byteSize);
2199 size_t count = streamsState.indexSize ? streamsState.indexData.count : streamsState.positionCount;
2200 for (
size_t i = 0; i + 2 < count; i += 3) {
2201 size_t ix[3] = { 0,0,0 };
2202 switch (streamsState.indexSize) {
2203 case 0:
for (
size_t k = 0; k < 3; k++) { ix[k] = i + k; }
break;
2204 case 1:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2205 case 2:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(
const uint16_t*)(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2206 case 4:
for (
size_t k = 0; k < 3; k++) { ix[k] = *(
const uint32_t*)(streamsState.indexData.data + streamsState.indexData.stride * (i + k)); }
break;
2207 default: assert(
false);
return false;
2209 glm::vec3 a = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[0]);
2210 glm::vec3 b = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[1]);
2211 glm::vec3 c = *(
const glm::vec3*)(streamsState.positionData.data + streamsState.positionData.stride * ix[2]);
2213 const glm::vec3 l1 = clockwise ? a - b : b - a;
2214 const glm::vec3 l2 = clockwise ? a - c : c - a;
2215 glm::vec3 n = glm::cross(l1, l2);
2217 if (glm::length(n) <= 0.0) {
2218 LOG_WARNING_ONCE(logger,
"Calculated normal is not valid (normalIdx=%zu)", i);
2219 n = glm::vec3(1, 0, 0);
2222 normals[ix[0]] += n;
2223 normals[ix[1]] += n;
2224 normals[ix[2]] += n;
2227 for (
size_t i = 0; i < streamsState.positionCount; i++) {
2228 normals[i] = glm::normalize(normals[i]);
2233 .data = (
const uint8_t*) normals,
2234 .stride =
sizeof(glm::vec3),
2235 .count = streamsState.positionCount
2237 .format = Cogs::DataFormat::R32G32B32_FLOAT,
2247 uint32_t dstStride = 0;
2248 switch (streamsState.indexType) {
2250 case AccessorComponentType::UnsignedByte: dstStride = 2; streamsState.indexSize = 1;
break;
2251 case AccessorComponentType::UnsignedShort: dstStride = 2; streamsState.indexSize = dstStride;
break;
2252 case AccessorComponentType::UnsignedInt: dstStride = 4; streamsState.indexSize = dstStride;
break;
2254 LOG_ERROR(logger,
"%.*s: Illegal index data component type %s", StringViewFormat(loadData.path), accessorComponentTypeName[
size_t(streamsState.indexType)]);
2258 if (uint8_t* dst = mesh->mapStream(VertexDataType::Indexes, 0, streamsState.indexData.count, dstStride,
true); dst) {
2259 switch (streamsState.indexType) {
2260 case AccessorComponentType::UnsignedByte: stridedCopy<1>(dst, 2, streamsState.indexData.data, streamsState.indexData.stride, streamsState.indexData.count);
break;
2261 case AccessorComponentType::UnsignedShort: stridedCopy<2>(dst, 2, streamsState.indexData.data, streamsState.indexData.stride, streamsState.indexData.count);
break;
2262 case AccessorComponentType::UnsignedInt: stridedCopy<4>(dst, 4, streamsState.indexData.data, streamsState.indexData.stride, streamsState.indexData.count);
break;
2263 default: assert(
false);
break;
2265 mesh->unmap(VertexDataType::Indexes);
2269 mesh->setCount(streamsState.indexData.count);
2279 const Cogs::FormatInfo* formatInfo = Cogs::getFormatInfo(vertexStream.format);
2282 vertexStream.formatSize = formatInfo->
blockSize;
2286 std::vector<Cogs::VertexElement> vertexElements;
2288 offset = (offset + 3) & ~3;
2289 vertexStream.offset = offset;
2291 .
offset = uint16_t(offset),
2292 .format = vertexStream.format,
2293 .semantic = vertexStream.semantic,
2294 .semanticIndex = uint16_t(vertexStream.semanticIndex),
2298 offset += vertexStream.formatSize;
2301 vertexFormat = Cogs::VertexFormats::createVertexFormat(vertexElements.data(), vertexElements.size());
2303 LOG_ERROR(logger,
"%.*s: Failed to create vertex format with %zu elements", StringViewFormat(loadData.path), vertexElements.size());
2308 size_t stride = Cogs::getSize(vertexFormat);
2309 if (uint8_t* base = mesh->mapStream(VertexDataType::Interleaved0, vertexFormat, 0, streamsState.positionCount, stride,
true)) {
2310 for (
const VertexStream& vertexStream : vertexStreams) {
2311 uint8_t* dst = base + vertexStream.offset;
2312 switch (vertexStream.format) {
2313 case Cogs::DataFormat::R32G32B32A32_FLOAT:
2314 assert(vertexStream.formatSize == 16);
2315 stridedCopy<16>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2318 case Cogs::DataFormat::R32G32B32_FLOAT:
2319 assert(vertexStream.formatSize == 12);
2320 stridedCopy<12>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2323 case Cogs::DataFormat::R32G32_FLOAT:
2324 case Cogs::DataFormat::R16G16B16A16_UNORM:
2325 case Cogs::DataFormat::R16G16B16A16_UINT:
2326 assert(vertexStream.formatSize == 8);
2327 stridedCopy<8>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2330 case Cogs::DataFormat::R16G16B16_UNORM:
2331 case Cogs::DataFormat::R16G16B16_UINT:
2332 case Cogs::DataFormat::R16G16B16_SINT:
2333 case Cogs::DataFormat::R16G16B16_SNORM:
2334 assert(vertexStream.formatSize == 6);
2335 stridedCopy<6>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2338 case Cogs::DataFormat::R16G16_UNORM:
2339 case Cogs::DataFormat::R16G16_UINT:
2340 case Cogs::DataFormat::R16G16_SNORM:
2341 case Cogs::DataFormat::R16G16_SINT:
2342 case Cogs::DataFormat::R8G8B8A8_UNORM:
2343 case Cogs::DataFormat::R8G8B8A8_UINT:
2344 assert(vertexStream.formatSize == 4);
2345 stridedCopy<4>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2348 case Cogs::DataFormat::R8G8B8_UNORM:
2349 case Cogs::DataFormat::R8G8B8_UINT:
2350 case Cogs::DataFormat::R8G8B8_SINT:
2351 case Cogs::DataFormat::R8G8B8_SNORM:
2352 assert(vertexStream.formatSize == 3);
2353 stridedCopy<3>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2356 case Cogs::DataFormat::R8G8_UNORM:
2357 assert(vertexStream.formatSize == 3);
2358 stridedCopy<2>(dst, stride, vertexStream.dataView.data, vertexStream.dataView.stride, streamsState.positionCount);
2362 assert(
false &&
"Unhandled DataFormat");
2366 mesh->unmap(VertexDataType::Interleaved0);
2374 std::vector<VertexStream> vertexStreams;
2377 bool needsNormals =
true;
2378 if (
auto materialIt = primitiveObject.FindMember(
"material"); materialIt != primitiveObject.MemberEnd()) {
2379 uint32_t materialIx = 0;
2380 if (!getUint(loadData, materialIx, *materialIt)) {
2381 LOG_ERROR(logger,
"The 'meshes.primitives.material' is not an integer");
2384 if (materialIx >= 0) {
2385 const Object& materialObject = loadData.materialsArray[materialIx];
2386 if (materialObject.HasMember(
"extensions") && materialObject[
"extensions"].HasMember(
"KHR_materials_unlit")) {
2387 needsNormals =
false;
2393 bool useDracoDecompression =
false;
2397 if (primitiveObject.HasMember(
"extensions") && primitiveObject[
"extensions"].HasMember(
"KHR_draco_mesh_compression")) {
2398 auto extSection = primitiveObject[
"extensions"].GetObject();
2399 auto dracoSection = extSection[
"KHR_draco_mesh_compression"].GetObject();
2400 draco.initAttributes(dracoSection);
2401 useDracoDecompression =
true;
2404 if (
const auto& attributesIt = primitiveObject.FindMember(
"attributes"); attributesIt != primitiveObject.MemberEnd()) {
2405 if (!checkIsObject(loadData, *attributesIt)) {
2409 if (useDracoDecompression) {
2410 if (!draco.decompress(loadData, streamsState, vertexStreams, attributesIt->value.GetObject(), !needsNormals)) {
2415 if (!getVertexAttributes(loadData, streamsState, vertexStreams, attributesIt->value.GetObject(), meshoptDecomp)) {
2421 LOG_ERROR(logger,
"%.*s: Primitive has no attribute property", StringViewFormat(loadData.path));
2426 bool isIndexed =
false;
2427 uint32_t indicesAccessorIx = 0;
2429 if (useDracoDecompression) {
2433 if (
auto indicesIt = primitiveObject.FindMember(
"indices"); indicesIt != primitiveObject.MemberEnd()) {
2434 if (!getUint(loadData, indicesAccessorIx, *indicesIt)) {
2435 LOG_ERROR(logger,
"The 'meshes.primitives.indices' is not an integer");
2439 if (loadData.accessors.size() <= indicesAccessorIx) {
2440 LOG_ERROR(logger,
"%.*s: Illegal accessor index %u", StringViewFormat(loadData.path), indicesAccessorIx);
2444 const GltfAccessor& accessor = loadData.accessors[indicesAccessorIx];
2445 streamsState.indexType = accessor.componentType;
2447 if (meshoptDecomp.isCompressed(loadData, indicesAccessorIx)) {
2448 bool ok = meshoptDecomp.decompress(loadData, indicesAccessorIx);
2454 if (!getBufferViewData(loadData, streamsState.indexData, accessor)) {
2463 if (
auto modeIt = primitiveObject.FindMember(
"mode"); modeIt != primitiveObject.MemberEnd()) {
2465 if (!getUint(loadData, mode, *modeIt)) {
2475 case 2: LOG_ERROR(logger,
"%.*s: Unsupported primitive mode 2: LINE_LOOP", StringViewFormat(loadData.path));
return false;
2476 case 6: LOG_ERROR(logger,
"%.*s: Unsupported primitive mode 6: TRIANGLE_FAN", StringViewFormat(loadData.path));
return false;
2477 default: LOG_ERROR(logger,
"%.*s: Illegal primitive mode %u", StringViewFormat(loadData.path), mode);
return false;
2483 if (
auto targetsIt = primitiveObject.FindMember(
"targets"); targetsIt != primitiveObject.MemberEnd()) {
2484 if (!checkIsArray(loadData, *targetsIt)) {
2487 if (!targetsIt->value.GetArray().Empty()) {
2489 LOG_WARNING_ONCE(logger,
"%.*s: Morph targets not implemented yet, ignoring.", StringViewFormat(loadData.path));
2494 bool isSkinned = streamsState.jointsStreamCount != 0;
2495 bool albedoPerVertex = streamsState.colorStreamCount != 0;
2497 if (
auto materialIt = primitiveObject.FindMember(
"material"); materialIt != primitiveObject.MemberEnd()) {
2498 uint32_t materialIx = 0;
2499 if (!getUint(loadData, materialIx, *materialIt)) {
2500 LOG_ERROR(logger,
"The 'meshes.primitives.material' is not an integer");
2503 if (!getMaterialHandle(loadData, model, modelMaterial, materialIx, isSkinned, albedoPerVertex,
false)) {
2508 if (!getMaterialHandle(loadData, model, modelMaterial, -1, isSkinned, albedoPerVertex,
false)) {
2512 assert(modelMaterial);
2513 modelMeshInstance.materialIx = modelMaterial->modelMaterialIx;
2516 if (streamsState.texcoordStreamCount < modelMaterial->texCoordSets) {
2517 LOG_ERROR(logger,
"%.*s: Material requires texture coordinates, something which the mesh doesn't have", StringViewFormat(loadData.path));
2522 if (modelMaterial->needsNormals && !streamsState.normalPresent) {
2523 if (!generateVertexNormals(loadData, streamsState, vertexStreams, primitiveType, clockwise)) {
2529 if (modelMaterial->needsTangents && !streamsState.tangentPresent) {
2530 if (!generateVertexTangents(loadData, streamsState, vertexStreams, primitiveType, clockwise)) {
2536 if (loadData.optimizationLevel >= 1) {
2537 std::vector<VertexStream> culledVertexStreams;
2539 for (
auto& stream : vertexStreams) {
2541 switch (stream.semantic) {
2543 keep = stream.semanticIndex == 0;
2546 keep = modelMaterial->needsNormals ? stream.semanticIndex == 0 :
false;
2549 if (stream.semanticIndex == 4 || stream.semanticIndex == 5) {
2553 keep = albedoPerVertex && stream.semanticIndex == 0;
2557 keep = stream.semanticIndex < modelMaterial->texCoordSets;
2560 keep = stream.semanticIndex == 0;
2566 culledVertexStreams.emplace_back(stream);
2569 debugMessageOnce(loadData,
"Culled unused vertex stream with semantic=%u:%u",
unsigned(stream.semantic), stream.semanticIndex);
2572 vertexStreams = std::move(culledVertexStreams);
2577 size_t N = vertexStreams.size();
2578 std::vector<VertexStream> sortedStreams(N);
2579 for (
const auto& stream : vertexStreams) {
2582 (
unsigned(stream.semantic) <
unsigned(sortedStreams[i - 1].semantic)) ||
2583 ((
unsigned(stream.semantic) ==
unsigned(sortedStreams[i - 1].semantic) && (stream.semanticIndex < sortedStreams[i - 1].semanticIndex)))))
2585 sortedStreams[i] = sortedStreams[i - 1];
2588 sortedStreams[i] = stream;
2590 vertexStreams = std::move(sortedStreams);
2598 clockwise ? uint32_t(1) : uint32_t(0),
2599 isIndexed ? uint32_t(~0) : indicesAccessorIx);
2600 for (
const VertexStream& vertexStream : vertexStreams) {
2601 meshHash =
hash(vertexStream, meshHash);
2604 if (
auto it = loadData.modelMeshCache.find(meshHash); it != loadData.modelMeshCache.end()) {
2605 debugMessageOnce(loadData,
"Found duplicate primitive description, recycling mesh");
2606 modelMeshInstance.mesh = it->second;
2611 modelMeshInstance.mesh.meshIx = uint32_t(model.meshes.size());
2612 model.meshes.push_back(mesh.getHandle());
2613 if (!setVertexAttributes(loadData, mesh, streamsState, vertexStreams)) {
2622 mesh->setMeshFlag(MeshFlags::Skinned);
2625 mesh->primitiveType = primitiveType;
2626 mesh->setBounds(streamsState.bbox);
2627 modelMeshInstance.mesh.bboxIx = uint32_t(model.bounds.size());
2628 model.bounds.push_back(streamsState.bbox);
2631 if (!setMeshIndices(loadData, mesh, streamsState)) {
2636 mesh->setCount(streamsState.positionCount);
2638 modelMeshInstance.mesh.vertexCount = mesh->getCount();
2643 assert(
size_t(skinIx) < loadData.skins.size());
2644 const GltfSkin& skin = loadData.skins[skinIx];
2645 size_t count = skin.joints.size();
2646 if (kMaxBones < count) {
2647 LOG_WARNING_ONCE(logger,
"%.*s: Unable to handle bone count %zu over %zu", StringViewFormat(loadData.path), count, kMaxBones);
2651 std::span<uint32_t> poseIndexes = mesh->mapPoseIndexes(
static_cast<uint32_t
>(count));
2652 for (
size_t i = 0; i < count; i++) {
2653 uint32_t jointIx = skin.joints[i];
2654 poseIndexes[i] = loadData.nodes[jointIx].bone_index;
2658 loadData.modelMeshCache[meshHash] = modelMeshInstance.mesh;
2664 [[nodiscard]]
bool buildMeshes(
GltfModelDefinition& loadData, std::vector<ModelMeshInstance>& modelMeshInstances,
Model& model, int32_t skinIx, int32_t gltfMeshIx,
bool clockwise,
MeshoptDecompressor & meshoptDecomp)
2666 if (loadData.meshesArray.size() <
size_t(gltfMeshIx)) {
2667 LOG_ERROR(logger,
"%.*s: Illegal gltf mesh index %u", StringViewFormat(loadData.path), gltfMeshIx);
2671 const Object& meshObject = loadData.meshesArray[gltfMeshIx];
2673 if (
auto primitivesIt = meshObject.FindMember(
"primitives"); primitivesIt != meshObject.MemberEnd()) {
2674 if (!checkIsArray(loadData, *primitivesIt)) {
2678 const Array& primitivesArray = primitivesIt->value.GetArray();
2679 for (
auto& primitiveItem : primitivesArray) {
2680 if (!checkIsObject(loadData,
"mesh primitive item", primitiveItem)) {
2683 if (!buildPrimitiveMesh(loadData, modelMeshInstances.emplace_back(), model, primitiveItem.GetObject(), skinIx, clockwise, meshoptDecomp)) {
2689 LOG_ERROR(logger,
"%.*s: Mesh %u has no primitives property", StringViewFormat(loadData.path), gltfMeshIx);
2696 bool loadNode(
Context* context,
2700 uint32_t parent_part,
2701 const glm::mat4& parent_transform,
2704 const bool clockwise = glm::determinant(node.mat) < 0.0f;
2705 const glm::mat4& localTransform = node.mat;
2706 const glm::mat4 globalTransform = parent_transform * localTransform;
2708 std::string nodeName = std::to_string(node.node_index) +
"_" + node.name;
2709 uint32_t partIndex = (uint32_t)model.parts.size();
2710 node.part_index = partIndex;
2712 ModelPart& nodePart = model.parts.emplace_back();
2713 nodePart.parentIndex = parent_part;
2714 model.setPartTransform(nodePart, localTransform);
2715 model.setPartName(nodePart, nodeName);
2717 if (node.mesh != (uint32_t)-1) {
2718 std::vector<ModelMeshInstance> meshes;
2719 if (!buildMeshes(definition, meshes, model, node.skin, node.mesh, clockwise, meshoptDecomp)) {
2724 if (meshes.size() == 1) {
2725 nodePart.meshIndex = meshes[0].mesh.meshIx;
2726 nodePart.materialIndex = meshes[0].materialIx;
2727 nodePart.boundsIndex = meshes[0].mesh.bboxIx;
2728 nodePart.vertexCount = meshes[0].mesh.vertexCount;
2732 for (
auto& instance : meshes) {
2733 ModelPart& part = model.parts.emplace_back();
2734 part.parentIndex = partIndex;
2735 part.meshIndex = instance.mesh.meshIx;
2736 part.materialIndex = instance.materialIx;
2737 part.boundsIndex = instance.mesh.bboxIx;
2738 part.vertexCount = instance.mesh.vertexCount;
2743 for (uint32_t child_index : node.children) {
2744 if (!loadNode(context, definition, definition.nodes[child_index], model, partIndex, globalTransform, meshoptDecomp)) {
2754 if (definition.animations.size()) model.animation = context->animationManager->create();
2756 auto& animation = *model.animation.
resolve();
2757 auto& skeleton = model.skeleton;
2759 for (
auto& animationValue : definition.animations) {
2760 auto& clip = animation.clips.emplace_back();
2762 clip.name = animationValue.name;
2763 clip.duration = 0.0f;
2764 clip.resolution = 1.0f;
2766 uint32_t prev_node = (
decltype(prev_node))-1;
2767 for (uint32_t i = 0; i < animationValue.channels.size(); i++) {
2768 GltfChannel& channel = animationValue.channels[i];
2769 GltfSampler& sampler = animationValue.samplers[channel.sampler];
2770 uint32_t nodeIndex = channel.node;
2771 GltfNode& node = definition.nodes[nodeIndex];
2772 uint32_t boneIndex = node.bone_index;
2774 if (sampler.interpolation !=
"" && sampler.interpolation !=
"LINEAR")
2775 LOG_ERROR(logger,
"Unable to handle animation interpolation %s", sampler.interpolation.c_str());
2777 auto& inputAccessor = definition.accessors[sampler.input];
2780 if (meshoptDecomp.isCompressed(definition, sampler.input)) {
2781 bool ok = meshoptDecomp.decompress(definition, sampler.input);
2788 if (!getBufferViewData(definition, dataView, inputAccessor)) {
2792 if (!dataView.data || !dataView.count) {
2796 float t_max = inputAccessor.max.s_float[0];
2798 clip.duration = std::max(t_max, clip.duration);
2800 if (prev_node != nodeIndex) {
2801 clip.tracks.push_back({});
2803 prev_node = nodeIndex;
2804 auto& track = clip.tracks.back();
2805 track.boneIndex = boneIndex;
2807 skeleton.bones[boneIndex].animated =
true;
2809 auto getTime = [](uint32_t i,
const uint8_t* iData,
const size_t iStride) {
2810 return *
reinterpret_cast<const float*
>(iData + iStride * i);
2813 if (definition.accessors.size() <= sampler.output) {
2817 const GltfAccessor& outputAccessor = definition.accessors[sampler.output];
2820 if (meshoptDecomp.isCompressed(definition, sampler.output)) {
2821 bool ok = meshoptDecomp.decompress(definition, sampler.output);
2828 if (!getBufferViewData(definition, oDataView, outputAccessor)) {
2832 if (channel.path ==
"translation") {
2833 assert(outputAccessor.type == AccessorType::Vec3);
2834 track.translations.resize(dataView.count);
2836 for (uint32_t j = 0; j < dataView.count; j++) {
2837 const float time = getTime(j, dataView.data, dataView.stride);
2838 glm::vec3 translation = *
reinterpret_cast<const glm::vec3*
>(oDataView.data + oDataView.stride * j);
2842 else if (channel.path ==
"scale") {
2843 assert(outputAccessor.type == AccessorType::Vec3);
2844 track.scales.resize(dataView.count);
2846 for (uint32_t j = 0; j < dataView.count; j++) {
2847 const float time = getTime(j, dataView.data, dataView.stride);
2848 glm::vec3 scale = *
reinterpret_cast<const glm::vec3*
>(oDataView.data + oDataView.stride * j);
2849 track.scales[j] =
ScaleKey{ time, scale };
2852 else if (channel.path ==
"rotation") {
2853 assert(outputAccessor.type == AccessorType::Vec4);
2854 track.rotations.resize(dataView.count);
2856 if (outputAccessor.componentType == AccessorComponentType::Float) {
2857 for (uint32_t j = 0; j < dataView.count; j++) {
2858 const float time = getTime(j, dataView.data, dataView.stride);
2859 glm::vec4 rot = *
reinterpret_cast<const glm::vec4*
>(oDataView.data + oDataView.stride * j);
2860 track.rotations[j] =
RotationKey{ time, glm::quat(rot.w, rot.x, rot.y, rot.z) };
2865 LOG_ERROR(logger,
"Unknown animation channel path: %s", channel.path.c_str());
2869 if (model.animation) {
2870 model.animation->skeleton = model.skeleton;
2881 if (
Variable* enable = context->
variables->get(enableVariableName); !enable->isEmpty() && enable->getBool() ==
false) {
2888 if (resourcePath.starts_with(
"http")) {
2889 size_t paramPos = resourcePath.find(
'?');
2890 if (paramPos != std::string::npos) {
2891 resourcePath = resourcePath.substr(0, paramPos);
2895 auto extension = Cogs::IO::extension(resourcePath);
2896 if (extension.starts_with(
".gltf"))
return true;
2897 if (extension.starts_with(
".glb"))
return true;
2898 if (extension.starts_with(
".b3dm"))
return true;
2915 uint32_t featureTableJSONLength;
2916 uint32_t featureTableBinaryLength;
2917 uint32_t batchTableJSONLength;
2918 uint32_t batchTableBinaryLength;
2921 const uint32_t b3dmMagic = uint32_t(
'b') + uint32_t(
'3' << 8) + uint32_t(
'd' << 16) + uint32_t(
'm' << 24);
2922 B3DMHeader* header =
reinterpret_cast<B3DMHeader*
>(content);
2924 if (header->magic != b3dmMagic) {
2928 assert(header->length <= size &&
"Invalid length value in header");
2930 if (header->version != 1) {
2931 LOG_ERROR(logger,
"The B3DM file is not version 1.0");
2935 uint32_t featureTableStart =
sizeof(B3DMHeader);
2936 uint32_t batchTableStart = featureTableStart + header->featureTableJSONLength + header->featureTableBinaryLength;
2937 uint32_t glbStart = batchTableStart + header->batchTableJSONLength + header->batchTableBinaryLength;
2939 assert(glbStart < size &&
"Offset out of bounds");
2943 bool GltfLoader::load(
Context* context,
const ModelLoadInfo& loadInfo, std::unique_ptr<Cogs::FileContents> contents)
2945 LOG_TRACE(logger,
"Loading model: '%s'", loadInfo.
resourceName.c_str());
2949 if (!content.size()) {
2950 LOG_ERROR(logger,
"Could not open file '%s'.", loadInfo.
resourcePath.c_str());
2957 const uint8_t* dataPtr = (uint8_t*)(content.data()) + fileOffset;
2966 struct GlbChunkHeader
2974 GlbChunkHeader header;
2978 JsonParseFlags flags = JsonParseFlags::NoCachedContent;
2982 const GlbHeader* header = (
const GlbHeader*) dataPtr;
2983 GltfModelDefinition loadData(context, loadInfo.
resourcePath);
2984 loadData.default_material = -1;
2985 if (
auto var = context->
variables->get(optimizeVariableName); !var->isEmpty()) {
2986 loadData.optimizationLevel = unsigned(std::max(0, var->getInt()));
2989 std::span<const uint8_t> glb_data;
2990 if (
sizeof(GlbHeader) <= content.size() && header->magic == 0x46546C67) {
2991 loadData.version = header->version * 100;
2993 if (header->version != 2) {
2994 LOG_ERROR(logger,
"Unable to load glb format %d", loadData.version);
2998 size_t offset =
sizeof(GlbHeader);
3000 while (offset < (content.size() - fileOffset)) {
3001 const GlbChunk* chunk = (GlbChunk*)(
reinterpret_cast<const char*
>(dataPtr) + offset);
3002 if (chunk->header.type == 0x4E4F534A)
3003 document = parseJson(
Cogs::StringView(chunk->data, chunk->header.length), flags);
3004 else if (chunk->header.type == 0x004E4942)
3005 glb_data = std::span((
const uint8_t*)chunk->data, (
const uint8_t*)chunk->data + chunk->header.length);
3007 LOG_ERROR(logger,
"Unknown glb chunk type 0x%x (index %d)", chunk->header.type, i);
3009 offset += chunk->header.length +
sizeof(GlbChunkHeader);
3014 document = parseJson(
Cogs::StringView(
reinterpret_cast<const char*
>(dataPtr), content.size() - fileOffset), flags);
3017 if (!document.IsObject()) {
3018 LOG_ERROR(logger,
"Could not load asset file %s: JSON root is not an object.", loadInfo.
resourceName.c_str());
3022 auto& modelManager = *context->modelManager;
3023 auto model = modelManager.lock(loadInfo.
handle);
3025 MeshoptDecompressor meshoptDecomp;
3027 auto root = document.GetObject();
3030 if (
const auto& assetItem = root.FindMember(
"asset"); assetItem != root.MemberEnd()) {
3031 if (!parseAsset(loadData, path, assetItem->value))
return false;
3034 LOG_ERROR(logger,
"%.*s: No required JSON asset member", StringViewFormat(path));
3038 if (
const auto& buffersItem = root.FindMember(
"buffers"); buffersItem != root.MemberEnd()) {
3039 if (!parseBuffers(context, loadData, path, buffersItem->value, glb_data)) {
3044 if (
const auto& bufferViewsItem = root.FindMember(
"bufferViews"); bufferViewsItem != root.MemberEnd()) {
3045 if (!parseBufferViews(loadData, path, bufferViewsItem->value, meshoptDecomp)) {
3050 if (
const auto& imagesItem = root.FindMember(
"images"); imagesItem != root.MemberEnd()) {
3051 if (!parseImages(context, loadData, imagesItem->value)) {
3056 if (
const auto& samplersItem = root.FindMember(
"samplers"); samplersItem != root.MemberEnd()) {
3057 if (!parseSamplers(loadData, path, samplersItem->value)) {
3062 if (
const auto& texturesItem = root.FindMember(
"textures"); texturesItem != root.MemberEnd()) {
3063 if (!parseTextures(loadData, path, texturesItem->value)) {
3068 if (
auto materialsItem = root.FindMember(
"materials"); materialsItem != root.MemberEnd()) {
3069 if (!checkIsArray(loadData, *materialsItem))
return false;
3070 for (
const Value& materialsElement : materialsItem->value.GetArray()) {
3071 if (!checkIsObject(loadData,
"materials element", materialsElement)) {
3074 loadData.materialsArray.emplace_back(materialsElement.GetObject());
3078 if (
auto meshesItem = root.FindMember(
"meshes"); meshesItem != root.MemberEnd()) {
3079 if (!checkIsArray(loadData, *meshesItem))
return false;
3080 for (
const Value& meshesElement : meshesItem->value.GetArray()) {
3081 if (!checkIsObject(loadData,
"meshes element", meshesElement)) {
3084 loadData.meshesArray.emplace_back(meshesElement.GetObject());
3088 for (
const auto& section : root) {
3089 switch (toView(section.name).
hash()) {
3102 if (!parseNodes(loadData, path, section.value)) return false;
3106 if (!parseScenes(loadData, path, section.value)) return false;
3110 loadData.load_scene = section.value.GetUint();
3114 if (!checkIsArray(loadData, section)) return false;
3115 if (!parseAccessors(loadData, path, section.value))
return false;
3119 if (!parseSkins(loadData, path, section.value)) return false;
3123 if (!parseAnimations(loadData, path, section.value)) return false;
3127 for (auto& extension : section.value.GetArray()) {
3128 if (extension.GetString() ==
Cogs::StringView(
"KHR_materials_pbrSpecularGlossiness") ||
3143 LOG_ERROR(logger,
"Unknown required extension: %s", extension.GetString());
3151 if (section.value.IsArray()) {
3152 for (
auto& extension : section.value.GetArray()) {
3153 if (extension.GetString() ==
Cogs::StringView(
"KHR_materials_pbrSpecularGlossiness") ||
3165 LOG_WARNING(logger,
"Unknown extension: %s", extension.GetString());
3169 else if (section.value.IsObject()) {
3170 for (
const auto& extension : section.value.GetObject()) {
3171 LOG_WARNING(logger,
"Unknown extension: %s", extension.name.GetString());
3175 LOG_WARNING(logger,
"Unknown extension: %s", section.value.GetString());
3180 LOG_WARNING(logger,
"%.*s: Unrecognized root key %s", StringViewFormat(path), section.name.GetString());
3185 uint32_t sceneIndex = 0;
3186 for (
auto& scene : loadData.scenes) {
3187 if (sceneIndex > 1) {
3188 LOG_ERROR(logger,
"Loader unable to handle more than one scene.");
3192 model->setName(scene.name);
3196 auto& root_ = model->parts.emplace_back();
3197 model->setPartTransform(root_, glm::rotate(glm::mat4(1.0f), glm::half_pi<float>(), glm::vec3(1, 0, 0)));
3198 model->setPartName(root_,
"CogsRoot");
3201 glm::mat4 rootTransform(1.0f);
3203 for (uint32_t nodeIndex : scene.nodes) {
3204 assert(nodeIndex < loadData.nodes.size());
3206 if (!loadBone(loadData, loadData.nodes[nodeIndex], model->skeleton, (
size_t)-1, rootTransform)) {
3210 if (!loadJoints(loadData, loadData.nodes[nodeIndex], model->skeleton, meshoptDecomp)) {
3215 if (!loadNode(context, loadData, loadData.nodes[nodeIndex], *model, 0, rootTransform, meshoptDecomp)) {
3220 if (!loadAnimation(context, loadData, *model, meshoptDecomp)) {
3225 for (
auto const & it : loadData.debugMessages) {
3226 LOG_TRACE(logger,
"%s (x %u)", it.first.c_str(), it.second);
3231 bool GltfLoader::load(Context* context,
const ModelLoadInfo& loadInfo)
3233 JsonParseFlags flags = JsonParseFlags::NoCachedContent;
3235 const auto resourceFlags = ResourceStoreFlags::None;
3237 const auto resourceFlags = (flags & JsonParseFlags::NoCachedContent) == JsonParseFlags::NoCachedContent
3239 : ResourceStoreFlags::None;
3242 std::unique_ptr<Cogs::FileContents> contents;
3244 if (loadInfo.protocol == ResourceProtocol::Archive) {
3246 ResourceBuffer buffer = context->resourceStore->getResourceContents(loadInfo.resourcePath);
3247 if (!buffer.buffer->data()) {
3248 LOG_ERROR(logger,
"Could not load resource data for model %s.", loadInfo.resourcePath.c_str());
3251 contents = std::make_unique<Cogs::Platform::ResourceBufferBackedFileContents>(buffer, loadInfo.resourcePath, Cogs::FileContentsHints::None);
3255 Cogs::FileHandle::Ptr file = Cogs::IO::openFile(loadInfo.resourcePath, Cogs::FileHandle::OpenMode::OpenAlways, Cogs::FileHandle::AccessMode::Read);
3257 LOG_ERROR(logger,
"Could not open file %s for mapping.", loadInfo.resourcePath.c_str());
3261 size_t size = Cogs::IO::fileSize(file);
3263 LOG_ERROR(logger,
"File size invalid: %zd.", size);
3267 const uint8_t* ptr =
static_cast<const uint8_t*
>(Cogs::IO::mapFile(file, Cogs::FileHandle::ProtectionMode::ReadOnly, 0, size));
3269 LOG_ERROR(logger,
"Could not map file %s for reading model data.", loadInfo.resourcePath.c_str());
3273 contents = std::make_unique<Cogs::MMapBackedFileContents>(ptr, size, file, loadInfo.resourcePath, Cogs::FileContentsHints::None);
3278 const ResourceBuffer content = context->resourceStore->getResourceContents(path, resourceFlags);
3280 return load(context, loadInfo, std::move(contents));
3283 inline GltfModelDefinition::GltfModelDefinition(Context* context,
Cogs::StringView path) :
3287 timer = Cogs::Timer::startNew();
3288 rootTask = context->taskManager->createGroup(context->taskManager->ResourceQueue);
3291 inline GltfModelDefinition::~GltfModelDefinition()
3293 context->taskManager->wait(rootTask);
3294 for (
auto& image : images) {
3295 if (image.decoded.data) {
3296 stbi_image_free(image.decoded.data);
3297 image.decoded.data =
nullptr;
3300 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.
ResourceProxy< Mesh, ResourceManager > ResourceProxy
Type of resource proxy objects, specialized on the type of resource.
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.
@ 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.
EPrimitiveType
Primitive type enumeration.
@ TriangleStrip
Triangle strip.
@ PointList
List of points.
@ TriangleList
List of triangles.
@ 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.