diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ed4fd9..0df1614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,9 @@ cmake_minimum_required(VERSION 3.6) PROJECT (tinygltf) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + SET(CMAKE_CXX_STANDARD 11) option(TINYGLTF_BUILD_LOADER_EXAMPLE "Build loader_example(load glTF and dump infos)" ON) @@ -54,7 +57,10 @@ else (TINYGLTF_HEADER_ONLY) endif (TINYGLTF_HEADER_ONLY) if (TINYGLTF_INSTALL) - + install(TARGETS tinygltf EXPORT tinygltfTargets) + install(EXPORT tinygltfTargets NAMESPACE tinygltf:: FILE TinyGLTFTargets.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) + configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/TinyGLTFConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/TinyGLTFConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/TinyGLTFConfig.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) # Do not install .lib even if !TINYGLTF_HEADER_ONLY INSTALL ( FILES @@ -67,10 +73,4 @@ if (TINYGLTF_INSTALL) include ) - INSTALL ( FILES - cmake/TinyGLTFConfig.cmake - DESTINATION - cmake - ) - endif(TINYGLTF_INSTALL) diff --git a/cmake/TinyGLTFConfig.cmake b/cmake/TinyGLTFConfig.cmake deleted file mode 100644 index 2c2a75d..0000000 --- a/cmake/TinyGLTFConfig.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# -*- cmake -*- -# - Find TinyGLTF - -# TinyGLTF_INCLUDE_DIR TinyGLTF's include directory - - -FIND_PACKAGE ( PackageHandleStandardArgs ) - -SET ( TinyGLTF_INCLUDE_DIR "${TinyGLTF_DIR}/../include" CACHE STRING "TinyGLTF include directory") - -FIND_FILE ( TinyGLTF_HEADER tiny_gltf.h PATHS ${TinyGLTF_INCLUDE_DIR} ) - -IF (NOT TinyGLTF_HEADER) - MESSAGE ( FATAL_ERROR "Unable to find tiny_gltf.h, TinyGLTF_INCLUDE_DIR = ${TinyGLTF_INCLUDE_DIR}") -ENDIF () diff --git a/cmake/TinyGLTFConfig.cmake.in b/cmake/TinyGLTFConfig.cmake.in new file mode 100644 index 0000000..fcccacf --- /dev/null +++ b/cmake/TinyGLTFConfig.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include(${CMAKE_CURRENT_LIST_DIR}/TinyGLTFTargets.cmake) diff --git a/tests/tester.cc b/tests/tester.cc index 0d69a83..b43cf4c 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -540,3 +540,66 @@ TEST_CASE("empty-bin-buffer", "[issue-382]") { } REQUIRE(true == ret); } + +TEST_CASE("serialize-const-image", "[issue-394]") { + tinygltf::Model m; + tinygltf::Image i; + i.width = 1; + i.height = 1; + i.component = 4; + i.bits = 8; + i.pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + i.image = {255, 255, 255, 255}; + i.uri = "image.png"; + m.images.push_back(i); + + std::stringstream os; + + tinygltf::TinyGLTF ctx; + ctx.WriteGltfSceneToStream(const_cast(&m), os, false, + false); + REQUIRE(m.images[0].uri == i.uri); + + // use nlohmann json + nlohmann::json j = nlohmann::json::parse(os.str()); + + REQUIRE(1 == j["images"].size()); + REQUIRE(j["images"][0].is_object()); + REQUIRE(j["images"][0]["uri"].get() != i.uri); + REQUIRE(j["images"][0]["uri"].get().rfind("data:", 0) == 0); +} + +TEST_CASE("serialize-image-callback", "[issue-394]") { + tinygltf::Model m; + tinygltf::Image i; + i.width = 1; + i.height = 1; + i.bits = 32; + i.image = {255, 255, 255, 255}; + i.uri = "foo"; + m.images.push_back(i); + + std::stringstream os; + + auto writer = [](const std::string *basepath, const std::string *filename, + const tinygltf::Image *image, bool embedImages, + std::string *out_uri, void *user_pointer) -> bool { + REQUIRE(*filename == "foo"); + REQUIRE(embedImages == true); + REQUIRE(user_pointer == (void *)0xba5e1e55); + *out_uri = "bar"; + return true; + }; + + tinygltf::TinyGLTF ctx; + ctx.SetImageWriter(writer, (void *)0xba5e1e55); + ctx.WriteGltfSceneToStream(const_cast(&m), os, false, + false); + + // use nlohmann json + nlohmann::json j = nlohmann::json::parse(os.str()); + + REQUIRE(1 == j["images"].size()); + REQUIRE(j["images"][0].is_object()); + REQUIRE(j["images"][0]["uri"].get() == "bar"); +} diff --git a/tiny_gltf.h b/tiny_gltf.h index 5d1407f..b83daa1 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -26,6 +26,7 @@ // THE SOFTWARE. // Version: +// - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393. // - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383. // - v2.6.2 Fix out-of-bounds access of accessors. PR#379. // - v2.6.1 Better GLB validation check when loading. @@ -1216,9 +1217,13 @@ typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, /// /// WriteImageDataFunction type. Signature for custom image writing callbacks. +/// The out_uri parameter becomes the URI written to the gltf and may reference +/// a file or contain a data URI. /// -typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *, - Image *, bool, void *); +typedef bool (*WriteImageDataFunction)(const std::string *basepath, + const std::string *filename, + const Image *image, bool embedImages, + std::string *out_uri, void *user_pointer); #ifndef TINYGLTF_NO_STB_IMAGE // Declaration of default image loader callback @@ -1230,7 +1235,8 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, #ifndef TINYGLTF_NO_STB_IMAGE_WRITE // Declaration of default image writer callback bool WriteImageData(const std::string *basepath, const std::string *filename, - Image *image, bool embedImages, void *); + const Image *image, bool embedImages, std::string *out_uri, + void *); #endif /// @@ -1357,13 +1363,13 @@ class TinyGLTF { /// /// Write glTF to stream, buffers and images will be embedded /// - bool WriteGltfSceneToStream(Model *model, std::ostream &stream, + bool WriteGltfSceneToStream(const Model *model, std::ostream &stream, bool prettyPrint, bool writeBinary); /// /// Write glTF to file. /// - bool WriteGltfSceneToFile(Model *model, const std::string &filename, + bool WriteGltfSceneToFile(const Model *model, const std::string &filename, bool embedImages, bool embedBuffers, bool prettyPrint, bool writeBinary); @@ -2495,7 +2501,8 @@ static void WriteToMemory_stbi(void *context, void *data, int size) { } bool WriteImageData(const std::string *basepath, const std::string *filename, - Image *image, bool embedImages, void *fsPtr) { + const Image *image, bool embedImages, std::string *out_uri, + void *fsPtr) { const std::string ext = GetFilePathExtension(*filename); // Write image to temporary buffer @@ -2537,9 +2544,8 @@ bool WriteImageData(const std::string *basepath, const std::string *filename, if (embedImages) { // Embed base64-encoded image into URI if (data.size()) { - image->uri = - header + - base64_encode(&data[0], static_cast(data.size())); + *out_uri = header + + base64_encode(&data[0], static_cast(data.size())); } else { // Throw error? } @@ -2557,7 +2563,7 @@ bool WriteImageData(const std::string *basepath, const std::string *filename, } else { // Throw error? } - image->uri = *filename; + *out_uri = *filename; } return true; @@ -2828,10 +2834,10 @@ static std::string MimeToExt(const std::string &mimeType) { return ""; } -static void UpdateImageObject(Image &image, std::string &baseDir, int index, - bool embedImages, - WriteImageDataFunction *WriteImageData = nullptr, - void *user_data = nullptr) { +static void UpdateImageObject(const Image &image, std::string &baseDir, + int index, bool embedImages, + WriteImageDataFunction *WriteImageData, + std::string *out_uri, void *user_data) { std::string filename; std::string ext; // If image has uri, use it as a filename @@ -2852,9 +2858,15 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index, } // If callback is set, modify image data object + bool imageWritten = false; if (*WriteImageData != nullptr && !filename.empty()) { - std::string uri; - (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data); + imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages, + out_uri, user_data); + } + + // Use the original uri if the image was not written. + if (!imageWritten) { + *out_uri = image.uri; } } @@ -6755,7 +6767,7 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) { JsonAddMember(o, "extensions", std::move(extMap)); } -static void SerializeGltfAccessor(Accessor &accessor, json &o) { +static void SerializeGltfAccessor(const Accessor &accessor, json &o) { if (accessor.bufferView >= 0) SerializeNumberProperty("bufferView", accessor.bufferView, o); @@ -6847,7 +6859,8 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) { } } -static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { +static void SerializeGltfAnimationChannel(const AnimationChannel &channel, + json &o) { SerializeNumberProperty("sampler", channel.sampler, o); { json target; @@ -6866,7 +6879,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { SerializeExtensionMap(channel.extensions, o); } -static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) { +static void SerializeGltfAnimationSampler(const AnimationSampler &sampler, + json &o) { SerializeNumberProperty("input", sampler.input, o); SerializeNumberProperty("output", sampler.output, o); SerializeStringProperty("interpolation", sampler.interpolation, o); @@ -6876,7 +6890,7 @@ static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) { } } -static void SerializeGltfAnimation(Animation &animation, json &o) { +static void SerializeGltfAnimation(const Animation &animation, json &o) { if (!animation.name.empty()) SerializeStringProperty("name", animation.name, o); @@ -6912,7 +6926,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) { SerializeExtensionMap(animation.extensions, o); } -static void SerializeGltfAsset(Asset &asset, json &o) { +static void SerializeGltfAsset(const Asset &asset, json &o) { if (!asset.generator.empty()) { SerializeStringProperty("generator", asset.generator, o); } @@ -6921,14 +6935,15 @@ static void SerializeGltfAsset(Asset &asset, json &o) { SerializeStringProperty("copyright", asset.copyright, o); } - if (asset.version.empty()) { + auto version = asset.version; + if (version.empty()) { // Just in case // `version` must be defined - asset.version = "2.0"; + version = "2.0"; } // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0? - SerializeStringProperty("version", asset.version, o); + SerializeStringProperty("version", version, o); if (asset.extras.Keys().size()) { SerializeValue("extras", asset.extras, o); @@ -6937,7 +6952,7 @@ static void SerializeGltfAsset(Asset &asset, json &o) { SerializeExtensionMap(asset.extensions, o); } -static void SerializeGltfBufferBin(Buffer &buffer, json &o, +static void SerializeGltfBufferBin(const Buffer &buffer, json &o, std::vector &binBuffer) { SerializeNumberProperty("byteLength", buffer.data.size(), o); binBuffer = buffer.data; @@ -6949,7 +6964,7 @@ static void SerializeGltfBufferBin(Buffer &buffer, json &o, } } -static void SerializeGltfBuffer(Buffer &buffer, json &o) { +static void SerializeGltfBuffer(const Buffer &buffer, json &o) { SerializeNumberProperty("byteLength", buffer.data.size(), o); SerializeGltfBufferData(buffer.data, o); @@ -6960,7 +6975,7 @@ static void SerializeGltfBuffer(Buffer &buffer, json &o) { } } -static bool SerializeGltfBuffer(Buffer &buffer, json &o, +static bool SerializeGltfBuffer(const Buffer &buffer, json &o, const std::string &binFilename, const std::string &binBaseFilename) { if (!SerializeGltfBufferData(buffer.data, binFilename)) return false; @@ -6975,7 +6990,7 @@ static bool SerializeGltfBuffer(Buffer &buffer, json &o, return true; } -static void SerializeGltfBufferView(BufferView &bufferView, json &o) { +static void SerializeGltfBufferView(const BufferView &bufferView, json &o) { SerializeNumberProperty("buffer", bufferView.buffer, o); SerializeNumberProperty("byteLength", bufferView.byteLength, o); @@ -7001,14 +7016,16 @@ static void SerializeGltfBufferView(BufferView &bufferView, json &o) { } } -static void SerializeGltfImage(Image &image, json &o) { - // if uri empty, the mimeType and bufferview should be set - if (image.uri.empty()) { +static void SerializeGltfImage(const Image &image, const std::string uri, + json &o) { + // From 2.7.0, we look for `uri` parameter, not `Image.uri` + // if uri is empty, the mimeType and bufferview should be set + if (uri.empty()) { SerializeStringProperty("mimeType", image.mimeType, o); SerializeNumberProperty("bufferView", image.bufferView, o); } else { // TODO(syoyo): dlib::urilencode? - SerializeStringProperty("uri", image.uri, o); + SerializeStringProperty("uri", uri, o); } if (image.name.size()) { @@ -7022,7 +7039,7 @@ static void SerializeGltfImage(Image &image, json &o) { SerializeExtensionMap(image.extensions, o); } -static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) { +static void SerializeGltfTextureInfo(const TextureInfo &texinfo, json &o) { SerializeNumberProperty("index", texinfo.index, o); if (texinfo.texCoord != 0) { @@ -7036,7 +7053,7 @@ static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) { SerializeExtensionMap(texinfo.extensions, o); } -static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo, +static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo, json &o) { SerializeNumberProperty("index", texinfo.index, o); @@ -7055,8 +7072,8 @@ static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo, SerializeExtensionMap(texinfo.extensions, o); } -static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo, - json &o) { +static void SerializeGltfOcclusionTextureInfo( + const OcclusionTextureInfo &texinfo, json &o) { SerializeNumberProperty("index", texinfo.index, o); if (texinfo.texCoord != 0) { @@ -7074,7 +7091,7 @@ static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo, SerializeExtensionMap(texinfo.extensions, o); } -static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr, +static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr, json &o) { std::vector default_baseColorFactor = {1.0, 1.0, 1.0, 1.0}; if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) { @@ -7109,7 +7126,7 @@ static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr, } } -static void SerializeGltfMaterial(Material &material, json &o) { +static void SerializeGltfMaterial(const Material &material, json &o) { if (material.name.size()) { SerializeStringProperty("name", material.name, o); } @@ -7185,7 +7202,7 @@ static void SerializeGltfMaterial(Material &material, json &o) { } } -static void SerializeGltfMesh(Mesh &mesh, json &o) { +static void SerializeGltfMesh(const Mesh &mesh, json &o) { json primitives; JsonReserveArray(primitives, mesh.primitives.size()); for (unsigned int i = 0; i < mesh.primitives.size(); ++i) { @@ -7254,7 +7271,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) { } } -static void SerializeSpotLight(SpotLight &spot, json &o) { +static void SerializeSpotLight(const SpotLight &spot, json &o) { SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o); SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o); SerializeExtensionMap(spot.extensions, o); @@ -7263,7 +7280,7 @@ static void SerializeSpotLight(SpotLight &spot, json &o) { } } -static void SerializeGltfLight(Light &light, json &o) { +static void SerializeGltfLight(const Light &light, json &o) { if (!light.name.empty()) SerializeStringProperty("name", light.name, o); SerializeNumberProperty("intensity", light.intensity, o); if (light.range > 0.0) { @@ -7282,7 +7299,7 @@ static void SerializeGltfLight(Light &light, json &o) { } } -static void SerializeGltfNode(Node &node, json &o) { +static void SerializeGltfNode(const Node &node, json &o) { if (node.translation.size() > 0) { SerializeNumberArrayProperty("translation", node.translation, o); } @@ -7320,7 +7337,7 @@ static void SerializeGltfNode(Node &node, json &o) { SerializeNumberArrayProperty("children", node.children, o); } -static void SerializeGltfSampler(Sampler &sampler, json &o) { +static void SerializeGltfSampler(const Sampler &sampler, json &o) { if (sampler.magFilter != -1) { SerializeNumberProperty("magFilter", sampler.magFilter, o); } @@ -7389,7 +7406,7 @@ static void SerializeGltfCamera(const Camera &camera, json &o) { SerializeExtensionMap(camera.extensions, o); } -static void SerializeGltfScene(Scene &scene, json &o) { +static void SerializeGltfScene(const Scene &scene, json &o) { SerializeNumberArrayProperty("nodes", scene.nodes, o); if (scene.name.size()) { @@ -7401,7 +7418,7 @@ static void SerializeGltfScene(Scene &scene, json &o) { SerializeExtensionMap(scene.extensions, o); } -static void SerializeGltfSkin(Skin &skin, json &o) { +static void SerializeGltfSkin(const Skin &skin, json &o) { // required SerializeNumberArrayProperty("joints", skin.joints, o); @@ -7418,7 +7435,7 @@ static void SerializeGltfSkin(Skin &skin, json &o) { } } -static void SerializeGltfTexture(Texture &texture, json &o) { +static void SerializeGltfTexture(const Texture &texture, json &o) { if (texture.sampler > -1) { SerializeNumberProperty("sampler", texture.sampler, o); } @@ -7437,7 +7454,7 @@ static void SerializeGltfTexture(Texture &texture, json &o) { /// /// Serialize all properties except buffers and images. /// -static void SerializeGltfModel(Model *model, json &o) { +static void SerializeGltfModel(const Model *model, json &o) { // ACCESSORS if (model->accessors.size()) { json accessors; @@ -7763,7 +7780,7 @@ static bool WriteBinaryGltfFile(const std::string &output, return WriteBinaryGltfStream(gltfFile, content, binBuffer); } -bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, +bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream, bool prettyPrint = true, bool writeBinary = false) { JsonDocument output; @@ -7799,9 +7816,11 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, // UpdateImageObject need baseDir but only uses it if embeddedImages is // enabled, since we won't write separate images when writing to a stream // we + std::string uri; UpdateImageObject(model->images[i], dummystring, int(i), true, - &this->WriteImageData, this->write_image_user_data_); - SerializeGltfImage(model->images[i], image); + &this->WriteImageData, &uri, + this->write_image_user_data_); + SerializeGltfImage(model->images[i], uri, image); JsonPushBack(images, std::move(image)); } JsonAddMember(output, "images", std::move(images)); @@ -7812,10 +7831,10 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, } else { return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1)); } - } -bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, +bool TinyGLTF::WriteGltfSceneToFile(const Model *model, + const std::string &filename, bool embedImages = false, bool embedBuffers = false, bool prettyPrint = true, @@ -7888,9 +7907,11 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, for (unsigned int i = 0; i < model->images.size(); ++i) { json image; + std::string uri; UpdateImageObject(model->images[i], baseDir, int(i), embedImages, - &this->WriteImageData, this->write_image_user_data_); - SerializeGltfImage(model->images[i], image); + &this->WriteImageData, &uri, + this->write_image_user_data_); + SerializeGltfImage(model->images[i], uri, image); JsonPushBack(images, std::move(image)); } JsonAddMember(output, "images", std::move(images)); @@ -7901,7 +7922,6 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, } else { return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1))); } - } } // namespace tinygltf