mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-08 11:13:50 +00:00
Compare commits
62 Commits
store-json
...
mesh-dump
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb0a8794c3 | ||
|
|
60e22f3281 | ||
|
|
4cfd2849fb | ||
|
|
a5416707c9 | ||
|
|
8ec9af7f2e | ||
|
|
40982716f9 | ||
|
|
4bdc96fb9b | ||
|
|
96ecea080a | ||
|
|
22bfc843ee | ||
|
|
eaa25a307a | ||
|
|
ab600b8e72 | ||
|
|
eb9d29c06e | ||
|
|
57356933c5 | ||
|
|
c3d6716c56 | ||
|
|
ac5e09f8c3 | ||
|
|
e2c3fe1c0b | ||
|
|
973d9b3394 | ||
|
|
28ad4ab7b8 | ||
|
|
0227ae5d9b | ||
|
|
a0a62bde1c | ||
|
|
23d1ff2880 | ||
|
|
bcf2ce586e | ||
|
|
aa3c5a1cad | ||
|
|
db2fbeec9d | ||
|
|
c4166e4c60 | ||
|
|
1cf179d377 | ||
|
|
ddc14f8ba6 | ||
|
|
72f4a55edd | ||
|
|
379bb612f1 | ||
|
|
1da4e5d633 | ||
|
|
4e47bc7799 | ||
|
|
9cc3e91968 | ||
|
|
d09788d10f | ||
|
|
a11f6e1939 | ||
|
|
8ced108571 | ||
|
|
3eb65e269a | ||
|
|
1322c7bf09 | ||
|
|
49651fe3d9 | ||
|
|
db93a87c4a | ||
|
|
7b0d81bf12 | ||
|
|
2e8a115d7e | ||
|
|
ae9364902a | ||
|
|
06d2fbdae6 | ||
|
|
d5ff5cbfb1 | ||
|
|
1d20520680 | ||
|
|
4ab0386d09 | ||
|
|
a471770f38 | ||
|
|
125d8e50a9 | ||
|
|
45cac78709 | ||
|
|
a9d86c1af4 | ||
|
|
2f26eddac4 | ||
|
|
a0939550ca | ||
|
|
58ab95be2f | ||
|
|
bf0e4f8e45 | ||
|
|
30f333c607 | ||
|
|
419162716e | ||
|
|
9533c352a1 | ||
|
|
9ff3d93084 | ||
|
|
aea514a975 | ||
|
|
05a4456948 | ||
|
|
fb256609f2 | ||
|
|
5cecef2b92 |
11
.travis.yml
11
.travis.yml
@@ -31,6 +31,17 @@ 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
|
||||
|
||||
@@ -4,14 +4,21 @@ PROJECT (tinygltf)
|
||||
|
||||
SET(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
ADD_EXECUTABLE ( loader_example
|
||||
loader_example.cc
|
||||
)
|
||||
option(TINYGLTF_BUILD_EXAMPLES "Build examples" ON)
|
||||
|
||||
ADD_SUBDIRECTORY ( examples/gltfutil )
|
||||
ADD_SUBDIRECTORY ( examples/glview )
|
||||
ADD_SUBDIRECTORY ( examples/validator )
|
||||
if (TINYGLTF_BUILD_EXAMPLES)
|
||||
ADD_EXECUTABLE ( loader_example
|
||||
loader_example.cc
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
24
README.md
24
README.md
@@ -34,8 +34,11 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
|
||||
* Moderate parsing time and memory consumption.
|
||||
* glTF specification v2.0.0
|
||||
* [x] ASCII glTF
|
||||
* [x] Load
|
||||
* [x] Save
|
||||
* [x] Binary glTF(GLB)
|
||||
* [x] PBR material description
|
||||
* [x] Load
|
||||
* [x] Save(.bin embedded .glb)
|
||||
* Buffers
|
||||
* [x] Parse BASE64 encoded embedded buffer data(DataURI).
|
||||
* [x] Load `.bin` file.
|
||||
@@ -55,6 +58,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
|
||||
* [x] Image save
|
||||
* Extensions
|
||||
* [x] Draco mesh decoding
|
||||
* [ ] Draco mesh encoding
|
||||
|
||||
## Note on extension property
|
||||
|
||||
@@ -75,9 +79,10 @@ 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
|
||||
* 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
|
||||
@@ -159,14 +164,17 @@ if (!ret) {
|
||||
|
||||
|
||||
### Saving gltTF 2.0 model
|
||||
* [ ] Buffers.
|
||||
|
||||
* Buffers.
|
||||
* [x] To file
|
||||
* [x] Embedded
|
||||
* [ ] Draco compressed?
|
||||
* [x] Images
|
||||
* [x] To file
|
||||
* [x] Embedded
|
||||
* [ ] Binary(.glb)
|
||||
* Binary(.glb)
|
||||
* [x] .bin embedded single .glb
|
||||
* [ ] External .bin
|
||||
|
||||
## Running tests.
|
||||
|
||||
@@ -194,9 +202,17 @@ $ ./tester
|
||||
$ ./tester_noexcept
|
||||
```
|
||||
|
||||
### Fuzzing tests
|
||||
|
||||
See `tests/fuzzer` for details.
|
||||
|
||||
After running fuzzer on Ryzen9 3950X a week, at least `LoadASCIIFromString` looks safe except for out-of-memory error in Fuzzer.
|
||||
We may be better to introduce bounded memory size checking when parsing glTF data.
|
||||
|
||||
## Third party licenses
|
||||
|
||||
* json.hpp : Licensed under the MIT License <http://opensource.org/licenses/MIT>. Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
* stb_image : Public domain.
|
||||
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.
|
||||
* RapidJSON : Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. http://rapidjson.org/
|
||||
* dlib(uridecode, uriencode) : Copyright (C) 2003 Davis E. King Boost Software License 1.0. http://dlib.net/dlib/server/server_http.cpp.html
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
5
examples/mesh-modify/Makefile
Normal file
5
examples/mesh-modify/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
#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-modify mesh-modify.cc mesh-util.cc tinygltf_impl.cc
|
||||
13
examples/mesh-modify/README.md
Normal file
13
examples/mesh-modify/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 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.
|
||||
|
||||
This example show how to
|
||||
|
||||
- Export mesh data from .bin to .obj
|
||||
- Import mesh data to .bin(update corresponding buffer data) from .obj
|
||||
|
||||
## Requirement
|
||||
|
||||
Assume Buffer is stored as external file(`.bin`)
|
||||
760
examples/mesh-modify/mesh-modify.cc
Normal file
760
examples/mesh-modify/mesh-modify.cc
Normal file
@@ -0,0 +1,760 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#if !defined(__ANDROID__) && !defined(_WIN32)
|
||||
#include <wordexp.h>
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Weverything"
|
||||
#endif
|
||||
|
||||
#include "../../json.hpp"
|
||||
|
||||
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
|
||||
|
||||
#if 0
|
||||
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 "";
|
||||
}
|
||||
#endif
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#if 0
|
||||
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 "";
|
||||
}
|
||||
#endif
|
||||
|
||||
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_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,
|
||||
example::MeshPrim *out) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
std::cout << "vertex_index[" << k << "] = " << idx << "\n";
|
||||
|
||||
indices.push_back(idx);
|
||||
}
|
||||
|
||||
out->indices = indices;
|
||||
out->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);
|
||||
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->position = attrib;
|
||||
} else if (attrib.name.compare("NORMAL") == 0) {
|
||||
out->normal = attrib;
|
||||
} else if (attrib.name.compare("TANGENT") == 0) {
|
||||
out->tangent = attrib;
|
||||
} else if (attrib.name.rfind("TEXCOORD_", 0) == 0) {
|
||||
int id = GetSlotId(attrib.name);
|
||||
std::cout << "texcoord[" << id << "]\n";
|
||||
out->texcoords[id] = attrib;
|
||||
} else if (attrib.name.rfind("JOINTS_", 0) == 0) {
|
||||
int id = GetSlotId(attrib.name);
|
||||
std::cout << "joints[" << id << "]\n";
|
||||
out->joints[id] = attrib;
|
||||
} else if (attrib.name.rfind("WEIGHTS_", 0) == 0) {
|
||||
int id = GetSlotId(attrib.name);
|
||||
std::cout << "weights[" << id << "]\n";
|
||||
out->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->mode = primitive.mode;
|
||||
out->name = mesh.name;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
|
||||
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(buffer.uri);
|
||||
if (bin.size() != buffer.data.size()) {
|
||||
std::cerr << "Byte size mismatch. Failed to load file: " << buffer.uri
|
||||
<< "\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, &output);
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outs->push_back(output);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "mesh-dump input.gltf" << std::endl;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string input_filename(argv[1] ? argv[1]
|
||||
: "../../../models/Cube/Cube.gltf");
|
||||
#else
|
||||
std::string input_filename(argv[1] ? argv[1] : "../../models/Cube/Cube.gltf");
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
std::string basedir = GetBaseDir(input_filename);
|
||||
std::vector<example::MeshPrim> meshes;
|
||||
bool ret = ExtractMesh(basedir, model, &meshes);
|
||||
|
||||
size_t n = 0;
|
||||
for (const auto &mesh : meshes) {
|
||||
// Assume no duplicated name in .glTF data
|
||||
std::string filename;
|
||||
if (mesh.name.empty()) {
|
||||
filename = "untitled-" + std::to_string(n) + ".obj";
|
||||
} else {
|
||||
filename = mesh.name + ".obj";
|
||||
}
|
||||
|
||||
bool flip_y = true; // flip texcoord Y?
|
||||
bool ok = example::SaveAsObjMesh(filename, mesh,);
|
||||
if (!ok) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
#else
|
||||
|
||||
{
|
||||
std::string input_filename(argv[1]);
|
||||
|
||||
// Require facevarying layout?
|
||||
// false = try to keep GL-like mesh data as much as possible.
|
||||
// true = reorder vertex data and re-assign vertex indices.
|
||||
bool facevarying = false;
|
||||
|
||||
example::MeshPrim mesh;
|
||||
bool ok = example::LoadObjMesh(input_filename, facevarying, &mesh);
|
||||
if (!ok) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
PrintMeshPrim(mesh);
|
||||
|
||||
std::string output_filename("output.gltf");
|
||||
|
||||
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;
|
||||
#endif
|
||||
|
||||
}
|
||||
1369
examples/mesh-modify/mesh-util.cc
Normal file
1369
examples/mesh-modify/mesh-util.cc
Normal file
File diff suppressed because it is too large
Load Diff
76
examples/mesh-modify/mesh-util.hh
Normal file
76
examples/mesh-modify/mesh-util.hh
Normal file
@@ -0,0 +1,76 @@
|
||||
#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 MeshPrim {
|
||||
std::string name;
|
||||
int32_t id{-1};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
///
|
||||
/// Save MeshPrim as wavefront .obj
|
||||
///
|
||||
bool SaveAsObjMesh(const std::string &filename, const MeshPrim &mesh);
|
||||
|
||||
///
|
||||
/// 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
|
||||
7
examples/mesh-modify/meson.build
Normal file
7
examples/mesh-modify/meson.build
Normal file
@@ -0,0 +1,7 @@
|
||||
project('mesh-modify', 'cpp', default_options : ['cpp_std=c++11'])
|
||||
|
||||
thread_dep = dependency('threads')
|
||||
|
||||
incdir = include_directories(['../../', '../common'])
|
||||
|
||||
executable('mesh-modify', ['mesh-modify.cc', 'mesh-util.cc', 'tinygltf_impl.cc'], include_directories : incdir, dependencies : thread_dep)
|
||||
@@ -150,6 +150,7 @@ 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) + "/";
|
||||
@@ -158,12 +159,16 @@ bool LoadObj(const std::string &filename, float scale,
|
||||
// auto t_start = std::chrono::system_clock::now();
|
||||
|
||||
bool ret =
|
||||
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename.c_str(),
|
||||
tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &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;
|
||||
}
|
||||
|
||||
BIN
models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png
Normal file
BIN
models/CubeImageUriSpaces/ 2x2 image has multiple spaces.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 B |
BIN
models/CubeImageUriSpaces/2x2 image has spaces.png
Normal file
BIN
models/CubeImageUriSpaces/2x2 image has spaces.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 B |
171
models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf
Normal file
171
models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf
Normal file
@@ -0,0 +1,171 @@
|
||||
{
|
||||
"asset": {
|
||||
"version": "2.0"
|
||||
},
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 2,
|
||||
"POSITION": 1,
|
||||
"TEXCOORD_0": 3
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorTexture": {
|
||||
"index": 0,
|
||||
"texCoord": 0
|
||||
},
|
||||
"baseColorFactor": [
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"metallicFactor": 1,
|
||||
"roughnessFactor": 1
|
||||
},
|
||||
"emissiveFactor": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"alphaMode": "OPAQUE"
|
||||
}
|
||||
],
|
||||
"textures": [
|
||||
{
|
||||
"source": 0,
|
||||
"sampler": 0
|
||||
}
|
||||
],
|
||||
"samplers": [
|
||||
{
|
||||
"wrapS": 33071,
|
||||
"wrapT": 33071
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"uri": " 2x2 image has multiple spaces.png"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5121,
|
||||
"count": 36,
|
||||
"normalized": false,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"normalized": false,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 2,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"normalized": false,
|
||||
"max": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min": [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 3,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"normalized": false,
|
||||
"max": [
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type": "VEC2"
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 36
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 36,
|
||||
"byteLength": 288
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 324,
|
||||
"byteLength": 288
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 612,
|
||||
"byteLength": 192
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 804,
|
||||
"uri": "CubeImageUriSpaces.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
models/CubeImageUriSpaces/CubeImageUriSpaces.bin
Normal file
BIN
models/CubeImageUriSpaces/CubeImageUriSpaces.bin
Normal file
Binary file not shown.
171
models/CubeImageUriSpaces/CubeImageUriSpaces.gltf
Normal file
171
models/CubeImageUriSpaces/CubeImageUriSpaces.gltf
Normal file
@@ -0,0 +1,171 @@
|
||||
{
|
||||
"asset": {
|
||||
"version": "2.0"
|
||||
},
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"NORMAL": 2,
|
||||
"POSITION": 1,
|
||||
"TEXCOORD_0": 3
|
||||
},
|
||||
"indices": 0,
|
||||
"mode": 4,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorTexture": {
|
||||
"index": 0,
|
||||
"texCoord": 0
|
||||
},
|
||||
"baseColorFactor": [
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"metallicFactor": 1,
|
||||
"roughnessFactor": 1
|
||||
},
|
||||
"emissiveFactor": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"alphaMode": "OPAQUE"
|
||||
}
|
||||
],
|
||||
"textures": [
|
||||
{
|
||||
"source": 0,
|
||||
"sampler": 0
|
||||
}
|
||||
],
|
||||
"samplers": [
|
||||
{
|
||||
"wrapS": 33071,
|
||||
"wrapT": 33071
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"uri": "2x2 image has spaces.png"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5121,
|
||||
"count": 36,
|
||||
"normalized": false,
|
||||
"max": [
|
||||
23
|
||||
],
|
||||
"min": [
|
||||
0
|
||||
],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"normalized": false,
|
||||
"max": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
],
|
||||
"min": [
|
||||
-0.5,
|
||||
-0.5,
|
||||
-0.5
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 2,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"normalized": false,
|
||||
"max": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min": [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 3,
|
||||
"byteOffset": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"normalized": false,
|
||||
"max": [
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type": "VEC2"
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 0,
|
||||
"byteLength": 36
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 36,
|
||||
"byteLength": 288
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 324,
|
||||
"byteLength": 288
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 612,
|
||||
"byteLength": 192
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 804,
|
||||
"uri": "CubeImageUriSpaces.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
46
tests/fuzzer/README.md
Normal file
46
tests/fuzzer/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Fuzzing test
|
||||
|
||||
Do fuzzing test for TinyGLTF API.
|
||||
|
||||
## Supported API
|
||||
|
||||
* [x] LoadASCIIFromMemory
|
||||
* [ ] LoadBinaryFromMemory
|
||||
|
||||
## Requirements
|
||||
|
||||
* meson
|
||||
* clang with fuzzer support(`-fsanitize=fuzzer`. at least clang 8.0 should work)
|
||||
|
||||
## Setup
|
||||
|
||||
### Ubuntu 18.04
|
||||
|
||||
```
|
||||
$ sudo apt install clang++-8
|
||||
$ sudo apt install libfuzzer-8-dev
|
||||
```
|
||||
|
||||
Optionally, if you didn't set `update-alternatives` you can set `clang++` to point to `clang++8`
|
||||
|
||||
```
|
||||
$ sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 10
|
||||
$ sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 10
|
||||
```
|
||||
|
||||
## How to compile
|
||||
|
||||
```
|
||||
$ CXX=clang++ CC=clang meson build
|
||||
$ cd build
|
||||
$ ninja
|
||||
```
|
||||
|
||||
## How to run
|
||||
|
||||
Increase memory limit. e.g. `-rss_limit_mb=50000`
|
||||
|
||||
```
|
||||
$ ./fuzz_gltf -rss_limit_mb=20000 -jobs 4
|
||||
```
|
||||
|
||||
33
tests/fuzzer/fuzz_gltf.cc
Normal file
33
tests/fuzzer/fuzz_gltf.cc
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
static void parse_intCoding4(const uint8_t *data, size_t size)
|
||||
{
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF ctx;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
const char *str = reinterpret_cast<const char *>(data);
|
||||
|
||||
bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, str, size, /* base_dir */"" );
|
||||
(void)ret;
|
||||
|
||||
}
|
||||
|
||||
extern "C"
|
||||
int LLVMFuzzerTestOneInput(std::uint8_t const* data, std::size_t size)
|
||||
{
|
||||
parse_intCoding4(data, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
9
tests/fuzzer/meson.build
Normal file
9
tests/fuzzer/meson.build
Normal file
@@ -0,0 +1,9 @@
|
||||
project('fuzz_tinygltf', 'cpp', default_options : ['cpp_std=c++11'])
|
||||
|
||||
incdirs = include_directories('../../')
|
||||
executable('fuzz_gltf',
|
||||
'fuzz_gltf.cc',
|
||||
include_directories : incdirs,
|
||||
cpp_args : '-fsanitize=address,fuzzer',
|
||||
link_args : '-fsanitize=address,fuzzer' )
|
||||
|
||||
@@ -333,3 +333,29 @@ TEST_CASE("pbr-khr-texture-transform", "[material]") {
|
||||
REQUIRE(scale[1] == Approx(-1.0));
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("image-uri-spaces", "[issue-236]") {
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF ctx;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
// Test image file with single spaces.
|
||||
bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriSpaces.gltf");
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
|
||||
REQUIRE(true == ret);
|
||||
|
||||
// Test image file with a beginning space, trailing space, and greater than
|
||||
// one consecutive spaces.
|
||||
ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf");
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
|
||||
REQUIRE(true == ret);
|
||||
}
|
||||
|
||||
|
||||
507
tiny_gltf.h
507
tiny_gltf.h
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 - 2019 Syoyo Fujita, Aurélien Chatelain and many
|
||||
// Copyright (c) 2015 - 2020 Syoyo Fujita, Aurélien Chatelain and many
|
||||
// contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -26,6 +26,7 @@
|
||||
// THE SOFTWARE.
|
||||
|
||||
// Version:
|
||||
// - v2.4.2 Decode percent-encoded URI.
|
||||
// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
|
||||
// `extras` property.
|
||||
// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
|
||||
@@ -526,10 +527,12 @@ struct AnimationChannel {
|
||||
// "weights"]
|
||||
Value extras;
|
||||
ExtensionMap extensions;
|
||||
ExtensionMap target_extensions;
|
||||
|
||||
// Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
|
||||
std::string extras_json_string;
|
||||
std::string extensions_json_string;
|
||||
std::string target_extensions_json_string;
|
||||
|
||||
AnimationChannel() : sampler(-1), target_node(-1) {}
|
||||
DEFAULT_METHODS(AnimationChannel)
|
||||
@@ -637,7 +640,8 @@ struct Image {
|
||||
int bufferView; // (required if no uri)
|
||||
std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
|
||||
// "image/bmp", "image/gif"]
|
||||
std::string uri; // (required if no mimeType)
|
||||
std::string uri; // (required if no mimeType) uri is not decoded(e.g.
|
||||
// whitespace may be represented as %20)
|
||||
Value extras;
|
||||
ExtensionMap extensions;
|
||||
|
||||
@@ -797,12 +801,13 @@ struct Material {
|
||||
|
||||
struct BufferView {
|
||||
std::string name;
|
||||
int buffer; // Required
|
||||
size_t byteOffset; // minimum 0, default 0
|
||||
size_t byteLength; // required, minimum 1
|
||||
size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
|
||||
// understood to be tightly packed
|
||||
int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
|
||||
int buffer{-1}; // Required
|
||||
size_t byteOffset{0}; // minimum 0, default 0
|
||||
size_t byteLength{0}; // required, minimum 1. 0 = invalid
|
||||
size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
|
||||
// understood to be tightly packed
|
||||
int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
|
||||
// or atttribs. Could be 0 for other data
|
||||
Value extras;
|
||||
ExtensionMap extensions;
|
||||
|
||||
@@ -810,9 +815,15 @@ struct BufferView {
|
||||
std::string extras_json_string;
|
||||
std::string extensions_json_string;
|
||||
|
||||
bool dracoDecoded; // Flag indicating this has been draco decoded
|
||||
bool dracoDecoded{false}; // Flag indicating this has been draco decoded
|
||||
|
||||
BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {}
|
||||
BufferView()
|
||||
: buffer(-1),
|
||||
byteOffset(0),
|
||||
byteLength(0),
|
||||
byteStride(0),
|
||||
target(0),
|
||||
dracoDecoded(false) {}
|
||||
DEFAULT_METHODS(BufferView)
|
||||
bool operator==(const BufferView &) const;
|
||||
};
|
||||
@@ -887,8 +898,13 @@ struct Accessor {
|
||||
// unreachable return 0;
|
||||
}
|
||||
|
||||
Accessor() {
|
||||
bufferView = -1;
|
||||
Accessor()
|
||||
: bufferView(-1),
|
||||
byteOffset(0),
|
||||
normalized(false),
|
||||
componentType(-1),
|
||||
count(0),
|
||||
type(-1) {
|
||||
sparse.isSparse = false;
|
||||
}
|
||||
DEFAULT_METHODS(Accessor)
|
||||
@@ -1032,6 +1048,7 @@ struct Buffer {
|
||||
std::vector<unsigned char> data;
|
||||
std::string
|
||||
uri; // considered as required here but not in the spec (need to clarify)
|
||||
// uri is not decoded(e.g. whitespace may be represented as %20)
|
||||
Value extras;
|
||||
ExtensionMap extensions;
|
||||
|
||||
@@ -1136,7 +1153,7 @@ class Model {
|
||||
std::vector<Scene> scenes;
|
||||
std::vector<Light> lights;
|
||||
|
||||
int defaultScene;
|
||||
int defaultScene = -1;
|
||||
std::vector<std::string> extensionsUsed;
|
||||
std::vector<std::string> extensionsRequired;
|
||||
|
||||
@@ -1415,6 +1432,7 @@ class TinyGLTF {
|
||||
#include <algorithm>
|
||||
//#include <cassert>
|
||||
#ifndef TINYGLTF_NO_FS
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#endif
|
||||
#include <sstream>
|
||||
@@ -1541,6 +1559,14 @@ class TinyGLTF {
|
||||
#undef NOMINMAX
|
||||
#endif
|
||||
|
||||
#if defined(__GLIBCXX__) // mingw
|
||||
|
||||
#include <fcntl.h> // _O_RDONLY
|
||||
|
||||
#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
|
||||
|
||||
#endif
|
||||
|
||||
#elif !defined(__ANDROID__)
|
||||
#include <wordexp.h>
|
||||
#endif
|
||||
@@ -2106,6 +2132,88 @@ std::string base64_decode(std::string const &encoded_string) {
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
// https://github.com/syoyo/tinygltf/issues/228
|
||||
// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
|
||||
// decoding?
|
||||
//
|
||||
// https://stackoverflow.com/questions/18307429/encode-decode-url-in-c
|
||||
// http://dlib.net/dlib/server/server_http.cpp.html
|
||||
|
||||
// --- dlib beign ------------------------------------------------------------
|
||||
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
|
||||
namespace dlib {
|
||||
|
||||
#if 0
|
||||
inline unsigned char to_hex( unsigned char x )
|
||||
{
|
||||
return x + (x > 9 ? ('A'-10) : '0');
|
||||
}
|
||||
|
||||
const std::string urlencode( const std::string& s )
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci )
|
||||
{
|
||||
if ( (*ci >= 'a' && *ci <= 'z') ||
|
||||
(*ci >= 'A' && *ci <= 'Z') ||
|
||||
(*ci >= '0' && *ci <= '9') )
|
||||
{ // allowed
|
||||
os << *ci;
|
||||
}
|
||||
else if ( *ci == ' ')
|
||||
{
|
||||
os << '+';
|
||||
}
|
||||
else
|
||||
{
|
||||
os << '%' << to_hex(static_cast<unsigned char>(*ci >> 4)) << to_hex(static_cast<unsigned char>(*ci % 16));
|
||||
}
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
inline unsigned char from_hex(unsigned char ch) {
|
||||
if (ch <= '9' && ch >= '0')
|
||||
ch -= '0';
|
||||
else if (ch <= 'f' && ch >= 'a')
|
||||
ch -= 'a' - 10;
|
||||
else if (ch <= 'F' && ch >= 'A')
|
||||
ch -= 'A' - 10;
|
||||
else
|
||||
ch = 0;
|
||||
return ch;
|
||||
}
|
||||
|
||||
static const std::string urldecode(const std::string &str) {
|
||||
using namespace std;
|
||||
string result;
|
||||
string::size_type i;
|
||||
for (i = 0; i < str.size(); ++i) {
|
||||
if (str[i] == '+') {
|
||||
result += ' ';
|
||||
} else if (str[i] == '%' && str.size() > i + 2) {
|
||||
const unsigned char ch1 =
|
||||
from_hex(static_cast<unsigned char>(str[i + 1]));
|
||||
const unsigned char ch2 =
|
||||
from_hex(static_cast<unsigned char>(str[i + 2]));
|
||||
const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
|
||||
result += static_cast<char>(ch);
|
||||
i += 2;
|
||||
} else {
|
||||
result += str[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace dlib
|
||||
// --- dlib end --------------------------------------------------------------
|
||||
|
||||
static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
|
||||
std::string *warn, const std::string &filename,
|
||||
const std::string &basedir, bool required,
|
||||
@@ -2364,6 +2472,17 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
|
||||
|
||||
void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
|
||||
|
||||
#ifdef _WIN32
|
||||
static inline std::wstring UTF8ToWchar(const std::string &str) {
|
||||
int wstr_size =
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
||||
std::wstring wstr(wstr_size, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
|
||||
(int)wstr.size());
|
||||
return wstr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef TINYGLTF_NO_FS
|
||||
// Default implementations of filesystem functions
|
||||
|
||||
@@ -2383,11 +2502,20 @@ bool FileExists(const std::string &abs_filename, void *) {
|
||||
}
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
FILE *fp;
|
||||
#if defined(_MSC_VER) || defined(__GLIBCXX__)
|
||||
FILE *fp = nullptr;
|
||||
errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
|
||||
if (err != 0) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
FILE *fp = nullptr;
|
||||
errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
|
||||
if (err != 0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
FILE *fp = fopen(abs_filename.c_str(), "rb");
|
||||
#endif
|
||||
@@ -2427,8 +2555,10 @@ std::string ExpandFilePath(const std::string &filepath, void *) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Quote the string to keep any spaces in filepath intact.
|
||||
std::string quoted_path = "\"" + filepath + "\"";
|
||||
// char** w;
|
||||
int ret = wordexp(filepath.c_str(), &p, 0);
|
||||
int ret = wordexp(quoted_path.c_str(), &p, 0);
|
||||
if (ret) {
|
||||
// err
|
||||
s = filepath;
|
||||
@@ -2479,7 +2609,20 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
#if defined(__GLIBCXX__) // mingw
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::istream f(&wfile_buf);
|
||||
#elif defined(_MSC_VER)
|
||||
std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
|
||||
#else // clang?
|
||||
std::ifstream f(filepath.c_str(), std::ifstream::binary);
|
||||
#endif
|
||||
#else
|
||||
std::ifstream f(filepath.c_str(), std::ifstream::binary);
|
||||
#endif
|
||||
if (!f) {
|
||||
if (err) {
|
||||
(*err) += "File open error : " + filepath + "\n";
|
||||
@@ -2507,7 +2650,6 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
||||
out->resize(sz);
|
||||
f.read(reinterpret_cast<char *>(&out->at(0)),
|
||||
static_cast<std::streamsize>(sz));
|
||||
f.close();
|
||||
|
||||
return true;
|
||||
#endif
|
||||
@@ -2515,7 +2657,20 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
||||
|
||||
bool WriteWholeFile(std::string *err, const std::string &filepath,
|
||||
const std::vector<unsigned char> &contents, void *) {
|
||||
#ifdef _WIN32
|
||||
#if defined(__GLIBCXX__) // mingw
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(filepath).c_str(), _O_WRONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::ostream f(&wfile_buf);
|
||||
#elif defined(_MSC_VER)
|
||||
std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
|
||||
#else // clang?
|
||||
std::ofstream f(filepath.c_str(), std::ofstream::binary);
|
||||
#endif
|
||||
#else
|
||||
std::ofstream f(filepath.c_str(), std::ofstream::binary);
|
||||
#endif
|
||||
if (!f) {
|
||||
if (err) {
|
||||
(*err) += "File open error for writing : " + filepath + "\n";
|
||||
@@ -2532,7 +2687,6 @@ bool WriteWholeFile(std::string *err, const std::string &filepath,
|
||||
return false;
|
||||
}
|
||||
|
||||
f.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2675,6 +2829,7 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(syoyo): Allow empty buffer? #229
|
||||
if (data.empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -3455,6 +3610,7 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o,
|
||||
ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
|
||||
ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
|
||||
ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
|
||||
ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
|
||||
|
||||
ParseExtensionsProperty(&asset->extensions, err, o);
|
||||
|
||||
@@ -3593,7 +3749,10 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
|
||||
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
|
||||
return true;
|
||||
#endif
|
||||
if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
|
||||
std::string decoded_uri = dlib::urldecode(uri);
|
||||
if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
|
||||
/* required */ false, /* required bytes */ 0,
|
||||
/* checksize */ false, fs)) {
|
||||
if (warn) {
|
||||
(*warn) += "Failed to load external 'uri' for image[" +
|
||||
std::to_string(image_idx) + "] name = [" + image->name +
|
||||
@@ -3815,9 +3974,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
|
||||
}
|
||||
} else {
|
||||
// External .bin file.
|
||||
std::string decoded_uri = dlib::urldecode(buffer->uri);
|
||||
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
|
||||
buffer->uri, basedir, true, byteLength, true,
|
||||
fs)) {
|
||||
decoded_uri, basedir, /* required */ true,
|
||||
byteLength, /* checkSize */ true, fs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3859,8 +4019,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
|
||||
}
|
||||
} else {
|
||||
// Assume external .bin file.
|
||||
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
|
||||
basedir, true, byteLength, true, fs)) {
|
||||
std::string decoded_uri = dlib::urldecode(buffer->uri);
|
||||
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
|
||||
basedir, /* required */ true, byteLength,
|
||||
/* checkSize */ true, fs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -4698,6 +4860,13 @@ static bool ParseAnimationChannel(
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ParseExtensionsProperty(&channel->target_extensions, err, target_object);
|
||||
if (store_original_json_for_extras_and_extensions) {
|
||||
json_const_iterator it;
|
||||
if (FindMember(target_object, "extensions", it)) {
|
||||
channel->target_extensions_json_string = JsonToString(GetValue(it));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
channel->sampler = samplerIndex;
|
||||
@@ -5199,7 +5368,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
|
||||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
|
||||
defined(_CPPUNWIND)) && \
|
||||
not defined(TINYGLTF_NOEXCEPTION)
|
||||
!defined(TINYGLTF_NOEXCEPTION)
|
||||
try {
|
||||
JsonParse(v, json_str, json_str_length, true);
|
||||
|
||||
@@ -5472,7 +5641,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
|
||||
// Assign missing bufferView target types
|
||||
// - Look for missing Mesh indices
|
||||
// - Look for missing bufferView targets
|
||||
// - Look for missing Mesh attributes
|
||||
for (auto &mesh : model->meshes) {
|
||||
for (auto &primitive : mesh.primitives) {
|
||||
if (primitive.indices >
|
||||
@@ -5500,14 +5669,20 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
// we could optionally check if acessors' bufferView type is Scalar, as
|
||||
// it should be
|
||||
}
|
||||
}
|
||||
}
|
||||
// find any missing targets, must be an array buffer type if not fulfilled
|
||||
// from previous check
|
||||
for (auto &bufferView : model->bufferViews) {
|
||||
if (bufferView.target == 0) // missing target type
|
||||
{
|
||||
bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
||||
|
||||
for (auto &attribute : primitive.attributes) {
|
||||
model
|
||||
->bufferViews[size_t(
|
||||
model->accessors[size_t(attribute.second)].bufferView)]
|
||||
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
||||
}
|
||||
|
||||
for(auto &target : primitive.targets) {
|
||||
for(auto &attribute : target) {
|
||||
model->bufferViews[size_t(model->accessors[size_t(attribute.second)].bufferView)]
|
||||
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6225,18 +6400,45 @@ static void SerializeValue(const std::string &key, const Value &value,
|
||||
static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
|
||||
json &o) {
|
||||
std::string header = "data:application/octet-stream;base64,";
|
||||
std::string encodedData =
|
||||
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
|
||||
SerializeStringProperty("uri", header + encodedData, o);
|
||||
if (data.size() > 0) {
|
||||
std::string encodedData =
|
||||
base64_encode(&data[0], static_cast<unsigned int>(data.size()));
|
||||
SerializeStringProperty("uri", header + encodedData, o);
|
||||
} else {
|
||||
// Issue #229
|
||||
// size 0 is allowd. Just emit mime header.
|
||||
SerializeStringProperty("uri", header, o);
|
||||
}
|
||||
}
|
||||
|
||||
static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
|
||||
const std::string &binFilename) {
|
||||
#ifdef _WIN32
|
||||
#if defined(__GLIBCXX__) // mingw
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(binFilename).c_str(), _O_WRONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::ostream output(&wfile_buf);
|
||||
if (!wfile_buf.is_open()) return false;
|
||||
#elif defined(_MSC_VER)
|
||||
std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
|
||||
if (!output.is_open()) return false;
|
||||
#else
|
||||
std::ofstream output(binFilename.c_str(), std::ofstream::binary);
|
||||
if (!output.is_open()) return false;
|
||||
output.write(reinterpret_cast<const char *>(&data[0]),
|
||||
std::streamsize(data.size()));
|
||||
output.close();
|
||||
#endif
|
||||
#else
|
||||
std::ofstream output(binFilename.c_str(), std::ofstream::binary);
|
||||
if (!output.is_open()) return false;
|
||||
#endif
|
||||
if (data.size() > 0) {
|
||||
output.write(reinterpret_cast<const char *>(&data[0]),
|
||||
std::streamsize(data.size()));
|
||||
} else {
|
||||
// Issue #229
|
||||
// size 0 will be still valid buffer data.
|
||||
// write empty file.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6307,7 +6509,8 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) {
|
||||
SerializeNumberProperty<size_t>("count", accessor.count, o);
|
||||
SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
|
||||
SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
|
||||
SerializeValue("normalized", Value(accessor.normalized), o);
|
||||
if (accessor.normalized)
|
||||
SerializeValue("normalized", Value(accessor.normalized), o);
|
||||
std::string type;
|
||||
switch (accessor.type) {
|
||||
case TINYGLTF_TYPE_SCALAR:
|
||||
@@ -6348,6 +6551,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
|
||||
SerializeNumberProperty("node", channel.target_node, target);
|
||||
SerializeStringProperty("path", channel.target_path, target);
|
||||
|
||||
SerializeExtensionMap(channel.target_extensions, target);
|
||||
|
||||
JsonAddMember(o, "target", std::move(target));
|
||||
}
|
||||
|
||||
@@ -6387,6 +6592,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) {
|
||||
|
||||
{
|
||||
json samplers;
|
||||
JsonReserveArray(samplers, animation.samplers.size());
|
||||
for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
|
||||
json sampler;
|
||||
AnimationSampler gltfSampler = animation.samplers[i];
|
||||
@@ -6423,6 +6629,18 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
|
||||
SerializeExtensionMap(asset.extensions, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfBufferBin(Buffer &buffer, json &o,
|
||||
std::vector<unsigned char> &binBuffer) {
|
||||
SerializeNumberProperty("byteLength", buffer.data.size(), o);
|
||||
binBuffer = buffer.data;
|
||||
|
||||
if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
|
||||
|
||||
if (buffer.extras.Type() != NULL_TYPE) {
|
||||
SerializeValue("extras", buffer.extras, o);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfBuffer(Buffer &buffer, json &o) {
|
||||
SerializeNumberProperty("byteLength", buffer.data.size(), o);
|
||||
SerializeGltfBufferData(buffer.data, o);
|
||||
@@ -6481,6 +6699,7 @@ static void SerializeGltfImage(Image &image, json &o) {
|
||||
SerializeStringProperty("mimeType", image.mimeType, o);
|
||||
SerializeNumberProperty<int>("bufferView", image.bufferView, o);
|
||||
} else {
|
||||
// TODO(syoyo): dlib::urilencode?
|
||||
SerializeStringProperty("uri", image.uri, o);
|
||||
}
|
||||
|
||||
@@ -6597,7 +6816,8 @@ static void SerializeGltfMaterial(Material &material, json &o) {
|
||||
SerializeStringProperty("alphaMode", material.alphaMode, o);
|
||||
}
|
||||
|
||||
JsonAddMember(o, "doubleSided", json(material.doubleSided));
|
||||
if (material.doubleSided != false)
|
||||
JsonAddMember(o, "doubleSided", json(material.doubleSided));
|
||||
|
||||
if (material.normalTexture.index > -1) {
|
||||
json texinfo;
|
||||
@@ -6701,7 +6921,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) {
|
||||
JsonAddMember(primitive, "targets", std::move(targets));
|
||||
}
|
||||
|
||||
SerializeExtensionMap(gltfPrimitive.extensions, o);
|
||||
SerializeExtensionMap(gltfPrimitive.extensions, primitive);
|
||||
|
||||
if (gltfPrimitive.extras.Type() != NULL_TYPE) {
|
||||
SerializeValue("extras", gltfPrimitive.extras, primitive);
|
||||
@@ -6898,14 +7118,16 @@ static void SerializeGltfTexture(Texture &texture, json &o) {
|
||||
///
|
||||
static void SerializeGltfModel(Model *model, json &o) {
|
||||
// ACCESSORS
|
||||
json accessors;
|
||||
JsonReserveArray(accessors, model->accessors.size());
|
||||
for (unsigned int i = 0; i < model->accessors.size(); ++i) {
|
||||
json accessor;
|
||||
SerializeGltfAccessor(model->accessors[i], accessor);
|
||||
JsonPushBack(accessors, std::move(accessor));
|
||||
if (model->accessors.size()) {
|
||||
json accessors;
|
||||
JsonReserveArray(accessors, model->accessors.size());
|
||||
for (unsigned int i = 0; i < model->accessors.size(); ++i) {
|
||||
json accessor;
|
||||
SerializeGltfAccessor(model->accessors[i], accessor);
|
||||
JsonPushBack(accessors, std::move(accessor));
|
||||
}
|
||||
JsonAddMember(o, "accessors", std::move(accessors));
|
||||
}
|
||||
JsonAddMember(o, "accessors", std::move(accessors));
|
||||
|
||||
// ANIMATIONS
|
||||
if (model->animations.size()) {
|
||||
@@ -6928,14 +7150,16 @@ static void SerializeGltfModel(Model *model, json &o) {
|
||||
JsonAddMember(o, "asset", std::move(asset));
|
||||
|
||||
// BUFFERVIEWS
|
||||
json bufferViews;
|
||||
JsonReserveArray(bufferViews, model->bufferViews.size());
|
||||
for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
|
||||
json bufferView;
|
||||
SerializeGltfBufferView(model->bufferViews[i], bufferView);
|
||||
JsonPushBack(bufferViews, std::move(bufferView));
|
||||
if(model->bufferViews.size()) {
|
||||
json bufferViews;
|
||||
JsonReserveArray(bufferViews, model->bufferViews.size());
|
||||
for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
|
||||
json bufferView;
|
||||
SerializeGltfBufferView(model->bufferViews[i], bufferView);
|
||||
JsonPushBack(bufferViews, std::move(bufferView));
|
||||
}
|
||||
JsonAddMember(o, "bufferViews", std::move(bufferViews));
|
||||
}
|
||||
JsonAddMember(o, "bufferViews", std::move(bufferViews));
|
||||
|
||||
// Extensions used
|
||||
if (model->extensionsUsed.size()) {
|
||||
@@ -7090,28 +7314,58 @@ static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
|
||||
|
||||
static bool WriteGltfFile(const std::string &output,
|
||||
const std::string &content) {
|
||||
#ifdef _WIN32
|
||||
#if defined(_MSC_VER)
|
||||
std::ofstream gltfFile(UTF8ToWchar(output).c_str());
|
||||
#elif defined(__GLIBCXX__)
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::ostream gltfFile(&wfile_buf);
|
||||
if (!wfile_buf.is_open()) return false;
|
||||
#else
|
||||
std::ofstream gltfFile(output.c_str());
|
||||
if (!gltfFile.is_open()) return false;
|
||||
#endif
|
||||
#else
|
||||
std::ofstream gltfFile(output.c_str());
|
||||
if (!gltfFile.is_open()) return false;
|
||||
#endif
|
||||
return WriteGltfStream(gltfFile, content);
|
||||
}
|
||||
|
||||
static void WriteBinaryGltfStream(std::ostream &stream,
|
||||
const std::string &content) {
|
||||
const std::string &content,
|
||||
const std::vector<unsigned char> &binBuffer) {
|
||||
const std::string header = "glTF";
|
||||
const int version = 2;
|
||||
const int padding_size = content.size() % 4;
|
||||
|
||||
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info,
|
||||
// padding
|
||||
const int length = 12 + 8 + int(content.size()) + padding_size;
|
||||
// https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
|
||||
auto roundUp = [](uint32_t numToRound, uint32_t multiple) {
|
||||
if (multiple == 0) return numToRound;
|
||||
|
||||
uint32_t remainder = numToRound % multiple;
|
||||
if (remainder == 0) return numToRound;
|
||||
|
||||
return numToRound + multiple - remainder;
|
||||
};
|
||||
|
||||
const uint32_t padding_size =
|
||||
roundUp(uint32_t(content.size()), 4) - uint32_t(content.size());
|
||||
|
||||
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
|
||||
// Chunk data must be located at 4-byte boundary.
|
||||
const uint32_t length =
|
||||
12 + 8 + roundUp(uint32_t(content.size()), 4) +
|
||||
(binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 0);
|
||||
|
||||
stream.write(header.c_str(), std::streamsize(header.size()));
|
||||
stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
|
||||
stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
|
||||
|
||||
// JSON chunk info, then JSON data
|
||||
const int model_length = int(content.size()) + padding_size;
|
||||
const int model_format = 0x4E4F534A;
|
||||
const uint32_t model_length = uint32_t(content.size()) + padding_size;
|
||||
const uint32_t model_format = 0x4E4F534A;
|
||||
stream.write(reinterpret_cast<const char *>(&model_length),
|
||||
sizeof(model_length));
|
||||
stream.write(reinterpret_cast<const char *>(&model_format),
|
||||
@@ -7123,12 +7377,46 @@ static void WriteBinaryGltfStream(std::ostream &stream,
|
||||
const std::string padding = std::string(size_t(padding_size), ' ');
|
||||
stream.write(padding.c_str(), std::streamsize(padding.size()));
|
||||
}
|
||||
if (binBuffer.size() > 0) {
|
||||
const uint32_t bin_padding_size =
|
||||
roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size());
|
||||
// BIN chunk info, then BIN data
|
||||
const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
|
||||
const uint32_t bin_format = 0x004e4942;
|
||||
stream.write(reinterpret_cast<const char *>(&bin_length),
|
||||
sizeof(bin_length));
|
||||
stream.write(reinterpret_cast<const char *>(&bin_format),
|
||||
sizeof(bin_format));
|
||||
stream.write(reinterpret_cast<const char *>(binBuffer.data()),
|
||||
std::streamsize(binBuffer.size()));
|
||||
// Chunksize must be multiplies of 4, so pad with zeroes
|
||||
if (bin_padding_size > 0) {
|
||||
const std::vector<unsigned char> padding =
|
||||
std::vector<unsigned char>(size_t(bin_padding_size), 0);
|
||||
stream.write(reinterpret_cast<const char *>(padding.data()),
|
||||
std::streamsize(padding.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteBinaryGltfFile(const std::string &output,
|
||||
const std::string &content) {
|
||||
const std::string &content,
|
||||
const std::vector<unsigned char> &binBuffer) {
|
||||
#ifdef _WIN32
|
||||
#if defined(_MSC_VER)
|
||||
std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
|
||||
#elif defined(__GLIBCXX__)
|
||||
int file_descriptor =
|
||||
_wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
|
||||
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
|
||||
std::ostream gltfFile(&wfile_buf);
|
||||
#else
|
||||
std::ofstream gltfFile(output.c_str(), std::ios::binary);
|
||||
WriteBinaryGltfStream(gltfFile, content);
|
||||
#endif
|
||||
#else
|
||||
std::ofstream gltfFile(output.c_str(), std::ios::binary);
|
||||
#endif
|
||||
WriteBinaryGltfStream(gltfFile, content, binBuffer);
|
||||
}
|
||||
|
||||
bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
||||
@@ -7140,15 +7428,21 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
||||
SerializeGltfModel(model, output);
|
||||
|
||||
// BUFFERS
|
||||
std::vector<std::string> usedUris;
|
||||
json buffers;
|
||||
JsonReserveArray(buffers, model->buffers.size());
|
||||
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
|
||||
json buffer;
|
||||
SerializeGltfBuffer(model->buffers[i], buffer);
|
||||
JsonPushBack(buffers, std::move(buffer));
|
||||
std::vector<unsigned char> binBuffer;
|
||||
if(model->buffers.size()) {
|
||||
json buffers;
|
||||
JsonReserveArray(buffers, model->buffers.size());
|
||||
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
|
||||
json buffer;
|
||||
if (writeBinary && i==0 && model->buffers[i].uri.empty()){
|
||||
SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer);
|
||||
} else {
|
||||
SerializeGltfBuffer(model->buffers[i], buffer);
|
||||
}
|
||||
JsonPushBack(buffers, std::move(buffer));
|
||||
}
|
||||
JsonAddMember(output, "buffers", std::move(buffers));
|
||||
}
|
||||
JsonAddMember(output, "buffers", std::move(buffers));
|
||||
|
||||
// IMAGES
|
||||
if (model->images.size()) {
|
||||
@@ -7170,7 +7464,7 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
|
||||
}
|
||||
|
||||
if (writeBinary) {
|
||||
WriteBinaryGltfStream(stream, JsonToString(output));
|
||||
WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
|
||||
} else {
|
||||
WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
|
||||
}
|
||||
@@ -7201,42 +7495,47 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
|
||||
|
||||
// BUFFERS
|
||||
std::vector<std::string> usedUris;
|
||||
json buffers;
|
||||
JsonReserveArray(buffers, model->buffers.size());
|
||||
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
|
||||
json buffer;
|
||||
if (embedBuffers) {
|
||||
SerializeGltfBuffer(model->buffers[i], buffer);
|
||||
} else {
|
||||
std::string binSavePath;
|
||||
std::string binUri;
|
||||
if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
|
||||
binUri = model->buffers[i].uri;
|
||||
std::vector<unsigned char> binBuffer;
|
||||
if (model->buffers.size()) {
|
||||
json buffers;
|
||||
JsonReserveArray(buffers, model->buffers.size());
|
||||
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
|
||||
json buffer;
|
||||
if (writeBinary && i==0 && model->buffers[i].uri.empty()){
|
||||
SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer);
|
||||
} else if (embedBuffers) {
|
||||
SerializeGltfBuffer(model->buffers[i], buffer);
|
||||
} else {
|
||||
binUri = defaultBinFilename + defaultBinFileExt;
|
||||
bool inUse = true;
|
||||
int numUsed = 0;
|
||||
while (inUse) {
|
||||
inUse = false;
|
||||
for (const std::string &usedName : usedUris) {
|
||||
if (binUri.compare(usedName) != 0) continue;
|
||||
inUse = true;
|
||||
binUri = defaultBinFilename + std::to_string(numUsed++) +
|
||||
defaultBinFileExt;
|
||||
break;
|
||||
std::string binSavePath;
|
||||
std::string binUri;
|
||||
if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
|
||||
binUri = model->buffers[i].uri;
|
||||
} else {
|
||||
binUri = defaultBinFilename + defaultBinFileExt;
|
||||
bool inUse = true;
|
||||
int numUsed = 0;
|
||||
while (inUse) {
|
||||
inUse = false;
|
||||
for (const std::string &usedName : usedUris) {
|
||||
if (binUri.compare(usedName) != 0) continue;
|
||||
inUse = true;
|
||||
binUri = defaultBinFilename + std::to_string(numUsed++) +
|
||||
defaultBinFileExt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
usedUris.push_back(binUri);
|
||||
binSavePath = JoinPath(baseDir, binUri);
|
||||
if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
|
||||
binUri)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
usedUris.push_back(binUri);
|
||||
binSavePath = JoinPath(baseDir, binUri);
|
||||
if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
|
||||
binUri)) {
|
||||
return false;
|
||||
}
|
||||
JsonPushBack(buffers, std::move(buffer));
|
||||
}
|
||||
JsonPushBack(buffers, std::move(buffer));
|
||||
}
|
||||
JsonAddMember(output, "buffers", std::move(buffers));
|
||||
}
|
||||
|
||||
// IMAGES
|
||||
if (model->images.size()) {
|
||||
@@ -7254,7 +7553,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
|
||||
}
|
||||
|
||||
if (writeBinary) {
|
||||
WriteBinaryGltfFile(filename, JsonToString(output));
|
||||
WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
|
||||
} else {
|
||||
WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user