|
45 | 45 | #include "Align.hpp" |
46 | 46 | #include "GLTFBuilder.hpp" |
47 | 47 | #include "GLTFUtilities.hpp" |
| 48 | +#include "StringTools.hpp" |
48 | 49 | #include "FixedLinearAllocator.hpp" |
49 | 50 | #include "DefaultRawMemoryAllocator.hpp" |
50 | 51 |
|
@@ -1735,14 +1736,49 @@ struct LoaderData |
1735 | 1736 | TextureCacheType* const pTextureCache; |
1736 | 1737 | ResourceManager* const pResourceMgr; |
1737 | 1738 |
|
1738 | | - std::vector<RefCntAutoPtr<IObject>> TexturesHold; |
| 1739 | + std::vector<RefCntAutoPtr<IObject>> TexturesHold = {}; |
1739 | 1740 |
|
1740 | | - std::string BaseDir; |
| 1741 | + std::string BaseDir = {}; |
| 1742 | + bool DecodeImages = true; |
1741 | 1743 |
|
1742 | 1744 | ModelCreateInfo::FileExistsCallbackType FileExists = nullptr; |
1743 | 1745 | ModelCreateInfo::ReadWholeFileCallbackType ReadWholeFile = nullptr; |
1744 | 1746 | }; |
1745 | 1747 |
|
| 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 | + |
1746 | 1782 | bool LoadImageData(tinygltf::Image* gltf_image, |
1747 | 1783 | const int gltf_image_idx, |
1748 | 1784 | std::string* error, |
@@ -1952,13 +1988,63 @@ bool LoadImageData(tinygltf::Image* gltf_image, |
1952 | 1988 | return true; |
1953 | 1989 | } |
1954 | 1990 |
|
| 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 | + |
1955 | 2037 | bool FileExists(const std::string& abs_filename, void* user_data) |
1956 | 2038 | { |
1957 | 2039 | // FileSystem::FileExists() is a pretty slow function. |
1958 | 2040 | // Try to find the file in the cache first to avoid calling it. |
1959 | 2041 | if (LoaderData* pLoaderData = static_cast<LoaderData*>(user_data)) |
1960 | 2042 | { |
1961 | 2043 | const std::string CacheId = FileSystem::SimplifyPath(abs_filename.c_str()); |
| 2044 | + |
| 2045 | + if (!pLoaderData->DecodeImages && IsImageFilePath(CacheId)) |
| 2046 | + return true; |
| 2047 | + |
1962 | 2048 | if (pLoaderData->pResourceMgr != nullptr) |
1963 | 2049 | { |
1964 | 2050 | if (pLoaderData->pResourceMgr->FindTextureAllocation(CacheId.c_str()) != nullptr) |
@@ -1992,6 +2078,15 @@ bool ReadWholeFile(std::vector<unsigned char>* out, |
1992 | 2078 | if (LoaderData* pLoaderData = static_cast<LoaderData*>(user_data)) |
1993 | 2079 | { |
1994 | 2080 | 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 | + |
1995 | 2090 | if (pLoaderData->pResourceMgr != nullptr) |
1996 | 2091 | { |
1997 | 2092 | if (RefCntAutoPtr<ITextureAtlasSuballocation> pAllocation = pLoaderData->pResourceMgr->FindTextureAllocation(CacheId.c_str())) |
@@ -2069,12 +2164,14 @@ Document::Document(const DocumentLoadInfo& LoadInfo) : |
2069 | 2164 | if (LoadInfo.pTextureCache != nullptr && LoadInfo.pResourceManager != nullptr) |
2070 | 2165 | LOG_WARNING_MESSAGE("Texture cache is ignored when resource manager is used"); |
2071 | 2166 |
|
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; |
2073 | 2170 | LoaderData.FileExists = LoadInfo.FileExistsCallback; |
2074 | 2171 | LoaderData.ReadWholeFile = LoadInfo.ReadWholeFileCallback; |
2075 | 2172 |
|
2076 | 2173 | tinygltf::TinyGLTF gltf_context; |
2077 | | - gltf_context.SetImageLoader(Callbacks::LoadImageData, &LoaderData); |
| 2174 | + gltf_context.SetImageLoader(LoadInfo.DecodeImages ? Callbacks::LoadImageData : Callbacks::LoadImageDataNoDecode, &LoaderData); |
2078 | 2175 | tinygltf::FsCallbacks fsCallbacks = {}; |
2079 | 2176 | fsCallbacks.ExpandFilePath = tinygltf::ExpandFilePath; |
2080 | 2177 | fsCallbacks.FileExists = Callbacks::FileExists; |
|
0 commit comments