From c7bf8000750140a384ade11270343cb7da4dabb5 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 5 Jul 2019 16:19:34 +0900 Subject: [PATCH] Support KTX texture of R8G8B8 format in `basic` example. --- examples/basic/main.cpp | 88 ++++++++-- models/Cube-KTX/Cube.bin | Bin 0 -> 1800 bytes models/Cube-KTX/Cube.gltf | 193 +++++++++++++++++++++ models/Cube-KTX/Cube_BaseColor.ktx | Bin 0 -> 49248 bytes models/Cube-KTX/Cube_MetallicRoughness.ktx | Bin 0 -> 49248 bytes models/Cube-KTX/README.md | 12 ++ tiny_gltf.h | 53 ++++-- 7 files changed, 313 insertions(+), 33 deletions(-) create mode 100644 models/Cube-KTX/Cube.bin create mode 100644 models/Cube-KTX/Cube.gltf create mode 100644 models/Cube-KTX/Cube_BaseColor.ktx create mode 100644 models/Cube-KTX/Cube_MetallicRoughness.ktx create mode 100644 models/Cube-KTX/README.md diff --git a/examples/basic/main.cpp b/examples/basic/main.cpp index 03fb67c..d3b7b4a 100644 --- a/examples/basic/main.cpp +++ b/examples/basic/main.cpp @@ -14,6 +14,7 @@ #endif // Inlude tinyktx.h before tiny_gltf.h +// to get TKTX_*** definitions #define TINYKTX_IMPLEMENTATION #include "../../tinyktx.h" @@ -27,6 +28,8 @@ #define TINYGLTF_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" @@ -56,6 +59,30 @@ bool loadModel(tinygltf::Model &model, const char *filename) { 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 bindMesh(std::map vbos, tinygltf::Model &model, tinygltf::Mesh &mesh) { for (size_t i = 0; i < model.bufferViews.size(); ++i) { @@ -114,7 +141,7 @@ std::map bindMesh(std::map vbos, accessor.normalized ? GL_TRUE : GL_FALSE, byteStride, BUFFER_OFFSET(accessor.byteOffset)); } else - std::cout << "vaa missing: " << attrib.first << std::endl; + std::cout << "Unsupported vertex attribute: " << attrib.first << std::endl; } GLuint texid; @@ -130,29 +157,52 @@ std::map bindMesh(std::map vbos, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + GLint internal_format = GL_RGBA; 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; + + bool valid = false; + + // KTX extension + if (image.extras.Has("ktx_format")) { + + valid = GetOpenGLFormatFromKTX(image.extras.Get("ktx_format").Get(), &internal_format, &format, &type); + + if (valid) { + + std::cout << "ktx_format: " << image.extras.Get("ktx_format").Get() << std::endl; + std::cout << "ktx image size: " << image.width << ", " << image.height << std::endl; + } + } else { - // ??? + + if (image.component == 1) { + format = GL_RED; + } else if (image.component == 2) { + format = GL_RG; + } else if (image.component == 3) { + format = GL_RGB; + } else if (image.component == 4) { + format = GL_RGBA; + } else { + valid = false; + } + + if (image.bits == 8) { + // ok + } else if (image.bits == 16) { + type = GL_UNSIGNED_SHORT; + } else { + valid = false; + } + } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0, - format, type, &image.image.at(0)); + if (valid) { + + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, image.width, image.height, 0, + format, type, &image.image.at(0)); + } } return vbos; diff --git a/models/Cube-KTX/Cube.bin b/models/Cube-KTX/Cube.bin new file mode 100644 index 0000000000000000000000000000000000000000..7dae4b0b93e3251ca8886c43b6a9403f27648a39 GIT binary patch literal 1800 zcmcIhNm2wc3={jX@B1<=Z=g8#)5wWmQS&680G1PF(jh%P2S`;{Y*|ulH?V?LtYIA+ z*u)kl*v1Zav4?#e;1EYR#tBYwhI3rt5?8p!4Q_FVdpux@M?B#fF91Sk->kS959VLr z@8oxrsPot`@w+0f-8FtBIc~hgDG4+1eGd6JQYQAF) zU*f9i?i!eQ^_{o~QDeW(f`OPNhH^g zGgfh|f4+UZiu*fWv=`dZn0fUy zjohgr`J2aB?taGS(VMy7)I&251>eO*ZZ&GMAM19;=ZXK`x&HQ89zETBDgVR_skeXF zPwqv#OMPX$EB3-ZEZ5P#bUPmAeBwIVWvOSa)Rr}A%es4oxu#b$T&Ia!U7nf8+0vJ> I)N}Os0UGHvApigX literal 0 HcmV?d00001 diff --git a/models/Cube-KTX/Cube.gltf b/models/Cube-KTX/Cube.gltf new file mode 100644 index 0000000..fc8aca0 --- /dev/null +++ b/models/Cube-KTX/Cube.gltf @@ -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 + } + ] +} diff --git a/models/Cube-KTX/Cube_BaseColor.ktx b/models/Cube-KTX/Cube_BaseColor.ktx new file mode 100644 index 0000000000000000000000000000000000000000..888c9e789fb130527dc9556a4f73750264f7fe2e GIT binary patch literal 49248 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 P0RjXF5FkK+z`q4Pzupc~ literal 0 HcmV?d00001 diff --git a/models/Cube-KTX/README.md b/models/Cube-KTX/README.md new file mode 100644 index 0000000..f9f032c --- /dev/null +++ b/models/Cube-KTX/README.md @@ -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 + diff --git a/tiny_gltf.h b/tiny_gltf.h index 888053e..883fd52 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -1808,6 +1808,7 @@ static void tinyktxCallbackError(void *user, char const *msg) { static void *tinyktxCallbackAlloc(void *user, size_t size) { (void)user; + //std::cerr << "Alloc : " << std::to_string(size) << "\n"; return malloc(size); }; @@ -1819,7 +1820,7 @@ static void tinyktxCallbackFree(void *user, void *data) { static size_t tinyktxCallbackRead(void *user, void* data, size_t size) { SimpleVFile *ss = reinterpret_cast(user); - if ((ss->pos + size) >= ss->len) { + 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"; @@ -1830,6 +1831,9 @@ static size_t tinyktxCallbackRead(void *user, void* data, size_t size) { memcpy(data, ss->data + ss->pos, size); + // Advance pos + ss->pos += size; + return size; }; @@ -1871,7 +1875,7 @@ bool LoadImageDataKTX(Image *image, const int image_idx, std::string *err, &tinyktxCallbackError, &tinyktxCallbackAlloc, &tinyktxCallbackFree, - tinyktxCallbackRead, + &tinyktxCallbackRead, &tinyktxCallbackSeek, &tinyktxCallbackTell }; @@ -1920,6 +1924,12 @@ bool LoadImageDataKTX(Image *image, const int image_idx, std::string *err, 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); @@ -1952,34 +1962,49 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, 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, err, warn, req_width, req_height, + 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 defined(TINYGLTF_NO_STB_IMAGE) - // Try to load images using stb_image(png, gif, bmp, tga, ...) - - ret = tinygltf::LoadImageDataSTB(image, image_idx, err, warn, req_width, req_height, - bytes, size, user_data); - - if (ret) { - return true; + if (err) { + (*err) = stb_err + ktx_err; } -#endif + if (warn) { + (*warn) = stb_warn + ktx_warn; + } (void)image; (void)image_idx; - (void)err; - (void)warn; (void)req_width; (void)req_height; (void)bytes;