|
|
|
|
@@ -34,6 +34,7 @@
|
|
|
|
|
// - jsonhpp: C++ JSON library.
|
|
|
|
|
// - base64: base64 decode/encode library.
|
|
|
|
|
// - stb_image: Image loading library.
|
|
|
|
|
// - lodepng: Load 16bit PNG.
|
|
|
|
|
//
|
|
|
|
|
#ifndef TINY_GLTF_H_
|
|
|
|
|
#define TINY_GLTF_H_
|
|
|
|
|
@@ -47,6 +48,12 @@
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
|
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
|
|
|
|
#include <android/asset_manager.h>
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace tinygltf {
|
|
|
|
|
|
|
|
|
|
#define TINYGLTF_MODE_POINTS (0)
|
|
|
|
|
@@ -139,6 +146,12 @@ namespace tinygltf {
|
|
|
|
|
#define TINYGLTF_DOUBLE_EPS (1.e-12)
|
|
|
|
|
#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
|
|
|
|
|
|
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
|
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
|
|
|
|
AAssetManager* asset_manager = nullptr;
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
NULL_TYPE = 0,
|
|
|
|
|
NUMBER_TYPE = 1,
|
|
|
|
|
@@ -464,8 +477,10 @@ struct Image {
|
|
|
|
|
std::string name;
|
|
|
|
|
int width;
|
|
|
|
|
int height;
|
|
|
|
|
int component;
|
|
|
|
|
std::vector<unsigned char> image;
|
|
|
|
|
int component; // channels. e.g. RGB=3, RGBA=4
|
|
|
|
|
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; // width * height * component * (bits/8)
|
|
|
|
|
int bufferView; // (required if no uri)
|
|
|
|
|
std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
|
|
|
|
|
// "image/bmp", "image/gif"]
|
|
|
|
|
@@ -486,6 +501,8 @@ struct Image {
|
|
|
|
|
width = -1;
|
|
|
|
|
height = -1;
|
|
|
|
|
component = -1;
|
|
|
|
|
bits = -1;
|
|
|
|
|
pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
|
|
|
|
|
}
|
|
|
|
|
bool operator==(const Image &) const;
|
|
|
|
|
};
|
|
|
|
|
@@ -789,7 +806,7 @@ enum SectionCheck {
|
|
|
|
|
///
|
|
|
|
|
/// LoadImageDataFunction type. Signature for custom image loading callbacks.
|
|
|
|
|
///
|
|
|
|
|
typedef bool (*LoadImageDataFunction)(Image *, std::string *, std::string *,
|
|
|
|
|
typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, std::string *,
|
|
|
|
|
int, int, const unsigned char *, int,
|
|
|
|
|
void *);
|
|
|
|
|
|
|
|
|
|
@@ -801,7 +818,7 @@ typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
|
|
|
|
|
|
|
|
|
|
#ifndef TINYGLTF_NO_STB_IMAGE
|
|
|
|
|
// Declaration of default image loader callback
|
|
|
|
|
bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
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
|
|
|
|
|
@@ -1058,6 +1075,10 @@ class TinyGLTF {
|
|
|
|
|
#include "./stb_image_write.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(TINYGLTF_USE_LODEPNG)
|
|
|
|
|
#include "./lodepng.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef __clang__
|
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
|
#endif
|
|
|
|
|
@@ -1191,6 +1212,7 @@ bool Camera::operator==(const Camera &other) const {
|
|
|
|
|
bool Image::operator==(const Image &other) const {
|
|
|
|
|
return this->bufferView == other.bufferView &&
|
|
|
|
|
this->component == other.component && this->extras == other.extras &&
|
|
|
|
|
this->bits == other.bits &&
|
|
|
|
|
this->height == other.height && this->image == other.image &&
|
|
|
|
|
this->mimeType == other.mimeType && this->name == other.name &&
|
|
|
|
|
this->uri == other.uri && this->width == other.width;
|
|
|
|
|
@@ -1576,9 +1598,9 @@ void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef TINYGLTF_NO_STB_IMAGE
|
|
|
|
|
bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
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 *) {
|
|
|
|
|
int size, void *user_data) {
|
|
|
|
|
(void)warn;
|
|
|
|
|
|
|
|
|
|
int w, h, comp, req_comp;
|
|
|
|
|
@@ -1587,6 +1609,9 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
@@ -1596,17 +1621,34 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
unsigned char *data =
|
|
|
|
|
stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
|
|
|
|
|
if (!data) {
|
|
|
|
|
#if defined(TINYGLTF_USE_LODEPNG)
|
|
|
|
|
// try to load as 16bit PNG RGBA
|
|
|
|
|
unsigned ret = lodepng_decode_memory(&data, reinterpret_cast<unsigned *>(&w), reinterpret_cast<unsigned *>(&h), bytes, size, LCT_RGBA, /* bitdepth*/16);
|
|
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
// NOTE: you can use `warn` instead of `err`
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Unknown image format. STB and LodePNG cannot decode image data for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bits = 16;
|
|
|
|
|
pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
// NOTE: you can use `warn` instead of `err`
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Unknown image format.\n";
|
|
|
|
|
(*err) += "Unknown image format. STB cannot decode image data for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\". Proably 16bit PNG?\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (w < 1 || h < 1) {
|
|
|
|
|
free(data);
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Invalid image data.\n";
|
|
|
|
|
(*err) += "Invalid image data for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -1615,7 +1657,7 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
if (req_width != w) {
|
|
|
|
|
free(data);
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Image width mismatch.\n";
|
|
|
|
|
(*err) += "Image width mismatch for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -1625,7 +1667,7 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
if (req_height != h) {
|
|
|
|
|
free(data);
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Image height mismatch.\n";
|
|
|
|
|
(*err) += "Image height mismatch. for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -1634,8 +1676,10 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
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());
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
|
|
free(data);
|
|
|
|
|
|
|
|
|
|
@@ -1729,6 +1773,18 @@ 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 {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
FILE *fp;
|
|
|
|
|
errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
|
|
|
|
|
@@ -1744,6 +1800,7 @@ bool FileExists(const std::string &abs_filename, void *) {
|
|
|
|
|
} else {
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
@@ -1797,6 +1854,33 @@ std::string ExpandFilePath(const std::string &filepath, void *) {
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
if (!asset) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "File open error : " + filepath + "\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
size_t size = AAsset_getLength(asset);
|
|
|
|
|
if (size <= 0) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Invalid file size : " + filepath +
|
|
|
|
|
" (does the path point to a directory?)";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
out->resize(size);
|
|
|
|
|
AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
|
|
|
|
|
AAsset_close(asset);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "No asset manager specified : " + filepath + "\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
std::ifstream f(filepath.c_str(), std::ifstream::binary);
|
|
|
|
|
if (!f) {
|
|
|
|
|
if (err) {
|
|
|
|
|
@@ -1828,6 +1912,7 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
|
|
|
|
|
f.close();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool WriteWholeFile(std::string *err, const std::string &filepath,
|
|
|
|
|
@@ -2374,7 +2459,7 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
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,
|
|
|
|
|
@@ -2386,24 +2471,25 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
bool hasBufferView = (o.find("bufferView") != o.end());
|
|
|
|
|
bool hasURI = (o.find("uri") != o.end());
|
|
|
|
|
|
|
|
|
|
ParseStringProperty(&image->name, err, o, "name", false);
|
|
|
|
|
|
|
|
|
|
if (hasBufferView && hasURI) {
|
|
|
|
|
// Should not both defined.
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) +=
|
|
|
|
|
"Only one of `bufferView` or `uri` should be defined, but both are "
|
|
|
|
|
"defined for Image.\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.\n";
|
|
|
|
|
(*err) += "Neither required `bufferView` nor `uri` defined for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ParseStringProperty(&image->name, err, o, "name", false);
|
|
|
|
|
ParseExtensionsProperty(&image->extensions, err, o);
|
|
|
|
|
ParseExtrasProperty(&image->extras, o);
|
|
|
|
|
|
|
|
|
|
@@ -2411,7 +2497,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
double bufferView = -1;
|
|
|
|
|
if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Failed to parse `bufferView` for Image.\n";
|
|
|
|
|
(*err) += "Failed to parse `bufferView` for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -2441,7 +2527,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
std::string tmp_err;
|
|
|
|
|
if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Failed to parse `uri` for Image.\n";
|
|
|
|
|
(*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -2451,7 +2537,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
if (IsDataURI(uri)) {
|
|
|
|
|
if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "Failed to decode 'uri' for image parameter.\n";
|
|
|
|
|
(*err) += "Failed to decode 'uri' for image[" + std::to_string(image_idx) + "] name = [" + image->name + "]\n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -2460,11 +2546,12 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
// Keep texture path (for textures that cannot be decoded)
|
|
|
|
|
image->uri = uri;
|
|
|
|
|
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
|
|
|
|
|
// TODO(syoyo): Call LoadImageData callback?
|
|
|
|
|
return true;
|
|
|
|
|
#endif
|
|
|
|
|
if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
|
|
|
|
|
if (warn) {
|
|
|
|
|
(*warn) += "Failed to load external 'uri' for image parameter\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;
|
|
|
|
|
@@ -2472,7 +2559,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
|
|
|
|
|
if (img.empty()) {
|
|
|
|
|
if (warn) {
|
|
|
|
|
(*warn) += "Image is empty.\n";
|
|
|
|
|
(*warn) += "Image data is empty for image[" + std::to_string(image_idx) + "] name = [" + image->name + "] \n";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -2484,7 +2571,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return (*LoadImageData)(image, err, warn, 0, 0, &img.at(0),
|
|
|
|
|
return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
|
|
|
|
|
static_cast<int>(img.size()), load_image_user_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -3612,15 +3699,16 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
|
|
|
|
|
|
|
|
json::const_iterator it(root.begin());
|
|
|
|
|
json::const_iterator itEnd(root.end());
|
|
|
|
|
for (; it != itEnd; it++) {
|
|
|
|
|
int idx = 0;
|
|
|
|
|
for (; it != itEnd; it++, idx++) {
|
|
|
|
|
if (!it.value().is_object()) {
|
|
|
|
|
if (err) {
|
|
|
|
|
(*err) += "`images' does not contain an JSON object.";
|
|
|
|
|
(*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
Image image;
|
|
|
|
|
if (!ParseImage(&image, err, warn, it.value(), base_dir, &fs,
|
|
|
|
|
if (!ParseImage(&image, idx, err, warn, it.value(), base_dir, &fs,
|
|
|
|
|
&this->LoadImageData, load_image_user_data_)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -3630,7 +3718,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
|
|
|
if (size_t(image.bufferView) >= model->bufferViews.size()) {
|
|
|
|
|
if (err) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "bufferView \"" << image.bufferView
|
|
|
|
|
ss << "image[" << idx << "] bufferView \"" << image.bufferView
|
|
|
|
|
<< "\" not found in the scene." << std::endl;
|
|
|
|
|
(*err) += ss.str();
|
|
|
|
|
}
|
|
|
|
|
@@ -3647,7 +3735,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
bool ret = LoadImageData(&image, err, warn, image.width, image.height,
|
|
|
|
|
bool ret = LoadImageData(&image, idx, err, warn, image.width, image.height,
|
|
|
|
|
&buffer.data[bufferView.byteOffset],
|
|
|
|
|
static_cast<int>(bufferView.byteLength),
|
|
|
|
|
load_image_user_data_);
|
|
|
|
|
|