|
|
|
|
@@ -26,6 +26,7 @@
|
|
|
|
|
// THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
// Version:
|
|
|
|
|
// - v2.1.0 Add draco compression.
|
|
|
|
|
// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
|
|
|
|
|
// - v2.0.0 glTF 2.0!.
|
|
|
|
|
//
|
|
|
|
|
@@ -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,
|
|
|
|
|
@@ -526,8 +539,9 @@ struct BufferView {
|
|
|
|
|
// understood to be tightly packed
|
|
|
|
|
int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
|
|
|
|
|
Value extras;
|
|
|
|
|
bool dracoDecoded; // Flag indicating this has been draco decoded
|
|
|
|
|
|
|
|
|
|
BufferView() : byteOffset(0), byteStride(0) {}
|
|
|
|
|
BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {}
|
|
|
|
|
bool operator==(const BufferView &) const;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -646,6 +660,7 @@ struct Primitive {
|
|
|
|
|
// where each target is a dict with attribues in ["POSITION, "NORMAL",
|
|
|
|
|
// "TANGENT"] pointing
|
|
|
|
|
// to their corresponding accessors
|
|
|
|
|
ExtensionMap extensions;
|
|
|
|
|
Value extras;
|
|
|
|
|
|
|
|
|
|
Primitive() {
|
|
|
|
|
@@ -789,7 +804,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 +816,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
|
|
|
|
|
@@ -1048,14 +1063,19 @@ class TinyGLTF {
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "./json.hpp"
|
|
|
|
|
#include "json.hpp"
|
|
|
|
|
|
|
|
|
|
#ifdef TINYGLTF_ENABLE_DRACO
|
|
|
|
|
#include "draco/core/decoder_buffer.h"
|
|
|
|
|
#include "draco/compression/decode.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef TINYGLTF_NO_STB_IMAGE
|
|
|
|
|
#include "./stb_image.h"
|
|
|
|
|
#include "stb_image.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
|
|
|
|
|
#include "./stb_image_write.h"
|
|
|
|
|
#include "stb_image_write.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef __clang__
|
|
|
|
|
@@ -1180,7 +1200,8 @@ bool BufferView::operator==(const BufferView &other) const {
|
|
|
|
|
return this->buffer == other.buffer && this->byteLength == other.byteLength &&
|
|
|
|
|
this->byteOffset == other.byteOffset &&
|
|
|
|
|
this->byteStride == other.byteStride && this->name == other.name &&
|
|
|
|
|
this->target == other.target && this->extras == other.extras;
|
|
|
|
|
this->target == other.target && this->extras == other.extras &&
|
|
|
|
|
this->dracoDecoded == other.dracoDecoded;
|
|
|
|
|
}
|
|
|
|
|
bool Camera::operator==(const Camera &other) const {
|
|
|
|
|
return this->name == other.name && this->extensions == other.extensions &&
|
|
|
|
|
@@ -1576,9 +1597,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;
|
|
|
|
|
@@ -1598,7 +1619,7 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
if (!data) {
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
@@ -1606,7 +1627,7 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
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 +1636,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 +1646,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;
|
|
|
|
|
}
|
|
|
|
|
@@ -1729,6 +1750,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 +1777,7 @@ bool FileExists(const std::string &abs_filename, void *) {
|
|
|
|
|
} else {
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
@@ -1797,6 +1831,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 +1889,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 +2436,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 +2448,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 +2474,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 +2504,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 +2514,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;
|
|
|
|
|
}
|
|
|
|
|
@@ -2464,7 +2527,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn,
|
|
|
|
|
#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 +2535,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 +2547,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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -2674,10 +2737,7 @@ static bool ParseBufferView(BufferView *bufferView, std::string *err,
|
|
|
|
|
|
|
|
|
|
static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
|
|
|
|
|
double bufferView = -1.0;
|
|
|
|
|
if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true,
|
|
|
|
|
"Accessor")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ParseNumberProperty(&bufferView, err, o, "bufferView", false, "Accessor");
|
|
|
|
|
|
|
|
|
|
double byteOffset = 0.0;
|
|
|
|
|
ParseNumberProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
|
|
|
|
|
@@ -2759,7 +2819,184 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ParsePrimitive(Primitive *primitive, std::string *err,
|
|
|
|
|
#ifdef TINYGLTF_ENABLE_DRACO
|
|
|
|
|
|
|
|
|
|
static void DecodeIndexBuffer(draco::Mesh* mesh, size_t componentSize, std::vector<uint8_t>& outBuffer)
|
|
|
|
|
{
|
|
|
|
|
if (componentSize == 4)
|
|
|
|
|
{
|
|
|
|
|
assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
|
|
|
|
|
memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0], outBuffer.size());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
size_t faceStride = componentSize * 3;
|
|
|
|
|
for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f)
|
|
|
|
|
{
|
|
|
|
|
const draco::Mesh::Face& face = mesh->face(f);
|
|
|
|
|
if (componentSize == 2)
|
|
|
|
|
{
|
|
|
|
|
uint16_t indices[3] = { (uint16_t)face[0].value(), (uint16_t)face[1].value(), (uint16_t)face[2].value() };
|
|
|
|
|
memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], faceStride);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
uint8_t indices[3] = { (uint8_t)face[0].value(), (uint8_t)face[1].value(), (uint8_t)face[2].value() };
|
|
|
|
|
memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], faceStride);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
static bool GetAttributeForAllPoints(draco::Mesh* mesh, const draco::PointAttribute* pAttribute, std::vector<uint8_t>& outBuffer)
|
|
|
|
|
{
|
|
|
|
|
size_t byteOffset = 0;
|
|
|
|
|
T values[4] = { 0, 0, 0, 0 };
|
|
|
|
|
for (draco::PointIndex i(0); i < mesh->num_points(); ++i)
|
|
|
|
|
{
|
|
|
|
|
const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
|
|
|
|
|
if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(), values))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
memcpy(outBuffer.data() + byteOffset, &values[0], sizeof(T) * pAttribute->num_components());
|
|
|
|
|
byteOffset += sizeof(T) * pAttribute->num_components();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh* mesh, const draco::PointAttribute* pAttribute, std::vector<uint8_t>& outBuffer)
|
|
|
|
|
{
|
|
|
|
|
bool decodeResult = false;
|
|
|
|
|
switch (componentType)
|
|
|
|
|
{
|
|
|
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
|
|
|
|
decodeResult = GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
|
|
|
|
|
break;
|
|
|
|
|
case TINYGLTF_COMPONENT_TYPE_BYTE:
|
|
|
|
|
decodeResult = GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
|
|
|
|
|
break;
|
|
|
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
|
|
|
|
decodeResult = GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
|
|
|
|
|
break;
|
|
|
|
|
case TINYGLTF_COMPONENT_TYPE_SHORT:
|
|
|
|
|
decodeResult = GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
|
|
|
|
|
break;
|
|
|
|
|
case TINYGLTF_COMPONENT_TYPE_INT:
|
|
|
|
|
decodeResult = GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
|
|
|
|
|
break;
|
|
|
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
|
|
|
|
|
decodeResult = GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
|
|
|
|
|
break;
|
|
|
|
|
case TINYGLTF_COMPONENT_TYPE_FLOAT:
|
|
|
|
|
decodeResult = GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
|
|
|
|
|
break;
|
|
|
|
|
case TINYGLTF_COMPONENT_TYPE_DOUBLE:
|
|
|
|
|
decodeResult = GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return decodeResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ParseDracoExtension(Primitive *primitive, Model *model, std::string *err, const Value &dracoExtensionValue)
|
|
|
|
|
{
|
|
|
|
|
auto bufferViewValue = dracoExtensionValue.Get("bufferView");
|
|
|
|
|
if (!bufferViewValue.IsInt())
|
|
|
|
|
return false;
|
|
|
|
|
auto attributesValue = dracoExtensionValue.Get("attributes");
|
|
|
|
|
if (!attributesValue.IsObject())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
auto attributesObject = attributesValue.Get<Value::Object>();
|
|
|
|
|
int bufferView = bufferViewValue.Get<int>();
|
|
|
|
|
|
|
|
|
|
BufferView& view = model->bufferViews[bufferView];
|
|
|
|
|
Buffer& buffer = model->buffers[view.buffer];
|
|
|
|
|
// BufferView has already been decoded
|
|
|
|
|
if (view.dracoDecoded)
|
|
|
|
|
return true;
|
|
|
|
|
view.dracoDecoded = true;
|
|
|
|
|
|
|
|
|
|
const char* bufferViewData = reinterpret_cast<const char*>(buffer.data.data() + view.byteOffset);
|
|
|
|
|
size_t bufferViewSize = view.byteLength;
|
|
|
|
|
|
|
|
|
|
// decode draco
|
|
|
|
|
draco::DecoderBuffer decoderBuffer;
|
|
|
|
|
decoderBuffer.Init(bufferViewData, bufferViewSize);
|
|
|
|
|
draco::Decoder decoder;
|
|
|
|
|
auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
|
|
|
|
|
if (!decodeResult.ok()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const std::unique_ptr<draco::Mesh>& mesh = decodeResult.value();
|
|
|
|
|
|
|
|
|
|
// create new bufferView for indices
|
|
|
|
|
if (primitive->indices >= 0)
|
|
|
|
|
{
|
|
|
|
|
int32_t componentSize = GetComponentSizeInBytes(model->accessors[primitive->indices].componentType);
|
|
|
|
|
Buffer decodedIndexBuffer;
|
|
|
|
|
decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
|
|
|
|
|
|
|
|
|
|
DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
|
|
|
|
|
|
|
|
|
|
model->buffers.emplace_back(std::move(decodedIndexBuffer));
|
|
|
|
|
|
|
|
|
|
BufferView decodedIndexBufferView;
|
|
|
|
|
decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
|
|
|
|
|
decodedIndexBufferView.byteLength = int(mesh->num_faces() * 3 * componentSize);
|
|
|
|
|
decodedIndexBufferView.byteOffset = 0;
|
|
|
|
|
decodedIndexBufferView.byteStride = 0;
|
|
|
|
|
decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
|
|
|
|
model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
|
|
|
|
|
|
|
|
|
|
model->accessors[primitive->indices].bufferView = int(model->bufferViews.size() - 1);
|
|
|
|
|
model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& attribute : attributesObject)
|
|
|
|
|
{
|
|
|
|
|
if (!attribute.second.IsInt())
|
|
|
|
|
return false;
|
|
|
|
|
auto primitiveAttribute = primitive->attributes.find(attribute.first);
|
|
|
|
|
if (primitiveAttribute == primitive->attributes.end())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
int dracoAttributeIndex = attribute.second.Get<int>();
|
|
|
|
|
const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
|
|
|
|
|
const auto pBuffer = pAttribute->buffer();
|
|
|
|
|
const auto componentType = model->accessors[primitiveAttribute->second].componentType;
|
|
|
|
|
|
|
|
|
|
// Create a new buffer for this decoded buffer
|
|
|
|
|
Buffer decodedBuffer;
|
|
|
|
|
size_t bufferSize = mesh->num_points() * pAttribute->num_components() * GetComponentSizeInBytes(componentType);
|
|
|
|
|
decodedBuffer.data.resize(bufferSize);
|
|
|
|
|
|
|
|
|
|
if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute, decodedBuffer.data))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
model->buffers.emplace_back(std::move(decodedBuffer));
|
|
|
|
|
|
|
|
|
|
BufferView decodedBufferView;
|
|
|
|
|
decodedBufferView.buffer = int(model->buffers.size() - 1);
|
|
|
|
|
decodedBufferView.byteLength = bufferSize;
|
|
|
|
|
decodedBufferView.byteOffset = pAttribute->byte_offset();
|
|
|
|
|
decodedBufferView.byteStride = pAttribute->byte_stride();
|
|
|
|
|
decodedBufferView.target = primitive->indices >= 0 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER : TINYGLTF_TARGET_ARRAY_BUFFER;
|
|
|
|
|
model->bufferViews.emplace_back(std::move(decodedBufferView));
|
|
|
|
|
|
|
|
|
|
model->accessors[primitiveAttribute->second].bufferView = int(model->bufferViews.size() - 1);
|
|
|
|
|
model->accessors[primitiveAttribute->second].count = int(mesh->num_points());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
|
|
|
|
|
const json &o) {
|
|
|
|
|
double material = -1.0;
|
|
|
|
|
ParseNumberProperty(&material, err, o, "material", false);
|
|
|
|
|
@@ -2799,10 +3036,20 @@ static bool ParsePrimitive(Primitive *primitive, std::string *err,
|
|
|
|
|
|
|
|
|
|
ParseExtrasProperty(&(primitive->extras), o);
|
|
|
|
|
|
|
|
|
|
ParseExtensionsProperty(&primitive->extensions, err, o);
|
|
|
|
|
|
|
|
|
|
#ifdef TINYGLTF_ENABLE_DRACO
|
|
|
|
|
auto dracoExtension = primitive->extensions.find("KHR_draco_mesh_compression");
|
|
|
|
|
if (dracoExtension != primitive->extensions.end())
|
|
|
|
|
{
|
|
|
|
|
ParseDracoExtension(primitive, model, err, dracoExtension->second);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ParseMesh(Mesh *mesh, std::string *err, const json &o) {
|
|
|
|
|
static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o) {
|
|
|
|
|
ParseStringProperty(&mesh->name, err, o, "name", false);
|
|
|
|
|
|
|
|
|
|
mesh->primitives.clear();
|
|
|
|
|
@@ -2811,7 +3058,7 @@ static bool ParseMesh(Mesh *mesh, std::string *err, const json &o) {
|
|
|
|
|
for (json::const_iterator i = primObject.value().begin();
|
|
|
|
|
i != primObject.value().end(); i++) {
|
|
|
|
|
Primitive primitive;
|
|
|
|
|
if (ParsePrimitive(&primitive, err, i.value())) {
|
|
|
|
|
if (ParsePrimitive(&primitive, model, err, i.value())) {
|
|
|
|
|
// Only add the primitive if the parsing succeeds.
|
|
|
|
|
mesh->primitives.push_back(primitive);
|
|
|
|
|
}
|
|
|
|
|
@@ -3470,7 +3717,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
Mesh mesh;
|
|
|
|
|
if (!ParseMesh(&mesh, err, it->get<json>())) {
|
|
|
|
|
if (!ParseMesh(&mesh, model, err, it->get<json>())) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -3612,15 +3859,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 +3878,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 +3895,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_);
|
|
|
|
|
@@ -4575,14 +4823,14 @@ static void WriteBinaryGltfFile(const std::string &output,
|
|
|
|
|
const int padding_size = content.size() % 4;
|
|
|
|
|
|
|
|
|
|
// 12 bytes for header, JSON content length, 8 bytes for JSON chunk info, padding
|
|
|
|
|
const int length = 12 + 8 + content.size() + padding_size;
|
|
|
|
|
const int length = 12 + 8 + int(content.size()) + padding_size;
|
|
|
|
|
|
|
|
|
|
gltfFile.write(header.c_str(), header.size());
|
|
|
|
|
gltfFile.write(reinterpret_cast<const char *>(&version), sizeof(version));
|
|
|
|
|
gltfFile.write(reinterpret_cast<const char *>(&length), sizeof(length));
|
|
|
|
|
|
|
|
|
|
// JSON chunk info, then JSON data
|
|
|
|
|
const int model_length = content.size() + padding_size;
|
|
|
|
|
const int model_length = int(content.size()) + padding_size;
|
|
|
|
|
const int model_format = 0x4E4F534A;
|
|
|
|
|
gltfFile.write(reinterpret_cast<const char *>(&model_length), sizeof(model_length));
|
|
|
|
|
gltfFile.write(reinterpret_cast<const char *>(&model_format), sizeof(model_format));
|
|
|
|
|
|