Skip to content

Commit d496a9c

Browse files
AssetLoader: add metadata-only GLTF image loading
Allow GLTF documents to skip image decoding while preserving image metadata. Keep external images as URI-only metadata, avoid copying buffer-view image data, and copy data URI payloads only because tinygltf supplies them through temporary callback storage.
1 parent ac38e67 commit d496a9c

3 files changed

Lines changed: 326 additions & 4 deletions

File tree

AssetLoader/interface/GLTFLoader.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,17 @@ struct DocumentLoadInfo
756756

757757
/// Optional resource manager to use when loading images referenced by the document.
758758
ResourceManager* pResourceManager = nullptr;
759+
760+
/// Whether image data should be decoded into pixels while loading the document.
761+
///
762+
/// When this is false, the document still parses image and texture metadata.
763+
/// Images stored in buffer views remain addressable through tinygltf::Image::bufferView
764+
/// without duplicating their bytes in tinygltf::Image::image. External image files with
765+
/// known image extensions are not read by the document loader and remain addressable
766+
/// through tinygltf::Image::uri. Embedded data URI images are copied into
767+
/// tinygltf::Image::image as encoded image bytes because tinygltf supplies them
768+
/// through temporary callback storage.
769+
bool DecodeImages = true;
759770
};
760771

761772
/// Parsed GLTF document.

AssetLoader/src/GLTFLoader.cpp

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "Align.hpp"
4646
#include "GLTFBuilder.hpp"
4747
#include "GLTFUtilities.hpp"
48+
#include "StringTools.hpp"
4849
#include "FixedLinearAllocator.hpp"
4950
#include "DefaultRawMemoryAllocator.hpp"
5051

@@ -1735,14 +1736,49 @@ struct LoaderData
17351736
TextureCacheType* const pTextureCache;
17361737
ResourceManager* const pResourceMgr;
17371738

1738-
std::vector<RefCntAutoPtr<IObject>> TexturesHold;
1739+
std::vector<RefCntAutoPtr<IObject>> TexturesHold = {};
17391740

1740-
std::string BaseDir;
1741+
std::string BaseDir = {};
1742+
bool DecodeImages = true;
17411743

17421744
ModelCreateInfo::FileExistsCallbackType FileExists = nullptr;
17431745
ModelCreateInfo::ReadWholeFileCallbackType ReadWholeFile = nullptr;
17441746
};
17451747

1748+
bool IsImageFilePath(const std::string& FilePath)
1749+
{
1750+
const size_t DotPos = FilePath.find_last_of('.');
1751+
if (DotPos == std::string::npos || DotPos + 1 >= FilePath.length())
1752+
return false;
1753+
1754+
const char* Ext = &FilePath[DotPos + 1];
1755+
1756+
static constexpr const char* ImageExtensions[] =
1757+
{
1758+
"png",
1759+
"jpg",
1760+
"jpeg",
1761+
"dds",
1762+
"ktx",
1763+
"ktx2",
1764+
"bmp",
1765+
"gif",
1766+
"tga",
1767+
"hdr",
1768+
"pic",
1769+
"pnm",
1770+
"ppm",
1771+
"pgm",
1772+
};
1773+
for (const char* ImageExt : ImageExtensions)
1774+
{
1775+
if (StrCmpNoCase(Ext, ImageExt) == 0)
1776+
return true;
1777+
}
1778+
1779+
return false;
1780+
}
1781+
17461782
bool LoadImageData(tinygltf::Image* gltf_image,
17471783
const int gltf_image_idx,
17481784
std::string* error,
@@ -1952,13 +1988,63 @@ bool LoadImageData(tinygltf::Image* gltf_image,
19521988
return true;
19531989
}
19541990

1991+
bool LoadImageDataNoDecode(tinygltf::Image* gltf_image,
1992+
const int gltf_image_idx,
1993+
std::string* error,
1994+
std::string* warning,
1995+
int req_width,
1996+
int req_height,
1997+
const unsigned char* image_data,
1998+
int size,
1999+
void* user_data)
2000+
{
2001+
(void)warning;
2002+
(void)req_width;
2003+
(void)req_height;
2004+
(void)user_data;
2005+
2006+
if (gltf_image == nullptr)
2007+
return false;
2008+
2009+
gltf_image->image.clear();
2010+
2011+
if (gltf_image->bufferView >= 0)
2012+
{
2013+
// Embedded buffer-view image data is owned by tinygltf::Model::buffers.
2014+
// Keep the image as a view into that data and avoid duplicating it in image.image.
2015+
return true;
2016+
}
2017+
2018+
// At this point, a non-empty URI means an external image file. Leave it
2019+
// as URI-only metadata so higher-level loaders can fetch and decode it.
2020+
if (!gltf_image->uri.empty())
2021+
return true;
2022+
2023+
if (image_data == nullptr || size <= 0)
2024+
{
2025+
if (error != nullptr)
2026+
*error += FormatString("Missing encoded image data for image[", gltf_image_idx, "] name = '", gltf_image->name, "'");
2027+
return false;
2028+
}
2029+
2030+
// The remaining no-decode path is an embedded data URI. TinyGLTF supplies
2031+
// these encoded image bytes through temporary callback storage, so keep a copy.
2032+
gltf_image->image.assign(image_data, image_data + size);
2033+
2034+
return true;
2035+
}
2036+
19552037
bool FileExists(const std::string& abs_filename, void* user_data)
19562038
{
19572039
// FileSystem::FileExists() is a pretty slow function.
19582040
// Try to find the file in the cache first to avoid calling it.
19592041
if (LoaderData* pLoaderData = static_cast<LoaderData*>(user_data))
19602042
{
19612043
const std::string CacheId = FileSystem::SimplifyPath(abs_filename.c_str());
2044+
2045+
if (!pLoaderData->DecodeImages && IsImageFilePath(CacheId))
2046+
return true;
2047+
19622048
if (pLoaderData->pResourceMgr != nullptr)
19632049
{
19642050
if (pLoaderData->pResourceMgr->FindTextureAllocation(CacheId.c_str()) != nullptr)
@@ -1992,6 +2078,15 @@ bool ReadWholeFile(std::vector<unsigned char>* out,
19922078
if (LoaderData* pLoaderData = static_cast<LoaderData*>(user_data))
19932079
{
19942080
const std::string CacheId = FileSystem::SimplifyPath(filepath.c_str());
2081+
2082+
if (!pLoaderData->DecodeImages && IsImageFilePath(CacheId))
2083+
{
2084+
// TinyGLTF requires non-empty data before it calls the image
2085+
// loader. The no-decode image callback will leave URI images empty.
2086+
out->assign(1, 0);
2087+
return true;
2088+
}
2089+
19952090
if (pLoaderData->pResourceMgr != nullptr)
19962091
{
19972092
if (RefCntAutoPtr<ITextureAtlasSuballocation> pAllocation = pLoaderData->pResourceMgr->FindTextureAllocation(CacheId.c_str()))
@@ -2069,12 +2164,14 @@ Document::Document(const DocumentLoadInfo& LoadInfo) :
20692164
if (LoadInfo.pTextureCache != nullptr && LoadInfo.pResourceManager != nullptr)
20702165
LOG_WARNING_MESSAGE("Texture cache is ignored when resource manager is used");
20712166

2072-
Callbacks::LoaderData LoaderData{LoadInfo.pTextureCache, LoadInfo.pResourceManager, {}, m_BaseDir};
2167+
Callbacks::LoaderData LoaderData{LoadInfo.pTextureCache, LoadInfo.pResourceManager};
2168+
LoaderData.BaseDir = m_BaseDir;
2169+
LoaderData.DecodeImages = LoadInfo.DecodeImages;
20732170
LoaderData.FileExists = LoadInfo.FileExistsCallback;
20742171
LoaderData.ReadWholeFile = LoadInfo.ReadWholeFileCallback;
20752172

20762173
tinygltf::TinyGLTF gltf_context;
2077-
gltf_context.SetImageLoader(Callbacks::LoadImageData, &LoaderData);
2174+
gltf_context.SetImageLoader(LoadInfo.DecodeImages ? Callbacks::LoadImageData : Callbacks::LoadImageDataNoDecode, &LoaderData);
20782175
tinygltf::FsCallbacks fsCallbacks = {};
20792176
fsCallbacks.ExpandFilePath = tinygltf::ExpandFilePath;
20802177
fsCallbacks.FileExists = Callbacks::FileExists;

0 commit comments

Comments
 (0)