diff --git a/loader_example.cc b/loader_example.cc index c0eec95..93a6866 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -175,7 +175,10 @@ static std::string PrintIntArray(const std::vector &arr) { std::stringstream ss; ss << "[ "; for (size_t i = 0; i < arr.size(); i++) { - ss << arr[i] << ((i != arr.size() - 1) ? ", " : ""); + ss << arr[i]; + if (i != arr.size() - 1) { + ss << ", "; + } } ss << " ]"; @@ -190,7 +193,10 @@ static std::string PrintFloatArray(const std::vector &arr) { std::stringstream ss; ss << "[ "; for (size_t i = 0; i < arr.size(); i++) { - ss << arr[i] << ((i != arr.size() - 1) ? ", " : ""); + ss << arr[i]; + if (i != arr.size() - 1) { + ss << ", "; + } } ss << " ]"; @@ -243,35 +249,36 @@ static std::string PrintValue(const std::string &name, if (tag) { ss << Indent(indent) << name << " : " << value.Get(); } else { - ss << " " << value.Get() << " "; + ss << Indent(indent) << value.Get() << " "; } } else if (value.IsBool()) { if (tag) { ss << Indent(indent) << name << " : " << value.Get(); } else { - ss << " " << value.Get() << " "; + ss << Indent(indent) << value.Get() << " "; } } else if (value.IsNumber()) { if (tag) { ss << Indent(indent) << name << " : " << value.Get(); } else { - ss << " " << value.Get() << " "; + ss << Indent(indent) << value.Get() << " "; } } else if (value.IsInt()) { if (tag) { ss << Indent(indent) << name << " : " << value.Get(); } else { - ss << " " << value.Get() << " "; + ss << Indent(indent) << value.Get() << " "; } } else if (value.IsArray()) { - ss << Indent(indent) << name << " [ "; + // TODO(syoyo): Better pretty printing of array item + ss << Indent(indent) << name << " [ \n"; for (size_t i = 0; i < value.Size(); i++) { ss << PrintValue("", value.Get(int(i)), indent + 1, /* tag */ false); if (i != (value.ArrayLen() - 1)) { - ss << ", "; + ss << ", \n"; } } - ss << Indent(indent) << "] "; + ss << "\n" << Indent(indent) << "] "; } // @todo { binary } @@ -339,6 +346,54 @@ static void DumpExtensions(const tinygltf::ExtensionMap &extension, } } +static void DumpTextureInfo(const tinygltf::TextureInfo &texinfo, + const int indent) { + std::cout << Indent(indent) << "index : " << texinfo.index << "\n"; + std::cout << Indent(indent) << "texCoord : TEXCOORD_" << texinfo.texCoord + << "\n"; + DumpExtensions(texinfo.extensions, indent + 1); + std::cout << PrintValue("extras", texinfo.extras, indent + 1) << "\n"; +} + +static void DumpNormalTextureInfo(const tinygltf::NormalTextureInfo &texinfo, + const int indent) { + std::cout << Indent(indent) << "index : " << texinfo.index << "\n"; + std::cout << Indent(indent) << "texCoord : TEXCOORD_" << texinfo.texCoord + << "\n"; + std::cout << Indent(indent) << "scale : " << texinfo.scale << "\n"; + DumpExtensions(texinfo.extensions, indent + 1); + std::cout << PrintValue("extras", texinfo.extras, indent + 1) << "\n"; +} + +static void DumpOcclusionTextureInfo( + const tinygltf::OcclusionTextureInfo &texinfo, const int indent) { + std::cout << Indent(indent) << "index : " << texinfo.index << "\n"; + std::cout << Indent(indent) << "texCoord : TEXCOORD_" << texinfo.texCoord + << "\n"; + std::cout << Indent(indent) << "strength : " << texinfo.strength << "\n"; + DumpExtensions(texinfo.extensions, indent + 1); + std::cout << PrintValue("extras", texinfo.extras, indent + 1) << "\n"; +} + +static void DumpPbrMetallicRoughness(const tinygltf::PbrMetallicRoughness &pbr, + const int indent) { + std::cout << Indent(indent) + << "baseColorFactor : " << PrintFloatArray(pbr.baseColorFactor) + << "\n"; + std::cout << Indent(indent) << "baseColorTexture :\n"; + DumpTextureInfo(pbr.baseColorTexture, indent + 1); + + std::cout << Indent(indent) << "metallicFactor : " << pbr.metallicFactor + << "\n"; + std::cout << Indent(indent) << "roughnessFactor : " << pbr.roughnessFactor + << "\n"; + + std::cout << Indent(indent) << "metallicRoughnessTexture :\n"; + DumpTextureInfo(pbr.metallicRoughnessTexture, indent + 1); + DumpExtensions(pbr.extensions, indent + 1); + std::cout << PrintValue("extras", pbr.extras, indent + 1) << "\n"; +} + static void Dump(const tinygltf::Model &model) { std::cout << "=== Dump glTF ===" << std::endl; std::cout << "asset.copyright : " << model.asset.copyright @@ -509,16 +564,44 @@ static void Dump(const tinygltf::Model &model) { << std::endl; for (size_t i = 0; i < model.materials.size(); i++) { const tinygltf::Material &material = model.materials[i]; - std::cout << Indent(1) << "name : " << material.name << std::endl; - std::cout << Indent(1) << "values(items=" << material.values.size() << ")" + std::cout << Indent(1) << "name : " << material.name << std::endl; + std::cout << Indent(1) << "alphaMode : " << material.alphaMode + << std::endl; + std::cout << Indent(1) + << "alphaCutoff : " << material.alphaCutoff + << std::endl; + std::cout << Indent(1) << "doubleSided : " + << (material.doubleSided ? "true" : "false") << std::endl; + std::cout << Indent(1) << "emissiveFactor : " + << PrintFloatArray(material.emissiveFactor) << std::endl; + + std::cout << Indent(1) << "pbrMetallicRoughness :\n"; + DumpPbrMetallicRoughness(material.pbrMetallicRoughness, 2); + + std::cout << Indent(1) << "normalTexture :\n"; + DumpNormalTextureInfo(material.normalTexture, 2); + + std::cout << Indent(1) << "occlusionTexture :\n"; + DumpOcclusionTextureInfo(material.occlusionTexture, 2); + + std::cout << Indent(1) << "emissiveTexture :\n"; + DumpTextureInfo(material.emissiveTexture, 2); + + std::cout << Indent(1) << "---- legacy material parameter ----\n"; + std::cout << Indent(1) << "values(items=" << material.values.size() << ")" + << std::endl; tinygltf::ParameterMap::const_iterator p(material.values.begin()); tinygltf::ParameterMap::const_iterator pEnd(material.values.end()); for (; p != pEnd; p++) { std::cout << Indent(2) << p->first << ": " << PrintParameterValue(p->second) << std::endl; } + std::cout << Indent(1) << "-------------------------------------\n"; + + DumpExtensions(material.extensions, 1); + std::cout << PrintValue("extras", material.extras, 2) << std::endl; } } diff --git a/models/Cube-texture-ext/Cube-textransform.gltf b/models/Cube-texture-ext/Cube-textransform.gltf new file mode 100644 index 0000000..12c8130 --- /dev/null +++ b/models/Cube-texture-ext/Cube-textransform.gltf @@ -0,0 +1,224 @@ +{ + "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, + 1, + 1.000001 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 36, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 36, + "max": [ + 1, + -0, + -0, + 1 + ], + "min": [ + 0, + -0, + -1, + -1 + ], + "type": "VEC4" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5126, + "count": 36, + "max": [ + 1, + 1 + ], + "min": [ + -1, + -1 + ], + "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": [ + { + "0comment": "Use Cube_MetallicRoughness.png to reduce scene filesize", + "uri": "Cube_MetallicRoughness.png" + }, + { + "uri": "Cube_MetallicRoughness.png" + } + ], + "materials": [ + { + "emissiveTexture": { + "index": 0, + "extensions": { + "KHR_texture_transform": { + "offset": [ + 0, + 1 + ], + "scale": [ + 1, + -1 + ] + } + } + } + }, + { + "name": "Cube", + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicRoughnessTexture": { + "index": 1, + "extensions": { + "KHR_texture_transform": { + "offset": [ + 0, + 1 + ], + "rotation": 1.57079632679, + "scale": [ + 0.5, + 0.5 + ] + } + } + } + } + } + ], + "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-texture-ext/Cube.bin b/models/Cube-texture-ext/Cube.bin new file mode 100644 index 0000000..7dae4b0 Binary files /dev/null and b/models/Cube-texture-ext/Cube.bin differ diff --git a/models/Cube-texture-ext/Cube_MetallicRoughness.png b/models/Cube-texture-ext/Cube_MetallicRoughness.png new file mode 100644 index 0000000..efd2026 Binary files /dev/null and b/models/Cube-texture-ext/Cube_MetallicRoughness.png differ diff --git a/models/Cube-texture-ext/README.md b/models/Cube-texture-ext/README.md new file mode 100644 index 0000000..796671b --- /dev/null +++ b/models/Cube-texture-ext/README.md @@ -0,0 +1,6 @@ +Added KHR_texture_transform property to Cube scene. + +License: Donated by Norbert Nopper for glTF testing. + +https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Cube + diff --git a/tests/tester.cc b/tests/tester.cc index f3e96dd..2fcc21d 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -279,3 +279,37 @@ TEST_CASE("parse-integer-array", "[bounds-checking]") { REQUIRE_THAT(err, Catch::Contains("not an integer type")); } } + +TEST_CASE("pbr-khr-texture-transform", "[material]") { + tinygltf::Model model; + tinygltf::TinyGLTF ctx; + std::string err; + std::string warn; + + // Loading is expected to fail, but not crash. + bool ret = ctx.LoadASCIIFromFile( + &model, &err, &warn, + "../models/Cube-texture-ext/Cube-textransform.gltf"); + REQUIRE(ret == true); + + REQUIRE(model.materials.size() == 2); + REQUIRE(model.materials[0].emissiveTexture.extensions.count("KHR_texture_transform") == 1); + REQUIRE(model.materials[0].emissiveTexture.extensions["KHR_texture_transform"].IsObject()); + + tinygltf::Value::Object &texform = model.materials[0].emissiveTexture.extensions["KHR_texture_transform"].Get(); + + REQUIRE(texform.count("scale")); + + REQUIRE(texform["scale"].IsArray()); + std::cout << "ty " << int(texform["scale"].Get(0).Type()) << "\n"; + REQUIRE(texform["scale"].Get(0).IsNumberOrInt()); + REQUIRE(texform["scale"].Get(1).IsNumberOrInt()); + + double scale[2]; + scale[0] = texform["scale"].Get(0).GetAsFloat(); + scale[1] = texform["scale"].Get(1).GetAsFloat(); + + REQUIRE(scale[0] == Approx(1.0)); + REQUIRE(scale[1] == Approx(-1.0)); + +} diff --git a/tiny_gltf.h b/tiny_gltf.h index 3d85db7..64941cb 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -26,6 +26,8 @@ // THE SOFTWARE. // Version: +// - v2.3.0 Modified Material representation according to glTF 2.0 schema +// (and introduced TextureInfo class) // - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks // to @Ybalrid) // - v2.1.0 Add draco compression. @@ -46,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -228,7 +231,11 @@ class Value { typedef std::vector Array; typedef std::map Object; - Value() : type_(NULL_TYPE) {} + Value() + : type_(NULL_TYPE), + int_value_(0), + number_value_(0.0), + boolean_value_(false) {} explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; } explicit Value(int i) : type_(INT_TYPE) { int_value_ = i; } @@ -253,8 +260,14 @@ class Value { bool IsInt() const { return (type_ == INT_TYPE); } + // for backward compatibility, we use `IsNumber` for floating point value + // only. bool IsNumber() const { return (type_ == NUMBER_TYPE); } + bool IsNumberOrInt() const { + return (type_ == INT_TYPE) || (type_ == NUMBER_TYPE); + } + bool IsString() const { return (type_ == STRING_TYPE); } bool IsBinary() const { return (type_ == BINARY_TYPE); } @@ -263,6 +276,17 @@ class Value { bool IsObject() const { return (type_ == OBJECT_TYPE); } + double GetAsFloat() const { + if (type_ == INT_TYPE) { + return double(int_value_); + } else if (type_ == NUMBER_TYPE) { + return number_value_; + } + + // TODO(syoyo): Raise error? + return std::numeric_limits::quiet_NaN(); + } + // Accessor template const T &Get() const; @@ -317,15 +341,15 @@ class Value { bool operator==(const tinygltf::Value &other) const; protected: - int type_; + int type_ = NULL_TYPE; - int int_value_; - double number_value_; + int int_value_ = 0; + double number_value_ = 0.0; std::string string_value_; std::vector binary_value_; Array array_value_; Object object_value_; - bool boolean_value_; + bool boolean_value_ = false; }; #ifdef __clang__ @@ -359,6 +383,8 @@ TINYGLTF_VALUE_GET(Value::Object, object_value_) /// Agregate object for representing a color using ColorValue = std::array; +// === legacy interface ==== +// TODO(syoyo): Deprecate `Parameter` class. struct Parameter { bool bool_value = false; bool has_number_value = false; @@ -366,6 +392,7 @@ struct Parameter { std::vector number_array; std::map json_double_value; double number_value = 0.0; + // context sensitive methods. depending the type of the Parameter you are // accessing, these are either valid or not // If this parameter represent a texture map in a material, will return the @@ -560,9 +587,9 @@ struct Texture { }; struct TextureInfo { - int index; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. + int index = -1; // required. + int texCoord; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. Value extras; ExtensionMap extensions; @@ -572,11 +599,11 @@ struct TextureInfo { }; struct NormalTextureInfo { - int index; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. - double scale; // scaledNormal = normalize(( - // * 2.0 - 1.0) * vec3(, , 1.0)) + int index = -1; // required + int texCoord; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. + double scale; // scaledNormal = normalize(( + // * 2.0 - 1.0) * vec3(, , 1.0)) Value extras; ExtensionMap extensions; @@ -586,7 +613,7 @@ struct NormalTextureInfo { }; struct OcclusionTextureInfo { - int index; // required + int index = -1; // required int texCoord; // The set index of texture's TEXCOORD attribute used for // texture coordinate mapping. double strength; // occludedColor = lerp(color, color * baseColorFactor; // len = 4. default [1,1,1,1] TextureInfo baseColorTexture; double metallicFactor; // default 1 double roughnessFactor; // default 1 @@ -610,12 +637,7 @@ struct PbrMetallicRoughness { Value extras; ExtensionMap extensions; - PbrMetallicRoughness() : metallicFactor(1.0), roughnessFactor(1.0) { - baseColorFactor[0] = 1.0; - baseColorFactor[1] = 1.0; - baseColorFactor[2] = 1.0; - baseColorFactor[3] = 1.0; - } + PbrMetallicRoughness() : metallicFactor(1.0), roughnessFactor(1.0) {} bool operator==(const PbrMetallicRoughness &) const; }; @@ -626,9 +648,9 @@ struct Material { std::string name; std::vector emissiveFactor; // length 3. default [0, 0, 0] - std::string alphaMode; // default "OPAQUE" - double alphaCutoff; // default 0.5 - bool doubleSided; // default false; + std::string alphaMode; // default "OPAQUE" + double alphaCutoff; // default 0.5 + bool doubleSided; // default false; PbrMetallicRoughness pbrMetallicRoughness; @@ -636,14 +658,15 @@ struct Material { OcclusionTextureInfo occlusionTexture; TextureInfo emissiveTexture; - // ParameterMap values; - // ParameterMap additionalValues; + // For backward compatibility + // TODO(syoyo): Remove `values` and `additionalValues` in the next release. + ParameterMap values; + ParameterMap additionalValues; ExtensionMap extensions; Value extras; - Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) { - } + Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {} bool operator==(const Material &) const; }; @@ -1435,17 +1458,14 @@ bool Material::operator==(const Material &other) const { (this->normalTexture == other.normalTexture) && (this->occlusionTexture == other.occlusionTexture) && (this->emissiveTexture == other.emissiveTexture) && - TINYGLTF_DOUBLE_EQUAL(this->emissiveFactor[0], - other.emissiveFactor[0]) && - TINYGLTF_DOUBLE_EQUAL(this->emissiveFactor[1], - other.emissiveFactor[1]) && - TINYGLTF_DOUBLE_EQUAL(this->emissiveFactor[2], - other.emissiveFactor[2]) && + Equals(this->emissiveFactor, other.emissiveFactor) && (this->alphaMode == other.alphaMode) && (this->alphaCutoff == other.alphaCutoff) && (this->doubleSided == other.doubleSided) && (this->extensions == other.extensions) && - (this->extras == other.extras) && this->name == other.name; + (this->extras == other.extras) && (this->values == other.values) && + (this->additionalValues == other.additionalValues) && + (this->name == other.name); } bool Mesh::operator==(const Mesh &other) const { return this->extensions == other.extensions && this->extras == other.extras && @@ -1562,14 +1582,7 @@ bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const { return this->extensions == other.extensions && this->extras == other.extras && (this->baseColorTexture == other.baseColorTexture) && (this->metallicRoughnessTexture == other.metallicRoughnessTexture) && - TINYGLTF_DOUBLE_EQUAL(this->baseColorFactor[0], - other.baseColorFactor[0]) && - TINYGLTF_DOUBLE_EQUAL(this->baseColorFactor[1], - other.baseColorFactor[1]) && - TINYGLTF_DOUBLE_EQUAL(this->baseColorFactor[2], - other.baseColorFactor[2]) && - TINYGLTF_DOUBLE_EQUAL(this->baseColorFactor[3], - other.baseColorFactor[3]) && + Equals(this->baseColorFactor, other.baseColorFactor) && TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) && TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor); } @@ -2999,6 +3012,65 @@ static bool ParseTexture(Texture *texture, std::string *err, const json &o, return true; } +static bool ParseTextureInfo(TextureInfo *texinfo, std::string *err, + const json &o) { + if (texinfo == nullptr) { + return false; + } + + if (!ParseIntegerProperty(&texinfo->index, err, o, "index", + /* required */ true, "TextureInfo")) { + return false; + } + + ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); + + ParseExtensionsProperty(&texinfo->extensions, err, o); + ParseExtrasProperty(&texinfo->extras, o); + + return true; +} + +static bool ParseNormalTextureInfo(NormalTextureInfo *texinfo, std::string *err, + const json &o) { + if (texinfo == nullptr) { + return false; + } + + if (!ParseIntegerProperty(&texinfo->index, err, o, "index", + /* required */ true, "NormalTextureInfo")) { + return false; + } + + ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); + ParseNumberProperty(&texinfo->scale, err, o, "scale", false); + + ParseExtensionsProperty(&texinfo->extensions, err, o); + ParseExtrasProperty(&texinfo->extras, o); + + return true; +} + +static bool ParseOcclusionTextureInfo(OcclusionTextureInfo *texinfo, + std::string *err, const json &o) { + if (texinfo == nullptr) { + return false; + } + + if (!ParseIntegerProperty(&texinfo->index, err, o, "index", + /* required */ true, "NormalTextureInfo")) { + return false; + } + + ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); + ParseNumberProperty(&texinfo->strength, err, o, "strength", false); + + ParseExtensionsProperty(&texinfo->extensions, err, o); + ParseExtrasProperty(&texinfo->extras, o); + + return true; +} + static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, FsCallbacks *fs, const std::string &basedir, bool is_binary = false, @@ -3601,23 +3673,106 @@ static bool ParseNode(Node *node, std::string *err, const json &o) { return true; } +static bool ParsePbrMetallicRoughness(PbrMetallicRoughness *pbr, + std::string *err, const json &o) { + if (pbr == nullptr) { + return false; + } + + std::vector baseColorFactor; + if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor", + /* required */ false)) { + if (baseColorFactor.size() != 4) { + if (err) { + (*err) += + "Array length of `baseColorFactor` parameter in " + "pbrMetallicRoughness must be 4, but got " + + std::to_string(baseColorFactor.size()) + "\n"; + } + return false; + } + } else { + // fill with default values + baseColorFactor = {1.0, 1.0, 1.0, 1.0}; + } + pbr->baseColorFactor = baseColorFactor; + + { + json::const_iterator it = o.find("baseColorTexture"); + if (it != o.end()) { + ParseTextureInfo(&pbr->baseColorTexture, err, it.value()); + } + } + + { + json::const_iterator it = o.find("metallicRoughnessTexture"); + if (it != o.end()) { + ParseTextureInfo(&pbr->metallicRoughnessTexture, err, it.value()); + } + } + + ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false); + ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false); + + ParseExtensionsProperty(&pbr->extensions, err, o); + ParseExtrasProperty(&pbr->extras, o); + + return true; +} + static bool ParseMaterial(Material *material, std::string *err, const json &o) { - //material->values.clear(); - //material->additionalValues.clear(); - material->extensions.clear(); + ParseStringProperty(&material->name, err, o, "name", /* required */ false); + + ParseNumberArrayProperty(&material->emissiveFactor, err, o, "emissiveFactor", + /* required */ false); + + ParseStringProperty(&material->alphaMode, err, o, "alphaMode", + /* required */ false); + ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff", + /* required */ false); + ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided", + /* required */ false); + + { + json::const_iterator it = o.find("pbrMetallicRoughness"); + if (it != o.end()) { + ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err, + it.value()); + } + } + + { + json::const_iterator it = o.find("normalTexture"); + if (it != o.end()) { + ParseNormalTextureInfo(&material->normalTexture, err, it.value()); + } + } + + { + json::const_iterator it = o.find("occlusionTexture"); + if (it != o.end()) { + ParseOcclusionTextureInfo(&material->occlusionTexture, err, it.value()); + } + } + + { + json::const_iterator it = o.find("emissiveTexture"); + if (it != o.end()) { + ParseTextureInfo(&material->emissiveTexture, err, it.value()); + } + } + + // Old code path. For backward compatibility, we still store material values + // as Parameter. This will create duplicated information for + // example(pbrMetallicRoughness), but should be neglible in terms of memory + // consumption. + // TODO(syoyo): Remove in the next major release. + material->values.clear(); + material->additionalValues.clear(); json::const_iterator it(o.begin()); json::const_iterator itEnd(o.end()); - ParseStringProperty(&material->name, err, o, "name", /* required */false); - - ParseNumberArrayProperty(&material->emissiveFactor, err, o, "emissiveFactor", /* required */false); - - ParseStringProperty(&material->alphaMode, err, o, "alphaMode", /* required */false); - ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff", /* required */false); - ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided", /* required */false); - -#if 0 for (; it != itEnd; it++) { if (it.key() == "pbrMetallicRoughness") { if (it.value().is_object()) { @@ -3634,8 +3789,6 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o) { } } } - } else if (it.key() == "normalTexture") { - } else if (it.key() == "occlusionTexture") { } else if (it.key() == "extensions" || it.key() == "extras") { // done later, skip, otherwise poorly parsed contents will be saved in the // parametermap and serialized again later @@ -3648,8 +3801,8 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o) { } } } -#endif + material->extensions.clear(); ParseExtensionsProperty(&material->extensions, err, o); ParseExtrasProperty(&(material->extras), o); @@ -5155,10 +5308,146 @@ static void SerializeGltfImage(Image &image, json &o) { SerializeExtensionMap(image.extensions, o); } -static void SerializeGltfMaterial(Material &material, json &o) { - if (material.extras.Size()) SerializeValue("extras", material.extras, o); - SerializeExtensionMap(material.extensions, o); +static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) { + SerializeNumberProperty("index", texinfo.index, o); + if (texinfo.texCoord != 0) { + SerializeNumberProperty("texCoord", texinfo.texCoord, o); + } + + if (texinfo.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texinfo.extras, o); + } + + SerializeExtensionMap(texinfo.extensions, o); +} + +static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo, + json &o) { + SerializeNumberProperty("index", texinfo.index, o); + + if (texinfo.texCoord != 0) { + SerializeNumberProperty("texCoord", texinfo.texCoord, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 0.0)) { + SerializeNumberProperty("scale", texinfo.scale, o); + } + + if (texinfo.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texinfo.extras, o); + } + + SerializeExtensionMap(texinfo.extensions, o); +} + +static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo, + json &o) { + SerializeNumberProperty("index", texinfo.index, o); + + if (texinfo.texCoord != 0) { + SerializeNumberProperty("texCoord", texinfo.texCoord, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 0.0)) { + SerializeNumberProperty("strength", texinfo.strength, o); + } + + if (texinfo.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texinfo.extras, o); + } + + SerializeExtensionMap(texinfo.extensions, o); +} + +static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr, + json &o) { + std::vector default_baseColorFactor = {1.0, 1.0, 1.0, 1.0}; + if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) { + SerializeNumberArrayProperty("baseColorFactor", pbr.baseColorFactor, + o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 0.0)) { + SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 0.0)) { + SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o); + } + + if (pbr.baseColorTexture.index >= -1) { + json texinfo; + SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo); + o["baseColorTexture"] = texinfo; + } + + if (pbr.metallicRoughnessTexture.index >= -1) { + json texinfo; + SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo); + o["metallicRoughnessTexture"] = texinfo; + } + + SerializeExtensionMap(pbr.extensions, o); + + if (pbr.extras.Type() != NULL_TYPE) { + SerializeValue("extras", pbr.extras, o); + } +} + +static void SerializeGltfMaterial(Material &material, json &o) { + if (material.name.size()) { + SerializeStringProperty("name", material.name, o); + } + + // QUESTION(syoyo): Write material parameters regardless of its default value? + + if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) { + SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o); + } + + if (material.alphaMode.compare("OPAQUE") == 0) { + SerializeStringProperty("alphaMode", material.alphaMode, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) { + SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o); + } + + o["doubleSided"] = json(material.doubleSided); + + if (material.normalTexture.index >= -1) { + json texinfo; + SerializeGltfNormalTextureInfo(material.normalTexture, texinfo); + o["normalTexture"] = texinfo; + } + + if (material.occlusionTexture.index >= -1) { + json texinfo; + SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo); + o["occlusionTexture"] = texinfo; + } + + if (material.emissiveTexture.index >= -1) { + json texinfo; + SerializeGltfTextureInfo(material.emissiveTexture, texinfo); + o["emissiveTexture"] = texinfo; + } + + std::vector default_emissiveFactor = {0.0, 0.0, 0.0}; + if (!Equals(material.emissiveFactor, default_emissiveFactor)) { + SerializeNumberArrayProperty("emissiveFactor", + material.emissiveFactor, o); + } + + { + json pbrMetallicRoughness; + SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness, + pbrMetallicRoughness); + o["pbrMetallicRoughness"] = pbrMetallicRoughness; + } + +#if 0 // legacy way. just for the record. if (material.values.size()) { json pbrMetallicRoughness; SerializeParameterMap(material.values, pbrMetallicRoughness); @@ -5166,10 +5455,11 @@ static void SerializeGltfMaterial(Material &material, json &o) { } SerializeParameterMap(material.additionalValues, o); +#else - if (material.name.size()) { - SerializeStringProperty("name", material.name, o); - } +#endif + + SerializeExtensionMap(material.extensions, o); if (material.extras.Type() != NULL_TYPE) { SerializeValue("extras", material.extras, o);