Compare commits

..

4 Commits

Author SHA1 Message Date
Syoyo Fujita
98eef6ee75 Fix non-KTX texture was not handled correctly.
Experience file drop API of glfw(not working yet).
2019-07-06 15:13:13 +09:00
Syoyo Fujita
c7bf800075 Support KTX texture of R8G8B8 format in basic example. 2019-07-05 16:20:53 +09:00
Syoyo Fujita
006ab90c67 Initial support of KTX texture load using TinyKTX. 2019-07-04 14:30:28 +09:00
Syoyo Fujita
6f7518255f Add tinyktx.
Add workaround for OpenGL3 context creation in `basic` example.
2019-07-03 16:41:11 +09:00
41 changed files with 3692 additions and 16051 deletions

View File

@@ -31,17 +31,6 @@ matrix:
- addons: *1
compiler: clang
env: COMPILER_VERSION=3.9 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0"
- addons: &3
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
compiler: gcc
env: COMPILER_VERSION=4.8 BUILD_TYPE=Debug
- addons: *3
compiler: gcc
env: COMPILER_VERSION=4.8 BUILD_TYPE=Release
before_install:
- ./.travis-before-install.sh
@@ -54,7 +43,6 @@ script:
- ${CXX} ${EXTRA_CXXFLAGS} -std=c++11 -Wall -g -o loader_example loader_example.cc
- ./loader_example ./models/Cube/Cube.gltf
- cd tests
- clang++ -v
- make
- ./tester
- ./tester_noexcept

View File

@@ -4,21 +4,14 @@ PROJECT (tinygltf)
SET(CMAKE_CXX_STANDARD 11)
option(TINYGLTF_BUILD_EXAMPLES "Build examples" ON)
ADD_EXECUTABLE ( loader_example
loader_example.cc
)
if (TINYGLTF_BUILD_EXAMPLES)
ADD_EXECUTABLE ( loader_example
loader_example.cc
)
ADD_SUBDIRECTORY ( examples/gltfutil )
ADD_SUBDIRECTORY ( examples/glview )
ADD_SUBDIRECTORY ( examples/validator )
ADD_SUBDIRECTORY ( examples/gltfutil )
ADD_SUBDIRECTORY ( examples/glview )
ADD_SUBDIRECTORY ( examples/validator )
endif (TINYGLTF_BUILD_EXAMPLES)
#
# TinuGLTF is a header-only library, so no library build. just install header files.
#
INSTALL ( FILES
json.hpp
stb_image.h

25
LICENSE.tinyktx Normal file
View File

@@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2019, DeanoC
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -7,8 +7,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
## Status
- 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.3.0 release(Support loading KTX image through tiny_ktx)
- v2.2.0 release(Support loading 16bit PNG. Sparse accessor support)
- v2.1.0 release(Draco support)
- v2.0.0 release(22 Aug, 2018)!
@@ -34,11 +33,8 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
* Moderate parsing time and memory consumption.
* glTF specification v2.0.0
* [x] ASCII glTF
* [x] Load
* [x] Save
* [x] Binary glTF(GLB)
* [x] Load
* [x] Save(.bin embedded .glb)
* [x] PBR material description
* Buffers
* [x] Parse BASE64 encoded embedded buffer data(DataURI).
* [x] Load `.bin` file.
@@ -57,21 +53,14 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
* [x] Image load
* [x] Image save
* Extensions
* [x] Draco mesh decoding
* [ ] Draco mesh encoding
## Note on extension property
In extension(`ExtensionMap`), JSON number value is parsed as int or float(number) and stored as `tinygltf::Value` object. If you want a floating point value from `tinygltf::Value`, use `GetNumberAsDouble()` method.
`IsNumber()` returns true if the underlying value is an int value or a floating point value.
* [x] Draco mesh decoding(`TINYGLTF_ENABLE_DRACO` required)
* [x] KTX image support(no mipmap. `TINYGLTF_ENABLE_KTX` required)
## Examples
* [glview](examples/glview) : Simple glTF geometry viewer.
* [validator](examples/validator) : Simple glTF validator with JSON schema.
* [basic](examples/basic) : Basic glTF viewer with texturing support.
* [mesh-conv](examples/mesh-conv) : Convert glTF mesh to wavefront .obj, wavefront .obj to glTF mesh.
## Projects using TinyGLTF
@@ -80,10 +69,6 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
* GLTF loader plugin for OGRE 2.1. Support for PBR materials via HLMS/PBS https://github.com/Ybalrid/Ogre_glTF
* [TinyGltfImporter](http://doc.magnum.graphics/magnum/classMagnum_1_1Trade_1_1TinyGltfImporter.html) plugin for [Magnum](https://github.com/mosra/magnum), a lightweight and modular C++11/C++14 graphics middleware for games and data visualization.
* [Diligent Engine](https://github.com/DiligentGraphics/DiligentEngine) - A modern cross-platform low-level graphics library and rendering framework
* Lighthouse 2: a rendering framework for real-time ray tracing / path tracing experiments. https://github.com/jbikker/lighthouse2
* [QuickLook GLTF](https://github.com/toshiks/glTF-quicklook) - quicklook plugin for macos. Also SceneKit wrapper for tinygltf.
* [GlslViewer](https://github.com/patriciogonzalezvivo/glslViewer) - live GLSL coding for MacOS and Linux
* [Vulkan-Samples](https://github.com/KhronosGroup/Vulkan-Samples) - The Vulkan Samples is collection of resources to help you develop optimized Vulkan applications.
* Your projects here! (Please send PR)
## TODOs
@@ -93,11 +78,12 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
* [x] Load Draco compressed mesh
* [ ] Save Draco compressed mesh
* [ ] Open3DGC?
* [x] Support `extensions` and `extras` property
* [ ] Support `extensions` and `extras` property
* [ ] HDR image?
* [ ] OpenEXR extension through TinyEXR.
* [ ] 16bit PNG support in Serialization
* [ ] Write example and tests for `animation` and `skin`
* [ ] mipmap support for KTX image.
## Licenses
@@ -109,12 +95,15 @@ TinyGLTF uses the following third party libraries.
* base64 : Copyright (C) 2004-2008 René Nyffenegger
* stb_image.h : v2.08 - public domain image loader - [Github link](https://github.com/nothings/stb/blob/master/stb_image.h)
* stb_image_write.h : v1.09 - public domain image writer - [Github link](https://github.com/nothings/stb/blob/master/stb_image_write.h)
* tinyktx.h : Copyright (c) 2019, DeanoC. Licensed under 2 clause BSD license.
## Build and example
Copy `stb_image.h`, `stb_image_write.h`, `json.hpp` and `tiny_gltf.h` to your project.
If you enable KTX support(`TINYGLTF_ENABLE_KTX`), Copy `tinyktx.h` to your project.
### Loading glTF 2.0 model
```c++
@@ -160,22 +149,19 @@ if (!ret) {
* `TINYGLTF_NO_INCLUDE_JSON `: Disable including `json.hpp` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
* `TINYGLTF_NO_INCLUDE_STB_IMAGE `: Disable including `stb_image.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
* `TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE `: Disable including `stb_image_write.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
* `TINYGLTF_USE_RAPIDJSON` : Use RapidJSON as a JSON parser/serializer. RapidJSON files are not included in TinyGLTF repo. Please set an include path to RapidJSON if you enable this featrure.
* `TINYGLTF_USE_CPP14` : Use C++14 feature(requires C++14 compiler). This may give better performance than C++11.
* `TINYGLTF_ENABLE_KTX` : Enable loading KTX images( https://www.khronos.org/opengles/sdk/tools/KTX/ ) using `tiny_ktx.h`. Supported MIME is `image/ktx` ( https://github.com/KhronosGroup/glTF/issues/835 ). See `models/Cube-KTX` for details. Application also defined `TINYKTX_IMPLEMENTATION` in **one** .cc file.
* `TINYGLTF_NO_INCLUDE_TINYKTX` : Disable including `tinyktx.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
### Saving gltTF 2.0 model
* Buffers.
* [ ] Buffers.
* [x] To file
* [x] Embedded
* [ ] Draco compressed?
* [x] Images
* [x] To file
* [x] Embedded
* Binary(.glb)
* [x] .bin embedded single .glb
* [ ] External .bin
* [ ] Binary(.glb)
## Running tests.
@@ -203,21 +189,6 @@ $ ./tester
$ ./tester_noexcept
```
### Fuzzing tests
## Third party licenses used in unit tests
See `tests/fuzzer` for details.
After running fuzzer on Ryzen9 3950X a week, at least `LoadASCIIFromString` looks safe except for out-of-memory error in Fuzzer.
We may be better to introduce bounded memory size checking when parsing glTF data.
## Third party licenses
* json.hpp : Licensed under the MIT License <http://opensource.org/licenses/MIT>. Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
* stb_image : Public domain.
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.
* RapidJSON : Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. http://rapidjson.org/
* dlib(uridecode, uriencode) : Copyright (C) 2003 Davis E. King Boost Software License 1.0. http://dlib.net/dlib/server/server_http.cpp.html
### Used in examples
* clipp: MIT License. https://github.com/muellan/clipp

View File

@@ -8,14 +8,34 @@
#include "shaders.h"
#include "window.h"
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
// Inlude tinyktx.h before tiny_gltf.h
// to get TKTX_*** definitions
#define TINYKTX_IMPLEMENTATION
#include "../../tinyktx.h"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define TINYGLTF_NOEXCEPTION
#define JSON_NOEXCEPTION
#define TINYGLTF_ENABLE_KTX
// tinyktx.h is already included above,
// so let tiny_gltf.h know do not include tinyktx.h anymore
#define TINYGLTF_NO_INCLUDE_TINY_KTX
#include "../../tiny_gltf.h"
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
//#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#define BUFFER_OFFSET(i) \
(reinterpret_cast<void *>(i)) // TODO(syoyo): Is this right way?
bool loadModel(tinygltf::Model &model, const char *filename) {
tinygltf::TinyGLTF loader;
@@ -39,6 +59,30 @@ bool loadModel(tinygltf::Model &model, const char *filename) {
return res;
}
static bool GetOpenGLFormatFromKTX(int ktx_fmt, GLint *internal_format, GLenum *format, GLenum *type) {
#if defined(TINYGLTF_ENABLE_KTX)
bool ret = true;
std::cout << "fmt = " << ktx_fmt << ", rgb8 fmt = " << TKTX_R8G8B8_UNORM << "\n";
if (ktx_fmt == TKTX_R8G8B8_UNORM) {
(*internal_format) = GL_RGB;
(*format) = GL_RGB;
(*type) = GL_UNSIGNED_BYTE;
} else {
// TODO(syoyo): Support more KTX formats.
ret = false;
}
return ret;
#else
(void)fmt;
(void)internal_format;
(void)internal_format;
return false;
#endif
}
std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
tinygltf::Model &model, tinygltf::Mesh &mesh) {
for (size_t i = 0; i < model.bufferViews.size(); ++i) {
@@ -51,12 +95,12 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
... drawArrays function should be used with a count equal to
the count property of any of the accessors referenced by the
attributes property (they are all equal for a given
primitive).
attributes property (they are all equal for a
given primitive).
*/
}
const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer];
tinygltf::Buffer buffer = model.buffers[bufferView.buffer];
std::cout << "bufferview.target " << bufferView.target << std::endl;
GLuint vbo;
@@ -97,7 +141,7 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
accessor.normalized ? GL_TRUE : GL_FALSE,
byteStride, BUFFER_OFFSET(accessor.byteOffset));
} else
std::cout << "vaa missing: " << attrib.first << std::endl;
std::cout << "Unsupported vertex attribute: " << attrib.first << std::endl;
}
GLuint texid;
@@ -113,29 +157,54 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GLint internal_format = GL_RGBA;
GLenum format = GL_RGBA;
if (image.component == 1) {
format = GL_RED;
} else if (image.component == 2) {
format = GL_RG;
} else if (image.component == 3) {
format = GL_RGB;
} else {
// ???
}
GLenum type = GL_UNSIGNED_BYTE;
if (image.bits == 8) {
// ok
} else if (image.bits == 16) {
type = GL_UNSIGNED_SHORT;
bool valid = false;
// KTX extension
if (image.extras.Has("ktx_format")) {
valid = GetOpenGLFormatFromKTX(image.extras.Get("ktx_format").Get<int>(), &internal_format, &format, &type);
if (valid) {
std::cout << "ktx_format: " << image.extras.Get("ktx_format").Get<int>() << std::endl;
std::cout << "ktx image size: " << image.width << ", " << image.height << std::endl;
}
} else {
// ???
valid = true;
if (image.component == 1) {
format = GL_RED;
} else if (image.component == 2) {
format = GL_RG;
} else if (image.component == 3) {
format = GL_RGB;
} else if (image.component == 4) {
format = GL_RGBA;
} else {
valid = false;
}
if (image.bits == 8) {
type = GL_UNSIGNED_BYTE;
} else if (image.bits == 16) {
type = GL_UNSIGNED_SHORT;
} else {
valid = false;
}
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
format, type, &image.image.at(0));
if (valid) {
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, image.width, image.height, 0,
format, type, &image.image.at(0));
}
}
return vbos;
@@ -314,6 +383,14 @@ static void error_callback(int error, const char *description) {
fprintf(stderr, "Error: %s\n", description);
}
static void drop_callback(GLFWwindow *window, int num, const char **paths) {
(void)window;
printf("dropCB %d\n", num);
for (int i = 0; i < num; i++) {
printf("%s\n", paths[i]);
}
}
int main(int argc, char **argv) {
std::string filename = "../../models/Cube/Cube.gltf";
@@ -325,16 +402,26 @@ int main(int argc, char **argv) {
if (!glfwInit()) return -1;
// Force create OpenGL 3.3
// NOTE(syoyo): Linux + NVIDIA driver segfaults for some reason? commenting out glfwWindowHint will work.
// NOTE(syoyo): For some reason, Linux + NVIDIA driver + apt-installed
// glew(1.13) cannot initialize some ARB functions when CONTEXT_VERSION are
// explicitly given. Proably we need to compile app with recent glfw and
// glew(or use glad) package
#if !defined(__linux__)
// Try to create OpenGL 3.3 context on Windows and macOS
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#endif
// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
Window window = Window(800, 600, "TinyGLTF basic example");
glfwSetDropCallback(window.window, drop_callback);
glfwMakeContextCurrent(window.window);
#ifdef __APPLE__
@@ -342,10 +429,19 @@ int main(int argc, char **argv) {
glewExperimental = GL_TRUE;
#endif
glewInit();
if (glewInit() != GLEW_OK) {
std::cerr << "Failed to initialie glew." << std::endl;
return EXIT_FAILURE;
}
std::cout << glGetString(GL_RENDERER) << ", " << glGetString(GL_VERSION)
<< std::endl;
if (!GLEW_ARB_vertex_array_object) {
std::cerr << "GLEW_ARB_vertex_array_object was not available." << std::endl;
return EXIT_FAILURE;
}
if (!GLEW_VERSION_3_3) {
std::cerr << "OpenGL 3.3 is required to execute this app." << std::endl;
return EXIT_FAILURE;

View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2017 André Müller; foss@andremueller-online.de
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +0,0 @@
#EXTRA_CXXFLAGS := -fsanitize=address -fno-omit-frame-pointer -Wall -Werror -Weverything -Wno-c++11-long-long -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded
EXTRA_CXXFLAGS :=
all:
clang++ -std=c++11 -g -O1 -I../../ -I../common $(EXTRA_CXXFLAGS) -o mesh-conv mesh-conv.cc mesh-util.cc tinygltf_impl.cc

View File

@@ -1,5 +0,0 @@
all:
ninja
clean:
ninja -t clean

View File

@@ -1,58 +0,0 @@
# Mesh modify experiment
Sometimes we want to tweak mesh attributes(e.g. vertex position, uv coord, etc).
glTF itself does not allow ASCII representation of such data so we need to write a converter.
This example show how to
- Export mesh data from .bin to .obj
- Import mesh data to .bin(update corresponding buffer data) from .obj
## Features
* Support skin weights(`JOINTS_N`, `WEIGHTS_N`)
## Supported attributes
* [x] POSITION
* [x] NORMAL
* [x] TANGENT
* [ ] COLOR (vertex color)
* [x] TEXCOORD_N
* Only single texcoord(uv set) is supported
* Specify `--uvset 1` to specify which UV to use.
* [x] WEIGHTS_N, JOINTS_N
## Usage
### Wavefront .obj to glTF
```
$ mesh-modify --op=obj2gltf input.obj
```
All shapes in .obj are concatenated and create single glTF mesh.
(tinyobjloader's skin weight extension `vw` supported)
#### Limitation
Buffer is stored as external file(`.bin`)
### glTF to Wavefront .obj
```
$ mesh-modify --op=gltf2obj input.gltf
```
.obj will be created for each glTF Mesh.
(Skin weight data is exported to .obj using tinyobjloader's skin weight extension `vw`)
#### Limitation
Buffer is stored as external file(`.bin`)
## TODO
* [ ] obj2gltf: Assign glTF material from .mtl
* [ ] modify/patch mesh geometry in glTF
* By using JSON Patch or JSON Merge feature

View File

@@ -1,3 +0,0 @@
rm -rf build
CXX=clang++ CC=clang meson build -Db_sanitize=address -Db_lundef=false --buildtype=debug
cp Makefile.meson build/Makefile

View File

@@ -1,798 +0,0 @@
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <limits>
#include <string>
#include <vector>
#if !defined(__ANDROID__) && !defined(_WIN32)
#include <wordexp.h>
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
#include "../../json.hpp"
#include "../common/clipp.h"
using json = nlohmann::json;
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#ifdef _WIN32
#include "../../tiny_gltf.h"
#else
#include "tiny_gltf.h"
#endif
#include "mesh-util.hh"
#ifdef __clang__
#pragma clang diagnostic ignored "-Wunused-function"
#endif
namespace {
static std::string PrintMode(int mode) {
if (mode == TINYGLTF_MODE_POINTS) {
return "POINTS";
} else if (mode == TINYGLTF_MODE_LINE) {
return "LINE";
} else if (mode == TINYGLTF_MODE_LINE_LOOP) {
return "LINE_LOOP";
} else if (mode == TINYGLTF_MODE_TRIANGLES) {
return "TRIANGLES";
} else if (mode == TINYGLTF_MODE_TRIANGLE_FAN) {
return "TRIANGLE_FAN";
} else if (mode == TINYGLTF_MODE_TRIANGLE_STRIP) {
return "TRIANGLE_STRIP";
}
return "**UNKNOWN**";
}
static std::string PrintTarget(int target) {
if (target == 34962) {
return "GL_ARRAY_BUFFER";
} else if (target == 34963) {
return "GL_ELEMENT_ARRAY_BUFFER";
} else {
return "**UNKNOWN**";
}
}
static std::string PrintType(int ty) {
if (ty == TINYGLTF_TYPE_SCALAR) {
return "SCALAR";
} else if (ty == TINYGLTF_TYPE_VECTOR) {
return "VECTOR";
} else if (ty == TINYGLTF_TYPE_VEC2) {
return "VEC2";
} else if (ty == TINYGLTF_TYPE_VEC3) {
return "VEC3";
} else if (ty == TINYGLTF_TYPE_VEC4) {
return "VEC4";
} else if (ty == TINYGLTF_TYPE_MATRIX) {
return "MATRIX";
} else if (ty == TINYGLTF_TYPE_MAT2) {
return "MAT2";
} else if (ty == TINYGLTF_TYPE_MAT3) {
return "MAT3";
} else if (ty == TINYGLTF_TYPE_MAT4) {
return "MAT4";
}
return "**UNKNOWN**";
}
static std::string PrintComponentType(int ty) {
if (ty == TINYGLTF_COMPONENT_TYPE_BYTE) {
return "BYTE";
} else if (ty == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
return "UNSIGNED_BYTE";
} else if (ty == TINYGLTF_COMPONENT_TYPE_SHORT) {
return "SHORT";
} else if (ty == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
return "UNSIGNED_SHORT";
} else if (ty == TINYGLTF_COMPONENT_TYPE_INT) {
return "INT";
} else if (ty == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
return "UNSIGNED_INT";
} else if (ty == TINYGLTF_COMPONENT_TYPE_FLOAT) {
return "FLOAT";
} else if (ty == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
return "DOUBLE";
}
return "**UNKNOWN**";
}
#if 0
// TODO(syoyo): Support sparse accessor(sparse vertex attribute).
// TODO(syoyo): Support more data type
struct VertexAttrib {
std::string name;
// Value are converted to float type.
std::vector<float> data;
// Corresponding info in binary data
int data_type; // e.g. TINYGLTF_TYPE_VEC2
int component_type; // storage type. e.g.
// TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
uint64_t buffer_offset{0};
size_t buffer_length{0};
};
struct MeshPrim {
std::string name;
int32_t id{-1};
int mode{TINYGLTF_MODE_TRIANGLES};
VertexAttrib position; // vec3
VertexAttrib normal; // vec3
VertexAttrib tangent; // vec4
VertexAttrib color; // vec3 or vec4
std::map<int, VertexAttrib> texcoords; // <slot, attrib> vec2
std::map<int, VertexAttrib> weights; // <slot, attrib>
std::map<int, VertexAttrib>
joints; // <slot, attrib> store data as float type
int indices_type{-1}; // storage type(componentType) of `indices`.
std::vector<uint32_t> indices; // vertex indices
};
#endif
static std::string GetFilePathExtension(const std::string &FileName) {
if (FileName.find_last_of(".") != std::string::npos)
return FileName.substr(FileName.find_last_of(".") + 1);
return "";
}
static size_t ComponentTypeByteSize(int type) {
switch (type) {
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
case TINYGLTF_COMPONENT_TYPE_BYTE:
return sizeof(char);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
case TINYGLTF_COMPONENT_TYPE_SHORT:
return sizeof(short);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
case TINYGLTF_COMPONENT_TYPE_INT:
return sizeof(int);
case TINYGLTF_COMPONENT_TYPE_FLOAT:
return sizeof(float);
case TINYGLTF_COMPONENT_TYPE_DOUBLE:
return sizeof(double);
default:
return 0;
}
}
std::vector<uint8_t> LoadBin(const std::string &filename) {
std::vector<uint8_t> data;
std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate);
if (is.is_open()) {
size_t size = size_t(is.tellg());
is.seekg(0, std::ios::beg);
if (size < 4) {
std::cerr << "File size is zero or too short: " << size << "\n";
return data;
}
data.resize(size);
is.read(reinterpret_cast<char *>(data.data()), std::streamsize(size));
}
return data;
}
// TODO(syoyo): Use C++17 like filesystem library
bool FileExists(const std::string &abs_filename) {
bool ret;
#ifdef _WIN32
// TODO(syoyo): Support utf8 filepath
FILE *fp = nullptr;
errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
if (err != 0) {
return false;
}
#else
FILE *fp = fopen(abs_filename.c_str(), "rb");
#endif
if (fp) {
ret = true;
fclose(fp);
} else {
ret = false;
}
return ret;
}
static std::string JoinPath(const std::string &path0,
const std::string &path1) {
if (path0.empty()) {
return path1;
} else {
// check '/'
char lastChar = *path0.rbegin();
if (lastChar != '/') {
return path0 + std::string("/") + path1;
} else {
return path0 + path1;
}
}
}
std::string ExpandFilePath(const std::string &filepath) {
#ifdef _WIN32
DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
char *str = new char[len];
ExpandEnvironmentStringsA(filepath.c_str(), str, len);
std::string s(str);
delete[] str;
return s;
#else
#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
defined(__ANDROID__) || defined(__EMSCRIPTEN__)
// no expansion
std::string s = filepath;
#else
std::string s;
wordexp_t p;
if (filepath.empty()) {
return "";
}
// Quote the string to keep any spaces in filepath intact.
std::string quoted_path = "\"" + filepath + "\"";
// char** w;
int ret = wordexp(quoted_path.c_str(), &p, 0);
if (ret) {
// err
s = filepath;
return s;
}
// Use first element only.
if (p.we_wordv) {
s = std::string(p.we_wordv[0]);
wordfree(&p);
} else {
s = filepath;
}
#endif
return s;
#endif
}
static std::string FindFile(const std::vector<std::string> &paths,
const std::string &filepath) {
for (size_t i = 0; i < paths.size(); i++) {
std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
if (FileExists(absPath)) {
return absPath;
}
}
return std::string();
}
static std::string GetBaseDir(const std::string &filepath) {
if (filepath.find_last_of("/\\") != std::string::npos)
return filepath.substr(0, filepath.find_last_of("/\\"));
return "";
}
static int GetSlotId(const std::string &name) {
if (name.rfind("TEXCOORD_", 0) == 0) {
int id = 0;
sscanf(name.c_str(), "TEXCOORD_%d", &id);
return id;
} else if (name.rfind("WEIGHTS_", 0) == 0) {
int id = 0;
sscanf(name.c_str(), "WEIGHTS_%d", &id);
return id;
} else if (name.rfind("JOINTS_", 0) == 0) {
int id = 0;
sscanf(name.c_str(), "JOINTS_%d", &id);
return id;
}
return -1;
}
static bool IsAttributeSupported(const std::string &name) {
constexpr int max_slots = 8;
if (name.compare("POSITION") == 0) {
return true;
}
if (name.compare("NORMAL") == 0) {
return true;
}
if (name.compare("TANGENT") == 0) {
return true;
}
for (int i = 0; i < max_slots; i++) {
std::string n = "TEXCOORD_" + std::to_string(i);
if (n.compare(name) == 0) {
return true;
}
}
for (int i = 0; i < max_slots; i++) {
std::string n = "WEIGHTS_" + std::to_string(i);
if (n.compare(name) == 0) {
return true;
}
}
for (int i = 0; i < max_slots; i++) {
std::string n = "JOINTS_" + std::to_string(i);
if (n.compare(name) == 0) {
return true;
}
}
return false;
}
static float Unpack(const unsigned char *ptr, int type) {
if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
unsigned char data = *ptr;
return float(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_BYTE) {
char data = static_cast<char>(*ptr);
return float(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
uint16_t data = *reinterpret_cast<const uint16_t *>(ptr);
return float(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_SHORT) {
int16_t data = *reinterpret_cast<const int16_t *>(ptr);
return float(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_FLOAT) {
float data = *reinterpret_cast<const float *>(ptr);
return data;
} else {
std::cerr << "???: Unsupported type: " << PrintComponentType(type) << "\n";
return 0.0f;
}
}
static uint32_t UnpackIndex(const unsigned char *ptr, int type) {
if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
unsigned char data = *ptr;
return uint32_t(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_BYTE) {
char data = static_cast<char>(*ptr);
return uint32_t(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
uint16_t data = *reinterpret_cast<const uint16_t *>(ptr);
return uint32_t(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_SHORT) {
int16_t data = *reinterpret_cast<const int16_t *>(ptr);
return uint32_t(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_INT) {
// TODO(syoyo): Check overflow(2G+ index)
int32_t data = *reinterpret_cast<const int32_t *>(ptr);
return uint32_t(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
uint32_t data = *reinterpret_cast<const uint32_t *>(ptr);
return uint32_t(data);
} else if (type == TINYGLTF_COMPONENT_TYPE_SHORT) {
uint32_t data = *reinterpret_cast<const uint32_t *>(ptr);
return data;
} else {
std::cerr << "???: Unsupported type: " << PrintComponentType(type) << "\n";
return static_cast<uint32_t>(-1);
}
}
static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
bool verbose, example::MeshPrim *out) {
out->prims.clear();
for (size_t i = 0; i < mesh.primitives.size(); i++) {
const tinygltf::Primitive &primitive = mesh.primitives[i];
if (primitive.indices < 0) {
std::cerr << "Primitive indices must be provided\n";
return false;
}
example::PrimSet out_prim;
// indices.
{
const tinygltf::Accessor &indexAccessor =
model.accessors[size_t(primitive.indices)];
size_t num_elements = indexAccessor.count;
std::cout << "index.elements = " << num_elements << "\n";
size_t byte_stride = ComponentTypeByteSize(indexAccessor.componentType);
const tinygltf::BufferView &indexBufferView =
model.bufferViews[size_t(indexAccessor.bufferView)];
// should be 34963(ELEMENT_ARRAY_BUFFER)
std::cout << "index.target = " << PrintTarget(indexBufferView.target)
<< "\n";
if (indexBufferView.target != TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
std::cerr << "indexBufferView.target must be ELEMENT_ARRAY_BUFFER\n";
return false;
}
const tinygltf::Buffer &indexBuffer =
model.buffers[size_t(indexBufferView.buffer)];
std::vector<uint32_t> indices;
for (size_t k = 0; k < num_elements; k++) {
// TODO(syoyo): out-of-bounds check.
const unsigned char *ptr = indexBuffer.data.data() +
indexBufferView.byteOffset +
(k * byte_stride) + indexAccessor.byteOffset;
uint32_t idx = UnpackIndex(ptr, indexAccessor.componentType);
if (verbose) {
std::cout << "vertex_index[" << k << "] = " << idx << "\n";
}
indices.push_back(idx);
}
out_prim.indices = indices;
out_prim.indices_type = indexAccessor.componentType;
}
// attributes
{
std::map<std::string, int>::const_iterator it(
primitive.attributes.begin());
std::map<std::string, int>::const_iterator itEnd(
primitive.attributes.end());
for (; it != itEnd; it++) {
// it->first would be "POSITION", "NORMAL", "TEXCOORD_0", ...
if (!IsAttributeSupported(it->first)) {
std::cout << "Unsupported attribute: " << it->first << "\n";
continue;
}
if (size_t(it->second) >= model.accessors.size()) {
std::cerr << "Invalid accessor id: " << it->second << "\n";
return false;
}
const tinygltf::Accessor &accessor =
model.accessors[size_t(it->second)];
size_t elem_size = 1;
if (accessor.type == TINYGLTF_TYPE_SCALAR) {
elem_size = 1;
} else if (accessor.type == TINYGLTF_TYPE_VEC2) {
elem_size = 2;
} else if (accessor.type == TINYGLTF_TYPE_VEC3) {
elem_size = 3;
} else if (accessor.type == TINYGLTF_TYPE_VEC4) {
elem_size = 4;
} else {
std::cerr << "Invalid or unsupported accessor type: "
<< PrintType(accessor.type) << "\n";
return false;
}
std::cout << PrintComponentType(accessor.componentType) << "\n";
size_t byte_stride = ComponentTypeByteSize(accessor.componentType);
std::cout << "attribute: " << it->first << "\n";
// if (gGLProgramState.attribs[it->first] >= 0) {
// Compute byteStride from Accessor + BufferView combination.
int byteStride =
accessor.ByteStride(model.bufferViews[size_t(accessor.bufferView)]);
assert(byteStride != -1);
std::cout << "byteOffset: " << accessor.byteOffset << "\n";
const tinygltf::BufferView &bufferView =
model.bufferViews[size_t(accessor.bufferView)];
const tinygltf::Buffer &buffer =
model.buffers[size_t(bufferView.buffer)];
size_t num_elems = accessor.count * elem_size;
example::VertexAttrib attrib;
for (size_t k = 0; k < num_elems; k++) {
// TODO(syoyo): out-of-bounds check.
const unsigned char *ptr = buffer.data.data() +
bufferView.byteOffset + (k * byte_stride) +
accessor.byteOffset;
float value = Unpack(ptr, accessor.componentType);
if (verbose) {
std::cout << "[" << k << "] value = " << value << "\n";
}
attrib.data.push_back(value);
}
attrib.component_type = accessor.componentType;
attrib.data_type = accessor.type;
attrib.name = it->first;
if (attrib.name.compare("POSITION") == 0) {
out_prim.position = attrib;
} else if (attrib.name.compare("NORMAL") == 0) {
out_prim.normal = attrib;
} else if (attrib.name.compare("TANGENT") == 0) {
out_prim.tangent = attrib;
} else if (attrib.name.rfind("TEXCOORD_", 0) == 0) {
int id = GetSlotId(attrib.name);
std::cout << "texcoord[" << id << "]\n";
out_prim.texcoords[id] = attrib;
} else if (attrib.name.rfind("JOINTS_", 0) == 0) {
int id = GetSlotId(attrib.name);
std::cout << "joints[" << id << "]\n";
out_prim.joints[id] = attrib;
} else if (attrib.name.rfind("WEIGHTS_", 0) == 0) {
int id = GetSlotId(attrib.name);
std::cout << "weights[" << id << "]\n";
out_prim.weights[id] = attrib;
} else {
std::cerr << "???: attrib.name = " << attrib.name << "\n";
return false;
}
}
}
const tinygltf::Accessor &indexAccessor =
model.accessors[size_t(primitive.indices)];
(void)indexAccessor;
PrintMode(primitive.mode);
if (primitive.mode != TINYGLTF_MODE_TRIANGLES) {
std::cerr << "Supported Primitive mode is TRIANGLES only at the moment\n";
return false;
}
out_prim.mode = primitive.mode;
out->prims.push_back(out_prim);
}
out->name = mesh.name;
return true;
}
static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
bool verbose, std::vector<example::MeshPrim> *outs) {
// Get .bin data
{
if (model.buffers.size() != 1) {
std::cerr << "Single element of `buffers` is supported at the moment.\n";
return false;
}
const tinygltf::Buffer &buffer = model.buffers[0];
if (buffer.uri.empty()) {
std::cerr << "buffer.uri must be a filepath.\n";
return false;
}
if (buffer.data.size() < 4) {
std::cerr << "Invalid buffer.byteLength.\n";
return false;
}
std::vector<std::string> search_paths;
search_paths.push_back(asset_path);
std::string abs_filepath = FindFile(search_paths, buffer.uri);
std::vector<uint8_t> bin = LoadBin(abs_filepath);
if (bin.size() != buffer.data.size()) {
std::cerr << "Byte size mismatch. Failed to load file: " << buffer.uri
<< "\n";
std::cerr << " Searched absolute file path: " << abs_filepath << "\n";
std::cerr << " .bin size = " << bin.size()
<< ", size in 'buffer.uri' = " << buffer.data.size() << "\n";
return false;
}
}
for (const auto &mesh : model.meshes) {
std::cout << "mesh.name: " << mesh.name << "\n";
example::MeshPrim output;
bool ret = DumpMesh(model, mesh, verbose, &output);
if (!ret) {
return false;
}
outs->push_back(output);
}
return true;
}
} // namespace
int main(int argc, char **argv) {
std::string op;
std::string input_filename;
std::string output_filename = "output.gltf";
int uvset = 0;
bool verbose = false;
bool export_skinweight = true;
bool no_flip_texcoord_y = false;
auto cli =
(clipp::required("-i", "--input") &
clipp::value("input filename", input_filename),
clipp::option("-o", "--outout") &
clipp::value("Output filename(obj2fltf)", output_filename),
clipp::option("-v", "--verbose").set(verbose).doc("Verbose output"),
clipp::option("--export_skinweight") &
clipp::value("Export skin weights(gltf2obj). default true.",
export_skinweight),
clipp::option("--uvset").set(uvset).doc("UV set(TEXCOORD_N) to use"),
clipp::option("--op") &
clipp::value("operation mode(`gltf2obj`, `obj2gltf`", op),
clipp::option("--no-flip-texcoord-y")
.set(no_flip_texcoord_y)
.doc("Do not flip texcoord Y"));
if (!clipp::parse(argc, argv, cli)) {
std::cout << clipp::make_man_page(cli, argv[0]);
return EXIT_FAILURE;
}
if (op == "gltf2obj") {
tinygltf::Model model;
tinygltf::TinyGLTF loader;
std::string err;
std::string warn;
std::string ext = GetFilePathExtension(input_filename);
{
bool ret = false;
if (ext.compare("glb") == 0) {
// assume binary glTF.
ret = loader.LoadBinaryFromFile(&model, &err, &warn,
input_filename.c_str());
} else {
// assume ascii glTF.
ret = loader.LoadASCIIFromFile(&model, &err, &warn,
input_filename.c_str());
}
if (!warn.empty()) {
printf("Warn: %s\n", warn.c_str());
}
if (!err.empty()) {
printf("ERR: %s\n", err.c_str());
}
if (!ret) {
printf("Failed to load .glTF : %s\n", argv[1]);
exit(-1);
}
}
#if 0
json j;
{
std::ifstream i(input_filename);
i >> j;
}
std::cout << "j = " << j << "\n";
json j_patch = R"([
{ "op": "add", "path": "/buffers/-", "value": {
"name": "plane/data",
"byteLength": 480,
"uri": "plane1.bin"
} }
])"_json;
// a JSON value
json j_original = R"({
"baz": ["one", "two", "three"],
"foo": "bar"
})"_json;
//json j_patch = R"([
// { "op": "remove", "path": "/buffers" }
//])"_json;
std::cout << "patch = " << j_patch.dump(2) << "\n";
json j_ret = j.patch(j_patch);
std::cout << "patched = " << j_ret.dump(2) << "\n";
#endif
std::string basedir = GetBaseDir(input_filename);
std::vector<example::MeshPrim> meshes;
bool ret = ExtractMesh(basedir, model, verbose, &meshes);
size_t n = 0;
for (const auto &mesh : meshes) {
// Assume no duplicated name in .glTF data
std::string basename;
if (mesh.name.empty()) {
basename = "untitled-" + std::to_string(n);
} else {
basename = mesh.name;
}
for (size_t primid = 0; primid < mesh.prims.size(); primid++) {
example::ObjExportOption options;
options.primid = int(primid);
options.export_skinweights = export_skinweight;
options.uvset = uvset;
options.flip_texcoord_y = !no_flip_texcoord_y;
bool ok = example::SaveAsObjMesh(basename, mesh, options);
if (!ok) {
std::cout << "Failed to export mesh[" << mesh.name << "].primitives["
<< primid << "]\n";
// may ok;
}
}
n++;
}
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
} else if (op == "obj2gltf") {
// Require facevarying layout?
// facevarying representation is required if a vertex can have multiple
// normal/uv value. drawback of facevarying is mesh data increases. false =
// try to keep shared vertex representation as much as possible. true =
// reorder vertex data and re-assign vertex indices for facevarying data
// layout.
bool facevarying = false;
example::MeshPrim mesh;
bool ok = example::LoadObjMesh(input_filename, facevarying, &mesh);
if (!ok) {
return EXIT_FAILURE;
}
if (verbose) {
PrintMeshPrim(mesh);
}
ok = example::SaveAsGLTFMesh(output_filename, mesh);
if (!ok) {
std::cerr << "Failed to save mesh as glTF\n";
return EXIT_FAILURE;
}
std::cout << "Write glTF: " << output_filename << "\n";
return EXIT_SUCCESS;
} else {
std::cerr << "Unknown operation: " << op << "\n";
return EXIT_FAILURE;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,95 +0,0 @@
#pragma once
#include <vector>
#include <map>
namespace example {
// TODO(syoyo): Support sparse accessor(sparse vertex attribute).
// TODO(syoyo): Support more data type
struct VertexAttrib {
std::string name;
// Value are converted to float type.
std::vector<float> data;
// Corresponding info in binary data
int data_type; // e.g. TINYGLTF_TYPE_VEC2
int component_type; // storage type. e.g.
// TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
uint64_t buffer_offset{0};
size_t buffer_length{0};
// Optional.
std::vector<double> minValues;
std::vector<double> maxValues;
size_t numElements() const;
};
struct PrimSet {
int mode; // e.g. TRIANGLES
VertexAttrib position; // vec3
VertexAttrib normal; // vec3
VertexAttrib tangent; // vec4
VertexAttrib color; // vec3 or vec4
std::map<int, VertexAttrib> texcoords; // <slot, attrib> vec2
std::map<int, VertexAttrib> weights; // <slot, attrib> vec4
std::map<int, VertexAttrib>
joints; // <slot, attrib> store data as float type
// min/max of index value. -1 = undef
int indices_min{-1};
int indices_max{-1};
int indices_type{-1}; // storage type(componentType) of `indices`.
std::vector<uint32_t> indices; // vertex indices
};
struct MeshPrim {
std::string name;
int32_t id{-1};
std::vector<PrimSet> prims;
};
struct ObjExportOption
{
bool export_skinweights{true};
int primid{0}; /// Primitive id to export(default 0).
int uvset{0}; /// Tex coord ID to export(default 0).
bool flip_texcoord_y{true}; /// Flip texture coordinate V?(default true).
};
///
/// Save MeshPrim as wavefront .obj
///
/// @param[in] basename Base filename. ".obj" will be appended.
/// @param[in] mesh MeshPrim.
/// @param[in] option Export options
///
bool SaveAsObjMesh(const std::string &basename, const MeshPrim &mesh, const ObjExportOption &option);
//
/// Save MeshPrim as glTF mesh
///
bool SaveAsGLTFMesh(const std::string &filename, const MeshPrim &mesh);
///
/// Loads .obj and convert to MeshPrim
///
/// @param[in] facevarying Construct mesh with facevarying vertex layout(default false)
///
bool LoadObjMesh(const std::string &filename, bool facevarying, MeshPrim *mesh);
///
/// Print MeshPrim datra
///
void PrintMeshPrim(const MeshPrim &mesh);
} // namespace example

View File

@@ -1,7 +0,0 @@
project('mesh-conv', 'cpp', default_options : ['cpp_std=c++11'])
thread_dep = dependency('threads')
incdir = include_directories(['../../', '../common'])
executable('mesh-conv', ['mesh-conv.cc', 'mesh-util.cc', 'tinygltf_impl.cc'], include_directories : incdir, dependencies : thread_dep)

View File

@@ -1,27 +0,0 @@
# concat_mesh.py
Append(merge) mesh of glTF A to glTF B.
`meshes`, `accessors`, `bufferViews`, `materials` of glTF A is appended to glTF B(index to accessor, bufferViews, etc will be recomputed).
`skin`, `nodes`, etc are not appended(to be merged).
## Usage
concatenate sourceN.gltf to target.gltf and save it to merged.gltf
```
$ python concat_mesh.py source0.gltf <source1.gltf source2.gltf ...> target.gltf merged.gltf
```
## TODO
* [x] Support multiple glTFs to merge(concat)
* [ ] Support merging skin
* [ ] Support merging different node hierarchies
* [x] Support `images`, `textures`, `materials`, `samplers`
* [ ] Support other glTF info
# replace_attrib.py
Replace the accessor id of specified attribute.

View File

@@ -1,272 +0,0 @@
# concat mesh to glTF
# support multiple source glTF to me concatenated to target.gltf
# TODO: skins, nodes, scenes
import json
import sys, os
g_prefix = "added/"
def adjustMaterialTextureIndex(mat, texture_index_offset):
# Args: mat(dict)
# texture_index_offset(int) Index offset
if "normalTexture" in mat:
if "index" in mat["normalTexture"]:
mat["normalTexture"]["index"] += texture_index_offset
if "occlusionTexture" in mat:
if "index" in mat["occlusionTexture"]:
mat["occlusionTexture"]["index"] += texture_index_offset
if "emissiveTexture" in mat:
if "index" in mat["emissiveTexture"]:
mat["emissiveTexture"]["index"] += texture_index_offset
if "pbrMetallicRoughness" in mat:
if "baseColorTexture" in mat["pbrMetallicRoughness"]:
if "index" in mat["pbrMetallicRoughness"]["baseColorTexture"]:
mat["pbrMetallicRoughness"]["baseColorTexture"]["index"] += texture_index_offset
if "metallicRoughnessTexture" in mat["pbrMetallicRoughness"]:
if "index" in mat["pbrMetallicRoughness"]["metallicRoughnessTexture"]:
mat["pbrMetallicRoughness"]["metallicRoughnessTexture"]["index"] += texture_index_offset
return mat
def concat(source, target, offsets, prefix, extraInfo):
# Args:
# source: dict(modified), target: dict(modified),
# offsets: dict(modified). contains offset table.
# prefix: str
# extraInfo: dict(inout)
num_source_meshes = len(source["meshes"])
num_source_buffers = len(source["buffers"])
num_source_bufferViews = len(source["bufferViews"])
num_source_accessors = len(source["accessors"])
num_source_materials = len(source["materials"]) if "materials" in source else 0
num_source_samplers = len(source["samplers"]) if "samplers" in source else 0
num_source_images = len(source["images"]) if "images" in source else 0
num_source_textures = len(source["textures"]) if "textures" in source else 0
print("src =====")
print("num_src_meshes: ", num_source_meshes)
print("num_src_buffers: ", num_source_buffers)
print("num_src_bufferViews: ", num_source_bufferViews)
print("num_src_accessors: ", num_source_accessors)
print("num_src_materials: ", num_source_materials)
print("num_src_textures: ", num_source_textures)
print("num_src_images: ", num_source_images)
print("num_src_samplers: ", num_source_samplers)
#
# Modify name and adjust index offset
#
for i in range(len(source["buffers"])):
if "name" in source["buffers"][i]:
source["buffers"][i]["name"] = prefix + source["buffers"][i]["name"]
for i in range(len(source["bufferViews"])):
if "name" in source["bufferViews"][i]:
source["bufferViews"][i]["name"] = prefix + source["bufferViews"][i]["name"]
source["bufferViews"][i]["buffer"] += offsets["buffers"]
for i in range(len(source["accessors"])):
if "name" in source["accessors"][i]:
source["accessors"][i]["name"] = prefix + source["accessors"][i]["name"]
source["accessors"][i]["bufferView"] += offsets["bufferViews"]
for i in range(len(source["meshes"])):
mesh = source["meshes"][i]
if "name" in mesh:
source["meshes"][i]["name"] = prefix + source["meshes"][i]["name"]
for primid in range(len(mesh["primitives"])):
for attrib in mesh["primitives"][primid]["attributes"]:
#print(source["meshes"][i]["primitives"][primid]["attributes"][attrib])
source["meshes"][i]["primitives"][primid]["attributes"][attrib] += offsets["accessors"]
source["meshes"][i]["primitives"][primid]["indices"] += offsets["accessors"]
if "material" in source["meshes"][i]["primitives"][primid]:
source["meshes"][i]["primitives"][primid]["material"] += offsets["materials"]
if "materials" in source:
for i in range(len(source["materials"])):
if "name" in source["materials"][i]:
source["materials"][i]["name"] = prefix + source["materials"][i]["name"]
source["materials"][i] = adjustMaterialTextureIndex(source["materials"][i], offsets["textures"])
if "images" in source:
for i in range(len(source["images"])):
if "name" in source["images"][i]:
source["images"][i]["name"] = prefix + source["images"][i]["name"]
if "samplers" in source:
for i in range(len(source["samplers"])):
if "name" in source["samplers"][i]:
source["samplers"][i]["name"] = prefix + source["samplers"][i]["name"]
if "textures" in source:
for i in range(len(source["textures"])):
if "name" in source["textures"][i]:
source["textures"][i]["name"] = prefix + source["textures"][i]["name"]
source["textures"][i]["sampler"] += offsets["samplers"]
source["textures"][i]["source"] += offsets["images"]
#
# Append mesh info
#
target["buffers"] += source["buffers"]
target["bufferViews"] += source["bufferViews"]
target["meshes"] += source["meshes"]
target["accessors"] += source["accessors"]
if "materials" in source:
if "materials" in target:
target["materials"] += source["materials"]
else:
target["materials"] = source["materials"]
if "images" in source:
if "images" in target:
target["images"] += source["images"]
else:
target["images"] = source["images"]
if "textures" in source:
if "textures" in target:
target["textures"] += source["textures"]
else:
target["textures"] = source["textures"]
if "samplers" in source:
if "samplers" in target:
target["samplers"] += source["samplers"]
else:
target["samplers"] = source["samplers"]
# assume `prefix` is unique
extraInfo["num_{}_meshes".format(prefix)] = num_source_meshes
extraInfo["num_{}_buffers".format(prefix)] = num_source_buffers
extraInfo["num_{}_bufferViews".format(prefix)] = num_source_bufferViews
extraInfo["num_{}_accessors".format(prefix)] = num_source_accessors
extraInfo["num_{}_materials".format(prefix)] = num_source_materials
extraInfo["num_{}_images".format(prefix)] = num_source_images
extraInfo["num_{}_samplers".format(prefix)] = num_source_samplers
extraInfo["num_{}_textures".format(prefix)] = num_source_textures
# update offsets
offsets["meshes"] += num_source_meshes
offsets["buffers"] += num_source_buffers
offsets["bufferViews"] += num_source_bufferViews
offsets["accessors"] += num_source_accessors
offsets["materials"] += num_source_materials
offsets["textures"] += num_source_textures
offsets["images"] += num_source_images
offsets["samplers"] += num_source_samplers
def main():
if len(sys.argv) < 4:
print("Needs source0.gltf <source1.gltf, ...> target.gltf output.gltf")
sys.exit(-1)
num_args = len(sys.argv)
print("num_args = ", num_args)
source_filenames = []
num_srcs = num_args - 3
for i in range(num_srcs):
source_filenames.append(sys.argv[1+i])
print("source[{}] = {}".format(i, sys.argv[1+i]))
target_filename = sys.argv[num_args - 2]
output_filename = sys.argv[num_args - 1]
print("target = ", target_filename)
print("output = ", output_filename)
sources = []
for i in range(num_srcs):
sources.append(json.loads(open(source_filenames[i]).read()))
target = json.loads(open(target_filename).read())
num_target_meshes = len(target["meshes"])
num_target_buffers = len(target["buffers"])
num_target_bufferViews = len(target["bufferViews"])
num_target_accessors = len(target["accessors"])
num_target_materials = 0
if "materials" in target:
num_target_materials = len(target["materials"])
num_target_images = 0
if "images" in target:
num_target_images = len(target["images"])
num_target_samplers = 0
if "samplers" in target:
num_target_samplers = len(target["samplers"])
num_target_textures = 0
if "textures" in target:
num_target_textures = len(target["textures"])
print("num_target_meshes: ", num_target_meshes)
print("num_target_buffers: ", num_target_buffers)
print("num_target_bufferViews: ", num_target_bufferViews)
print("num_target_accessors: ", num_target_accessors)
print("num_target_materials: ", num_target_materials)
print("num_target_textures: ", num_target_textures)
print("num_target_images: ", num_target_images)
print("num_target_samplers: ", num_target_samplers)
#
# add some info to asset.extras
#
extraInfo = {}
extraInfo["num_target_meshes"] = num_target_meshes
extraInfo["num_target_buffers"] = num_target_buffers
extraInfo["num_target_bufferViews"] = num_target_bufferViews
extraInfo["num_target_accessors"] = num_target_accessors
extraInfo["num_target_materials"] = num_target_materials
extraInfo["num_target_textures"] = num_target_textures
extraInfo["num_target_images"] = num_target_images
extraInfo["num_target_samplers"] = num_target_samplers
offsets = {}
offsets["meshes"] = num_target_meshes
offsets["buffers"] = num_target_buffers
offsets["bufferViews"] = num_target_bufferViews
offsets["accessors"] = num_target_accessors
offsets["materials"] = num_target_materials
offsets["textures"] = num_target_textures
offsets["samplers"] = num_target_samplers
offsets["images"] = num_target_images
for i in range(num_srcs):
source = sources[i]
prefix = g_prefix + "{}/".format(source_filenames[i])
concat(source, target, offsets, prefix, extraInfo)
extraInfo["num_total_meshes"] = offsets["meshes"]
extraInfo["num_total_buffers"] = offsets["buffers"]
extraInfo["num_total_bufferViews"] = offsets["bufferViews"]
extraInfo["num_total_accessors"] = offsets["accessors"]
extraInfo["num_total_materials"] = offsets["materials"]
extraInfo["num_total_textures"] = offsets["textures"]
extraInfo["num_total_images"] = offsets["images"]
extraInfo["num_total_samplers"] = offsets["samplers"]
target["asset"]["extras"] = extraInfo
with open(output_filename, "w") as f:
f.write(json.dumps(target, indent=2))
print("Merged glTF was exported to : ", output_filename)
main()

View File

@@ -1,107 +0,0 @@
# Replace accessor id of attributes for speicified mesh
# Usually called after concat_mesh.py
# Example usecase is to replace UV coordinate of a mesh.
import json
import sys, os
replace_indices = False
attrib_names = ["TEXCOORD_0"]
def check_accessor(src, target):
if src["componentType"] != target["componentType"]:
print("componentType mismatch!")
return False
if src["count"] != target["count"]:
print("`count` mismatch!")
return False
if src["type"] != target["type"]:
print("`type` mismatch!")
return False
return True
def main():
if len(sys.argv) < 5:
print("Needs input.gltf output.gltf source_mesh_name target_mesh_name <source_primid> <target_primid>")
sys.exit(-1)
input_filename = sys.argv[1]
output_filename = sys.argv[2]
source_mesh_name = sys.argv[3]
target_mesh_name = sys.argv[4]
source_primid = 0
target_primid = 0
if len(sys.argv) > 5:
source_primid = int(sys.argv[5])
if len(sys.argv) > 6:
target_primid = int(sys.argv[6])
gltf = json.loads(open(input_filename).read())
source_mesh_id = -1
target_mesh_id = -1
for i in range(len(gltf["meshes"])):
mesh = gltf["meshes"][i]
print("mesh[{}].name = {}".format(i, mesh["name"]))
if target_mesh_name == mesh["name"]:
target_mesh_id = i
if source_mesh_name == mesh["name"]:
source_mesh_id = i
if source_mesh_id == -1:
print("source mesh with name [{}] not found.".format(source_mesh_name))
sys.exit(-1)
if target_mesh_id == -1:
print("target mesh with name [{}] not found.".format(target_mesh_name))
sys.exit(-1)
print("target: name = {}, id = {}".format(target_mesh_name, target_mesh_id))
print("source: name = {}, id = {}".format(source_mesh_name, source_mesh_id))
source_mesh = gltf["meshes"][source_mesh_id]
target_mesh = gltf["meshes"][target_mesh_id]
source_prim = source_mesh["primitives"][source_primid]
target_prim = target_mesh["primitives"][target_primid]
if replace_indices:
target_indices_id = target_prim["indices"]
src_indices_id = source_prim["indices"]
print("Replace vertex indices from {} to {}".format(target_indices_id, src_indices_id))
gltf["meshes"][target_mesh_id]["primitives"][target_primid]["indices"] = gltf["meshes"][source_mesh_id]["primitives"][source_primid]["indices"]
for attrib in target_prim["attributes"]:
print("attrib ", attrib)
if attrib in attrib_names:
if attrib in source_prim["attributes"]:
target_accessor_id = target_prim["attributes"][attrib]
src_accessor_id = source_prim["attributes"][attrib]
if check_accessor(gltf["accessors"][src_accessor_id], gltf["accessors"][target_accessor_id]):
gltf["meshes"][target_mesh_id]["primitives"][target_primid]["attributes"][attrib] = src_accessor_id
print("Replaced accessor id for attrib {} from {} to {}".format(attrib, target_accessor_id, src_accessor_id))
else:
print("Accessor type/format is not identical. Skip replace")
print(" attrib {}".format(attrib))
else:
print("attribute[{}] not found in source primitive: mesh[{}].primitives[{}]".format(attrib, source_mesh_name, source_primid))
with open(output_filename, "w") as f:
f.write(json.dumps(gltf, indent=2))
print("Merged glTF was exported to : ", output_filename)
main()

View File

@@ -1,9 +0,0 @@
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#ifdef _WIN32
#include "../../tiny_gltf.h"
#else
#include "tiny_gltf.h"
#endif

View File

@@ -150,7 +150,6 @@ bool LoadObj(const std::string &filename, float scale,
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn;
std::string err;
std::string basedir = GetBaseDir(filename) + "/";
@@ -159,16 +158,12 @@ bool LoadObj(const std::string &filename, float scale,
// auto t_start = std::chrono::system_clock::now();
bool ret =
tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename.c_str(),
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename.c_str(),
basepath, /* triangulate */ true);
// auto t_end = std::chrono::system_clock::now();
// std::chrono::duration<double, std::milli> ms = t_end - t_start;
if (!warn.empty()) {
std::cout << warn << std::endl;
}
if (!err.empty()) {
std::cerr << err << std::endl;
}

View File

@@ -175,10 +175,7 @@ static std::string PrintIntArray(const std::vector<int> &arr) {
std::stringstream ss;
ss << "[ ";
for (size_t i = 0; i < arr.size(); i++) {
ss << arr[i];
if (i != arr.size() - 1) {
ss << ", ";
}
ss << arr[i] << ((i != arr.size() - 1) ? ", " : "");
}
ss << " ]";
@@ -193,10 +190,7 @@ static std::string PrintFloatArray(const std::vector<double> &arr) {
std::stringstream ss;
ss << "[ ";
for (size_t i = 0; i < arr.size(); i++) {
ss << arr[i];
if (i != arr.size() - 1) {
ss << ", ";
}
ss << arr[i] << ((i != arr.size() - 1) ? ", " : "");
}
ss << " ]";
@@ -249,36 +243,35 @@ static std::string PrintValue(const std::string &name,
if (tag) {
ss << Indent(indent) << name << " : " << value.Get<std::string>();
} else {
ss << Indent(indent) << value.Get<std::string>() << " ";
ss << " " << value.Get<std::string>() << " ";
}
} else if (value.IsBool()) {
if (tag) {
ss << Indent(indent) << name << " : " << value.Get<bool>();
} else {
ss << Indent(indent) << value.Get<bool>() << " ";
ss << " " << value.Get<bool>() << " ";
}
} else if (value.IsNumber()) {
if (tag) {
ss << Indent(indent) << name << " : " << value.Get<double>();
} else {
ss << Indent(indent) << value.Get<double>() << " ";
ss << " " << value.Get<double>() << " ";
}
} else if (value.IsInt()) {
if (tag) {
ss << Indent(indent) << name << " : " << value.Get<int>();
} else {
ss << Indent(indent) << value.Get<int>() << " ";
ss << " " << value.Get<int>() << " ";
}
} else if (value.IsArray()) {
// TODO(syoyo): Better pretty printing of array item
ss << Indent(indent) << name << " [ \n";
ss << Indent(indent) << name << " [ ";
for (size_t i = 0; i < value.Size(); i++) {
ss << PrintValue("", value.Get(int(i)), indent + 1, /* tag */ false);
if (i != (value.ArrayLen() - 1)) {
ss << ", \n";
ss << ", ";
}
}
ss << "\n" << Indent(indent) << "] ";
ss << Indent(indent) << "] ";
}
// @todo { binary }
@@ -322,15 +315,6 @@ static void DumpStringIntMap(const std::map<std::string, int> &m, int indent) {
}
}
static void DumpExtensions(const tinygltf::ExtensionMap &extension,
const int indent) {
// TODO(syoyo): pritty print Value
for (auto &e : extension) {
std::cout << Indent(indent) << e.first << std::endl;
std::cout << PrintValue("extensions", e.second, indent + 1) << std::endl;
}
}
static void DumpPrimitive(const tinygltf::Primitive &primitive, int indent) {
std::cout << Indent(indent) << "material : " << primitive.material
<< std::endl;
@@ -342,80 +326,17 @@ static void DumpPrimitive(const tinygltf::Primitive &primitive, int indent) {
<< std::endl;
DumpStringIntMap(primitive.attributes, indent + 1);
DumpExtensions(primitive.extensions, indent);
std::cout << Indent(indent) << "extras :" << std::endl
<< PrintValue("extras", primitive.extras, indent + 1) << std::endl;
if (!primitive.extensions_json_string.empty()) {
std::cout << Indent(indent + 1) << "extensions(JSON string) = "
<< primitive.extensions_json_string << "\n";
}
if (!primitive.extras_json_string.empty()) {
std::cout << Indent(indent + 1)
<< "extras(JSON string) = " << primitive.extras_json_string
<< "\n";
}
}
static void DumpTextureInfo(const tinygltf::TextureInfo &texinfo,
const int indent) {
std::cout << Indent(indent) << "index : " << texinfo.index << "\n";
std::cout << Indent(indent) << "texCoord : TEXCOORD_" << texinfo.texCoord
<< "\n";
DumpExtensions(texinfo.extensions, indent + 1);
std::cout << PrintValue("extras", texinfo.extras, indent + 1) << "\n";
if (!texinfo.extensions_json_string.empty()) {
std::cout << Indent(indent)
<< "extensions(JSON string) = " << texinfo.extensions_json_string
<< "\n";
static void DumpExtensions(const tinygltf::ExtensionMap &extension,
const int indent) {
// TODO(syoyo): pritty print Value
for (auto &e : extension) {
std::cout << Indent(indent) << e.first << std::endl;
std::cout << PrintValue("extensions", e.second, indent + 1) << std::endl;
}
if (!texinfo.extras_json_string.empty()) {
std::cout << Indent(indent)
<< "extras(JSON string) = " << texinfo.extras_json_string << "\n";
}
}
static void DumpNormalTextureInfo(const tinygltf::NormalTextureInfo &texinfo,
const int indent) {
std::cout << Indent(indent) << "index : " << texinfo.index << "\n";
std::cout << Indent(indent) << "texCoord : TEXCOORD_" << texinfo.texCoord
<< "\n";
std::cout << Indent(indent) << "scale : " << texinfo.scale << "\n";
DumpExtensions(texinfo.extensions, indent + 1);
std::cout << PrintValue("extras", texinfo.extras, indent + 1) << "\n";
}
static void DumpOcclusionTextureInfo(
const tinygltf::OcclusionTextureInfo &texinfo, const int indent) {
std::cout << Indent(indent) << "index : " << texinfo.index << "\n";
std::cout << Indent(indent) << "texCoord : TEXCOORD_" << texinfo.texCoord
<< "\n";
std::cout << Indent(indent) << "strength : " << texinfo.strength << "\n";
DumpExtensions(texinfo.extensions, indent + 1);
std::cout << PrintValue("extras", texinfo.extras, indent + 1) << "\n";
}
static void DumpPbrMetallicRoughness(const tinygltf::PbrMetallicRoughness &pbr,
const int indent) {
std::cout << Indent(indent)
<< "baseColorFactor : " << PrintFloatArray(pbr.baseColorFactor)
<< "\n";
std::cout << Indent(indent) << "baseColorTexture :\n";
DumpTextureInfo(pbr.baseColorTexture, indent + 1);
std::cout << Indent(indent) << "metallicFactor : " << pbr.metallicFactor
<< "\n";
std::cout << Indent(indent) << "roughnessFactor : " << pbr.roughnessFactor
<< "\n";
std::cout << Indent(indent) << "metallicRoughnessTexture :\n";
DumpTextureInfo(pbr.metallicRoughnessTexture, indent + 1);
DumpExtensions(pbr.extensions, indent + 1);
std::cout << PrintValue("extras", pbr.extras, indent + 1) << "\n";
}
static void Dump(const tinygltf::Model &model) {
@@ -570,21 +491,6 @@ static void Dump(const tinygltf::Model &model) {
std::cout << Indent(2)
<< "target : " << PrintTarget(bufferView.target)
<< std::endl;
std::cout << Indent(1) << "-------------------------------------\n";
DumpExtensions(bufferView.extensions, 1);
std::cout << PrintValue("extras", bufferView.extras, 2) << std::endl;
if (!bufferView.extensions_json_string.empty()) {
std::cout << Indent(2) << "extensions(JSON string) = "
<< bufferView.extensions_json_string << "\n";
}
if (!bufferView.extras_json_string.empty()) {
std::cout << Indent(2)
<< "extras(JSON string) = " << bufferView.extras_json_string
<< "\n";
}
}
}
@@ -595,21 +501,6 @@ static void Dump(const tinygltf::Model &model) {
std::cout << Indent(1) << "name : " << buffer.name << std::endl;
std::cout << Indent(2) << "byteLength : " << buffer.data.size()
<< std::endl;
std::cout << Indent(1) << "-------------------------------------\n";
DumpExtensions(buffer.extensions, 1);
std::cout << PrintValue("extras", buffer.extras, 2) << std::endl;
if (!buffer.extensions_json_string.empty()) {
std::cout << Indent(2) << "extensions(JSON string) = "
<< buffer.extensions_json_string << "\n";
}
if (!buffer.extras_json_string.empty()) {
std::cout << Indent(2)
<< "extras(JSON string) = " << buffer.extras_json_string
<< "\n";
}
}
}
@@ -618,55 +509,16 @@ static void Dump(const tinygltf::Model &model) {
<< std::endl;
for (size_t i = 0; i < model.materials.size(); i++) {
const tinygltf::Material &material = model.materials[i];
std::cout << Indent(1) << "name : " << material.name
<< std::endl;
std::cout << Indent(1) << "alphaMode : " << material.alphaMode
<< std::endl;
std::cout << Indent(1)
<< "alphaCutoff : " << material.alphaCutoff
<< std::endl;
std::cout << Indent(1) << "doubleSided : "
<< (material.doubleSided ? "true" : "false") << std::endl;
std::cout << Indent(1) << "emissiveFactor : "
<< PrintFloatArray(material.emissiveFactor) << std::endl;
std::cout << Indent(1) << "pbrMetallicRoughness :\n";
DumpPbrMetallicRoughness(material.pbrMetallicRoughness, 2);
std::cout << Indent(1) << "normalTexture :\n";
DumpNormalTextureInfo(material.normalTexture, 2);
std::cout << Indent(1) << "occlusionTexture :\n";
DumpOcclusionTextureInfo(material.occlusionTexture, 2);
std::cout << Indent(1) << "emissiveTexture :\n";
DumpTextureInfo(material.emissiveTexture, 2);
std::cout << Indent(1) << "---- legacy material parameter ----\n";
std::cout << Indent(1) << "name : " << material.name << std::endl;
std::cout << Indent(1) << "values(items=" << material.values.size() << ")"
<< std::endl;
tinygltf::ParameterMap::const_iterator p(material.values.begin());
tinygltf::ParameterMap::const_iterator pEnd(material.values.end());
for (; p != pEnd; p++) {
std::cout << Indent(2) << p->first << ": "
<< PrintParameterValue(p->second) << std::endl;
}
std::cout << Indent(1) << "-------------------------------------\n";
DumpExtensions(material.extensions, 1);
std::cout << PrintValue("extras", material.extras, 2) << std::endl;
if (!material.extensions_json_string.empty()) {
std::cout << Indent(2) << "extensions(JSON string) = "
<< material.extensions_json_string << "\n";
}
if (!material.extras_json_string.empty()) {
std::cout << Indent(2)
<< "extras(JSON string) = " << material.extras_json_string
<< "\n";
}
}
}
@@ -690,18 +542,6 @@ static void Dump(const tinygltf::Model &model) {
std::cout << Indent(2) << "height : " << image.height << std::endl;
std::cout << Indent(2) << "component : " << image.component << std::endl;
DumpExtensions(image.extensions, 1);
std::cout << PrintValue("extras", image.extras, 2) << std::endl;
if (!image.extensions_json_string.empty()) {
std::cout << Indent(2) << "extensions(JSON string) = "
<< image.extensions_json_string << "\n";
}
if (!image.extras_json_string.empty()) {
std::cout << Indent(2)
<< "extras(JSON string) = " << image.extras_json_string
<< "\n";
}
}
}
@@ -714,18 +554,6 @@ static void Dump(const tinygltf::Model &model) {
std::cout << Indent(1) << "source : " << texture.source
<< std::endl;
DumpExtensions(texture.extensions, 1);
std::cout << PrintValue("extras", texture.extras, 2) << std::endl;
if (!texture.extensions_json_string.empty()) {
std::cout << Indent(2) << "extensions(JSON string) = "
<< texture.extensions_json_string << "\n";
}
if (!texture.extras_json_string.empty()) {
std::cout << Indent(2)
<< "extras(JSON string) = " << texture.extras_json_string
<< "\n";
}
}
}
@@ -747,20 +575,6 @@ static void Dump(const tinygltf::Model &model) {
std::cout << Indent(2)
<< "wrapT : " << PrintWrapMode(sampler.wrapT)
<< std::endl;
DumpExtensions(sampler.extensions, 1);
std::cout << PrintValue("extras", sampler.extras, 2) << std::endl;
if (!sampler.extensions_json_string.empty()) {
std::cout << Indent(2) << "extensions(JSON string) = "
<< sampler.extensions_json_string << "\n";
}
if (!sampler.extras_json_string.empty()) {
std::cout << Indent(2)
<< "extras(JSON string) = " << sampler.extras_json_string
<< "\n";
}
}
}
@@ -793,54 +607,6 @@ static void Dump(const tinygltf::Model &model) {
<< "znear : " << camera.orthographic.znear
<< std::endl;
}
std::cout << Indent(1) << "-------------------------------------\n";
DumpExtensions(camera.extensions, 1);
std::cout << PrintValue("extras", camera.extras, 2) << std::endl;
if (!camera.extensions_json_string.empty()) {
std::cout << Indent(2) << "extensions(JSON string) = "
<< camera.extensions_json_string << "\n";
}
if (!camera.extras_json_string.empty()) {
std::cout << Indent(2)
<< "extras(JSON string) = " << camera.extras_json_string
<< "\n";
}
}
}
{
std::cout << "skins(items=" << model.skins.size() << ")" << std::endl;
for (size_t i = 0; i < model.skins.size(); i++) {
const tinygltf::Skin &skin = model.skins[i];
std::cout << Indent(1) << "name : " << skin.name << std::endl;
std::cout << Indent(2)
<< "inverseBindMatrices : " << skin.inverseBindMatrices
<< std::endl;
std::cout << Indent(2) << "skeleton : " << skin.skeleton
<< std::endl;
std::cout << Indent(2)
<< "joints : " << PrintIntArray(skin.joints)
<< std::endl;
std::cout << Indent(1) << "-------------------------------------\n";
DumpExtensions(skin.extensions, 1);
std::cout << PrintValue("extras", skin.extras, 2) << std::endl;
if (!skin.extensions_json_string.empty()) {
std::cout << Indent(2)
<< "extensions(JSON string) = " << skin.extensions_json_string
<< "\n";
}
if (!skin.extras_json_string.empty()) {
std::cout << Indent(2)
<< "extras(JSON string) = " << skin.extras_json_string
<< "\n";
}
}
}
@@ -858,12 +624,6 @@ int main(int argc, char **argv) {
exit(1);
}
// Store original JSON string for `extras` and `extensions`
bool store_original_json_for_extras_and_extensions = false;
if (argc > 2) {
store_original_json_for_extras_and_extensions = true;
}
tinygltf::Model model;
tinygltf::TinyGLTF gltf_ctx;
std::string err;
@@ -871,9 +631,6 @@ int main(int argc, char **argv) {
std::string input_filename(argv[1]);
std::string ext = GetFilePathExtension(input_filename);
gltf_ctx.SetStoreOriginalJSONForExtrasAndExtensions(
store_original_json_for_extras_and_extensions);
bool ret = false;
if (ext.compare("glb") == 0) {
std::cout << "Reading binary glTF" << std::endl;

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

193
models/Cube-KTX/Cube.gltf Normal file
View File

@@ -0,0 +1,193 @@
{
"accessors" : [
{
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5123,
"count" : 36,
"max" : [
35
],
"min" : [
0
],
"type" : "SCALAR"
},
{
"bufferView" : 1,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
1.000000,
1.000001
],
"min" : [
-1.000000,
-1.000000,
-1.000000
],
"type" : "VEC3"
},
{
"bufferView" : 2,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
1.000000,
1.000000
],
"min" : [
-1.000000,
-1.000000,
-1.000000
],
"type" : "VEC3"
},
{
"bufferView" : 3,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
-0.000000,
-0.000000,
1.000000
],
"min" : [
0.000000,
-0.000000,
-1.000000,
-1.000000
],
"type" : "VEC4"
},
{
"bufferView" : 4,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
1.000000
],
"min" : [
-1.000000,
-1.000000
],
"type" : "VEC2"
}
],
"asset" : {
"generator" : "VKTS glTF 2.0 exporter",
"version" : "2.0"
},
"bufferViews" : [
{
"buffer" : 0,
"byteLength" : 72,
"byteOffset" : 0,
"target" : 34963
},
{
"buffer" : 0,
"byteLength" : 432,
"byteOffset" : 72,
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 432,
"byteOffset" : 504,
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 576,
"byteOffset" : 936,
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 288,
"byteOffset" : 1512,
"target" : 34962
}
],
"buffers" : [
{
"byteLength" : 1800,
"uri" : "Cube.bin"
}
],
"images" : [
{
"uri" : "Cube_BaseColor.ktx"
},
{
"uri" : "Cube_MetallicRoughness.ktx"
}
],
"materials" : [
{
"name" : "Cube",
"pbrMetallicRoughness" : {
"baseColorTexture" : {
"index" : 0
},
"metallicRoughnessTexture" : {
"index" : 1
}
}
}
],
"meshes" : [
{
"name" : "Cube",
"primitives" : [
{
"attributes" : {
"NORMAL" : 2,
"POSITION" : 1,
"TANGENT" : 3,
"TEXCOORD_0" : 4
},
"indices" : 0,
"material" : 0,
"mode" : 4
}
]
}
],
"nodes" : [
{
"mesh" : 0,
"name" : "Cube"
}
],
"samplers" : [
{}
],
"scene" : 0,
"scenes" : [
{
"nodes" : [
0
]
}
],
"textures" : [
{
"sampler" : 0,
"source" : 0
},
{
"sampler" : 0,
"source" : 1
}
]
}

Binary file not shown.

Binary file not shown.

12
models/Cube-KTX/README.md Normal file
View File

@@ -0,0 +1,12 @@
License: Donated by Norbert Nopper for glTF testing.
https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Cube
----
Converted .png to .ktx image by Syoyo Fujita.
.png -> .ppm using image magic(resize with 25% to reduce file size)
.ppm -> .ktx using `toktx` in KTX-Software https://github.com/KhronosGroup/KTX-Software

View File

@@ -1,224 +0,0 @@
{
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 36,
"max": [
35
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 36,
"max": [
1,
1,
1.000001
],
"min": [
-1,
-1,
-1
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 36,
"max": [
1,
1,
1
],
"min": [
-1,
-1,
-1
],
"type": "VEC3"
},
{
"bufferView": 3,
"byteOffset": 0,
"componentType": 5126,
"count": 36,
"max": [
1,
-0,
-0,
1
],
"min": [
0,
-0,
-1,
-1
],
"type": "VEC4"
},
{
"bufferView": 4,
"byteOffset": 0,
"componentType": 5126,
"count": 36,
"max": [
1,
1
],
"min": [
-1,
-1
],
"type": "VEC2"
}
],
"asset": {
"generator": "VKTS glTF 2.0 exporter",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 72,
"byteOffset": 0,
"target": 34963
},
{
"buffer": 0,
"byteLength": 432,
"byteOffset": 72,
"target": 34962
},
{
"buffer": 0,
"byteLength": 432,
"byteOffset": 504,
"target": 34962
},
{
"buffer": 0,
"byteLength": 576,
"byteOffset": 936,
"target": 34962
},
{
"buffer": 0,
"byteLength": 288,
"byteOffset": 1512,
"target": 34962
}
],
"buffers": [
{
"byteLength": 1800,
"uri": "Cube.bin"
}
],
"images": [
{
"0comment": "Use Cube_MetallicRoughness.png to reduce scene filesize",
"uri": "Cube_MetallicRoughness.png"
},
{
"uri": "Cube_MetallicRoughness.png"
}
],
"materials": [
{
"emissiveTexture": {
"index": 0,
"extensions": {
"KHR_texture_transform": {
"offset": [
0,
1
],
"scale": [
1,
-1
]
}
}
}
},
{
"name": "Cube",
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicRoughnessTexture": {
"index": 1,
"extensions": {
"KHR_texture_transform": {
"offset": [
0,
1
],
"rotation": 1.57079632679,
"scale": [
0.5,
0.5
]
}
}
}
}
}
],
"meshes": [
{
"name": "Cube",
"primitives": [
{
"attributes": {
"NORMAL": 2,
"POSITION": 1,
"TANGENT": 3,
"TEXCOORD_0": 4
},
"indices": 0,
"material": 0,
"mode": 4
}
]
}
],
"nodes": [
{
"mesh": 0,
"name": "Cube"
}
],
"samplers": [
{}
],
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"textures": [
{
"sampler": 0,
"source": 0
},
{
"sampler": 0,
"source": 1
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 B

View File

@@ -1,6 +0,0 @@
Added KHR_texture_transform property to Cube scene.
License: Donated by Norbert Nopper for glTF testing.
https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Cube

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 B

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,14 +13,6 @@
#include <sstream>
#include <fstream>
static JsonDocument JsonConstruct(const char* str)
{
JsonDocument doc;
JsonParse(doc, str, strlen(str));
return doc;
}
TEST_CASE("parse-error", "[parse]") {
tinygltf::Model model;
@@ -28,7 +20,7 @@ TEST_CASE("parse-error", "[parse]") {
std::string err;
std::string warn;
bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, "bora", static_cast<int>(strlen("bora")), /* basedir*/ "");
bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, "bora", strlen("bora"), /* basedir*/ "");
REQUIRE(false == ret);
@@ -175,12 +167,12 @@ TEST_CASE("parse-integer", "[bounds-checking]") {
SECTION("parses valid numbers") {
std::string err;
int result = 123;
CHECK(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"zero\" : 0}"), "zero",
CHECK(tinygltf::ParseIntegerProperty(&result, &err, {{"zero", 0}}, "zero",
true));
REQUIRE(err == "");
REQUIRE(result == 0);
CHECK(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"int\": -1234}"), "int",
CHECK(tinygltf::ParseIntegerProperty(&result, &err, {{"int", -1234}}, "int",
true));
REQUIRE(err == "");
REQUIRE(result == -1234);
@@ -189,7 +181,7 @@ TEST_CASE("parse-integer", "[bounds-checking]") {
SECTION("detects missing properties") {
std::string err;
int result = -1;
CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), "int", true));
CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, {}, "int", true));
REQUIRE_THAT(err, Catch::Contains("'int' property is missing"));
REQUIRE(result == -1);
}
@@ -198,7 +190,7 @@ TEST_CASE("parse-integer", "[bounds-checking]") {
std::string err;
int result = -1;
CHECK_FALSE(
tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), "int", false));
tinygltf::ParseIntegerProperty(&result, &err, {}, "int", false));
REQUIRE(err == "");
REQUIRE(result == -1);
}
@@ -207,26 +199,21 @@ TEST_CASE("parse-integer", "[bounds-checking]") {
std::string err;
int result = -1;
CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"int\": 0.5}"),
"int", true));
CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, {{"int", 0.5}},
"int", true));
REQUIRE_THAT(err, Catch::Contains("not an integer type"));
// Excessively large values and NaN aren't allowed either.
err.clear();
CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"int\": 1e300}"),
"int", true));
CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, {{"int", 1e300}},
"int", true));
REQUIRE_THAT(err, Catch::Contains("not an integer type"));
err.clear();
{
JsonDocument o;
double nan = std::numeric_limits<double>::quiet_NaN();
tinygltf::JsonAddMember(o, "int", json(nan));
CHECK_FALSE(tinygltf::ParseIntegerProperty(
&result, &err, o,
CHECK_FALSE(tinygltf::ParseIntegerProperty(
&result, &err, {{"int", std::numeric_limits<double>::quiet_NaN()}},
"int", true));
REQUIRE_THAT(err, Catch::Contains("not an integer type"));
}
REQUIRE_THAT(err, Catch::Contains("not an integer type"));
}
}
@@ -234,7 +221,7 @@ TEST_CASE("parse-unsigned", "[bounds-checking]") {
SECTION("parses valid unsigned integers") {
// Use string-based parsing here, using the initializer list syntax doesn't
// parse 0 as unsigned.
auto zero_obj = JsonConstruct("{\"zero\": 0}");
json zero_obj = json::parse("{\"zero\": 0}");
std::string err;
size_t result = 123;
@@ -248,31 +235,26 @@ TEST_CASE("parse-unsigned", "[bounds-checking]") {
std::string err;
size_t result = -1;
CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, JsonConstruct("{\"int\": -1234}"),
CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, {{"int", -1234}},
"int", true));
REQUIRE_THAT(err, Catch::Contains("not a positive integer"));
err.clear();
CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, JsonConstruct("{\"int\": 0.5}"),
CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, {{"int", 0.5}},
"int", true));
REQUIRE_THAT(err, Catch::Contains("not a positive integer"));
// Excessively large values and NaN aren't allowed either.
err.clear();
CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, JsonConstruct("{\"int\": 1e300}"),
CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, {{"int", 1e300}},
"int", true));
REQUIRE_THAT(err, Catch::Contains("not a positive integer"));
err.clear();
{
JsonDocument o;
double nan = std::numeric_limits<double>::quiet_NaN();
tinygltf::JsonAddMember(o, "int", json(nan));
CHECK_FALSE(tinygltf::ParseUnsignedProperty(
&result, &err, o,
CHECK_FALSE(tinygltf::ParseUnsignedProperty(
&result, &err, {{"int", std::numeric_limits<double>::quiet_NaN()}},
"int", true));
REQUIRE_THAT(err, Catch::Contains("not a positive integer"));
}
REQUIRE_THAT(err, Catch::Contains("not a positive integer"));
}
}
@@ -281,7 +263,7 @@ TEST_CASE("parse-integer-array", "[bounds-checking]") {
std::string err;
std::vector<int> result;
CHECK(tinygltf::ParseIntegerArrayProperty(&result, &err,
JsonConstruct("{\"x\": [-1, 2, 3]}"), "x", true));
{{"x", {-1, 2, 3}}}, "x", true));
REQUIRE(err == "");
REQUIRE(result.size() == 3);
REQUIRE(result[0] == -1);
@@ -293,69 +275,7 @@ TEST_CASE("parse-integer-array", "[bounds-checking]") {
std::string err;
std::vector<int> result;
CHECK_FALSE(tinygltf::ParseIntegerArrayProperty(
&result, &err, JsonConstruct("{\"x\": [-1, 1e300, 3]}"), "x", true));
&result, &err, {{"x", {-1, 1e300, 3}}}, "x", true));
REQUIRE_THAT(err, Catch::Contains("not an integer type"));
}
}
TEST_CASE("pbr-khr-texture-transform", "[material]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
// Loading is expected to fail, but not crash.
bool ret = ctx.LoadASCIIFromFile(
&model, &err, &warn,
"../models/Cube-texture-ext/Cube-textransform.gltf");
REQUIRE(ret == true);
REQUIRE(model.materials.size() == 2);
REQUIRE(model.materials[0].emissiveTexture.extensions.count("KHR_texture_transform") == 1);
REQUIRE(model.materials[0].emissiveTexture.extensions["KHR_texture_transform"].IsObject());
tinygltf::Value::Object &texform = model.materials[0].emissiveTexture.extensions["KHR_texture_transform"].Get<tinygltf::Value::Object>();
REQUIRE(texform.count("scale"));
REQUIRE(texform["scale"].IsArray());
// Note: It looks json.hpp parse integer JSON number as integer, not floating point.
// IsNumber return true either value is int or floating point.
REQUIRE(texform["scale"].Get(0).IsNumber());
REQUIRE(texform["scale"].Get(1).IsNumber());
double scale[2];
scale[0] = texform["scale"].Get(0).GetNumberAsDouble();
scale[1] = texform["scale"].Get(1).GetNumberAsDouble();
REQUIRE(scale[0] == Approx(1.0));
REQUIRE(scale[1] == Approx(-1.0));
}
TEST_CASE("image-uri-spaces", "[issue-236]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
// Test image file with single spaces.
bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriSpaces.gltf");
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
// Test image file with a beginning space, trailing space, and greater than
// one consecutive spaces.
ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf");
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
}

File diff suppressed because it is too large Load Diff

1600
tinyktx.h Normal file

File diff suppressed because it is too large Load Diff