Merge branch 'separate-serializer' into sajson

This commit is contained in:
Syoyo Fujita 2020-06-08 16:47:46 +09:00
commit 6fcee26d0f
7 changed files with 755 additions and 76 deletions

165
.github/workflows/c-cpp.yml vendored Normal file
View File

@ -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

View File

@ -4,18 +4,24 @@ PROJECT (tinygltf)
SET(CMAKE_CXX_STANDARD 11) 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 ADD_EXECUTABLE ( loader_example
loader_example.cc loader_example.cc
) )
endif (TINYGLTF_BUILD_LOADER_EXAMPLE)
if (TINYGLTF_BUILD_GL_EXAMPLES)
ADD_SUBDIRECTORY ( examples/gltfutil ) ADD_SUBDIRECTORY ( examples/gltfutil )
ADD_SUBDIRECTORY ( examples/glview ) ADD_SUBDIRECTORY ( examples/glview )
ADD_SUBDIRECTORY ( examples/validator ) endif (TINYGLTF_BUILD_GL_EXAMPLES)
endif (TINYGLTF_BUILD_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. # TinuGLTF is a header-only library, so no library build. just install header files.
# #

View File

@ -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) [![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 ## Features
* Written in portable C++. C++-11 with STL dependency only. * 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. * [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 * [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. * [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) * Your projects here! (Please send PR)
## TODOs ## TODOs
@ -150,6 +153,7 @@ if (!ret) {
## Compile options ## 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_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` : 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. * `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.

Binary file not shown.

View File

@ -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
]
}
]
}

View File

@ -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]") { TEST_CASE("invalid-primitive-indices", "[bounds-checking]") {
tinygltf::Model model; tinygltf::Model model;
tinygltf::TinyGLTF ctx; tinygltf::TinyGLTF ctx;
@ -359,3 +412,19 @@ TEST_CASE("image-uri-spaces", "[issue-236]") {
REQUIRE(true == ret); 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<uint8_t>(ret[0]));
REQUIRE(0xaf == static_cast<uint8_t>(ret[1]));
REQUIRE(0xb9 == static_cast<uint8_t>(ret[2]));
}
#endif

View File

@ -27,6 +27,7 @@
// Version: // Version:
// - v2.4.3 Experimental sajson(lightweight JSON parser) backend support. // - v2.4.3 Experimental sajson(lightweight JSON parser) backend support.
// Introduce TINYGLTF_ENABLE_SERIALIZER.
// - v2.4.2 Decode percent-encoded URI. // - v2.4.2 Decode percent-encoded URI.
// - v2.4.1 Fix some glTF object class does not have `extensions` and/or // - v2.4.1 Fix some glTF object class does not have `extensions` and/or
// `extras` property. // `extras` property.
@ -54,6 +55,7 @@
#include <array> #include <array>
#include <cassert> #include <cassert>
#include <cmath> // std::fabs
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@ -61,7 +63,6 @@
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include <cmath> // std::fabs
#ifndef TINYGLTF_USE_CPP14 #ifndef TINYGLTF_USE_CPP14
#include <functional> #include <functional>
@ -325,7 +326,8 @@ class Value {
} }
// Use this function if you want to have number value as int. // 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) { if (type_ == REAL_TYPE) {
return int(real_value_); return int(real_value_);
} else { } else {
@ -1116,9 +1118,9 @@ struct SpotLight {
struct Light { struct Light {
std::string name; std::string name;
std::vector<double> color; std::vector<double> color;
double intensity; double intensity{1.0};
std::string type; std::string type;
double range; double range{0.0}; // 0.0 = inifinite
SpotLight spot; SpotLight spot;
Light() : intensity(1.0), range(0.0) {} Light() : intensity(1.0), range(0.0) {}
@ -1250,7 +1252,13 @@ struct FsCallbacks {
bool FileExists(const std::string &abs_filename, void *); 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<unsigned char> *out, std::string *err, bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
const std::string &filepath, void *); const std::string &filepath, void *);
@ -1318,6 +1326,8 @@ class TinyGLTF {
const std::string &base_dir = "", const std::string &base_dir = "",
unsigned int check_sections = REQUIRE_VERSION); unsigned int check_sections = REQUIRE_VERSION);
#if defined(TINYGLTF_ENABLE_SERIALIZER)
/// ///
/// Write glTF to stream, buffers and images will be embeded /// Write glTF to stream, buffers and images will be embeded
/// ///
@ -1331,16 +1341,22 @@ class TinyGLTF {
bool embedImages, bool embedBuffers, bool embedImages, bool embedBuffers,
bool prettyPrint, bool writeBinary); bool prettyPrint, bool writeBinary);
#endif
/// ///
/// Set callback to use for loading image data /// Set callback to use for loading image data
/// ///
void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
#if defined(TINYGLTF_ENABLE_SERIALIZER)
/// ///
/// Set callback to use for writing image data /// Set callback to use for writing image data
/// ///
void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
#endif
/// ///
/// Set callbacks to use for filesystem (fs) access and their user data /// 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 #endif
#if defined(TINYGLTF_ENABLE_SERIALIZER)
void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
WriteImageData = func; WriteImageData = func;
write_image_user_data_ = user_data; write_image_user_data_ = user_data;
} }
#endif
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
static void WriteToMemory_stbi(void *context, void *data, int size) { static void WriteToMemory_stbi(void *context, void *data, int size) {
std::vector<unsigned char> *buffer = std::vector<unsigned char> *buffer =
@ -2516,6 +2536,15 @@ static inline std::wstring UTF8ToWchar(const std::string &str) {
(int)wstr.size()); (int)wstr.size());
return wstr; 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 #endif
#ifndef TINYGLTF_NO_FS #ifndef TINYGLTF_NO_FS
@ -2567,15 +2596,16 @@ bool FileExists(const std::string &abs_filename, void *) {
std::string ExpandFilePath(const std::string &filepath, void *) { std::string ExpandFilePath(const std::string &filepath, void *) {
#ifdef _WIN32 #ifdef _WIN32
DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0); // Assume input `filepath` is encoded in UTF-8
char *str = new char[len]; std::wstring wfilepath = UTF8ToWchar(filepath);
ExpandEnvironmentStringsA(filepath.c_str(), str, len); 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 #else
#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \ #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
@ -2651,9 +2681,11 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
_wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY); _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in); __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
std::istream f(&wfile_buf); 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); 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); std::ifstream f(filepath.c_str(), std::ifstream::binary);
#endif #endif
#else #else
@ -2695,12 +2727,10 @@ bool WriteWholeFile(std::string *err, const std::string &filepath,
const std::vector<unsigned char> &contents, void *) { const std::vector<unsigned char> &contents, void *) {
#ifdef _WIN32 #ifdef _WIN32
#if defined(__GLIBCXX__) // mingw #if defined(__GLIBCXX__) // mingw
int file_descriptor = int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
_wopen(UTF8ToWchar(filepath).c_str(), _O_CREAT | _O_WRONLY | _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
_O_TRUNC | _O_BINARY); __gnu_cxx::stdio_filebuf<char> wfile_buf(
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, file_descriptor, std::ios_base::out | std::ios_base::binary);
std::ios_base::out |
std::ios_base::binary);
std::ostream f(&wfile_buf); std::ostream f(&wfile_buf);
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary); 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()) { if (image.uri.size()) {
filename = GetBaseFilename(image.uri); filename = GetBaseFilename(image.uri);
ext = GetFilePathExtension(filename); ext = GetFilePathExtension(filename);
} } else if (image.bufferView != -1) {
else if (image.bufferView != -1) { // If there's no URI and the data exists in a buffer,
//If there's no URI and the data exists in a buffer, // don't change properties or write images
//don't change properties or write images
} else if (image.name.size()) { } else if (image.name.size()) {
ext = MimeToExt(image.mimeType); ext = MimeToExt(image.mimeType);
// Otherwise use name as filename // Otherwise use name as filename
@ -5761,9 +5790,11 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
.target = TINYGLTF_TARGET_ARRAY_BUFFER; .target = TINYGLTF_TARGET_ARRAY_BUFFER;
} }
for(auto &target : primitive.targets) { for (auto &target : primitive.targets) {
for(auto &attribute : target) { for (auto &attribute : target) {
model->bufferViews[size_t(model->accessors[size_t(attribute.second)].bufferView)] model
->bufferViews[size_t(
model->accessors[size_t(attribute.second)].bufferView)]
.target = TINYGLTF_TARGET_ARRAY_BUFFER; .target = TINYGLTF_TARGET_ARRAY_BUFFER;
} }
} }
@ -6273,6 +6304,8 @@ bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
return ret; return ret;
} }
#if defined(TINYGLTF_ENABLE_SERIALIZER)
/////////////////////// ///////////////////////
// GLTF Serialization // GLTF Serialization
/////////////////////// ///////////////////////
@ -6349,6 +6382,13 @@ static void SerializeNumberProperty(const std::string &key, T number,
JsonAddMember(obj, key.c_str(), json(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<uint64_t>(number)));
}
#endif
template <typename T> template <typename T>
static void SerializeNumberArrayProperty(const std::string &key, static void SerializeNumberArrayProperty(const std::string &key,
const std::vector<T> &value, const std::vector<T> &value,
@ -6499,12 +6539,10 @@ static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
const std::string &binFilename) { const std::string &binFilename) {
#ifdef _WIN32 #ifdef _WIN32
#if defined(__GLIBCXX__) // mingw #if defined(__GLIBCXX__) // mingw
int file_descriptor = int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
_wopen(UTF8ToWchar(binFilename).c_str(), _O_CREAT | _O_WRONLY | _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
_O_TRUNC | _O_BINARY); __gnu_cxx::stdio_filebuf<char> wfile_buf(
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, file_descriptor, std::ios_base::out | std::ios_base::binary);
std::ios_base::out |
std::ios_base::binary);
std::ostream output(&wfile_buf); std::ostream output(&wfile_buf);
if (!wfile_buf.is_open()) return false; if (!wfile_buf.is_open()) return false;
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
@ -7046,7 +7084,9 @@ static void SerializeSpotLight(SpotLight &spot, json &o) {
static void SerializeGltfLight(Light &light, json &o) { static void SerializeGltfLight(Light &light, json &o) {
if (!light.name.empty()) SerializeStringProperty("name", light.name, o); if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
SerializeNumberProperty("intensity", light.intensity, o); SerializeNumberProperty("intensity", light.intensity, o);
if (light.range > 0.0) {
SerializeNumberProperty("range", light.range, o); SerializeNumberProperty("range", light.range, o);
}
SerializeNumberArrayProperty("color", light.color, o); SerializeNumberArrayProperty("color", light.color, o);
SerializeStringProperty("type", light.type, o); SerializeStringProperty("type", light.type, o);
if (light.type == "spot") { if (light.type == "spot") {
@ -7160,6 +7200,11 @@ static void SerializeGltfCamera(const Camera &camera, json &o) {
} else { } else {
// ??? // ???
} }
if (camera.extras.Type() != NULL_TYPE) {
SerializeValue("extras", camera.extras, o);
}
SerializeExtensionMap(camera.extensions, o);
} }
static void SerializeGltfScene(Scene &scene, json &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)); JsonAddMember(o, "asset", std::move(asset));
// BUFFERVIEWS // BUFFERVIEWS
if(model->bufferViews.size()) { if (model->bufferViews.size()) {
json bufferViews; json bufferViews;
JsonReserveArray(bufferViews, model->bufferViews.size()); JsonReserveArray(bufferViews, model->bufferViews.size());
for (unsigned int i = 0; i < model->bufferViews.size(); ++i) { 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)); JsonAddMember(o, "bufferViews", std::move(bufferViews));
} }
// Extensions used
if (model->extensionsUsed.size()) {
SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o);
}
// Extensions required // Extensions required
if (model->extensionsRequired.size()) { if (model->extensionsRequired.size()) {
SerializeStringArrayProperty("extensionsRequired", SerializeStringArrayProperty("extensionsRequired",
@ -7364,7 +7404,9 @@ static void SerializeGltfModel(Model *model, json &o) {
// EXTENSIONS // EXTENSIONS
SerializeExtensionMap(model->extensions, o); SerializeExtensionMap(model->extensions, o);
// LIGHTS as KHR_lights_cmn auto extensionsUsed = model->extensionsUsed;
// LIGHTS as KHR_lights_punctual
if (model->lights.size()) { if (model->lights.size()) {
json lights; json lights;
JsonReserveArray(lights, model->lights.size()); JsonReserveArray(lights, model->lights.size());
@ -7379,7 +7421,7 @@ static void SerializeGltfModel(Model *model, json &o) {
{ {
json_const_iterator it; json_const_iterator it;
if (!FindMember(o, "extensions", it)) { if (FindMember(o, "extensions", it)) {
JsonAssign(ext_j, GetValue(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(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
JsonAddMember(o, "extensions", std::move(ext_j)); 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 // EXTRAS
@ -7406,12 +7465,10 @@ static bool WriteGltfFile(const std::string &output,
#if defined(_MSC_VER) #if defined(_MSC_VER)
std::ofstream gltfFile(UTF8ToWchar(output).c_str()); std::ofstream gltfFile(UTF8ToWchar(output).c_str());
#elif defined(__GLIBCXX__) #elif defined(__GLIBCXX__)
int file_descriptor = int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
_wopen(UTF8ToWchar(output).c_str(), _O_CREAT | _O_WRONLY | _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
_O_TRUNC | _O_BINARY); __gnu_cxx::stdio_filebuf<char> wfile_buf(
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, file_descriptor, std::ios_base::out | std::ios_base::binary);
std::ios_base::out |
std::ios_base::binary);
std::ostream gltfFile(&wfile_buf); std::ostream gltfFile(&wfile_buf);
if (!wfile_buf.is_open()) return false; if (!wfile_buf.is_open()) return false;
#else #else
@ -7497,12 +7554,10 @@ static void WriteBinaryGltfFile(const std::string &output,
#if defined(_MSC_VER) #if defined(_MSC_VER)
std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
#elif defined(__GLIBCXX__) #elif defined(__GLIBCXX__)
int file_descriptor = int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
_wopen(UTF8ToWchar(output).c_str(), _O_CREAT | _O_WRONLY | _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
_O_TRUNC | _O_BINARY); __gnu_cxx::stdio_filebuf<char> wfile_buf(
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, file_descriptor, std::ios_base::out | std::ios_base::binary);
std::ios_base::out |
std::ios_base::binary);
std::ostream gltfFile(&wfile_buf); std::ostream gltfFile(&wfile_buf);
#else #else
std::ofstream gltfFile(output.c_str(), std::ios::binary); std::ofstream gltfFile(output.c_str(), std::ios::binary);
@ -7523,13 +7578,13 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
// BUFFERS // BUFFERS
std::vector<unsigned char> binBuffer; std::vector<unsigned char> binBuffer;
if(model->buffers.size()) { if (model->buffers.size()) {
json buffers; json buffers;
JsonReserveArray(buffers, model->buffers.size()); JsonReserveArray(buffers, model->buffers.size());
for (unsigned int i = 0; i < model->buffers.size(); ++i) { for (unsigned int i = 0; i < model->buffers.size(); ++i) {
json buffer; json buffer;
if (writeBinary && i==0 && model->buffers[i].uri.empty()){ if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer); SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
} else { } else {
SerializeGltfBuffer(model->buffers[i], buffer); SerializeGltfBuffer(model->buffers[i], buffer);
} }
@ -7547,7 +7602,8 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
std::string dummystring = ""; std::string dummystring = "";
// UpdateImageObject need baseDir but only uses it if embeddedImages is // UpdateImageObject need baseDir but only uses it if embeddedImages is
// enabled, since we won't write separate images when writing to a stream we // enabled, since we won't write separate images when writing to a stream
// we
UpdateImageObject(model->images[i], dummystring, int(i), false, UpdateImageObject(model->images[i], dummystring, int(i), false,
&this->WriteImageData, this->write_image_user_data_); &this->WriteImageData, this->write_image_user_data_);
SerializeGltfImage(model->images[i], image); SerializeGltfImage(model->images[i], image);
@ -7594,14 +7650,15 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
JsonReserveArray(buffers, model->buffers.size()); JsonReserveArray(buffers, model->buffers.size());
for (unsigned int i = 0; i < model->buffers.size(); ++i) { for (unsigned int i = 0; i < model->buffers.size(); ++i) {
json buffer; json buffer;
if (writeBinary && i==0 && model->buffers[i].uri.empty()){ if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer); SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
} else if (embedBuffers) { } else if (embedBuffers) {
SerializeGltfBuffer(model->buffers[i], buffer); SerializeGltfBuffer(model->buffers[i], buffer);
} else { } else {
std::string binSavePath; std::string binSavePath;
std::string binUri; 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; binUri = model->buffers[i].uri;
} else { } else {
binUri = defaultBinFilename + defaultBinFileExt; binUri = defaultBinFilename + defaultBinFileExt;
@ -7654,6 +7711,8 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
return true; return true;
} }
#endif // TINYGLTF_ENABLE_SERIALIZER
} // namespace tinygltf } // namespace tinygltf
#ifdef __clang__ #ifdef __clang__