Merge branch '16bit-lodepng'

This commit is contained in:
Syoyo Fujita 2019-03-07 21:04:25 +09:00
commit ca56f726d6
12 changed files with 9645 additions and 653 deletions

View File

@ -7,6 +7,8 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
## Status ## Status
v2.2.0 release(Support loading 16bit PNG. Sparse accessor support)
v2.1.0 release(Draco support)
v2.0.0 release(22 Aug, 2018)! v2.0.0 release(22 Aug, 2018)!
## Builds ## Builds
@ -38,14 +40,17 @@ v2.0.0 release(22 Aug, 2018)!
* Image(Using stb_image) * Image(Using stb_image)
* [x] Parse BASE64 encoded embedded image data(DataURI). * [x] Parse BASE64 encoded embedded image data(DataURI).
* [x] Load external image file. * [x] Load external image file.
* [x] PNG(8bit only) * [x] Load PNG(8bit and 16bit)
* [x] JPEG(8bit only) * [x] Load JPEG(8bit only)
* [x] BMP * [x] Load BMP
* [x] GIF * [x] Load GIF
* [x] Custom Image decoder callback(e.g. for decoding OpenEXR image) * [x] Custom Image decoder callback(e.g. for decoding OpenEXR image)
* Morph traget * Morph traget
* [x] Sparse accessor * [x] Sparse accessor
* Load glTF from memory * Load glTF from memory
* Custom callback handler
* [x] Image load
* [x] Image save
* Extensions * Extensions
* [x] Draco mesh decoding * [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. * [ ] Write C++ code generator which emits C++ code from JSON schema for robust parsing.
* [ ] Mesh Compression/decompression(Open3DGC, etc) * [ ] Mesh Compression/decompression(Open3DGC, etc)
* [x] Load Draco compressed mesh * [x] Load Draco compressed mesh
* [x] Save Draco compressed mesh * [ ] Save Draco compressed mesh
* [ ] Open3DGC?
* [ ] Support `extensions` and `extras` property * [ ] Support `extensions` and `extras` property
* [ ] HDR image? * [ ] HDR image?
* [ ] OpenEXR extension through TinyEXR. * [ ] OpenEXR extension through TinyEXR.
* [ ] 16bit PNG support in Serialization
* [ ] Write example and tests for `animation` and `skin` * [ ] Write example and tests for `animation` and `skin`
* [ ] Skinning
* [ ] Morph targets
## Licenses ## Licenses

View File

@ -50,8 +50,9 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
From spec2.0 readme: From spec2.0 readme:
https://github.com/KhronosGroup/glTF/tree/master/specification/2.0 https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
... drawArrays function should be used with a count equal to ... drawArrays function should be used with a count equal to
the count property of any of the accessors referenced by the attributes the count property of any of the accessors referenced by the
property (they are all equal for a given primitive). 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_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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, 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; return vbos;
@ -255,7 +277,7 @@ void displayLoop(Window &window, const std::string &filename) {
glm::vec3 model_pos = glm::vec3(-3, 0, -3); glm::vec3 model_pos = glm::vec3(-3, 0, -3);
// generate a camera view, based on eye-position and lookAt world-position // 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_position = glm::vec3(3.0, 10.0, -5.0);
glm::vec3 sun_color = glm::vec3(1.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) { int main(int argc, char **argv) {
std::string filename = "../../models/Cube/Cube.gltf"; std::string filename = "../../models/Cube/Cube.gltf";
@ -294,14 +321,18 @@ int main(int argc, char **argv) {
filename = argv[1]; filename = argv[1];
} }
glfwSetErrorCallback(error_callback);
if (!glfwInit()) return -1; 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_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__ #ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
#endif #endif
Window window = Window(800, 600, "TinyGLTF basic example"); 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) set(CMAKE_CXX_STANDARD 11)
include_directories(../../) include_directories(../../)
include_directories(../common/)
file(GLOB gltfutil_sources *.cc *.h) file(GLOB gltfutil_sources *.cc *.h)
add_executable(gltfutil ${gltfutil_sources}) add_executable(gltfutil ${gltfutil_sources} ../common/lodepng.cpp)
install ( TARGETS install ( TARGETS
gltfutil gltfutil

View File

@ -49,6 +49,7 @@ struct configuration {
cli_action action = cli_action::not_set; cli_action action = cli_action::not_set;
texture_dumper::texture_output_format requested_format = texture_dumper::texture_output_format requested_format =
texture_dumper::texture_output_format::not_specified; texture_dumper::texture_output_format::not_specified;
bool use_exr = false;
bool has_output_dir; bool has_output_dir;
bool is_valid() { bool is_valid() {

View File

@ -11,6 +11,9 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h" #include "stb_image_write.h"
#define TINYEXR_IMPLEMENTATION
#include "tinyexr.h"
namespace gltfutil { namespace gltfutil {
int usage(int ret = 0) { int usage(int ret = 0) {
using std::cout; using std::cout;
@ -22,6 +25,7 @@ int usage(int ret = 0) {
<< "\t\t -d: dump enclosed content (image assets)\n" << "\t\t -d: dump enclosed content (image assets)\n"
<< "\t\t -f: file format for image output\n" << "\t\t -f: file format for image output\n"
<< "\t\t -o: ouptput directory path\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"; << "\t\t -h: print this help\n";
return ret; return ret;
} }
@ -44,6 +48,9 @@ int parse_args(int argc, char** argv) {
config.mode = ui_mode::cli; config.mode = ui_mode::cli;
config.action = cli_action::dump; config.action = cli_action::dump;
break; break;
case 'e':
config.use_exr = true;
break;
case 'i': case 'i':
config.mode = ui_mode::interactive; config.mode = ui_mode::interactive;
break; break;
@ -97,6 +104,11 @@ int parse_args(int argc, char** argv) {
case cli_action::dump: { case cli_action::dump: {
texture_dumper dumper(model); texture_dumper dumper(model);
if (config.use_exr) {
dumper.set_use_exr(true);
}
if (config.requested_format != if (config.requested_format !=
texture_dumper::texture_output_format::not_specified) texture_dumper::texture_output_format::not_specified)
dumper.set_output_format(config.requested_format); dumper.set_output_format(config.requested_format);

View File

@ -4,12 +4,81 @@
#include "stb_image_write.h" #include "stb_image_write.h"
#include "texture_dumper.h" #include "texture_dumper.h"
#include "lodepng.h" // ../common
#include "tinyexr.h"
#include <tiny_gltf.h> #include <tiny_gltf.h>
using namespace gltfutil; using namespace gltfutil;
using namespace tinygltf; using namespace tinygltf;
using std::cout; 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) texture_dumper::texture_dumper(const Model& input)
: model(input), configured_format(texture_output_format::png) { : model(input), configured_format(texture_output_format::png) {
cout << "Texture dumper\n"; cout << "Texture dumper\n";
@ -27,49 +96,59 @@ void texture_dumper::dump_to_folder(const std::string& path) {
cout << "image size is: " << image.width << 'x' << image.height << '\n'; cout << "image size is: " << image.width << 'x' << image.height << '\n';
cout << "pixel channel count :" << image.component << '\n'; cout << "pixel channel count :" << image.component << '\n';
cout << "pixel bit depth :" << image.bits << '\n'; cout << "pixel bit depth :" << image.bits << '\n';
std::string name = image.name.empty() ? std::to_string(index) : image.name; std::string basename =
image.name.empty() ? std::to_string(index) : image.name;
// TODO stb_image_write doesn't support any 16bit wirtes;
unsigned char* bytes_to_write = unsigned char* bytes_to_write =
const_cast<unsigned char*>(image.image.data()); const_cast<unsigned char*>(image.image.data());
unsigned char* tmp_buffer = nullptr;
if (image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
unsigned short max = 0xFFFF;
tmp_buffer = new unsigned char[image.width * image.height *
image.component * sizeof(unsigned char)];
unsigned short* buffer_as_shorts = (unsigned short*)bytes_to_write;
for (int i = 0; i < image.component * image.width * image.height; ++i) {
tmp_buffer[i] =
(unsigned char)(0xFF * (float(buffer_as_shorts[i]) / float(max)));
}
bytes_to_write =
tmp_buffer; // swap the pointer, but keep tmp_buffer around as we
// need to delete that memory later
}
std::string filename;
switch (configured_format) { switch (configured_format) {
case texture_output_format::png: case texture_output_format::png:
name = path + "/" + name + ".png"; filename = path + "/" + basename + ".png";
std::cout << "Image will be written to " << name << '\n';
stbi_write_png(name.c_str(), image.width, image.height, image.component, if (this->use_exr) {
bytes_to_write, 0); if (image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
break; filename = path + "/" + basename + ".exr";
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,
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,
bytes_to_write);
break;
} }
delete[] tmp_buffer; 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:
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:
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: private:
const tinygltf::Model& model; const tinygltf::Model& model;
texture_output_format configured_format; texture_output_format configured_format;
bool use_exr = false; // Use EXR for 16bit image?
public: public:
texture_dumper(const tinygltf::Model& inputModel); texture_dumper(const tinygltf::Model& inputModel);
void dump_to_folder(const std::string& path = "./"); void dump_to_folder(const std::string& path = "./");
void set_output_format(texture_output_format format); 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); static texture_output_format get_fromat_from_string(const std::string& str);
}; };

View File

@ -4,7 +4,7 @@
// //
// The MIT License (MIT) // 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. // contributors.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
@ -26,6 +26,8 @@
// THE SOFTWARE. // THE SOFTWARE.
// Version: // Version:
// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
// to @Ybalrid)
// - v2.1.0 Add draco compression. // - v2.1.0 Add draco compression.
// - v2.0.1 Add comparsion feature(Thanks to @Selmar). // - v2.0.1 Add comparsion feature(Thanks to @Selmar).
// - v2.0.0 glTF 2.0!. // - v2.0.0 glTF 2.0!.
@ -1091,7 +1093,19 @@ class TinyGLTF {
#if __has_warning("-Wnewline-eof") #if __has_warning("-Wnewline-eof")
#pragma clang diagnostic ignored "-Wnewline-eof" #pragma clang diagnostic ignored "-Wnewline-eof"
#endif #endif
#if __has_warning("-Wunused-parameter")
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif #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 #ifndef TINYGLTF_NO_INCLUDE_JSON
#include "json.hpp" #include "json.hpp"
@ -1118,6 +1132,10 @@ class TinyGLTF {
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif #endif
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#ifdef _WIN32 #ifdef _WIN32
// issue 143. // issue 143.
@ -1658,6 +1676,7 @@ void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
bool LoadImageData(Image *image, const int image_idx, std::string *err, bool LoadImageData(Image *image, const int image_idx, std::string *err,
std::string *warn, int req_width, int req_height, std::string *warn, int req_width, int req_height,
const unsigned char *bytes, int size, void *user_data) { const unsigned char *bytes, int size, void *user_data) {
(void)user_data;
(void)warn; (void)warn;
int w, h, comp, req_comp; int w, h, comp, req_comp;
@ -1775,6 +1794,12 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
std::vector<unsigned char> data; std::vector<unsigned char> data;
if (ext == "png") { 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, if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
image->height, image->component, image->height, image->component,
&image->image[0], 0)) { &image->image[0], 0)) {
@ -3213,6 +3238,8 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
if (dracoExtension != primitive->extensions.end()) { if (dracoExtension != primitive->extensions.end()) {
ParseDracoExtension(primitive, model, err, dracoExtension->second); ParseDracoExtension(primitive, model, err, dracoExtension->second);
} }
#else
(void)model;
#endif #endif
return true; return true;