Compare commits

..

29 Commits

Author SHA1 Message Date
Syoyo Fujita
a6c3945ed3 Bump version 2.8.0. 2023-01-10 20:42:24 +09:00
Syoyo Fujita
6614bddef3 Merge pull request #397 from pknowlesnv/encode_image_uri
urlencode image urls
2023-01-10 20:31:39 +09:00
Pyarelal Knowles
385946dfd8 add URI encode/decode API
Tinygltf is able to write files defined by a URI, so it needs to be able
to decode it. Since it may also modify the path where the image is saved
it may need to re-encode the URI too. This patch provides an API to set
URI encoding and decoding callbacks and exposes the default decode
method.

Uses the existing dlib::urldecode as a decode default. The encode
callback is left null, matching existing behaviour.

Updates the WriteImageDataFunction signature to include
tinygltf::URICallbacks.

Decodes the image and buffer uris before using them as a filename.

If the encode callback is set, encodes the written image location in the
default WriteImageDataFunction and encodes generated buffer locations
when writing .bin files.

Adds a save+load step to the test image-uri-spaces to verify uri
encoding.
2023-01-09 20:54:29 -08:00
Syoyo Fujita
03bbe0921c Merge pull request #396 from pknowlesnv/image_write_failure
propagate image writing failures
2022-12-30 17:59:41 +09:00
Pyarelal Knowles
a9121550b9 SerializeGltfImage: add missing & std::string ref
Hopefully the compiler would optimize this away but better to avoid the
copy in the first place.
2022-12-29 14:12:29 -08:00
Pyarelal Knowles
d2b0af6915 propagate image writing failures
Modifies UpdateImageObject() so that Returning false from the
WriteImageDataFunction callback results in the WriteGltfScene*() call
returning false.

Does not call WriteImageDataFunction if there is no image data.

Adds test case serialize-image-failure to verify the callback return
code is able to cause an overall failure to save the gltf.
2022-12-29 13:50:17 -08:00
Syoyo Fujita
695608dd11 Update readme. 2022-12-29 21:50:47 +09:00
Syoyo Fujita
584f1dfbe4 Merge branch 'release' of github.com:syoyo/tinygltf into release 2022-12-29 21:13:48 +09:00
Syoyo Fujita
a40ca4c5ab Merge pull request #393 from pknowlesnv/serialize_const
allow serializing a const Model
2022-12-29 21:00:24 +09:00
Pyarelal Knowles
de75d87cfd allow serializing a const Model
Adds 'const' to all Serialize*() methods.

Updates WriteImageData callback to take a URI out pointer that was
previously being written to the Image::uri, which is now const.

This breaks the WriteImageData API and as a side effect, Image::uri will
no longer contain the written image URI after saving.

Adds test serialize-const-image, which verifies the model's image is not
changed (because it's const), but the uri written to the gltf json is
still correct.

Adds test serialize-image-callback that defines a WriteImageDataFunction
and also verifies the uri can be overwritten.
2022-12-28 17:20:09 -08:00
Syoyo Fujita
cf11842e64 Merge pull request #395 from ethanpepro/cmake-export
Add CMake export configuration
2022-12-28 18:33:36 +09:00
James Luke
38003032e3 Add CMake export configuration 2022-12-27 21:12:54 -05:00
Syoyo Fujita
186016bf11 Merge branch 'release' of github.com:syoyo/tinygltf into release 2022-12-03 19:42:27 +09:00
Syoyo Fujita
5ee08f9274 Remove Python 2 description. 2022-12-03 19:40:50 +09:00
Syoyo Fujita
aa613a1f57 Update README.md
Remove Python 2.x description
2022-12-03 19:40:45 +09:00
Syoyo Fujita
222454cc6d Create FUNDING.yml 2022-12-03 18:33:05 +09:00
Syoyo Fujita
16c2d3a8bf Merge pull request #390 from eduardodoria/patch-1
Added Supernova Engine to Projects using TinyGLTF
2022-11-07 21:18:50 +09:00
Eduardo Doria
186093657a Added Supernova Engine to Projects using TinyGLTF 2022-11-07 08:53:47 -03:00
Syoyo Fujita
9bdd256625 Merge pull request #388 from stromaster/patch-1
Avoid multiple asset_manager definitions on Android
2022-10-31 17:40:40 +09:00
Serdar Kocdemir
264ae4c131 Avoid multiple asset_manager definition on Android
Using TINYGLTF_IMPLEMENTATION to make the actual definition of the 'asset_manager' global variable.
2022-10-30 22:32:59 +00:00
Syoyo Fujita
091a1fcc1a Merge pull request #386 from geometrian/master
Fix Clang Compile Warnings/Errors (and Typos)
2022-10-08 19:08:22 +09:00
imallett
3a295887d6 Patch to fix previous commit for MinGW. 2022-10-07 11:20:39 -07:00
imallett
d9ce9eb9d2 Fix a gazillion typos. 2022-10-07 10:37:09 -07:00
imallett
56e1098993 Fix various type mismatches and header include case (fixes compile warnings on Clang). 2022-10-07 10:35:16 -07:00
Syoyo Fujita
9bb5806df4 Merge pull request #385 from operatios/master
Fix UTF-8 filepath on LLVM MinGW
2022-09-25 05:35:36 +09:00
operatios
1668d1ecc5 Fix UTF-8 filepath on LLVM MinGW 2022-09-24 22:37:14 +03:00
Syoyo Fujita
6e8a858c45 Add WASI build procedure. 2022-09-22 04:36:58 +09:00
Syoyo Fujita
e0b625561c v2.6.3 2022-09-19 03:36:58 +09:00
Syoyo Fujita
18450eafe7 Merge pull request #382 from syoyo/glb-zero-chunk
Fix parsing GLB file with empty Chunk1(BIN data).
2022-09-19 03:34:09 +09:00
10 changed files with 532 additions and 156 deletions

13
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: syoyo
#patreon: # Replace with a single Patreon username
#open_collective: # Replace with a single Open Collective username
#ko_fi: # Replace with a single Ko-fi username
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
#issuehunt: # Replace with a single IssueHunt username
#otechie: # Replace with a single Otechie username
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -2,6 +2,9 @@ cmake_minimum_required(VERSION 3.6)
PROJECT (tinygltf)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
SET(CMAKE_CXX_STANDARD 11)
option(TINYGLTF_BUILD_LOADER_EXAMPLE "Build loader_example(load glTF and dump infos)" ON)
@@ -54,7 +57,10 @@ else (TINYGLTF_HEADER_ONLY)
endif (TINYGLTF_HEADER_ONLY)
if (TINYGLTF_INSTALL)
install(TARGETS tinygltf EXPORT tinygltfTargets)
install(EXPORT tinygltfTargets NAMESPACE tinygltf:: FILE TinyGLTFTargets.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake)
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/TinyGLTFConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/TinyGLTFConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/TinyGLTFConfig.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake)
# Do not install .lib even if !TINYGLTF_HEADER_ONLY
INSTALL ( FILES
@@ -67,10 +73,4 @@ if (TINYGLTF_INSTALL)
include
)
INSTALL ( FILES
cmake/TinyGLTFConfig.cmake
DESTINATION
cmake
)
endif(TINYGLTF_INSTALL)

View File

@@ -10,12 +10,14 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch(bu
Currently TinyGLTF is stable and maintainance mode. No drastic changes and feature additions planned.
- v2.8.0 Add URICallbacks for custom URI handling in Buffer and Image. PR#397
- v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393
- v2.6.0 Support serializing sparse accessor(Thanks to @fynv).
- v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
- v2.4.0 Experimental RapidJSON support. Experimental C++14 support(C++14 may give better performance)
- v2.3.0 Modified Material representation according to glTF 2.0 schema(and introduced TextureInfo class)
- v2.2.0 release(Support loading 16bit PNG. Sparse accessor support)
- v2.1.0 release(Draco support)
- v2.1.0 release(Draco decoding support)
- v2.0.0 release(22 Aug, 2018)!
### Branches
@@ -86,6 +88,12 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
* [basic](examples/basic) : Basic glTF viewer with texturing support.
* [build-gltf](examples/build-gltf) : Build simple glTF scene from a scratch.
### WASI/WASM build
Users who want to run TinyGLTF securely and safely(e.g. need to handle malcious glTF file to serve online glTF conver),
I recommend to build TinyGLTF for WASM target.
WASI build example is located in [wasm](wasm) .
## Projects using TinyGLTF
* px_render Single header C++ Libraries for Thread Scheduling, Rendering, and so on... https://github.com/pplux/px
@@ -100,6 +108,7 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
* [TDME2](https://github.com/andreasdr/tdme2) - TDME2 - ThreeDeeMiniEngine2 is a lightweight 3D engine including tools suited for 3D game development using C++11
* [SanityEngine](https://github.com/DethRaid/SanityEngine) - A C++/D3D12 renderer focused on the personal and proessional development of its developer
* [Open3D](http://www.open3d.org/) - A Modern Library for 3D Data Processing
* [Supernova Engine](https://github.com/supernovaengine/supernova) - Game engine for 2D and 3D projects with Lua or C++ in data oriented design.
* Your projects here! (Please send PR)
## TODOs
@@ -222,7 +231,7 @@ add_subdirectory(/path/to/tinygltf)
#### Setup
Python 2.6 or 2.7 required.
Python required.
Git clone https://github.com/KhronosGroup/glTF-Sample-Models to your local dir.
#### Run parsing test

View File

@@ -1,15 +0,0 @@
# -*- cmake -*-
# - Find TinyGLTF
# TinyGLTF_INCLUDE_DIR TinyGLTF's include directory
FIND_PACKAGE ( PackageHandleStandardArgs )
SET ( TinyGLTF_INCLUDE_DIR "${TinyGLTF_DIR}/../include" CACHE STRING "TinyGLTF include directory")
FIND_FILE ( TinyGLTF_HEADER tiny_gltf.h PATHS ${TinyGLTF_INCLUDE_DIR} )
IF (NOT TinyGLTF_HEADER)
MESSAGE ( FATAL_ERROR "Unable to find tiny_gltf.h, TinyGLTF_INCLUDE_DIR = ${TinyGLTF_INCLUDE_DIR}")
ENDIF ()

View File

@@ -0,0 +1,3 @@
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/TinyGLTFTargets.cmake)

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env python
# Assume python 2.6 or 2.7
import glob
import os
import subprocess

View File

@@ -392,27 +392,105 @@ TEST_CASE("pbr-khr-texture-transform", "[material]") {
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;
}
{
tinygltf::Model model;
bool ret = ctx.LoadASCIIFromFile(
&model, &err, &warn,
"../models/CubeImageUriSpaces/CubeImageUriSpaces.gltf");
if (!warn.empty()) {
std::cerr << warn << std::endl;
}
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(true == ret);
REQUIRE(warn.empty());
REQUIRE(err.empty());
REQUIRE(model.images.size() == 1);
REQUIRE(model.images[0].uri.find(' ') != std::string::npos);
}
// 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");
tinygltf::Model model;
bool ret = ctx.LoadASCIIFromFile(
&model, &err, &warn,
"../models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf");
if (!warn.empty()) {
std::cerr << warn << std::endl;
}
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(warn.empty());
REQUIRE(err.empty());
REQUIRE(model.images.size() == 1);
REQUIRE(model.images[0].uri.size() > 1);
REQUIRE(model.images[0].uri[0] == ' ');
// Test the URI encoding API by saving and re-load the file, without embedding
// the image.
// TODO(syoyo): create temp directory.
{
// Encoder that only replaces spaces with "%20".
auto uriencode = [](const std::string &in_uri,
const std::string &object_type, std::string *out_uri,
void *user_data) -> bool {
(void)user_data;
bool imageOrBuffer = object_type == "image" || object_type == "buffer";
REQUIRE(true == imageOrBuffer);
*out_uri = {};
for (char c : in_uri) {
if (c == ' ')
*out_uri += "%20";
else
*out_uri += c;
}
return true;
};
// Remove the buffer URI, so a new one is generated based on the gltf
// filename and then encoded with the above callback.
model.buffers[0].uri.clear();
tinygltf::URICallbacks uri_cb{uriencode, tinygltf::URIDecode, nullptr};
ctx.SetURICallbacks(uri_cb);
ret = ctx.WriteGltfSceneToFile(&model, " issue-236.gltf", false, false);
REQUIRE(true == ret);
// read back serialized glTF
tinygltf::Model saved;
bool ret = ctx.LoadASCIIFromFile(&saved, &err, &warn, " issue-236.gltf");
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(err.empty());
REQUIRE(!warn.empty()); // relative image path won't exist in tests/
REQUIRE(saved.images.size() == model.images.size());
// The image uri in CubeImageUriMultipleSpaces.gltf is not encoded and
// should be different after encoding spaces with %20.
REQUIRE(model.images[0].uri != saved.images[0].uri);
// Verify the image path remains the same after uri decoding
std::string image_uri, image_uri_saved;
(void)tinygltf::URIDecode(model.images[0].uri, &image_uri, nullptr);
(void)tinygltf::URIDecode(saved.images[0].uri, &image_uri_saved, nullptr);
REQUIRE(image_uri == image_uri_saved);
// Verify the buffer's generated and encoded URI
REQUIRE(saved.buffers.size() == model.buffers.size());
REQUIRE(saved.buffers[0].uri == "%20issue-236.bin");
}
}
TEST_CASE("serialize-empty-material", "[issue-294]") {
@@ -540,3 +618,103 @@ TEST_CASE("empty-bin-buffer", "[issue-382]") {
}
REQUIRE(true == ret);
}
TEST_CASE("serialize-const-image", "[issue-394]") {
tinygltf::Model m;
tinygltf::Image i;
i.width = 1;
i.height = 1;
i.component = 4;
i.bits = 8;
i.pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
i.image = {255, 255, 255, 255};
i.uri = "image.png";
m.images.push_back(i);
std::stringstream os;
tinygltf::TinyGLTF ctx;
ctx.WriteGltfSceneToStream(const_cast<const tinygltf::Model *>(&m), os, false,
false);
REQUIRE(m.images[0].uri == i.uri);
// use nlohmann json
nlohmann::json j = nlohmann::json::parse(os.str());
REQUIRE(1 == j["images"].size());
REQUIRE(j["images"][0].is_object());
REQUIRE(j["images"][0]["uri"].get<std::string>() != i.uri);
REQUIRE(j["images"][0]["uri"].get<std::string>().rfind("data:", 0) == 0);
}
TEST_CASE("serialize-image-callback", "[issue-394]") {
tinygltf::Model m;
tinygltf::Image i;
i.width = 1;
i.height = 1;
i.bits = 32;
i.image = {255, 255, 255, 255};
i.uri = "foo";
m.images.push_back(i);
std::stringstream os;
auto writer = [](const std::string *basepath, const std::string *filename,
const tinygltf::Image *image, bool embedImages,
const tinygltf::URICallbacks *uri_cb, std::string *out_uri,
void *user_pointer) -> bool {
(void)basepath;
(void)image;
(void)uri_cb;
REQUIRE(*filename == "foo");
REQUIRE(embedImages == true);
REQUIRE(user_pointer == (void *)0xba5e1e55);
*out_uri = "bar";
return true;
};
tinygltf::TinyGLTF ctx;
ctx.SetImageWriter(writer, (void *)0xba5e1e55);
bool result = ctx.WriteGltfSceneToStream(const_cast<const tinygltf::Model *>(&m), os, false,
false);
// use nlohmann json
nlohmann::json j = nlohmann::json::parse(os.str());
REQUIRE(true == result);
REQUIRE(1 == j["images"].size());
REQUIRE(j["images"][0].is_object());
REQUIRE(j["images"][0]["uri"].get<std::string>() == "bar");
}
TEST_CASE("serialize-image-failure", "[issue-394]") {
tinygltf::Model m;
tinygltf::Image i;
// Set some data so the ImageWriter callback will be called
i.image = {255, 255, 255, 255};
m.images.push_back(i);
std::stringstream os;
auto writer = [](const std::string *basepath, const std::string *filename,
const tinygltf::Image *image, bool embedImages,
const tinygltf::URICallbacks *uri_cb, std::string *out_uri,
void *user_pointer) -> bool {
(void)basepath;
(void)filename;
(void)image;
(void)embedImages;
(void)uri_cb;
(void)out_uri;
(void)user_pointer;
return false;
};
tinygltf::TinyGLTF ctx;
ctx.SetImageWriter(writer, (void *)0xba5e1e55);
bool result = ctx.WriteGltfSceneToStream(const_cast<const tinygltf::Model *>(&m), os, false,
false);
REQUIRE(false == result);
REQUIRE(os.str().size() == 0);
}

View File

@@ -26,12 +26,15 @@
// THE SOFTWARE.
// Version:
// - v2.8.0 Add URICallbacks for custom URI handling in Buffer and Image. PR#397.
// - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393.
// - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383.
// - v2.6.2 Fix out-of-bounds access of accessors. PR#379.
// - v2.6.1 Better GLB validation check when loading.
// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv).
// Disable expanding file path for security(no use of awkward `wordexp` anymore).
// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
// - v2.4.3 Fix null object output when when material has all default
// - v2.4.3 Fix null object output when material has all default
// parameters.
// - v2.4.2 Decode percent-encoded URI.
// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
@@ -45,7 +48,7 @@
// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
// to @Ybalrid)
// - v2.1.0 Add draco compression.
// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
// - v2.0.1 Add comparison feature(Thanks to @Selmar).
// - v2.0.0 glTF 2.0!.
//
// Tiny glTF loader is using following third party libraries:
@@ -199,7 +202,11 @@ namespace tinygltf {
#ifdef __ANDROID__
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
#ifdef TINYGLTF_IMPLEMENTATION
AAssetManager *asset_manager = nullptr;
#else
extern AAssetManager *asset_manager;
#endif
#endif
#endif
@@ -232,7 +239,7 @@ static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
} else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
return 8;
} else {
// Unknown componenty type
// Unknown component type
return -1;
}
}
@@ -253,7 +260,7 @@ static inline int32_t GetNumComponentsInType(uint32_t ty) {
} else if (ty == TINYGLTF_TYPE_MAT4) {
return 16;
} else {
// Unknown componenty type
// Unknown component type
return -1;
}
}
@@ -440,7 +447,7 @@ TINYGLTF_VALUE_GET(Value::Object, object_value_)
#pragma clang diagnostic ignored "-Wpadded"
#endif
/// Agregate object for representing a color
/// Aggregate object for representing a color
using ColorValue = std::array<double, 4>;
// === legacy interface ====
@@ -477,7 +484,7 @@ struct Parameter {
if (it != std::end(json_double_value)) {
return int(it->second);
}
// As per the spec, if texCoord is ommited, this parameter is 0
// As per the spec, if texCoord is omitted, this parameter is 0
return 0;
}
@@ -489,7 +496,7 @@ struct Parameter {
if (it != std::end(json_double_value)) {
return it->second;
}
// As per the spec, if scale is ommited, this paramter is 1
// As per the spec, if scale is omitted, this parameter is 1
return 1;
}
@@ -501,7 +508,7 @@ struct Parameter {
if (it != std::end(json_double_value)) {
return it->second;
}
// As per the spec, if strenghth is ommited, this parameter is 1
// As per the spec, if strength is omitted, this parameter is 1
return 1;
}
@@ -515,7 +522,7 @@ struct Parameter {
/// material
ColorValue ColorFactor() const {
return {
{// this agregate intialize the std::array object, and uses C++11 RVO.
{// this aggregate initialize the std::array object, and uses C++11 RVO.
number_array[0], number_array[1], number_array[2],
(number_array.size() > 3 ? number_array[3] : 1.0)}};
}
@@ -826,7 +833,7 @@ struct BufferView {
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
// or attribs. Could be 0 for other data
Value extras;
ExtensionMap extensions;
@@ -902,7 +909,7 @@ struct Accessor {
return componentSizeInBytes * numComponents;
} else {
// Check if byteStride is a mulple of the size of the accessor's component
// Check if byteStride is a multiple of the size of the accessor's component
// type.
int componentSizeInBytes =
GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
@@ -941,7 +948,7 @@ struct PerspectiveCamera {
PerspectiveCamera()
: aspectRatio(0.0),
yfov(0.0),
zfar(0.0) // 0 = use infinite projecton matrix
zfar(0.0) // 0 = use infinite projection matrix
,
znear(0.0) {}
DEFAULT_METHODS(PerspectiveCamera)
@@ -1002,7 +1009,7 @@ struct Primitive {
int indices; // The index of the accessor that contains the indices.
int mode; // one of TINYGLTF_MODE_***
std::vector<std::map<std::string, int> > targets; // array of morph targets,
// where each target is a dict with attribues in ["POSITION, "NORMAL",
// where each target is a dict with attributes in ["POSITION, "NORMAL",
// "TANGENT"] pointing
// to their corresponding accessors
ExtensionMap extensions;
@@ -1137,7 +1144,7 @@ struct Light {
std::vector<double> color;
double intensity{1.0};
std::string type;
double range{0.0}; // 0.0 = inifinite
double range{0.0}; // 0.0 = infinite
SpotLight spot;
Light() : intensity(1.0), range(0.0) {}
@@ -1201,6 +1208,39 @@ enum SectionCheck {
REQUIRE_ALL = 0x7f
};
///
/// URIEncodeFunction type. Signature for custom URI encoding of external
/// resources such as .bin and image files. Used by tinygltf to re-encode the
/// final location of saved files. object_type may be used to encode buffer and
/// image URIs differently, for example. See
/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris
///
typedef bool (*URIEncodeFunction)(const std::string &in_uri,
const std::string &object_type,
std::string *out_uri, void *user_data);
///
/// URIDecodeFunction type. Signature for custom URI decoding of external
/// resources such as .bin and image files. Used by tinygltf when computing
/// filenames to write resources.
///
typedef bool (*URIDecodeFunction)(const std::string &in_uri,
std::string *out_uri, void *user_data);
// Declaration of default uri decode function
bool URIDecode(const std::string &in_uri, std::string *out_uri,
void *user_data);
///
/// A structure containing URI callbacks and a pointer to their user data.
///
struct URICallbacks {
URIEncodeFunction encode; // Optional encode method
URIDecodeFunction decode; // Required decode method
void *user_data; // An argument that is passed to all uri callbacks
};
///
/// LoadImageDataFunction type. Signature for custom image loading callbacks.
///
@@ -1211,9 +1251,15 @@ typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
///
/// WriteImageDataFunction type. Signature for custom image writing callbacks.
/// The out_uri parameter becomes the URI written to the gltf and may reference
/// a file or contain a data URI.
///
typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
Image *, bool, void *);
typedef bool (*WriteImageDataFunction)(const std::string *basepath,
const std::string *filename,
const Image *image, bool embedImages,
const URICallbacks *uri_cb,
std::string *out_uri,
void *user_pointer);
#ifndef TINYGLTF_NO_STB_IMAGE
// Declaration of default image loader callback
@@ -1225,7 +1271,8 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
// Declaration of default image writer callback
bool WriteImageData(const std::string *basepath, const std::string *filename,
Image *image, bool embedImages, void *);
const Image *image, bool embedImages,
const URICallbacks *uri_cb, std::string *out_uri, void *);
#endif
///
@@ -1287,7 +1334,7 @@ bool WriteWholeFile(std::string *err, const std::string &filepath,
#endif
///
/// glTF Parser/Serialier context.
/// glTF Parser/Serializer context.
///
class TinyGLTF {
public:
@@ -1350,15 +1397,15 @@ class TinyGLTF {
unsigned int check_sections = REQUIRE_VERSION);
///
/// Write glTF to stream, buffers and images will be embeded
/// Write glTF to stream, buffers and images will be embedded
///
bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
bool WriteGltfSceneToStream(const Model *model, std::ostream &stream,
bool prettyPrint, bool writeBinary);
///
/// Write glTF to file.
///
bool WriteGltfSceneToFile(Model *model, const std::string &filename,
bool WriteGltfSceneToFile(const Model *model, const std::string &filename,
bool embedImages, bool embedBuffers,
bool prettyPrint, bool writeBinary);
@@ -1377,6 +1424,11 @@ class TinyGLTF {
///
void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
///
/// Set callbacks to use for URI encoding and decoding and their user data
///
void SetURICallbacks(URICallbacks callbacks);
///
/// Set callbacks to use for filesystem (fs) access and their user data
///
@@ -1385,7 +1437,7 @@ class TinyGLTF {
///
/// Set serializing default values(default = false).
/// When true, default values are force serialized to .glTF.
/// This may be helpfull if you want to serialize a full description of glTF
/// This may be helpful if you want to serialize a full description of glTF
/// data.
///
/// TODO(LTE): Supply parsing option as function arguments to
@@ -1411,8 +1463,8 @@ class TinyGLTF {
}
///
/// Specify whether preserve image channales when loading images or not.
/// (Not effective when the user suppy their own LoadImageData callbacks)
/// Specify whether preserve image channels when loading images or not.
/// (Not effective when the user supplies their own LoadImageData callbacks)
///
void SetPreserveImageChannels(bool onoff) {
preserve_image_channels_ = onoff;
@@ -1459,6 +1511,15 @@ class TinyGLTF {
#endif
};
URICallbacks uri_cb = {
// Use paths as-is by default. This will use JSON string escaping.
nullptr,
// Decode all URIs before using them as paths as the application may have
// percent encoded them.
&tinygltf::URIDecode,
// URI callback user data
nullptr};
LoadImageDataFunction LoadImageData =
#ifndef TINYGLTF_NO_STB_IMAGE
&tinygltf::LoadImageData;
@@ -1549,7 +1610,7 @@ class TinyGLTF {
#endif
#endif
// Disable GCC warnigs
// Disable GCC warnings
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
@@ -1598,7 +1659,7 @@ class TinyGLTF {
// issue 143.
// Define NOMINMAX to avoid min/max defines,
// but undef it after included windows.h
// but undef it after included Windows.h
#ifndef NOMINMAX
#define TINYGLTF_INTERNAL_NOMINMAX
#define NOMINMAX
@@ -1608,7 +1669,11 @@ class TinyGLTF {
#define WIN32_LEAN_AND_MEAN
#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
#endif
#include <windows.h> // include API for expanding a file path
#ifndef __MINGW32__
#include <Windows.h> // include API for expanding a file path
#else
#include <windows.h>
#endif
#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
@@ -1739,7 +1804,7 @@ namespace tinygltf {
struct LoadImageDataOption {
// true: preserve image channels(e.g. load as RGB image if the image has RGB
// channels) default `false`(channels are expanded to RGBA for backward
// compatiblity).
// compatibility).
bool preserve_channels{false};
};
@@ -2276,6 +2341,13 @@ static const std::string urldecode(const std::string &str) {
} // namespace dlib
// --- dlib end --------------------------------------------------------------
bool URIDecode(const std::string &in_uri, std::string *out_uri,
void *user_data) {
(void)user_data;
*out_uri = dlib::urldecode(in_uri);
return true;
}
static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
std::string *warn, const std::string &filename,
const std::string &basedir, bool required,
@@ -2386,7 +2458,7 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
// It is possible that the image we want to load is a 16bit per channel image
// We are going to attempt to load it as 16bit per channel, and if it worked,
// set the image data accodingly. We are casting the returned pointer into
// set the image data accordingly. We are casting the returned pointer into
// unsigned char, because we are representing "bytes". But we are updating
// the Image metadata to signal that this image uses 2 bytes (16bits) per
// channel:
@@ -2401,7 +2473,7 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
// at this point, if data is still NULL, it means that the image wasn't
// 16bit per channel, we are going to load it as a normal 8bit per channel
// mage as we used to do:
// image as we used to do:
// if image cannot be decoded, ignore parsing and keep it by its path
// don't break in this case
// FIXME we should only enter this function if the image is embedded. If
@@ -2486,7 +2558,9 @@ static void WriteToMemory_stbi(void *context, void *data, int size) {
}
bool WriteImageData(const std::string *basepath, const std::string *filename,
Image *image, bool embedImages, void *fsPtr) {
const Image *image, bool embedImages,
const URICallbacks *uri_cb, std::string *out_uri,
void *fsPtr) {
const std::string ext = GetFilePathExtension(*filename);
// Write image to temporary buffer
@@ -2528,9 +2602,8 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
if (embedImages) {
// Embed base64-encoded image into URI
if (data.size()) {
image->uri =
header +
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
*out_uri = header +
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
} else {
// Throw error?
}
@@ -2548,20 +2621,33 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
} else {
// Throw error?
}
image->uri = *filename;
if (uri_cb->encode) {
if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) {
return false;
}
} else {
*out_uri = *filename;
}
}
return true;
}
#endif
void TinyGLTF::SetURICallbacks(URICallbacks callbacks) {
assert(callbacks.decode);
if (callbacks.decode) {
uri_cb = callbacks;
}
}
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);
std::wstring wstr(wstr_size, 0);
std::wstring wstr((size_t)wstr_size, 0);
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
(int)wstr.size());
return wstr;
@@ -2569,10 +2655,10 @@ static inline std::wstring UTF8ToWchar(const std::string &str) {
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);
nullptr, 0, nullptr, nullptr);
std::string str((size_t)str_size, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
(int)str.size(), NULL, NULL);
(int)str.size(), nullptr, nullptr);
return str;
}
#endif
@@ -2596,7 +2682,7 @@ bool FileExists(const std::string &abs_filename, void *) {
}
#else
#ifdef _WIN32
#if defined(_MSC_VER) || defined(__GLIBCXX__)
#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
FILE *fp = nullptr;
errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
if (err != 0) {
@@ -2819,15 +2905,21 @@ static std::string MimeToExt(const std::string &mimeType) {
return "";
}
static void UpdateImageObject(Image &image, std::string &baseDir, int index,
bool embedImages,
WriteImageDataFunction *WriteImageData = nullptr,
void *user_data = nullptr) {
static bool UpdateImageObject(const Image &image, std::string &baseDir,
int index, bool embedImages,
const URICallbacks *uri_cb,
WriteImageDataFunction *WriteImageData,
void *user_data, std::string *out_uri) {
std::string filename;
std::string ext;
// If image has uri, use it it as a filename
// If image has uri, use it as a filename
if (image.uri.size()) {
filename = GetBaseFilename(image.uri);
std::string decoded_uri;
if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) {
// A decode failure results in a failure to write the gltf.
return false;
}
filename = GetBaseFilename(decoded_uri);
ext = GetFilePathExtension(filename);
} else if (image.bufferView != -1) {
// If there's no URI and the data exists in a buffer,
@@ -2842,11 +2934,24 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
filename = std::to_string(index) + "." + ext;
}
// If callback is set, modify image data object
if (*WriteImageData != nullptr && !filename.empty()) {
std::string uri;
(*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
// If callback is set and image data exists, modify image data object. If
// image data does not exist, this is not considered a failure and the
// original uri should be maintained.
bool imageWritten = false;
if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) {
imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
uri_cb, out_uri, user_data);
if (!imageWritten) {
return false;
}
}
// Use the original uri if the image was not written.
if (!imageWritten) {
*out_uri = image.uri;
}
return true;
}
bool IsDataURI(const std::string &in) {
@@ -3765,6 +3870,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
std::string *warn, const json &o,
bool store_original_json_for_extras_and_extensions,
const std::string &basedir, FsCallbacks *fs,
const URICallbacks *uri_cb,
LoadImageDataFunction *LoadImageData = nullptr,
void *load_image_user_data = nullptr) {
// A glTF image must either reference a bufferView or an image uri
@@ -3875,7 +3981,18 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
return true;
#else
std::string decoded_uri = dlib::urldecode(uri);
std::string decoded_uri;
if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) {
if (warn) {
(*warn) += "Failed to decode 'uri' for image[" +
std::to_string(image_idx) + "] name = [" + image->name +
"]\n";
}
// Image loading failure is not critical to overall gltf loading.
return true;
}
if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
/* required */ false, /* required bytes */ 0,
/* checksize */ false, fs)) {
@@ -4054,8 +4171,8 @@ static bool ParseOcclusionTextureInfo(
static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
bool store_original_json_for_extras_and_extensions,
FsCallbacks *fs, const std::string &basedir,
bool is_binary = false,
FsCallbacks *fs, const URICallbacks *uri_cb,
const std::string &basedir, bool is_binary = false,
const unsigned char *bin_data = nullptr,
size_t bin_size = 0) {
size_t byteLength;
@@ -4101,7 +4218,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
}
} else {
// External .bin file.
std::string decoded_uri = dlib::urldecode(buffer->uri);
std::string decoded_uri;
if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
return false;
}
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
decoded_uri, basedir, /* required */ true,
byteLength, /* checkSize */ true, fs)) {
@@ -4146,7 +4266,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
}
} else {
// Assume external .bin file.
std::string decoded_uri = dlib::urldecode(buffer->uri);
std::string decoded_uri;
if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
return false;
}
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
basedir, /* required */ true, byteLength,
/* checkSize */ true, fs)) {
@@ -4271,7 +4394,7 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
}
if (!FindMember(o, "values", values_iterator)) {
(*err) = "the sparse object ob ths accessor doesn't have values";
(*err) = "the sparse object of this accessor doesn't have values";
return false;
}
@@ -4617,7 +4740,7 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
int mode = TINYGLTF_MODE_TRIANGLES;
ParseIntegerProperty(&mode, err, o, "mode", false);
primitive->mode = mode; // Why only triangled were supported ?
primitive->mode = mode; // Why only triangles were supported ?
int indices = -1;
ParseIntegerProperty(&indices, err, o, "indices", false);
@@ -4900,7 +5023,7 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o,
// Old code path. For backward compatibility, we still store material values
// as Parameter. This will create duplicated information for
// example(pbrMetallicRoughness), but should be neglible in terms of memory
// example(pbrMetallicRoughness), but should be negligible in terms of memory
// consumption.
// TODO(syoyo): Remove in the next major release.
material->values.clear();
@@ -4933,7 +5056,7 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o,
Parameter param;
if (ParseParameterProperty(&param, err, o, key, false)) {
// names of materials have already been parsed. Putting it in this map
// doesn't correctly reflext the glTF specification
// doesn't correctly reflect the glTF specification
if (key != "name")
material->additionalValues.emplace(std::move(key), std::move(param));
}
@@ -5139,7 +5262,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
// ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
// extension
// TODO(syoyo): Check the value is alloed one.
// TODO(syoyo): Check the value is allowed one.
// (e.g. we allow 9728(NEAREST), but don't allow 9727)
sampler->minFilter = minFilter;
@@ -5348,7 +5471,7 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o,
if (!FindMember(o, "orthographic", orthoIt)) {
if (err) {
std::stringstream ss;
ss << "Orhographic camera description not found." << std::endl;
ss << "Orthographic camera description not found." << std::endl;
(*err) += ss.str();
}
return false;
@@ -5689,7 +5812,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Buffer buffer;
if (!ParseBuffer(&buffer, err, o,
store_original_json_for_extras_and_extensions_, &fs,
base_dir, is_binary_, bin_data_, bin_size_)) {
&uri_cb, base_dir, is_binary_, bin_data_, bin_size_)) {
return false;
}
@@ -5800,7 +5923,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
// we could optionally check if acessors' bufferView type is Scalar, as
// we could optionally check if accessors' bufferView type is Scalar, as
// it should be
}
@@ -5809,7 +5932,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
if (accessorsIndex < model->accessors.size()) {
const auto bufferView = model->accessors[accessorsIndex].bufferView;
// bufferView could be null(-1) for sparse morph target
if (bufferView >= 0 && bufferView < model->bufferViews.size()) {
if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ARRAY_BUFFER;
}
@@ -5822,7 +5945,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
if (accessorsIndex < model->accessors.size()) {
const auto bufferView = model->accessors[accessorsIndex].bufferView;
// bufferView could be null(-1) for sparse morph target
if (bufferView >= 0 && bufferView < model->bufferViews.size()) {
if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ARRAY_BUFFER;
}
@@ -5960,7 +6083,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Image image;
if (!ParseImage(&image, idx, err, warn, o,
store_original_json_for_extras_and_extensions_, base_dir,
&fs, &this->LoadImageData, load_image_user_data)) {
&fs, &uri_cb, &this->LoadImageData,
load_image_user_data)) {
return false;
}
@@ -6323,7 +6447,7 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
bin_size_ = 0;
} else {
// Read Chunk1 info(BIN data)
// At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aliged to 4 bytes)
// At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aligned to 4 bytes)
if ((header_and_json_size + 12ull) > uint64_t(length)) {
if (err) {
(*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n";
@@ -6363,7 +6487,7 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
if (chunk1_format != 0x004e4942) {
if (err) {
(*err) = "Invlid type for chunk1 data.";
(*err) = "Invalid type for chunk1 data.";
}
return false;
}
@@ -6652,7 +6776,7 @@ static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
SerializeStringProperty("uri", header + encodedData, o);
} else {
// Issue #229
// size 0 is allowd. Just emit mime header.
// size 0 is allowed. Just emit mime header.
SerializeStringProperty("uri", header, o);
}
}
@@ -6746,7 +6870,7 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
JsonAddMember(o, "extensions", std::move(extMap));
}
static void SerializeGltfAccessor(Accessor &accessor, json &o) {
static void SerializeGltfAccessor(const Accessor &accessor, json &o) {
if (accessor.bufferView >= 0)
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
@@ -6838,7 +6962,8 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) {
}
}
static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
json &o) {
SerializeNumberProperty("sampler", channel.sampler, o);
{
json target;
@@ -6857,7 +6982,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
SerializeExtensionMap(channel.extensions, o);
}
static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
static void SerializeGltfAnimationSampler(const AnimationSampler &sampler,
json &o) {
SerializeNumberProperty("input", sampler.input, o);
SerializeNumberProperty("output", sampler.output, o);
SerializeStringProperty("interpolation", sampler.interpolation, o);
@@ -6867,7 +6993,7 @@ static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
}
}
static void SerializeGltfAnimation(Animation &animation, json &o) {
static void SerializeGltfAnimation(const Animation &animation, json &o) {
if (!animation.name.empty())
SerializeStringProperty("name", animation.name, o);
@@ -6903,7 +7029,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) {
SerializeExtensionMap(animation.extensions, o);
}
static void SerializeGltfAsset(Asset &asset, json &o) {
static void SerializeGltfAsset(const Asset &asset, json &o) {
if (!asset.generator.empty()) {
SerializeStringProperty("generator", asset.generator, o);
}
@@ -6912,14 +7038,15 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
SerializeStringProperty("copyright", asset.copyright, o);
}
if (asset.version.empty()) {
auto version = asset.version;
if (version.empty()) {
// Just in case
// `version` must be defined
asset.version = "2.0";
version = "2.0";
}
// TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
SerializeStringProperty("version", asset.version, o);
SerializeStringProperty("version", version, o);
if (asset.extras.Keys().size()) {
SerializeValue("extras", asset.extras, o);
@@ -6928,7 +7055,7 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
SerializeExtensionMap(asset.extensions, o);
}
static void SerializeGltfBufferBin(Buffer &buffer, json &o,
static void SerializeGltfBufferBin(const Buffer &buffer, json &o,
std::vector<unsigned char> &binBuffer) {
SerializeNumberProperty("byteLength", buffer.data.size(), o);
binBuffer = buffer.data;
@@ -6940,7 +7067,7 @@ static void SerializeGltfBufferBin(Buffer &buffer, json &o,
}
}
static void SerializeGltfBuffer(Buffer &buffer, json &o) {
static void SerializeGltfBuffer(const Buffer &buffer, json &o) {
SerializeNumberProperty("byteLength", buffer.data.size(), o);
SerializeGltfBufferData(buffer.data, o);
@@ -6951,12 +7078,12 @@ static void SerializeGltfBuffer(Buffer &buffer, json &o) {
}
}
static bool SerializeGltfBuffer(Buffer &buffer, json &o,
static bool SerializeGltfBuffer(const Buffer &buffer, json &o,
const std::string &binFilename,
const std::string &binBaseFilename) {
const std::string &binUri) {
if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
SerializeNumberProperty("byteLength", buffer.data.size(), o);
SerializeStringProperty("uri", binBaseFilename, o);
SerializeStringProperty("uri", binUri, o);
if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
@@ -6966,7 +7093,7 @@ static bool SerializeGltfBuffer(Buffer &buffer, json &o,
return true;
}
static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
static void SerializeGltfBufferView(const BufferView &bufferView, json &o) {
SerializeNumberProperty("buffer", bufferView.buffer, o);
SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
@@ -6992,14 +7119,15 @@ static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
}
}
static void SerializeGltfImage(Image &image, json &o) {
// if uri empty, the mimeType and bufferview should be set
if (image.uri.empty()) {
static void SerializeGltfImage(const Image &image, const std::string &uri,
json &o) {
// From 2.7.0, we look for `uri` parameter, not `Image.uri`
// if uri is empty, the mimeType and bufferview should be set
if (uri.empty()) {
SerializeStringProperty("mimeType", image.mimeType, o);
SerializeNumberProperty<int>("bufferView", image.bufferView, o);
} else {
// TODO(syoyo): dlib::urilencode?
SerializeStringProperty("uri", image.uri, o);
SerializeStringProperty("uri", uri, o);
}
if (image.name.size()) {
@@ -7013,7 +7141,7 @@ static void SerializeGltfImage(Image &image, json &o) {
SerializeExtensionMap(image.extensions, o);
}
static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
static void SerializeGltfTextureInfo(const TextureInfo &texinfo, json &o) {
SerializeNumberProperty("index", texinfo.index, o);
if (texinfo.texCoord != 0) {
@@ -7027,7 +7155,7 @@ static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
SerializeExtensionMap(texinfo.extensions, o);
}
static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
json &o) {
SerializeNumberProperty("index", texinfo.index, o);
@@ -7046,8 +7174,8 @@ static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
SerializeExtensionMap(texinfo.extensions, o);
}
static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
json &o) {
static void SerializeGltfOcclusionTextureInfo(
const OcclusionTextureInfo &texinfo, json &o) {
SerializeNumberProperty("index", texinfo.index, o);
if (texinfo.texCoord != 0) {
@@ -7065,7 +7193,7 @@ static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
SerializeExtensionMap(texinfo.extensions, o);
}
static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
json &o) {
std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
@@ -7100,7 +7228,7 @@ static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
}
}
static void SerializeGltfMaterial(Material &material, json &o) {
static void SerializeGltfMaterial(const Material &material, json &o) {
if (material.name.size()) {
SerializeStringProperty("name", material.name, o);
}
@@ -7150,7 +7278,7 @@ static void SerializeGltfMaterial(Material &material, json &o) {
// Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
// default values(json is null). Otherwise it will serialize to
// `pbrMetallicRoughness : null`, which cannot be read by other glTF
// importers(and validators).
// importers (and validators).
//
if (!JsonIsNull(pbrMetallicRoughness)) {
JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
@@ -7176,7 +7304,7 @@ static void SerializeGltfMaterial(Material &material, json &o) {
}
}
static void SerializeGltfMesh(Mesh &mesh, json &o) {
static void SerializeGltfMesh(const Mesh &mesh, json &o) {
json primitives;
JsonReserveArray(primitives, mesh.primitives.size());
for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
@@ -7192,7 +7320,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
JsonAddMember(primitive, "attributes", std::move(attributes));
}
// Indicies is optional
// Indices is optional
if (gltfPrimitive.indices > -1) {
SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
}
@@ -7245,7 +7373,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
}
}
static void SerializeSpotLight(SpotLight &spot, json &o) {
static void SerializeSpotLight(const SpotLight &spot, json &o) {
SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
SerializeExtensionMap(spot.extensions, o);
@@ -7254,7 +7382,7 @@ static void SerializeSpotLight(SpotLight &spot, json &o) {
}
}
static void SerializeGltfLight(Light &light, json &o) {
static void SerializeGltfLight(const Light &light, json &o) {
if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
SerializeNumberProperty("intensity", light.intensity, o);
if (light.range > 0.0) {
@@ -7273,7 +7401,7 @@ static void SerializeGltfLight(Light &light, json &o) {
}
}
static void SerializeGltfNode(Node &node, json &o) {
static void SerializeGltfNode(const Node &node, json &o) {
if (node.translation.size() > 0) {
SerializeNumberArrayProperty<double>("translation", node.translation, o);
}
@@ -7311,7 +7439,7 @@ static void SerializeGltfNode(Node &node, json &o) {
SerializeNumberArrayProperty<int>("children", node.children, o);
}
static void SerializeGltfSampler(Sampler &sampler, json &o) {
static void SerializeGltfSampler(const Sampler &sampler, json &o) {
if (sampler.magFilter != -1) {
SerializeNumberProperty("magFilter", sampler.magFilter, o);
}
@@ -7380,7 +7508,7 @@ static void SerializeGltfCamera(const Camera &camera, json &o) {
SerializeExtensionMap(camera.extensions, o);
}
static void SerializeGltfScene(Scene &scene, json &o) {
static void SerializeGltfScene(const Scene &scene, json &o) {
SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
if (scene.name.size()) {
@@ -7392,7 +7520,7 @@ static void SerializeGltfScene(Scene &scene, json &o) {
SerializeExtensionMap(scene.extensions, o);
}
static void SerializeGltfSkin(Skin &skin, json &o) {
static void SerializeGltfSkin(const Skin &skin, json &o) {
// required
SerializeNumberArrayProperty<int>("joints", skin.joints, o);
@@ -7409,7 +7537,7 @@ static void SerializeGltfSkin(Skin &skin, json &o) {
}
}
static void SerializeGltfTexture(Texture &texture, json &o) {
static void SerializeGltfTexture(const Texture &texture, json &o) {
if (texture.sampler > -1) {
SerializeNumberProperty("sampler", texture.sampler, o);
}
@@ -7428,7 +7556,7 @@ static void SerializeGltfTexture(Texture &texture, json &o) {
///
/// Serialize all properties except buffers and images.
///
static void SerializeGltfModel(Model *model, json &o) {
static void SerializeGltfModel(const Model *model, json &o) {
// ACCESSORS
if (model->accessors.size()) {
json accessors;
@@ -7754,7 +7882,7 @@ static bool WriteBinaryGltfFile(const std::string &output,
return WriteBinaryGltfStream(gltfFile, content, binBuffer);
}
bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream,
bool prettyPrint = true,
bool writeBinary = false) {
JsonDocument output;
@@ -7790,9 +7918,13 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
// UpdateImageObject need baseDir but only uses it if embeddedImages is
// enabled, since we won't write separate images when writing to a stream
// we
UpdateImageObject(model->images[i], dummystring, int(i), true,
&this->WriteImageData, this->write_image_user_data_);
SerializeGltfImage(model->images[i], image);
std::string uri;
if (!UpdateImageObject(model->images[i], dummystring, int(i), true,
&uri_cb, &this->WriteImageData,
this->write_image_user_data_, &uri)) {
return false;
}
SerializeGltfImage(model->images[i], uri, image);
JsonPushBack(images, std::move(image));
}
JsonAddMember(output, "images", std::move(images));
@@ -7803,10 +7935,10 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
} else {
return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
}
}
bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
bool TinyGLTF::WriteGltfSceneToFile(const Model *model,
const std::string &filename,
bool embedImages = false,
bool embedBuffers = false,
bool prettyPrint = true,
@@ -7828,7 +7960,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
SerializeGltfModel(model, output);
// BUFFERS
std::vector<std::string> usedUris;
std::vector<std::string> usedFilenames;
std::vector<unsigned char> binBuffer;
if (model->buffers.size()) {
json buffers;
@@ -7841,27 +7973,40 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
SerializeGltfBuffer(model->buffers[i], buffer);
} else {
std::string binSavePath;
std::string binFilename;
std::string binUri;
if (!model->buffers[i].uri.empty() &&
!IsDataURI(model->buffers[i].uri)) {
binUri = model->buffers[i].uri;
if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) {
return false;
}
} else {
binUri = defaultBinFilename + defaultBinFileExt;
binFilename = defaultBinFilename + defaultBinFileExt;
bool inUse = true;
int numUsed = 0;
while (inUse) {
inUse = false;
for (const std::string &usedName : usedUris) {
if (binUri.compare(usedName) != 0) continue;
for (const std::string &usedName : usedFilenames) {
if (binFilename.compare(usedName) != 0) continue;
inUse = true;
binUri = defaultBinFilename + std::to_string(numUsed++) +
defaultBinFileExt;
binFilename = defaultBinFilename + std::to_string(numUsed++) +
defaultBinFileExt;
break;
}
}
if (uri_cb.encode) {
if (!uri_cb.encode(binFilename, "buffer", &binUri,
uri_cb.user_data)) {
return false;
}
} else {
binUri = binFilename;
}
}
usedUris.push_back(binUri);
binSavePath = JoinPath(baseDir, binUri);
usedFilenames.push_back(binFilename);
binSavePath = JoinPath(baseDir, binFilename);
if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
binUri)) {
return false;
@@ -7879,9 +8024,13 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
for (unsigned int i = 0; i < model->images.size(); ++i) {
json image;
UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
&this->WriteImageData, this->write_image_user_data_);
SerializeGltfImage(model->images[i], image);
std::string uri;
if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
&uri_cb, &this->WriteImageData,
this->write_image_user_data_, &uri)) {
return false;
}
SerializeGltfImage(model->images[i], uri, image);
JsonPushBack(images, std::move(image));
}
JsonAddMember(output, "images", std::move(images));
@@ -7892,7 +8041,6 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
} else {
return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
}
}
} // namespace tinygltf

11
wasm/Makefile Normal file
View File

@@ -0,0 +1,11 @@
WASI_VERSION=16
WASI_VERSION_FULL=${WASI_VERSION}.0
WASI_SDK_PATH=$(HOME)/local/wasi-sdk-${WASI_VERSION_FULL}
CC=${WASI_SDK_PATH}/bin/clang
CXX=${WASI_SDK_PATH}/bin/clang++
CXXFLAGS=-fno-rtti -fno-exceptions -g -Os
all:
$(CXX) ../loader_example.cc $(CXXFLAGS) -I../

31
wasm/README.md Normal file
View File

@@ -0,0 +1,31 @@
Experimental WASI/WASM build
## Build
Download wasi-sdk https://github.com/WebAssembly/wasi-sdk
Compile tinygltf with C++ exceptions and threads off. See `Makefile` for details
(NOTE: TinyGLTF itself does not use RTTI and threading feature(C++ threads, posix, win32 thread))
## Build examples and Run
Build `loader_example.cc`
```
$ /path/to/wasi-sdk-16.0/bin/clang++ ../loader_example.cc -fno-rtti -fno-exceptions -g -Os -I../ -o loader_example.wasi
```
Tested with wasmtime. https://github.com/bytecodealliance/wasmtime
Set a folder containing .gltf file to `--dir`
```
$ wasmtime --dir=../models loader_example.wasi ../models/Cube/Cube.gltf
```
## Emscripen
T.B.W. ...
EoL.