Compare commits

...

4 Commits

Author SHA1 Message Date
Syoyo Fujita
98eef6ee75 Fix non-KTX texture was not handled correctly.
Experience file drop API of glfw(not working yet).
2019-07-06 15:13:13 +09:00
Syoyo Fujita
c7bf800075 Support KTX texture of R8G8B8 format in basic example. 2019-07-05 16:20:53 +09:00
Syoyo Fujita
006ab90c67 Initial support of KTX texture load using TinyKTX. 2019-07-04 14:30:28 +09:00
Syoyo Fujita
6f7518255f Add tinyktx.
Add workaround for OpenGL3 context creation in `basic` example.
2019-07-03 16:41:11 +09:00
10 changed files with 2564 additions and 373 deletions

25
LICENSE.tinyktx Normal file
View File

@@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2019, DeanoC
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -7,6 +7,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
## Status ## Status
- v2.3.0 release(Support loading KTX image through tiny_ktx)
- v2.2.0 release(Support loading 16bit PNG. Sparse accessor support) - v2.2.0 release(Support loading 16bit PNG. Sparse accessor support)
- v2.1.0 release(Draco support) - v2.1.0 release(Draco support)
- v2.0.0 release(22 Aug, 2018)! - v2.0.0 release(22 Aug, 2018)!
@@ -52,7 +53,8 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
* [x] Image load * [x] Image load
* [x] Image save * [x] Image save
* Extensions * Extensions
* [x] Draco mesh decoding * [x] Draco mesh decoding(`TINYGLTF_ENABLE_DRACO` required)
* [x] KTX image support(no mipmap. `TINYGLTF_ENABLE_KTX` required)
## Examples ## Examples
@@ -81,6 +83,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
* [ ] OpenEXR extension through TinyEXR. * [ ] OpenEXR extension through TinyEXR.
* [ ] 16bit PNG support in Serialization * [ ] 16bit PNG support in Serialization
* [ ] Write example and tests for `animation` and `skin` * [ ] Write example and tests for `animation` and `skin`
* [ ] mipmap support for KTX image.
## Licenses ## Licenses
@@ -92,12 +95,15 @@ TinyGLTF uses the following third party libraries.
* base64 : Copyright (C) 2004-2008 René Nyffenegger * base64 : Copyright (C) 2004-2008 René Nyffenegger
* stb_image.h : v2.08 - public domain image loader - [Github link](https://github.com/nothings/stb/blob/master/stb_image.h) * stb_image.h : v2.08 - public domain image loader - [Github link](https://github.com/nothings/stb/blob/master/stb_image.h)
* stb_image_write.h : v1.09 - public domain image writer - [Github link](https://github.com/nothings/stb/blob/master/stb_image_write.h) * stb_image_write.h : v1.09 - public domain image writer - [Github link](https://github.com/nothings/stb/blob/master/stb_image_write.h)
* tinyktx.h : Copyright (c) 2019, DeanoC. Licensed under 2 clause BSD license.
## Build and example ## Build and example
Copy `stb_image.h`, `stb_image_write.h`, `json.hpp` and `tiny_gltf.h` to your project. Copy `stb_image.h`, `stb_image_write.h`, `json.hpp` and `tiny_gltf.h` to your project.
If you enable KTX support(`TINYGLTF_ENABLE_KTX`), Copy `tinyktx.h` to your project.
### Loading glTF 2.0 model ### Loading glTF 2.0 model
```c++ ```c++
@@ -143,6 +149,8 @@ if (!ret) {
* `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_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 `: 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`. * `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`.
* `TINYGLTF_ENABLE_KTX` : Enable loading KTX images( https://www.khronos.org/opengles/sdk/tools/KTX/ ) using `tiny_ktx.h`. Supported MIME is `image/ktx` ( https://github.com/KhronosGroup/glTF/issues/835 ). See `models/Cube-KTX` for details. Application also defined `TINYKTX_IMPLEMENTATION` in **one** .cc file.
* `TINYGLTF_NO_INCLUDE_TINYKTX` : Disable including `tinyktx.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 ### Saving gltTF 2.0 model
@@ -181,8 +189,6 @@ $ ./tester
$ ./tester_noexcept $ ./tester_noexcept
``` ```
## Third party licenses ## Third party licenses used in unit tests
* json.hpp : Licensed under the MIT License <http://opensource.org/licenses/MIT>. Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
* stb_image : Public domain.
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0. * catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.

View File

@@ -1,4 +1,4 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <GL/glew.h> #include <GL/glew.h>
@@ -8,14 +8,34 @@
#include "shaders.h" #include "shaders.h"
#include "window.h" #include "window.h"
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#endif
// Inlude tinyktx.h before tiny_gltf.h
// to get TKTX_*** definitions
#define TINYKTX_IMPLEMENTATION
#include "../../tinyktx.h"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#define TINYGLTF_IMPLEMENTATION #define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#define TINYGLTF_NOEXCEPTION #define TINYGLTF_NOEXCEPTION
#define JSON_NOEXCEPTION #define JSON_NOEXCEPTION
#define TINYGLTF_ENABLE_KTX
// tinyktx.h is already included above,
// so let tiny_gltf.h know do not include tinyktx.h anymore
#define TINYGLTF_NO_INCLUDE_TINY_KTX
#include "../../tiny_gltf.h" #include "../../tiny_gltf.h"
#define BUFFER_OFFSET(i) ((char *)NULL + (i)) //#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
#define BUFFER_OFFSET(i) \
(reinterpret_cast<void *>(i)) // TODO(syoyo): Is this right way?
bool loadModel(tinygltf::Model &model, const char *filename) { bool loadModel(tinygltf::Model &model, const char *filename) {
tinygltf::TinyGLTF loader; tinygltf::TinyGLTF loader;
@@ -39,6 +59,30 @@ bool loadModel(tinygltf::Model &model, const char *filename) {
return res; return res;
} }
static bool GetOpenGLFormatFromKTX(int ktx_fmt, GLint *internal_format, GLenum *format, GLenum *type) {
#if defined(TINYGLTF_ENABLE_KTX)
bool ret = true;
std::cout << "fmt = " << ktx_fmt << ", rgb8 fmt = " << TKTX_R8G8B8_UNORM << "\n";
if (ktx_fmt == TKTX_R8G8B8_UNORM) {
(*internal_format) = GL_RGB;
(*format) = GL_RGB;
(*type) = GL_UNSIGNED_BYTE;
} else {
// TODO(syoyo): Support more KTX formats.
ret = false;
}
return ret;
#else
(void)fmt;
(void)internal_format;
(void)internal_format;
return false;
#endif
}
std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos, std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
tinygltf::Model &model, tinygltf::Mesh &mesh) { tinygltf::Model &model, tinygltf::Mesh &mesh) {
for (size_t i = 0; i < model.bufferViews.size(); ++i) { for (size_t i = 0; i < model.bufferViews.size(); ++i) {
@@ -51,8 +95,8 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
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 the count property of any of the accessors referenced by the
attributes property (they are all equal for a given attributes property (they are all equal for a
primitive). given primitive).
*/ */
} }
@@ -97,7 +141,7 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
accessor.normalized ? GL_TRUE : GL_FALSE, accessor.normalized ? GL_TRUE : GL_FALSE,
byteStride, BUFFER_OFFSET(accessor.byteOffset)); byteStride, BUFFER_OFFSET(accessor.byteOffset));
} else } else
std::cout << "vaa missing: " << attrib.first << std::endl; std::cout << "Unsupported vertex attribute: " << attrib.first << std::endl;
} }
GLuint texid; GLuint texid;
@@ -113,7 +157,26 @@ 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);
GLint internal_format = GL_RGBA;
GLenum format = GL_RGBA; GLenum format = GL_RGBA;
GLenum type = GL_UNSIGNED_BYTE;
bool valid = false;
// KTX extension
if (image.extras.Has("ktx_format")) {
valid = GetOpenGLFormatFromKTX(image.extras.Get("ktx_format").Get<int>(), &internal_format, &format, &type);
if (valid) {
std::cout << "ktx_format: " << image.extras.Get("ktx_format").Get<int>() << std::endl;
std::cout << "ktx image size: " << image.width << ", " << image.height << std::endl;
}
} else {
valid = true;
if (image.component == 1) { if (image.component == 1) {
format = GL_RED; format = GL_RED;
@@ -121,22 +184,28 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
format = GL_RG; format = GL_RG;
} else if (image.component == 3) { } else if (image.component == 3) {
format = GL_RGB; format = GL_RGB;
} else if (image.component == 4) {
format = GL_RGBA;
} else { } else {
// ??? valid = false;
} }
GLenum type = GL_UNSIGNED_BYTE;
if (image.bits == 8) { if (image.bits == 8) {
// ok type = GL_UNSIGNED_BYTE;
} else if (image.bits == 16) { } else if (image.bits == 16) {
type = GL_UNSIGNED_SHORT; type = GL_UNSIGNED_SHORT;
} else { } else {
// ??? valid = false;
} }
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0, }
if (valid) {
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, image.width, image.height, 0,
format, type, &image.image.at(0)); format, type, &image.image.at(0));
} }
}
return vbos; return vbos;
} }
@@ -314,6 +383,14 @@ static void error_callback(int error, const char *description) {
fprintf(stderr, "Error: %s\n", description); fprintf(stderr, "Error: %s\n", description);
} }
static void drop_callback(GLFWwindow *window, int num, const char **paths) {
(void)window;
printf("dropCB %d\n", num);
for (int i = 0; i < num; i++) {
printf("%s\n", paths[i]);
}
}
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";
@@ -325,17 +402,26 @@ int main(int argc, char **argv) {
if (!glfwInit()) return -1; if (!glfwInit()) return -1;
// Force create OpenGL 3.3 // NOTE(syoyo): For some reason, Linux + NVIDIA driver + apt-installed
// NOTE(syoyo): Linux + NVIDIA driver segfaults for some reason? commenting out glfwWindowHint will work. // glew(1.13) cannot initialize some ARB functions when CONTEXT_VERSION are
// explicitly given. Proably we need to compile app with recent glfw and
// glew(or use glad) package
#if !defined(__linux__)
// Try to create OpenGL 3.3 context on Windows and macOS
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);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif #endif
// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif #endif
Window window = Window(800, 600, "TinyGLTF basic example"); Window window = Window(800, 600, "TinyGLTF basic example");
glfwSetDropCallback(window.window, drop_callback);
glfwMakeContextCurrent(window.window); glfwMakeContextCurrent(window.window);
#ifdef __APPLE__ #ifdef __APPLE__
@@ -343,10 +429,19 @@ int main(int argc, char **argv) {
glewExperimental = GL_TRUE; glewExperimental = GL_TRUE;
#endif #endif
glewInit(); if (glewInit() != GLEW_OK) {
std::cerr << "Failed to initialie glew." << std::endl;
return EXIT_FAILURE;
}
std::cout << glGetString(GL_RENDERER) << ", " << glGetString(GL_VERSION) std::cout << glGetString(GL_RENDERER) << ", " << glGetString(GL_VERSION)
<< std::endl; << std::endl;
if (!GLEW_ARB_vertex_array_object) {
std::cerr << "GLEW_ARB_vertex_array_object was not available." << std::endl;
return EXIT_FAILURE;
}
if (!GLEW_VERSION_3_3) { if (!GLEW_VERSION_3_3) {
std::cerr << "OpenGL 3.3 is required to execute this app." << std::endl; std::cerr << "OpenGL 3.3 is required to execute this app." << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;

BIN
models/Cube-KTX/Cube.bin Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

193
models/Cube-KTX/Cube.gltf Normal file
View File

@@ -0,0 +1,193 @@
{
"accessors" : [
{
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5123,
"count" : 36,
"max" : [
35
],
"min" : [
0
],
"type" : "SCALAR"
},
{
"bufferView" : 1,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
1.000000,
1.000001
],
"min" : [
-1.000000,
-1.000000,
-1.000000
],
"type" : "VEC3"
},
{
"bufferView" : 2,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
1.000000,
1.000000
],
"min" : [
-1.000000,
-1.000000,
-1.000000
],
"type" : "VEC3"
},
{
"bufferView" : 3,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
-0.000000,
-0.000000,
1.000000
],
"min" : [
0.000000,
-0.000000,
-1.000000,
-1.000000
],
"type" : "VEC4"
},
{
"bufferView" : 4,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 36,
"max" : [
1.000000,
1.000000
],
"min" : [
-1.000000,
-1.000000
],
"type" : "VEC2"
}
],
"asset" : {
"generator" : "VKTS glTF 2.0 exporter",
"version" : "2.0"
},
"bufferViews" : [
{
"buffer" : 0,
"byteLength" : 72,
"byteOffset" : 0,
"target" : 34963
},
{
"buffer" : 0,
"byteLength" : 432,
"byteOffset" : 72,
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 432,
"byteOffset" : 504,
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 576,
"byteOffset" : 936,
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 288,
"byteOffset" : 1512,
"target" : 34962
}
],
"buffers" : [
{
"byteLength" : 1800,
"uri" : "Cube.bin"
}
],
"images" : [
{
"uri" : "Cube_BaseColor.ktx"
},
{
"uri" : "Cube_MetallicRoughness.ktx"
}
],
"materials" : [
{
"name" : "Cube",
"pbrMetallicRoughness" : {
"baseColorTexture" : {
"index" : 0
},
"metallicRoughnessTexture" : {
"index" : 1
}
}
}
],
"meshes" : [
{
"name" : "Cube",
"primitives" : [
{
"attributes" : {
"NORMAL" : 2,
"POSITION" : 1,
"TANGENT" : 3,
"TEXCOORD_0" : 4
},
"indices" : 0,
"material" : 0,
"mode" : 4
}
]
}
],
"nodes" : [
{
"mesh" : 0,
"name" : "Cube"
}
],
"samplers" : [
{}
],
"scene" : 0,
"scenes" : [
{
"nodes" : [
0
]
}
],
"textures" : [
{
"sampler" : 0,
"source" : 0
},
{
"sampler" : 0,
"source" : 1
}
]
}

Binary file not shown.

Binary file not shown.

12
models/Cube-KTX/README.md Normal file
View File

@@ -0,0 +1,12 @@
License: Donated by Norbert Nopper for glTF testing.
https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Cube
----
Converted .png to .ktx image by Syoyo Fujita.
.png -> .ppm using image magic(resize with 25% to reduce file size)
.ppm -> .ktx using `toktx` in KTX-Software https://github.com/KhronosGroup/KTX-Software

View File

@@ -492,14 +492,19 @@ struct Image {
std::string name; std::string name;
int width; int width;
int height; int height;
int component;
//
// For KTX image, component, bits and pixel_type will be set to `0`, and KTX attributes are stored in `extras`.
// FIXME(syoyo): Find more proper way to handle KTX and non-KTX image by keeping backward-compatibility as much as possible.
//
int component; // The number of channel components in the image.
int bits; // bit depth per channel. 8(byte), 16 or 32. int bits; // bit depth per channel. 8(byte), 16 or 32.
int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
// UBYTE(bits = 8) or USHORT(bits = 16) // UBYTE(bits = 8) or USHORT(bits = 16)
std::vector<unsigned char> image; std::vector<unsigned char> image;
int bufferView; // (required if no uri) int bufferView; // (required if no uri)
std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
// "image/bmp", "image/gif"] // "image/bmp", "image/gif", etc]
std::string uri; // (required if no mimeType) std::string uri; // (required if no mimeType)
Value extras; Value extras;
ExtensionMap extensions; ExtensionMap extensions;
@@ -1124,6 +1129,12 @@ class TinyGLTF {
#include "draco/core/decoder_buffer.h" #include "draco/core/decoder_buffer.h"
#endif #endif
#ifdef TINYGLTF_ENABLE_KTX
#ifndef TINYGLTF_NO_INCLUDE_TINY_KTX
#include "tiny_ktx.h"
#endif
#endif
#ifndef TINYGLTF_NO_STB_IMAGE #ifndef TINYGLTF_NO_STB_IMAGE
#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE #ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
#include "stb_image.h" #include "stb_image.h"
@@ -1679,8 +1690,8 @@ void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
load_image_user_data_ = user_data; load_image_user_data_ = user_data;
} }
#ifndef TINYGLTF_NO_STB_IMAGE #if !defined(TINYGLTF_NO_STB_IMAGE)
bool LoadImageData(Image *image, const int image_idx, std::string *err, bool LoadImageDataSTB(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)user_data;
@@ -1777,6 +1788,232 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err,
} }
#endif #endif
#if defined(TINYGLTF_ENABLE_KTX)
struct SimpleVFile {
std::string *err = nullptr;
const uint8_t *data = nullptr;
size_t pos = 0;
size_t len = 0;
};
static void tinyktxCallbackError(void *user, char const *msg) {
SimpleVFile *ss = reinterpret_cast<SimpleVFile *>(user);
if (ss->err) {
(*ss->err) += "Tiny_Ktx ERROR: " + std::string(msg) + "\n";
}
}
static void *tinyktxCallbackAlloc(void *user, size_t size) {
(void)user;
//std::cerr << "Alloc : " << std::to_string(size) << "\n";
return malloc(size);
};
static void tinyktxCallbackFree(void *user, void *data) {
(void)user;
free(data);
};
static size_t tinyktxCallbackRead(void *user, void* data, size_t size) {
SimpleVFile *ss = reinterpret_cast<SimpleVFile *>(user);
if ((ss->pos + size) > ss->len) {
if (ss->err) {
std::stringstream msg;
msg << "KTX read: Invalid data length. pos = " << ss->pos << ", len = " << ss->len << ", requested size to read = " << size << "\n";
(*ss->err) += msg.str();
}
return size_t(0);
}
memcpy(data, ss->data + ss->pos, size);
// Advance pos
ss->pos += size;
return size;
};
static bool tinyktxCallbackSeek(void *user, int64_t offset) {
SimpleVFile *ss = reinterpret_cast<SimpleVFile *>(user);
if ((offset < 0) || (size_t(offset) >= ss->len)) {
if (ss->err) {
std::stringstream msg;
msg << "KTX read: Invalid data offset. len = " << ss->len << ", requested offset = " << offset << "\n";
(*ss->err) += msg.str();
}
return false;
}
ss->pos = offset;
return true;
}
static int64_t tinyktxCallbackTell(void *user) {
SimpleVFile *ss = reinterpret_cast<SimpleVFile *>(user);
return int64_t(ss->pos);
};
bool LoadImageDataKTX(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;
(void)req_width;
(void)req_height;
TinyKtx_Callbacks callbacks {
&tinyktxCallbackError,
&tinyktxCallbackAlloc,
&tinyktxCallbackFree,
&tinyktxCallbackRead,
&tinyktxCallbackSeek,
&tinyktxCallbackTell
};
SimpleVFile vfile;
vfile.err = err;
vfile.pos = 0;
vfile.data = bytes;
vfile.len = size;
auto ctx = TinyKtx_CreateContext( &callbacks, reinterpret_cast<void *>(&vfile) );
TinyKtx_ReadHeader(ctx);
uint32_t w = TinyKtx_Width(ctx);
uint32_t h = TinyKtx_Height(ctx);
uint32_t d = TinyKtx_Depth(ctx);
TinyKtx_Format fmt = TinyKtx_GetFormat(ctx);
if(fmt == TKTX_UNDEFINED) {
TinyKtx_DestroyContext(ctx);
return false;
}
int num_mipmaps = TinyKtx_NumberOfMipmaps(ctx);
if (num_mipmaps < 1) {
// ???
if (err) {
(*err) += "Number of mipmaps are zero for KTX image[" + std::to_string(image_idx) + ".\n";
}
TinyKtx_DestroyContext(ctx);
return false;
}
{
// TODO(syoyo): Support mipmap.
int miplevel = 0;
size_t byte_count = TinyKtx_ImageSize(ctx, miplevel);
if (byte_count == 0) {
// ???
if (err) {
(*err) += "KTX image [" + std::to_string(image_idx) + "] has zero bytes.\n";
}
TinyKtx_DestroyContext(ctx);
return false;
}
const void *src_ptr = TinyKtx_ImageRawData(ctx, miplevel);
if (src_ptr == nullptr) {
TinyKtx_DestroyContext(ctx);
return false;
}
image->image.resize(byte_count);
memcpy(image->image.data(), TinyKtx_ImageRawData(ctx, miplevel), byte_count);
TinyKtx_DestroyContext(ctx);
// Set to 0 for KTX image.
image->pixel_type = 0;
image->bits = 0;
image->component = 0;
// extras for KTX
Value::Object extras;
extras["ktx_format"] = Value(int(fmt)); // TODO(syoyo): Store OpenGL format
extras["depth"] = Value(int(d));
extras["byteCount"] = Value(int(byte_count)); // Assume image size is < 2GB
image->extras = Value(extras);
// TODO(syoyo): Store more KTX attributes.
}
image->width = w;
image->height = h;
return true;
}
#endif
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 ret = false;
std::string stb_err;
std::string stb_warn;
#if !defined(TINYGLTF_NO_STB_IMAGE)
// Try to load images using stb_image(png, gif, bmp, tga, ...)
ret = tinygltf::LoadImageDataSTB(image, image_idx, &stb_err, &stb_warn, req_width, req_height,
bytes, size, user_data);
if (ret) {
return true;
}
#endif
std::string ktx_err;
std::string ktx_warn;
#if defined(TINYGLTF_ENABLE_KTX)
// Try KTX image
ret = tinygltf::LoadImageDataKTX(image, image_idx, &ktx_err, &ktx_warn, req_width, req_height,
bytes, size, user_data);
if (ret) {
if (warn) {
(*warn) = ktx_warn;
}
return true;
}
#endif
if (err) {
(*err) = stb_err + ktx_err;
}
if (warn) {
(*warn) = stb_warn + ktx_warn;
}
(void)image;
(void)image_idx;
(void)req_width;
(void)req_height;
(void)bytes;
(void)size;
(void)user_data;
return false;
}
void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
WriteImageData = func; WriteImageData = func;
write_image_user_data_ = user_data; write_image_user_data_ = user_data;
@@ -1800,6 +2037,8 @@ bool WriteImageData(const std::string *basepath, const std::string *filename,
std::string header; std::string header;
std::vector<unsigned char> data; std::vector<unsigned char> data;
// TODO(LTE): Support ktx
if (ext == "png") { if (ext == "png") {
if ((image->bits != 8) || if ((image->bits != 8) ||
(image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
@@ -2047,6 +2286,10 @@ static std::string MimeToExt(const std::string &mimeType) {
return "bmp"; return "bmp";
} else if (mimeType == "image/gif") { } else if (mimeType == "image/gif") {
return "gif"; return "gif";
#if defined(TINYGLTF_ENABLE_KTX)
} else if (mimeType == "image/ktx") {
return "ktx";
#endif
} }
return ""; return "";
@@ -2107,6 +2350,13 @@ bool IsDataURI(const std::string &in) {
return true; return true;
} }
#if defined(TINYGLTF_ENABLE_KTX)
header = "data:image/ktx;base64,";
if (in.find(header) == 0) {
return true;
}
#endif
header = "data:text/plain;base64,"; header = "data:text/plain;base64,";
if (in.find(header) == 0) { if (in.find(header) == 0) {
return true; return true;
@@ -2160,6 +2410,16 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
} }
} }
#if defined(TINYGLTF_ENABLE_KTX)
if (data.empty()) {
header = "data:image/ktx;base64,";
if (in.find(header) == 0) {
mime_type = "image/ktx";
data = base64_decode(in.substr(header.size())); // cut mime string.
}
}
#endif
if (data.empty()) { if (data.empty()) {
header = "data:text/plain;base64,"; header = "data:text/plain;base64,";
if (in.find(header) == 0) { if (in.find(header) == 0) {

1600
tinyktx.h Normal file

File diff suppressed because it is too large Load Diff