Compare commits

..

80 Commits

Author SHA1 Message Date
Syoyo Fujita
fbbeb4d6a9 Use nullptr instead of NULL. 2020-06-06 17:11:50 +09:00
Syoyo Fujita
cef1787ef8 Add regression test for PR-266. 2020-06-06 17:10:49 +09:00
cwbhhjl
2f5aa9f13b fix an error occurred while expanding utf8 path 2020-06-04 10:40:38 +08:00
Syoyo Fujita
28bafe4b11 Add GitHub Actions badge. 2020-05-30 17:06:45 +09:00
Syoyo Fujita
34894a6325 Build validator for MSVC build. 2020-05-30 02:28:58 +09:00
Syoyo Fujita
2e7006797b Separrate build of examples. 2020-05-30 02:27:25 +09:00
Syoyo Fujita
d9c03d041a Build examples for VS2019 build job. 2020-05-30 02:21:07 +09:00
Syoyo Fujita
50a3061ddf Fix path to rapidjson headers 2020-05-30 02:19:47 +09:00
Syoyo Fujita
014dce27f4 Add VS2019 build job. 2020-05-30 02:16:41 +09:00
Syoyo Fujita
c046769054 Add mingw build job 2020-05-30 02:12:48 +09:00
Syoyo Fujita
c43266fdc9 Add argument to loader_example run 2020-05-30 02:07:00 +09:00
Syoyo Fujita
aca196e8d3 add aarch64 cross compile and macos build job. 2020-05-30 02:04:06 +09:00
Syoyo Fujita
b48c027c9a Fix yaml syntax. 2020-05-30 02:00:03 +09:00
Syoyo Fujita
db5c3bea45 Add gcc-4.8 build job. 2020-05-30 01:59:02 +09:00
Syoyo Fujita
ddfa1f2f0b Fix workflow 2020-05-30 01:56:35 +09:00
Syoyo Fujita
7b69c778fe Add rapidjson build job 2020-05-29 22:21:24 +09:00
Syoyo Fujita
73e73bf3c1 Create github actions workflow 2020-05-29 20:57:39 +09:00
Syoyo Fujita
35d664e417 Merge pull request #265 from ivovandongen/ivd_rapidjson_size_t
cast size_t to uint_64_t for rapidjson serialization
2020-05-29 18:19:07 +09:00
Ivo van Dongen
272a9dfa5f cast size_t to uint_64_t for rapidjson serialization 2020-05-29 11:24:11 +03:00
Syoyo
f3bf6ee78e Add TDME2 2020-05-22 01:25:03 +09:00
Syoyo Fujita
aba57bb907 Support llvm-mingw(clang + libcxx) to open UTF-8 path in ifstream. 2020-05-18 20:50:45 +09:00
Syoyo Fujita
6fad7adb9c Fix existing "extensions" were overwritten in serialization when the scene contains lights(serialized as "KHR_light_punctual")
Append "KHR_light_punctual" to `extensionsUsed` if not exist in serialization.
Apply clang-format.
Fixes #261
2020-05-15 21:32:06 +09:00
Syoyo Fujita
3dad303bea Only serialize light.range when range > 0. Fixes #260 2020-05-13 21:22:23 +09:00
Syoyo Fujita
ff0a2e9fb4 Return type must be int for GetNumberAsInt(). Fixes #258 2020-05-11 19:07:00 +09:00
Syoyo
fe77cc5cdd Serialize extension and extras for Camera. Fixes #257 2020-05-09 02:41:07 +09:00
Syoyo Fujita
18f0e20a11 Fix float and int comparison. 2020-04-29 19:16:35 +09:00
Syoyo Fujita
978adee547 Include cmath for std::fabs 2020-04-29 17:33:48 +09:00
Syoyo Fujita
73c4cce303 Return false for zero-sized asset. Fixes #255 2020-04-28 01:06:34 +09:00
Syoyo Fujita
925b83627a Merge pull request #254 from LHLaurini/master
Fix write functions so they work with MinGW
2020-04-14 01:40:00 +09:00
Luiz Henrique Laurini
92c23725ae Fix write functions so they work with MinGW 2020-04-12 16:00:33 -03:00
Syoyo Fujita
af750a5104 Merge pull request #252 from freddiehonohan/master
Fixed small error if node has no mesh in examples/basic.
2020-04-10 02:04:12 +09:00
root
ff48fa0ff4 Fixed small error if node has no mesh. 2020-04-09 10:42:41 -04:00
Syoyo Fujita
ec45334d3d Merge branch 'master' of github.com:syoyo/tinygltf 2020-04-06 22:12:39 +09:00
Syoyo Fujita
5f603c56dc Do not segfault when the scene does not contain texture images. Fixes #251 2020-04-06 22:11:16 +09:00
Syoyo Fujita
063b8586f1 Merge pull request #250 from FsiGuy00015623/prevent-cloning-buffer-images
Prevent cloning of images in buffers
2020-03-10 14:53:13 +09:00
FsiGuy00015623
db855c6794 Prevent cloning of images in buffers
Now, in UpdateImageObject, if an image has no URI but had a valid bufferView, it will no longer change the image object or copy the buffer object to an outside file at baseDir. 
(When saving files from streams, this function would previously save textures in buffers as "#.png" in the program's working directory instead of the model file's directory, and create a broken link in the updated image object if the two locations were different.)
2020-03-09 16:57:21 -05:00
Syoyo Fujita
e391f6b03e Merge pull request #249 from CesiumGS/accessor-index-fix
Ensures only valid bufferViews are mentioned in accessor
2020-03-05 23:59:23 +09:00
Sanjeet Suhag
5ecede71f0 Ensures only valid bufferViews are mentioned in accessor 2020-03-04 17:40:10 -05:00
Syoyo Fujita
40982716f9 Merge branch 'uri-decode' 2020-03-03 18:49:27 +09:00
Syoyo Fujita
4bdc96fb9b Update copyright year.
Add note on the result from fuzzer test.
2020-02-24 01:36:24 +09:00
Syoyo Fujita
96ecea080a Merge pull request #243 from mfpgs/reading_copyright
Reading copyright field in ParseAsset()
2020-02-20 23:42:14 +09:00
Miguel Sousa
22bfc843ee Reading copyright field in ParseAsset() 2020-02-20 14:16:58 +01:00
Syoyo Fujita
eaa25a307a Merge pull request #242 from Selmar/some_empty_array_fixes
Some empty array fixes
2020-01-29 01:27:44 +09:00
Selmar Kok
ab600b8e72 Merge remote-tracking branch 'origin/master' into some_empty_array_fixes 2020-01-28 13:46:22 +01:00
Selmar Kok
eb9d29c06e dont serialize a couple of empty arrays 2020-01-28 13:45:38 +01:00
Syoyo Fujita
57356933c5 Merge pull request #240 from kacprzak/master
Set target for bufferView used by MorphTargets
2020-01-22 22:32:41 +09:00
Marcin Kacprzak
c3d6716c56 Set target for bufferView used by MorphTargets 2020-01-22 13:13:35 +01:00
Syoyo Fujita
ac5e09f8c3 Merge pull request #239 from Selmar/channel_target_extensions
Channel target extensions
2020-01-22 03:52:20 +09:00
Selmar Kok
e2c3fe1c0b Merge remote-tracking branch 'origin/master' into channel_target_extensions 2020-01-21 18:46:58 +01:00
Selmar Kok
973d9b3394 add channel.target extensions 2020-01-21 18:45:24 +01:00
Syoyo Fujita
28ad4ab7b8 Add clang fuzzer tester. 2020-01-18 20:31:54 +09:00
Syoyo Fujita
0227ae5d9b Merge pull request #238 from sammyKhan/patch-1
Initialize defaultScene to -1
2020-01-17 21:58:00 +09:00
sammyKhan
a0a62bde1c Initialize defaultScene to -1
To prevent undefined behavior if the model is serialized without defaultScene being set explicitly.
2020-01-17 13:41:16 +01:00
Syoyo Fujita
23d1ff2880 Possible fix for error: invalid path 'models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png ' on Windows(NTFS? dislikes extra white space after the file extension ) 2020-01-17 16:15:03 +09:00
Syoyo Fujita
bcf2ce586e Merge pull request #237 from FrankGalligan/fix_image_uri_with_spaces
Fix loading images with spaces on Linux
2020-01-14 18:00:48 +09:00
Frank Galligan
aa3c5a1cad Fix loading images with spaces on Linux
This change quotes the string before it is passed into wordexp.

This addresses issue https://github.com/syoyo/tinygltf/issues/236
2020-01-13 15:06:56 -08:00
Syoyo Fujita
db2fbeec9d Add dlib license. 2020-01-08 02:39:46 +09:00
Syoyo Fujita
c4166e4c60 Initial support of decoding percent-encoding URI. 2020-01-08 02:38:01 +09:00
Syoyo Fujita
1cf179d377 Merge pull request #235 from ziocleto/master
BufferView ctor now uses correct members order.
2020-01-08 00:52:09 +09:00
Syoyo Fujita
ddc14f8ba6 Merge pull request #233 from 8i/fix-animation-sampler-serialization
Reserve space for animation samplers in serialization
2020-01-08 00:47:41 +09:00
Syoyo Fujita
72f4a55edd Suppress clang warnings. Fixes #234 2020-01-08 00:40:41 +09:00
Dado
379bb612f1 BufferView ctor now uses correct members order.
tiny_gltf.h line 815 
(Moved target(0) to second to last)
2020-01-07 14:41:12 +00:00
Jacek
1da4e5d633 Reserve space for animation samplers in serialization 2020-01-06 15:15:21 -06:00
Syoyo Fujita
4e47bc7799 Fill BufferView variables with initial/invalid values just in case. 2020-01-02 22:07:25 +09:00
Syoyo Fujita
9cc3e91968 Merge pull request #232 from kacprzak/master
Do not set target on bufferView pointing to animation data
2020-01-02 21:53:53 +09:00
Marcin Kacprzak
d09788d10f Do not set target on bufferView pointing to animation data 2020-01-02 13:00:48 +01:00
Syoyo Fujita
a11f6e1939 Allow empty buffer when serializing glTF buffer data. 2019-12-19 14:20:01 +09:00
Syoyo Fujita
8ced108571 Merge pull request #230 from itainter/fix-primitive-extension
Fix the issue that the extension of primitive written as extension of mesh.
2019-12-18 13:33:46 +09:00
zhaozhiquan
3eb65e269a Fix the issue that the extension of primitive written as extension of mesh. 2019-12-18 11:28:57 +08:00
Syoyo Fujita
1322c7bf09 Merge branch 'master' of github.com:syoyo/tinygltf 2019-11-24 20:50:50 +09:00
Syoyo Fujita
49651fe3d9 Update READE. 2019-11-24 20:50:35 +09:00
Syoyo Fujita
db93a87c4a Merge pull request #226 from epajarre/glb-bin-blob
Glb bin blob
2019-11-19 01:30:10 +09:00
Eero Pajarre
7b0d81bf12 Added support for BIN chunk when saving in glb format
If saving in glb (binary) format the first buffer in
model is saved as gbl chunk number 1 in binary format.
This operation is not performed if the first buffer
as an url specified.
2019-11-18 14:15:48 +02:00
Eero Pajarre
2e8a115d7e Only serialize Accessor.normalized if it is true 2019-11-18 13:09:25 +02:00
Eero Pajarre
ae9364902a Added Accessor initializers
Added Accessor intializers
Now byteOffset and normalized are initialized to their default
values. componentType and Type are initialized to bad values
on purpose, they must be set when loading or creating the model.
2019-11-18 12:59:05 +02:00
Syoyo Fujita
06d2fbdae6 not is alternative keyword and not recommended to use. Fixes #225 2019-11-17 02:11:59 +09:00
Syoyo Fujita
d5ff5cbfb1 Merge branch 'master' of github.com:syoyo/tinygltf 2019-11-16 17:00:42 +09:00
Syoyo Fujita
1d20520680 Correct computation of padding size. Fixes #224. 2019-11-16 17:00:17 +09:00
Syoyo Fujita
4ab0386d09 Fix MinGW code path reused linux code path. 2019-11-10 15:31:17 +09:00
Syoyo Fujita
a471770f38 Merge pull request #223 from syoyo/mingw-fix
Mingw fix
2019-11-10 02:03:18 +09:00
17 changed files with 1560 additions and 188 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)
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.
#

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)
![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.
@@ -34,8 +36,11 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
* Moderate parsing time and memory consumption.
* glTF specification v2.0.0
* [x] ASCII glTF
* [x] Load
* [x] Save
* [x] Binary glTF(GLB)
* [x] PBR material description
* [x] Load
* [x] Save(.bin embedded .glb)
* Buffers
* [x] Parse BASE64 encoded embedded buffer data(DataURI).
* [x] Load `.bin` file.
@@ -55,6 +60,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
* [x] Image save
* Extensions
* [x] Draco mesh decoding
* [ ] Draco mesh encoding
## Note on extension property
@@ -79,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
@@ -160,14 +167,17 @@ if (!ret) {
### Saving gltTF 2.0 model
* [ ] Buffers.
* Buffers.
* [x] To file
* [x] Embedded
* [ ] Draco compressed?
* [x] Images
* [x] To file
* [x] Embedded
* [ ] Binary(.glb)
* Binary(.glb)
* [x] .bin embedded single .glb
* [ ] External .bin
## Running tests.
@@ -195,9 +205,17 @@ $ ./tester
$ ./tester_noexcept
```
### Fuzzing tests
See `tests/fuzzer` for details.
After running fuzzer on Ryzen9 3950X a week, at least `LoadASCIIFromString` looks safe except for out-of-memory error in Fuzzer.
We may be better to introduce bounded memory size checking when parsing glTF data.
## Third party licenses
* json.hpp : Licensed under the MIT License <http://opensource.org/licenses/MIT>. Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
* stb_image : Public domain.
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.
* RapidJSON : Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. http://rapidjson.org/
* dlib(uridecode, uriencode) : Copyright (C) 2003 Davis E. King Boost Software License 1.0. http://dlib.net/dlib/server/server_http.cpp.html

View File

@@ -19,3 +19,7 @@ $ make
Plese use solution file located at `basic` folder.
## Limitation
There are so many limitations in this example(e.g. no PBR shader. the shader only shows texture of textures[0] if available).

View File

@@ -100,42 +100,49 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
std::cout << "vaa missing: " << attrib.first << std::endl;
}
GLuint texid;
glGenTextures(1, &texid);
if (model.textures.size() > 0) {
// fixme: Use material's baseColor
tinygltf::Texture &tex = model.textures[0];
tinygltf::Texture &tex = model.textures[0];
tinygltf::Image &image = model.images[tex.source];
if (tex.source > -1) {
glBindTexture(GL_TEXTURE_2D, texid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GLuint texid;
glGenTextures(1, &texid);
GLenum format = GL_RGBA;
tinygltf::Image &image = model.images[tex.source];
if (image.component == 1) {
format = GL_RED;
} else if (image.component == 2) {
format = GL_RG;
} else if (image.component == 3) {
format = GL_RGB;
} else {
// ???
glBindTexture(GL_TEXTURE_2D, texid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GLenum format = GL_RGBA;
if (image.component == 1) {
format = GL_RED;
} else if (image.component == 2) {
format = GL_RG;
} else if (image.component == 3) {
format = GL_RGB;
} else {
// ???
}
GLenum type = GL_UNSIGNED_BYTE;
if (image.bits == 8) {
// ok
} else if (image.bits == 16) {
type = GL_UNSIGNED_SHORT;
} else {
// ???
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
format, type, &image.image.at(0));
}
}
GLenum type = GL_UNSIGNED_BYTE;
if (image.bits == 8) {
// ok
} else if (image.bits == 16) {
type = GL_UNSIGNED_SHORT;
} else {
// ???
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
format, type, &image.image.at(0));
}
return vbos;
@@ -144,8 +151,12 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
// bind models
void bindModelNodes(std::map<int, GLuint> vbos, tinygltf::Model &model,
tinygltf::Node &node) {
bindMesh(vbos, model, model.meshes[node.mesh]);
if ((node.mesh >= 0) && (node.mesh < model.meshes.size())) {
bindMesh(vbos, model, model.meshes[node.mesh]);
}
for (size_t i = 0; i < node.children.size(); i++) {
assert((node.children[i] >= 0) && (node.children[i] < model.nodes.size()));
bindModelNodes(vbos, model, model.nodes[node.children[i]]);
}
}
@@ -157,6 +168,7 @@ GLuint bindModel(tinygltf::Model &model) {
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
for (size_t i = 0; i < scene.nodes.size(); ++i) {
assert((scene.nodes[i] >= 0) && (scene.nodes[i] < model.nodes.size()));
bindModelNodes(vbos, model, model.nodes[scene.nodes[i]]);
}
@@ -182,7 +194,9 @@ void drawMesh(tinygltf::Model &model, tinygltf::Mesh &mesh) {
// recursively draw node and children nodes of model
void drawModelNodes(tinygltf::Model &model, tinygltf::Node &node) {
drawMesh(model, model.meshes[node.mesh]);
if ((node.mesh >= 0) && (node.mesh < model.meshes.size())) {
drawMesh(model, model.meshes[node.mesh]);
}
for (size_t i = 0; i < node.children.size(); i++) {
drawModelNodes(model, model.nodes[node.children[i]]);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

View File

@@ -0,0 +1,171 @@
{
"asset": {
"version": "2.0"
},
"scenes": [
{
"nodes": [
0
]
}
],
"scene": 0,
"nodes": [
{
"mesh": 0
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 2,
"POSITION": 1,
"TEXCOORD_0": 3
},
"indices": 0,
"mode": 4,
"material": 0
}
]
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0,
"texCoord": 0
},
"baseColorFactor": [
1,
1,
1,
1
],
"metallicFactor": 1,
"roughnessFactor": 1
},
"emissiveFactor": [
0,
0,
0
],
"alphaMode": "OPAQUE"
}
],
"textures": [
{
"source": 0,
"sampler": 0
}
],
"samplers": [
{
"wrapS": 33071,
"wrapT": 33071
}
],
"images": [
{
"uri": " 2x2 image has multiple spaces.png"
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5121,
"count": 36,
"normalized": false,
"max": [
23
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"normalized": false,
"max": [
0.5,
0.5,
0.5
],
"min": [
-0.5,
-0.5,
-0.5
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"normalized": false,
"max": [
1,
1,
1
],
"min": [
-1,
-1,
-1
],
"type": "VEC3"
},
{
"bufferView": 3,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"normalized": false,
"max": [
1,
1
],
"min": [
0,
0
],
"type": "VEC2"
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 36
},
{
"buffer": 0,
"byteOffset": 36,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 324,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 612,
"byteLength": 192
}
],
"buffers": [
{
"byteLength": 804,
"uri": "CubeImageUriSpaces.bin"
}
]
}

Binary file not shown.

View File

@@ -0,0 +1,171 @@
{
"asset": {
"version": "2.0"
},
"scenes": [
{
"nodes": [
0
]
}
],
"scene": 0,
"nodes": [
{
"mesh": 0
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 2,
"POSITION": 1,
"TEXCOORD_0": 3
},
"indices": 0,
"mode": 4,
"material": 0
}
]
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0,
"texCoord": 0
},
"baseColorFactor": [
1,
1,
1,
1
],
"metallicFactor": 1,
"roughnessFactor": 1
},
"emissiveFactor": [
0,
0,
0
],
"alphaMode": "OPAQUE"
}
],
"textures": [
{
"source": 0,
"sampler": 0
}
],
"samplers": [
{
"wrapS": 33071,
"wrapT": 33071
}
],
"images": [
{
"uri": "2x2 image has spaces.png"
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5121,
"count": 36,
"normalized": false,
"max": [
23
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"normalized": false,
"max": [
0.5,
0.5,
0.5
],
"min": [
-0.5,
-0.5,
-0.5
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"normalized": false,
"max": [
1,
1,
1
],
"min": [
-1,
-1,
-1
],
"type": "VEC3"
},
{
"bufferView": 3,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"normalized": false,
"max": [
1,
1
],
"min": [
0,
0
],
"type": "VEC2"
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 36
},
{
"buffer": 0,
"byteOffset": 36,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 324,
"byteLength": 288
},
{
"buffer": 0,
"byteOffset": 612,
"byteLength": 192
}
],
"buffers": [
{
"byteLength": 804,
"uri": "CubeImageUriSpaces.bin"
}
]
}

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

46
tests/fuzzer/README.md Normal file
View File

@@ -0,0 +1,46 @@
# Fuzzing test
Do fuzzing test for TinyGLTF API.
## Supported API
* [x] LoadASCIIFromMemory
* [ ] LoadBinaryFromMemory
## Requirements
* meson
* clang with fuzzer support(`-fsanitize=fuzzer`. at least clang 8.0 should work)
## Setup
### Ubuntu 18.04
```
$ sudo apt install clang++-8
$ sudo apt install libfuzzer-8-dev
```
Optionally, if you didn't set `update-alternatives` you can set `clang++` to point to `clang++8`
```
$ sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 10
$ sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 10
```
## How to compile
```
$ CXX=clang++ CC=clang meson build
$ cd build
$ ninja
```
## How to run
Increase memory limit. e.g. `-rss_limit_mb=50000`
```
$ ./fuzz_gltf -rss_limit_mb=20000 -jobs 4
```

33
tests/fuzzer/fuzz_gltf.cc Normal file
View File

@@ -0,0 +1,33 @@
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define TINYGLTF_IMPLEMENTATION
#include "tiny_gltf.h"
static void parse_intCoding4(const uint8_t *data, size_t size)
{
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
const char *str = reinterpret_cast<const char *>(data);
bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, str, size, /* base_dir */"" );
(void)ret;
}
extern "C"
int LLVMFuzzerTestOneInput(std::uint8_t const* data, std::size_t size)
{
parse_intCoding4(data, size);
return 0;
}

9
tests/fuzzer/meson.build Normal file
View File

@@ -0,0 +1,9 @@
project('fuzz_tinygltf', 'cpp', default_options : ['cpp_std=c++11'])
incdirs = include_directories('../../')
executable('fuzz_gltf',
'fuzz_gltf.cc',
include_directories : incdirs,
cpp_args : '-fsanitize=address,fuzzer',
link_args : '-fsanitize=address,fuzzer' )

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]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
@@ -333,3 +386,45 @@ TEST_CASE("pbr-khr-texture-transform", "[material]") {
REQUIRE(scale[1] == Approx(-1.0));
}
TEST_CASE("image-uri-spaces", "[issue-236]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
// Test image file with single spaces.
bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriSpaces.gltf");
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
// Test image file with a beginning space, trailing space, and greater than
// one consecutive spaces.
ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf");
if (!err.empty()) {
std::cerr << err << std::endl;
}
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

@@ -4,7 +4,7 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2015 - 2019 Syoyo Fujita, Aurélien Chatelain and many
// Copyright (c) 2015 - 2020 Syoyo Fujita, Aurélien Chatelain and many
// contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -26,6 +26,7 @@
// THE SOFTWARE.
// Version:
// - v2.4.2 Decode percent-encoded URI.
// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
// `extras` property.
// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
@@ -51,6 +52,7 @@
#include <array>
#include <cassert>
#include <cmath> // std::fabs
#include <cstdint>
#include <cstdlib>
#include <cstring>
@@ -321,7 +323,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 {
@@ -526,10 +529,12 @@ struct AnimationChannel {
// "weights"]
Value extras;
ExtensionMap extensions;
ExtensionMap target_extensions;
// Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
std::string extras_json_string;
std::string extensions_json_string;
std::string target_extensions_json_string;
AnimationChannel() : sampler(-1), target_node(-1) {}
DEFAULT_METHODS(AnimationChannel)
@@ -637,7 +642,8 @@ struct 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; // (required if no mimeType)
std::string uri; // (required if no mimeType) uri is not decoded(e.g.
// whitespace may be represented as %20)
Value extras;
ExtensionMap extensions;
@@ -797,12 +803,13 @@ struct Material {
struct BufferView {
std::string name;
int buffer; // Required
size_t byteOffset; // minimum 0, default 0
size_t byteLength; // required, minimum 1
size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
// understood to be tightly packed
int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
int buffer{-1}; // Required
size_t byteOffset{0}; // minimum 0, default 0
size_t byteLength{0}; // required, minimum 1. 0 = invalid
size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
// understood to be tightly packed
int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
// or atttribs. Could be 0 for other data
Value extras;
ExtensionMap extensions;
@@ -810,9 +817,15 @@ struct BufferView {
std::string extras_json_string;
std::string extensions_json_string;
bool dracoDecoded; // Flag indicating this has been draco decoded
bool dracoDecoded{false}; // Flag indicating this has been draco decoded
BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {}
BufferView()
: buffer(-1),
byteOffset(0),
byteLength(0),
byteStride(0),
target(0),
dracoDecoded(false) {}
DEFAULT_METHODS(BufferView)
bool operator==(const BufferView &) const;
};
@@ -887,8 +900,13 @@ struct Accessor {
// unreachable return 0;
}
Accessor() {
bufferView = -1;
Accessor()
: bufferView(-1),
byteOffset(0),
normalized(false),
componentType(-1),
count(0),
type(-1) {
sparse.isSparse = false;
}
DEFAULT_METHODS(Accessor)
@@ -1032,6 +1050,7 @@ struct Buffer {
std::vector<unsigned char> data;
std::string
uri; // considered as required here but not in the spec (need to clarify)
// uri is not decoded(e.g. whitespace may be represented as %20)
Value extras;
ExtensionMap extensions;
@@ -1096,9 +1115,9 @@ struct SpotLight {
struct Light {
std::string name;
std::vector<double> 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) {}
@@ -1136,7 +1155,7 @@ class Model {
std::vector<Scene> scenes;
std::vector<Light> lights;
int defaultScene;
int defaultScene = -1;
std::vector<std::string> extensionsUsed;
std::vector<std::string> extensionsRequired;
@@ -1542,10 +1561,11 @@ class TinyGLTF {
#undef NOMINMAX
#endif
#if defined(__GLIBCXX__) // mingw
#if defined(__GLIBCXX__) // mingw
#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
#include <fcntl.h> // _O_RDONLY
#include <fcntl.h> // _O_RDONLY
#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
#endif
@@ -2114,6 +2134,88 @@ std::string base64_decode(std::string const &encoded_string) {
#pragma clang diagnostic pop
#endif
// https://github.com/syoyo/tinygltf/issues/228
// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
// decoding?
//
// https://stackoverflow.com/questions/18307429/encode-decode-url-in-c
// http://dlib.net/dlib/server/server_http.cpp.html
// --- dlib beign ------------------------------------------------------------
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
namespace dlib {
#if 0
inline unsigned char to_hex( unsigned char x )
{
return x + (x > 9 ? ('A'-10) : '0');
}
const std::string urlencode( const std::string& s )
{
std::ostringstream os;
for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci )
{
if ( (*ci >= 'a' && *ci <= 'z') ||
(*ci >= 'A' && *ci <= 'Z') ||
(*ci >= '0' && *ci <= '9') )
{ // allowed
os << *ci;
}
else if ( *ci == ' ')
{
os << '+';
}
else
{
os << '%' << to_hex(static_cast<unsigned char>(*ci >> 4)) << to_hex(static_cast<unsigned char>(*ci % 16));
}
}
return os.str();
}
#endif
inline unsigned char from_hex(unsigned char ch) {
if (ch <= '9' && ch >= '0')
ch -= '0';
else if (ch <= 'f' && ch >= 'a')
ch -= 'a' - 10;
else if (ch <= 'F' && ch >= 'A')
ch -= 'A' - 10;
else
ch = 0;
return ch;
}
static const std::string urldecode(const std::string &str) {
using namespace std;
string result;
string::size_type i;
for (i = 0; i < str.size(); ++i) {
if (str[i] == '+') {
result += ' ';
} else if (str[i] == '%' && str.size() > i + 2) {
const unsigned char ch1 =
from_hex(static_cast<unsigned char>(str[i + 1]));
const unsigned char ch2 =
from_hex(static_cast<unsigned char>(str[i + 2]));
const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
result += static_cast<char>(ch);
i += 2;
} else {
result += str[i];
}
}
return result;
}
} // namespace dlib
// --- dlib end --------------------------------------------------------------
static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
std::string *warn, const std::string &filename,
const std::string &basedir, bool required,
@@ -2374,11 +2476,22 @@ void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
#ifdef _WIN32
static inline std::wstring UTF8ToWchar(const std::string &str) {
int wstr_size = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
int wstr_size =
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
std::wstring wstr(wstr_size, 0);
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], (int)wstr.size());
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
(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
@@ -2430,15 +2543,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) || \
@@ -2453,8 +2567,10 @@ std::string ExpandFilePath(const std::string &filepath, void *) {
return "";
}
// Quote the string to keep any spaces in filepath intact.
std::string quoted_path = "\"" + filepath + "\"";
// char** w;
int ret = wordexp(filepath.c_str(), &p, 0);
int ret = wordexp(quoted_path.c_str(), &p, 0);
if (ret) {
// err
s = filepath;
@@ -2488,11 +2604,12 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
return false;
}
size_t size = AAsset_getLength(asset);
if (size <= 0) {
if (size == 0) {
if (err) {
(*err) += "Invalid file size : " + filepath +
" (does the path point to a directory?)";
}
return false;
}
out->resize(size);
AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
@@ -2506,13 +2623,16 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
}
#else
#ifdef _WIN32
#if defined(__GLIBCXX__) // mingw
int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
#if defined(__GLIBCXX__) // mingw
int file_descriptor =
_wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> 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
@@ -2553,13 +2673,15 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
bool WriteWholeFile(std::string *err, const std::string &filepath,
const std::vector<unsigned char> &contents, void *) {
#ifdef _WIN32
#if defined(__GLIBCXX__) // mingw
int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), _O_WRONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
#if defined(__GLIBCXX__) // mingw
int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> 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);
#else // clang?
#else // clang?
std::ofstream f(filepath.c_str(), std::ofstream::binary);
#endif
#else
@@ -2606,12 +2728,13 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
void *user_data = nullptr) {
std::string filename;
std::string ext;
// If image have uri. Use it it as a filename
// If image has uri, use it it as a filename
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.name.size()) {
ext = MimeToExt(image.mimeType);
// Otherwise use name as filename
@@ -2623,7 +2746,7 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
}
// If callback is set, modify image data object
if (*WriteImageData != nullptr) {
if (*WriteImageData != nullptr && !filename.empty()) {
std::string uri;
(*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
}
@@ -2723,6 +2846,7 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
}
}
// TODO(syoyo): Allow empty buffer? #229
if (data.empty()) {
return false;
}
@@ -3503,6 +3627,7 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o,
ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
ParseExtensionsProperty(&asset->extensions, err, o);
@@ -3641,7 +3766,10 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
return true;
#endif
if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
std::string decoded_uri = dlib::urldecode(uri);
if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
/* required */ false, /* required bytes */ 0,
/* checksize */ false, fs)) {
if (warn) {
(*warn) += "Failed to load external 'uri' for image[" +
std::to_string(image_idx) + "] name = [" + image->name +
@@ -3863,9 +3991,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
}
} else {
// External .bin file.
std::string decoded_uri = dlib::urldecode(buffer->uri);
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
buffer->uri, basedir, true, byteLength, true,
fs)) {
decoded_uri, basedir, /* required */ true,
byteLength, /* checkSize */ true, fs)) {
return false;
}
}
@@ -3907,8 +4036,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
}
} else {
// Assume external .bin file.
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
basedir, true, byteLength, true, fs)) {
std::string decoded_uri = dlib::urldecode(buffer->uri);
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
basedir, /* required */ true, byteLength,
/* checkSize */ true, fs)) {
return false;
}
}
@@ -4746,6 +4877,13 @@ static bool ParseAnimationChannel(
}
return false;
}
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;
@@ -5247,7 +5385,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
defined(_CPPUNWIND)) && \
not defined(TINYGLTF_NOEXCEPTION)
!defined(TINYGLTF_NOEXCEPTION)
try {
JsonParse(v, json_str, json_str_length, true);
@@ -5520,7 +5658,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// Assign missing bufferView target types
// - Look for missing Mesh indices
// - Look for missing bufferView targets
// - Look for missing Mesh attributes
for (auto &mesh : model->meshes) {
for (auto &primitive : mesh.primitives) {
if (primitive.indices >
@@ -5548,14 +5686,22 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// we could optionally check if acessors' bufferView type is Scalar, as
// it should be
}
}
}
// find any missing targets, must be an array buffer type if not fulfilled
// from previous check
for (auto &bufferView : model->bufferViews) {
if (bufferView.target == 0) // missing target type
{
bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
for (auto &attribute : primitive.attributes) {
model
->bufferViews[size_t(
model->accessors[size_t(attribute.second)].bufferView)]
.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)]
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
}
}
}
}
@@ -6138,6 +6284,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<uint64_t>(number)));
}
#endif
template <typename T>
static void SerializeNumberArrayProperty(const std::string &key,
const std::vector<T> &value,
@@ -6273,17 +6426,25 @@ static void SerializeValue(const std::string &key, const Value &value,
static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
json &o) {
std::string header = "data:application/octet-stream;base64,";
std::string encodedData =
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
SerializeStringProperty("uri", header + encodedData, o);
if (data.size() > 0) {
std::string encodedData =
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
SerializeStringProperty("uri", header + encodedData, o);
} else {
// Issue #229
// size 0 is allowd. Just emit mime header.
SerializeStringProperty("uri", header, o);
}
}
static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
const std::string &binFilename) {
#ifdef _WIN32
#if defined(__GLIBCXX__) // mingw
int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(), _O_WRONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
#if defined(__GLIBCXX__) // mingw
int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> 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)
@@ -6297,8 +6458,14 @@ static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
std::ofstream output(binFilename.c_str(), std::ofstream::binary);
if (!output.is_open()) return false;
#endif
output.write(reinterpret_cast<const char *>(&data[0]),
std::streamsize(data.size()));
if (data.size() > 0) {
output.write(reinterpret_cast<const char *>(&data[0]),
std::streamsize(data.size()));
} else {
// Issue #229
// size 0 will be still valid buffer data.
// write empty file.
}
return true;
}
@@ -6360,16 +6527,18 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
}
static void SerializeGltfAccessor(Accessor &accessor, json &o) {
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
if (accessor.bufferView >= 0)
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
if (accessor.byteOffset != 0.0)
if (accessor.byteOffset != 0)
SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
SerializeNumberProperty<int>("componentType", accessor.componentType, o);
SerializeNumberProperty<size_t>("count", accessor.count, o);
SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
SerializeValue("normalized", Value(accessor.normalized), o);
if (accessor.normalized)
SerializeValue("normalized", Value(accessor.normalized), o);
std::string type;
switch (accessor.type) {
case TINYGLTF_TYPE_SCALAR:
@@ -6410,6 +6579,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
SerializeNumberProperty("node", channel.target_node, target);
SerializeStringProperty("path", channel.target_path, target);
SerializeExtensionMap(channel.target_extensions, target);
JsonAddMember(o, "target", std::move(target));
}
@@ -6449,6 +6620,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) {
{
json samplers;
JsonReserveArray(samplers, animation.samplers.size());
for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
json sampler;
AnimationSampler gltfSampler = animation.samplers[i];
@@ -6485,6 +6657,18 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
SerializeExtensionMap(asset.extensions, o);
}
static void SerializeGltfBufferBin(Buffer &buffer, json &o,
std::vector<unsigned char> &binBuffer) {
SerializeNumberProperty("byteLength", buffer.data.size(), o);
binBuffer = buffer.data;
if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
if (buffer.extras.Type() != NULL_TYPE) {
SerializeValue("extras", buffer.extras, o);
}
}
static void SerializeGltfBuffer(Buffer &buffer, json &o) {
SerializeNumberProperty("byteLength", buffer.data.size(), o);
SerializeGltfBufferData(buffer.data, o);
@@ -6543,6 +6727,7 @@ static void SerializeGltfImage(Image &image, json &o) {
SerializeStringProperty("mimeType", image.mimeType, o);
SerializeNumberProperty<int>("bufferView", image.bufferView, o);
} else {
// TODO(syoyo): dlib::urilencode?
SerializeStringProperty("uri", image.uri, o);
}
@@ -6659,8 +6844,8 @@ static void SerializeGltfMaterial(Material &material, json &o) {
SerializeStringProperty("alphaMode", material.alphaMode, o);
}
if(material.doubleSided != false)
JsonAddMember(o, "doubleSided", json(material.doubleSided));
if (material.doubleSided != false)
JsonAddMember(o, "doubleSided", json(material.doubleSided));
if (material.normalTexture.index > -1) {
json texinfo;
@@ -6764,7 +6949,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
JsonAddMember(primitive, "targets", std::move(targets));
}
SerializeExtensionMap(gltfPrimitive.extensions, o);
SerializeExtensionMap(gltfPrimitive.extensions, primitive);
if (gltfPrimitive.extras.Type() != NULL_TYPE) {
SerializeValue("extras", gltfPrimitive.extras, primitive);
@@ -6801,7 +6986,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") {
@@ -6915,6 +7102,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) {
@@ -6961,14 +7153,16 @@ static void SerializeGltfTexture(Texture &texture, json &o) {
///
static void SerializeGltfModel(Model *model, json &o) {
// ACCESSORS
json accessors;
JsonReserveArray(accessors, model->accessors.size());
for (unsigned int i = 0; i < model->accessors.size(); ++i) {
json accessor;
SerializeGltfAccessor(model->accessors[i], accessor);
JsonPushBack(accessors, std::move(accessor));
if (model->accessors.size()) {
json accessors;
JsonReserveArray(accessors, model->accessors.size());
for (unsigned int i = 0; i < model->accessors.size(); ++i) {
json accessor;
SerializeGltfAccessor(model->accessors[i], accessor);
JsonPushBack(accessors, std::move(accessor));
}
JsonAddMember(o, "accessors", std::move(accessors));
}
JsonAddMember(o, "accessors", std::move(accessors));
// ANIMATIONS
if (model->animations.size()) {
@@ -6991,18 +7185,15 @@ static void SerializeGltfModel(Model *model, json &o) {
JsonAddMember(o, "asset", std::move(asset));
// BUFFERVIEWS
json bufferViews;
JsonReserveArray(bufferViews, model->bufferViews.size());
for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
json bufferView;
SerializeGltfBufferView(model->bufferViews[i], bufferView);
JsonPushBack(bufferViews, std::move(bufferView));
}
JsonAddMember(o, "bufferViews", std::move(bufferViews));
// Extensions used
if (model->extensionsUsed.size()) {
SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o);
if (model->bufferViews.size()) {
json bufferViews;
JsonReserveArray(bufferViews, model->bufferViews.size());
for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
json bufferView;
SerializeGltfBufferView(model->bufferViews[i], bufferView);
JsonPushBack(bufferViews, std::move(bufferView));
}
JsonAddMember(o, "bufferViews", std::move(bufferViews));
}
// Extensions required
@@ -7115,7 +7306,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());
@@ -7130,7 +7323,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));
}
}
@@ -7138,6 +7331,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
@@ -7157,8 +7367,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_WRONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> 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
@@ -7173,22 +7385,37 @@ static bool WriteGltfFile(const std::string &output,
}
static void WriteBinaryGltfStream(std::ostream &stream,
const std::string &content) {
const std::string &content,
const std::vector<unsigned char> &binBuffer) {
const std::string header = "glTF";
const int version = 2;
const int padding_size = content.size() % 4;
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info,
// padding
const int length = 12 + 8 + int(content.size()) + padding_size;
// https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
auto roundUp = [](uint32_t numToRound, uint32_t multiple) {
if (multiple == 0) return numToRound;
uint32_t remainder = numToRound % multiple;
if (remainder == 0) return numToRound;
return numToRound + multiple - remainder;
};
const uint32_t padding_size =
roundUp(uint32_t(content.size()), 4) - uint32_t(content.size());
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
// Chunk data must be located at 4-byte boundary.
const uint32_t length =
12 + 8 + roundUp(uint32_t(content.size()), 4) +
(binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 0);
stream.write(header.c_str(), std::streamsize(header.size()));
stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
// JSON chunk info, then JSON data
const int model_length = int(content.size()) + padding_size;
const int model_format = 0x4E4F534A;
const uint32_t model_length = uint32_t(content.size()) + padding_size;
const uint32_t model_format = 0x4E4F534A;
stream.write(reinterpret_cast<const char *>(&model_length),
sizeof(model_length));
stream.write(reinterpret_cast<const char *>(&model_format),
@@ -7200,22 +7427,47 @@ static void WriteBinaryGltfStream(std::ostream &stream,
const std::string padding = std::string(size_t(padding_size), ' ');
stream.write(padding.c_str(), std::streamsize(padding.size()));
}
if (binBuffer.size() > 0) {
const uint32_t bin_padding_size =
roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size());
// BIN chunk info, then BIN data
const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
const uint32_t bin_format = 0x004e4942;
stream.write(reinterpret_cast<const char *>(&bin_length),
sizeof(bin_length));
stream.write(reinterpret_cast<const char *>(&bin_format),
sizeof(bin_format));
stream.write(reinterpret_cast<const char *>(binBuffer.data()),
std::streamsize(binBuffer.size()));
// Chunksize must be multiplies of 4, so pad with zeroes
if (bin_padding_size > 0) {
const std::vector<unsigned char> padding =
std::vector<unsigned char>(size_t(bin_padding_size), 0);
stream.write(reinterpret_cast<const char *>(padding.data()),
std::streamsize(padding.size()));
}
}
}
static void WriteBinaryGltfFile(const std::string &output,
const std::string &content) {
const std::string &content,
const std::vector<unsigned char> &binBuffer) {
#ifdef _WIN32
#if defined(_MSC_VER)
std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
#elif defined(__GLIBCXX__)
std::ofstream gltfFile(output.c_str(), std::ios::binary);
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> 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);
#endif
#else
std::ofstream gltfFile(output.c_str(), std::ios::binary);
#endif
WriteBinaryGltfStream(gltfFile, content);
WriteBinaryGltfStream(gltfFile, content, binBuffer);
}
bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
@@ -7227,15 +7479,21 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
SerializeGltfModel(model, output);
// BUFFERS
std::vector<std::string> usedUris;
json buffers;
JsonReserveArray(buffers, model->buffers.size());
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
json buffer;
SerializeGltfBuffer(model->buffers[i], buffer);
JsonPushBack(buffers, std::move(buffer));
std::vector<unsigned char> binBuffer;
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);
} else {
SerializeGltfBuffer(model->buffers[i], buffer);
}
JsonPushBack(buffers, std::move(buffer));
}
JsonAddMember(output, "buffers", std::move(buffers));
}
JsonAddMember(output, "buffers", std::move(buffers));
// IMAGES
if (model->images.size()) {
@@ -7245,9 +7503,9 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
json image;
std::string dummystring = "";
// UpdateImageObject need baseDir but only uses it if embededImages is
// enable, since we won't write separte images when writing to a stream we
// use a 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_);
SerializeGltfImage(model->images[i], image);
@@ -7257,7 +7515,7 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
}
if (writeBinary) {
WriteBinaryGltfStream(stream, JsonToString(output));
WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
} else {
WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
}
@@ -7288,42 +7546,48 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
// BUFFERS
std::vector<std::string> usedUris;
json buffers;
JsonReserveArray(buffers, model->buffers.size());
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
json buffer;
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)) {
binUri = model->buffers[i].uri;
std::vector<unsigned char> binBuffer;
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);
} else if (embedBuffers) {
SerializeGltfBuffer(model->buffers[i], buffer);
} else {
binUri = defaultBinFilename + defaultBinFileExt;
bool inUse = true;
int numUsed = 0;
while (inUse) {
inUse = false;
for (const std::string &usedName : usedUris) {
if (binUri.compare(usedName) != 0) continue;
inUse = true;
binUri = defaultBinFilename + std::to_string(numUsed++) +
defaultBinFileExt;
break;
std::string binSavePath;
std::string binUri;
if (!model->buffers[i].uri.empty() &&
!IsDataURI(model->buffers[i].uri)) {
binUri = model->buffers[i].uri;
} else {
binUri = defaultBinFilename + defaultBinFileExt;
bool inUse = true;
int numUsed = 0;
while (inUse) {
inUse = false;
for (const std::string &usedName : usedUris) {
if (binUri.compare(usedName) != 0) continue;
inUse = true;
binUri = defaultBinFilename + std::to_string(numUsed++) +
defaultBinFileExt;
break;
}
}
}
usedUris.push_back(binUri);
binSavePath = JoinPath(baseDir, binUri);
if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
binUri)) {
return false;
}
}
usedUris.push_back(binUri);
binSavePath = JoinPath(baseDir, binUri);
if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
binUri)) {
return false;
}
JsonPushBack(buffers, std::move(buffer));
}
JsonPushBack(buffers, std::move(buffer));
JsonAddMember(output, "buffers", std::move(buffers));
}
JsonAddMember(output, "buffers", std::move(buffers));
// IMAGES
if (model->images.size()) {
@@ -7341,7 +7605,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
}
if (writeBinary) {
WriteBinaryGltfFile(filename, JsonToString(output));
WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
} else {
WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
}