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

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

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;
@ -22,6 +25,7 @@ int usage(int ret = 0) {
<< "\t\t -d: dump enclosed content (image assets)\n"
<< "\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

@ -4,12 +4,81 @@
#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";
@ -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 << "pixel channel count :" << image.component << '\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 =
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) {
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,
bytes_to_write, 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,
bytes_to_write);
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,
bytes_to_write);
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;
}
delete[] tmp_buffer;
}
}

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

@ -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!.
@ -1091,7 +1093,19 @@ 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"
@ -1118,6 +1132,10 @@ class TinyGLTF {
#pragma clang diagnostic pop
#endif
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#ifdef _WIN32
// 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,
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;
@ -1775,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)) {
@ -3213,6 +3238,8 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
if (dracoExtension != primitive->extensions.end()) {
ParseDracoExtension(primitive, model, err, dracoExtension->second);
}
#else
(void)model;
#endif
return true;