Compare commits

...

53 Commits

Author SHA1 Message Date
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
Syoyo Fujita
e9fbc03e2d Clear error/warn message. 2022-09-19 03:29:57 +09:00
Syoyo Fujita
612e57816f Fix handling <4 byte BIN data.
Fix handling GLB file with empty CHUNK1(BIN).
2022-09-18 21:01:39 +09:00
Kh4n
a778c089d1 readd toplevel makefile 2022-09-17 15:39:28 -05:00
Kh4n
387fd61b83 update test to match gltf-validator 2022-09-17 13:02:39 -05:00
Kh4n
6514490090 update gitignore to remove test file
readd accidental removals in gitignore

undo autoformat

more undo autoformatting
2022-09-17 12:52:59 -05:00
Kh4n
6b7ec9f494 added tests to cover empty, empty buffer, and single byte buffer cases 2022-09-17 12:28:39 -05:00
Syoyo Fujita
c670f08a3b Fix parsing GLB file with empty Chunk1(BIN data). 2022-09-17 19:52:25 +09:00
Syoyo Fujita
eec4c98862 Add note on v2.6.2(Fix out-of-bounds access of accessors. PR#379) 2022-09-16 17:27:20 +09:00
Syoyo Fujita
c7e911cf11 Merge pull request #380 from AlvaroBarua/master
Fixes compiler warning on VS (Unreachable code detected)
2022-09-11 17:56:55 +09:00
AlvaroBarua
43172238f7 Fixes compiler warning on VS (Unreachable code detected) 2022-09-11 00:41:43 +01:00
Syoyo Fujita
0cc23356dc Merge pull request #379 from nirmal/patch-1
Fix possible out of bounds index in LoadFromString
2022-09-07 01:52:55 +09:00
Nirmal Patel
e413216722 Fix possible out of bounds index in LoadFromString 2022-09-06 09:16:31 -07:00
Syoyo Fujita
4581d37bec v2.6.1 2022-09-06 22:02:31 +09:00
Syoyo Fujita
966a9d0df7 Merge pull request #374 from syoyo/glb_chunk_check
Better GLB data size check when reading.
2022-09-06 21:35:47 +09:00
Syoyo Fujita
64452bb5fa Merge pull request #377 from zbendefy/master
Auto detect C++14 standard version
2022-09-06 14:01:26 +09:00
zbendefy
69eeea145b Auto detect C++14 standard version 2022-09-05 23:54:57 +02:00
Syoyo Fujita
24e539621d Merge pull request #376 from kacprzak/master
Read from moved object
2022-09-03 01:16:41 +09:00
Marcin Kacprzak
f4f5c3cf3a Fix read from moved object. 2022-09-02 16:15:54 +02:00
Marcin Kacprzak
b12a54ed15 Silence MS code analysis tool. 2022-09-02 16:13:11 +02:00
Syoyo Fujita
240d993f94 Create codeql-analysis.yml 2022-09-02 05:09:09 +09:00
Syoyo Fujita
46ee8b4276 GLB: Fix interger-overflow when calculating JSON Chunk size.
GLB: Check BIN Chunk size.
GLB: Chenk byte alignment of each chunk.
2022-08-26 22:06:53 +09:00
Syoyo Fujita
3bddc09af4 Update README. 2022-08-19 18:21:24 +09:00
Syoyo Fujita
9117abb45d Merge branch 'master' of github.com:syoyo/tinygltf 2022-08-16 20:11:52 +09:00
Syoyo Fujita
52ff00a384 Do not expand file path since its not necessary for glTF asset path(URI) and for security reason(wordexp). 2022-08-16 20:08:45 +09:00
Syoyo Fujita
0fa56e239c Merge pull request #367 from fynv/master
Sparse accessor: typo fix
2022-08-14 20:54:01 +09:00
Fei Yang
5c2dd83964 Merge branch 'syoyo:master' into master 2022-08-14 19:50:14 +08:00
feiy
1a8814d687 Sparse accessor serialization: typo fix. 2022-08-14 19:49:07 +08:00
Syoyo Fujita
666ab8661c Merge pull request #366 from fynv/master
Adding serialization code for sparse accessors.
2022-08-13 18:34:24 +09:00
feiy
0b31543098 Adding serialization code for sparse accessors. 2022-08-13 10:08:17 +08:00
Syoyo Fujita
81f7dbe53a Partial fix of issue 365 2022-08-02 01:05:34 +09:00
Syoyo Fujita
544969b732 Check the failure of RapidJson Accept(). Fixes #332
TODO: Better error reporting when Accept() failed.
2022-07-13 19:03:33 +09:00
Syoyo Fujita
3d4150419e Merge pull request #362 from hschwane/master
fix images not beeing embeded into stream
2022-06-29 20:16:35 +09:00
Hendrik Schwanekamp
41e1102e57 fix images not beeing embeded into stream 2022-06-29 12:22:40 +02:00
Syoyo Fujita
b901dd62d8 Merge pull request #357 from eariassoto/fix-basic-example
Fix basic example
2022-05-15 20:06:17 +09:00
Syoyo Fujita
bc03ceaac1 Merge pull request #358 from brlcad/patch-2
treat powerpc as big endian too
2022-05-15 20:02:28 +09:00
Christopher Sean Morrison
2c9b25642b treat powerpc as big endian too
added powerpc to the list of big endian types
2022-05-14 19:00:19 -04:00
Emmanuel Arias Soto
a894904f11 Fix basic example
Context: When parsing a mesh with primitives that have indices pointing to different buffer views, the VAO will use the last GL_ELEMENT_ARRAY_BUFFER that was bound to it. In this scenario, the mesh will not be rendered properly.

When binding a mesh, return VAO and EBOs so that primitives can bind to the proper element array buffer.
2022-05-14 19:54:03 +02:00
Syoyo Fujita
912856823d Add Open3D to "projects using TinyGLTF". 2022-05-07 01:35:14 +09:00
Syoyo Fujita
1a7c7d2001 Merge pull request #353 from mgerhardy/master
Fixed find_last_of call in GetBaseFilename
2022-03-14 14:50:32 +09:00
Martin Gerhardy
fae6543b7a Fixed find_last_of call in GetBaseFilename
this leads to a stack overflow on linux - it looks like this magically works on msvc though...
2022-03-13 18:42:39 +01:00
Syoyo Fujita
6ed7c39d71 Add note on componentType double type. 2022-03-03 16:40:54 +09:00
Syoyo Fujita
5a90ab76ee Merge pull request #350 from tsmaster/master
Examples/glview - apply translation, rotation, scale in that order
2022-03-03 03:16:29 +09:00
Dave LeCompte
0ffdc4334a Examples/glview - apply translation, rotation, scale in that order
Tested on the Kenney.nl CarKit models: https://www.kenney.nl/assets/car-kit
2022-03-02 07:01:34 -08:00
Syoyo Fujita
53338352f2 Merge pull request #349 from tsmaster/master
Fix examples/glview rotations
2022-03-02 02:55:15 +09:00
Dave LeCompte
d642c80a86 Fix examples/glview rotations
GLtf defines rotations as quaternions, glRotated expects angle/axis, with angle in degrees.
2022-03-01 08:11:22 -08:00
Syoyo Fujita
a1952053ee Merge pull request #347 from syoyo/jmousseau-sparse-accessor-optionality
Jmousseau sparse accessor optionality
2022-02-26 21:27:35 +09:00
Syoyo Fujita
cbd3885049 Check required attribute exists for SparseAccessor. 2022-02-26 21:19:15 +09:00
Jack Mousseau
9471517c1e Fix byte offset optionality for sparse accessor indices and values 2022-02-24 14:25:37 -08:00
Syoyo Fujita
e59028d68f Merge pull request #345 from syoyo/github-actions-ci-fix
GitHub actions ci fix
2022-02-25 02:07:46 +09:00
Syoyo Fujita
0c7789b1e5 Merge pull request #344 from epajarre/fix-wrapS
Added missing comparison for wrapS
2022-02-24 21:52:43 +09:00
Eero Pajarre
51b12d2546 Added missing comparison for wrapS 2022-02-24 14:38:20 +02:00
7 changed files with 454 additions and 103 deletions

72
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '21 20 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

7
.gitignore vendored
View File

@@ -68,4 +68,11 @@ loader_example
tests/tester
tests/tester_noexcept
tests/issue-97.gltf
tests/issue-261.gltf
# unignore
!Makefile
!examples/build-gltf/Makefile
!examples/raytrace/cornellbox_suzanne.obj
!tests/Makefile
!tools/windows/premake5.exe

View File

@@ -3,12 +3,15 @@
`TinyGLTF` is a header only C++11 glTF 2.0 https://github.com/KhronosGroup/glTF library.
`TinyGLTF` uses Niels Lohmann's json library(https://github.com/nlohmann/json), so now it requires C++11 compiler.
(Also, you can use RadpidJSON as an JSON backend)
If you are looking for old, C++03 version, please use `devel-picojson` branch(but not maintained anymore).
## Status
Currently TinyGLTF is stable and maintainance mode. No drastic changes and feature addition planned.
Currently TinyGLTF is stable and maintainance mode. No drastic changes and feature additions planned.
- 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)
@@ -17,7 +20,7 @@ Currently TinyGLTF is stable and maintainance mode. No drastic changes and featu
### Branches
* `sajson` : Use sajson to parse JSON. Parsing only but faster compile time(2x reduction compared to json.hpp and RapidJson)
* `sajson` : Use sajson to parse JSON. Parsing only but faster compile time(2x reduction compared to json.hpp and RapidJson), but not well maintained.
## Builds
@@ -29,6 +32,8 @@ Currently TinyGLTF is stable and maintainance mode. No drastic changes and featu
## Features
Probably mostly feature-complete. Last missing feature is Draco encoding: https://github.com/syoyo/tinygltf/issues/207
* Written in portable C++. C++-11 with STL dependency only.
* [x] macOS + clang(LLVM)
* [x] iOS + clang
@@ -94,11 +99,12 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
* [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
* [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
* Your projects here! (Please send PR)
## TODOs
* [ ] Write C++ code generator which emits C++ code from JSON schema for robust parsing.
* [ ] Robust URI decoding/encoding. https://github.com/syoyo/tinygltf/issues/369
* [ ] Mesh Compression/decompression(Open3DGC, etc)
* [x] Load Draco compressed mesh
* [ ] Save Draco compressed mesh
@@ -109,6 +115,10 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
* [ ] 16bit PNG support in Serialization
* [ ] Write example and tests for `animation` and `skin`
### Optional
* [ ] Write C++ code generator which emits C++ code from JSON schema for robust parsing?
## Licenses
TinyGLTF is licensed under MIT license.

View File

@@ -39,8 +39,8 @@ bool loadModel(tinygltf::Model &model, const char *filename) {
return res;
}
std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
tinygltf::Model &model, tinygltf::Mesh &mesh) {
void bindMesh(std::map<int, GLuint>& vbos,
tinygltf::Model &model, tinygltf::Mesh &mesh) {
for (size_t i = 0; i < model.bufferViews.size(); ++i) {
const tinygltf::BufferView &bufferView = model.bufferViews[i];
if (bufferView.target == 0) { // TODO impl drawarrays
@@ -144,12 +144,10 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
}
}
}
return vbos;
}
// bind models
void bindModelNodes(std::map<int, GLuint> vbos, tinygltf::Model &model,
void bindModelNodes(std::map<int, GLuint>& vbos, tinygltf::Model &model,
tinygltf::Node &node) {
if ((node.mesh >= 0) && (node.mesh < model.meshes.size())) {
bindMesh(vbos, model, model.meshes[node.mesh]);
@@ -160,7 +158,8 @@ void bindModelNodes(std::map<int, GLuint> vbos, tinygltf::Model &model,
bindModelNodes(vbos, model, model.nodes[node.children[i]]);
}
}
GLuint bindModel(tinygltf::Model &model) {
std::pair<GLuint, std::map<int, GLuint>> bindModel(tinygltf::Model &model) {
std::map<int, GLuint> vbos;
GLuint vao;
glGenVertexArrays(1, &vao);
@@ -173,19 +172,29 @@ GLuint bindModel(tinygltf::Model &model) {
}
glBindVertexArray(0);
// cleanup vbos
for (size_t i = 0; i < vbos.size(); ++i) {
glDeleteBuffers(1, &vbos[i]);
// cleanup vbos but do not delete index buffers yet
for (auto it = vbos.cbegin(); it != vbos.cend();) {
tinygltf::BufferView bufferView = model.bufferViews[it->first];
if (bufferView.target != GL_ELEMENT_ARRAY_BUFFER) {
glDeleteBuffers(1, &vbos[it->first]);
vbos.erase(it++);
}
else {
++it;
}
}
return vao;
return {vao, vbos};
}
void drawMesh(tinygltf::Model &model, tinygltf::Mesh &mesh) {
void drawMesh(const std::map<int, GLuint>& vbos,
tinygltf::Model &model, tinygltf::Mesh &mesh) {
for (size_t i = 0; i < mesh.primitives.size(); ++i) {
tinygltf::Primitive primitive = mesh.primitives[i];
tinygltf::Accessor indexAccessor = model.accessors[primitive.indices];
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos.at(indexAccessor.bufferView));
glDrawElements(primitive.mode, indexAccessor.count,
indexAccessor.componentType,
BUFFER_OFFSET(indexAccessor.byteOffset));
@@ -193,20 +202,22 @@ void drawMesh(tinygltf::Model &model, tinygltf::Mesh &mesh) {
}
// recursively draw node and children nodes of model
void drawModelNodes(tinygltf::Model &model, tinygltf::Node &node) {
void drawModelNodes(const std::pair<GLuint, std::map<int, GLuint>>& vaoAndEbos,
tinygltf::Model &model, tinygltf::Node &node) {
if ((node.mesh >= 0) && (node.mesh < model.meshes.size())) {
drawMesh(model, model.meshes[node.mesh]);
drawMesh(vaoAndEbos.second, model, model.meshes[node.mesh]);
}
for (size_t i = 0; i < node.children.size(); i++) {
drawModelNodes(model, model.nodes[node.children[i]]);
drawModelNodes(vaoAndEbos, model, model.nodes[node.children[i]]);
}
}
void drawModel(GLuint vao, tinygltf::Model &model) {
glBindVertexArray(vao);
void drawModel(const std::pair<GLuint, std::map<int, GLuint>>& vaoAndEbos,
tinygltf::Model &model) {
glBindVertexArray(vaoAndEbos.first);
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
for (size_t i = 0; i < scene.nodes.size(); ++i) {
drawModelNodes(model, model.nodes[scene.nodes[i]]);
drawModelNodes(vaoAndEbos, model, model.nodes[scene.nodes[i]]);
}
glBindVertexArray(0);
@@ -282,7 +293,7 @@ void displayLoop(Window &window, const std::string &filename) {
tinygltf::Model model;
if (!loadModel(model, filename.c_str())) return;
GLuint vao = bindModel(model);
std::pair<GLuint, std::map<int, GLuint>> vaoAndEbos = bindModel(model);
// dbgModel(model); return;
// Model matrix : an identity matrix (model will be at the origin)
@@ -317,10 +328,12 @@ void displayLoop(Window &window, const std::string &filename) {
glUniform3fv(sun_position_u, 1, &sun_position[0]);
glUniform3fv(sun_color_u, 1, &sun_color[0]);
drawModel(vao, model);
drawModel(vaoAndEbos, model);
glfwSwapBuffers(window.window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &vaoAndEbos.first);
}
static void error_callback(int error, const char *description) {

View File

@@ -771,6 +771,30 @@ static void DrawCurves(tinygltf::Scene &scene, const tinygltf::Mesh &mesh) {
}
#endif
static void QuatToAngleAxis(const std::vector<double> quaternion,
double &outAngleDegrees,
double *axis) {
double qx = quaternion[0];
double qy = quaternion[1];
double qz = quaternion[2];
double qw = quaternion[3];
double angleRadians = 2 * acos(qw);
if (angleRadians == 0.0) {
outAngleDegrees = 0.0;
axis[0] = 0.0;
axis[1] = 0.0;
axis[2] = 1.0;
return;
}
double denom = sqrt(1-qw*qw);
outAngleDegrees = angleRadians * 180.0 / M_PI;
axis[0] = qx / denom;
axis[1] = qy / denom;
axis[2] = qz / denom;
}
// Hierarchically draw nodes
static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
// Apply xform
@@ -781,18 +805,22 @@ static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
glMultMatrixd(node.matrix.data());
} else {
// Assume Trans x Rotate x Scale order
if (node.scale.size() == 3) {
glScaled(node.scale[0], node.scale[1], node.scale[2]);
}
if (node.rotation.size() == 4) {
glRotated(node.rotation[0], node.rotation[1], node.rotation[2],
node.rotation[3]);
}
if (node.translation.size() == 3) {
glTranslated(node.translation[0], node.translation[1],
node.translation[2]);
}
if (node.rotation.size() == 4) {
double angleDegrees;
double axis[3];
QuatToAngleAxis(node.rotation, angleDegrees, axis);
glRotated(angleDegrees, axis[0], axis[1], axis[2]);
}
if (node.scale.size() == 3) {
glScaled(node.scale[0], node.scale[1], node.scale[2]);
}
}

View File

@@ -482,3 +482,61 @@ TEST_CASE("expandpath-utf-8", "[pr-226]") {
}
#endif
TEST_CASE("empty-bin-buffer", "[issue-382]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
tinygltf::Model model_empty;
std::stringstream stream;
bool ret = ctx.WriteGltfSceneToStream(&model_empty, stream, false, true);
REQUIRE(ret == true);
std::string str = stream.str();
const unsigned char* bytes = (unsigned char*)str.data();
ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, bytes, str.size());
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
err.clear();
warn.clear();
tinygltf::Model model_empty_buffer;
model_empty_buffer.buffers.push_back(tinygltf::Buffer());
stream = std::stringstream();
ret = ctx.WriteGltfSceneToStream(&model_empty_buffer, stream, false, true);
REQUIRE(ret == true);
str = stream.str();
bytes = (unsigned char*)str.data();
ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, bytes, str.size());
if (err.empty()) {
std::cerr << "there should have been an error reported" << std::endl;
}
REQUIRE(false == ret);
err.clear();
warn.clear();
tinygltf::Model model_single_byte_buffer;
tinygltf::Buffer buffer;
buffer.data.push_back(0);
model_single_byte_buffer.buffers.push_back(buffer);
stream = std::stringstream();
ret = ctx.WriteGltfSceneToStream(&model_single_byte_buffer, stream, false, true);
REQUIRE(ret == true);
str = stream.str();
{
std::ofstream ofs("tmp.glb");
ofs.write(str.data(), str.size());
}
bytes = (unsigned char*)str.data();
ret = ctx.LoadBinaryFromMemory(&model_single_byte_buffer, &err, &warn, bytes, str.size());
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
}

View File

@@ -26,6 +26,11 @@
// THE SOFTWARE.
// Version:
// - 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
// parameters.
@@ -64,6 +69,11 @@
#include <string>
#include <vector>
//Auto-detect C++14 standard version
#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
#define TINYGLTF_USE_CPP14
#endif
#ifndef TINYGLTF_USE_CPP14
#include <functional>
#endif
@@ -108,7 +118,11 @@ namespace tinygltf {
#define TINYGLTF_COMPONENT_TYPE_INT (5124)
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
(5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
// support double type even the schema seems allow any value of
// integer:
// https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
@@ -613,7 +627,8 @@ struct Sampler {
int wrapT =
TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
// "REPEAT"], default "REPEAT"
//int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used.
// int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
// not used.
Value extras;
ExtensionMap extensions;
@@ -1302,8 +1317,10 @@ class TinyGLTF {
///
/// Loads glTF ASCII asset from string(memory).
/// `length` = strlen(str);
/// Set warning message to `warn` for example it fails to load asserts.
/// Returns false and set error string to `err` if there's an error.
/// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
/// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
/// message to `warn` for example it fails to load asserts. Returns false and
/// set error string to `err` if there's an error.
///
bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
const char *str, const unsigned int length,
@@ -1322,6 +1339,8 @@ class TinyGLTF {
///
/// Loads glTF binary asset from memory.
/// `length` = strlen(str);
/// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
/// expanded path (e.g. no tilde(`~`), no environment variables).
/// Set warning message to `warn` for example it fails to load asserts.
/// Returns false and set error string to `err` if there's an error.
///
@@ -1424,6 +1443,10 @@ class TinyGLTF {
bool preserve_image_channels_ = false; /// Default false(expand channels to
/// RGBA) for backward compatibility.
// Warning & error messages
std::string warn_;
std::string err_;
FsCallbacks fs = {
#ifndef TINYGLTF_NO_FS
&tinygltf::FileExists, &tinygltf::ExpandFilePath,
@@ -1605,10 +1628,10 @@ class TinyGLTF {
#endif
#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
#include <wordexp.h>
//#include <wordexp.h>
#endif
#if defined(__sparcv9)
#if defined(__sparcv9) || defined(__powerpc__)
// Big endian
#else
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
@@ -1933,9 +1956,9 @@ bool Sampler::operator==(const Sampler &other) const {
return this->extensions == other.extensions && this->extras == other.extras &&
this->magFilter == other.magFilter &&
this->minFilter == other.minFilter && this->name == other.name &&
this->wrapT == other.wrapT;
this->wrapS == other.wrapS && this->wrapT == other.wrapT;
//this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
// this->wrapR == other.wrapR
}
bool Scene::operator==(const Scene &other) const {
return this->extensions == other.extensions && this->extras == other.extras &&
@@ -2040,10 +2063,8 @@ static std::string GetBaseDir(const std::string &filepath) {
}
static std::string GetBaseFilename(const std::string &filepath) {
constexpr char path_separators[2] = { '/', '\\' };
auto idx = filepath.find_last_of(path_separators);
if (idx != std::string::npos)
return filepath.substr(idx + 1);
auto idx = filepath.find_last_of("/\\");
if (idx != std::string::npos) return filepath.substr(idx + 1);
return filepath;
}
@@ -2216,7 +2237,7 @@ std::string base64_decode(std::string const &encoded_string) {
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//
namespace dlib {
inline unsigned char from_hex(unsigned char ch) {
@@ -2605,6 +2626,18 @@ bool FileExists(const std::string &abs_filename, void *) {
}
std::string ExpandFilePath(const std::string &filepath, void *) {
// https://github.com/syoyo/tinygltf/issues/368
//
// No file path expansion in built-in FS function anymore, since glTF URI
// should not contain tilde('~') and environment variables, and for security
// reason(`wordexp`).
//
// Users need to supply `base_dir`(in `LoadASCIIFromString`,
// `LoadBinaryFromMemory`) in expanded absolute path.
return filepath;
#if 0
#ifdef _WIN32
// Assume input `filepath` is encoded in UTF-8
std::wstring wfilepath = UTF8ToWchar(filepath);
@@ -2652,6 +2685,7 @@ std::string ExpandFilePath(const std::string &filepath, void *) {
return s;
#endif
#endif
}
bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
@@ -3092,11 +3126,17 @@ std::string JsonToString(const json &o, int spacing = -1) {
StringBuffer buffer;
if (spacing == -1) {
Writer<StringBuffer> writer(buffer);
o.Accept(writer);
// TODO: Better error handling.
// https://github.com/syoyo/tinygltf/issues/332
if (!o.Accept(writer)) {
return "tiny_gltf::JsonToString() failed rapidjson conversion";
}
} else {
PrettyWriter<StringBuffer> writer(buffer);
writer.SetIndent(' ', uint32_t(spacing));
o.Accept(writer);
if (!o.Accept(writer)) {
return "tiny_gltf::JsonToString() failed rapidjson conversion";
}
}
return buffer.GetString();
#else
@@ -3197,9 +3237,11 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
break;
}
#endif
const bool isNotNull = val.Type() != NULL_TYPE;
if (ret) *ret = std::move(val);
return val.Type() != NULL_TYPE;
return isNotNull;
}
static bool ParseExtrasProperty(Value *ret, const json &o) {
@@ -3639,7 +3681,8 @@ static bool ParseParameterProperty(Parameter *param, std::string *err,
// Found a number array.
return true;
} else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
return param->has_number_value = true;
param->has_number_value = true;
return true;
} else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
false)) {
return true;
@@ -3832,7 +3875,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
image->uri = uri;
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
return true;
#endif
#else
std::string decoded_uri = dlib::urldecode(uri);
if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
/* required */ false, /* required bytes */ 0,
@@ -3854,6 +3897,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
}
return false;
}
#endif
}
if (*LoadImageData == nullptr) {
@@ -4070,7 +4114,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
if ((bin_size == 0) || (bin_data == nullptr)) {
if (err) {
(*err) += "Invalid binary data in `Buffer'.\n";
(*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
}
return false;
}
@@ -4216,7 +4260,9 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
accessor->sparse.isSparse = true;
int count = 0;
ParseIntegerProperty(&count, err, o, "count", true);
if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
return false;
}
json_const_iterator indices_iterator;
json_const_iterator values_iterator;
@@ -4234,18 +4280,24 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
const json &values_obj = GetValue(values_iterator);
int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
true);
if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
"bufferView", true, "SparseAccessor")) {
return false;
}
ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
true);
ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
true);
false);
if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
true, "SparseAccessor")) {
return false;
}
int values_buffer_view = 0, values_byte_offset = 0;
ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
true);
if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
true, "SparseAccessor")) {
return false;
}
ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
true);
false);
accessor->sparse.count = count;
accessor->sparse.indices.bufferView = indices_buffer_view;
@@ -4254,8 +4306,6 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
accessor->sparse.values.bufferView = values_buffer_view;
accessor->sparse.values.byteOffset = values_byte_offset;
// todo check theses values
return true;
}
@@ -5082,12 +5132,13 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
int magFilter = -1;
int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
//int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
// int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
//ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
// ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
// extension
// TODO(syoyo): Check the value is alloed one.
// (e.g. we allow 9728(NEAREST), but don't allow 9727)
@@ -5096,7 +5147,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
sampler->magFilter = magFilter;
sampler->wrapS = wrapS;
sampler->wrapT = wrapT;
//sampler->wrapR = wrapR;
// sampler->wrapR = wrapR;
ParseExtensionsProperty(&(sampler->extensions), err, o);
ParseExtrasProperty(&(sampler->extras), o);
@@ -5755,20 +5806,27 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
}
for (auto &attribute : primitive.attributes) {
model
->bufferViews[size_t(
model->accessors[size_t(attribute.second)].bufferView)]
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
const auto accessorsIndex = size_t(attribute.second);
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()) {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ARRAY_BUFFER;
}
}
}
for (auto &target : primitive.targets) {
for (auto &attribute : target) {
auto bufferView =
model->accessors[size_t(attribute.second)].bufferView;
// bufferView could be null(-1) for sparse morph target
if (bufferView >= 0) {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ARRAY_BUFFER;
const auto accessorsIndex = size_t(attribute.second);
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()) {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ARRAY_BUFFER;
}
}
}
}
@@ -6209,44 +6267,125 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
unsigned int version; // 4 bytes
unsigned int length; // 4 bytes
unsigned int model_length; // 4 bytes
unsigned int model_format; // 4 bytes;
unsigned int chunk0_length; // 4 bytes
unsigned int chunk0_format; // 4 bytes;
// @todo { Endian swap for big endian machine. }
memcpy(&version, bytes + 4, 4);
swap4(&version);
memcpy(&length, bytes + 8, 4);
swap4(&length);
memcpy(&model_length, bytes + 12, 4);
swap4(&model_length);
memcpy(&model_format, bytes + 16, 4);
swap4(&model_format);
memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
swap4(&chunk0_length);
memcpy(&chunk0_format, bytes + 16, 4);
swap4(&chunk0_format);
// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
//
// In case the Bin buffer is not present, the size is exactly 20 + size of
// JSON contents,
// so use "greater than" operator.
if ((20 + model_length > size) || (model_length < 1) || (length > size) ||
(20 + model_length > length) ||
(model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
//
// https://github.com/syoyo/tinygltf/issues/372
// Use 64bit uint to avoid integer overflow.
uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
// Do not allow 4GB or more GLB data.
(*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
}
if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
(header_and_json_size > uint64_t(length)) ||
(chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
if (err) {
(*err) = "Invalid glTF binary.";
}
return false;
}
// Padding check
// The start and the end of each chunk must be aligned to a 4-byte boundary.
// No padding check for chunk0 start since its 4byte-boundary is ensured.
if ((header_and_json_size % 4) != 0) {
if (err) {
(*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
}
}
//std::cout << "header_and_json_size = " << header_and_json_size << "\n";
//std::cout << "length = " << length << "\n";
// Chunk1(BIN) data
// The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted.
// So when header + JSON data == binary size, Chunk1 is omitted.
if (header_and_json_size == uint64_t(length)) {
bin_data_ = nullptr;
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)
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";
}
return false;
}
unsigned int chunk1_length; // 4 bytes
unsigned int chunk1_format; // 4 bytes;
memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
swap4(&chunk1_length);
memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
swap4(&chunk1_format);
//std::cout << "chunk1_length = " << chunk1_length << "\n";
if (chunk1_length < 4) {
if (err) {
(*err) = "Insufficient Chunk1(BIN) data size.";
}
return false;
}
if ((chunk1_length % 4) != 0) {
if (err) {
(*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
}
return false;
}
if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
if (err) {
(*err) = "BIN Chunk data length exceeds the GLB size.";
}
return false;
}
if (chunk1_format != 0x004e4942) {
if (err) {
(*err) = "Invlid type for chunk1 data.";
}
return false;
}
//std::cout << "chunk1_length = " << chunk1_length << "\n";
bin_data_ = bytes + header_and_json_size +
8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
bin_size_ = size_t(chunk1_length);
}
// Extract JSON string.
std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
model_length);
chunk0_length);
is_binary_ = true;
bin_data_ = bytes + 20 + model_length +
8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
bin_size_ =
length - (20 + model_length); // extract header + JSON scene data.
bool ret = LoadFromString(model, err, warn,
reinterpret_cast<const char *>(&bytes[20]),
model_length, base_dir, check_sections);
chunk0_length, base_dir, check_sections);
if (!ret) {
return ret;
}
@@ -6677,6 +6816,27 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) {
if (accessor.extras.Type() != NULL_TYPE) {
SerializeValue("extras", accessor.extras, o);
}
// sparse
if (accessor.sparse.isSparse)
{
json sparse;
SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
{
json indices;
SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
JsonAddMember(sparse, "indices", std::move(indices));
}
{
json values;
SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
JsonAddMember(sparse, "values", std::move(values));
}
JsonAddMember(o, "sparse", std::move(sparse));
}
}
static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
@@ -7159,7 +7319,7 @@ static void SerializeGltfSampler(Sampler &sampler, json &o) {
if (sampler.minFilter != -1) {
SerializeNumberProperty("minFilter", sampler.minFilter, o);
}
//SerializeNumberProperty("wrapR", sampler.wrapR, o);
// SerializeNumberProperty("wrapR", sampler.wrapR, o);
SerializeNumberProperty("wrapS", sampler.wrapS, o);
SerializeNumberProperty("wrapT", sampler.wrapT, o);
@@ -7513,7 +7673,7 @@ static bool WriteGltfFile(const std::string &output,
return WriteGltfStream(gltfFile, content);
}
static void WriteBinaryGltfStream(std::ostream &stream,
static bool WriteBinaryGltfStream(std::ostream &stream,
const std::string &content,
const std::vector<unsigned char> &binBuffer) {
const std::string header = "glTF";
@@ -7522,8 +7682,10 @@ static void WriteBinaryGltfStream(std::ostream &stream,
const uint32_t content_size = uint32_t(content.size());
const uint32_t binBuffer_size = uint32_t(binBuffer.size());
// determine number of padding bytes required to ensure 4 byte alignment
const uint32_t content_padding_size = content_size % 4 == 0 ? 0 : 4 - content_size % 4;
const uint32_t bin_padding_size = binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
const uint32_t content_padding_size =
content_size % 4 == 0 ? 0 : 4 - content_size % 4;
const uint32_t bin_padding_size =
binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
// Chunk data must be located at 4-byte boundary, which may require padding
@@ -7567,9 +7729,12 @@ static void WriteBinaryGltfStream(std::ostream &stream,
std::streamsize(padding.size()));
}
}
// TODO: Check error on stream.write
return true;
}
static void WriteBinaryGltfFile(const std::string &output,
static bool WriteBinaryGltfFile(const std::string &output,
const std::string &content,
const std::vector<unsigned char> &binBuffer) {
#ifdef _WIN32
@@ -7587,7 +7752,7 @@ static void WriteBinaryGltfFile(const std::string &output,
#else
std::ofstream gltfFile(output.c_str(), std::ios::binary);
#endif
WriteBinaryGltfStream(gltfFile, content, binBuffer);
return WriteBinaryGltfStream(gltfFile, content, binBuffer);
}
bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
@@ -7626,7 +7791,7 @@ 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), false,
UpdateImageObject(model->images[i], dummystring, int(i), true,
&this->WriteImageData, this->write_image_user_data_);
SerializeGltfImage(model->images[i], image);
JsonPushBack(images, std::move(image));
@@ -7635,12 +7800,11 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
}
if (writeBinary) {
WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
return WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
} else {
WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
}
return true;
}
bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
@@ -7725,12 +7889,11 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
}
if (writeBinary) {
WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
return WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
} else {
WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
}
return true;
}
} // namespace tinygltf