diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 0000000..9424b6d --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,165 @@ +name: C/C++ CI + +on: [push, pull_request] + +jobs: + + # compile with older gcc4.8 + build-gcc48: + + runs-on: ubuntu-18.04 + name: Build with gcc 4.8 + + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Build + run: | + sudo apt-get update + sudo apt-get install -y build-essential + sudo apt-get install -y gcc-4.8 g++-4.8 + g++-4.8 -std=c++11 -o loader_example loader_example.cc + + - name: NoexceptBuild + run: | + g++-4.8 -DTINYGLTF_NOEXCEPTION -std=c++11 -o loader_example loader_example.cc + + - name: RapidjsonBuild + run: | + git clone https://github.com/Tencent/rapidjson + g++-4.8 -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -o loader_example loader_example.cc + + # compile with mingw gcc cross + build-mingw-cross: + + runs-on: ubuntu-18.04 + name: Build with MinGW gcc cross + + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Build + run: | + sudo apt-get update + sudo apt-get install -y build-essential + sudo apt-get install -y mingw-w64 + x86_64-w64-mingw32-g++ -std=c++11 -o loader_example loader_example.cc + + # Windows(x64) + Visual Studio 2019 build + build-windows-msvc: + + runs-on: windows-latest + name: Build for Windows(x64, MSVC) + + # Use system installed cmake + # https://help.github.com/en/actions/reference/software-installed-on-github-hosted-runners + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Configure + run: | + mkdir build + cd build + cmake -G "Visual Studio 16 2019" -DTINYGLTF_BUILD_LOADER_EXAMPLE=On -DTINYGLTF_BUILD_GL_EXAMPLES=Off -DTINYGLTF_BUILD_VALIDATOR_EXAMPLE=On .. + cd .. + - name: Build + run: cmake --build build --config Release + + + build-linux: + + runs-on: ubuntu-latest + name: Buld with gcc + + steps: + - uses: actions/checkout@v2 + - name: build + run: | + g++ -std=c++11 -o loader_example loader_example.cc + - name: test + run: | + ./loader_example models/Cube/Cube.gltf + + - name: tests + run: | + cd tests + g++ -I../ -std=c++11 -g -O0 -o tester tester.cc + ./tester + cd .. + + - name: noexcept_tests + run: | + cd tests + g++ -DTINYGLTF_NOEXCEPTION -I../ -std=c++11 -g -O0 -o tester_noexcept tester.cc + ./tester_noexcept + cd .. + + + build-rapidjson-linux: + + runs-on: ubuntu-latest + name: Buld with gcc + rapidjson + + steps: + - uses: actions/checkout@v2 + - name: build + run: | + git clone https://github.com/Tencent/rapidjson + g++ -v + g++ -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -o loader_example loader_example.cc + + - name: loader_example_test + run: | + ./loader_example models/Cube/Cube.gltf + + - name: tests + run: | + cd tests + g++ -DTINYGLTF_USE_RAPIDJSON -I../rapidjson/include/rapidjson -I../ -std=c++11 -g -O0 -o tester tester.cc + ./tester + cd .. + + - name: noexcept_tests + run: | + cd tests + g++ -DTINYGLTF_USE_RAPIDJSON -I../rapidjson/include/rapidjson -DTINYGLTF_NOEXCEPTION -I../ -std=c++11 -g -O0 -o tester_noexcept tester.cc + ./tester_noexcept + cd .. + + # Cross-compile for aarch64 linux target + build-cross-aarch64: + + runs-on: ubuntu-18.04 + name: Build on cross aarch64 + + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Build + run: | + sudo apt-get update + sudo apt-get install -y build-essential + sudo apt-get install -y gcc-8-aarch64-linux-gnu g++-8-aarch64-linux-gnu + + git clone https://github.com/Tencent/rapidjson + aarch64-linux-gnu-g++-8 -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -g -O0 -o loader_example loader_example.cc + + # macOS clang + build-macos: + + runs-on: macos-latest + name: Build on macOS + + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Build + run: | + clang++ -std=c++11 -g -O0 -o loader_example loader_example.cc + ./loader_example models/Cube/Cube.gltf + + git clone https://github.com/Tencent/rapidjson + clang++ -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -g -O0 -o loader_example loader_example.cc + diff --git a/CMakeLists.txt b/CMakeLists.txt index e7864e5..30cc854 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,18 +4,24 @@ PROJECT (tinygltf) SET(CMAKE_CXX_STANDARD 11) -option(TINYGLTF_BUILD_EXAMPLES "Build examples" ON) +option(TINYGLTF_BUILD_LOADER_EXAMPLE "Build loader_example" ON) +option(TINYGLTF_BUILD_GL_EXAMPLES "Build GL exampels(requires glfw, OpenGL, etc)" OFF) +option(TINYGLTF_BUILD_VALIDATOR_EXAMPLE "Build validator exampe" OFF) -if (TINYGLTF_BUILD_EXAMPLES) +if (TINYGLTF_BUILD_LOADER_EXAMPLE) ADD_EXECUTABLE ( loader_example loader_example.cc ) +endif (TINYGLTF_BUILD_LOADER_EXAMPLE) +if (TINYGLTF_BUILD_GL_EXAMPLES) ADD_SUBDIRECTORY ( examples/gltfutil ) ADD_SUBDIRECTORY ( examples/glview ) - ADD_SUBDIRECTORY ( examples/validator ) -endif (TINYGLTF_BUILD_EXAMPLES) +endif (TINYGLTF_BUILD_GL_EXAMPLES) +if (TINYGLTF_BUILD_VALIDATOR_EXAMPLE) + ADD_SUBDIRECTORY ( examples/validator ) +endif (TINYGLTF_BUILD_VALIDATOR_EXAMPLE) # # TinuGLTF is a header-only library, so no library build. just install header files. # diff --git a/README.md b/README.md index 2d9143d..0b9d4c5 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch. [![Build status](https://ci.appveyor.com/api/projects/status/warngenu9wjjhlm8?svg=true)](https://ci.appveyor.com/project/syoyo/tinygltf) +![C/C++ CI](https://github.com/syoyo/tinygltf/workflows/C/C++%20CI/badge.svg) + ## Features * Written in portable C++. C++-11 with STL dependency only. @@ -83,6 +85,7 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number * [QuickLook GLTF](https://github.com/toshiks/glTF-quicklook) - quicklook plugin for macos. Also SceneKit wrapper for tinygltf. * [GlslViewer](https://github.com/patriciogonzalezvivo/glslViewer) - live GLSL coding for MacOS and Linux * [Vulkan-Samples](https://github.com/KhronosGroup/Vulkan-Samples) - The Vulkan Samples is collection of resources to help you develop optimized Vulkan applications. +* [TDME2](https://github.com/andreasdr/tdme2) - TDME2 - ThreeDeeMiniEngine2 is a lightweight 3D engine including tools suited for 3D game development using C++11 * Your projects here! (Please send PR) ## TODOs @@ -150,6 +153,7 @@ if (!ret) { ## Compile options +* `TINYGLTF_ENABLE_SERIALIZER` : Enable glTF serialization feature. * `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. * `TINYGLTF_NO_STB_IMAGE_WRITE` : Do not write images with stb_image_write. Instead use `TinyGLTF::SetImageWriter(WriteimageDataFunction WriteImageData, void *user_data)` to set a callback for writing images. diff --git a/models/Extensions-overwrite-issue261/issue-261.bin b/models/Extensions-overwrite-issue261/issue-261.bin new file mode 100644 index 0000000..60ed1b7 Binary files /dev/null and b/models/Extensions-overwrite-issue261/issue-261.bin differ diff --git a/models/Extensions-overwrite-issue261/issue-261.gltf b/models/Extensions-overwrite-issue261/issue-261.gltf new file mode 100644 index 0000000..7dd85e2 --- /dev/null +++ b/models/Extensions-overwrite-issue261/issue-261.gltf @@ -0,0 +1,376 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 8, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5125, + "count": 36, + "type": "SCALAR" + } + ], + "asset": { + "copyright": "NVIDIA Corporation", + "generator": "Iray glTF plugin", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 96, + "byteStride": 12 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 96 + } + ], + "buffers": [ + { + "byteLength": 240, + "uri": "issue-261.bin" + } + ], + "cameras": [ + { + "extensions": { + "NV_Iray": { + "mip_burn_highlights": 0.699999988079071, + "mip_burn_highlights_per_component": false, + "mip_camera_shutter": 4.0, + "mip_cm2_factor": 1.0, + "mip_crush_blacks": 0.5, + "mip_f_number": 1.0, + "mip_film_iso": 100.0, + "mip_gamma": 2.200000047683716, + "mip_saturation": 1.0, + "mip_vignetting": 0.00019999999494757503, + "mip_whitepoint": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "tm_enable_tonemapper": true, + "tm_tonemapper": "mia_exposure_photographic" + } + }, + "extras": { + "resolution": [ + 640, + 480 + ] + }, + "name": "default", + "perspective": { + "aspectRatio": 1.3333333730697632, + "yfov": 0.9272952079772949, + "zfar": 1000.0, + "znear": 0.1 + }, + "type": "perspective" + } + ], + "extensions": { + "KHR_lights_punctual": { + "lights": [ + { + "color": [ + 1.0, + 1.0, + 1.0 + ], + "intensity": 1000.0, + "name": "light", + "type": "point" + } + ] + }, + "NV_MDL": { + "modules": [ + "mdl::base", + "mdl::nvidia::core_definitions", + "mdl::state" + ], + "shaders": [ + { + "definition": "mdl::base::environment_spherical(texture_2d)", + "name": "env_shd" + }, + { + "arguments": { + "base_color": [ + 0.0055217444896698, + 0.36859095096588135, + 0.0041161770932376385 + ], + "normal=": 2 + }, + "definition": "mdl::nvidia::core_definitions::flex_material", + "name": "cube_instance_material" + }, + { + "definition": "mdl::state::normal()", + "name": "mdl::state::normal_341" + }, + { + "arguments": { + "base_color": [ + 0.0055217444896698, + 0.36859095096588135, + 0.0041161770932376385 + ], + "normal=": 4 + }, + "definition": "mdl::nvidia::core_definitions::flex_material", + "name": "cube_instance_material" + }, + { + "definition": "mdl::state::normal()", + "name": "mdl::state::normal_341" + } + ] + } + }, + "extensionsUsed": [ + "NV_MDL", + "NV_Iray", + "KHR_lights_punctual" + ], + "materials": [ + { + "doubleSided": true, + "extras": { + "mdl_shader": 1 + }, + "name": "cube_instance_material", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.0055217444896698, + 0.36859095096588135, + 0.0041161770932376385, + 1.0 + ] + } + } + ], + "meshes": [ + { + "name": "cube", + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "camera": 0, + "extensions": { + "NV_Iray": { + "iview:fkey": -1, + "iview:fov": 53.130104064941406, + "iview:interest": [ + 0.1342654824256897, + -0.14356137812137604, + 0.037264324724674225 + ], + "iview:position": [ + 0.9729073643684387, + 1.2592438459396362, + 2.4199187755584717 + ], + "iview:roll": 0.0, + "iview:up": [ + 0.0, + 1.0, + 0.0 + ] + } + }, + "matrix": [ + 0.9432751389105357, + -1.1769874756875739e-16, + -0.3320120665176343, + 0.0, + -0.16119596696756341, + 0.8742297945345237, + -0.45797175303889276, + 0.0, + 0.290254840694694, + 0.48551237507207207, + 0.8246392308792816, + 0.0, + 0.9729073377709113, + 1.2592438262175363, + 2.419918748461627, + 1.0 + ], + "name": "CamInst" + }, + { + "extensions": { + "NV_Iray": { + "caustic": true, + "caustic_cast": true, + "caustic_recv": true, + "face_back": true, + "face_front": true, + "finalgather": true, + "finalgather_cast": true, + "finalgather_recv": true, + "globillum": true, + "globillum_cast": true, + "globillum_recv": true, + "material=": 3, + "pickable": true, + "reflection_cast": true, + "reflection_recv": true, + "refraction_cast": true, + "refraction_recv": true, + "shadow_cast": true, + "shadow_recv": true, + "transparency_cast": true, + "transparency_recv": true, + "visible": true + } + }, + "mesh": 0, + "name": "cube_instance" + }, + { + "extensions": { + "KHR_lights_punctual": { + "light": 0 + }, + "NV_Iray": { + "shadow_cast": true, + "visible": false + } + }, + "matrix": [ + 0.6988062355563571, + -7.76042172309776e-17, + -0.7153110128800992, + -0.0, + -0.4276881690736487, + 0.8015668284138362, + -0.41781987700564616, + -0.0, + 0.57336957992379, + 0.5979051928078428, + 0.5601398979107212, + 0.0, + 2.3640632834071384, + 2.465226204472449, + 2.309515908392224, + 1.0 + ], + "name": "light_inst" + } + ], + "scene": 0, + "scenes": [ + { + "extensions": { + "NV_Iray": { + "CP_canny_threshold1": 40, + "CP_canny_threshold2": 120, + "CP_color_quantization": 8, + "IVP_color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ], + "TM_drago_bias": 0.8500000238418579, + "TM_drago_gamma2": 2.200000047683716, + "TM_drago_saturation": 1.0, + "TM_durand_contrast": 4.0, + "TM_durand_gamma": 2.200000047683716, + "TM_durand_saturation": 1.0, + "TM_durand_sigma_color": 2.0, + "TM_durand_sigma_space": 2.0, + "TM_linear_gamma": 2.200000047683716, + "TM_reinhard_color_adapt": 0.8999999761581421, + "TM_reinhard_gamma": 1.0, + "TM_reinhard_intensity": 0.0, + "TM_reinhard_light_adapt": 1.0, + "TM_reye_Ywhite": 1000000.0, + "TM_reye_bsize": 2, + "TM_reye_bthres": 3.0, + "TM_reye_gamma": 2.200000047683716, + "TM_reye_key": 0.5, + "TM_reye_saturation": 1.0, + "TM_reye_whitebalance": [ + 1.0, + 0.9965101480484009, + 0.9805564880371094, + 0.0 + ], + "environment_dome_depth": 200.0, + "environment_dome_height": 200.0, + "environment_dome_mode": "infinite", + "environment_dome_position": [ + 0.0, + 0.0, + 0.0 + ], + "environment_dome_radius": 100.0, + "environment_dome_rotation_angle": 0.0, + "environment_dome_width": 200.0, + "environment_function=": 0, + "environment_function_intensity": 1.9900000095367432, + "iray_instancing": "off", + "iview::inline_color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ], + "iview::inline_width": 1.0, + "iview::magnifier_size": 300, + "iview::offset": 10.0, + "iview::outline_color": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "iview::outline_width": 2.0, + "iview::overview": true, + "iview::zoom_factor": 1.0, + "samples": 4.0, + "shadow_cast": true, + "shadow_recv": true + } + }, + "nodes": [ + 0, + 1, + 2 + ] + } + ] +} diff --git a/tests/tester.cc b/tests/tester.cc index c6d31b0..012b54d 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -93,6 +93,59 @@ TEST_CASE("extension-with-empty-object", "[issue-97]") { } +TEST_CASE("extension-overwrite", "[issue-261]") { + + tinygltf::Model model; + tinygltf::TinyGLTF ctx; + std::string err; + std::string warn; + + bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/Extensions-overwrite-issue261/issue-261.gltf"); + if (!err.empty()) { + std::cerr << err << std::endl; + } + REQUIRE(true == ret); + + REQUIRE(model.extensionsUsed.size() == 3); + { + bool has_ext_lights = false; + has_ext_lights |= (model.extensionsUsed[0].compare("KHR_lights_punctual") == 0); + has_ext_lights |= (model.extensionsUsed[1].compare("KHR_lights_punctual") == 0); + has_ext_lights |= (model.extensionsUsed[2].compare("KHR_lights_punctual") == 0); + + REQUIRE(true == has_ext_lights); + } + + { + REQUIRE(model.extensions.size() == 2); + REQUIRE(model.extensions.count("NV_MDL")); + REQUIRE(model.extensions.count("KHR_lights_punctual")); + } + + // TODO(syoyo): create temp directory. + { + ret = ctx.WriteGltfSceneToFile(&model, "issue-261.gltf", true, true); + REQUIRE(true == ret); + + tinygltf::Model m; + + // read back serialized glTF + bool ret = ctx.LoadASCIIFromFile(&m, &err, &warn, "issue-261.gltf"); + if (!err.empty()) { + std::cerr << err << std::endl; + } + REQUIRE(true == ret); + + REQUIRE(m.extensionsUsed.size() == 3); + + REQUIRE(m.extensions.size() == 2); + REQUIRE(m.extensions.count("NV_MDL")); + REQUIRE(m.extensions.count("KHR_lights_punctual")); + + } + +} + TEST_CASE("invalid-primitive-indices", "[bounds-checking]") { tinygltf::Model model; tinygltf::TinyGLTF ctx; @@ -359,3 +412,19 @@ TEST_CASE("image-uri-spaces", "[issue-236]") { REQUIRE(true == ret); } +#ifndef TINYGLTF_NO_FS +TEST_CASE("expandpath-utf-8", "[pr-226]") { + + std::string s1 = "\xe5\xaf\xb9"; // utf-8 string + + std::string ret = tinygltf::ExpandFilePath(s1, /* userdata */nullptr); + + // expected: E5 AF B9 + REQUIRE(3 == ret.size()); + + REQUIRE(0xe5 == static_cast(ret[0])); + REQUIRE(0xaf == static_cast(ret[1])); + REQUIRE(0xb9 == static_cast(ret[2])); + +} +#endif diff --git a/tiny_gltf.h b/tiny_gltf.h index 262dbd3..d7592e1 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -27,6 +27,7 @@ // Version: // - v2.4.3 Experimental sajson(lightweight JSON parser) backend support. +// Introduce TINYGLTF_ENABLE_SERIALIZER. // - v2.4.2 Decode percent-encoded URI. // - v2.4.1 Fix some glTF object class does not have `extensions` and/or // `extras` property. @@ -54,6 +55,7 @@ #include #include +#include // std::fabs #include #include #include @@ -61,7 +63,6 @@ #include #include #include -#include // std::fabs #ifndef TINYGLTF_USE_CPP14 #include @@ -325,7 +326,8 @@ class Value { } // Use this function if you want to have number value as int. - double GetNumberAsInt() const { + // TODO(syoyo): Support int value larger than 32 bits + int GetNumberAsInt() const { if (type_ == REAL_TYPE) { return int(real_value_); } else { @@ -1116,9 +1118,9 @@ struct SpotLight { struct Light { std::string name; std::vector color; - double intensity; + double intensity{1.0}; std::string type; - double range; + double range{0.0}; // 0.0 = inifinite SpotLight spot; Light() : intensity(1.0), range(0.0) {} @@ -1250,7 +1252,13 @@ struct FsCallbacks { bool FileExists(const std::string &abs_filename, void *); -std::string ExpandFilePath(const std::string &filepath, void *); +/// +/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to `C:\Users\tinygltf\AppData`) +/// +/// @param[in] filepath File path string. Assume UTF-8 +/// @param[in] userdata User data. Set to `nullptr` if you don't need it. +/// +std::string ExpandFilePath(const std::string &filepath, void *userdata); bool ReadWholeFile(std::vector *out, std::string *err, const std::string &filepath, void *); @@ -1318,6 +1326,8 @@ class TinyGLTF { const std::string &base_dir = "", unsigned int check_sections = REQUIRE_VERSION); +#if defined(TINYGLTF_ENABLE_SERIALIZER) + /// /// Write glTF to stream, buffers and images will be embeded /// @@ -1331,16 +1341,22 @@ class TinyGLTF { bool embedImages, bool embedBuffers, bool prettyPrint, bool writeBinary); +#endif + /// /// Set callback to use for loading image data /// void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); +#if defined(TINYGLTF_ENABLE_SERIALIZER) + /// /// Set callback to use for writing image data /// void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); +#endif + /// /// Set callbacks to use for filesystem (fs) access and their user data /// @@ -2420,11 +2436,15 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, } #endif +#if defined(TINYGLTF_ENABLE_SERIALIZER) + void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { WriteImageData = func; write_image_user_data_ = user_data; } +#endif + #ifndef TINYGLTF_NO_STB_IMAGE_WRITE static void WriteToMemory_stbi(void *context, void *data, int size) { std::vector *buffer = @@ -2516,6 +2536,15 @@ static inline std::wstring UTF8ToWchar(const std::string &str) { (int)wstr.size()); return wstr; } + +static inline std::string WcharToUTF8(const std::wstring &wstr) { + int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), + nullptr, 0, NULL, NULL); + std::string str(str_size, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0], + (int)str.size(), NULL, NULL); + return str; +} #endif #ifndef TINYGLTF_NO_FS @@ -2567,15 +2596,16 @@ bool FileExists(const std::string &abs_filename, void *) { std::string ExpandFilePath(const std::string &filepath, void *) { #ifdef _WIN32 - DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0); - char *str = new char[len]; - ExpandEnvironmentStringsA(filepath.c_str(), str, len); + // Assume input `filepath` is encoded in UTF-8 + std::wstring wfilepath = UTF8ToWchar(filepath); + DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0); + wchar_t *wstr = new wchar_t[wlen]; + ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen); - std::string s(str); + std::wstring ws(wstr); + delete[] wstr; + return WcharToUTF8(ws); - delete[] str; - - return s; #else #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \ @@ -2651,9 +2681,11 @@ bool ReadWholeFile(std::vector *out, std::string *err, _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY); __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, std::ios_base::in); std::istream f(&wfile_buf); -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION) + // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept `wchar_t *` std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary); -#else // clang? +#else + // Unknown compiler/runtime std::ifstream f(filepath.c_str(), std::ifstream::binary); #endif #else @@ -2695,12 +2727,10 @@ bool WriteWholeFile(std::string *err, const std::string &filepath, const std::vector &contents, void *) { #ifdef _WIN32 #if defined(__GLIBCXX__) // mingw - int file_descriptor = - _wopen(UTF8ToWchar(filepath).c_str(), _O_CREAT | _O_WRONLY | - _O_TRUNC | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, - std::ios_base::out | - std::ios_base::binary); + int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream f(&wfile_buf); #elif defined(_MSC_VER) std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary); @@ -2755,10 +2785,9 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index, if (image.uri.size()) { filename = GetBaseFilename(image.uri); ext = GetFilePathExtension(filename); - } - else if (image.bufferView != -1) { - //If there's no URI and the data exists in a buffer, - //don't change properties or write images + } else if (image.bufferView != -1) { + // If there's no URI and the data exists in a buffer, + // don't change properties or write images } else if (image.name.size()) { ext = MimeToExt(image.mimeType); // Otherwise use name as filename @@ -2909,7 +2938,7 @@ bool GetInt(const json &o, int &val) { return false; #elif defined(TINYGLTF_USE_SAJSON) auto type = o.get_type(); - + if (type == sajson::TYPE_INTEGER) { val = static_cast(o.get_number_value()); return true; @@ -2950,7 +2979,7 @@ bool GetNumber(const json &o, double &val) { return false; #elif defined(TINYGLTF_USE_SAJSON) auto type = o.get_type(); - + if ((type == sajson::TYPE_DOUBLE) || (type == sajson::TYPE_INTEGER)) { val = static_cast(o.get_number_value()); @@ -2978,7 +3007,7 @@ bool GetString(const json &o, std::string &val) { return false; #elif defined(TINYGLTF_USE_SAJSON) auto type = o.get_type(); - + if (type == sajson::TYPE_STRING) { val = o.as_string(); return true; @@ -4944,13 +4973,13 @@ static bool ParseAnimationChannel( } return false; } - ParseExtensionsProperty(&channel->target_extensions, err, target_object); - if (store_original_json_for_extras_and_extensions) { + ParseExtensionsProperty(&channel->target_extensions, err, target_object); + if (store_original_json_for_extras_and_extensions) { json_const_iterator it; if (FindMember(target_object, "extensions", it)) { channel->target_extensions_json_string = JsonToString(GetValue(it)); } - } + } } channel->sampler = samplerIndex; @@ -5761,9 +5790,11 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, .target = TINYGLTF_TARGET_ARRAY_BUFFER; } - for(auto &target : primitive.targets) { - for(auto &attribute : target) { - model->bufferViews[size_t(model->accessors[size_t(attribute.second)].bufferView)] + for (auto &target : primitive.targets) { + for (auto &attribute : target) { + model + ->bufferViews[size_t( + model->accessors[size_t(attribute.second)].bufferView)] .target = TINYGLTF_TARGET_ARRAY_BUFFER; } } @@ -6273,6 +6304,8 @@ bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err, return ret; } +#if defined(TINYGLTF_ENABLE_SERIALIZER) + /////////////////////// // GLTF Serialization /////////////////////// @@ -6349,6 +6382,13 @@ static void SerializeNumberProperty(const std::string &key, T number, JsonAddMember(obj, key.c_str(), json(number)); } +#ifdef TINYGLTF_USE_RAPIDJSON +template <> +void SerializeNumberProperty(const std::string &key, size_t number, json &obj) { + JsonAddMember(obj, key.c_str(), json(static_cast(number))); +} +#endif + template static void SerializeNumberArrayProperty(const std::string &key, const std::vector &value, @@ -6499,12 +6539,10 @@ static bool SerializeGltfBufferData(const std::vector &data, const std::string &binFilename) { #ifdef _WIN32 #if defined(__GLIBCXX__) // mingw - int file_descriptor = - _wopen(UTF8ToWchar(binFilename).c_str(), _O_CREAT | _O_WRONLY | - _O_TRUNC | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, - std::ios_base::out | - std::ios_base::binary); + int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream output(&wfile_buf); if (!wfile_buf.is_open()) return false; #elif defined(_MSC_VER) @@ -6639,7 +6677,7 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { SerializeNumberProperty("node", channel.target_node, target); SerializeStringProperty("path", channel.target_path, target); - SerializeExtensionMap(channel.target_extensions, target); + SerializeExtensionMap(channel.target_extensions, target); JsonAddMember(o, "target", std::move(target)); } @@ -7046,7 +7084,9 @@ static void SerializeSpotLight(SpotLight &spot, json &o) { static void SerializeGltfLight(Light &light, json &o) { if (!light.name.empty()) SerializeStringProperty("name", light.name, o); SerializeNumberProperty("intensity", light.intensity, o); - SerializeNumberProperty("range", light.range, o); + if (light.range > 0.0) { + SerializeNumberProperty("range", light.range, o); + } SerializeNumberArrayProperty("color", light.color, o); SerializeStringProperty("type", light.type, o); if (light.type == "spot") { @@ -7160,6 +7200,11 @@ static void SerializeGltfCamera(const Camera &camera, json &o) { } else { // ??? } + + if (camera.extras.Type() != NULL_TYPE) { + SerializeValue("extras", camera.extras, o); + } + SerializeExtensionMap(camera.extensions, o); } static void SerializeGltfScene(Scene &scene, json &o) { @@ -7238,7 +7283,7 @@ static void SerializeGltfModel(Model *model, json &o) { JsonAddMember(o, "asset", std::move(asset)); // BUFFERVIEWS - if(model->bufferViews.size()) { + if (model->bufferViews.size()) { json bufferViews; JsonReserveArray(bufferViews, model->bufferViews.size()); for (unsigned int i = 0; i < model->bufferViews.size(); ++i) { @@ -7249,11 +7294,6 @@ static void SerializeGltfModel(Model *model, json &o) { JsonAddMember(o, "bufferViews", std::move(bufferViews)); } - // Extensions used - if (model->extensionsUsed.size()) { - SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o); - } - // Extensions required if (model->extensionsRequired.size()) { SerializeStringArrayProperty("extensionsRequired", @@ -7364,7 +7404,9 @@ static void SerializeGltfModel(Model *model, json &o) { // EXTENSIONS SerializeExtensionMap(model->extensions, o); - // LIGHTS as KHR_lights_cmn + auto extensionsUsed = model->extensionsUsed; + + // LIGHTS as KHR_lights_punctual if (model->lights.size()) { json lights; JsonReserveArray(lights, model->lights.size()); @@ -7379,7 +7421,7 @@ static void SerializeGltfModel(Model *model, json &o) { { json_const_iterator it; - if (!FindMember(o, "extensions", it)) { + if (FindMember(o, "extensions", it)) { JsonAssign(ext_j, GetValue(it)); } } @@ -7387,6 +7429,23 @@ static void SerializeGltfModel(Model *model, json &o) { JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn)); JsonAddMember(o, "extensions", std::move(ext_j)); + + // Also add "KHR_lights_punctual" to `extensionsUsed` + { + auto has_khr_lights_punctual = std::find_if( + extensionsUsed.begin(), extensionsUsed.end(), [](const std::string &s) { + return (s.compare("KHR_lights_punctual") == 0); + }); + + if (has_khr_lights_punctual == extensionsUsed.end()) { + extensionsUsed.push_back("KHR_lights_punctual"); + } + } + } + + // Extensions used + if (model->extensionsUsed.size()) { + SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o); } // EXTRAS @@ -7406,12 +7465,10 @@ static bool WriteGltfFile(const std::string &output, #if defined(_MSC_VER) std::ofstream gltfFile(UTF8ToWchar(output).c_str()); #elif defined(__GLIBCXX__) - int file_descriptor = - _wopen(UTF8ToWchar(output).c_str(), _O_CREAT | _O_WRONLY | - _O_TRUNC | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, - std::ios_base::out | - std::ios_base::binary); + int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream gltfFile(&wfile_buf); if (!wfile_buf.is_open()) return false; #else @@ -7497,12 +7554,10 @@ static void WriteBinaryGltfFile(const std::string &output, #if defined(_MSC_VER) std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); #elif defined(__GLIBCXX__) - int file_descriptor = - _wopen(UTF8ToWchar(output).c_str(), _O_CREAT | _O_WRONLY | - _O_TRUNC | _O_BINARY); - __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, - std::ios_base::out | - std::ios_base::binary); + int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream gltfFile(&wfile_buf); #else std::ofstream gltfFile(output.c_str(), std::ios::binary); @@ -7523,13 +7578,13 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, // BUFFERS std::vector binBuffer; - if(model->buffers.size()) { + if (model->buffers.size()) { json buffers; JsonReserveArray(buffers, model->buffers.size()); for (unsigned int i = 0; i < model->buffers.size(); ++i) { json buffer; - if (writeBinary && i==0 && model->buffers[i].uri.empty()){ - SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer); + if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { + SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); } else { SerializeGltfBuffer(model->buffers[i], buffer); } @@ -7546,10 +7601,11 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, json image; std::string dummystring = ""; - // UpdateImageObject need baseDir but only uses it if embeddedImages is - // enabled, since we won't write separate images when writing to a stream we - UpdateImageObject(model->images[i], dummystring, int(i), false, - &this->WriteImageData, this->write_image_user_data_); + // UpdateImageObject need baseDir but only uses it if embeddedImages is + // enabled, since we won't write separate images when writing to a stream + // we + UpdateImageObject(model->images[i], dummystring, int(i), false, + &this->WriteImageData, this->write_image_user_data_); SerializeGltfImage(model->images[i], image); JsonPushBack(images, std::move(image)); } @@ -7594,14 +7650,15 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, JsonReserveArray(buffers, model->buffers.size()); for (unsigned int i = 0; i < model->buffers.size(); ++i) { json buffer; - if (writeBinary && i==0 && model->buffers[i].uri.empty()){ - SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer); + if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { + SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); } else if (embedBuffers) { SerializeGltfBuffer(model->buffers[i], buffer); } else { std::string binSavePath; std::string binUri; - if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) { + if (!model->buffers[i].uri.empty() && + !IsDataURI(model->buffers[i].uri)) { binUri = model->buffers[i].uri; } else { binUri = defaultBinFilename + defaultBinFileExt; @@ -7627,7 +7684,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, } JsonPushBack(buffers, std::move(buffer)); } - JsonAddMember(output, "buffers", std::move(buffers)); + JsonAddMember(output, "buffers", std::move(buffers)); } // IMAGES @@ -7654,6 +7711,8 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, return true; } +#endif // TINYGLTF_ENABLE_SERIALIZER + } // namespace tinygltf #ifdef __clang__