diff --git a/README.md b/README.md index aefa6be..f8f135b 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ if (!ret) { ## Compile options * `TINYGLTF_NOEXCEPTION` : Disable C++ exception in JSON parsing. You can use `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION` and `TINYGLTF_NOEXCEPTION` to fully remove C++ exception codes when compiling TinyGLTF. +* `TINYGLTF_NO_STB_IMAGE` : Do not load images with stb_image. Instead use `TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)` to set a callback for loading images. ### Saving gltTF 2.0 model diff --git a/tiny_gltf.h b/tiny_gltf.h index c38f815..7d26b95 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -450,7 +450,7 @@ struct Image { std::vector image; int bufferView; // (required if no uri) std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", "image/bmp", "image/gif"] - std::string uri; // (reqiored if no mimeType) + std::string uri; // (required if no mimeType) Value extras; Image() { bufferView = -1; } @@ -725,6 +725,17 @@ enum SectionCheck { REQUIRE_ALL = 0x3f }; +/// +/// LoadImageDataFunction type. Signature for custom image loading callbacks. +/// +typedef bool (*LoadImageDataFunction)(Image *, std::string *, int, int, const unsigned char *, int, void *); + +#ifndef TINYGLTF_NO_STB_IMAGE +// Declaration of default image loader callback +static bool LoadImageData(Image *image, std::string *err, int req_width, + int req_height, const unsigned char *bytes, + int size, void*); +#endif class TinyGLTF { public: @@ -788,6 +799,11 @@ class TinyGLTF { const std::string & filename /*, bool embedImages, bool embedBuffers, bool writeBinary*/); + /// + /// Set callback to use for loading image data + /// + void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); + private: /// /// Loads glTF asset from string(memory). @@ -801,6 +817,14 @@ class TinyGLTF { const unsigned char *bin_data_; size_t bin_size_; bool is_binary_; + + LoadImageDataFunction LoadImageData = +#ifndef TINYGLTF_NO_STB_IMAGE + &tinygltf::LoadImageData; +#else + nullptr; +#endif + void *load_image_user_data_ = nullptr; }; #ifdef __clang__ @@ -847,7 +871,11 @@ class TinyGLTF { #endif #include "./json.hpp" + +#ifndef TINYGLTF_NO_STB_IMAGE #include "./stb_image.h" +#endif + #ifdef __clang__ #pragma clang diagnostic pop #endif @@ -1159,11 +1187,15 @@ static bool LoadExternalFile(std::vector *out, std::string *err, return true; } +void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) { + LoadImageData = func; + load_image_user_data_ = user_data; +} + +#ifndef TINYGLTF_NO_STB_IMAGE static bool LoadImageData(Image *image, std::string *err, int req_width, int req_height, const unsigned char *bytes, - int size) { - //std::cout << "size " << size << std::endl; - + int size, void*) { int w, h, comp; // if image cannot be decoded, ignore parsing and keep it by its path // don't break in this case @@ -1217,6 +1249,7 @@ static bool LoadImageData(Image *image, std::string *err, int req_width, return true; } +#endif static bool IsDataURI(const std::string &in) { std::string header = "data:application/octet-stream;base64,"; @@ -1228,12 +1261,12 @@ static bool IsDataURI(const std::string &in) { if (in.find(header) == 0) { return true; } - + header = "data:image/png;base64,"; if (in.find(header) == 0) { return true; } - + header = "data:image/bmp;base64,"; if(in.find(header) == 0) { return true; @@ -1274,7 +1307,7 @@ static bool DecodeDataURI(std::vector *out, data = base64_decode(in.substr(header.size())); // cut mime string. } } - + if (data.empty()) { header = "data:image/bmp;base64,"; if (in.find(header) == 0) { @@ -1611,7 +1644,8 @@ static bool ParseAsset(Asset *asset, std::string *err, static bool ParseImage(Image *image, std::string *err, const json &o, const std::string &basedir, bool is_binary, const unsigned char *bin_data, - size_t bin_size) { + size_t bin_size, LoadImageDataFunction* LoadImageData = nullptr, + void *user_data = nullptr) { // A glTF image must either reference a bufferView or an image uri double bufferView = -1; bool isEmbedded = @@ -1703,8 +1737,14 @@ static bool ParseImage(Image *image, std::string *err, } } - return LoadImageData(image, err, 0, 0, &img.at(0), - static_cast(img.size())); + if (*LoadImageData == nullptr) { + if (err) { + (*err) += "No LoadImageData callback specified.\n"; + } + return false; + } + return (*LoadImageData)(image, err, 0, 0, &img.at(0), + static_cast(img.size()), user_data); } static bool ParseTexture(Texture *texture, std::string *err, @@ -2560,7 +2600,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // FIXME Maybe a better way to handle it than removing the code { - json::const_iterator it = v.find("scenes"); + json::const_iterator it = v.find("scenes"); if ((it != v.end()) && it.value().is_array()) { // OK } else if (check_sections & REQUIRE_SCENES) { @@ -2572,7 +2612,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, } { - json::const_iterator it = v.find("nodes"); + json::const_iterator it = v.find("nodes"); if ((it != v.end()) && it.value().is_array()) { // OK } else if (check_sections & REQUIRE_NODES) { @@ -2584,7 +2624,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, } { - json::const_iterator it = v.find("accessors"); + json::const_iterator it = v.find("accessors"); if ((it != v.end()) && it.value().is_array()) { // OK } else if (check_sections & REQUIRE_ACCESSORS) { @@ -2596,7 +2636,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, } { - json::const_iterator it = v.find("buffers"); + json::const_iterator it = v.find("buffers"); if ((it != v.end()) && it.value().is_array()) { // OK } else if (check_sections & REQUIRE_BUFFERS) { @@ -2608,7 +2648,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, } { - json::const_iterator it = v.find("bufferViews"); + json::const_iterator it = v.find("bufferViews"); if ((it != v.end()) && it.value().is_array()) { // OK } else if (check_sections & REQUIRE_BUFFER_VIEWS) { @@ -2631,7 +2671,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 1. Parse Asset { - json::const_iterator it = v.find("asset"); + json::const_iterator it = v.find("asset"); if ((it != v.end()) && it.value().is_object()) { const json &root = it.value(); @@ -2641,7 +2681,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 2. Parse extensionUsed { - json::const_iterator it = v.find("extensionsUsed"); + json::const_iterator it = v.find("extensionsUsed"); if ((it != v.end()) && it.value().is_array()) { const json &root = it.value(); for (unsigned int i = 0; i < root.size(); ++i) { @@ -2651,7 +2691,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, } { - json::const_iterator it = v.find("extensionsRequired"); + json::const_iterator it = v.find("extensionsRequired"); if ((it != v.end()) && it.value().is_array()) { const json &root = it.value(); for (unsigned int i = 0; i < root.size(); ++i) { @@ -2662,7 +2702,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 3. Parse Buffer { - json::const_iterator rootIt = v.find("buffers"); + json::const_iterator rootIt = v.find("buffers"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2685,10 +2725,10 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, } } } - + // 4. Parse BufferView { - json::const_iterator rootIt = v.find("bufferViews"); + json::const_iterator rootIt = v.find("bufferViews"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2713,7 +2753,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 5. Parse Accessor { - json::const_iterator rootIt = v.find("accessors"); + json::const_iterator rootIt = v.find("accessors"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2738,7 +2778,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 6. Parse Mesh { - json::const_iterator rootIt = v.find("meshes"); + json::const_iterator rootIt = v.find("meshes"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2763,7 +2803,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 7. Parse Node { - json::const_iterator rootIt = v.find("nodes"); + json::const_iterator rootIt = v.find("nodes"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2788,7 +2828,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 8. Parse scenes. { - json::const_iterator rootIt = v.find("scenes"); + json::const_iterator rootIt = v.find("scenes"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2823,7 +2863,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 9. Parse default scenes. { - json::const_iterator rootIt = v.find("scene"); + json::const_iterator rootIt = v.find("scene"); if ((rootIt != v.end()) && rootIt.value().is_number_integer()) { const int defaultScene = rootIt.value(); @@ -2833,7 +2873,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 10. Parse Material { - json::const_iterator rootIt = v.find("materials"); + json::const_iterator rootIt = v.find("materials"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2862,7 +2902,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 11. Parse Image { - json::const_iterator rootIt = v.find("images"); + json::const_iterator rootIt = v.find("images"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2877,7 +2917,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, } Image image; if (!ParseImage(&image, err, it.value(), base_dir, - is_binary_, bin_data_, bin_size_)) { + is_binary_, bin_data_, bin_size_, &this->LoadImageData, load_image_user_data_)) { return false; } @@ -2897,9 +2937,15 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, model->bufferViews[size_t(image.bufferView)]; const Buffer &buffer = model->buffers[size_t(bufferView.buffer)]; + if (*LoadImageData == nullptr) { + if (err) { + (*err) += "No LoadImageData callback specified.\n"; + } + return false; + } bool ret = LoadImageData(&image, err, image.width, image.height, &buffer.data[bufferView.byteOffset], - static_cast(bufferView.byteLength)); + static_cast(bufferView.byteLength), load_image_user_data_); if (!ret) { return false; } @@ -2912,7 +2958,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 12. Parse Texture { - json::const_iterator rootIt = v.find("textures"); + json::const_iterator rootIt = v.find("textures"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2937,7 +2983,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 13. Parse Animation { - json::const_iterator rootIt = v.find("animations"); + json::const_iterator rootIt = v.find("animations"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2962,7 +3008,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 14. Parse Skin { - json::const_iterator rootIt = v.find("skins"); + json::const_iterator rootIt = v.find("skins"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -2987,7 +3033,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 15. Parse Sampler { - json::const_iterator rootIt = v.find("samplers"); + json::const_iterator rootIt = v.find("samplers"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -3012,7 +3058,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 16. Parse Camera { - json::const_iterator rootIt = v.find("cameras"); + json::const_iterator rootIt = v.find("cameras"); if ((rootIt != v.end()) && rootIt.value().is_array()) { const json &root = rootIt.value(); @@ -3037,7 +3083,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, // 17. Parse Extensions { - json::const_iterator rootIt = v.find("extensions"); + json::const_iterator rootIt = v.find("extensions"); if ((rootIt != v.end()) && rootIt.value().is_object()) { const json &root = rootIt.value(); @@ -3283,7 +3329,7 @@ static void SerializeValue(const std::string &key, const Value &value, for (unsigned int i = 0; i < valueKeys.size(); ++i) { Value elementValue = value.Get(valueKeys[i]); if (elementValue.IsInt()) - jsonValue[valueKeys[i]] = + jsonValue[valueKeys[i]] = static_cast(elementValue.Get()); }