mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-13 10:48:56 +00:00
Compare commits
39 Commits
anim-dump
...
16bit-lode
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8a46c4e1d | ||
|
|
8fd91aea04 | ||
|
|
8f76d790b8 | ||
|
|
853f6df7b5 | ||
|
|
758a1240c9 | ||
|
|
7bdfed3bec | ||
|
|
962552c5c8 | ||
|
|
5a4c898912 | ||
|
|
f2addc0e44 | ||
|
|
70d16a7b92 | ||
|
|
326d7ea310 | ||
|
|
eb011068c0 | ||
|
|
87be0ce34b | ||
|
|
7d9a0bda3a | ||
|
|
7ece5c8275 | ||
|
|
6bdffedcbe | ||
|
|
d2fb7dc2af | ||
|
|
c0d0251e2c | ||
|
|
62a72c4845 | ||
|
|
73584ba7b7 | ||
|
|
ead876fce9 | ||
|
|
6c4a006496 | ||
|
|
281af41b6c | ||
|
|
215e1fae61 | ||
|
|
cba75b9927 | ||
|
|
4de57db325 | ||
|
|
a32fa80102 | ||
|
|
5f34dab548 | ||
|
|
0d77a291f7 | ||
|
|
b926195ef8 | ||
|
|
c8ba17fcab | ||
|
|
df39e04e7b | ||
|
|
7319db7a50 | ||
|
|
7ae7110800 | ||
|
|
b864ea7349 | ||
|
|
d6b0b0b990 | ||
|
|
af3ebb2e76 | ||
|
|
105694b468 | ||
|
|
5f9cb24245 |
34
README.md
34
README.md
@@ -3,11 +3,13 @@
|
||||
`TinyGLTF` is a header only C++11 glTF 2.0 https://github.com/KhronosGroup/glTF library.
|
||||
|
||||
`TinyGLTF` uses Niels Lohmann's json library(https://github.com/nlohmann/json), so now it requires C++11 compiler.
|
||||
If you are looking for old, C++03 version, please use `devel-picojson` branch.
|
||||
If you are looking for old, C++03 version, please use `devel-picojson` branch.
|
||||
|
||||
## Status
|
||||
|
||||
v2.0.0 release(22 Aug, 2018)!
|
||||
v2.2.0 release(Support loading 16bit PNG)
|
||||
v2.1.0 release(Draco support)
|
||||
v2.0.0 release(22 Aug, 2018)!
|
||||
|
||||
## Builds
|
||||
|
||||
@@ -38,19 +40,23 @@ v2.0.0 release(22 Aug, 2018)!
|
||||
* Image(Using stb_image)
|
||||
* [x] Parse BASE64 encoded embedded image data(DataURI).
|
||||
* [x] Load external image file.
|
||||
* [x] PNG(8bit only)
|
||||
* [x] JPEG(8bit only)
|
||||
* [x] BMP
|
||||
* [x] GIF
|
||||
* [x] Load PNG(8bit and 16bit)
|
||||
* [x] Load JPEG(8bit only)
|
||||
* [x] Load BMP
|
||||
* [x] Load GIF
|
||||
* [x] Custom Image decoder callback(e.g. for decoding OpenEXR image)
|
||||
* Load from memory
|
||||
* Custom callback handler
|
||||
* [x] Image load
|
||||
* [x] Image save
|
||||
* Extensions
|
||||
* [x] Draco mesh decoding
|
||||
|
||||
## Examples
|
||||
|
||||
* [glview](examples/glview) : Simple glTF geometry viewer.
|
||||
* [validator](examples/validator) : Simple glTF validator with JSON schema.
|
||||
* [basic](examples/basic) : Basic glTF viewer with texturing support.
|
||||
|
||||
## Projects using TinyGLTF
|
||||
|
||||
@@ -62,14 +68,16 @@ v2.0.0 release(22 Aug, 2018)!
|
||||
|
||||
## TODOs
|
||||
|
||||
* [ ] Sparse accesors(e.g. for efficient morph targets)
|
||||
* [ ] Write C++ code generator which emits C++ code from JSON schema for robust parsing.
|
||||
* [ ] Mesh Compression/decompression(Open3DGC, etc)
|
||||
* [ ] Load Draco compressed mesh
|
||||
* [x] Load Draco compressed mesh
|
||||
* [x] Save Draco compressed mesh
|
||||
* [ ] Open3DGC?
|
||||
* [ ] Support `extensions` and `extras` property
|
||||
* [ ] HDR image?
|
||||
* [ ] OpenEXR extension through TinyEXR.
|
||||
* [ ] Write example and tests for `animation` and `skin`
|
||||
* [ ] 16bit PNG support in Serialization
|
||||
* [ ] Write example and tests for `animation` and `skin`
|
||||
* [ ] Skinning
|
||||
* [ ] Morph targets
|
||||
|
||||
@@ -101,13 +109,13 @@ Copy `stb_image.h`, `stb_image_write.h`, `json.hpp` and `tiny_gltf.h` to your pr
|
||||
|
||||
using namespace tinygltf;
|
||||
|
||||
Model model;
|
||||
Model model;
|
||||
TinyGLTF loader;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
|
||||
bool ret = loader.LoadASCIIFromFile(&model, &err, &warn, argv[1]);
|
||||
//bool ret = loader.LoadBinaryFromFile(&model, &err, &warn, argv[1]); // for binary glTF(.glb)
|
||||
//bool ret = loader.LoadBinaryFromFile(&model, &err, &warn, argv[1]); // for binary glTF(.glb)
|
||||
|
||||
if (!warn.empty()) {
|
||||
printf("Warn: %s\n", warn.c_str());
|
||||
@@ -129,6 +137,8 @@ if (!ret) {
|
||||
* `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.
|
||||
* `TINYGLTF_NO_EXTERNAL_IMAGE` : Do not try to load external image file. This option woulde be helpful if you do not want load image file during glTF parsing.
|
||||
* `TINYGLTF_ANDROID_LOAD_FROM_ASSETS`: Load all files from packaged app assets instead of the regular file system. **Note:** You must pass a valid asset manager from your android app to `tinygltf::asset_manager` beforehand.
|
||||
* `TINYGLTF_ENABLE_DRACO`: Enable Draco compression. User must provide include path and link correspnding libraries in your project file.
|
||||
|
||||
### Saving gltTF 2.0 model
|
||||
* [ ] Buffers.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# Use this for strict compilation check(will work on clang 3.8+)
|
||||
#EXTRA_CXXFLAGS := -fsanitize=address -Wall -Werror -Weverything -Wno-c++11-long-long -Wno-c++98-compat
|
||||
|
||||
all:
|
||||
clang++ -I../../ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o anim-dump anim-dump.cc
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Simple animation value dumper
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
//
|
||||
// TODO(syoyo): Print extensions and extras for each glTF object.
|
||||
//
|
||||
#include "tiny_gltf_util.h"
|
||||
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
static std::string GetFilePathExtension(const std::string &FileName) {
|
||||
if (FileName.find_last_of(".") != std::string::npos)
|
||||
return FileName.substr(FileName.find_last_of(".") + 1);
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string Indent(const int indent) {
|
||||
std::string s;
|
||||
for (int i = 0; i < indent; i++) {
|
||||
s += " ";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ProcessAnimation(const tinygltf::Animation &animation, const tinygltf::Model &model)
|
||||
{
|
||||
#if 0
|
||||
if (animaton_channel.target_path.compare("translation") == 0) {
|
||||
} else if (animaton_channel.target_path.compare("rotation") == 0) {
|
||||
} else if (animaton_channel.target_path.compare("scale") == 0) {
|
||||
} else if (animaton_channel.target_path.compare("weights") == 0) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
for (size_t j = 0; j < animation.samplers.size(); j++) {
|
||||
std::cout << "== samplers[" << j << "] ===============" << std::endl;
|
||||
const tinygltf::AnimationSampler &sampler = animation.samplers[j];
|
||||
std::cout << Indent(1) << "interpolation = " << sampler.interpolation<< std::endl;
|
||||
std::cout << Indent(1) << "input = " << sampler.input << std::endl;
|
||||
std::cout << Indent(1) << "output = " << sampler.output << std::endl;
|
||||
|
||||
// input accessor must have min/max property.
|
||||
const tinygltf::Accessor &accessor = model.accessors[sampler.input];
|
||||
|
||||
for (size_t i = 0; i < accessor.minValues.size(); i++) {
|
||||
std::cout << Indent(1) << "input min[" << i << "] = " << accessor.minValues[i] << std::endl;
|
||||
}
|
||||
for (size_t i = 0; i < accessor.maxValues.size(); i++) {
|
||||
std::cout << Indent(1) << "input max[" << i << "] = " << accessor.maxValues[i] << std::endl;
|
||||
}
|
||||
|
||||
std::cout << Indent(1) << "input count = " << accessor.count << std::endl;
|
||||
|
||||
for (size_t i = 0; i < accessor.count; i++) {
|
||||
if (accessor.type == TINYGLTF_TYPE_SCALAR) {
|
||||
float v;
|
||||
if (tinygltf::util::DecodeScalarAnimationValue(i, accessor, model, &v)) {
|
||||
std::cout << Indent(2) << "input value[" << i << "] = " << v << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//const tinygltf::Accessor &accessor = model.accessors[sampler.output];
|
||||
//std::cout << Indent(2) << "output : " << sampler.output
|
||||
// << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void DumpAnim(const tinygltf::Model &model) {
|
||||
std::cout << "=== Dump glTF ===" << std::endl;
|
||||
std::cout << "asset.copyright : " << model.asset.copyright
|
||||
<< std::endl;
|
||||
std::cout << "asset.generator : " << model.asset.generator
|
||||
<< std::endl;
|
||||
std::cout << "asset.version : " << model.asset.version
|
||||
<< std::endl;
|
||||
std::cout << "asset.minVersion : " << model.asset.minVersion
|
||||
<< std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "=== Dump scene ===" << std::endl;
|
||||
std::cout << "defaultScene: " << model.defaultScene << std::endl;
|
||||
|
||||
{
|
||||
std::cout << "animations(items=" << model.animations.size() << ")"
|
||||
<< std::endl;
|
||||
for (size_t i = 0; i < model.animations.size(); i++) {
|
||||
const tinygltf::Animation &animation = model.animations[i];
|
||||
std::cout << Indent(1) << "name : " << animation.name
|
||||
<< std::endl;
|
||||
|
||||
std::cout << Indent(1) << "channels : [ " << std::endl;
|
||||
for (size_t j = 0; i < animation.channels.size(); i++) {
|
||||
std::cout << Indent(2)
|
||||
<< "sampler : " << animation.channels[j].sampler
|
||||
<< std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "target.id : " << animation.channels[j].target_node
|
||||
<< std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "target.path : " << animation.channels[j].target_path
|
||||
<< std::endl;
|
||||
std::cout << ((i != (animation.channels.size() - 1)) ? " , " : "");
|
||||
}
|
||||
std::cout << " ]" << std::endl;
|
||||
|
||||
std::cout << Indent(1) << "samplers(items=" << animation.samplers.size()
|
||||
<< ")" << std::endl;
|
||||
for (size_t j = 0; j < animation.samplers.size(); j++) {
|
||||
const tinygltf::AnimationSampler &sampler = animation.samplers[j];
|
||||
std::cout << Indent(2) << "input : " << sampler.input
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "interpolation : " << sampler.interpolation
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "output : " << sampler.output
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
ProcessAnimation(animation, model);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
printf("Needs input.gltf\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF gltf_ctx;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
std::string input_filename(argv[1]);
|
||||
std::string ext = GetFilePathExtension(input_filename);
|
||||
|
||||
bool ret = false;
|
||||
if (ext.compare("glb") == 0) {
|
||||
std::cout << "Reading binary glTF" << std::endl;
|
||||
// assume binary glTF.
|
||||
ret = gltf_ctx.LoadBinaryFromFile(&model, &err, &warn, input_filename.c_str());
|
||||
} else {
|
||||
std::cout << "Reading ASCII glTF" << std::endl;
|
||||
// assume ascii glTF.
|
||||
ret = gltf_ctx.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 parse glTF\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DumpAnim(model);
|
||||
|
||||
return 0;
|
||||
}
|
||||
21
examples/basic/README.md
Normal file
21
examples/basic/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Basic glTF viewer
|
||||
|
||||
## Requirements
|
||||
|
||||
* glew
|
||||
* glfw3
|
||||
* premake5(linux)
|
||||
* OpenGL 3.3+ GPU
|
||||
|
||||
## Build on Linux and macOS
|
||||
|
||||
```
|
||||
$ premake5 gmake
|
||||
$ make
|
||||
```
|
||||
|
||||
## Build on Visual Studio
|
||||
|
||||
Plese use solution file located at `basic` folder.
|
||||
|
||||
|
||||
Binary file not shown.
44
examples/basic/premake5.lua
Normal file
44
examples/basic/premake5.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
solution "basic_viewer"
|
||||
-- location ( "build" )
|
||||
configurations { "Debug", "Release" }
|
||||
platforms {"native", "x64", "x32"}
|
||||
|
||||
project "basic_viewer"
|
||||
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
cppdialect "C++11"
|
||||
files { "main.cpp", "shaders.cpp", "window.cpp" }
|
||||
includedirs { "./" }
|
||||
includedirs { "../../" }
|
||||
includedirs { "../common/glm" }
|
||||
|
||||
configuration { "linux" }
|
||||
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"
|
||||
@@ -19,7 +19,7 @@ uniform vec3 sun_color; \n\
|
||||
out vec4 color;\n\
|
||||
void main() {\n\
|
||||
float lum = max(dot(normal, normalize(sun_position)), 0.0);\n\
|
||||
color = texture2D(tex, texcoord) * vec4((0.3 + 0.7 * lum) * sun_color, 1.0);\n\
|
||||
color = texture(tex, texcoord) * vec4((0.3 + 0.7 * lum) * sun_color, 1.0);\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
|
||||
21
examples/common/LICENSE.lodepng
Normal file
21
examples/common/LICENSE.lodepng
Normal file
@@ -0,0 +1,21 @@
|
||||
Copyright (c) 2005-2018 Lode Vandevenne
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
|
||||
5992
examples/common/lodepng.cpp
Normal file
5992
examples/common/lodepng.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1919
examples/common/lodepng.h
Normal file
1919
examples/common/lodepng.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,9 +4,10 @@ project(gltfutil)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
include_directories(../../)
|
||||
include_directories(../common/)
|
||||
|
||||
file(GLOB gltfutil_sources *.cc *.h)
|
||||
add_executable(gltfutil ${gltfutil_sources})
|
||||
add_executable(gltfutil ${gltfutil_sources} ../common/lodepng.cpp)
|
||||
|
||||
install ( TARGETS
|
||||
gltfutil
|
||||
|
||||
@@ -49,6 +49,7 @@ struct configuration {
|
||||
cli_action action = cli_action::not_set;
|
||||
texture_dumper::texture_output_format requested_format =
|
||||
texture_dumper::texture_output_format::not_specified;
|
||||
bool use_exr = false;
|
||||
|
||||
bool has_output_dir;
|
||||
bool is_valid() {
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
#define TINYEXR_IMPLEMENTATION
|
||||
#include "tinyexr.h"
|
||||
|
||||
namespace gltfutil {
|
||||
int usage(int ret = 0) {
|
||||
using std::cout;
|
||||
@@ -20,8 +23,9 @@ int usage(int ret = 0) {
|
||||
"[path to output directory])\n\n"
|
||||
//<< "\t\t -i: start in interactive mode\n"
|
||||
<< "\t\t -d: dump enclosed content (image assets)\n"
|
||||
<< "\t\t -f: file format for image output"
|
||||
<< "\t\t -o: ouptput directory path"
|
||||
<< "\t\t -f: file format for image output\n"
|
||||
<< "\t\t -o: ouptput directory path\n"
|
||||
<< "\t\t -e: Use OpenEXR format for 16bit image\n"
|
||||
<< "\t\t -h: print this help\n";
|
||||
return ret;
|
||||
}
|
||||
@@ -44,6 +48,9 @@ int parse_args(int argc, char** argv) {
|
||||
config.mode = ui_mode::cli;
|
||||
config.action = cli_action::dump;
|
||||
break;
|
||||
case 'e':
|
||||
config.use_exr = true;
|
||||
break;
|
||||
case 'i':
|
||||
config.mode = ui_mode::interactive;
|
||||
break;
|
||||
@@ -97,6 +104,11 @@ int parse_args(int argc, char** argv) {
|
||||
|
||||
case cli_action::dump: {
|
||||
texture_dumper dumper(model);
|
||||
|
||||
if (config.use_exr) {
|
||||
dumper.set_use_exr(true);
|
||||
}
|
||||
|
||||
if (config.requested_format !=
|
||||
texture_dumper::texture_output_format::not_specified)
|
||||
dumper.set_output_format(config.requested_format);
|
||||
|
||||
@@ -1,15 +1,84 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include "stb_image_write.h"
|
||||
#include "texture_dumper.h"
|
||||
|
||||
#include "lodepng.h" // ../common
|
||||
|
||||
#include "tinyexr.h"
|
||||
|
||||
#include <tiny_gltf.h>
|
||||
|
||||
using namespace gltfutil;
|
||||
using namespace tinygltf;
|
||||
using std::cout;
|
||||
|
||||
static LodePNGColorType GetLodePNGColorType(int channels) {
|
||||
if (channels == 1) {
|
||||
return LodePNGColorType::LCT_GREY;
|
||||
} else if (channels == 2) {
|
||||
return LodePNGColorType::LCT_GREY_ALPHA;
|
||||
} else if (channels == 3) {
|
||||
return LodePNGColorType::LCT_RGB;
|
||||
} else if (channels == 4) {
|
||||
return LodePNGColorType::LCT_RGBA;
|
||||
} else {
|
||||
std::cerr << "??? unsupported channels " << channels << "\n";
|
||||
return LodePNGColorType::LCT_RGB; // FIXME(syoyo): Raise error
|
||||
}
|
||||
}
|
||||
|
||||
static void ToBigEndian(std::vector<uint8_t>* image) {
|
||||
|
||||
assert(image->size() % 2 == 0);
|
||||
|
||||
union {
|
||||
unsigned int i;
|
||||
char c[4];
|
||||
} bint = {0x01020304};
|
||||
|
||||
bool is_big_endian = (bint.c[0] == 1);
|
||||
|
||||
if (is_big_endian) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t *ptr = reinterpret_cast<uint16_t *>(image->data());
|
||||
size_t n = image->size() / 2;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
ptr[i] = ((0xFF00 & ptr[i]) >> 8) | ((0x00FF & ptr[i]) << 8);
|
||||
}
|
||||
}
|
||||
|
||||
static bool Save16bitImageAsEXR(const std::string& filename,
|
||||
const tinygltf::Image& image) {
|
||||
assert(image.bits == 16);
|
||||
|
||||
std::vector<float> buf(image.width * image.height * image.component);
|
||||
|
||||
// widen to float image.
|
||||
// Store as is(i.e, pixel value range is [0.0, 65535.0])
|
||||
const unsigned short* ptr =
|
||||
reinterpret_cast<const unsigned short*>(image.image.data());
|
||||
for (size_t i = 0; i < image.width * image.height * image.component; i++) {
|
||||
buf[i] = float(ptr[i]);
|
||||
}
|
||||
|
||||
const char* err = nullptr;
|
||||
int ret = SaveEXR(buf.data(), image.width, image.height, image.component,
|
||||
/* save_as_fp16 */ 0, filename.c_str(), &err);
|
||||
|
||||
if (err) {
|
||||
std::cerr << "EXR err: " << err << std::endl;
|
||||
FreeEXRErrorMessage(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
return (ret == TINYEXR_SUCCESS);
|
||||
}
|
||||
|
||||
texture_dumper::texture_dumper(const Model& input)
|
||||
: model(input), configured_format(texture_output_format::png) {
|
||||
cout << "Texture dumper\n";
|
||||
@@ -26,26 +95,58 @@ void texture_dumper::dump_to_folder(const std::string& path) {
|
||||
cout << "image name is: \"" << image.name << "\"\n";
|
||||
cout << "image size is: " << image.width << 'x' << image.height << '\n';
|
||||
cout << "pixel channel count :" << image.component << '\n';
|
||||
std::string name = image.name.empty() ? std::to_string(index) : image.name;
|
||||
cout << "pixel bit depth :" << image.bits << '\n';
|
||||
std::string basename =
|
||||
image.name.empty() ? std::to_string(index) : image.name;
|
||||
|
||||
unsigned char* bytes_to_write =
|
||||
const_cast<unsigned char*>(image.image.data());
|
||||
|
||||
std::string filename;
|
||||
switch (configured_format) {
|
||||
case texture_output_format::png:
|
||||
name = path + "/" + name + ".png";
|
||||
std::cout << "Image will be written to " << name << '\n';
|
||||
stbi_write_png(name.c_str(), image.width, image.height, image.component,
|
||||
image.image.data(), 0);
|
||||
filename = path + "/" + basename + ".png";
|
||||
|
||||
if (this->use_exr) {
|
||||
if (image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
||||
filename = path + "/" + basename + ".exr";
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Image will be written to " << filename << '\n';
|
||||
|
||||
if (image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
||||
if (this->use_exr) {
|
||||
bool ret = Save16bitImageAsEXR(filename, image);
|
||||
assert(ret);
|
||||
} else {
|
||||
// Use lodepng to save 16bit PNG.
|
||||
// NOTE(syoyo): `loadpng::encode` requires image data must be stored in big endian.
|
||||
std::vector<uint8_t> tmp = image.image; // copy
|
||||
ToBigEndian(&tmp);
|
||||
|
||||
unsigned ret = lodepng::encode(
|
||||
filename, tmp.data(), image.width, image.height,
|
||||
GetLodePNGColorType(image.component), /* bits */ 16);
|
||||
assert(ret == 0); // 0 = no err.
|
||||
}
|
||||
} else {
|
||||
// TODO(syoyo): check status
|
||||
stbi_write_png(filename.c_str(), image.width, image.height,
|
||||
image.component, bytes_to_write, 0);
|
||||
}
|
||||
break;
|
||||
case texture_output_format::bmp:
|
||||
std::cout << "Image will be written to " << name << '\n';
|
||||
name = path + "/" + name + ".bmp";
|
||||
stbi_write_bmp(name.c_str(), image.width, image.height, image.component,
|
||||
image.image.data());
|
||||
filename = path + "/" + basename + ".bmp";
|
||||
std::cout << "Image will be written to " << filename << '\n';
|
||||
stbi_write_bmp(filename.c_str(), image.width, image.height,
|
||||
image.component, bytes_to_write);
|
||||
break;
|
||||
case texture_output_format::tga:
|
||||
std::cout << "Image will be written to " << name << '\n';
|
||||
name = path + "/" + name + ".tga";
|
||||
stbi_write_tga(name.c_str(), image.width, image.height, image.component,
|
||||
image.image.data());
|
||||
filename = path + "/" + basename + ".tga";
|
||||
std::cout << "Image will be written to " << filename << '\n';
|
||||
stbi_write_tga(filename.c_str(), image.width, image.height,
|
||||
image.component, bytes_to_write);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,15 @@ class texture_dumper {
|
||||
private:
|
||||
const tinygltf::Model& model;
|
||||
texture_output_format configured_format;
|
||||
bool use_exr = false; // Use EXR for 16bit image?
|
||||
|
||||
public:
|
||||
texture_dumper(const tinygltf::Model& inputModel);
|
||||
void dump_to_folder(const std::string& path = "./");
|
||||
void set_output_format(texture_output_format format);
|
||||
void set_use_exr(const bool value) {
|
||||
use_exr = value;
|
||||
}
|
||||
|
||||
static texture_output_format get_fromat_from_string(const std::string& str);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(glview)
|
||||
|
||||
set ( CMAKE_PREFIX_PATH cmake )
|
||||
|
||||
set ( DRACO_DIR "" CACHE STRING "Path to draco" )
|
||||
|
||||
find_package ( GLEW REQUIRED )
|
||||
find_package ( GLFW3 REQUIRED )
|
||||
find_package ( OpenGL REQUIRED )
|
||||
@@ -21,6 +23,18 @@ endif (APPLE)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
if (DEFINED DRACO_DIR)
|
||||
if (DRACO_DIR STREQUAL "")
|
||||
else ()
|
||||
# TODO(syoyo): better CMake script for draco
|
||||
add_definitions(-DTINYGLTF_ENABLE_DRACO)
|
||||
include_directories(${DRACO_DIR}/include)
|
||||
|
||||
link_directories(${DRACO_DIR}/lib)
|
||||
set(DRACO_LIBRARY draco)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
../../
|
||||
../common
|
||||
@@ -35,6 +49,7 @@ add_executable(glview
|
||||
)
|
||||
|
||||
target_link_libraries ( glview
|
||||
${DRACO_LIBRARY}
|
||||
${GLFW3_UNIX_LINK_LIBRARIES}
|
||||
${GLEW_LIBRARY}
|
||||
${GLFW3_glfw_LIBRARY}
|
||||
|
||||
@@ -27,6 +27,17 @@ Open .sln in Visual Studio 2013
|
||||
|
||||
When running .exe, glew and glfw dll must exist in the working directory.
|
||||
|
||||
#### Build with Draco(optional)
|
||||
|
||||
Assume CMake build.
|
||||
|
||||
```
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -DDRACO_DIR=/path/to/draco ../
|
||||
$ make
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
* [ ] PBR Material
|
||||
|
||||
@@ -677,10 +677,14 @@ 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 > -1) {
|
||||
assert(node.mesh < model.meshes.size());
|
||||
DrawMesh(model, model.meshes[node.mesh]);
|
||||
}
|
||||
|
||||
// Draw child nodes.
|
||||
for (size_t i = 0; i < node.children.size(); i++) {
|
||||
assert(node.children[i] < model.nodes.size());
|
||||
DrawNode(model, model.nodes[node.children[i]]);
|
||||
}
|
||||
|
||||
@@ -697,10 +701,11 @@ static void DrawModel(tinygltf::Model &model) {
|
||||
DrawCurves(scene, it->second);
|
||||
}
|
||||
#else
|
||||
|
||||
// TODO(syoyo): Support non-default scenes.
|
||||
assert(model.defaultScene >= 0);
|
||||
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
|
||||
//If the glTF asset has at least one scene, and doesn't define a default one
|
||||
//just show the first one we can find
|
||||
assert(model.scenes.size() > 0);
|
||||
int scene_to_display = model.defaultScene > -1 ? model.defaultScene : 0;
|
||||
const tinygltf::Scene &scene = model.scenes[scene_to_display];
|
||||
for (size_t i = 0; i < scene.nodes.size(); i++) {
|
||||
DrawNode(model, model.nodes[scene.nodes[i]]);
|
||||
}
|
||||
@@ -779,17 +784,19 @@ int main(int argc, char **argv) {
|
||||
Init();
|
||||
|
||||
// DBG
|
||||
PrintNodes(model.scenes[model.defaultScene]);
|
||||
PrintNodes(model.scenes[model.defaultScene > -1 ? model.defaultScene : 0]);
|
||||
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "Failed to initialize GLFW." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char title[1024];
|
||||
sprintf(title, "Simple glTF viewer: %s", input_filename.c_str());
|
||||
std::stringstream ss;
|
||||
ss << "Simple glTF viewer: " << input_filename;
|
||||
|
||||
window = glfwCreateWindow(width, height, title, NULL, NULL);
|
||||
std::string title = ss.str();
|
||||
|
||||
window = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);
|
||||
if (window == NULL) {
|
||||
std::cerr << "Failed to open GLFW window. " << std::endl;
|
||||
glfwTerminate();
|
||||
|
||||
2753
stb_image.h
2753
stb_image.h
File diff suppressed because it is too large
Load Diff
2632
stb_image_write.h
2632
stb_image_write.h
File diff suppressed because it is too large
Load Diff
494
tiny_gltf.h
494
tiny_gltf.h
@@ -26,6 +26,8 @@
|
||||
// THE SOFTWARE.
|
||||
|
||||
// Version:
|
||||
// - v2.2.0 Add loading 16bit PNG support.
|
||||
// - v2.1.0 Add draco compression.
|
||||
// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
|
||||
// - v2.0.0 glTF 2.0!.
|
||||
//
|
||||
@@ -47,16 +49,22 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
||||
#include <android/asset_manager.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace tinygltf {
|
||||
|
||||
#define TINYGLTF_MODE_POINTS (0)
|
||||
#define TINYGLTF_MODE_LINE (1)
|
||||
#define TINYGLTF_MODE_LINE_LOOP (2)
|
||||
#define TINYGLTF_MODE_LINE_STRIP (3)
|
||||
#define TINYGLTF_MODE_TRIANGLES (4)
|
||||
#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
|
||||
#define TINYGLTF_MODE_TRIANGLE_FAN (6)
|
||||
|
||||
#define TINYGLTF_COMPONENT_TYPE_INVALID (-1)
|
||||
#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
|
||||
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
|
||||
#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
|
||||
@@ -107,7 +115,6 @@ namespace tinygltf {
|
||||
|
||||
// End parameter types
|
||||
|
||||
#define TINYGLTF_TYPE_INVALID (-1)
|
||||
#define TINYGLTF_TYPE_VEC2 (2)
|
||||
#define TINYGLTF_TYPE_VEC3 (3)
|
||||
#define TINYGLTF_TYPE_VEC4 (4)
|
||||
@@ -141,6 +148,12 @@ namespace tinygltf {
|
||||
#define TINYGLTF_DOUBLE_EPS (1.e-12)
|
||||
#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
||||
AAssetManager* asset_manager = nullptr;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
NULL_TYPE = 0,
|
||||
NUMBER_TYPE = 1,
|
||||
@@ -368,6 +381,17 @@ struct Parameter {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// Return the index of a texture coordinate set if this Parameter is a texture map.
|
||||
/// Returned value is only valid if the parameter represent a texture from a
|
||||
/// material
|
||||
int TextureTexCoord() const {
|
||||
const auto it = json_double_value.find("texCoord");
|
||||
if (it != std::end(json_double_value)) {
|
||||
return int(it->second);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Material factor, like the roughness or metalness of a material
|
||||
/// Returned value is only valid if the parameter represent a texture from a
|
||||
/// material
|
||||
@@ -418,7 +442,6 @@ struct AnimationSampler {
|
||||
|
||||
AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
|
||||
bool operator==(const AnimationSampler &) const;
|
||||
|
||||
};
|
||||
|
||||
struct Animation {
|
||||
@@ -457,7 +480,9 @@ struct Sampler {
|
||||
Value extras;
|
||||
|
||||
Sampler()
|
||||
: wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
|
||||
: minFilter(TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR),
|
||||
magFilter(TINYGLTF_TEXTURE_FILTER_LINEAR),
|
||||
wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
|
||||
wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT),
|
||||
wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT){}
|
||||
bool operator==(const Sampler &) const;
|
||||
@@ -468,6 +493,8 @@ struct Image {
|
||||
int width;
|
||||
int height;
|
||||
int component;
|
||||
int bits; // bit depth per channel. 8(byte), 16 or 32.
|
||||
int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually UBYTE(bits = 8) or USHORT(bits = 16)
|
||||
std::vector<unsigned char> image;
|
||||
int bufferView; // (required if no uri)
|
||||
std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
|
||||
@@ -529,8 +556,9 @@ struct BufferView {
|
||||
// understood to be tightly packed
|
||||
int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
|
||||
Value extras;
|
||||
bool dracoDecoded; // Flag indicating this has been draco decoded
|
||||
|
||||
BufferView() : byteOffset(0), byteStride(0) {}
|
||||
BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {}
|
||||
bool operator==(const BufferView &) const;
|
||||
};
|
||||
|
||||
@@ -538,11 +566,11 @@ struct Accessor {
|
||||
int bufferView; // optional in spec but required here since sparse accessor
|
||||
// are not supported
|
||||
std::string name;
|
||||
size_t byteOffset = 0;
|
||||
bool normalized = false; // optinal.
|
||||
int componentType = TINYGLTF_COMPONENT_TYPE_INVALID; // (required) One of TINYGLTF_COMPONENT_TYPE_***
|
||||
size_t count = 0; // required
|
||||
int type = TINYGLTF_TYPE_INVALID; // (required) One of TINYGLTF_TYPE_*** ..
|
||||
size_t byteOffset;
|
||||
bool normalized; // optinal.
|
||||
int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
|
||||
size_t count; // required
|
||||
int type; // (required) One of TINYGLTF_TYPE_*** ..
|
||||
Value extras;
|
||||
|
||||
std::vector<double> minValues; // optional
|
||||
@@ -591,7 +619,6 @@ struct Accessor {
|
||||
bool operator==(const tinygltf::Accessor &) const;
|
||||
};
|
||||
|
||||
|
||||
struct PerspectiveCamera {
|
||||
double aspectRatio; // min > 0
|
||||
double yfov; // required. min > 0
|
||||
@@ -650,6 +677,7 @@ struct Primitive {
|
||||
// where each target is a dict with attribues in ["POSITION, "NORMAL",
|
||||
// "TANGENT"] pointing
|
||||
// to their corresponding accessors
|
||||
ExtensionMap extensions;
|
||||
Value extras;
|
||||
|
||||
Primitive() {
|
||||
@@ -779,14 +807,6 @@ class Model {
|
||||
Value extras;
|
||||
};
|
||||
|
||||
///
|
||||
/// Utility function to get an address of underlying buffer for i'th element.
|
||||
/// Returns nullptr for invalid parameter or invalid data.
|
||||
/// Assume `buffer` parameter = model.buffers[bufferViewObject.buffer]
|
||||
///
|
||||
const uint8_t *GetBufferAddress(const int i, const Accessor &accessor, const BufferView &bufferViewObject, const Buffer &buffer);
|
||||
|
||||
|
||||
enum SectionCheck {
|
||||
NO_REQUIRE = 0x00,
|
||||
REQUIRE_SCENE = 0x01,
|
||||
@@ -801,7 +821,7 @@ enum SectionCheck {
|
||||
///
|
||||
/// LoadImageDataFunction type. Signature for custom image loading callbacks.
|
||||
///
|
||||
typedef bool (*LoadImageDataFunction)(Image *, std::string *, std::string *,
|
||||
typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, std::string *,
|
||||
int, int, const unsigned char *, int,
|
||||
void *);
|
||||
|
||||
@@ -813,7 +833,7 @@ typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
|
||||
|
||||
#ifndef TINYGLTF_NO_STB_IMAGE
|
||||
// Declaration of default image loader callback
|
||||
bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
||||
bool LoadImageData(Image *image, const int image_idx, std::string *err, std::string *warn,
|
||||
int req_width, int req_height, const unsigned char *bytes,
|
||||
int size, void *);
|
||||
#endif
|
||||
@@ -1058,24 +1078,67 @@ class TinyGLTF {
|
||||
#if __has_warning("-Wnewline-eof")
|
||||
#pragma clang diagnostic ignored "-Wnewline-eof"
|
||||
#endif
|
||||
#if __has_warning("-Wunused-parameter")
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
#if __has_warning("-Wmismatched-tags")
|
||||
#pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "./json.hpp"
|
||||
// Disable GCC warnigs
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
#endif // __GNUC__
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
#ifdef TINYGLTF_ENABLE_DRACO
|
||||
#include "draco/core/decoder_buffer.h"
|
||||
#include "draco/compression/decode.h"
|
||||
#endif
|
||||
|
||||
#ifndef TINYGLTF_NO_STB_IMAGE
|
||||
#include "./stb_image.h"
|
||||
#include "stb_image.h"
|
||||
#endif
|
||||
|
||||
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
|
||||
#include "./stb_image_write.h"
|
||||
#include "stb_image_write.h"
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
// issue 143.
|
||||
// Define NOMINMAX to avoid min/max defines,
|
||||
// but undef it after included windows.h
|
||||
#ifndef NOMINMAX
|
||||
#define TINYGLTF_INTERNAL_NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h> // include API for expanding a file path
|
||||
|
||||
#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#if defined(TINYGLTF_INTERNAL_NOMINMAX)
|
||||
#undef NOMINMAX
|
||||
#endif
|
||||
|
||||
#elif !defined(__ANDROID__)
|
||||
#include <wordexp.h>
|
||||
#endif
|
||||
@@ -1192,7 +1255,8 @@ bool BufferView::operator==(const BufferView &other) const {
|
||||
return this->buffer == other.buffer && this->byteLength == other.byteLength &&
|
||||
this->byteOffset == other.byteOffset &&
|
||||
this->byteStride == other.byteStride && this->name == other.name &&
|
||||
this->target == other.target && this->extras == other.extras;
|
||||
this->target == other.target && this->extras == other.extras &&
|
||||
this->dracoDecoded == other.dracoDecoded;
|
||||
}
|
||||
bool Camera::operator==(const Camera &other) const {
|
||||
return this->name == other.name && this->extensions == other.extensions &&
|
||||
@@ -1588,46 +1652,73 @@ void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
|
||||
}
|
||||
|
||||
#ifndef TINYGLTF_NO_STB_IMAGE
|
||||
bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
||||
int req_width, int req_height, const unsigned char *bytes,
|
||||
int size, void *) {
|
||||
bool LoadImageData(Image *image, const int image_idx, std::string *err,
|
||||
std::string *warn, int req_width, int req_height,
|
||||
const unsigned char *bytes, int size, void *user_data) {
|
||||
(void)user_data;
|
||||
(void)warn;
|
||||
|
||||
int w, h, comp, req_comp;
|
||||
|
||||
unsigned char *data = nullptr;
|
||||
|
||||
// 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;
|
||||
int bits = 8;
|
||||
int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
|
||||
|
||||
// It is possible that the image we want to load is a 16bit per channel image
|
||||
// We are going to attempt to load it as 16bit per channel, and if it worked,
|
||||
// set the image data accodingly. We are casting the returned pointer into
|
||||
// unsigned char, because we are representing "bytes". But we are updating
|
||||
// the Image metadata to signal that this image uses 2 bytes (16bits) per
|
||||
// channel:
|
||||
if (stbi_is_16_bit_from_memory(bytes, size)) {
|
||||
data = (unsigned char *)stbi_load_16_from_memory(bytes, size, &w, &h, &comp,
|
||||
req_comp);
|
||||
if (data) {
|
||||
bits = 16;
|
||||
pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
// at this point, if data is still NULL, it means that the image wasn't
|
||||
// 16bit per channel, we are going to load it as a normal 8bit per channel
|
||||
// mage as we used to do:
|
||||
// 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, req_comp);
|
||||
if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
|
||||
if (!data) {
|
||||
// NOTE: you can use `warn` instead of `err`
|
||||
if (err) {
|
||||
(*err) += "Unknown image format.\n";
|
||||
(*err) +=
|
||||
"Unknown image format. STB cannot decode image data for image[" +
|
||||
std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (w < 1 || h < 1) {
|
||||
free(data);
|
||||
stbi_image_free(data);
|
||||
if (err) {
|
||||
(*err) += "Invalid image data.\n";
|
||||
(*err) += "Invalid image data for image[" + std::to_string(image_idx) +
|
||||
"] name = \"" + image->name + "\"\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req_width > 0) {
|
||||
if (req_width != w) {
|
||||
free(data);
|
||||
stbi_image_free(data);
|
||||
if (err) {
|
||||
(*err) += "Image width mismatch.\n";
|
||||
(*err) += "Image width mismatch for image[" +
|
||||
std::to_string(image_idx) + "] name = \"" + image->name +
|
||||
"\"\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1635,9 +1726,11 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
||||
|
||||
if (req_height > 0) {
|
||||
if (req_height != h) {
|
||||
free(data);
|
||||
stbi_image_free(data);
|
||||
if (err) {
|
||||
(*err) += "Image height mismatch.\n";
|
||||
(*err) += "Image height mismatch. for image[" +
|
||||
std::to_string(image_idx) + "] name = \"" + image->name +
|
||||
"\"\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1646,10 +1739,11 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
||||
image->width = w;
|
||||
image->height = h;
|
||||
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);
|
||||
image->bits = bits;
|
||||
image->pixel_type = pixel_type;
|
||||
image->image.resize(static_cast<size_t>(w * h * req_comp) * (bits / 8));
|
||||
std::copy(data, data + w * h * req_comp * (bits / 8), image->image.begin());
|
||||
stbi_image_free(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1679,6 +1773,11 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
|
||||
std::vector<unsigned char> data;
|
||||
|
||||
if (ext == "png") {
|
||||
if ((image->bits != 8) || (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
|
||||
// Unsupported pixel format
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
|
||||
image->height, image->component,
|
||||
&image->image[0], 0)) {
|
||||
@@ -1741,6 +1840,18 @@ void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
|
||||
|
||||
bool FileExists(const std::string &abs_filename, void *) {
|
||||
bool ret;
|
||||
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
||||
if (asset_manager) {
|
||||
AAsset* asset = AAssetManager_open(asset_manager, abs_filename.c_str(), AASSET_MODE_STREAMING);
|
||||
if (!asset) {
|
||||
return false;
|
||||
}
|
||||
AAsset_close(asset);
|
||||
ret = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
FILE *fp;
|
||||
errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
|
||||
@@ -1756,6 +1867,7 @@ bool FileExists(const std::string &abs_filename, void *) {
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1809,6 +1921,33 @@ std::string ExpandFilePath(const std::string &filepath, void *) {
|
||||
|
||||
bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
||||
const std::string &filepath, void *) {
|
||||
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
||||
if (asset_manager) {
|
||||
AAsset* asset = AAssetManager_open(asset_manager, filepath.c_str(), AASSET_MODE_STREAMING);
|
||||
if (!asset) {
|
||||
if (err) {
|
||||
(*err) += "File open error : " + filepath + "\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
size_t size = AAsset_getLength(asset);
|
||||
if (size <= 0) {
|
||||
if (err) {
|
||||
(*err) += "Invalid file size : " + filepath +
|
||||
" (does the path point to a directory?)";
|
||||
}
|
||||
}
|
||||
out->resize(size);
|
||||
AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
|
||||
AAsset_close(asset);
|
||||
return true;
|
||||
} else {
|
||||
if (err) {
|
||||
(*err) += "No asset manager specified : " + filepath + "\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
std::ifstream f(filepath.c_str(), std::ifstream::binary);
|
||||
if (!f) {
|
||||
if (err) {
|
||||
@@ -1840,6 +1979,7 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
||||
f.close();
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WriteWholeFile(std::string *err, const std::string &filepath,
|
||||
@@ -2020,21 +2160,6 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t *GetBufferAddress(const int i, const Accessor &accessor, const BufferView &bufferViewObject, const Buffer &buffer) {
|
||||
|
||||
if (i >= int(accessor.count)) return nullptr;
|
||||
|
||||
int byte_stride = accessor.ByteStride(bufferViewObject);
|
||||
if (byte_stride == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO(syoyo): Bounds check.
|
||||
const uint8_t *base_addr = buffer.data.data() + bufferViewObject.byteOffset + accessor.byteOffset;
|
||||
const uint8_t *addr = base_addr + i * byte_stride;
|
||||
return addr;
|
||||
}
|
||||
|
||||
static bool ParseJsonAsValue(Value *ret, const json &o) {
|
||||
Value val{};
|
||||
switch (o.type()) {
|
||||
@@ -2401,7 +2526,7 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
||||
static bool ParseImage(Image *image, const int image_idx, std::string *err, std::string *warn,
|
||||
const json &o, const std::string &basedir,
|
||||
FsCallbacks *fs,
|
||||
LoadImageDataFunction *LoadImageData = nullptr,
|
||||
@@ -2413,24 +2538,25 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
||||
bool hasBufferView = (o.find("bufferView") != o.end());
|
||||
bool hasURI = (o.find("uri") != o.end());
|
||||
|
||||
ParseStringProperty(&image->name, err, o, "name", false);
|
||||
|
||||
if (hasBufferView && hasURI) {
|
||||
// Should not both defined.
|
||||
if (err) {
|
||||
(*err) +=
|
||||
"Only one of `bufferView` or `uri` should be defined, but both are "
|
||||
"defined for Image.\n";
|
||||
"defined for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasBufferView && !hasURI) {
|
||||
if (err) {
|
||||
(*err) += "Neither required `bufferView` nor `uri` defined for Image.\n";
|
||||
(*err) += "Neither required `bufferView` nor `uri` defined for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseStringProperty(&image->name, err, o, "name", false);
|
||||
ParseExtensionsProperty(&image->extensions, err, o);
|
||||
ParseExtrasProperty(&image->extras, o);
|
||||
|
||||
@@ -2438,7 +2564,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
||||
double bufferView = -1;
|
||||
if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true)) {
|
||||
if (err) {
|
||||
(*err) += "Failed to parse `bufferView` for Image.\n";
|
||||
(*err) += "Failed to parse `bufferView` for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2468,7 +2594,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
||||
std::string tmp_err;
|
||||
if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
|
||||
if (err) {
|
||||
(*err) += "Failed to parse `uri` for Image.\n";
|
||||
(*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2478,7 +2604,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
||||
if (IsDataURI(uri)) {
|
||||
if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
|
||||
if (err) {
|
||||
(*err) += "Failed to decode 'uri' for image parameter.\n";
|
||||
(*err) += "Failed to decode 'uri' for image[" + std::to_string(image_idx) + "] name = [" + image->name + "]\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2491,7 +2617,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
||||
#endif
|
||||
if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
|
||||
if (warn) {
|
||||
(*warn) += "Failed to load external 'uri' for image parameter\n";
|
||||
(*warn) += "Failed to load external 'uri' for image[" + std::to_string(image_idx) + "] name = [" + image->name + "]\n";
|
||||
}
|
||||
// If the image cannot be loaded, keep uri as image->uri.
|
||||
return true;
|
||||
@@ -2499,7 +2625,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
||||
|
||||
if (img.empty()) {
|
||||
if (warn) {
|
||||
(*warn) += "Image is empty.\n";
|
||||
(*warn) += "Image data is empty for image[" + std::to_string(image_idx) + "] name = [" + image->name + "] \n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2511,7 +2637,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return (*LoadImageData)(image, err, warn, 0, 0, &img.at(0),
|
||||
return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
|
||||
static_cast<int>(img.size()), load_image_user_data);
|
||||
}
|
||||
|
||||
@@ -2701,10 +2827,7 @@ static bool ParseBufferView(BufferView *bufferView, std::string *err,
|
||||
|
||||
static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
|
||||
double bufferView = -1.0;
|
||||
if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true,
|
||||
"Accessor")) {
|
||||
return false;
|
||||
}
|
||||
ParseNumberProperty(&bufferView, err, o, "bufferView", false, "Accessor");
|
||||
|
||||
double byteOffset = 0.0;
|
||||
ParseNumberProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
|
||||
@@ -2786,7 +2909,184 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParsePrimitive(Primitive *primitive, std::string *err,
|
||||
#ifdef TINYGLTF_ENABLE_DRACO
|
||||
|
||||
static void DecodeIndexBuffer(draco::Mesh* mesh, size_t componentSize, std::vector<uint8_t>& outBuffer)
|
||||
{
|
||||
if (componentSize == 4)
|
||||
{
|
||||
assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
|
||||
memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0], outBuffer.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t faceStride = componentSize * 3;
|
||||
for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f)
|
||||
{
|
||||
const draco::Mesh::Face& face = mesh->face(f);
|
||||
if (componentSize == 2)
|
||||
{
|
||||
uint16_t indices[3] = { (uint16_t)face[0].value(), (uint16_t)face[1].value(), (uint16_t)face[2].value() };
|
||||
memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], faceStride);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t indices[3] = { (uint8_t)face[0].value(), (uint8_t)face[1].value(), (uint8_t)face[2].value() };
|
||||
memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], faceStride);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static bool GetAttributeForAllPoints(draco::Mesh* mesh, const draco::PointAttribute* pAttribute, std::vector<uint8_t>& outBuffer)
|
||||
{
|
||||
size_t byteOffset = 0;
|
||||
T values[4] = { 0, 0, 0, 0 };
|
||||
for (draco::PointIndex i(0); i < mesh->num_points(); ++i)
|
||||
{
|
||||
const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
|
||||
if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(), values))
|
||||
return false;
|
||||
|
||||
memcpy(outBuffer.data() + byteOffset, &values[0], sizeof(T) * pAttribute->num_components());
|
||||
byteOffset += sizeof(T) * pAttribute->num_components();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh* mesh, const draco::PointAttribute* pAttribute, std::vector<uint8_t>& outBuffer)
|
||||
{
|
||||
bool decodeResult = false;
|
||||
switch (componentType)
|
||||
{
|
||||
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
||||
decodeResult = GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
|
||||
break;
|
||||
case TINYGLTF_COMPONENT_TYPE_BYTE:
|
||||
decodeResult = GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
|
||||
break;
|
||||
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
||||
decodeResult = GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
|
||||
break;
|
||||
case TINYGLTF_COMPONENT_TYPE_SHORT:
|
||||
decodeResult = GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
|
||||
break;
|
||||
case TINYGLTF_COMPONENT_TYPE_INT:
|
||||
decodeResult = GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
|
||||
break;
|
||||
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
|
||||
decodeResult = GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
|
||||
break;
|
||||
case TINYGLTF_COMPONENT_TYPE_FLOAT:
|
||||
decodeResult = GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
|
||||
break;
|
||||
case TINYGLTF_COMPONENT_TYPE_DOUBLE:
|
||||
decodeResult = GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return decodeResult;
|
||||
}
|
||||
|
||||
static bool ParseDracoExtension(Primitive *primitive, Model *model, std::string *err, const Value &dracoExtensionValue)
|
||||
{
|
||||
auto bufferViewValue = dracoExtensionValue.Get("bufferView");
|
||||
if (!bufferViewValue.IsInt())
|
||||
return false;
|
||||
auto attributesValue = dracoExtensionValue.Get("attributes");
|
||||
if (!attributesValue.IsObject())
|
||||
return false;
|
||||
|
||||
auto attributesObject = attributesValue.Get<Value::Object>();
|
||||
int bufferView = bufferViewValue.Get<int>();
|
||||
|
||||
BufferView& view = model->bufferViews[bufferView];
|
||||
Buffer& buffer = model->buffers[view.buffer];
|
||||
// BufferView has already been decoded
|
||||
if (view.dracoDecoded)
|
||||
return true;
|
||||
view.dracoDecoded = true;
|
||||
|
||||
const char* bufferViewData = reinterpret_cast<const char*>(buffer.data.data() + view.byteOffset);
|
||||
size_t bufferViewSize = view.byteLength;
|
||||
|
||||
// decode draco
|
||||
draco::DecoderBuffer decoderBuffer;
|
||||
decoderBuffer.Init(bufferViewData, bufferViewSize);
|
||||
draco::Decoder decoder;
|
||||
auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
|
||||
if (!decodeResult.ok()) {
|
||||
return false;
|
||||
}
|
||||
const std::unique_ptr<draco::Mesh>& mesh = decodeResult.value();
|
||||
|
||||
// create new bufferView for indices
|
||||
if (primitive->indices >= 0)
|
||||
{
|
||||
int32_t componentSize = GetComponentSizeInBytes(model->accessors[primitive->indices].componentType);
|
||||
Buffer decodedIndexBuffer;
|
||||
decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
|
||||
|
||||
DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
|
||||
|
||||
model->buffers.emplace_back(std::move(decodedIndexBuffer));
|
||||
|
||||
BufferView decodedIndexBufferView;
|
||||
decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
|
||||
decodedIndexBufferView.byteLength = int(mesh->num_faces() * 3 * componentSize);
|
||||
decodedIndexBufferView.byteOffset = 0;
|
||||
decodedIndexBufferView.byteStride = 0;
|
||||
decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
||||
model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
|
||||
|
||||
model->accessors[primitive->indices].bufferView = int(model->bufferViews.size() - 1);
|
||||
model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
|
||||
}
|
||||
|
||||
for (const auto& attribute : attributesObject)
|
||||
{
|
||||
if (!attribute.second.IsInt())
|
||||
return false;
|
||||
auto primitiveAttribute = primitive->attributes.find(attribute.first);
|
||||
if (primitiveAttribute == primitive->attributes.end())
|
||||
return false;
|
||||
|
||||
int dracoAttributeIndex = attribute.second.Get<int>();
|
||||
const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
|
||||
const auto pBuffer = pAttribute->buffer();
|
||||
const auto componentType = model->accessors[primitiveAttribute->second].componentType;
|
||||
|
||||
// Create a new buffer for this decoded buffer
|
||||
Buffer decodedBuffer;
|
||||
size_t bufferSize = mesh->num_points() * pAttribute->num_components() * GetComponentSizeInBytes(componentType);
|
||||
decodedBuffer.data.resize(bufferSize);
|
||||
|
||||
if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute, decodedBuffer.data))
|
||||
return false;
|
||||
|
||||
model->buffers.emplace_back(std::move(decodedBuffer));
|
||||
|
||||
BufferView decodedBufferView;
|
||||
decodedBufferView.buffer = int(model->buffers.size() - 1);
|
||||
decodedBufferView.byteLength = bufferSize;
|
||||
decodedBufferView.byteOffset = pAttribute->byte_offset();
|
||||
decodedBufferView.byteStride = pAttribute->byte_stride();
|
||||
decodedBufferView.target = primitive->indices >= 0 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER : TINYGLTF_TARGET_ARRAY_BUFFER;
|
||||
model->bufferViews.emplace_back(std::move(decodedBufferView));
|
||||
|
||||
model->accessors[primitiveAttribute->second].bufferView = int(model->bufferViews.size() - 1);
|
||||
model->accessors[primitiveAttribute->second].count = int(mesh->num_points());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
|
||||
const json &o) {
|
||||
double material = -1.0;
|
||||
ParseNumberProperty(&material, err, o, "material", false);
|
||||
@@ -2826,10 +3126,22 @@ static bool ParsePrimitive(Primitive *primitive, std::string *err,
|
||||
|
||||
ParseExtrasProperty(&(primitive->extras), o);
|
||||
|
||||
ParseExtensionsProperty(&primitive->extensions, err, o);
|
||||
|
||||
#ifdef TINYGLTF_ENABLE_DRACO
|
||||
auto dracoExtension = primitive->extensions.find("KHR_draco_mesh_compression");
|
||||
if (dracoExtension != primitive->extensions.end())
|
||||
{
|
||||
ParseDracoExtension(primitive, model, err, dracoExtension->second);
|
||||
}
|
||||
#else
|
||||
(void)model;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseMesh(Mesh *mesh, std::string *err, const json &o) {
|
||||
static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o) {
|
||||
ParseStringProperty(&mesh->name, err, o, "name", false);
|
||||
|
||||
mesh->primitives.clear();
|
||||
@@ -2838,7 +3150,7 @@ static bool ParseMesh(Mesh *mesh, std::string *err, const json &o) {
|
||||
for (json::const_iterator i = primObject.value().begin();
|
||||
i != primObject.value().end(); i++) {
|
||||
Primitive primitive;
|
||||
if (ParsePrimitive(&primitive, err, i.value())) {
|
||||
if (ParsePrimitive(&primitive, model, err, i.value())) {
|
||||
// Only add the primitive if the parsing succeeds.
|
||||
mesh->primitives.push_back(primitive);
|
||||
}
|
||||
@@ -3040,13 +3352,8 @@ static bool ParseAnimation(Animation *animation, std::string *err,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!ParseStringProperty(&sampler.interpolation, err, s,
|
||||
"interpolation", true)) {
|
||||
if (err) {
|
||||
(*err) += "`interpolation` field is missing in animation.sampler\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
|
||||
false);
|
||||
if (!ParseNumberProperty(&outputIndex, err, s, "output", true)) {
|
||||
if (err) {
|
||||
(*err) += "`output` field is missing in animation.sampler\n";
|
||||
@@ -3497,7 +3804,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
return false;
|
||||
}
|
||||
Mesh mesh;
|
||||
if (!ParseMesh(&mesh, err, it->get<json>())) {
|
||||
if (!ParseMesh(&mesh, model, err, it->get<json>())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3515,7 +3822,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
{
|
||||
if (primitive.indices > -1) // has indices from parsing step, must be Element Array Buffer
|
||||
{
|
||||
model->bufferViews[size_t(model->accessors[size_t(primitive.indices)].bufferView)]
|
||||
model->bufferViews[model->accessors[primitive.indices].bufferView]
|
||||
.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
|
||||
// we could optionally check if acessors' bufferView type is Scalar, as it should be
|
||||
}
|
||||
@@ -3639,15 +3946,16 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
|
||||
json::const_iterator it(root.begin());
|
||||
json::const_iterator itEnd(root.end());
|
||||
for (; it != itEnd; it++) {
|
||||
int idx = 0;
|
||||
for (; it != itEnd; it++, idx++) {
|
||||
if (!it.value().is_object()) {
|
||||
if (err) {
|
||||
(*err) += "`images' does not contain an JSON object.";
|
||||
(*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Image image;
|
||||
if (!ParseImage(&image, err, warn, it.value(), base_dir, &fs,
|
||||
if (!ParseImage(&image, idx, err, warn, it.value(), base_dir, &fs,
|
||||
&this->LoadImageData, load_image_user_data_)) {
|
||||
return false;
|
||||
}
|
||||
@@ -3657,7 +3965,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
if (size_t(image.bufferView) >= model->bufferViews.size()) {
|
||||
if (err) {
|
||||
std::stringstream ss;
|
||||
ss << "bufferView \"" << image.bufferView
|
||||
ss << "image[" << idx << "] bufferView \"" << image.bufferView
|
||||
<< "\" not found in the scene." << std::endl;
|
||||
(*err) += ss.str();
|
||||
}
|
||||
@@ -3674,7 +3982,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool ret = LoadImageData(&image, err, warn, image.width, image.height,
|
||||
bool ret = LoadImageData(&image, idx, err, warn, image.width, image.height,
|
||||
&buffer.data[bufferView.byteOffset],
|
||||
static_cast<int>(bufferView.byteLength),
|
||||
load_image_user_data_);
|
||||
@@ -4603,8 +4911,8 @@ static void WriteBinaryGltfFile(const std::string &output,
|
||||
|
||||
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info, padding
|
||||
const int length = 12 + 8 + int(content.size()) + padding_size;
|
||||
|
||||
gltfFile.write(header.c_str(), std::streamsize(header.size()));
|
||||
|
||||
gltfFile.write(header.c_str(), header.size());
|
||||
gltfFile.write(reinterpret_cast<const char *>(&version), sizeof(version));
|
||||
gltfFile.write(reinterpret_cast<const char *>(&length), sizeof(length));
|
||||
|
||||
@@ -4613,12 +4921,12 @@ static void WriteBinaryGltfFile(const std::string &output,
|
||||
const int model_format = 0x4E4F534A;
|
||||
gltfFile.write(reinterpret_cast<const char *>(&model_length), sizeof(model_length));
|
||||
gltfFile.write(reinterpret_cast<const char *>(&model_format), sizeof(model_format));
|
||||
gltfFile.write(content.c_str(), std::streamsize(content.size()));
|
||||
gltfFile.write(content.c_str(), content.size());
|
||||
|
||||
// Chunk must be multiplies of 4, so pad with spaces
|
||||
if (padding_size > 0) {
|
||||
const std::string padding = std::string(size_t(padding_size), ' ');
|
||||
gltfFile.write(padding.c_str(), std::streamsize(padding.size()));
|
||||
const std::string padding = std::string(padding_size, ' ');
|
||||
gltfFile.write(padding.c_str(), padding.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
294
tiny_gltf_util.h
294
tiny_gltf_util.h
@@ -1,294 +0,0 @@
|
||||
//
|
||||
// TinyGLTF utility functions
|
||||
//
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 - 2018 Syoyo Fujita, Aurélien Chatelain and many
|
||||
// contributors.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
namespace tinygltf {
|
||||
|
||||
namespace util {
|
||||
|
||||
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**";
|
||||
}
|
||||
|
||||
static std::string PrintWrapMode(int mode) {
|
||||
if (mode == TINYGLTF_TEXTURE_WRAP_REPEAT) {
|
||||
return "REPEAT";
|
||||
} else if (mode == TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE) {
|
||||
return "CLAMP_TO_EDGE";
|
||||
} else if (mode == TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT) {
|
||||
return "MIRRORED_REPEAT";
|
||||
}
|
||||
|
||||
return "**UNKNOWN**";
|
||||
}
|
||||
|
||||
static std::string PrintFilterMode(int mode) {
|
||||
if (mode == TINYGLTF_TEXTURE_FILTER_NEAREST) {
|
||||
return "NEAREST";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_LINEAR) {
|
||||
return "LINEAR";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST) {
|
||||
return "NEAREST_MIPMAP_NEAREST";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR) {
|
||||
return "NEAREST_MIPMAP_LINEAR";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST) {
|
||||
return "LINEAR_MIPMAP_NEAREST";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR) {
|
||||
return "LINEAR_MIPMAP_LINEAR";
|
||||
}
|
||||
return "**UNKNOWN**";
|
||||
}
|
||||
|
||||
static int GetAnimationSamplerInputCount(const tinygltf::AnimationSampler &sampler, const tinygltf::Model &model)
|
||||
{
|
||||
const tinygltf::Accessor &accessor = model.accessors[sampler.input];
|
||||
return accessor.count;
|
||||
}
|
||||
|
||||
static int GetAnimationSamplerOutputCount(const tinygltf::AnimationSampler &sampler, const tinygltf::Model &model)
|
||||
{
|
||||
const tinygltf::Accessor &accessor = model.accessors[sampler.output];
|
||||
return accessor.count;
|
||||
}
|
||||
|
||||
static bool GetAnimationSamplerInputMinMax(const tinygltf::AnimationSampler &sampler, const tinygltf::Model &model, float *min_value, float *max_value)
|
||||
{
|
||||
const tinygltf::Accessor &accessor = model.accessors[sampler.input];
|
||||
|
||||
// Assume scalar value.
|
||||
if ((accessor.minValues.size() > 0) &&
|
||||
(accessor.maxValues.size() > 0)) {
|
||||
(*min_value) = accessor.minValues[0];
|
||||
(*max_value) = accessor.maxValues[0];
|
||||
return true;
|
||||
} else {
|
||||
(*min_value) = 0.0f;
|
||||
(*max_value) = 0.0f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function for decoding animation value
|
||||
static inline float DecodeAnimationChannelValue(int8_t c) {
|
||||
return std::max(float(c) / 127.0f, -1.0f);
|
||||
}
|
||||
static inline float DecodeAnimationChannelValue(uint8_t c) {
|
||||
return float(c) / 255.0f;
|
||||
}
|
||||
static inline float DecodeAnimationChannelValue(int16_t c) {
|
||||
return std::max(float(c) / 32767.0f, -1.0f);
|
||||
}
|
||||
static inline float DecodeAnimationChannelValue(uint16_t c) {
|
||||
return float(c) / 65525.0f;
|
||||
}
|
||||
|
||||
static bool DecodeScalarAnimationValue(const size_t i, const tinygltf::Accessor &accessor, const tinygltf::Model &model, float *scalar)
|
||||
{
|
||||
const BufferView &bufferView = model.bufferViews[accessor.bufferView];
|
||||
const Buffer &buffer = model.buffers[bufferView.buffer];
|
||||
|
||||
const uint8_t *addr = GetBufferAddress(i, accessor, bufferView, buffer);
|
||||
if (addr == nullptr) {
|
||||
std::cerr << "Invalid glTF data?" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
float value = 0.0f;
|
||||
|
||||
if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
|
||||
value = DecodeAnimationChannelValue(*(reinterpret_cast<const int8_t*>(addr)));
|
||||
} else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
|
||||
value = DecodeAnimationChannelValue(*(reinterpret_cast<const uint8_t*>(addr)));
|
||||
} else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
||||
value = DecodeAnimationChannelValue(*(reinterpret_cast<const int16_t*>(addr)));
|
||||
} else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
||||
value = DecodeAnimationChannelValue(*(reinterpret_cast<const uint16_t*>(addr)));
|
||||
} else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
|
||||
value = *(reinterpret_cast<const float*>(addr));
|
||||
} else {
|
||||
std::cerr << "??? Unknown componentType : " << PrintComponentType(accessor.componentType) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
(*scalar) = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DecodeTranslationAnimationValue(const size_t i, const tinygltf::Accessor &accessor, const tinygltf::Model &model, float *xyz)
|
||||
{
|
||||
if (accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
|
||||
std::cerr << "`translation` must be float type." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const BufferView &bufferView = model.bufferViews[accessor.bufferView];
|
||||
const Buffer &buffer = model.buffers[bufferView.buffer];
|
||||
|
||||
const uint8_t *addr = GetBufferAddress(i, accessor, bufferView, buffer);
|
||||
if (addr == nullptr) {
|
||||
std::cerr << "Invalid glTF data?" << std::endl;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float *ptr = reinterpret_cast<const float*>(addr);
|
||||
|
||||
xyz[0] = *(ptr + 0);
|
||||
xyz[1] = *(ptr + 1);
|
||||
xyz[2] = *(ptr + 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DecodeScaleAnimationValue(const size_t i, const tinygltf::Accessor &accessor, const tinygltf::Model &model, float *xyz)
|
||||
{
|
||||
if (accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
|
||||
std::cerr << "`scale` must be float type." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const BufferView &bufferView = model.bufferViews[accessor.bufferView];
|
||||
const Buffer &buffer = model.buffers[bufferView.buffer];
|
||||
|
||||
const uint8_t *addr = GetBufferAddress(i, accessor, bufferView, buffer);
|
||||
if (addr == nullptr) {
|
||||
std::cerr << "Invalid glTF data?" << std::endl;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float *ptr = reinterpret_cast<const float*>(addr);
|
||||
|
||||
xyz[0] = *(ptr + 0);
|
||||
xyz[1] = *(ptr + 1);
|
||||
xyz[2] = *(ptr + 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DecodeRotationAnimationValue(const size_t i, const tinygltf::Accessor &accessor, const tinygltf::Model &model, float *xyzw)
|
||||
{
|
||||
const BufferView &bufferView = model.bufferViews[accessor.bufferView];
|
||||
const Buffer &buffer = model.buffers[bufferView.buffer];
|
||||
|
||||
const uint8_t *addr = GetBufferAddress(i, accessor, bufferView, buffer);
|
||||
if (addr == nullptr) {
|
||||
std::cerr << "Invalid glTF data?" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
float value = 0.0f;
|
||||
|
||||
if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
|
||||
xyzw[0] = DecodeAnimationChannelValue(*(reinterpret_cast<const int8_t*>(addr) + 0));
|
||||
xyzw[1] = DecodeAnimationChannelValue(*(reinterpret_cast<const int8_t*>(addr) + 1));
|
||||
xyzw[2] = DecodeAnimationChannelValue(*(reinterpret_cast<const int8_t*>(addr) + 2));
|
||||
xyzw[3] = DecodeAnimationChannelValue(*(reinterpret_cast<const int8_t*>(addr) + 3));
|
||||
} else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
|
||||
xyzw[0] = DecodeAnimationChannelValue(*(reinterpret_cast<const uint8_t*>(addr) + 0));
|
||||
xyzw[1] = DecodeAnimationChannelValue(*(reinterpret_cast<const uint8_t*>(addr) + 1));
|
||||
xyzw[2] = DecodeAnimationChannelValue(*(reinterpret_cast<const uint8_t*>(addr) + 2));
|
||||
xyzw[3] = DecodeAnimationChannelValue(*(reinterpret_cast<const uint8_t*>(addr) + 3));
|
||||
} else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
||||
xyzw[0] = DecodeAnimationChannelValue(*(reinterpret_cast<const int16_t*>(addr) + 0));
|
||||
xyzw[1] = DecodeAnimationChannelValue(*(reinterpret_cast<const int16_t*>(addr) + 1));
|
||||
xyzw[2] = DecodeAnimationChannelValue(*(reinterpret_cast<const int16_t*>(addr) + 2));
|
||||
xyzw[3] = DecodeAnimationChannelValue(*(reinterpret_cast<const int16_t*>(addr) + 3));
|
||||
} else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
||||
xyzw[0] = DecodeAnimationChannelValue(*(reinterpret_cast<const uint16_t*>(addr) + 0));
|
||||
xyzw[1] = DecodeAnimationChannelValue(*(reinterpret_cast<const uint16_t*>(addr) + 1));
|
||||
xyzw[2] = DecodeAnimationChannelValue(*(reinterpret_cast<const uint16_t*>(addr) + 2));
|
||||
xyzw[3] = DecodeAnimationChannelValue(*(reinterpret_cast<const uint16_t*>(addr) + 3));
|
||||
} else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
|
||||
xyzw[0] = *(reinterpret_cast<const float*>(addr) + 0);
|
||||
xyzw[1] = *(reinterpret_cast<const float*>(addr) + 1);
|
||||
xyzw[2] = *(reinterpret_cast<const float*>(addr) + 2);
|
||||
xyzw[3] = *(reinterpret_cast<const float*>(addr) + 3);
|
||||
} else {
|
||||
std::cerr << "??? Unknown componentType : " << PrintComponentType(accessor.componentType) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
} // namespace tinygltf
|
||||
Reference in New Issue
Block a user