Compare commits

...

31 Commits

Author SHA1 Message Date
Syoyo Fujita
04d8d637fc Add HandmadeMath.
Add simple morphing code.
2018-10-08 14:20:45 +09:00
Syoyo Fujita
f02c504481 Merge branch 'skinning' of github.com:syoyo/tinygltf into skinning 2018-10-07 21:27:12 +09:00
Syoyo Fujita
5c47eda8be Implement some animation. 2018-10-07 21:25:19 +09:00
Syoyo Fujita
876f6e194e Fix build. 2018-09-25 02:21:53 +09:00
Syoyo Fujita
1b85cb8c59 Implement some skinnig stuff. 2018-09-24 22:41:15 +09:00
Syoyo Fujita
134d660722 Merge branch 'skinning' of github.com:syoyo/tinygltf into skinning 2018-09-24 21:09:46 +09:00
Syoyo Fujita
a15bfdc85d Add missing file. 2018-09-24 21:09:24 +09:00
Syoyo Fujita
39a309c416 Merge branch 'skinning' of github.com:syoyo/tinygltf into skinning 2018-09-24 21:03:49 +09:00
Syoyo Fujita
4402576a91 Check bufferView.target 2018-09-24 16:18:12 +09:00
Syoyo Fujita
26a7a9f525 Implement some vertex skinning . 2018-09-24 02:00:43 +09:00
Syoyo Fujita
d180641246 Do not draw skinned mesh(node) otherwise glview crashes. 2018-09-23 18:24:19 +09:00
Syoyo Fujita
0c0b993639 Show value when printing UNKNOWN 2018-09-23 17:57:00 +09:00
Syoyo Fujita
0de4d7c05f Initial skinning example(W.I.P). 2018-09-22 17:51:05 +09:00
Syoyo Fujita
e66d8c992f Add as-is flag to Image. Tentative solution for issue #82 2018-09-02 16:58:43 +09:00
Syoyo Fujita
3e53feb046 Parse extensions property of Image. 2018-09-02 15:36:17 +09:00
Syoyo Fujita
9ec7109089 Bump clang version from 3.7 to 3.9 since clang-3.7 apt source is untrusted. 2018-08-30 21:27:11 +09:00
Syoyo Fujita
5cf22e3abc Merge branch 'master' of github.com:syoyo/tinygltf 2018-08-28 21:34:06 +09:00
Syoyo Fujita
a8f0b1c383 Suppress unknown pragma warning on clang 3.7 2018-08-28 21:33:40 +09:00
Syoyo Fujita
be718436c1 Merge pull request #90 from victorbush/force_32bit_tex
Force default image loader to use 32-bit images
2018-08-23 12:51:43 +09:00
Victor Bushong
18ef338ff5 Force default image loader to use 32-bit images for Vulkan compatibility. 2018-08-22 22:03:30 -05:00
Syoyo Fujita
1d0bd6c64f Merge pull request #89 from Selmar/missing_bin_to_error_message
missing bin files are reported as warning instead of error
2018-08-23 02:16:10 +09:00
Selmar Kok
e3b3fa9eb6 add required parameter to LoadExternalFile 2018-08-22 19:04:21 +02:00
Selmar Kok
cda38e03ed change from warning to error for missing bin files 2018-08-22 18:26:10 +02:00
Syoyo Fujita
fcdfc71ba6 Merge pull request #88 from Selmar/forward_declare_dataUri_utils
forward declare DataURI helpers
2018-08-22 21:36:16 +09:00
Selmar Kok
0d0e97e8cd forward declare DataURI helper functions to allow usage outside of implementation file (in case of custom image handling) 2018-08-22 14:01:57 +02:00
Syoyo Fujita
dfc3545d79 Fix unit test.
Update README.
2018-08-22 20:35:04 +09:00
Syoyo Fujita
1898f10836 Merge branch 'devel' 2018-08-22 20:30:52 +09:00
Syoyo Fujita
4769b1ca37 Apply clang-format. 2018-06-04 18:40:16 +09:00
Syoyo Fujita
0067a9e43a Support gltf-buffer mime.
Make `min` and `max` parameters in `Accessor` optional.
2018-06-04 18:26:05 +09:00
Syoyo Fujita
57f8e7ca3b Correctly handle filename containing spaces for external resources. Fixes #74. 2018-06-04 17:52:08 +09:00
Syoyo Fujita
5307850555 Update README. 2017-06-21 19:27:18 +09:00
23 changed files with 4403 additions and 64 deletions

View File

@@ -7,15 +7,15 @@ matrix:
sources:
- george-edison55-precise-backports
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.7
- llvm-toolchain-trusty-3.9
packages:
- g++-4.9
- clang-3.7
- clang-3.9
compiler: clang
env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug
env: COMPILER_VERSION=3.9 BUILD_TYPE=Debug
- addons: *1
compiler: clang
env: COMPILER_VERSION=3.7 BUILD_TYPE=Release
env: COMPILER_VERSION=3.9 BUILD_TYPE=Release
- addons: &2
apt:
sources:
@@ -30,7 +30,7 @@ matrix:
env: COMPILER_VERSION=4.9 BUILD_TYPE=Release EXTRA_CXXFLAGS="-fsanitize=address"
- addons: *1
compiler: clang
env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0"
env: COMPILER_VERSION=3.9 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0"
before_install:
- ./.travis-before-install.sh

View File

@@ -2,13 +2,13 @@
`TinyGLTF` is a header only C++11 glTF 2.0 https://github.com/KhronosGroup/glTF library.
## Status
Work in process(`devel` branch). Very near to release, but need more tests and examples.
`TinyGLTF` uses Niels Lohmann's json library(https://github.com/nlohmann/json), so now it requires C++11 compiler.
If you are looking for old, C++03 version, please use `devel-picojson` branch.
## Status
v2.0.0 release(22 Aug, 2018)!
## Builds
[![Build Status](https://travis-ci.org/syoyo/tinygltf.svg?branch=devel)](https://travis-ci.org/syoyo/tinygltf)
@@ -96,9 +96,15 @@ using namespace tinygltf;
Model model;
TinyGLTF loader;
std::string err;
std::string warn;
bool ret = loader.LoadASCIIFromFile(&model, &err, argv[1]);
//bool ret = loader.LoadBinaryFromFile(&model, &err, argv[1]); // for binary glTF(.glb)
bool ret = loader.LoadASCIIFromFile(&model, &err, &warn, argv[1]);
//bool ret = loader.LoadBinaryFromFile(&model, &err, &warn, argv[1]); // for binary glTF(.glb)
if (!warn.empty()) {
printf("Warn: %s\n", warn.c_str());
}
if (!err.empty()) {
printf("Err: %s\n", err.c_str());
}

View File

@@ -99,6 +99,28 @@ void Matrix::LookAt(float m[4][4], float eye[3], float lookat[3],
#endif
}
void Matrix::Identity(float m[4][4]) {
m[0][0] = 1.0f;
m[0][1] = 0.0f;
m[0][2] = 0.0f;
m[0][3] = 0.0f;
m[1][0] = 0.0f;
m[1][1] = 1.0f;
m[1][2] = 0.0f;
m[1][3] = 0.0f;
m[2][0] = 0.0f;
m[2][1] = 0.0f;
m[2][2] = 1.0f;
m[2][3] = 0.0f;
m[3][0] = 0.0f;
m[3][1] = 0.0f;
m[3][2] = 0.0f;
m[3][3] = 1.0f;
}
void Matrix::Inverse(float m[4][4]) {
/*
* codes from intel web
@@ -195,7 +217,16 @@ void Matrix::Inverse(float m[4][4]) {
}
}
void Matrix::Mult(float dst[4][4], float m0[4][4], float m1[4][4]) {
void Matrix::Add(float dst[4][4], const float m0[4][4], const float m1[4][4]) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
dst[i][j] += m0[i][j] + m1[i][j];
}
}
}
void Matrix::Mult(float dst[4][4], const float m0[4][4], const float m1[4][4]) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
dst[i][j] = 0;
@@ -206,7 +237,7 @@ void Matrix::Mult(float dst[4][4], float m0[4][4], float m1[4][4]) {
}
}
void Matrix::MultV(float dst[3], float m[4][4], float v[3]) {
void Matrix::MultV(float dst[3], const float m[4][4], const float v[3]) {
// printf("v = %f, %f, %f\n", v[0], v[1], v[2]);
dst[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0];
dst[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1];
@@ -214,3 +245,10 @@ void Matrix::MultV(float dst[3], float m[4][4], float v[3]) {
// printf("m = %f, %f, %f\n", m[3][0], m[3][1], m[3][2]);
// printf("dst = %f, %f, %f\n", dst[0], dst[1], dst[2]);
}
void Matrix::MultV4(float dst[4], const float m[4][4], const float v[4]) {
dst[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0] * v[3];
dst[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1] * v[3];
dst[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2] * v[3];
dst[3] = m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3] * v[3];
}

View File

@@ -10,8 +10,11 @@ public:
static void LookAt(float m[4][4], float eye[3], float lookat[3],
float up[3]);
static void Inverse(float m[4][4]);
static void Mult(float dst[4][4], float m0[4][4], float m1[4][4]);
static void MultV(float dst[3], float m[4][4], float v[3]);
static void Identity(float m[4][4]);
static void Add(float dst[4][4], const float m0[4][4], const float m1[4][4]);
static void Mult(float dst[4][4], const float m0[4][4], const float m1[4][4]);
static void MultV(float dst[3], const float m[4][4], const float v[3]);
static void MultV4(float dst[4], const float m[4][4], const float v[4]);
};
#endif //

View File

@@ -52,11 +52,6 @@
#include <math.h>
#include "trackball.h"
#ifdef _MSC_VER
#pragma warning(disable : 4244)
#pragma warning(disable : 4305)
#endif
/*
* This size should really be based on the distance from the center of
* rotation to the point on the object underneath the mouse. That
@@ -173,11 +168,11 @@ void trackball(float q[4], float p1x, float p1y, float p2x, float p2y) {
/*
* Avoid problems with out-of-control values...
*/
if (t > 1.0f)
t = 1.0f;
if (t < -1.0f)
t = -1.0f;
phi = 2.0f * asinf(t);
if (t > 1.0)
t = 1.0;
if (t < -1.0)
t = -1.0;
phi = 2.0 * asin(t);
axis_to_quat(a, phi, q);
}

View File

@@ -19,7 +19,8 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "tiny_gltf.h"
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
//#define BUFFER_OFFSET(i) ((char *)NULL + (i))
#define BUFFER_OFFSET(i) (reinterpret_cast<void *>(i))
#define CheckGLErrors(desc) \
{ \
@@ -251,7 +252,7 @@ static void SetupMeshState(tinygltf::Model &model, GLuint progId) {
const tinygltf::BufferView &bufferView = model.bufferViews[i];
if (bufferView.target == 0) {
std::cout << "WARN: bufferView.target is zero" << std::endl;
continue; // Unsupported bufferView.
continue; // Unsupported or not directly used bufferView.
}
const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer];
@@ -528,6 +529,13 @@ static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) {
for (; it != itEnd; it++) {
assert(it->second >= 0);
const tinygltf::Accessor &accessor = model.accessors[it->second];
const tinygltf::BufferView &bufferView = model.bufferViews[accessor.bufferView];
if (bufferView.target == 0) {
// Unsupported or not directly used buffer
continue;
}
glBindBuffer(GL_ARRAY_BUFFER, gBufferState[accessor.bufferView].vb);
CheckErrors("bind buffer");
int size = 1;
@@ -667,7 +675,10 @@ static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
// std::cout << it->first << std::endl;
// FIXME(syoyo): Refactor.
// DrawCurves(scene, it->second);
DrawMesh(model, model.meshes[node.mesh]);
if ((node.mesh >= 0) && (node.mesh < int(model.meshes.size()))) {
DrawMesh(model, model.meshes[node.mesh]);
}
// Draw child nodes.
for (size_t i = 0; i < node.children.size(); i++) {
@@ -677,7 +688,7 @@ static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
glPopMatrix();
}
static void DrawModel(tinygltf::Model &model) {
static void DrawModel(tinygltf::Model &model, size_t scene_idx) {
#if 0
std::map<std::string, tinygltf::Mesh>::const_iterator it(scene.meshes.begin());
std::map<std::string, tinygltf::Mesh>::const_iterator itEnd(scene.meshes.end());
@@ -688,9 +699,8 @@ static void DrawModel(tinygltf::Model &model) {
}
#else
// TODO(syoyo): Support non-default scenes.
assert(model.defaultScene >= 0);
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
assert(scene_idx < model.scenes.size());
const tinygltf::Scene &scene = model.scenes[scene_idx];
for (size_t i = 0; i < scene.nodes.size(); i++) {
DrawNode(model, model.nodes[scene.nodes[i]]);
}
@@ -760,8 +770,18 @@ int main(int argc, char **argv) {
Init();
if (model.scenes.empty()) {
std::cerr << "glTF model does not have scenes" << std::endl;
return EXIT_FAILURE;
}
// DBG
PrintNodes(model.scenes[model.defaultScene]);
size_t scene_idx = size_t(model.defaultScene);
if (model.defaultScene == -1) {
scene_idx = 0;
}
PrintNodes(model.scenes[scene_idx]);
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW." << std::endl;
@@ -853,7 +873,7 @@ int main(int argc, char **argv) {
glScalef(scale, scale, scale);
DrawModel(model);
DrawModel(model, scene_idx);
glMatrixMode(GL_PROJECTION);
glPopMatrix();

View File

@@ -1,10 +1,20 @@
newoption {
trigger = "asan",
description = "Enable Address Sanitizer(gcc5+ ang clang only)"
}
solution "glview"
-- location ( "build" )
configurations { "Debug", "Release" }
platforms {"native", "x64", "x32"}
project "glview"
-- Use clang for better asan expericen
if _OPTIONS["asan"] then
toolset "clang"
end
kind "ConsoleApp"
language "C++"
cppdialect "C++11"
@@ -13,6 +23,12 @@ solution "glview"
includedirs { "../../" }
configuration { "linux" }
if _OPTIONS["asan"] then
buildoptions { "-fsanitize=address,undefined" }
linkoptions { "-fsanitize=address,undefined" }
end
linkoptions { "`pkg-config --libs glfw3`" }
links { "GL", "GLU", "m", "GLEW", "X11", "Xrandr", "Xinerama", "Xi", "Xxf86vm", "Xcursor", "dl" }

View File

@@ -86,7 +86,7 @@ static void vsub(const float *src1, const float *src2, float *dst) {
}
static void vcopy(const float *v1, float *v2) {
register int i;
int i;
for (i = 0; i < 3; i++)
v2[i] = v1[i];
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
# Simple glTF skinning sample with CPU skinning implementation.
Example use CPU implementation of skinning for the explanation of how to process skin property in glTF format.
Animation and skinning code is based on SacchaWillems' Vulkan-glTF-PBR: https://github.com/SaschaWillems/Vulkan-glTF-PBR
OpenGL is still used to display renderings.
## Build on Linux and macOS
```
$ premake5 gmake
$ make
$ ./bin/native/Debug/skinning simple-skin.gltf
```
## Note on asset
`simple-skin.gltf` is grabbed from gltfTutorial https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_019_SimpleSkin.md

1043
examples/skinning/main.cc Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
#include <vector>
#include <cstdlib>
#include <cassert>
void MorthTargets(std::vector<float> &weights,
std::vector<std::vector<float>> &targets,
std::vector<float> *output)
{
assert(weights.size() > 0);
assert(targets.size() > 0);
assert(weights.size() == targets.size());
// Assume all position has same number of vertices;
// TODO(parallelize)
for (size_t v = 0; v < targets[0].size(); v++) { // for each vertex
(*output)[v] = 0.0f;
for (size_t i = 0; i < weights.size(); i++) {
(*output)[v] += weights[i] * targets[i][v];
}
}
}

View File

@@ -0,0 +1,59 @@
newoption {
trigger = "asan",
description = "Enable Address Sanitizer(gcc5+ ang clang only)"
}
solution "skinning"
-- location ( "build" )
configurations { "Debug", "Release" }
platforms {"native", "x64", "x32"}
project "skinning"
-- Use clang for better asan expericen
if _OPTIONS["asan"] then
toolset "clang"
end
kind "ConsoleApp"
language "C++"
cppdialect "C++11"
files { "main.cc", "skinning.cc", "morph-targets.cc", "../common/trackball.cc", "../common/matrix.cc" }
includedirs { "./" }
includedirs { "../../" }
configuration { "linux" }
if _OPTIONS["asan"] then
buildoptions { "-fsanitize=address" }
linkoptions { "-fsanitize=address" }
end
linkoptions { "`pkg-config --libs glfw3`" }
links { "GL", "GLU", "m", "GLEW", "X11", "Xrandr", "Xinerama", "Xi", "Xxf86vm", "Xcursor", "dl" }
configuration { "windows" }
-- Edit path to glew and GLFW3 fit to your environment.
includedirs { "../../../../local/glew-1.13.0/include/" }
includedirs { "../../../../local/glfw-3.2.bin.WIN32/include/" }
libdirs { "../../../../local/glew-1.13.0/lib/Release/Win32/" }
libdirs { "../../../../local/glfw-3.2.bin.WIN32/lib-vc2013/" }
links { "glfw3", "gdi32", "winmm", "user32", "glew32", "glu32","opengl32", "kernel32" }
defines { "_CRT_SECURE_NO_WARNINGS" }
configuration { "macosx" }
includedirs { "/usr/local/include" }
buildoptions { "-Wno-deprecated-declarations" }
libdirs { "/usr/local/lib" }
links { "glfw", "GLEW" }
linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" }
configuration "Debug"
defines { "DEBUG" }
symbols "On"
warnings "Extra"
configuration "Release"
defines { "NDEBUG" }
optimize "On"
warnings "Extra"

View File

@@ -0,0 +1,16 @@
uniform sampler2D diffuseTex;
uniform int uIsCurve;
varying vec3 normal;
varying vec2 texcoord;
void main(void)
{
//gl_FragColor = vec4(0.5 * normalize(normal) + 0.5, 1.0);
//gl_FragColor = vec4(texcoord, 0.0, 1.0);
if (uIsCurve > 0) {
gl_FragColor = texture2D(diffuseTex, texcoord);
} else {
gl_FragColor = vec4(0.5 * normalize(normal) + 0.5, 1.0);
}
}

View File

@@ -0,0 +1,16 @@
attribute vec3 in_vertex;
attribute vec3 in_normal;
attribute vec2 in_texcoord;
varying vec3 normal;
varying vec2 texcoord;
void main(void)
{
vec4 p = gl_ModelViewProjectionMatrix * vec4(in_vertex, 1);
gl_Position = p;
vec4 nn = gl_ModelViewMatrixInverseTranspose * vec4(normalize(in_normal), 0);
normal = nn.xyz;
texcoord = in_texcoord;
}

View File

@@ -0,0 +1,148 @@
{
"scenes" : [ {
"nodes" : [ 0 ]
} ],
"nodes" : [ {
"skin" : 0,
"mesh" : 0,
"children" : [ 1 ]
}, {
"children" : [ 2 ],
"translation" : [ 0.0, 1.0, 0.0 ]
}, {
"rotation" : [ 0.0, 0.0, 0.0, 1.0 ]
} ],
"meshes" : [ {
"primitives" : [ {
"attributes" : {
"POSITION" : 1,
"JOINTS_0" : 2,
"WEIGHTS_0" : 3
},
"indices" : 0
} ]
} ],
"skins" : [ {
"inverseBindMatrices" : 4,
"joints" : [ 1, 2 ]
} ],
"animations" : [ {
"channels" : [ {
"sampler" : 0,
"target" : {
"node" : 2,
"path" : "rotation"
}
} ],
"samplers" : [ {
"input" : 5,
"interpolation" : "LINEAR",
"output" : 6
} ]
} ],
"buffers" : [ {
"uri" : "data:application/gltf-buffer;base64,AAABAAMAAAADAAIAAgADAAUAAgAFAAQABAAFAAcABAAHAAYABgAHAAkABgAJAAgAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAD8AAAAAAACAPwAAAD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAwD8AAAAAAACAPwAAwD8AAAAAAAAAAAAAAEAAAAAAAACAPwAAAEAAAAAA",
"byteLength" : 168
}, {
"uri" : "data:application/gltf-buffer;base64,AAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAABAPwAAgD4AAAAAAAAAAAAAQD8AAIA+AAAAAAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAPwAAAD8AAAAAAAAAAAAAgD4AAEA/AAAAAAAAAAAAAIA+AABAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAA=",
"byteLength" : 320
}, {
"uri" : "data:application/gltf-buffer;base64,AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAvwAAgL8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAL8AAIC/AAAAAAAAgD8=",
"byteLength" : 128
}, {
"uri" : "data:application/gltf-buffer;base64,AAAAAAAAAD8AAIA/AADAPwAAAEAAACBAAABAQAAAYEAAAIBAAACQQAAAoEAAALBAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAPT9ND/0/TQ/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAPT9NL/0/TQ/AAAAAAAAAAD0/TS/9P00PwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAAAAAAAAAIA/",
"byteLength" : 240
} ],
"bufferViews" : [ {
"buffer" : 0,
"byteOffset" : 0,
"byteLength" : 48,
"target" : 34963
}, {
"buffer" : 0,
"byteOffset" : 48,
"byteLength" : 120,
"target" : 34962
}, {
"buffer" : 1,
"byteOffset" : 0,
"byteLength" : 320,
"byteStride" : 16
}, {
"buffer" : 2,
"byteOffset" : 0,
"byteLength" : 128
}, {
"buffer" : 3,
"byteOffset" : 0,
"byteLength" : 240
} ],
"accessors" : [ {
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5123,
"count" : 24,
"type" : "SCALAR",
"max" : [ 9 ],
"min" : [ 0 ]
}, {
"bufferView" : 1,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 10,
"type" : "VEC3",
"max" : [ 1.0, 2.0, 0.0 ],
"min" : [ 0.0, 0.0, 0.0 ]
}, {
"bufferView" : 2,
"byteOffset" : 0,
"componentType" : 5123,
"count" : 10,
"type" : "VEC4",
"max" : [ 0, 1, 0, 0 ],
"min" : [ 0, 1, 0, 0 ]
}, {
"bufferView" : 2,
"byteOffset" : 160,
"componentType" : 5126,
"count" : 10,
"type" : "VEC4",
"max" : [ 1.0, 1.0, 0.0, 0.0 ],
"min" : [ 0.0, 0.0, 0.0, 0.0 ]
}, {
"bufferView" : 3,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 2,
"type" : "MAT4",
"max" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.5, -1.0, 0.0, 1.0 ],
"min" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.5, -1.0, 0.0, 1.0 ]
}, {
"bufferView" : 4,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 12,
"type" : "SCALAR",
"max" : [ 5.5 ],
"min" : [ 0.0 ]
}, {
"bufferView" : 4,
"byteOffset" : 48,
"componentType" : 5126,
"count" : 12,
"type" : "VEC4",
"max" : [ 0.0, 0.0, 0.707, 1.0 ],
"min" : [ 0.0, 0.0, -0.707, 0.707 ]
} ],
"asset" : {
"version" : "2.0"
}
}

View File

@@ -0,0 +1,252 @@
#include "skinning.h"
#include "../common/matrix.h"
#include "../common/trackball.h" // for quaternion
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
#define HANDMADE_MATH_IMPLEMENTATION
#include "HandmadeMath.h"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#include <cassert>
#include <cstring>
namespace example {
struct Node {
float translation[3] = {0.0f, 0.0f, 0.0f};
float scale[4] = {1.0f, 1.0f, 1.0f};
float rotation[4] = {0.0f, 0.0f, 0.0f, 1.0f};
void update() {
}
};
static inline vec4 mix(vec4 x, vec4 y, float a) {
vec4 v;
v.f[0] = (1.0f - a) * x.f[0] + a * y.f[0];
v.f[1] = (1.0f - a) * x.f[1] + a * y.f[1];
v.f[2] = (1.0f - a) * x.f[2] + a * y.f[2];
v.f[3] = (1.0f - a) * x.f[3] + a * y.f[3];
return v;
}
void BuildTransofrmMatrix(const float translate[3],
const float rotation[4], // as quaternion in glTF
const float scale[3], mat4 *transform_matrix) {
float T[4][4];
T[0][0] = 1.0f;
T[0][1] = 0.0f;
T[0][2] = 0.0f;
T[0][3] = 0.0f;
T[1][0] = 0.0f;
T[1][1] = 1.0f;
T[1][2] = 0.0f;
T[1][3] = 0.0f;
T[2][0] = 0.0f;
T[2][1] = 0.0f;
T[2][2] = 1.0f;
T[2][3] = 0.0f;
T[3][0] = translate[0];
T[3][1] = translate[1];
T[3][2] = translate[2];
T[3][3] = 1.0f;
float R[4][4];
build_rotmatrix(R, rotation);
float S[4][4];
S[0][0] = scale[0];
S[0][1] = 0.0f;
S[0][2] = 0.0f;
S[0][3] = 0.0f;
S[1][0] = 0.0f;
S[1][1] = scale[1];
S[1][2] = 0.0f;
S[1][3] = 0.0f;
S[2][0] = 0.0f;
S[2][1] = 0.0f;
S[2][2] = scale[2];
S[2][3] = 0.0f;
S[3][0] = 0.0f;
S[3][1] = 0.0f;
S[3][2] = 0.0f;
S[3][3] = 1.0f;
float RS[4][4];
Matrix::Mult(RS, R, S);
Matrix::Mult(transform_matrix->m, T, RS);
}
void ComputeJointMatrices(
const std::vector<mat4> global_transform_of_nodes,
const std::vector<mat4> global_transform_of_joint_nodes,
const std::vector<mat4> inverse_bind_matrix_for_joints,
std::vector<mat4> *output_joint_matrices) {
const size_t n = global_transform_of_nodes.size();
output_joint_matrices->resize(n);
for (size_t i = 0; i < n; i++) {
mat4 g_inv = global_transform_of_nodes[i];
Matrix::Inverse(g_inv.m);
mat4 g_joint = global_transform_of_joint_nodes[i];
mat4 inverse_bind_matrix = inverse_bind_matrix_for_joints[i];
float a[4][4]; // temp matrix
Matrix::Mult(a, g_joint.m, inverse_bind_matrix.m);
Matrix::Mult((*output_joint_matrices)[i].m, g_inv.m, a);
}
}
void Skining(const std::vector<float> vertices,
const std::vector<float> weights, const std::vector<size_t> joints,
const size_t num_skinning_weights,
const std::vector<mat4> joint_matrices, const float t,
std::vector<float> *skinned_vertices) {
assert((vertices.size() % 4) == 0);
const size_t num_vertices = vertices.size() / 4;
skinned_vertices->resize(vertices.size());
// TODO(syoyo): Ensure sum(weights) = 1.0;
for (size_t v = 0; v < num_vertices; v++) {
const float *w_p = weights.data() + v * num_skinning_weights;
const size_t *j_p = joints.data() + v * num_skinning_weights;
mat4 skin_mat;
memset(skin_mat.m, 0, sizeof(float) * 4 * 4);
for (size_t k = 0; k < num_skinning_weights; k++) {
const float w = w_p[k];
const mat4 &m = joint_matrices[j_p[k]];
for (size_t j = 0; j < 4; j++) {
for (size_t i = 0; i < 4; i++) {
skin_mat.m[j][i] += w * m.m[j][i];
}
}
}
// M = lerp I and skin_mat
mat4 M;
mat4 I;
Matrix::Identity(I.m);
for (size_t j = 0; j < 4; j++) {
for (size_t i = 0; i < 4; i++) {
M.m[j][i] = I.m[j][i] * t + (1.0f - t) * skin_mat.m[j][i];
}
}
float vtx[4];
vtx[0] = vertices[4 * v + 0];
vtx[1] = vertices[4 * v + 1];
vtx[2] = vertices[4 * v + 2];
vtx[3] = vertices[4 * v + 3];
float ret[4];
Matrix::MultV4(ret, M.m, vtx);
}
}
void UpdateAnimation(std::vector<Animation> &animations, uint32_t index,
float time, std::vector<Node*> &nodes) {
if (index > uint32_t(animations.size() - 1)) {
return;
}
const Animation &animation = animations[index];
bool updated = false;
for (auto &channel : animation.channels) {
const AnimationSampler &sampler = animation.samplers[channel.samplerIndex];
if (sampler.inputs.size() > sampler.outputsVec4.size()) {
continue;
}
// TODO(LTE): support interpolation other than LINEAR
for (size_t i = 0; i < sampler.inputs.size() - 1; i++) {
if ((time >= sampler.inputs[i]) && (time <= sampler.inputs[i + 1])) {
float u = std::max(0.0f, time - sampler.inputs[i]) /
(sampler.inputs[i + 1] - sampler.inputs[i]);
if (u <= 1.0f) {
switch (channel.path) {
case AnimationChannel::PathType::TRANSLATION: {
example::vec4 trans =
mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
channel.node->translation[0] = trans.f[0];
channel.node->translation[1] = trans.f[1];
channel.node->translation[2] = trans.f[2];
// drop w
break;
}
case AnimationChannel::PathType::SCALE: {
example::vec4 scale =
mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
channel.node->scale[0] = scale.f[0];
channel.node->scale[1] = scale.f[1];
channel.node->scale[2] = scale.f[2];
break;
}
case AnimationChannel::PathType::ROTATION: {
hmm_quaternion q1 = HMM_Quaternion(
sampler.outputsVec4[i].f[0],
sampler.outputsVec4[i].f[1],
sampler.outputsVec4[i].f[2],
sampler.outputsVec4[i].f[3]);
hmm_quaternion q2 = HMM_Quaternion(
sampler.outputsVec4[i + 1].f[0],
sampler.outputsVec4[i + 1].f[1],
sampler.outputsVec4[i + 1].f[2],
sampler.outputsVec4[i + 1].f[3]);
hmm_quaternion q = HMM_NormalizeQuaternion(HMM_Slerp(q1, u, q2));
channel.node->rotation[0] = q.Elements[0];
channel.node->rotation[1] = q.Elements[1];
channel.node->rotation[2] = q.Elements[2];
channel.node->rotation[3] = q.Elements[3];
break;
}
}
updated = true;
}
}
}
}
if (updated) {
for (auto &node : nodes) {
node->update();
}
}
}
} // namespace example

View File

@@ -0,0 +1,95 @@
#ifndef EXAMPLE_SKINNING_H_
#define EXAMPLE_SKINNING_H_
#include <vector>
#include <cstddef>
#include <cstdint>
#include <string>
#include <limits>
namespace example {
struct mat4 {
float m[4][4];
};
struct vec4 {
float f[4];
};
// glTF node
struct Node;
struct AnimationChannel {
enum PathType { TRANSLATION, ROTATION, SCALE };
PathType path;
Node *node;
uint32_t samplerIndex;
};
struct AnimationSampler {
enum InterpolationType { LINEAR, STEP, CUBICSPLINE };
InterpolationType interpolation;
std::vector<float> inputs;
std::vector<example::vec4> outputsVec4;
};
struct Animation {
std::string name;
std::vector<AnimationSampler> samplers;
std::vector<AnimationChannel> channels;
float start = std::numeric_limits<float>::max();
float end = std::numeric_limits<float>::min();
};
///
/// Utility function to build transformation matrix from translate/rotation/scale
///
/// https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md
///
/// M = T * R * S
///
void BuildTransofrmMatrix(
const float translate[3],
const float rotation[4], // as quaternion in glTF
const float scale[3],
mat4 *transform_matrix);
///
/// Compute joint matrices.
///
/// jointMatrix(j) =
/// globalTransformOfNodeThatTheMeshIsAttachedTo^-1 *
/// globalTransformOfJointNode(j) *
/// inverseBindMatrixForJoint(j);
///
void ComputeJointMatrices(
const std::vector<mat4> global_transform_of_nodes,
const std::vector<mat4> global_transform_of_joint_nodes,
const std::vector<mat4> inverse_bind_matrix_for_joints,
std::vector<mat4> output_joint_matrices);
///
///
/// @param[in] vertices Input vertices(# of elements = num_vertices * 4(xyzw))
/// @param[in] weights Linearized weights(# of elements = num_vertices * num_skinning_weights)
/// @param[in] weights Linearized weights(# of elements = num_vertices * num_skinning_weights)
/// @param[in] num_weights Linearized weights(# of elements = num_vertices *
/// @param[in] joint_matrices Array of joint matricies.
/// @param[in] t Interpolator. [0.0, 1.0]
/// @param[in] skinned_vertices Resulting skinned vertices
///
void Skining(const std::vector<float> vertices,
const std::vector<float> weights, const std::vector<size_t> joints,
const size_t num_skinning_weights,
const std::vector<mat4> joint_matrices,
const float t,
std::vector<float> *skinned_vertices);
} // namespace example
#endif // EXAMPLE_SKINNING_H_

15
experimental/README.md Normal file
View File

@@ -0,0 +1,15 @@
# Python script which generates C++11 code from JSON schema.
## Requirements
* python3
* jsonref
## Generate
Run `gen.py` by specifing the path to glTF schema directory(from https://github.com/KhronosGroup/glTF.git)
```
$ python gen.py /path/to/glTF/specification/2.0/schema
```

34
experimental/gen.py Normal file
View File

@@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys, os
import subprocess
import json
from pprint import pprint
import jsonref
# glTF 2.0
schema_files = [
"glTF.schema.json"
]
def main():
if len(sys.argv) < 2:
print("Requires path to glTF scheme directory.")
sys.exit(-1)
gltf_schema_dir = sys.argv[1]
gltf_schema_filepath = os.path.join(gltf_schema_dir, schema_files[0])
if not os.path.exists(gltf_schema_filepath):
print("File not found: {}".format(gltf_schema_filepath))
sys.exit(-1)
gltf_schema_uri = 'file://{}/'.format(gltf_schema_dir)
with open(gltf_schema_filepath) as schema_file:
j = jsonref.loads(schema_file.read(), base_uri=gltf_schema_uri, jsonschema=True)
pprint(j)
main()

View File

@@ -1,3 +1,6 @@
//
// TODO(syoyo): Print extensions and extras for each glTF object.
//
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
@@ -27,7 +30,8 @@ static std::string PrintMode(int mode) {
} else if (mode == TINYGLTF_MODE_TRIANGLE_STRIP) {
return "TRIANGLE_STRIP";
}
return "**UNKNOWN**";
return "**UNKNOWN**(" + std::to_string(mode) + ")";
}
static std::string PrintTarget(int target) {
@@ -36,7 +40,7 @@ static std::string PrintTarget(int target) {
} else if (target == 34963) {
return "GL_ELEMENT_ARRAY_BUFFER";
} else {
return "**UNKNOWN**";
return "**UNKNOWN**(" + std::to_string(target) + ")";
}
}
@@ -60,7 +64,7 @@ static std::string PrintType(int ty) {
} else if (ty == TINYGLTF_TYPE_MAT4) {
return "MAT4";
}
return "**UNKNOWN**";
return "**UNKNOWN**(" + std::to_string(ty) + ")";
}
static std::string PrintComponentType(int ty) {
@@ -82,7 +86,7 @@ static std::string PrintComponentType(int ty) {
return "DOUBLE";
}
return "**UNKNOWN**";
return "**UNKNOWN**(" + std::to_string(ty) + ")";
}
#if 0
@@ -144,7 +148,7 @@ static std::string PrintWrapMode(int mode) {
return "MIRRORED_REPEAT";
}
return "**UNKNOWN**";
return "**UNKNOWN**(" + std::to_string(mode) + ")";
}
static std::string PrintFilterMode(int mode) {
@@ -161,7 +165,7 @@ static std::string PrintFilterMode(int mode) {
} else if (mode == TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR) {
return "LINEAR_MIPMAP_LINEAR";
}
return "**UNKNOWN**";
return "**UNKNOWN**(" + std::to_string(mode) + ")";
}
static std::string PrintIntArray(const std::vector<int> &arr) {
@@ -514,6 +518,7 @@ static void Dump(const tinygltf::Model &model) {
std::cout << Indent(2) << "width : " << image.width << std::endl;
std::cout << Indent(2) << "height : " << image.height << std::endl;
std::cout << Indent(2) << "component : " << image.component << std::endl;
DumpExtensions(image.extensions, 1);
}
}
@@ -525,6 +530,7 @@ static void Dump(const tinygltf::Model &model) {
<< std::endl;
std::cout << Indent(1) << "source : " << texture.source
<< std::endl;
DumpExtensions(texture.extensions, 1);
}
}

View File

@@ -18,8 +18,9 @@ TEST_CASE("parse-error", "[parse]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
bool ret = ctx.LoadASCIIFromString(&model, &err, "bora", strlen("bora"), /* basedir*/ "");
bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, "bora", strlen("bora"), /* basedir*/ "");
REQUIRE(false == ret);
@@ -30,8 +31,9 @@ TEST_CASE("datauri-in-glb", "[issue-79]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
bool ret = ctx.LoadBinaryFromFile(&model, &err, "../models/box01.glb");
bool ret = ctx.LoadBinaryFromFile(&model, &err, &warn, "../models/box01.glb");
if (!err.empty()) {
std::cerr << err << std::endl;
}

View File

@@ -189,6 +189,11 @@ static inline int32_t GetTypeSizeInBytes(uint32_t ty) {
}
}
bool IsDataURI(const std::string &in);
bool DecodeDataURI(std::vector<unsigned char> *out,
std::string &mime_type, const std::string &in,
size_t reqBytes, bool checkSize);
#ifdef __clang__
#pragma clang diagnostic push
// Suppress warning for : static Value null_value
@@ -451,8 +456,16 @@ struct Image {
// "image/bmp", "image/gif"]
std::string uri; // (required if no mimeType)
Value extras;
ExtensionMap extensions;
Image() { bufferView = -1; }
// When this flag is true, data is stored to `image` in as-is format(e.g. jpeg compressed for "image/jpeg" mime)
// This feature is good if you use custom image loader function.
// (e.g. delayed decoding of images for faster glTF parsing)
// Default parser for Image does not provide as-is loading feature at the moment.
// (You can manipulate this by providing your own LoadImageData function)
bool as_is;
Image() : as_is(false) { bufferView = -1; }
};
struct Texture {
@@ -954,7 +967,6 @@ class TinyGLTF {
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wdouble-promotion"
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
@@ -966,6 +978,9 @@ class TinyGLTF {
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#pragma clang diagnostic ignored "-Wweak-vtables"
#pragma clang diagnostic ignored "-Wcovered-switch-default"
#if __has_warning("-Wdouble-promotion")
#pragma clang diagnostic ignored "-Wdouble-promotion"
#endif
#if __has_warning("-Wcomma")
#pragma clang diagnostic ignored "-Wcomma"
#endif
@@ -1233,7 +1248,7 @@ std::string base64_decode(std::string const &encoded_string) {
static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
std::string *warn, const std::string &filename,
const std::string &basedir, size_t reqBytes,
const std::string &basedir, bool required, size_t reqBytes,
bool checkSize, FsCallbacks *fs) {
if (fs == nullptr || fs->FileExists == nullptr ||
fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
@@ -1244,6 +1259,8 @@ static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
return false;
}
std::string* failMsgOut = required ? err : warn;
out->clear();
std::vector<std::string> paths;
@@ -1252,8 +1269,8 @@ static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
std::string filepath = FindFile(paths, filename, fs);
if (filepath.empty() || filename.empty()) {
if (warn) {
(*warn) += "File not found : " + filename + "\n";
if (failMsgOut) {
(*failMsgOut) += "File not found : " + filename + "\n";
}
return false;
}
@@ -1263,15 +1280,17 @@ static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
bool fileRead =
fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
if (!fileRead) {
if (err) {
(*err) += "File read error : " + filepath + " : " + fileReadErr + "\n";
if (failMsgOut) {
(*failMsgOut) += "File read error : " + filepath + " : " + fileReadErr + "\n";
}
return false;
}
size_t sz = buf.size();
if (sz == 0) {
(*err) += "File is empty : " + filepath + "\n";
if(failMsgOut) {
(*failMsgOut) += "File is empty : " + filepath + "\n";
}
return false;
}
@@ -1283,8 +1302,8 @@ static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
std::stringstream ss;
ss << "File size mismatch : " << filepath << ", requestedBytes "
<< reqBytes << ", but got " << sz << std::endl;
if (err) {
(*err) += ss.str();
if (failMsgOut) {
(*failMsgOut) += ss.str();
}
return false;
}
@@ -1305,14 +1324,19 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
int size, void *) {
(void)warn;
int w, h, comp;
int w, h, comp, req_comp;
// force 32-bit textures for common Vulkan compatibility. It appears that
// some GPU drivers do not support 24-bit images for Vulkan
req_comp = 4;
// if image cannot be decoded, ignore parsing and keep it by its path
// don't break in this case
// FIXME we should only enter this function if the image is embedded. If
// image->uri references
// an image file, it should be left as it is. Image loading should not be
// mandatory (to support other formats)
unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, 0);
unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
if (!data) {
// NOTE: you can use `warn` instead of `err`
if (err) {
@@ -1351,9 +1375,9 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
image->width = w;
image->height = h;
image->component = comp;
image->image.resize(static_cast<size_t>(w * h * comp));
std::copy(data, data + w * h * comp, image->image.begin());
image->component = req_comp;
image->image.resize(static_cast<size_t>(w * h * req_comp));
std::copy(data, data + w * h * req_comp, image->image.begin());
free(data);
@@ -1608,7 +1632,7 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
}
}
static bool IsDataURI(const std::string &in) {
bool IsDataURI(const std::string &in) {
std::string header = "data:application/octet-stream;base64,";
if (in.find(header) == 0) {
return true;
@@ -1647,7 +1671,7 @@ static bool IsDataURI(const std::string &in) {
return false;
}
static bool DecodeDataURI(std::vector<unsigned char> *out,
bool DecodeDataURI(std::vector<unsigned char> *out,
std::string &mime_type, const std::string &in,
size_t reqBytes, bool checkSize) {
std::string header = "data:application/octet-stream;base64,";
@@ -2110,6 +2134,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
}
ParseStringProperty(&image->name, err, o, "name", false);
ParseExtensionsProperty(&image->extensions, err, o);
if (hasBufferView) {
double bufferView = -1;
@@ -2166,7 +2191,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
return true;
#endif
if (!LoadExternalFile(&img, err, warn, uri, basedir, 0, false, fs)) {
if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
if (warn) {
(*warn) += "Failed to load external 'uri' for image parameter\n";
}
@@ -2261,7 +2286,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
} else {
// External .bin file.
if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
basedir, bytes, true, fs)) {
basedir, true, bytes, true, fs)) {
return false;
}
}
@@ -2303,7 +2328,7 @@ 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, bytes, true, fs)) {
basedir, true, bytes, true, fs)) {
return false;
}
}
@@ -3998,6 +4023,8 @@ static void SerializeGltfImage(Image &image, json &o) {
if (image.extras.Type() != NULL_TYPE) {
SerializeValue("extras", image.extras, o);
}
SerializeExtensionMap(image.extensions, o);
}
static void SerializeGltfMaterial(Material &material, json &o) {