Compare commits

...

38 Commits

Author SHA1 Message Date
Syoyo Fujita
29c431b2f2 Add build option with draco in Makefile. 2019-03-08 14:01:06 +09:00
Syoyo Fujita
d06b2c2022 Merge pull request #154 from Ybalrid/patch-3
Fix the display of the version history
2019-03-08 13:32:31 +09:00
Arthur Brainville
339c9d578a Fix the display of the version history
Markdown is like HTML: simple line-breaks don't work.

Besides, making a list with dots here looks nicer :)
2019-03-07 19:40:57 +01:00
Syoyo Fujita
7c315fa8a8 Merge pull request #153 from Ybalrid/patch-2
fix a few typos in README.md
2019-03-07 21:30:20 +09:00
Arthur Brainville
0f04ed018a fix a few typos in README.md 2019-03-07 13:23:59 +01:00
Syoyo Fujita
ca56f726d6 Merge branch '16bit-lodepng' 2019-03-07 21:04:25 +09:00
Syoyo Fujita
e8a46c4e1d Update README.
Check bit depth when saving image as PNG.
2019-03-07 20:50:58 +09:00
Syoyo Fujita
9cd14a461b Update README. 2019-03-07 01:05:02 +09:00
Syoyo Fujita
1ef603ea2a Merge pull request #150 from Ybalrid/sparse_accessor
Sparse accessor
2019-03-07 00:49:29 +09:00
Arthur Brainville (Ybalrid)
14d259f361 glview can now load static geometry modified by sparse accessor 2019-03-06 14:48:44 +00:00
Arthur Brainville (Ybalrid)
9223d3133a Ran clang-format on tiny_gltf.h 2019-03-06 14:00:56 +00:00
Arthur Brainville (Ybalrid)
9b321a8515 clang-format + added sparse accessor in loader_example 2019-03-06 12:39:39 +00:00
Arthur Brainville (Ybalrid)
7e9f734d73 Parse sparse accessors 2019-03-06 12:27:23 +00:00
Arthur Brainville (Ybalrid)
9d86405d3d Fix accessor ctor 2019-03-06 11:33:30 +00:00
Arthur Brainville (Ybalrid)
1ccb4ff580 added sparse structure to accessors 2019-03-06 11:30:00 +01:00
Syoyo Fujita
8fd91aea04 Update TinyEXR. 2019-03-04 02:15:03 +09:00
Syoyo Fujita
8f76d790b8 Merge pull request #148 from Ybalrid/16bit-lodepng-good-byteswap
When writing out a 16bit image with lodepng, simplify the byteswap to big-endian
2019-03-04 01:15:20 +09:00
Arthur Brainville (Ybalrid)
853f6df7b5 Simplify byteswap code to convert to big endian 16bit 2019-03-03 16:26:20 +01:00
Syoyo Fujita
758a1240c9 Reorder 16 bit pixel data to big endian when saving it as 16 png, since lodepng::encode expects image data is in big endian manner.
Add OpenEXR saver for 16bit image as a bonus.
2019-03-03 21:21:18 +09:00
Syoyo Fujita
7bdfed3bec Add lodepng to save 16bit PNG.
Suppress clang/gcc warnings.
2019-03-03 17:04:49 +09:00
Syoyo Fujita
bf9c2f4abd Merge pull request #146 from WARP-LAB/master
Fix for when json and stb is aready used in project
2019-03-03 15:31:09 +09:00
kroko
fc0116b323 rename TINYGLTF_BYPASS_INCLUDE_x to TINYGLTF_NO_INCLUDE_x 2019-03-03 08:28:49 +02:00
Syoyo Fujita
962552c5c8 Merge pull request #145 from Ybalrid/upgrade_stb_libs
Upgrade the STB libraries, and fix #132
2019-03-03 13:39:10 +09:00
kroko
606e5dde31 be more precise in readme about include bypassing flags 2019-03-03 01:56:34 +02:00
kroko
6a0d4c57b1 Fix for when json and stb is aready used in project v2 2019-03-03 01:28:45 +02:00
kroko
f4b6d11abc Fix for when json and stb is aready used in project 2019-03-03 01:11:31 +02:00
Arthur Brainville (Ybalrid)
5a4c898912 Fixed wrong metadata in case 16bit image wasn't actually loaded
The fallback to 8 bit would have been broken.
2019-03-02 22:03:34 +01:00
Arthur Brainville (Ybalrid)
f2addc0e44 16bit images are 16bit images: added Image::bits and Image::pixel_type 2019-03-02 22:00:48 +01:00
Arthur Brainville (Ybalrid)
70d16a7b92 Upgrade the STB libraries, and fix #132 2019-03-02 16:10:54 +01:00
Syoyo Fujita
326d7ea310 Merge pull request #144 from SaschaWillems/master
Add direct access to texture coordinate set on texture parameter
2019-02-24 14:42:51 +09:00
Sascha Willems
eb011068c0 Added function to easily access texture coordinate set index on a texture parameter 2019-02-23 21:15:45 +01:00
Syoyo Fujita
87be0ce34b Define WIN32_LEAN_AND_MEAN to save the number of including files. 2019-02-19 21:36:32 +09:00
Syoyo Fujita
7d9a0bda3a Define NOMINMAX to avoid defining MIN/MAX macros on Windows. Without defining NOMINMAX, MIN/MAX macro would affect other header/c++ files. Fixes #143 2019-02-19 16:03:23 +09:00
Syoyo Fujita
7ece5c8275 Merge pull request #142 from ebirenbaum/animation-sampler-interpolation
Change AnimationSampler::interpolation to a non-required field.
2019-02-15 14:04:06 +09:00
Evan Birenbaum
6bdffedcbe Change AnimationSampler::interpolation to a non-required field. The spec states to default to LINEAR when not present. 2019-02-14 13:30:57 -08:00
Syoyo Fujita
d2fb7dc2af Merge pull request #140 from timmmeh/master
Adding default values for min and mag filter.
2019-02-10 00:01:11 +09:00
timmmeh
62a72c4845 fix compile 2019-01-31 11:46:19 -08:00
timmmeh
73584ba7b7 Adding default values for min and mag filter. 2019-01-30 18:38:46 -08:00
18 changed files with 13503 additions and 3348 deletions

View File

@@ -2,8 +2,12 @@
# 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
# With draco
# EXTRA_CXXFLAGS := -I../draco/src/ -I../draco/build -DTINYGLTF_ENABLE_DRACO -L../draco/build
# EXTRA_LINKFLAGS := -L../draco/build/ -ldracodec -ldraco
all:
clang++ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o loader_example loader_example.cc
clang++ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o loader_example loader_example.cc $(EXTRA_LINKFLAGS)
lint:
deps/cpplint.py tiny_gltf.h

View File

@@ -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. Sparse accessor support)
- v2.1.0 release(Draco support)
- v2.0.0 release(22 Aug, 2018)!
## Builds
@@ -38,14 +40,17 @@ 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
* Morph traget
* [x] Sparse accessor
* Load glTF from memory
* Custom callback handler
* [x] Image load
* [x] Image save
* Extensions
* [x] Draco mesh decoding
@@ -68,13 +73,13 @@ v2.0.0 release(22 Aug, 2018)!
* [ ] Write C++ code generator which emits C++ code from JSON schema for robust parsing.
* [ ] Mesh Compression/decompression(Open3DGC, etc)
* [x] Load Draco compressed mesh
* [x] Save Draco compressed mesh
* [ ] Save Draco compressed mesh
* [ ] Open3DGC?
* [ ] Support `extensions` and `extras` property
* [ ] HDR image?
* [ ] OpenEXR extension through TinyEXR.
* [ ] Write example and tests for `animation` and `skin`
* [ ] Skinning
* [ ] Morph targets
* [ ] 16bit PNG support in Serialization
* [ ] Write example and tests for `animation` and `skin`
## Licenses
@@ -104,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());
@@ -131,9 +136,13 @@ if (!ret) {
* `TINYGLTF_NOEXCEPTION` : Disable C++ exception in JSON parsing. You can use `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION` and `TINYGLTF_NOEXCEPTION` to fully remove C++ exception codes when compiling TinyGLTF.
* `TINYGLTF_NO_STB_IMAGE` : Do not load images with stb_image. Instead use `TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)` to set a callback for loading images.
* `TINYGLTF_NO_STB_IMAGE_WRITE` : Do not write images with stb_image_write. Instead use `TinyGLTF::SetImageWriter(WriteimageDataFunction WriteImageData, void *user_data)` to set a callback for writing images.
* `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_NO_EXTERNAL_IMAGE` : Do not try to load external image file. This option would be helpful if you do not want to load image files 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.
* `TINYGLTF_NO_INCLUDE_JSON `: Disable including `json.hpp` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
* `TINYGLTF_NO_INCLUDE_STB_IMAGE `: Disable including `stb_image.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
* `TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE `: Disable including `stb_image_write.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
### Saving gltTF 2.0 model
* [ ] Buffers.

View File

@@ -50,8 +50,9 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
From spec2.0 readme:
https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
... drawArrays function should be used with a count equal to
the count property of any of the accessors referenced by the attributes
property (they are all equal for a given primitive).
the count property of any of the accessors referenced by the
attributes property (they are all equal for a given
primitive).
*/
}
@@ -112,8 +113,29 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GLenum format = GL_RGBA;
if (image.component == 1) {
format = GL_RED;
} else if (image.component == 2) {
format = GL_RG;
} else if (image.component == 3) {
format = GL_RGB;
} else {
// ???
}
GLenum type = GL_UNSIGNED_BYTE;
if (image.bits == 8) {
// ok
} else if (image.bits == 16) {
type = GL_UNSIGNED_SHORT;
} else {
// ???
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, &image.image.at(0));
format, type, &image.image.at(0));
}
return vbos;
@@ -255,7 +277,7 @@ void displayLoop(Window &window, const std::string &filename) {
glm::vec3 model_pos = glm::vec3(-3, 0, -3);
// generate a camera view, based on eye-position and lookAt world-position
glm::mat4 view_mat = genView(glm::vec3(2, 2, 2), model_pos);
glm::mat4 view_mat = genView(glm::vec3(2, 2, 20), model_pos);
glm::vec3 sun_position = glm::vec3(3.0, 10.0, -5.0);
glm::vec3 sun_color = glm::vec3(1.0);
@@ -287,6 +309,11 @@ void displayLoop(Window &window, const std::string &filename) {
}
}
static void error_callback(int error, const char *description) {
(void)error;
fprintf(stderr, "Error: %s\n", description);
}
int main(int argc, char **argv) {
std::string filename = "../../models/Cube/Cube.gltf";
@@ -294,14 +321,18 @@ int main(int argc, char **argv) {
filename = argv[1];
}
glfwSetErrorCallback(error_callback);
if (!glfwInit()) return -1;
// Force create 3.3 profile.
// Force create OpenGL 3.3
// NOTE(syoyo): Linux + NVIDIA driver segfaults for some reason? commenting out glfwWindowHint will work.
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
#endif
Window window = Window(800, 600, "TinyGLTF basic example");

View 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

File diff suppressed because it is too large Load Diff

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

View File

@@ -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

View File

@@ -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() {

View File

@@ -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);

View File

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

View File

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

View File

@@ -28,7 +28,6 @@
#include "tiny_gltf.h"
#endif
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
#define CheckGLErrors(desc) \
@@ -55,7 +54,9 @@ float eye[3], lookat[3], up[3];
GLFWwindow *window;
typedef struct { GLuint vb; } GLBufferState;
typedef struct {
GLuint vb;
} GLBufferState;
typedef struct {
std::vector<GLuint> diffuseTex; // for each primitive in mesh
@@ -254,6 +255,26 @@ void motionFunc(GLFWwindow *window, double mouse_x, double mouse_y) {
prevMouseY = mouse_y;
}
static size_t ComponentTypeByteSize(int type) {
switch (type) {
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
case TINYGLTF_COMPONENT_TYPE_BYTE:
return sizeof(char);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
case TINYGLTF_COMPONENT_TYPE_SHORT:
return sizeof(short);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
case TINYGLTF_COMPONENT_TYPE_INT:
return sizeof(int);
case TINYGLTF_COMPONENT_TYPE_FLOAT:
return sizeof(float);
case TINYGLTF_COMPONENT_TYPE_DOUBLE:
return sizeof(double);
default:
return 0;
}
}
static void SetupMeshState(tinygltf::Model &model, GLuint progId) {
// Buffer
{
@@ -264,14 +285,117 @@ static void SetupMeshState(tinygltf::Model &model, GLuint progId) {
continue; // Unsupported bufferView.
}
int sparse_accessor = -1;
for (size_t a_i = 0; a_i < model.accessors.size(); ++a_i) {
const auto &accessor = model.accessors[a_i];
if (accessor.bufferView == i) {
std::cout << i << " is used by accessor " << a_i << std::endl;
if (accessor.sparse.isSparse) {
std::cout
<< "WARN: this bufferView has at least one sparse accessor to "
"it. We are going to load the data as patched by this "
"sparse accessor, not the original data"
<< std::endl;
sparse_accessor = a_i;
break;
}
}
}
const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer];
GLBufferState state;
glGenBuffers(1, &state.vb);
glBindBuffer(bufferView.target, state.vb);
std::cout << "buffer.size= " << buffer.data.size()
<< ", byteOffset = " << bufferView.byteOffset << std::endl;
glBufferData(bufferView.target, bufferView.byteLength,
&buffer.data.at(0) + bufferView.byteOffset, GL_STATIC_DRAW);
if (sparse_accessor < 0)
glBufferData(bufferView.target, bufferView.byteLength,
&buffer.data.at(0) + bufferView.byteOffset,
GL_STATIC_DRAW);
else {
const auto accessor = model.accessors[sparse_accessor];
// copy the buffer to a temporary one for sparse patching
unsigned char *tmp_buffer = new unsigned char[bufferView.byteLength];
memcpy(tmp_buffer, buffer.data.data() + bufferView.byteOffset,
bufferView.byteLength);
const size_t size_of_object_in_buffer =
ComponentTypeByteSize(accessor.componentType);
const size_t size_of_sparse_indices =
ComponentTypeByteSize(accessor.sparse.indices.componentType);
const auto &indices_buffer_view =
model.bufferViews[accessor.sparse.indices.bufferView];
const auto &indices_buffer = model.buffers[indices_buffer_view.buffer];
const auto &values_buffer_view =
model.bufferViews[accessor.sparse.values.bufferView];
const auto &values_buffer = model.buffers[values_buffer_view.buffer];
for (size_t sparse_index = 0; sparse_index < accessor.sparse.count;
++sparse_index) {
int index = 0;
// std::cout << "accessor.sparse.indices.componentType = " <<
// accessor.sparse.indices.componentType << std::endl;
switch (accessor.sparse.indices.componentType) {
case TINYGLTF_COMPONENT_TYPE_BYTE:
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
index = (int)*(
unsigned char *)(indices_buffer.data.data() +
indices_buffer_view.byteOffset +
accessor.sparse.indices.byteOffset +
(sparse_index * size_of_sparse_indices));
break;
case TINYGLTF_COMPONENT_TYPE_SHORT:
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
index = (int)*(
unsigned short *)(indices_buffer.data.data() +
indices_buffer_view.byteOffset +
accessor.sparse.indices.byteOffset +
(sparse_index * size_of_sparse_indices));
break;
case TINYGLTF_COMPONENT_TYPE_INT:
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
index = (int)*(
unsigned int *)(indices_buffer.data.data() +
indices_buffer_view.byteOffset +
accessor.sparse.indices.byteOffset +
(sparse_index * size_of_sparse_indices));
break;
}
std::cout << "updating sparse data at index : " << index
<< std::endl;
// index is now the target of the sparse index to patch in
const unsigned char *read_from =
values_buffer.data.data() +
(values_buffer_view.byteOffset +
accessor.sparse.values.byteOffset) +
(sparse_index * (size_of_object_in_buffer * accessor.type));
/*
std::cout << ((float*)read_from)[0] << "\n";
std::cout << ((float*)read_from)[1] << "\n";
std::cout << ((float*)read_from)[2] << "\n";
*/
unsigned char *write_to =
tmp_buffer + index * (size_of_object_in_buffer * accessor.type);
memcpy(write_to, read_from, size_of_object_in_buffer * accessor.type);
}
// debug:
/*for(size_t p = 0; p < bufferView.byteLength/sizeof(float); p++)
{
float* b = (float*)tmp_buffer;
std::cout << "modified_buffer [" << p << "] = " << b[p] << '\n';
}*/
glBufferData(bufferView.target, bufferView.byteLength, tmp_buffer,
GL_STATIC_DRAW);
delete[] tmp_buffer;
}
glBindBuffer(bufferView.target, 0);
gBufferState[i] = state;
@@ -279,55 +403,55 @@ static void SetupMeshState(tinygltf::Model &model, GLuint progId) {
}
#if 0 // TODO(syoyo): Implement
// Texture
{
for (size_t i = 0; i < model.meshes.size(); i++) {
const tinygltf::Mesh &mesh = model.meshes[i];
// Texture
{
for (size_t i = 0; i < model.meshes.size(); i++) {
const tinygltf::Mesh &mesh = model.meshes[i];
gMeshState[mesh.name].diffuseTex.resize(mesh.primitives.size());
for (size_t primId = 0; primId < mesh.primitives.size(); primId++) {
const tinygltf::Primitive &primitive = mesh.primitives[primId];
gMeshState[mesh.name].diffuseTex.resize(mesh.primitives.size());
for (size_t primId = 0; primId < mesh.primitives.size(); primId++) {
const tinygltf::Primitive &primitive = mesh.primitives[primId];
gMeshState[mesh.name].diffuseTex[primId] = 0;
gMeshState[mesh.name].diffuseTex[primId] = 0;
if (primitive.material < 0) {
continue;
}
tinygltf::Material &mat = model.materials[primitive.material];
// printf("material.name = %s\n", mat.name.c_str());
if (mat.values.find("diffuse") != mat.values.end()) {
std::string diffuseTexName = mat.values["diffuse"].string_value;
if (model.textures.find(diffuseTexName) != model.textures.end()) {
tinygltf::Texture &tex = model.textures[diffuseTexName];
if (scene.images.find(tex.source) != model.images.end()) {
tinygltf::Image &image = model.images[tex.source];
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(tex.target, texId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(tex.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(tex.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (primitive.material < 0) {
continue;
}
tinygltf::Material &mat = model.materials[primitive.material];
// printf("material.name = %s\n", mat.name.c_str());
if (mat.values.find("diffuse") != mat.values.end()) {
std::string diffuseTexName = mat.values["diffuse"].string_value;
if (model.textures.find(diffuseTexName) != model.textures.end()) {
tinygltf::Texture &tex = model.textures[diffuseTexName];
if (scene.images.find(tex.source) != model.images.end()) {
tinygltf::Image &image = model.images[tex.source];
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(tex.target, texId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(tex.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(tex.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Ignore Texture.fomat.
GLenum format = GL_RGBA;
if (image.component == 3) {
format = GL_RGB;
}
glTexImage2D(tex.target, 0, tex.internalFormat, image.width,
image.height, 0, format, tex.type,
&image.image.at(0));
// Ignore Texture.fomat.
GLenum format = GL_RGBA;
if (image.component == 3) {
format = GL_RGB;
}
glTexImage2D(tex.target, 0, tex.internalFormat, image.width,
image.height, 0, format, tex.type,
&image.image.at(0));
CheckErrors("texImage2D");
glBindTexture(tex.target, 0);
CheckErrors("texImage2D");
glBindTexture(tex.target, 0);
printf("TexId = %d\n", texId);
gMeshState[mesh.name].diffuseTex[primId] = texId;
}
}
}
}
}
}
printf("TexId = %d\n", texId);
gMeshState[mesh.name].diffuseTex[primId] = texId;
}
}
}
}
}
}
#endif
glUseProgram(progId);
@@ -348,164 +472,164 @@ static void SetupMeshState(tinygltf::Model &model, GLuint progId) {
#if 0 // TODO(syoyo): Implement
// Setup curves geometry extension
static void SetupCurvesState(tinygltf::Scene &scene, GLuint progId) {
// Find curves primitive.
{
std::map<std::string, tinygltf::Mesh>::const_iterator it(
scene.meshes.begin());
std::map<std::string, tinygltf::Mesh>::const_iterator itEnd(
scene.meshes.end());
// Find curves primitive.
{
std::map<std::string, tinygltf::Mesh>::const_iterator it(
scene.meshes.begin());
std::map<std::string, tinygltf::Mesh>::const_iterator itEnd(
scene.meshes.end());
for (; it != itEnd; it++) {
const tinygltf::Mesh &mesh = it->second;
for (; it != itEnd; it++) {
const tinygltf::Mesh &mesh = it->second;
// Currently we only support one primitive per mesh.
if (mesh.primitives.size() > 1) {
continue;
}
// Currently we only support one primitive per mesh.
if (mesh.primitives.size() > 1) {
continue;
}
for (size_t primId = 0; primId < mesh.primitives.size(); primId++) {
const tinygltf::Primitive &primitive = mesh.primitives[primId];
for (size_t primId = 0; primId < mesh.primitives.size(); primId++) {
const tinygltf::Primitive &primitive = mesh.primitives[primId];
gMeshState[mesh.name].diffuseTex[primId] = 0;
gMeshState[mesh.name].diffuseTex[primId] = 0;
if (primitive.material.empty()) {
continue;
}
if (primitive.material.empty()) {
continue;
}
bool has_curves = false;
if (primitive.extras.IsObject()) {
if (primitive.extras.Has("ext_mode")) {
const tinygltf::Value::Object &o =
primitive.extras.Get<tinygltf::Value::Object>();
const tinygltf::Value &ext_mode = o.find("ext_mode")->second;
bool has_curves = false;
if (primitive.extras.IsObject()) {
if (primitive.extras.Has("ext_mode")) {
const tinygltf::Value::Object &o =
primitive.extras.Get<tinygltf::Value::Object>();
const tinygltf::Value &ext_mode = o.find("ext_mode")->second;
if (ext_mode.IsString()) {
const std::string &str = ext_mode.Get<std::string>();
if (str.compare("curves") == 0) {
has_curves = true;
}
}
}
}
if (ext_mode.IsString()) {
const std::string &str = ext_mode.Get<std::string>();
if (str.compare("curves") == 0) {
has_curves = true;
}
}
}
}
if (!has_curves) {
continue;
}
if (!has_curves) {
continue;
}
// Construct curves buffer
const tinygltf::Accessor &vtx_accessor =
scene.accessors[primitive.attributes.find("POSITION")->second];
const tinygltf::Accessor &nverts_accessor =
scene.accessors[primitive.attributes.find("NVERTS")->second];
const tinygltf::BufferView &vtx_bufferView =
scene.bufferViews[vtx_accessor.bufferView];
const tinygltf::BufferView &nverts_bufferView =
scene.bufferViews[nverts_accessor.bufferView];
const tinygltf::Buffer &vtx_buffer =
scene.buffers[vtx_bufferView.buffer];
const tinygltf::Buffer &nverts_buffer =
scene.buffers[nverts_bufferView.buffer];
// Construct curves buffer
const tinygltf::Accessor &vtx_accessor =
scene.accessors[primitive.attributes.find("POSITION")->second];
const tinygltf::Accessor &nverts_accessor =
scene.accessors[primitive.attributes.find("NVERTS")->second];
const tinygltf::BufferView &vtx_bufferView =
scene.bufferViews[vtx_accessor.bufferView];
const tinygltf::BufferView &nverts_bufferView =
scene.bufferViews[nverts_accessor.bufferView];
const tinygltf::Buffer &vtx_buffer =
scene.buffers[vtx_bufferView.buffer];
const tinygltf::Buffer &nverts_buffer =
scene.buffers[nverts_bufferView.buffer];
// std::cout << "vtx_bufferView = " << vtx_accessor.bufferView <<
// std::endl;
// std::cout << "nverts_bufferView = " << nverts_accessor.bufferView <<
// std::endl;
// std::cout << "vtx_buffer.size = " << vtx_buffer.data.size() <<
// std::endl;
// std::cout << "nverts_buffer.size = " << nverts_buffer.data.size() <<
// std::endl;
// std::cout << "vtx_bufferView = " << vtx_accessor.bufferView <<
// std::endl;
// std::cout << "nverts_bufferView = " << nverts_accessor.bufferView <<
// std::endl;
// std::cout << "vtx_buffer.size = " << vtx_buffer.data.size() <<
// std::endl;
// std::cout << "nverts_buffer.size = " << nverts_buffer.data.size() <<
// std::endl;
const int *nverts =
reinterpret_cast<const int *>(nverts_buffer.data.data());
const float *vtx =
reinterpret_cast<const float *>(vtx_buffer.data.data());
const int *nverts =
reinterpret_cast<const int *>(nverts_buffer.data.data());
const float *vtx =
reinterpret_cast<const float *>(vtx_buffer.data.data());
// Convert to GL_LINES data.
std::vector<float> line_pts;
size_t vtx_offset = 0;
for (int k = 0; k < static_cast<int>(nverts_accessor.count); k++) {
for (int n = 0; n < nverts[k] - 1; n++) {
// Convert to GL_LINES data.
std::vector<float> line_pts;
size_t vtx_offset = 0;
for (int k = 0; k < static_cast<int>(nverts_accessor.count); k++) {
for (int n = 0; n < nverts[k] - 1; n++) {
line_pts.push_back(vtx[3 * (vtx_offset + n) + 0]);
line_pts.push_back(vtx[3 * (vtx_offset + n) + 1]);
line_pts.push_back(vtx[3 * (vtx_offset + n) + 2]);
line_pts.push_back(vtx[3 * (vtx_offset + n) + 0]);
line_pts.push_back(vtx[3 * (vtx_offset + n) + 1]);
line_pts.push_back(vtx[3 * (vtx_offset + n) + 2]);
line_pts.push_back(vtx[3 * (vtx_offset + n + 1) + 0]);
line_pts.push_back(vtx[3 * (vtx_offset + n + 1) + 1]);
line_pts.push_back(vtx[3 * (vtx_offset + n + 1) + 2]);
line_pts.push_back(vtx[3 * (vtx_offset + n + 1) + 0]);
line_pts.push_back(vtx[3 * (vtx_offset + n + 1) + 1]);
line_pts.push_back(vtx[3 * (vtx_offset + n + 1) + 2]);
// std::cout << "p0 " << vtx[3 * (vtx_offset + n) + 0] << ", "
// << vtx[3 * (vtx_offset + n) + 1] << ", "
// << vtx[3 * (vtx_offset + n) + 2] << std::endl;
// std::cout << "p0 " << vtx[3 * (vtx_offset + n) + 0] << ", "
// << vtx[3 * (vtx_offset + n) + 1] << ", "
// << vtx[3 * (vtx_offset + n) + 2] << std::endl;
// std::cout << "p1 " << vtx[3 * (vtx_offset + n+1) + 0] << ", "
// << vtx[3 * (vtx_offset + n+1) + 1] << ", "
// << vtx[3 * (vtx_offset + n+1) + 2] << std::endl;
}
// std::cout << "p1 " << vtx[3 * (vtx_offset + n+1) + 0] << ", "
// << vtx[3 * (vtx_offset + n+1) + 1] << ", "
// << vtx[3 * (vtx_offset + n+1) + 2] << std::endl;
}
vtx_offset += nverts[k];
}
vtx_offset += nverts[k];
}
GLCurvesState state;
glGenBuffers(1, &state.vb);
glBindBuffer(GL_ARRAY_BUFFER, state.vb);
glBufferData(GL_ARRAY_BUFFER, line_pts.size() * sizeof(float),
line_pts.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
GLCurvesState state;
glGenBuffers(1, &state.vb);
glBindBuffer(GL_ARRAY_BUFFER, state.vb);
glBufferData(GL_ARRAY_BUFFER, line_pts.size() * sizeof(float),
line_pts.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
state.count = line_pts.size() / 3;
gCurvesMesh[mesh.name] = state;
state.count = line_pts.size() / 3;
gCurvesMesh[mesh.name] = state;
// Material
tinygltf::Material &mat = scene.materials[primitive.material];
// printf("material.name = %s\n", mat.name.c_str());
if (mat.values.find("diffuse") != mat.values.end()) {
std::string diffuseTexName = mat.values["diffuse"].string_value;
if (scene.textures.find(diffuseTexName) != scene.textures.end()) {
tinygltf::Texture &tex = scene.textures[diffuseTexName];
if (scene.images.find(tex.source) != scene.images.end()) {
tinygltf::Image &image = scene.images[tex.source];
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(tex.target, texId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(tex.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(tex.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Material
tinygltf::Material &mat = scene.materials[primitive.material];
// printf("material.name = %s\n", mat.name.c_str());
if (mat.values.find("diffuse") != mat.values.end()) {
std::string diffuseTexName = mat.values["diffuse"].string_value;
if (scene.textures.find(diffuseTexName) != scene.textures.end()) {
tinygltf::Texture &tex = scene.textures[diffuseTexName];
if (scene.images.find(tex.source) != scene.images.end()) {
tinygltf::Image &image = scene.images[tex.source];
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(tex.target, texId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(tex.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(tex.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Ignore Texture.fomat.
GLenum format = GL_RGBA;
if (image.component == 3) {
format = GL_RGB;
}
glTexImage2D(tex.target, 0, tex.internalFormat, image.width,
image.height, 0, format, tex.type,
&image.image.at(0));
// Ignore Texture.fomat.
GLenum format = GL_RGBA;
if (image.component == 3) {
format = GL_RGB;
}
glTexImage2D(tex.target, 0, tex.internalFormat, image.width,
image.height, 0, format, tex.type,
&image.image.at(0));
CheckErrors("texImage2D");
glBindTexture(tex.target, 0);
CheckErrors("texImage2D");
glBindTexture(tex.target, 0);
printf("TexId = %d\n", texId);
gMeshState[mesh.name].diffuseTex[primId] = texId;
}
}
}
}
}
}
printf("TexId = %d\n", texId);
gMeshState[mesh.name].diffuseTex[primId] = texId;
}
}
}
}
}
}
glUseProgram(progId);
GLint vtloc = glGetAttribLocation(progId, "in_vertex");
GLint nrmloc = glGetAttribLocation(progId, "in_normal");
GLint uvloc = glGetAttribLocation(progId, "in_texcoord");
glUseProgram(progId);
GLint vtloc = glGetAttribLocation(progId, "in_vertex");
GLint nrmloc = glGetAttribLocation(progId, "in_normal");
GLint uvloc = glGetAttribLocation(progId, "in_texcoord");
GLint diffuseTexLoc = glGetUniformLocation(progId, "diffuseTex");
GLint isCurvesLoc = glGetUniformLocation(progId, "uIsCurves");
GLint diffuseTexLoc = glGetUniformLocation(progId, "diffuseTex");
GLint isCurvesLoc = glGetUniformLocation(progId, "uIsCurves");
gGLProgramState.attribs["POSITION"] = vtloc;
gGLProgramState.attribs["NORMAL"] = nrmloc;
gGLProgramState.attribs["TEXCOORD_0"] = uvloc;
gGLProgramState.uniforms["diffuseTex"] = diffuseTexLoc;
gGLProgramState.uniforms["uIsCurves"] = isCurvesLoc;
gGLProgramState.attribs["POSITION"] = vtloc;
gGLProgramState.attribs["NORMAL"] = nrmloc;
gGLProgramState.attribs["TEXCOORD_0"] = uvloc;
gGLProgramState.uniforms["diffuseTex"] = diffuseTexLoc;
gGLProgramState.uniforms["uIsCurves"] = isCurvesLoc;
};
#endif
@@ -558,12 +682,13 @@ static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) {
(it->first.compare("TEXCOORD_0") == 0)) {
if (gGLProgramState.attribs[it->first] >= 0) {
// Compute byteStride from Accessor + BufferView combination.
int byteStride = accessor.ByteStride(model.bufferViews[accessor.bufferView]);
int byteStride =
accessor.ByteStride(model.bufferViews[accessor.bufferView]);
assert(byteStride != -1);
glVertexAttribPointer(gGLProgramState.attribs[it->first], size,
accessor.componentType, accessor.normalized ? GL_TRUE : GL_FALSE,
byteStride,
BUFFER_OFFSET(accessor.byteOffset));
accessor.componentType,
accessor.normalized ? GL_TRUE : GL_FALSE,
byteStride, BUFFER_OFFSET(accessor.byteOffset));
CheckErrors("vertex attrib pointer");
glEnableVertexAttribArray(gGLProgramState.attribs[it->first]);
CheckErrors("enable vertex attrib array");
@@ -617,32 +742,32 @@ static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) {
#if 0 // TODO(syoyo): Implement
static void DrawCurves(tinygltf::Scene &scene, const tinygltf::Mesh &mesh) {
(void)scene;
(void)scene;
if (gCurvesMesh.find(mesh.name) == gCurvesMesh.end()) {
return;
}
if (gCurvesMesh.find(mesh.name) == gCurvesMesh.end()) {
return;
}
if (gGLProgramState.uniforms["isCurvesLoc"] >= 0) {
glUniform1i(gGLProgramState.uniforms["isCurvesLoc"], 1);
}
if (gGLProgramState.uniforms["isCurvesLoc"] >= 0) {
glUniform1i(gGLProgramState.uniforms["isCurvesLoc"], 1);
}
GLCurvesState &state = gCurvesMesh[mesh.name];
GLCurvesState &state = gCurvesMesh[mesh.name];
if (gGLProgramState.attribs["POSITION"] >= 0) {
glBindBuffer(GL_ARRAY_BUFFER, state.vb);
glVertexAttribPointer(gGLProgramState.attribs["POSITION"], 3, GL_FLOAT,
GL_FALSE, /* stride */ 0, BUFFER_OFFSET(0));
CheckErrors("curve: vertex attrib pointer");
glEnableVertexAttribArray(gGLProgramState.attribs["POSITION"]);
CheckErrors("curve: enable vertex attrib array");
}
if (gGLProgramState.attribs["POSITION"] >= 0) {
glBindBuffer(GL_ARRAY_BUFFER, state.vb);
glVertexAttribPointer(gGLProgramState.attribs["POSITION"], 3, GL_FLOAT,
GL_FALSE, /* stride */ 0, BUFFER_OFFSET(0));
CheckErrors("curve: vertex attrib pointer");
glEnableVertexAttribArray(gGLProgramState.attribs["POSITION"]);
CheckErrors("curve: enable vertex attrib array");
}
glDrawArrays(GL_LINES, 0, state.count);
glDrawArrays(GL_LINES, 0, state.count);
if (gGLProgramState.attribs["POSITION"] >= 0) {
glDisableVertexAttribArray(gGLProgramState.attribs["POSITION"]);
}
if (gGLProgramState.attribs["POSITION"] >= 0) {
glDisableVertexAttribArray(gGLProgramState.attribs["POSITION"]);
}
}
#endif
@@ -693,16 +818,16 @@ static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
static void DrawModel(tinygltf::Model &model) {
#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());
std::map<std::string, tinygltf::Mesh>::const_iterator it(scene.meshes.begin());
std::map<std::string, tinygltf::Mesh>::const_iterator itEnd(scene.meshes.end());
for (; it != itEnd; it++) {
DrawMesh(scene, it->second);
DrawCurves(scene, it->second);
}
for (; it != itEnd; it++) {
DrawMesh(scene, it->second);
DrawCurves(scene, it->second);
}
#else
//If the glTF asset has at least one scene, and doesn't define a default one
//just show the first one we can find
// 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];
@@ -752,7 +877,8 @@ int main(int argc, char **argv) {
#ifdef _WIN32
#ifdef _DEBUG
std::string input_filename(argv[1] ? argv[1] : "../../../models/Cube/Cube.gltf");
std::string input_filename(argv[1] ? argv[1]
: "../../../models/Cube/Cube.gltf");
#endif
#else
std::string input_filename(argv[1] ? argv[1] : "../../models/Cube/Cube.gltf");
@@ -763,7 +889,8 @@ int main(int argc, char **argv) {
bool ret = false;
if (ext.compare("glb") == 0) {
// assume binary glTF.
ret = loader.LoadBinaryFromFile(&model, &err, &warn, input_filename.c_str());
ret =
loader.LoadBinaryFromFile(&model, &err, &warn, input_filename.c_str());
} else {
// assume ascii glTF.
ret = loader.LoadASCIIFromFile(&model, &err, &warn, input_filename.c_str());
@@ -825,15 +952,14 @@ int main(int argc, char **argv) {
#ifdef _WIN32
#ifdef _DEBUG
const char *shader_frag_filename = "../shader.frag";
const char *shader_vert_filename = "../shader.vert";
const char *shader_frag_filename = "../shader.frag";
const char *shader_vert_filename = "../shader.vert";
#endif
#else
const char *shader_frag_filename = "shader.frag";
const char *shader_vert_filename = "shader.vert";
#endif
if (false == LoadShader(GL_VERTEX_SHADER, vertId, shader_vert_filename)) {
return -1;
}

View File

@@ -228,7 +228,8 @@ static std::string PrintParameterMap(const tinygltf::ParameterMap &pmap) {
#endif
static std::string PrintValue(const std::string &name,
const tinygltf::Value &value, const int indent, const bool tag = true) {
const tinygltf::Value &value, const int indent,
const bool tag = true) {
std::stringstream ss;
if (value.IsObject()) {
@@ -265,11 +266,10 @@ static std::string PrintValue(const std::string &name,
} else if (value.IsArray()) {
ss << Indent(indent) << name << " [ ";
for (size_t i = 0; i < value.Size(); i++) {
ss << PrintValue("", value.Get(int(i)), indent + 1, /* tag */false);
if (i != (value.ArrayLen()-1)) {
ss << PrintValue("", value.Get(int(i)), indent + 1, /* tag */ false);
if (i != (value.ArrayLen() - 1)) {
ss << ", ";
}
}
ss << Indent(indent) << "] ";
}
@@ -330,13 +330,13 @@ static void DumpPrimitive(const tinygltf::Primitive &primitive, int indent) {
<< PrintValue("extras", primitive.extras, indent + 1) << std::endl;
}
static void DumpExtensions(const tinygltf::ExtensionMap &extension, const int indent)
{
static void DumpExtensions(const tinygltf::ExtensionMap &extension,
const int indent) {
// TODO(syoyo): pritty print Value
for (auto &e : extension) {
std::cout << Indent(indent) << e.first << std::endl;
std::cout << PrintValue("extensions", e.second, indent+1) << std::endl;
}
std::cout << PrintValue("extensions", e.second, indent + 1) << std::endl;
}
}
static void Dump(const tinygltf::Model &model) {
@@ -409,6 +409,30 @@ static void Dump(const tinygltf::Model &model) {
}
std::cout << "]" << std::endl;
}
if (accessor.sparse.isSparse) {
std::cout << Indent(2) << "sparse:" << std::endl;
std::cout << Indent(3) << "count : " << accessor.sparse.count
<< std::endl;
std::cout << Indent(3) << "indices: " << std::endl;
std::cout << Indent(4)
<< "bufferView : " << accessor.sparse.indices.bufferView
<< std::endl;
std::cout << Indent(4)
<< "byteOffset : " << accessor.sparse.indices.byteOffset
<< std::endl;
std::cout << Indent(4) << "componentType: "
<< PrintComponentType(accessor.sparse.indices.componentType)
<< "(" << accessor.sparse.indices.componentType << ")"
<< std::endl;
std::cout << Indent(3) << "values : " << std::endl;
std::cout << Indent(4)
<< "bufferView : " << accessor.sparse.values.bufferView
<< std::endl;
std::cout << Indent(4)
<< "byteOffset : " << accessor.sparse.values.byteOffset
<< std::endl;
}
}
}
@@ -585,10 +609,11 @@ static void Dump(const tinygltf::Model &model) {
}
}
}
// toplevel extensions
{
std::cout << "extensions(items=" << model.extensions.size() << ")" << std::endl;
std::cout << "extensions(items=" << model.extensions.size() << ")"
<< std::endl;
DumpExtensions(model.extensions, 1);
}
}
@@ -602,7 +627,7 @@ int main(int argc, char **argv) {
tinygltf::Model model;
tinygltf::TinyGLTF gltf_ctx;
std::string err;
std::string warn;
std::string warn;
std::string input_filename(argv[1]);
std::string ext = GetFilePathExtension(input_filename);
@@ -610,18 +635,19 @@ int main(int argc, char **argv) {
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());
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());
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());
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@ import subprocess
sample_model_dir = "/home/syoyo/work/glTF-Sample-Models"
base_model_dir = os.path.join(sample_model_dir, "2.0")
# Include `glTF-Draco` when you build `loader_example` with draco support.
kinds = [ "glTF", "glTF-Binary", "glTF-Embedded", "glTF-MaterialsCommon"]
# ---------------------------------

View File

@@ -4,7 +4,7 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2015 - 2018 Syoyo Fujita, Aurélien Chatelain and many
// Copyright (c) 2015 - 2019 Syoyo Fujita, Aurélien Chatelain and many
// contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -26,6 +26,8 @@
// THE SOFTWARE.
// Version:
// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
// to @Ybalrid)
// - v2.1.0 Add draco compression.
// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
// - v2.0.0 glTF 2.0!.
@@ -149,7 +151,7 @@ namespace tinygltf {
#ifdef __ANDROID__
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
AAssetManager* asset_manager = nullptr;
AAssetManager *asset_manager = nullptr;
#endif
#endif
@@ -380,6 +382,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
@@ -468,9 +481,11 @@ 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){}
wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
bool operator==(const Sampler &) const;
};
@@ -479,6 +494,9 @@ 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",
@@ -560,7 +578,19 @@ struct Accessor {
std::vector<double> minValues; // optional
std::vector<double> maxValues; // optional
// TODO(syoyo): "sparse"
struct {
int count;
bool isSparse;
struct {
int byteOffset;
int bufferView;
int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
} indices;
struct {
int bufferView;
int byteOffset;
} values;
} sparse;
///
/// Utility function to compute byteStride for a given bufferView object.
@@ -599,7 +629,10 @@ struct Accessor {
return 0;
}
Accessor() { bufferView = -1; }
Accessor() {
bufferView = -1;
sparse.isSparse = false;
}
bool operator==(const tinygltf::Accessor &) const;
};
@@ -805,9 +838,9 @@ enum SectionCheck {
///
/// LoadImageDataFunction type. Signature for custom image loading callbacks.
///
typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, std::string *,
int, int, const unsigned char *, int,
void *);
typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
std::string *, int, int,
const unsigned char *, int, void *);
///
/// WriteImageDataFunction type. Signature for custom image writing callbacks.
@@ -817,9 +850,9 @@ typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
#ifndef TINYGLTF_NO_STB_IMAGE
// Declaration of default image loader callback
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 *);
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
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
@@ -939,10 +972,8 @@ class TinyGLTF {
/// Write glTF to file.
///
bool WriteGltfSceneToFile(Model *model, const std::string &filename,
bool embedImages,
bool embedBuffers,
bool prettyPrint,
bool writeBinary);
bool embedImages, bool embedBuffers,
bool prettyPrint, bool writeBinary);
///
/// Set callback to use for loading image data
@@ -1062,29 +1093,73 @@ 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
// Disable GCC warnigs
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
#endif // __GNUC__
#ifndef TINYGLTF_NO_INCLUDE_JSON
#include "json.hpp"
#endif
#ifdef TINYGLTF_ENABLE_DRACO
#include "draco/core/decoder_buffer.h"
#include "draco/compression/decode.h"
#include "draco/core/decoder_buffer.h"
#endif
#ifndef TINYGLTF_NO_STB_IMAGE
#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
#include "stb_image.h"
#endif
#endif
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
#include "stb_image_write.h"
#endif
#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
@@ -1598,46 +1673,73 @@ void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
}
#ifndef TINYGLTF_NO_STB_IMAGE
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) {
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. STB cannot decode image data for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\". Proably 16bit PNG?\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 for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\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 for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
(*err) += "Image width mismatch for image[" +
std::to_string(image_idx) + "] name = \"" + image->name +
"\"\n";
}
return false;
}
@@ -1645,9 +1747,11 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, std::str
if (req_height > 0) {
if (req_height != h) {
free(data);
stbi_image_free(data);
if (err) {
(*err) += "Image height mismatch. for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
(*err) += "Image height mismatch. for image[" +
std::to_string(image_idx) + "] name = \"" + image->name +
"\"\n";
}
return false;
}
@@ -1656,10 +1760,11 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, std::str
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;
}
@@ -1689,6 +1794,12 @@ 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)) {
@@ -1752,16 +1863,17 @@ 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 {
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;
@@ -1834,7 +1946,8 @@ 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);
AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
AASSET_MODE_STREAMING);
if (!asset) {
if (err) {
(*err) += "File open error : " + filepath + "\n";
@@ -2411,12 +2524,13 @@ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
json::const_iterator extIt = it.value().begin();
for (; extIt != it.value().end(); extIt++) {
if (!extIt.value().is_object()) continue;
if (!ParseJsonAsValue(&extensions[extIt.key()], extIt.value())) {
if (!ParseJsonAsValue(&extensions[extIt.key()], extIt.value())) {
if (!extIt.key().empty()) {
// create empty object so that an extension object is still of type object
extensions[extIt.key()] = Value{ Value::Object{} };
// create empty object so that an extension object is still of type
// object
extensions[extIt.key()] = Value{Value::Object{}};
}
}
}
}
if (ret) {
(*ret) = extensions;
@@ -2437,9 +2551,9 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o) {
return true;
}
static bool ParseImage(Image *image, const int image_idx, std::string *err, std::string *warn,
const json &o, const std::string &basedir,
FsCallbacks *fs,
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,
void *load_image_user_data = nullptr) {
// A glTF image must either reference a bufferView or an image uri
@@ -2456,14 +2570,17 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, std:
if (err) {
(*err) +=
"Only one of `bufferView` or `uri` should be defined, but both are "
"defined for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\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[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
(*err) += "Neither required `bufferView` nor `uri` defined for image[" +
std::to_string(image_idx) + "] name = \"" + image->name +
"\"\n";
}
return false;
}
@@ -2475,7 +2592,9 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, std:
double bufferView = -1;
if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true)) {
if (err) {
(*err) += "Failed to parse `bufferView` for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
(*err) += "Failed to parse `bufferView` for image[" +
std::to_string(image_idx) + "] name = \"" + image->name +
"\"\n";
}
return false;
}
@@ -2505,7 +2624,8 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, std:
std::string tmp_err;
if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
if (err) {
(*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
(*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
"] name = \"" + image->name + "\".\n";
}
return false;
}
@@ -2515,7 +2635,9 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, std:
if (IsDataURI(uri)) {
if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
if (err) {
(*err) += "Failed to decode 'uri' for image[" + std::to_string(image_idx) + "] name = [" + image->name + "]\n";
(*err) += "Failed to decode 'uri' for image[" +
std::to_string(image_idx) + "] name = [" + image->name +
"]\n";
}
return false;
}
@@ -2528,7 +2650,9 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, std:
#endif
if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
if (warn) {
(*warn) += "Failed to load external 'uri' for image[" + std::to_string(image_idx) + "] name = [" + image->name + "]\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;
@@ -2536,7 +2660,9 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, std:
if (img.empty()) {
if (warn) {
(*warn) += "Image data is empty for image[" + std::to_string(image_idx) + "] name = [" + image->name + "] \n";
(*warn) += "Image data is empty for image[" +
std::to_string(image_idx) + "] name = [" + image->name +
"] \n";
}
return false;
}
@@ -2732,6 +2858,51 @@ static bool ParseBufferView(BufferView *bufferView, std::string *err,
bufferView->byteOffset = static_cast<size_t>(byteOffset);
bufferView->byteLength = static_cast<size_t>(byteLength);
bufferView->byteStride = static_cast<size_t>(byteStride);
return true;
}
static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
const json &o) {
accessor->sparse.isSparse = true;
double count = 0.0;
ParseNumberProperty(&count, err, o, "count", true);
const auto indices_iterator = o.find("indices");
const auto values_iterator = o.find("values");
if (indices_iterator == o.end()) {
(*err) = "the sparse object of this accessor doesn't have indices";
return false;
}
if (values_iterator == o.end()) {
(*err) = "the sparse object ob ths accessor doesn't have values";
return false;
}
const json &indices_obj = *indices_iterator;
const json &values_obj = *values_iterator;
double indices_buffer_view = 0.0, indices_byte_offset = 0.0,
component_type = 0.0;
ParseNumberProperty(&indices_buffer_view, err, indices_obj, "bufferView",
true);
ParseNumberProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
true);
ParseNumberProperty(&component_type, err, indices_obj, "componentType", true);
double values_buffer_view = 0.0, values_byte_offset = 0.0;
ParseNumberProperty(&values_buffer_view, err, values_obj, "bufferView", true);
ParseNumberProperty(&values_byte_offset, err, values_obj, "byteOffset", true);
accessor->sparse.count = static_cast<int>(count);
accessor->sparse.indices.bufferView = static_cast<int>(indices_buffer_view);
accessor->sparse.indices.byteOffset = static_cast<int>(indices_byte_offset);
accessor->sparse.indices.componentType = static_cast<int>(component_type);
accessor->sparse.values.bufferView = static_cast<int>(values_buffer_view);
accessor->sparse.values.byteOffset = static_cast<int>(values_byte_offset);
// todo check theses values
return true;
}
@@ -2817,112 +2988,128 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
ParseExtrasProperty(&(accessor->extras), o);
// check if accessor has a "sparse" object:
const auto iterator = o.find("sparse");
if (iterator != o.end()) {
// here this accessor has a "sparse" subobject
return ParseSparseAccessor(accessor, err, *iterator);
}
return true;
}
#ifdef TINYGLTF_ENABLE_DRACO
static void DecodeIndexBuffer(draco::Mesh* mesh, size_t componentSize, std::vector<uint8_t>& outBuffer)
{
if (componentSize == 4)
{
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
{
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);
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)
{
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)
{
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))
if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
values))
return false;
memcpy(outBuffer.data() + byteOffset, &values[0], sizeof(T) * pAttribute->num_components());
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)
{
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;
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)
{
static bool ParseDracoExtension(Primitive *primitive, Model *model,
std::string *err,
const Value &dracoExtensionValue) {
auto bufferViewValue = dracoExtensionValue.Get("bufferView");
if (!bufferViewValue.IsInt())
return false;
if (!bufferViewValue.IsInt()) return false;
auto attributesValue = dracoExtensionValue.Get("attributes");
if (!attributesValue.IsObject())
return false;
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 &view = model->bufferViews[bufferView];
Buffer &buffer = model->buffers[view.buffer];
// BufferView has already been decoded
if (view.dracoDecoded)
return true;
if (view.dracoDecoded) return true;
view.dracoDecoded = true;
const char* bufferViewData = reinterpret_cast<const char*>(buffer.data.data() + view.byteOffset);
const char *bufferViewData =
reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
size_t bufferViewSize = view.byteLength;
// decode draco
@@ -2933,12 +3120,12 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, std::string
if (!decodeResult.ok()) {
return false;
}
const std::unique_ptr<draco::Mesh>& mesh = decodeResult.value();
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);
if (primitive->indices >= 0) {
int32_t componentSize = GetComponentSizeInBytes(
model->accessors[primitive->indices].componentType);
Buffer decodedIndexBuffer;
decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
@@ -2948,35 +3135,37 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, std::string
BufferView decodedIndexBufferView;
decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
decodedIndexBufferView.byteLength = int(mesh->num_faces() * 3 * componentSize);
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].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;
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;
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;
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);
size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
GetComponentSizeInBytes(componentType);
decodedBuffer.data.resize(bufferSize);
if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute, decodedBuffer.data))
if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
decodedBuffer.data))
return false;
model->buffers.emplace_back(std::move(decodedBuffer));
@@ -2986,11 +3175,15 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, std::string
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;
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());
model->accessors[primitiveAttribute->second].bufferView =
int(model->bufferViews.size() - 1);
model->accessors[primitiveAttribute->second].count =
int(mesh->num_points());
}
return true;
@@ -3040,17 +3233,20 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
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);
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, Model *model, 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();
@@ -3261,13 +3457,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";
@@ -3730,22 +3921,22 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
// Assign missing bufferView target types
// - Look for missing Mesh indices
// - Look for missing bufferView targets
for (auto &mesh : model->meshes)
{
for (auto &primitive : mesh.primitives)
{
if (primitive.indices > -1) // has indices from parsing step, must be Element Array Buffer
for (auto &mesh : model->meshes) {
for (auto &primitive : mesh.primitives) {
if (primitive.indices >
-1) // has indices from parsing step, must be Element Array Buffer
{
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
// we could optionally check if acessors' bufferView type is Scalar, as
// it should be
}
}
}
// find any missing targets, must be an array buffer type if not fulfilled from previous check
for (auto &bufferView : model->bufferViews)
{
if (bufferView.target == 0) // missing target type
// find any missing targets, must be an array buffer type if not fulfilled
// from previous check
for (auto &bufferView : model->bufferViews) {
if (bufferView.target == 0) // missing target type
{
bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
}
@@ -3864,7 +4055,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
for (; it != itEnd; it++, idx++) {
if (!it.value().is_object()) {
if (err) {
(*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
(*err) +=
"image[" + std::to_string(idx) + "] is not a JSON object.";
}
return false;
}
@@ -3896,10 +4088,10 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
}
return false;
}
bool ret = LoadImageData(&image, idx, err, warn, image.width, image.height,
&buffer.data[bufferView.byteOffset],
static_cast<int>(bufferView.byteLength),
load_image_user_data_);
bool ret = LoadImageData(
&image, idx, err, warn, image.width, image.height,
&buffer.data[bufferView.byteOffset],
static_cast<int>(bufferView.byteLength), load_image_user_data_);
if (!ret) {
return false;
}
@@ -4353,7 +4545,7 @@ static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
const std::string &binFilename) {
std::ofstream output(binFilename.c_str(), std::ofstream::binary);
if(!output.is_open()) return false;
if (!output.is_open()) return false;
output.write(reinterpret_cast<const char *>(&data[0]),
std::streamsize(data.size()));
output.close();
@@ -4402,9 +4594,10 @@ static void SerializeExtensionMap(ExtensionMap &extensions, json &o) {
if (ValueToJson(extIt->second, &ret)) {
extMap[extIt->first] = ret;
}
if(ret.is_null()) {
if (!(extIt->first.empty())) { // name should not be empty, but for sure
// create empty object so that an extension name is still included in json.
if (ret.is_null()) {
if (!(extIt->first.empty())) { // name should not be empty, but for sure
// create empty object so that an extension name is still included in
// json.
extMap[extIt->first] = json({});
}
}
@@ -4535,7 +4728,7 @@ static void SerializeGltfBuffer(Buffer &buffer, json &o) {
static bool SerializeGltfBuffer(Buffer &buffer, json &o,
const std::string &binFilename,
const std::string &binBaseFilename) {
if(!SerializeGltfBufferData(buffer.data, binFilename)) return false;
if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
SerializeNumberProperty("byteLength", buffer.data.size(), o);
SerializeStringProperty("uri", binBaseFilename, o);
@@ -4823,9 +5016,10 @@ static void WriteBinaryGltfFile(const std::string &output,
const int version = 2;
const int padding_size = content.size() % 4;
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info, padding
// 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(), header.size());
gltfFile.write(reinterpret_cast<const char *>(&version), sizeof(version));
gltfFile.write(reinterpret_cast<const char *>(&length), sizeof(length));
@@ -4833,8 +5027,10 @@ static void WriteBinaryGltfFile(const std::string &output,
// JSON chunk info, then JSON data
const int model_length = int(content.size()) + padding_size;
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(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(), content.size());
// Chunk must be multiplies of 4, so pad with spaces
@@ -4880,7 +5076,8 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
std::string defaultBinFilename = GetBaseFilename(filename);
std::string defaultBinFileExt = ".bin";
std::string::size_type pos = defaultBinFilename.rfind('.', defaultBinFilename.length());
std::string::size_type pos =
defaultBinFilename.rfind('.', defaultBinFilename.length());
if (pos != std::string::npos) {
defaultBinFilename = defaultBinFilename.substr(0, pos);
@@ -4900,28 +5097,27 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
} else {
std::string binSavePath;
std::string binUri;
if (!model->buffers[i].uri.empty()
&& !IsDataURI(model->buffers[i].uri)) {
if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
binUri = model->buffers[i].uri;
}
else {
} else {
binUri = defaultBinFilename + defaultBinFileExt;
bool inUse = true;
int numUsed = 0;
while(inUse) {
while (inUse) {
inUse = false;
for (const std::string& usedName : usedUris) {
for (const std::string &usedName : usedUris) {
if (binUri.compare(usedName) != 0) continue;
inUse = true;
binUri = defaultBinFilename + std::to_string(numUsed++) + defaultBinFileExt;
binUri = defaultBinFilename + std::to_string(numUsed++) +
defaultBinFileExt;
break;
}
}
}
usedUris.push_back(binUri);
binSavePath = JoinPath(baseDir, binUri);
if(!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
binUri)) {
binSavePath = JoinPath(baseDir, binUri);
if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
binUri)) {
return false;
}
}