mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-08 11:13:50 +00:00
Compare commits
43 Commits
copilot/su
...
copilot/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fb9c9421d | ||
|
|
ebc9f765c5 | ||
|
|
5a3f56d43c | ||
|
|
577f08a680 | ||
|
|
aa41d25054 | ||
|
|
721430aa5f | ||
|
|
87b0d175c6 | ||
|
|
9b3947c976 | ||
|
|
9bcfcea6e0 | ||
|
|
9422613562 | ||
|
|
135695e918 | ||
|
|
b163ff225a | ||
|
|
1a04c114c6 | ||
|
|
b5a962f1f4 | ||
|
|
f143766625 | ||
|
|
1215adc13a | ||
|
|
826b71cc24 | ||
|
|
dfd94f03fb | ||
|
|
131c4489fa | ||
|
|
9248070755 | ||
|
|
d8fb6cad78 | ||
|
|
e2e40f58ae | ||
|
|
306c72fce9 | ||
|
|
594c3a057b | ||
|
|
ad316367b9 | ||
|
|
1f15c2d140 | ||
|
|
1d5e721a24 | ||
|
|
c9a9b1175a | ||
|
|
5e0c5b9ada | ||
|
|
03b9db782e | ||
|
|
c99e713fab | ||
|
|
8c8cbfa0ba | ||
|
|
0949983acc | ||
|
|
c870bd5fd6 | ||
|
|
b76cf7aa21 | ||
|
|
f7bd377a69 | ||
|
|
5d6984b9fd | ||
|
|
3331c6cee2 | ||
|
|
97316e140c | ||
|
|
12affdcc64 | ||
|
|
2c1a8be82d | ||
|
|
df3efc6453 | ||
|
|
99720ea0cc |
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure
|
||||
run: cmake -B build -DTINYGLTF_BUILD_LOADER_EXAMPLE=ON
|
||||
run: cmake -B build -DTINYGLTF_BUILD_LOADER_EXAMPLE=ON -DTINYGLTF_BUILD_V3_VALIDATOR_TOOL=ON
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
@@ -37,6 +37,17 @@ jobs:
|
||||
- name: Run tests
|
||||
run: ctest --test-dir build --output-on-failure
|
||||
|
||||
- name: Run v3 validator on tracked glTF files
|
||||
continue-on-error: true
|
||||
run: |
|
||||
rc=0
|
||||
while IFS= read -r path; do
|
||||
echo "::group::Validate ${path}"
|
||||
./build/tools/validator/tinygltf3-validator "${path}" || rc=1
|
||||
echo "::endgroup::"
|
||||
done < <(git ls-files '*.gltf')
|
||||
exit $rc
|
||||
|
||||
# Linux x64 - Clang 21
|
||||
linux-clang-x64:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
# CMake
|
||||
/build/
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
@@ -71,6 +72,7 @@ imgui.ini
|
||||
loader_example
|
||||
tests/tester
|
||||
tests/tester_noexcept
|
||||
tests/tester_intensive_customjson
|
||||
tests/issue-97.gltf
|
||||
tests/issue-261.gltf
|
||||
tests/issue-495-external.gltf
|
||||
|
||||
@@ -13,6 +13,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
option(TINYGLTF_BUILD_LOADER_EXAMPLE "Build loader_example(load glTF and dump infos)" ON)
|
||||
option(TINYGLTF_BUILD_GL_EXAMPLES "Build GL exampels(requires glfw, OpenGL, etc)" OFF)
|
||||
option(TINYGLTF_BUILD_VALIDATOR_EXAMPLE "Build validator exampe" OFF)
|
||||
option(TINYGLTF_BUILD_V3_VALIDATOR_TOOL "Build tg3_validate CLI tool" OFF)
|
||||
option(TINYGLTF_BUILD_BUILDER_EXAMPLE "Build glTF builder example" OFF)
|
||||
option(TINYGLTF_BUILD_TESTS "Build unit tests" OFF)
|
||||
option(TINYGLTF_HEADER_ONLY "On: header-only mode. Off: create tinygltf library(No TINYGLTF_IMPLEMENTATION required in your project)" OFF)
|
||||
@@ -35,6 +36,10 @@ if (TINYGLTF_BUILD_VALIDATOR_EXAMPLE)
|
||||
add_subdirectory( examples/validator )
|
||||
endif (TINYGLTF_BUILD_VALIDATOR_EXAMPLE)
|
||||
|
||||
if (TINYGLTF_BUILD_V3_VALIDATOR_TOOL)
|
||||
add_subdirectory( tools/validator )
|
||||
endif (TINYGLTF_BUILD_V3_VALIDATOR_TOOL)
|
||||
|
||||
if (TINYGLTF_BUILD_BUILDER_EXAMPLE)
|
||||
add_subdirectory ( examples/build-gltf )
|
||||
endif (TINYGLTF_BUILD_BUILDER_EXAMPLE)
|
||||
@@ -56,6 +61,24 @@ if (TINYGLTF_BUILD_TESTS)
|
||||
)
|
||||
target_compile_definitions(tester_customjson PRIVATE TINYGLTF_USE_CUSTOM_JSON)
|
||||
add_test(NAME tester_customjson COMMAND tester_customjson WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
||||
|
||||
# Intensive parser tests for the custom JSON backend
|
||||
add_executable(tester_intensive_customjson tests/tester_intensive_customjson.cc)
|
||||
target_include_directories(tester_intensive_customjson PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tests
|
||||
)
|
||||
target_compile_definitions(tester_intensive_customjson PRIVATE TINYGLTF_USE_CUSTOM_JSON)
|
||||
add_test(NAME tester_intensive_customjson COMMAND tester_intensive_customjson WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
||||
|
||||
# v3 API tests (parser + validator)
|
||||
add_executable(tester_v3 tests/v3/tester_v3.cc)
|
||||
target_include_directories(tester_v3 PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tests
|
||||
)
|
||||
set_target_properties(tester_v3 PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON)
|
||||
add_test(NAME tester_v3 COMMAND tester_v3 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
||||
endif (TINYGLTF_BUILD_TESTS)
|
||||
|
||||
#
|
||||
|
||||
53
README.md
53
README.md
@@ -1,14 +1,57 @@
|
||||
# Header only C++ tiny glTF library(loader/saver).
|
||||
|
||||
`TinyGLTF` is a header only C++11 glTF 2.0 https://github.com/KhronosGroup/glTF library.
|
||||
`TinyGLTF` is a header only C++ 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).
|
||||
## TinyGLTF v3 (new major release)
|
||||
|
||||
**`tiny_gltf_v3.h`** is the new major version of TinyGLTF and the recommended API for new projects.
|
||||
|
||||
### What's new in v3
|
||||
|
||||
v3 is a ground-up rewrite with a C-centric, low-overhead design:
|
||||
|
||||
- **Pure C POD structs** — no STL containers in the public API; easy to bind to other languages.
|
||||
- **Arena-based memory management** — all parse-time allocations come from a single arena; a single `tg3_model_free()` frees everything.
|
||||
- **Structured error reporting** — `tg3_error_stack` provides machine-readable errors with severity levels and source locations.
|
||||
- **Custom JSON backend** — backed by `tinygltf_json.h`, a high-performance, locale-independent JSON parser with optional SIMD acceleration (SSE2 / AVX2 / NEON) and a float32 fast-path.
|
||||
- **Streaming callbacks** — opt-in streaming parse/write via user-supplied callbacks.
|
||||
- **No RTTI, no exceptions required** — suitable for embedded and game-engine use.
|
||||
- **Opt-in filesystem and image I/O** — `TINYGLTF3_ENABLE_FS` / `TINYGLTF3_ENABLE_STB_IMAGE` are off by default; you control when and how assets are loaded.
|
||||
- **C++20 coroutine facade** (optional, auto-detected). C17/C++17 default.
|
||||
|
||||
### Quick start (v3)
|
||||
|
||||
Copy `tiny_gltf_v3.h` and `tinygltf_json.h` to your project. In **one** `.cpp` file:
|
||||
|
||||
```cpp
|
||||
#define TINYGLTF3_IMPLEMENTATION
|
||||
#define TINYGLTF3_ENABLE_FS // enable file I/O
|
||||
#define TINYGLTF3_ENABLE_STB_IMAGE // enable image decoding
|
||||
#include "tiny_gltf_v3.h"
|
||||
```
|
||||
|
||||
Loading a glTF file:
|
||||
|
||||
```c
|
||||
tg3_load_options_t opts = tg3_load_options_default();
|
||||
tg3_error_stack_t errors = {0};
|
||||
tg3_model_t *model = tg3_load_from_file("scene.gltf", &opts, &errors);
|
||||
if (!model) {
|
||||
for (int i = 0; i < errors.count; i++)
|
||||
fprintf(stderr, "[%s] %s\n", tg3_severity_str(errors.items[i].severity),
|
||||
errors.items[i].message);
|
||||
}
|
||||
// ... use model ...
|
||||
tg3_model_free(model);
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
Currently TinyGLTF is stable and maintenance mode. No drastic changes and feature additions planned.
|
||||
> ⚠️ **v2 deprecation notice:** `tiny_gltf.h` (v2) remains fully functional and is still supported,
|
||||
> but it is now in **maintenance mode only** — no new features will be added.
|
||||
> v2 will be **sunset after mid-2026**. New projects should use `tiny_gltf_v3.h`.
|
||||
|
||||
Currently TinyGLTF v2 is stable and in maintenance mode. No drastic changes and feature additions planned.
|
||||
- v2.9.0 Various fixes and improvements. Filesystem callback API change.
|
||||
- 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
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
all: ../tiny_gltf.h
|
||||
clang++ -I../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o tester tester.cc
|
||||
clang++ -DTINYGLTF_NOEXCEPTION -I../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o tester_noexcept tester.cc
|
||||
clang++ -DTINYGLTF_USE_CUSTOM_JSON -I../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o tester_intensive_customjson tester_intensive_customjson.cc
|
||||
|
||||
@@ -4,9 +4,14 @@ Do fuzzing test for TinyGLTF API.
|
||||
|
||||
## Supported API
|
||||
|
||||
* [x] LoadASCIIFromMemory
|
||||
* [x] LoadASCIIFromString
|
||||
* [ ] LoadBinaryFromMemory
|
||||
|
||||
### Custom JSON backend (`tinygltf_json.h`)
|
||||
|
||||
* [x] LoadASCIIFromString
|
||||
* [x] LoadBinaryFromMemory
|
||||
|
||||
## Requirements
|
||||
|
||||
* meson
|
||||
@@ -36,11 +41,17 @@ $ cd build
|
||||
$ ninja
|
||||
```
|
||||
|
||||
This builds two fuzzers:
|
||||
|
||||
* `fuzz_gltf` – default nlohmann/json backend
|
||||
* `fuzz_gltf_customjson` – custom `tinygltf_json.h` backend (tests both ASCII and binary parsing paths)
|
||||
|
||||
## How to run
|
||||
|
||||
Increase memory limit. e.g. `-rss_limit_mb=50000`
|
||||
|
||||
```
|
||||
$ ./fuzz_gltf -rss_limit_mb=20000 -jobs 4
|
||||
$ ./fuzz_gltf_customjson -rss_limit_mb=20000 -jobs 4
|
||||
```
|
||||
|
||||
|
||||
76
tests/fuzzer/fuzz_gltf_customjson.cc
Normal file
76
tests/fuzzer/fuzz_gltf_customjson.cc
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* LLVM libFuzzer harness for tinygltf with the custom JSON backend
|
||||
* (tinygltf_json.h).
|
||||
*
|
||||
* Exercises:
|
||||
* 1. LoadASCIIFromString – glTF JSON parsing
|
||||
* 2. LoadBinaryFromMemory – GLB binary parsing
|
||||
*
|
||||
* Build (clang with libFuzzer):
|
||||
* clang++ -std=c++11 -fsanitize=address,fuzzer \
|
||||
* -DTINYGLTF_USE_CUSTOM_JSON \
|
||||
* -I../../ fuzz_gltf_customjson.cc \
|
||||
* -o fuzz_gltf_customjson
|
||||
*
|
||||
* Run:
|
||||
* ./fuzz_gltf_customjson -rss_limit_mb=20000 -jobs 4
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#ifndef TINYGLTF_USE_CUSTOM_JSON
|
||||
#define TINYGLTF_USE_CUSTOM_JSON
|
||||
#endif
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
/* Fuzz the ASCII (JSON) parser path */
|
||||
static void fuzz_ascii(const uint8_t *data, size_t size) {
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF ctx;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
const char *str = reinterpret_cast<const char *>(data);
|
||||
|
||||
bool ret =
|
||||
ctx.LoadASCIIFromString(&model, &err, &warn, str,
|
||||
static_cast<unsigned int>(size), /* base_dir */ "");
|
||||
(void)ret;
|
||||
}
|
||||
|
||||
/* Fuzz the binary (GLB) parser path */
|
||||
static void fuzz_binary(const uint8_t *data, size_t size) {
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF ctx;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
bool ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, data,
|
||||
static_cast<unsigned int>(size),
|
||||
/* base_dir */ "");
|
||||
(void)ret;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
if (size == 0) return 0;
|
||||
|
||||
/* Use the lowest bit of the first byte to select the parse path.
|
||||
* The remaining bits are left for the fuzzer engine to explore;
|
||||
* additional paths (e.g. LoadASCIIFromFile, check_sections flags)
|
||||
* can be added here in the future using more selector bits. */
|
||||
uint8_t selector = data[0];
|
||||
const uint8_t *payload = data + 1;
|
||||
size_t payload_size = size - 1;
|
||||
|
||||
if (selector & 1) {
|
||||
fuzz_binary(payload, payload_size);
|
||||
} else {
|
||||
fuzz_ascii(payload, payload_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -7,3 +7,9 @@ executable('fuzz_gltf',
|
||||
cpp_args : '-fsanitize=address,fuzzer',
|
||||
link_args : '-fsanitize=address,fuzzer' )
|
||||
|
||||
executable('fuzz_gltf_customjson',
|
||||
'fuzz_gltf_customjson.cc',
|
||||
include_directories : incdirs,
|
||||
cpp_args : ['-fsanitize=address,fuzzer', '-DTINYGLTF_USE_CUSTOM_JSON'],
|
||||
link_args : '-fsanitize=address,fuzzer' )
|
||||
|
||||
|
||||
@@ -1249,3 +1249,37 @@ TEST_CASE("empty-images-not-written", "[issue-495]") {
|
||||
// WriteImageData should be invoked for both images
|
||||
CHECK(counter == 2);
|
||||
}
|
||||
|
||||
#ifdef TINYGLTF_USE_CUSTOM_JSON
|
||||
/* Regression test: in float32_mode, integer-only tokens with more than 9
|
||||
* digits must still be parsed as integers (is_int == 1), not floats.
|
||||
* Previously, max_sig=9 was applied to the integer part too, causing excess
|
||||
* digits to bump exp10, which broke the exp10==0 guard in the integer
|
||||
* fast-path and mis-classified the value as a float. */
|
||||
TEST_CASE("cj-float32-long-integer", "[customjson]") {
|
||||
// Values chosen to cover exactly-at, just-over, and near int64 boundaries.
|
||||
struct {
|
||||
const char *text;
|
||||
int64_t expected;
|
||||
} cases[] = {
|
||||
{ "1234567890", 1234567890LL }, /* 10 digits */
|
||||
{ "12345678901", 12345678901LL }, /* 11 digits */
|
||||
{ "1000000000000", 1000000000000LL }, /* 13 digits */
|
||||
{ "9223372036854775807", INT64_MAX }, /* max int64 (19 digits) */
|
||||
{ "-1234567890", -1234567890LL }, /* negative 10 digits */
|
||||
{ "-9223372036854775808", INT64_MIN }, /* min int64 */
|
||||
};
|
||||
|
||||
for (auto &tc : cases) {
|
||||
int is_int = 0;
|
||||
int64_t ival = 0;
|
||||
double dval = 0.0;
|
||||
const char *end = tc.text + strlen(tc.text);
|
||||
const char *ret = cj_parse_number(tc.text, end, &is_int, &ival, &dval, /*float32_mode=*/1);
|
||||
CAPTURE(tc.text);
|
||||
REQUIRE(ret != nullptr);
|
||||
CHECK(is_int == 1);
|
||||
CHECK(ival == tc.expected);
|
||||
}
|
||||
}
|
||||
#endif /* TINYGLTF_USE_CUSTOM_JSON */
|
||||
|
||||
1089
tests/tester_intensive_customjson.cc
Normal file
1089
tests/tester_intensive_customjson.cc
Normal file
File diff suppressed because it is too large
Load Diff
1079
tests/v3/tester_v3.cc
Normal file
1079
tests/v3/tester_v3.cc
Normal file
File diff suppressed because it is too large
Load Diff
993
tiny_gltf_v3.h
993
tiny_gltf_v3.h
File diff suppressed because it is too large
Load Diff
@@ -297,8 +297,9 @@ static const char *cj_scan_str(const char *p, const char *end) {
|
||||
* FAST NUMBER PARSING (C-style)
|
||||
*
|
||||
* Uses Clinger's fast path for float conversion, avoiding strtod() for the
|
||||
* vast majority of JSON numbers. This is locale-independent and typically
|
||||
* 4-10x faster than strtod.
|
||||
* vast majority of JSON numbers. The fast path itself is locale-independent
|
||||
* and typically 4-10x faster than strtod; however, rare fallback paths may
|
||||
* still invoke the C library's strtod(), which can be locale-dependent.
|
||||
*
|
||||
* Optional float32 mode (CJ_FLOAT32_MODE flag in cj_parse_number):
|
||||
* Parses floating-point values to float (single) precision and stores
|
||||
@@ -307,7 +308,8 @@ static const char *cj_scan_str(const char *p, const char *end) {
|
||||
* Breaks strict JSON/IEEE-754-double conformance.
|
||||
* ====================================================================== */
|
||||
|
||||
/* Safe double-to-int64 cast: clamp inf/NaN/out-of-range to 0. */
|
||||
/* Safe double-to-int64 cast: returns 0 for NaN; clamps +inf/out-of-range-high
|
||||
* to INT64_MAX and -inf/out-of-range-low to INT64_MIN. */
|
||||
static int64_t cj_dbl_to_i64(double d) {
|
||||
if (d != d) return 0; /* NaN */
|
||||
if (d >= (double)INT64_MAX) return INT64_MAX;
|
||||
@@ -421,9 +423,11 @@ static int cj_fast_flt_convert(uint64_t mantissa, int exp10, int neg, float *out
|
||||
* Returns pointer past the last character consumed, or NULL on error.
|
||||
*
|
||||
* float32_mode: when non-zero, floating-point values are parsed at float
|
||||
* (single) precision — fewer digits are significant, and the result is
|
||||
* stored as (double)(float)value. This is faster but not JSON-conformant
|
||||
* for high-precision doubles.
|
||||
* (single) precision — only 9 significant digits are tracked for the
|
||||
* fraction part, and the result is stored as (double)(float)value. This
|
||||
* is faster but not JSON-conformant for high-precision doubles. Integer-
|
||||
* only tokens (no '.'/'e') are always parsed at full int64 precision
|
||||
* regardless of this flag.
|
||||
*
|
||||
* Uses Clinger's fast path (no strtod) for ~99% of JSON float values.
|
||||
* Falls back to strtod only for extreme exponents or >19 significant digits. */
|
||||
@@ -443,8 +447,12 @@ static const char *cj_parse_number(const char *p, const char *end,
|
||||
int mantissa_overflow = 0; /* set if >19 significant digits */
|
||||
int has_frac = 0, has_exp = 0;
|
||||
|
||||
/* Max significant digits we track: 19 for double, 9 for float32 */
|
||||
int max_sig = float32_mode ? 9 : 19;
|
||||
/* Max significant digits we track:
|
||||
* Integer part: always 19, so integer-only tokens (no '.'/'e') are always
|
||||
* accumulated fully and can be typed as int64 regardless of float32_mode.
|
||||
* Fraction part: 9 in float32_mode (single precision), 19 otherwise. */
|
||||
int max_sig_int = 19;
|
||||
int max_sig_frac = float32_mode ? 9 : 19;
|
||||
|
||||
/* Integer part */
|
||||
if (*p == '0') {
|
||||
@@ -452,7 +460,7 @@ static const char *cj_parse_number(const char *p, const char *end,
|
||||
} else if ((unsigned)(*p - '1') <= 8u) {
|
||||
while (p < end && (unsigned)(*p - '0') <= 9u) {
|
||||
unsigned d = (unsigned)(*p - '0');
|
||||
if (ndigits < max_sig) {
|
||||
if (ndigits < max_sig_int) {
|
||||
mantissa = mantissa * 10 + d;
|
||||
} else {
|
||||
exp10++; /* excess digit: bump exponent instead */
|
||||
@@ -473,7 +481,7 @@ static const char *cj_parse_number(const char *p, const char *end,
|
||||
if (p >= end || (unsigned)(*p - '0') > 9u) return NULL;
|
||||
while (p < end && (unsigned)(*p - '0') <= 9u) {
|
||||
unsigned d = (unsigned)(*p - '0');
|
||||
if (ndigits < max_sig) {
|
||||
if (ndigits < max_sig_frac) {
|
||||
mantissa = mantissa * 10 + d;
|
||||
exp10--;
|
||||
}
|
||||
|
||||
22
tools/validator/CMakeLists.txt
Normal file
22
tools/validator/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
add_executable(tinygltf3_validator
|
||||
validator.cc
|
||||
)
|
||||
|
||||
target_include_directories(tinygltf3_validator PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../..
|
||||
)
|
||||
|
||||
if (TINYGLTF_USE_CUSTOM_JSON)
|
||||
target_compile_definitions(tinygltf3_validator PRIVATE TINYGLTF_USE_CUSTOM_JSON)
|
||||
endif ()
|
||||
|
||||
set_target_properties(tinygltf3_validator PROPERTIES
|
||||
CXX_STANDARD 11
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
OUTPUT_NAME tinygltf3-validator
|
||||
)
|
||||
|
||||
install(TARGETS tinygltf3_validator
|
||||
DESTINATION bin
|
||||
)
|
||||
95
tools/validator/validator.cc
Normal file
95
tools/validator/validator.cc
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#define TINYGLTF3_IMPLEMENTATION
|
||||
#define TINYGLTF3_ENABLE_FS
|
||||
#include "tiny_gltf_v3.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const char *severity_name(tg3_severity severity) {
|
||||
switch (severity) {
|
||||
case TG3_SEVERITY_WARNING:
|
||||
return "warning";
|
||||
case TG3_SEVERITY_ERROR:
|
||||
return "error";
|
||||
default:
|
||||
return "info";
|
||||
}
|
||||
}
|
||||
|
||||
void print_errors(std::ostream &os, const tg3_error_stack *errors) {
|
||||
const uint32_t count = tg3_errors_count(errors);
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const tg3_error_entry *entry = tg3_errors_get(errors, i);
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
os << severity_name(entry->severity);
|
||||
if (entry->json_path && entry->json_path[0] != '\0') {
|
||||
os << " " << entry->json_path;
|
||||
}
|
||||
if (entry->message && entry->message[0] != '\0') {
|
||||
os << ": " << entry->message;
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
int usage(const char *name) {
|
||||
std::cerr << "Usage: " << name << " <path/to/model.gltf|model.glb>\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 2) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
const char *filename = argv[1];
|
||||
|
||||
tg3_model model;
|
||||
tg3_error_stack errors;
|
||||
tg3_parse_options options;
|
||||
|
||||
std::memset(&model, 0, sizeof(model));
|
||||
model.default_scene = -1;
|
||||
tg3_error_stack_init(&errors);
|
||||
tg3_parse_options_init(&options);
|
||||
|
||||
tg3_error_code rc =
|
||||
tg3_parse_file(&model, &errors, filename,
|
||||
static_cast<uint32_t>(std::strlen(filename)), &options);
|
||||
|
||||
if (tg3_errors_count(&errors) > 0) {
|
||||
print_errors(std::cerr, &errors);
|
||||
}
|
||||
|
||||
if (rc != TG3_OK) {
|
||||
tg3_model_free(&model);
|
||||
tg3_error_stack_free(&errors);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
tg3_error_stack_free(&errors);
|
||||
tg3_error_stack_init(&errors);
|
||||
|
||||
rc = tg3_validate(&model, &errors);
|
||||
|
||||
if (tg3_errors_count(&errors) > 0) {
|
||||
print_errors(rc == TG3_OK ? std::cout : std::cerr, &errors);
|
||||
}
|
||||
|
||||
if (rc == TG3_OK) {
|
||||
std::cout << filename << ": valid glTF 2.0\n";
|
||||
}
|
||||
|
||||
tg3_model_free(&model);
|
||||
tg3_error_stack_free(&errors);
|
||||
|
||||
return (rc == TG3_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
Reference in New Issue
Block a user