diff --git a/.travis.yml b/.travis.yml index 6e731e5..37779f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,15 +7,15 @@ matrix: sources: - george-edison55-precise-backports - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.7 + - llvm-toolchain-trusty-3.9 packages: - g++-4.9 - - clang-3.7 + - clang-3.9 compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug + env: COMPILER_VERSION=3.9 BUILD_TYPE=Debug - addons: *1 compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Release + env: COMPILER_VERSION=3.9 BUILD_TYPE=Release - addons: &2 apt: sources: @@ -30,7 +30,7 @@ matrix: env: COMPILER_VERSION=4.9 BUILD_TYPE=Release EXTRA_CXXFLAGS="-fsanitize=address" - addons: *1 compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0" + env: COMPILER_VERSION=3.9 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0" before_install: - ./.travis-before-install.sh diff --git a/README.md b/README.md index 450f5b4..4e68225 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ `TinyGLTF` is a header only C++11 glTF 2.0 https://github.com/KhronosGroup/glTF library. -## Status - -Work in process(`devel` branch). Very near to release, but need more tests and examples. - `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. +## Status + +v2.0.0 release(22 Aug, 2018)! + ## Builds [![Build Status](https://travis-ci.org/syoyo/tinygltf.svg?branch=devel)](https://travis-ci.org/syoyo/tinygltf) @@ -57,13 +57,13 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch. ## TODOs -* [ ] Write C++ code generator from jSON schema for robust parsing. -* [x] Serialization -* [ ] Compression/decompression(Open3DGC, etc) +* [ ] Write C++ code generator which emits C++ code from JSON schema for robust parsing. +* [ ] Mesh Compression/decompression(Open3DGC, etc) + * [ ] Load Draco compressed mesh * [ ] Support `extensions` and `extras` property * [ ] HDR image? * [ ] OpenEXR extension through TinyEXR. -* [ ] Write tests for `animation` and `skin` +* [ ] Write exampple and tests for `animation` and `skin` ## Licenses @@ -96,9 +96,15 @@ using namespace tinygltf; Model model; TinyGLTF loader; std::string err; +std::string warn; -bool ret = loader.LoadASCIIFromFile(&model, &err, argv[1]); -//bool ret = loader.LoadBinaryFromFile(&model, &err, argv[1]); // for binary glTF(.glb) +bool ret = loader.LoadASCIIFromFile(&model, &err, &warn, argv[1]); +//bool ret = loader.LoadBinaryFromFile(&model, &err, &warn, argv[1]); // for binary glTF(.glb) + +if (!warn.empty()) { + printf("Warn: %s\n", warn.c_str()); +} + if (!err.empty()) { printf("Err: %s\n", err.c_str()); } diff --git a/experimental/README.md b/experimental/README.md new file mode 100644 index 0000000..cf8f21e --- /dev/null +++ b/experimental/README.md @@ -0,0 +1,15 @@ +# Python script which generates C++11 code from JSON schema. + +## Requirements + +* python3 +* jsonref + +## Generate + +Run `gen.py` by specifing the path to glTF schema directory(from https://github.com/KhronosGroup/glTF.git) + +``` +$ python gen.py /path/to/glTF/specification/2.0/schema +``` + diff --git a/experimental/gen.py b/experimental/gen.py new file mode 100644 index 0000000..25b0484 --- /dev/null +++ b/experimental/gen.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys, os +import subprocess +import json +from pprint import pprint +import jsonref + +# glTF 2.0 +schema_files = [ + "glTF.schema.json" +] + +def main(): + if len(sys.argv) < 2: + print("Requires path to glTF scheme directory.") + sys.exit(-1) + + gltf_schema_dir = sys.argv[1] + + gltf_schema_filepath = os.path.join(gltf_schema_dir, schema_files[0]) + if not os.path.exists(gltf_schema_filepath): + print("File not found: {}".format(gltf_schema_filepath)) + sys.exit(-1) + + gltf_schema_uri = 'file://{}/'.format(gltf_schema_dir) + with open(gltf_schema_filepath) as schema_file: + j = jsonref.loads(schema_file.read(), base_uri=gltf_schema_uri, jsonschema=True) + pprint(j) + + + +main() diff --git a/loader_example.cc b/loader_example.cc index 8ae20bc..7f8a426 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -1,3 +1,6 @@ +// +// TODO(syoyo): Print extensions and extras for each glTF object. +// #define TINYGLTF_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION @@ -514,6 +517,7 @@ static void Dump(const tinygltf::Model &model) { std::cout << Indent(2) << "width : " << image.width << std::endl; std::cout << Indent(2) << "height : " << image.height << std::endl; std::cout << Indent(2) << "component : " << image.component << std::endl; + DumpExtensions(image.extensions, 1); } } @@ -525,6 +529,7 @@ static void Dump(const tinygltf::Model &model) { << std::endl; std::cout << Indent(1) << "source : " << texture.source << std::endl; + DumpExtensions(texture.extensions, 1); } } diff --git a/tests/tester.cc b/tests/tester.cc index 377ea0e..ca0ced3 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -18,8 +18,9 @@ TEST_CASE("parse-error", "[parse]") { tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; + std::string warn; - bool ret = ctx.LoadASCIIFromString(&model, &err, "bora", strlen("bora"), /* basedir*/ ""); + bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, "bora", strlen("bora"), /* basedir*/ ""); REQUIRE(false == ret); @@ -30,8 +31,9 @@ TEST_CASE("datauri-in-glb", "[issue-79]") { tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; + std::string warn; - bool ret = ctx.LoadBinaryFromFile(&model, &err, "../models/box01.glb"); + bool ret = ctx.LoadBinaryFromFile(&model, &err, &warn, "../models/box01.glb"); if (!err.empty()) { std::cerr << err << std::endl; } diff --git a/tiny_gltf.h b/tiny_gltf.h index fc5f189..f69f7a7 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -189,6 +189,11 @@ static inline int32_t GetTypeSizeInBytes(uint32_t ty) { } } +bool IsDataURI(const std::string &in); +bool DecodeDataURI(std::vector *out, + std::string &mime_type, const std::string &in, + size_t reqBytes, bool checkSize); + #ifdef __clang__ #pragma clang diagnostic push // Suppress warning for : static Value null_value @@ -451,8 +456,16 @@ struct Image { // "image/bmp", "image/gif"] std::string uri; // (required if no mimeType) Value extras; + ExtensionMap extensions; - Image() { bufferView = -1; } + // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg compressed for "image/jpeg" mime) + // This feature is good if you use custom image loader function. + // (e.g. delayed decoding of images for faster glTF parsing) + // Default parser for Image does not provide as-is loading feature at the moment. + // (You can manipulate this by providing your own LoadImageData function) + bool as_is; + + Image() : as_is(false) { bufferView = -1; } }; struct Texture { @@ -954,7 +967,6 @@ class TinyGLTF { #pragma clang diagnostic ignored "-Wexit-time-destructors" #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wold-style-cast" -#pragma clang diagnostic ignored "-Wdouble-promotion" #pragma clang diagnostic ignored "-Wglobal-constructors" #pragma clang diagnostic ignored "-Wreserved-id-macro" #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" @@ -966,6 +978,9 @@ class TinyGLTF { #pragma clang diagnostic ignored "-Wimplicit-fallthrough" #pragma clang diagnostic ignored "-Wweak-vtables" #pragma clang diagnostic ignored "-Wcovered-switch-default" +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" +#endif #if __has_warning("-Wcomma") #pragma clang diagnostic ignored "-Wcomma" #endif @@ -1233,7 +1248,7 @@ std::string base64_decode(std::string const &encoded_string) { static bool LoadExternalFile(std::vector *out, std::string *err, std::string *warn, const std::string &filename, - const std::string &basedir, size_t reqBytes, + const std::string &basedir, bool required, size_t reqBytes, bool checkSize, FsCallbacks *fs) { if (fs == nullptr || fs->FileExists == nullptr || fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) { @@ -1244,6 +1259,8 @@ static bool LoadExternalFile(std::vector *out, std::string *err, return false; } + std::string* failMsgOut = required ? err : warn; + out->clear(); std::vector paths; @@ -1252,8 +1269,8 @@ static bool LoadExternalFile(std::vector *out, std::string *err, std::string filepath = FindFile(paths, filename, fs); if (filepath.empty() || filename.empty()) { - if (warn) { - (*warn) += "File not found : " + filename + "\n"; + if (failMsgOut) { + (*failMsgOut) += "File not found : " + filename + "\n"; } return false; } @@ -1263,15 +1280,17 @@ static bool LoadExternalFile(std::vector *out, std::string *err, bool fileRead = fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data); if (!fileRead) { - if (err) { - (*err) += "File read error : " + filepath + " : " + fileReadErr + "\n"; + if (failMsgOut) { + (*failMsgOut) += "File read error : " + filepath + " : " + fileReadErr + "\n"; } return false; } size_t sz = buf.size(); if (sz == 0) { - (*err) += "File is empty : " + filepath + "\n"; + if(failMsgOut) { + (*failMsgOut) += "File is empty : " + filepath + "\n"; + } return false; } @@ -1283,8 +1302,8 @@ static bool LoadExternalFile(std::vector *out, std::string *err, std::stringstream ss; ss << "File size mismatch : " << filepath << ", requestedBytes " << reqBytes << ", but got " << sz << std::endl; - if (err) { - (*err) += ss.str(); + if (failMsgOut) { + (*failMsgOut) += ss.str(); } return false; } @@ -1305,14 +1324,19 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn, int size, void *) { (void)warn; - int w, h, comp; + int w, h, comp, req_comp; + + // force 32-bit textures for common Vulkan compatibility. It appears that + // some GPU drivers do not support 24-bit images for Vulkan + req_comp = 4; + // 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 // image->uri references // an image file, it should be left as it is. Image loading should not be // mandatory (to support other formats) - unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, 0); + unsigned char *data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); if (!data) { // NOTE: you can use `warn` instead of `err` if (err) { @@ -1351,9 +1375,9 @@ bool LoadImageData(Image *image, std::string *err, std::string *warn, image->width = w; image->height = h; - image->component = comp; - image->image.resize(static_cast(w * h * comp)); - std::copy(data, data + w * h * comp, image->image.begin()); + image->component = req_comp; + image->image.resize(static_cast(w * h * req_comp)); + std::copy(data, data + w * h * req_comp, image->image.begin()); free(data); @@ -1615,7 +1639,7 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index, } } -static bool IsDataURI(const std::string &in) { +bool IsDataURI(const std::string &in) { std::string header = "data:application/octet-stream;base64,"; if (in.find(header) == 0) { return true; @@ -1654,7 +1678,7 @@ static bool IsDataURI(const std::string &in) { return false; } -static bool DecodeDataURI(std::vector *out, +bool DecodeDataURI(std::vector *out, std::string &mime_type, const std::string &in, size_t reqBytes, bool checkSize) { std::string header = "data:application/octet-stream;base64,"; @@ -2117,6 +2141,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn, } ParseStringProperty(&image->name, err, o, "name", false); + ParseExtensionsProperty(&image->extensions, err, o); if (hasBufferView) { double bufferView = -1; @@ -2173,7 +2198,7 @@ static bool ParseImage(Image *image, std::string *err, std::string *warn, #ifdef TINYGLTF_NO_EXTERNAL_IMAGE return true; #endif - if (!LoadExternalFile(&img, err, warn, uri, basedir, 0, false, fs)) { + if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) { if (warn) { (*warn) += "Failed to load external 'uri' for image parameter\n"; } @@ -2268,7 +2293,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } else { // External .bin file. if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri, - basedir, bytes, true, fs)) { + basedir, true, bytes, true, fs)) { return false; } } @@ -2310,7 +2335,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } else { // Assume external .bin file. if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri, - basedir, bytes, true, fs)) { + basedir, true, bytes, true, fs)) { return false; } } @@ -4005,6 +4030,8 @@ static void SerializeGltfImage(Image &image, json &o) { if (image.extras.Type() != NULL_TYPE) { SerializeValue("extras", image.extras, o); } + + SerializeExtensionMap(image.extensions, o); } static void SerializeGltfMaterial(Material &material, json &o) {