Compare commits

..

43 Commits

Author SHA1 Message Date
Syoyo Fujita
fed3f31e95 Implement iterator for SAJSON backend(W.I.P). 2020-06-11 21:57:54 +09:00
Syoyo Fujita
6fcee26d0f Merge branch 'separate-serializer' into sajson 2020-06-08 16:47:46 +09:00
Syoyo Fujita
bafde6a53e Use TINYGLTF_ENABLE_SERIALIZER ifdef to enable/disable serialization code. 2020-06-08 16:46:33 +09:00
Syoyo Fujita
3d939fd3ee Add comment on tinygltf::ExpandFilePath() 2020-06-06 18:13:15 +09:00
Syoyo Fujita
fbbeb4d6a9 Use nullptr instead of NULL. 2020-06-06 17:11:50 +09:00
Syoyo Fujita
cef1787ef8 Add regression test for PR-266. 2020-06-06 17:10:49 +09:00
cwbhhjl
2f5aa9f13b fix an error occurred while expanding utf8 path 2020-06-04 10:40:38 +08:00
Syoyo Fujita
28bafe4b11 Add GitHub Actions badge. 2020-05-30 17:06:45 +09:00
Syoyo Fujita
34894a6325 Build validator for MSVC build. 2020-05-30 02:28:58 +09:00
Syoyo Fujita
2e7006797b Separrate build of examples. 2020-05-30 02:27:25 +09:00
Syoyo Fujita
d9c03d041a Build examples for VS2019 build job. 2020-05-30 02:21:07 +09:00
Syoyo Fujita
50a3061ddf Fix path to rapidjson headers 2020-05-30 02:19:47 +09:00
Syoyo Fujita
014dce27f4 Add VS2019 build job. 2020-05-30 02:16:41 +09:00
Syoyo Fujita
c046769054 Add mingw build job 2020-05-30 02:12:48 +09:00
Syoyo Fujita
c43266fdc9 Add argument to loader_example run 2020-05-30 02:07:00 +09:00
Syoyo Fujita
aca196e8d3 add aarch64 cross compile and macos build job. 2020-05-30 02:04:06 +09:00
Syoyo Fujita
b48c027c9a Fix yaml syntax. 2020-05-30 02:00:03 +09:00
Syoyo Fujita
db5c3bea45 Add gcc-4.8 build job. 2020-05-30 01:59:02 +09:00
Syoyo Fujita
ddfa1f2f0b Fix workflow 2020-05-30 01:56:35 +09:00
Syoyo Fujita
7b69c778fe Add rapidjson build job 2020-05-29 22:21:24 +09:00
Syoyo Fujita
73e73bf3c1 Create github actions workflow 2020-05-29 20:57:39 +09:00
Syoyo Fujita
35d664e417 Merge pull request #265 from ivovandongen/ivd_rapidjson_size_t
cast size_t to uint_64_t for rapidjson serialization
2020-05-29 18:19:07 +09:00
Ivo van Dongen
272a9dfa5f cast size_t to uint_64_t for rapidjson serialization 2020-05-29 11:24:11 +03:00
Syoyo
f3bf6ee78e Add TDME2 2020-05-22 01:25:03 +09:00
Syoyo Fujita
aba57bb907 Support llvm-mingw(clang + libcxx) to open UTF-8 path in ifstream. 2020-05-18 20:50:45 +09:00
Syoyo Fujita
6fad7adb9c Fix existing "extensions" were overwritten in serialization when the scene contains lights(serialized as "KHR_light_punctual")
Append "KHR_light_punctual" to `extensionsUsed` if not exist in serialization.
Apply clang-format.
Fixes #261
2020-05-15 21:32:06 +09:00
Syoyo Fujita
3dad303bea Only serialize light.range when range > 0. Fixes #260 2020-05-13 21:22:23 +09:00
Syoyo Fujita
ff0a2e9fb4 Return type must be int for GetNumberAsInt(). Fixes #258 2020-05-11 19:07:00 +09:00
Syoyo
fe77cc5cdd Serialize extension and extras for Camera. Fixes #257 2020-05-09 02:41:07 +09:00
Syoyo Fujita
b8c04b0a9d sajson experiment(W.I.P.) 2020-05-05 03:22:22 +09:00
Syoyo Fujita
18f0e20a11 Fix float and int comparison. 2020-04-29 19:16:35 +09:00
Syoyo Fujita
978adee547 Include cmath for std::fabs 2020-04-29 17:33:48 +09:00
Syoyo Fujita
73c4cce303 Return false for zero-sized asset. Fixes #255 2020-04-28 01:06:34 +09:00
Syoyo Fujita
925b83627a Merge pull request #254 from LHLaurini/master
Fix write functions so they work with MinGW
2020-04-14 01:40:00 +09:00
Luiz Henrique Laurini
92c23725ae Fix write functions so they work with MinGW 2020-04-12 16:00:33 -03:00
Syoyo Fujita
af750a5104 Merge pull request #252 from freddiehonohan/master
Fixed small error if node has no mesh in examples/basic.
2020-04-10 02:04:12 +09:00
root
ff48fa0ff4 Fixed small error if node has no mesh. 2020-04-09 10:42:41 -04:00
Syoyo Fujita
ec45334d3d Merge branch 'master' of github.com:syoyo/tinygltf 2020-04-06 22:12:39 +09:00
Syoyo Fujita
5f603c56dc Do not segfault when the scene does not contain texture images. Fixes #251 2020-04-06 22:11:16 +09:00
Syoyo Fujita
063b8586f1 Merge pull request #250 from FsiGuy00015623/prevent-cloning-buffer-images
Prevent cloning of images in buffers
2020-03-10 14:53:13 +09:00
FsiGuy00015623
db855c6794 Prevent cloning of images in buffers
Now, in UpdateImageObject, if an image has no URI but had a valid bufferView, it will no longer change the image object or copy the buffer object to an outside file at baseDir. 
(When saving files from streams, this function would previously save textures in buffers as "#.png" in the program's working directory instead of the model file's directory, and create a broken link in the updated image object if the two locations were different.)
2020-03-09 16:57:21 -05:00
Syoyo Fujita
e391f6b03e Merge pull request #249 from CesiumGS/accessor-index-fix
Ensures only valid bufferViews are mentioned in accessor
2020-03-05 23:59:23 +09:00
Sanjeet Suhag
5ecede71f0 Ensures only valid bufferViews are mentioned in accessor 2020-03-04 17:40:10 -05:00
17 changed files with 1437 additions and 4258 deletions

165
.github/workflows/c-cpp.yml vendored Normal file
View File

@@ -0,0 +1,165 @@
name: C/C++ CI
on: [push, pull_request]
jobs:
# compile with older gcc4.8
build-gcc48:
runs-on: ubuntu-18.04
name: Build with gcc 4.8
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: |
sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y gcc-4.8 g++-4.8
g++-4.8 -std=c++11 -o loader_example loader_example.cc
- name: NoexceptBuild
run: |
g++-4.8 -DTINYGLTF_NOEXCEPTION -std=c++11 -o loader_example loader_example.cc
- name: RapidjsonBuild
run: |
git clone https://github.com/Tencent/rapidjson
g++-4.8 -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -o loader_example loader_example.cc
# compile with mingw gcc cross
build-mingw-cross:
runs-on: ubuntu-18.04
name: Build with MinGW gcc cross
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: |
sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y mingw-w64
x86_64-w64-mingw32-g++ -std=c++11 -o loader_example loader_example.cc
# Windows(x64) + Visual Studio 2019 build
build-windows-msvc:
runs-on: windows-latest
name: Build for Windows(x64, MSVC)
# Use system installed cmake
# https://help.github.com/en/actions/reference/software-installed-on-github-hosted-runners
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Configure
run: |
mkdir build
cd build
cmake -G "Visual Studio 16 2019" -DTINYGLTF_BUILD_LOADER_EXAMPLE=On -DTINYGLTF_BUILD_GL_EXAMPLES=Off -DTINYGLTF_BUILD_VALIDATOR_EXAMPLE=On ..
cd ..
- name: Build
run: cmake --build build --config Release
build-linux:
runs-on: ubuntu-latest
name: Buld with gcc
steps:
- uses: actions/checkout@v2
- name: build
run: |
g++ -std=c++11 -o loader_example loader_example.cc
- name: test
run: |
./loader_example models/Cube/Cube.gltf
- name: tests
run: |
cd tests
g++ -I../ -std=c++11 -g -O0 -o tester tester.cc
./tester
cd ..
- name: noexcept_tests
run: |
cd tests
g++ -DTINYGLTF_NOEXCEPTION -I../ -std=c++11 -g -O0 -o tester_noexcept tester.cc
./tester_noexcept
cd ..
build-rapidjson-linux:
runs-on: ubuntu-latest
name: Buld with gcc + rapidjson
steps:
- uses: actions/checkout@v2
- name: build
run: |
git clone https://github.com/Tencent/rapidjson
g++ -v
g++ -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -o loader_example loader_example.cc
- name: loader_example_test
run: |
./loader_example models/Cube/Cube.gltf
- name: tests
run: |
cd tests
g++ -DTINYGLTF_USE_RAPIDJSON -I../rapidjson/include/rapidjson -I../ -std=c++11 -g -O0 -o tester tester.cc
./tester
cd ..
- name: noexcept_tests
run: |
cd tests
g++ -DTINYGLTF_USE_RAPIDJSON -I../rapidjson/include/rapidjson -DTINYGLTF_NOEXCEPTION -I../ -std=c++11 -g -O0 -o tester_noexcept tester.cc
./tester_noexcept
cd ..
# Cross-compile for aarch64 linux target
build-cross-aarch64:
runs-on: ubuntu-18.04
name: Build on cross aarch64
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: |
sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y gcc-8-aarch64-linux-gnu g++-8-aarch64-linux-gnu
git clone https://github.com/Tencent/rapidjson
aarch64-linux-gnu-g++-8 -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -g -O0 -o loader_example loader_example.cc
# macOS clang
build-macos:
runs-on: macos-latest
name: Build on macOS
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: |
clang++ -std=c++11 -g -O0 -o loader_example loader_example.cc
./loader_example models/Cube/Cube.gltf
git clone https://github.com/Tencent/rapidjson
clang++ -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -g -O0 -o loader_example loader_example.cc

View File

@@ -4,18 +4,24 @@ PROJECT (tinygltf)
SET(CMAKE_CXX_STANDARD 11)
option(TINYGLTF_BUILD_EXAMPLES "Build examples" ON)
option(TINYGLTF_BUILD_LOADER_EXAMPLE "Build loader_example" ON)
option(TINYGLTF_BUILD_GL_EXAMPLES "Build GL exampels(requires glfw, OpenGL, etc)" OFF)
option(TINYGLTF_BUILD_VALIDATOR_EXAMPLE "Build validator exampe" OFF)
if (TINYGLTF_BUILD_EXAMPLES)
if (TINYGLTF_BUILD_LOADER_EXAMPLE)
ADD_EXECUTABLE ( loader_example
loader_example.cc
)
endif (TINYGLTF_BUILD_LOADER_EXAMPLE)
if (TINYGLTF_BUILD_GL_EXAMPLES)
ADD_SUBDIRECTORY ( examples/gltfutil )
ADD_SUBDIRECTORY ( examples/glview )
ADD_SUBDIRECTORY ( examples/validator )
endif (TINYGLTF_BUILD_EXAMPLES)
endif (TINYGLTF_BUILD_GL_EXAMPLES)
if (TINYGLTF_BUILD_VALIDATOR_EXAMPLE)
ADD_SUBDIRECTORY ( examples/validator )
endif (TINYGLTF_BUILD_VALIDATOR_EXAMPLE)
#
# TinuGLTF is a header-only library, so no library build. just install header files.
#

View File

@@ -19,6 +19,8 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
[![Build status](https://ci.appveyor.com/api/projects/status/warngenu9wjjhlm8?svg=true)](https://ci.appveyor.com/project/syoyo/tinygltf)
![C/C++ CI](https://github.com/syoyo/tinygltf/workflows/C/C++%20CI/badge.svg)
## Features
* Written in portable C++. C++-11 with STL dependency only.
@@ -83,6 +85,7 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
* [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.
* [TDME2](https://github.com/andreasdr/tdme2) - TDME2 - ThreeDeeMiniEngine2 is a lightweight 3D engine including tools suited for 3D game development using C++11
* Your projects here! (Please send PR)
## TODOs
@@ -150,6 +153,7 @@ if (!ret) {
## Compile options
* `TINYGLTF_ENABLE_SERIALIZER` : Enable glTF serialization feature.
* `TINYGLTF_NOEXCEPTION` : Disable C++ exception in JSON parsing. You can use `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION` and `TINYGLTF_NOEXCEPTION` to fully remove C++ exception codes when compiling TinyGLTF.
* `TINYGLTF_NO_STB_IMAGE` : Do not load images with stb_image. Instead use `TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)` to set a callback for loading images.
* `TINYGLTF_NO_STB_IMAGE_WRITE` : Do not write images with stb_image_write. Instead use `TinyGLTF::SetImageWriter(WriteimageDataFunction WriteImageData, void *user_data)` to set a callback for writing images.

View File

@@ -19,3 +19,7 @@ $ make
Plese use solution file located at `basic` folder.
## Limitation
There are so many limitations in this example(e.g. no PBR shader. the shader only shows texture of textures[0] if available).

View File

@@ -100,42 +100,49 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
std::cout << "vaa missing: " << attrib.first << std::endl;
}
GLuint texid;
glGenTextures(1, &texid);
if (model.textures.size() > 0) {
// fixme: Use material's baseColor
tinygltf::Texture &tex = model.textures[0];
tinygltf::Texture &tex = model.textures[0];
tinygltf::Image &image = model.images[tex.source];
if (tex.source > -1) {
glBindTexture(GL_TEXTURE_2D, texid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GLuint texid;
glGenTextures(1, &texid);
GLenum format = GL_RGBA;
tinygltf::Image &image = model.images[tex.source];
if (image.component == 1) {
format = GL_RED;
} else if (image.component == 2) {
format = GL_RG;
} else if (image.component == 3) {
format = GL_RGB;
} else {
// ???
glBindTexture(GL_TEXTURE_2D, texid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
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;
} else {
// ???
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
format, type, &image.image.at(0));
}
}
GLenum type = GL_UNSIGNED_BYTE;
if (image.bits == 8) {
// ok
} else if (image.bits == 16) {
type = GL_UNSIGNED_SHORT;
} else {
// ???
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
format, type, &image.image.at(0));
}
return vbos;
@@ -144,8 +151,12 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
// bind models
void bindModelNodes(std::map<int, GLuint> vbos, tinygltf::Model &model,
tinygltf::Node &node) {
bindMesh(vbos, model, model.meshes[node.mesh]);
if ((node.mesh >= 0) && (node.mesh < model.meshes.size())) {
bindMesh(vbos, model, model.meshes[node.mesh]);
}
for (size_t i = 0; i < node.children.size(); i++) {
assert((node.children[i] >= 0) && (node.children[i] < model.nodes.size()));
bindModelNodes(vbos, model, model.nodes[node.children[i]]);
}
}
@@ -157,6 +168,7 @@ GLuint bindModel(tinygltf::Model &model) {
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
for (size_t i = 0; i < scene.nodes.size(); ++i) {
assert((scene.nodes[i] >= 0) && (scene.nodes[i] < model.nodes.size()));
bindModelNodes(vbos, model, model.nodes[scene.nodes[i]]);
}
@@ -182,7 +194,9 @@ void drawMesh(tinygltf::Model &model, tinygltf::Mesh &mesh) {
// recursively draw node and children nodes of model
void drawModelNodes(tinygltf::Model &model, tinygltf::Node &node) {
drawMesh(model, model.meshes[node.mesh]);
if ((node.mesh >= 0) && (node.mesh < model.meshes.size())) {
drawMesh(model, model.meshes[node.mesh]);
}
for (size_t i = 0; i < node.children.size(); i++) {
drawModelNodes(model, model.nodes[node.children[i]]);
}

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-modify mesh-modify.cc mesh-util.cc tinygltf_impl.cc

View File

@@ -1,13 +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.
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`)

View File

@@ -1,760 +0,0 @@
#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
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,76 +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 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

View File

@@ -1,7 +0,0 @@
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)

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;
}

Binary file not shown.

View File

@@ -0,0 +1,376 @@
{
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 8,
"max": [
0.5,
0.5,
0.5
],
"min": [
-0.5,
-0.5,
-0.5
],
"type": "VEC3"
},
{
"bufferView": 1,
"componentType": 5125,
"count": 36,
"type": "SCALAR"
}
],
"asset": {
"copyright": "NVIDIA Corporation",
"generator": "Iray glTF plugin",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 96,
"byteStride": 12
},
{
"buffer": 0,
"byteLength": 144,
"byteOffset": 96
}
],
"buffers": [
{
"byteLength": 240,
"uri": "issue-261.bin"
}
],
"cameras": [
{
"extensions": {
"NV_Iray": {
"mip_burn_highlights": 0.699999988079071,
"mip_burn_highlights_per_component": false,
"mip_camera_shutter": 4.0,
"mip_cm2_factor": 1.0,
"mip_crush_blacks": 0.5,
"mip_f_number": 1.0,
"mip_film_iso": 100.0,
"mip_gamma": 2.200000047683716,
"mip_saturation": 1.0,
"mip_vignetting": 0.00019999999494757503,
"mip_whitepoint": [
1.0,
1.0,
1.0,
1.0
],
"tm_enable_tonemapper": true,
"tm_tonemapper": "mia_exposure_photographic"
}
},
"extras": {
"resolution": [
640,
480
]
},
"name": "default",
"perspective": {
"aspectRatio": 1.3333333730697632,
"yfov": 0.9272952079772949,
"zfar": 1000.0,
"znear": 0.1
},
"type": "perspective"
}
],
"extensions": {
"KHR_lights_punctual": {
"lights": [
{
"color": [
1.0,
1.0,
1.0
],
"intensity": 1000.0,
"name": "light",
"type": "point"
}
]
},
"NV_MDL": {
"modules": [
"mdl::base",
"mdl::nvidia::core_definitions",
"mdl::state"
],
"shaders": [
{
"definition": "mdl::base::environment_spherical(texture_2d)",
"name": "env_shd"
},
{
"arguments": {
"base_color": [
0.0055217444896698,
0.36859095096588135,
0.0041161770932376385
],
"normal=": 2
},
"definition": "mdl::nvidia::core_definitions::flex_material",
"name": "cube_instance_material"
},
{
"definition": "mdl::state::normal()",
"name": "mdl::state::normal_341"
},
{
"arguments": {
"base_color": [
0.0055217444896698,
0.36859095096588135,
0.0041161770932376385
],
"normal=": 4
},
"definition": "mdl::nvidia::core_definitions::flex_material",
"name": "cube_instance_material"
},
{
"definition": "mdl::state::normal()",
"name": "mdl::state::normal_341"
}
]
}
},
"extensionsUsed": [
"NV_MDL",
"NV_Iray",
"KHR_lights_punctual"
],
"materials": [
{
"doubleSided": true,
"extras": {
"mdl_shader": 1
},
"name": "cube_instance_material",
"pbrMetallicRoughness": {
"baseColorFactor": [
0.0055217444896698,
0.36859095096588135,
0.0041161770932376385,
1.0
]
}
}
],
"meshes": [
{
"name": "cube",
"primitives": [
{
"attributes": {
"POSITION": 0
},
"indices": 1,
"material": 0,
"mode": 4
}
]
}
],
"nodes": [
{
"camera": 0,
"extensions": {
"NV_Iray": {
"iview:fkey": -1,
"iview:fov": 53.130104064941406,
"iview:interest": [
0.1342654824256897,
-0.14356137812137604,
0.037264324724674225
],
"iview:position": [
0.9729073643684387,
1.2592438459396362,
2.4199187755584717
],
"iview:roll": 0.0,
"iview:up": [
0.0,
1.0,
0.0
]
}
},
"matrix": [
0.9432751389105357,
-1.1769874756875739e-16,
-0.3320120665176343,
0.0,
-0.16119596696756341,
0.8742297945345237,
-0.45797175303889276,
0.0,
0.290254840694694,
0.48551237507207207,
0.8246392308792816,
0.0,
0.9729073377709113,
1.2592438262175363,
2.419918748461627,
1.0
],
"name": "CamInst"
},
{
"extensions": {
"NV_Iray": {
"caustic": true,
"caustic_cast": true,
"caustic_recv": true,
"face_back": true,
"face_front": true,
"finalgather": true,
"finalgather_cast": true,
"finalgather_recv": true,
"globillum": true,
"globillum_cast": true,
"globillum_recv": true,
"material=": 3,
"pickable": true,
"reflection_cast": true,
"reflection_recv": true,
"refraction_cast": true,
"refraction_recv": true,
"shadow_cast": true,
"shadow_recv": true,
"transparency_cast": true,
"transparency_recv": true,
"visible": true
}
},
"mesh": 0,
"name": "cube_instance"
},
{
"extensions": {
"KHR_lights_punctual": {
"light": 0
},
"NV_Iray": {
"shadow_cast": true,
"visible": false
}
},
"matrix": [
0.6988062355563571,
-7.76042172309776e-17,
-0.7153110128800992,
-0.0,
-0.4276881690736487,
0.8015668284138362,
-0.41781987700564616,
-0.0,
0.57336957992379,
0.5979051928078428,
0.5601398979107212,
0.0,
2.3640632834071384,
2.465226204472449,
2.309515908392224,
1.0
],
"name": "light_inst"
}
],
"scene": 0,
"scenes": [
{
"extensions": {
"NV_Iray": {
"CP_canny_threshold1": 40,
"CP_canny_threshold2": 120,
"CP_color_quantization": 8,
"IVP_color": [
1.0,
0.0,
0.0,
1.0
],
"TM_drago_bias": 0.8500000238418579,
"TM_drago_gamma2": 2.200000047683716,
"TM_drago_saturation": 1.0,
"TM_durand_contrast": 4.0,
"TM_durand_gamma": 2.200000047683716,
"TM_durand_saturation": 1.0,
"TM_durand_sigma_color": 2.0,
"TM_durand_sigma_space": 2.0,
"TM_linear_gamma": 2.200000047683716,
"TM_reinhard_color_adapt": 0.8999999761581421,
"TM_reinhard_gamma": 1.0,
"TM_reinhard_intensity": 0.0,
"TM_reinhard_light_adapt": 1.0,
"TM_reye_Ywhite": 1000000.0,
"TM_reye_bsize": 2,
"TM_reye_bthres": 3.0,
"TM_reye_gamma": 2.200000047683716,
"TM_reye_key": 0.5,
"TM_reye_saturation": 1.0,
"TM_reye_whitebalance": [
1.0,
0.9965101480484009,
0.9805564880371094,
0.0
],
"environment_dome_depth": 200.0,
"environment_dome_height": 200.0,
"environment_dome_mode": "infinite",
"environment_dome_position": [
0.0,
0.0,
0.0
],
"environment_dome_radius": 100.0,
"environment_dome_rotation_angle": 0.0,
"environment_dome_width": 200.0,
"environment_function=": 0,
"environment_function_intensity": 1.9900000095367432,
"iray_instancing": "off",
"iview::inline_color": [
1.0,
0.0,
0.0,
1.0
],
"iview::inline_width": 1.0,
"iview::magnifier_size": 300,
"iview::offset": 10.0,
"iview::outline_color": [
0.0,
0.0,
0.0,
1.0
],
"iview::outline_width": 2.0,
"iview::overview": true,
"iview::zoom_factor": 1.0,
"samples": 4.0,
"shadow_cast": true,
"shadow_recv": true
}
},
"nodes": [
0,
1,
2
]
}
]
}

View File

@@ -93,6 +93,59 @@ TEST_CASE("extension-with-empty-object", "[issue-97]") {
}
TEST_CASE("extension-overwrite", "[issue-261]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/Extensions-overwrite-issue261/issue-261.gltf");
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(model.extensionsUsed.size() == 3);
{
bool has_ext_lights = false;
has_ext_lights |= (model.extensionsUsed[0].compare("KHR_lights_punctual") == 0);
has_ext_lights |= (model.extensionsUsed[1].compare("KHR_lights_punctual") == 0);
has_ext_lights |= (model.extensionsUsed[2].compare("KHR_lights_punctual") == 0);
REQUIRE(true == has_ext_lights);
}
{
REQUIRE(model.extensions.size() == 2);
REQUIRE(model.extensions.count("NV_MDL"));
REQUIRE(model.extensions.count("KHR_lights_punctual"));
}
// TODO(syoyo): create temp directory.
{
ret = ctx.WriteGltfSceneToFile(&model, "issue-261.gltf", true, true);
REQUIRE(true == ret);
tinygltf::Model m;
// read back serialized glTF
bool ret = ctx.LoadASCIIFromFile(&m, &err, &warn, "issue-261.gltf");
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(m.extensionsUsed.size() == 3);
REQUIRE(m.extensions.size() == 2);
REQUIRE(m.extensions.count("NV_MDL"));
REQUIRE(m.extensions.count("KHR_lights_punctual"));
}
}
TEST_CASE("invalid-primitive-indices", "[bounds-checking]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
@@ -359,3 +412,19 @@ TEST_CASE("image-uri-spaces", "[issue-236]") {
REQUIRE(true == ret);
}
#ifndef TINYGLTF_NO_FS
TEST_CASE("expandpath-utf-8", "[pr-226]") {
std::string s1 = "\xe5\xaf\xb9"; // utf-8 string
std::string ret = tinygltf::ExpandFilePath(s1, /* userdata */nullptr);
// expected: E5 AF B9
REQUIRE(3 == ret.size());
REQUIRE(0xe5 == static_cast<uint8_t>(ret[0]));
REQUIRE(0xaf == static_cast<uint8_t>(ret[1]));
REQUIRE(0xb9 == static_cast<uint8_t>(ret[2]));
}
#endif

View File

@@ -26,6 +26,8 @@
// THE SOFTWARE.
// Version:
// - v2.4.3 Experimental sajson(lightweight JSON parser) backend support.
// Introduce TINYGLTF_ENABLE_SERIALIZER.
// - v2.4.2 Decode percent-encoded URI.
// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
// `extras` property.
@@ -43,6 +45,7 @@
//
// Tiny glTF loader is using following third party libraries:
//
// - sajso: Lightweight C++ JSON library.
// - jsonhpp: C++ JSON library.
// - base64: base64 decode/encode library.
// - stb_image: Image loading library.
@@ -52,6 +55,7 @@
#include <array>
#include <cassert>
#include <cmath> // std::fabs
#include <cstdint>
#include <cstdlib>
#include <cstring>
@@ -322,7 +326,8 @@ class Value {
}
// Use this function if you want to have number value as int.
double GetNumberAsInt() const {
// TODO(syoyo): Support int value larger than 32 bits
int GetNumberAsInt() const {
if (type_ == REAL_TYPE) {
return int(real_value_);
} else {
@@ -1113,9 +1118,9 @@ struct SpotLight {
struct Light {
std::string name;
std::vector<double> color;
double intensity;
double intensity{1.0};
std::string type;
double range;
double range{0.0}; // 0.0 = inifinite
SpotLight spot;
Light() : intensity(1.0), range(0.0) {}
@@ -1247,7 +1252,13 @@ struct FsCallbacks {
bool FileExists(const std::string &abs_filename, void *);
std::string ExpandFilePath(const std::string &filepath, void *);
///
/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to `C:\Users\tinygltf\AppData`)
///
/// @param[in] filepath File path string. Assume UTF-8
/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
///
std::string ExpandFilePath(const std::string &filepath, void *userdata);
bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
const std::string &filepath, void *);
@@ -1315,6 +1326,8 @@ class TinyGLTF {
const std::string &base_dir = "",
unsigned int check_sections = REQUIRE_VERSION);
#if defined(TINYGLTF_ENABLE_SERIALIZER)
///
/// Write glTF to stream, buffers and images will be embeded
///
@@ -1328,16 +1341,22 @@ class TinyGLTF {
bool embedImages, bool embedBuffers,
bool prettyPrint, bool writeBinary);
#endif
///
/// Set callback to use for loading image data
///
void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
#if defined(TINYGLTF_ENABLE_SERIALIZER)
///
/// Set callback to use for writing image data
///
void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
#endif
///
/// Set callbacks to use for filesystem (fs) access and their user data
///
@@ -1499,14 +1518,38 @@ class TinyGLTF {
#endif // __GNUC__
#ifndef TINYGLTF_NO_INCLUDE_JSON
#ifndef TINYGLTF_USE_RAPIDJSON
#include "json.hpp"
#else
#if defined(TINYGLTF_USE_RAPIDJSON)
#include "document.h"
#include "prettywriter.h"
#include "rapidjson.h"
#include "stringbuffer.h"
#include "writer.h"
#elif defined(TINYGLTF_USE_SAJSON)
#ifdef __clang__
#if __has_warning("-Wc99-extensions")
#pragma clang diagnostic ignored "-Wc99-extensions"
#endif
#if __has_warning("-Wshadow-field-in-constructor")
#pragma clang diagnostic ignored "-Wshadow-field-in-constructor"
#endif
#endif
#include "sajson.h"
// Serialization is not available for sajson backend.
#ifdef TINYGLTF_ENABLE_SERIALIZER
#undef TINYGLTF_ENABLE_SERIALIZER
#endif
#else
// Default = nlohmann json
#include "json.hpp"
#endif
#endif
@@ -1642,7 +1685,62 @@ struct JsonDocument : public rapidjson::Document {
#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
#else
#elif defined(TINYGLTF_USE_SAJSON)
using json = sajson::value;
using JsonDocument = json;
// TODO(syoyo): Implement
// muda
class sajson_const_iterator
{
public:
sajson_const_iterator() = default;
sajson_const_iterator(const sajson::value &v) : _v(v) {
}
sajson_const_iterator(const sajson_const_iterator &rhs) = default;
sajson_const_iterator &operator=(const sajson_const_iterator &rhs) = default;
std::string key() const {
}
sajson::value &value() {
return _v;
}
sajson::value *begin() {
return &_v;
}
bool operator!=(sajson_const_iterator &rhs) {
// TODO
assert(0);
return false;
}
sajson_const_iterator &operator++() {
// TODO
assert(0);
return (*this);
}
sajson::value &operator*() {
return _v;
}
private:
// `sajson::value` itself is a small struct, so having a copy of it
// does not affect performance and memory consumption.
sajson::value _v;
};
using json_const_iterator = sajson_const_iterator;
using json_const_array_iterator = json_const_iterator;
#else // nlohmann JSON
using nlohmann::json;
using json_const_iterator = json::const_iterator;
using json_const_array_iterator = json_const_iterator;
@@ -1654,6 +1752,14 @@ void JsonParse(JsonDocument &doc, const char *str, size_t length,
#ifdef TINYGLTF_USE_RAPIDJSON
(void)throwExc;
doc.Parse(str, length);
#elif defined(TINYGLTF_USE_SAJSON)
// This code path is not available.
(void)doc;
(void)str;
(void)length;
(void)throwExc;
//doc = sajson::parse(sajson::dynamic_allocation(), sajson::mutable_string_view(length, const_cast<char *>(str)));
//doc.parse(sajson::dynamic_allocation(), sajson::mutable_string_view(length, const_cast<char *>(str)));
#else
doc = json::parse(str, str + length, nullptr, throwExc);
#endif
@@ -2385,11 +2491,15 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
}
#endif
#if defined(TINYGLTF_ENABLE_SERIALIZER)
void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
WriteImageData = func;
write_image_user_data_ = user_data;
}
#endif
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
static void WriteToMemory_stbi(void *context, void *data, int size) {
std::vector<unsigned char> *buffer =
@@ -2481,6 +2591,15 @@ static inline std::wstring UTF8ToWchar(const std::string &str) {
(int)wstr.size());
return wstr;
}
static inline std::string WcharToUTF8(const std::wstring &wstr) {
int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
nullptr, 0, NULL, NULL);
std::string str(str_size, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
(int)str.size(), NULL, NULL);
return str;
}
#endif
#ifndef TINYGLTF_NO_FS
@@ -2532,15 +2651,16 @@ bool FileExists(const std::string &abs_filename, void *) {
std::string ExpandFilePath(const std::string &filepath, void *) {
#ifdef _WIN32
DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
char *str = new char[len];
ExpandEnvironmentStringsA(filepath.c_str(), str, len);
// Assume input `filepath` is encoded in UTF-8
std::wstring wfilepath = UTF8ToWchar(filepath);
DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
wchar_t *wstr = new wchar_t[wlen];
ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
std::string s(str);
std::wstring ws(wstr);
delete[] wstr;
return WcharToUTF8(ws);
delete[] str;
return s;
#else
#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
@@ -2592,11 +2712,12 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
return false;
}
size_t size = AAsset_getLength(asset);
if (size <= 0) {
if (size == 0) {
if (err) {
(*err) += "Invalid file size : " + filepath +
" (does the path point to a directory?)";
}
return false;
}
out->resize(size);
AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
@@ -2615,9 +2736,11 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
_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)
#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
// For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept `wchar_t *`
std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
#else // clang?
#else
// Unknown compiler/runtime
std::ifstream f(filepath.c_str(), std::ifstream::binary);
#endif
#else
@@ -2659,9 +2782,10 @@ 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);
int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(
file_descriptor, std::ios_base::out | std::ios_base::binary);
std::ostream f(&wfile_buf);
#elif defined(_MSC_VER)
std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
@@ -2712,12 +2836,13 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
void *user_data = nullptr) {
std::string filename;
std::string ext;
// If image have uri. Use it it as a filename
// If image has uri, use it it as a filename
if (image.uri.size()) {
filename = GetBaseFilename(image.uri);
ext = GetFilePathExtension(filename);
} else if (image.bufferView != -1) {
// If there's no URI and the data exists in a buffer,
// don't change properties or write images
} else if (image.name.size()) {
ext = MimeToExt(image.mimeType);
// Otherwise use name as filename
@@ -2729,7 +2854,7 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
}
// If callback is set, modify image data object
if (*WriteImageData != nullptr) {
if (*WriteImageData != nullptr && !filename.empty()) {
std::string uri;
(*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
}
@@ -2865,6 +2990,15 @@ bool GetInt(const json &o, int &val) {
}
}
return false;
#elif defined(TINYGLTF_USE_SAJSON)
auto type = o.get_type();
if (type == sajson::TYPE_INTEGER) {
val = static_cast<int>(o.get_number_value());
return true;
}
return false;
#else
auto type = o.type();
@@ -2897,6 +3031,16 @@ bool GetNumber(const json &o, double &val) {
return true;
}
return false;
#elif defined(TINYGLTF_USE_SAJSON)
auto type = o.get_type();
if ((type == sajson::TYPE_DOUBLE) ||
(type == sajson::TYPE_INTEGER)) {
val = static_cast<double>(o.get_number_value());
return true;
}
return false;
#else
if (o.is_number()) {
@@ -2915,6 +3059,15 @@ bool GetString(const json &o, std::string &val) {
return true;
}
return false;
#elif defined(TINYGLTF_USE_SAJSON)
auto type = o.get_type();
if (type == sajson::TYPE_STRING) {
val = o.as_string();
return true;
}
return false;
#else
if (o.type() == json::value_t::string) {
@@ -2929,6 +3082,8 @@ bool GetString(const json &o, std::string &val) {
bool IsArray(const json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.IsArray();
#elif defined(TINYGLTF_USE_SAJSON)
return o.get_type() == sajson::TYPE_ARRAY;
#else
return o.is_array();
#endif
@@ -2937,6 +3092,10 @@ bool IsArray(const json &o) {
json_const_array_iterator ArrayBegin(const json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.Begin();
#elif TINYGLTF_USE_SAJSON
// TODO(syoyo): Implement
assert(0);
return json_const_array_iterator(o);
#else
return o.begin();
#endif
@@ -2945,6 +3104,10 @@ json_const_array_iterator ArrayBegin(const json &o) {
json_const_array_iterator ArrayEnd(const json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.End();
#elif TINYGLTF_USE_SAJSON
// TODO(syoyo): Implement
assert(0);
return json_const_array_iterator(o);
#else
return o.end();
#endif
@@ -2953,6 +3116,8 @@ json_const_array_iterator ArrayEnd(const json &o) {
bool IsObject(const json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.IsObject();
#elif defined(TINYGLTF_USE_SAJSON)
return o.get_type() == sajson::TYPE_OBJECT;
#else
return o.is_object();
#endif
@@ -2961,6 +3126,11 @@ bool IsObject(const json &o) {
json_const_iterator ObjectBegin(const json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.MemberBegin();
#elif defined(TINYGLTF_USE_SAJSON)
assert(o.get_type() == sajson::TYPE_OBJECT);
// TODO(syoyo): Implement
assert(0);
return json_const_iterator(o);
#else
return o.begin();
#endif
@@ -2969,6 +3139,11 @@ json_const_iterator ObjectBegin(const json &o) {
json_const_iterator ObjectEnd(const json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
return o.MemberEnd();
#elif defined(TINYGLTF_USE_SAJSON)
assert(o.get_type() == sajson::TYPE_OBJECT);
// TODO(syoyo): Implement
assert(0);
return json_const_iterator(o);
#else
return o.end();
#endif
@@ -2977,6 +3152,10 @@ json_const_iterator ObjectEnd(const json &o) {
const char *GetKey(json_const_iterator &it) {
#ifdef TINYGLTF_USE_RAPIDJSON
return it->name.GetString();
#elif defined(TINYGLTF_USE_SAJSON)
// TODO(syoyo): Implement
assert(0);
return nullptr;
#else
return it.key().c_str();
#endif
@@ -2989,6 +3168,10 @@ bool FindMember(const json &o, const char *member, json_const_iterator &it) {
}
it = o.FindMember(member);
return it != o.MemberEnd();
#elif defined(TINYGLTF_USE_SAJSON)
// TODO(syoyo): Implement
assert(0);
return false;
#else
it = o.find(member);
return it != o.end();
@@ -2998,6 +3181,10 @@ bool FindMember(const json &o, const char *member, json_const_iterator &it) {
const json &GetValue(json_const_iterator &it) {
#ifdef TINYGLTF_USE_RAPIDJSON
return it->value;
#elif defined(TINYGLTF_USE_SAJSON)
// TODO(syoyo): Implement
assert(0);
return it.value();
#else
return it.value();
#endif
@@ -3016,6 +3203,11 @@ std::string JsonToString(const json &o, int spacing = -1) {
o.Accept(writer);
}
return buffer.GetString();
#elif defined(TINYGLTF_USE_SAJSON)
// Serialize is not available for Sajson
(void)o;
(void)spacing;
return std::string();
#else
return o.dump(spacing);
#endif
@@ -3071,7 +3263,10 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
break;
// all types are covered, so no `case default`
}
#else
#elif defined(TINYGLTF_USE_SAJSON)
// TODO
assert(0);
#else // json.hpp
switch (o.type()) {
case json::value_t::object: {
Value::Object value_object;
@@ -3154,6 +3349,11 @@ static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
if (isBoolean) {
boolValue = value.GetBool();
}
#elif defined(TINYGLTF_USE_SAJSON)
isBoolean = value.is_boolean();
if (isBoolean) {
boolValue = value.get_boolean_value();
}
#else
isBoolean = value.is_boolean();
if (isBoolean) {
@@ -3243,6 +3443,11 @@ static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
uValue = value.GetUint64();
isUValue = true;
}
#elif defined(TINYGLTF_USE_SAJSON)
isUValue = value.get_type() == sajson::TYPE_INTEGER;
if (isUValue) {
uValue = value.get_integer_value();
}
#else
isUValue = value.is_number_unsigned();
if (isUValue) {
@@ -4860,13 +5065,13 @@ static bool ParseAnimationChannel(
}
return false;
}
ParseExtensionsProperty(&channel->target_extensions, err, target_object);
if (store_original_json_for_extras_and_extensions) {
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;
@@ -5677,9 +5882,11 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
.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)]
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;
}
}
@@ -6019,10 +6226,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// 19. Parse Extras
ParseExtrasProperty(&model->extras, v);
#if !defined(TINYGLTF_USE_SAJSON)
// TODO(syoyo): Support SAJSON backend
if (store_original_json_for_extras_and_extensions_) {
model->extras_json_string = JsonToString(v["extras"]);
model->extensions_json_string = JsonToString(v["extensions"]);
}
#endif
return true;
}
@@ -6189,6 +6399,8 @@ bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
return ret;
}
#if defined(TINYGLTF_ENABLE_SERIALIZER)
///////////////////////
// GLTF Serialization
///////////////////////
@@ -6265,6 +6477,13 @@ static void SerializeNumberProperty(const std::string &key, T number,
JsonAddMember(obj, key.c_str(), json(number));
}
#ifdef TINYGLTF_USE_RAPIDJSON
template <>
void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
}
#endif
template <typename T>
static void SerializeNumberArrayProperty(const std::string &key,
const std::vector<T> &value,
@@ -6415,9 +6634,10 @@ 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);
int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(
file_descriptor, std::ios_base::out | std::ios_base::binary);
std::ostream output(&wfile_buf);
if (!wfile_buf.is_open()) return false;
#elif defined(_MSC_VER)
@@ -6500,9 +6720,10 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
}
static void SerializeGltfAccessor(Accessor &accessor, json &o) {
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
if (accessor.bufferView >= 0)
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
if (accessor.byteOffset != 0.0)
if (accessor.byteOffset != 0)
SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
SerializeNumberProperty<int>("componentType", accessor.componentType, o);
@@ -6551,7 +6772,7 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
SerializeNumberProperty("node", channel.target_node, target);
SerializeStringProperty("path", channel.target_path, target);
SerializeExtensionMap(channel.target_extensions, target);
SerializeExtensionMap(channel.target_extensions, target);
JsonAddMember(o, "target", std::move(target));
}
@@ -6958,7 +7179,9 @@ static void SerializeSpotLight(SpotLight &spot, json &o) {
static void SerializeGltfLight(Light &light, json &o) {
if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
SerializeNumberProperty("intensity", light.intensity, o);
SerializeNumberProperty("range", light.range, o);
if (light.range > 0.0) {
SerializeNumberProperty("range", light.range, o);
}
SerializeNumberArrayProperty("color", light.color, o);
SerializeStringProperty("type", light.type, o);
if (light.type == "spot") {
@@ -7072,6 +7295,11 @@ static void SerializeGltfCamera(const Camera &camera, json &o) {
} else {
// ???
}
if (camera.extras.Type() != NULL_TYPE) {
SerializeValue("extras", camera.extras, o);
}
SerializeExtensionMap(camera.extensions, o);
}
static void SerializeGltfScene(Scene &scene, json &o) {
@@ -7150,7 +7378,7 @@ static void SerializeGltfModel(Model *model, json &o) {
JsonAddMember(o, "asset", std::move(asset));
// BUFFERVIEWS
if(model->bufferViews.size()) {
if (model->bufferViews.size()) {
json bufferViews;
JsonReserveArray(bufferViews, model->bufferViews.size());
for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
@@ -7161,11 +7389,6 @@ static void SerializeGltfModel(Model *model, json &o) {
JsonAddMember(o, "bufferViews", std::move(bufferViews));
}
// Extensions used
if (model->extensionsUsed.size()) {
SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o);
}
// Extensions required
if (model->extensionsRequired.size()) {
SerializeStringArrayProperty("extensionsRequired",
@@ -7276,7 +7499,9 @@ static void SerializeGltfModel(Model *model, json &o) {
// EXTENSIONS
SerializeExtensionMap(model->extensions, o);
// LIGHTS as KHR_lights_cmn
auto extensionsUsed = model->extensionsUsed;
// LIGHTS as KHR_lights_punctual
if (model->lights.size()) {
json lights;
JsonReserveArray(lights, model->lights.size());
@@ -7291,7 +7516,7 @@ static void SerializeGltfModel(Model *model, json &o) {
{
json_const_iterator it;
if (!FindMember(o, "extensions", it)) {
if (FindMember(o, "extensions", it)) {
JsonAssign(ext_j, GetValue(it));
}
}
@@ -7299,6 +7524,23 @@ static void SerializeGltfModel(Model *model, json &o) {
JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
JsonAddMember(o, "extensions", std::move(ext_j));
// Also add "KHR_lights_punctual" to `extensionsUsed`
{
auto has_khr_lights_punctual = std::find_if(
extensionsUsed.begin(), extensionsUsed.end(), [](const std::string &s) {
return (s.compare("KHR_lights_punctual") == 0);
});
if (has_khr_lights_punctual == extensionsUsed.end()) {
extensionsUsed.push_back("KHR_lights_punctual");
}
}
}
// Extensions used
if (model->extensionsUsed.size()) {
SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
}
// EXTRAS
@@ -7318,9 +7560,10 @@ static bool WriteGltfFile(const std::string &output,
#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);
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(
file_descriptor, std::ios_base::out | std::ios_base::binary);
std::ostream gltfFile(&wfile_buf);
if (!wfile_buf.is_open()) return false;
#else
@@ -7406,9 +7649,10 @@ static void WriteBinaryGltfFile(const std::string &output,
#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);
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(
file_descriptor, std::ios_base::out | std::ios_base::binary);
std::ostream gltfFile(&wfile_buf);
#else
std::ofstream gltfFile(output.c_str(), std::ios::binary);
@@ -7429,13 +7673,13 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
// BUFFERS
std::vector<unsigned char> binBuffer;
if(model->buffers.size()) {
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);
if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
} else {
SerializeGltfBuffer(model->buffers[i], buffer);
}
@@ -7452,9 +7696,9 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
json image;
std::string dummystring = "";
// UpdateImageObject need baseDir but only uses it if embededImages is
// enable, since we won't write separte images when writing to a stream we
// use a dummystring
// UpdateImageObject need baseDir but only uses it if embeddedImages is
// enabled, since we won't write separate images when writing to a stream
// we
UpdateImageObject(model->images[i], dummystring, int(i), false,
&this->WriteImageData, this->write_image_user_data_);
SerializeGltfImage(model->images[i], image);
@@ -7501,14 +7745,15 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
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);
if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
} else 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)) {
if (!model->buffers[i].uri.empty() &&
!IsDataURI(model->buffers[i].uri)) {
binUri = model->buffers[i].uri;
} else {
binUri = defaultBinFilename + defaultBinFileExt;
@@ -7534,7 +7779,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
}
JsonPushBack(buffers, std::move(buffer));
}
JsonAddMember(output, "buffers", std::move(buffers));
JsonAddMember(output, "buffers", std::move(buffers));
}
// IMAGES
@@ -7561,6 +7806,8 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
return true;
}
#endif // TINYGLTF_ENABLE_SERIALIZER
} // namespace tinygltf
#ifdef __clang__