diff --git a/tiny_gltf.h b/tiny_gltf.h index 3d18c48..f897668 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -134,6 +135,9 @@ namespace tinygltf { #define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633) #define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632) +#define TINYGLTF_DOUBLE_EPS (1.e-12) +#define TINYGLTF_DOUBLE_EQUAL(a,b) (abs((b)-(a)); struct Parameter { - bool bool_value; + bool bool_value = false; bool has_number_value = false; std::string string_value; std::vector number_array; std::map json_double_value; - double number_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 @@ -374,6 +380,8 @@ struct Parameter { number_array[0], number_array[1], number_array[2], (number_array.size() > 3 ? number_array[3] : 1.0)}}; } + + bool operator==(const Parameter&) const; }; #ifdef __clang__ @@ -396,6 +404,7 @@ struct AnimationChannel { Value extras; AnimationChannel() : sampler(-1), target_node(-1) {} + bool operator==(const AnimationChannel&) const; }; struct AnimationSampler { @@ -406,6 +415,7 @@ struct AnimationSampler { Value extras; AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {} + bool operator==(const AnimationSampler&) const; }; struct Animation { @@ -413,6 +423,8 @@ struct Animation { std::vector channels; std::vector samplers; Value extras; + + bool operator==(const Animation&) const; }; struct Skin { @@ -425,6 +437,7 @@ struct Skin { inverseBindMatrices = -1; skeleton = -1; } + bool operator==(const Skin&) const; }; struct Sampler { @@ -443,6 +456,7 @@ struct Sampler { Sampler() : wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT), wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {} + bool operator==(const Sampler&) const; }; struct Image { @@ -457,7 +471,8 @@ struct Image { std::string uri; // (required if no mimeType) Value extras; - Image() { bufferView = -1; } + Image() { bufferView = -1; width = -1; height = -1; component = -1; } + bool operator==(const Image&) const; }; struct Texture { @@ -469,6 +484,7 @@ struct Texture { ExtensionMap extensions; Texture() : sampler(-1), source(-1) {} + bool operator==(const Texture&) const; }; // Each extension should be stored in a ParameterMap. @@ -482,6 +498,8 @@ struct Material { ExtensionMap extensions; Value extras; + + bool operator==(const Material&) const; }; struct BufferView { @@ -495,6 +513,7 @@ struct BufferView { Value extras; BufferView() : byteOffset(0), byteStride(0) {} + bool operator==(const BufferView&) const; }; struct Accessor { @@ -551,13 +570,14 @@ struct Accessor { } Accessor() { bufferView = -1; } + bool operator==(const tinygltf::Accessor&) const; }; struct PerspectiveCamera { - float aspectRatio; // min > 0 - float yfov; // required. min > 0 - float zfar; // min > 0 - float znear; // required. min > 0 + double aspectRatio; // min > 0 + double yfov; // required. min > 0 + double zfar; // min > 0 + double znear; // required. min > 0 PerspectiveCamera() : aspectRatio(0.0f), @@ -565,18 +585,20 @@ struct PerspectiveCamera { zfar(0.0f) // 0 = use infinite projecton matrix , znear(0.0f) {} + bool operator==(const PerspectiveCamera&) const; ExtensionMap extensions; Value extras; }; struct OrthographicCamera { - float xmag; // required. must not be zero. - float ymag; // required. must not be zero. - float zfar; // required. `zfar` must be greater than `znear`. - float znear; // required + double xmag; // required. must not be zero. + double ymag; // required. must not be zero. + double zfar; // required. `zfar` must be greater than `znear`. + double znear; // required OrthographicCamera() : xmag(0.0f), ymag(0.0f), zfar(0.0f), znear(0.0f) {} + bool operator==(const OrthographicCamera&) const; ExtensionMap extensions; Value extras; @@ -590,6 +612,7 @@ struct Camera { OrthographicCamera orthographic; Camera() {} + bool operator==(const Camera&) const; ExtensionMap extensions; Value extras; @@ -614,6 +637,7 @@ struct Primitive { material = -1; indices = -1; } + bool operator==(const Primitive&) const; }; struct Mesh { @@ -623,6 +647,8 @@ struct Mesh { std::vector> targets; ExtensionMap extensions; Value extras; + + bool operator==(const Mesh&) const; }; class Node { @@ -645,8 +671,8 @@ class Node { extensions = rhs.extensions; extras = rhs.extras; } - ~Node() {} + bool operator==(const Node&) const; int camera; // the index of the camera referenced by this node @@ -670,6 +696,8 @@ struct Buffer { std::string uri; // considered as required here but not in the spec (need to clarify) Value extras; + + bool operator==(const Buffer&) const; }; struct Asset { @@ -679,6 +707,8 @@ struct Asset { std::string copyright; ExtensionMap extensions; Value extras; + + bool operator==(const Asset&) const; }; struct Scene { @@ -687,18 +717,23 @@ struct Scene { ExtensionMap extensions; Value extras; + + bool operator==(const Scene&) const; }; struct Light { std::string name; std::vector color; std::string type; + + bool operator==(const Light&) const; }; class Model { public: Model() {} ~Model() {} + bool operator==(const Model&) const; std::vector accessors; std::vector animations; @@ -944,7 +979,7 @@ class TinyGLTF { #endif // TINY_GLTF_H_ -#ifdef TINYGLTF_IMPLEMENTATION +#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__) #include //#include #ifndef TINYGLTF_NO_FS @@ -1035,6 +1070,311 @@ using nlohmann::json; namespace tinygltf { +// Equals function for Value, for recursivity +bool Equals(const tinygltf::Value& one, const tinygltf::Value& other) +{ + if (one.Type() != other.Type()) + return false; + + switch (one.Type()) + { + case NULL_TYPE: + return true; + case BOOL_TYPE: + return one.Get() == other.Get(); + case NUMBER_TYPE: + return one.Get() == other.Get(); + case INT_TYPE: + return one.Get() == other.Get(); + case OBJECT_TYPE: + { + auto oneObj = one.Get(); + auto otherObj = other.Get(); + if (oneObj.size() != otherObj.size()) + return false; + for (auto& it : oneObj) + { + auto otherIt = otherObj.find(it.first); + if (otherIt == otherObj.end()) + return false; + + if (!Equals(it.second, otherIt->second)) + return false; + } + return true; + } + case ARRAY_TYPE: + { + if (one.Size() != other.Size()) + return false; + for (int i = 0; i < one.Size(); ++i) + if (Equals(one.Get(i), other.Get(i))) + return false; + return true; + } + case STRING_TYPE: + return one.Get() == other.Get(); + case BINARY_TYPE: + return one.Get>() == other.Get>(); + default: + { + // unhandled type + return false; + } + } + + return false; +} + +// Equals function for std::vector using TINYGLTF_DOUBLE_EPSILON +bool Equals(const std::vector& one, const std::vector& other) +{ + if (one.size() != other.size()) + return false; + for (int i = 0; i < one.size(); ++i) + { + if (!TINYGLTF_DOUBLE_EQUAL(one[i], other[i])) + return false; + } + return true; +} + +bool Accessor::operator==(const Accessor& other) const +{ + return this->bufferView == other.bufferView + && this->byteOffset == other.byteOffset + && this->componentType == other.componentType + && this->count == other.count + && this->extras == other.extras + && Equals(this->maxValues, other.maxValues) + && Equals(this->minValues,other.minValues) + && this->name == other.name + && this->normalized == other.normalized + && this->type == other.type; +} +bool Animation::operator==(const Animation& other) const +{ + return this->channels == other.channels + && this->extras == other.extras + && this->name == other.name + && this->samplers == other.samplers; +} +bool AnimationChannel::operator==(const AnimationChannel& other) const +{ + return this->extras == other.extras + && this->target_node == other.target_node + && this->target_path == other.target_path + && this->sampler == other.sampler; +} +bool AnimationSampler::operator==(const AnimationSampler& other) const +{ + return this->extras == other.extras + && this->input == other.input + && this->interpolation == other.interpolation + && this->output == other.output; +} +bool Asset::operator==(const Asset& other) const +{ + return this->copyright == other.copyright + && this->extensions == other.extensions + && this->extras == other.extras + && this->generator == other.generator + && this->minVersion == other.minVersion + && this->version == other.version; +} +bool Buffer::operator==(const Buffer& other) const +{ + return this->data == other.data + && this->extras == other.extras + && this->name == other.name + && this->uri == other.uri; +} +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; +} +bool Camera::operator==(const Camera& other) const +{ + return this->name == other.name + && this->extensions == other.extensions + && this->extras == other.extras + && this->orthographic == other.orthographic + && this->perspective == other.perspective + && this->type == other.type; +} +bool Image::operator==(const Image& other) const +{ + return this->bufferView == other.bufferView + && this->component == other.component + && this->extras == other.extras + && this->height == other.height + && this->image == other.image + && this->mimeType == other.mimeType + && this->name == other.name + && this->uri == other.uri + && this->width == other.width; +} +bool Light::operator==(const Light& other) const +{ + return Equals(this->color, other.color) + && this->name == other.name + && this->type == other.type; +} +bool Material::operator==(const Material& other) const +{ + return this->additionalValues == other.additionalValues + && this->extensions == other.extensions + && this->extras == other.extras + && this->name == other.name + && this->values == other.values; +} +bool Mesh::operator==(const Mesh& other) const +{ + return this->extensions == other.extensions + && this->extras == other.extras + && this->name == other.name + && this->primitives == other.primitives + && this->targets == other.targets + && Equals(this->weights, other.weights); +} +bool Model::operator==(const Model& other) const +{ + return this->accessors == other.accessors + && this->animations == other.animations + && this->asset == other.asset + && this->buffers == other.buffers + && this->bufferViews == other.bufferViews + && this->cameras == other.cameras + && this->defaultScene == other.defaultScene + && this->extensions == other.extensions + && this->extensionsRequired == other.extensionsRequired + && this->extensionsUsed == other.extensionsUsed + && this->extras == other.extras + && this->images == other.images + && this->lights == other.lights + && this->materials == other.materials + && this->meshes == other.meshes + && this->nodes == other.nodes + && this->samplers == other.samplers + && this->scenes == other.scenes + && this->skins == other.skins + && this->textures == other.textures; +} +bool Node::operator==(const Node& other) const +{ + return this->camera == other.camera + && this->children == other.children + && this->extensions == other.extensions + && this->extras == other.extras + && Equals(this->matrix, other.matrix) + && this->mesh == other.mesh + && this->name == other.name + && Equals(this->rotation, other.rotation) + && Equals(this->scale, other.scale) + && this->skin == other.skin + && Equals(this->translation, other.translation) + && Equals(this->weights, other.weights); +} +bool OrthographicCamera::operator==(const OrthographicCamera& other) const +{ + return this->extensions == other.extensions + && this->extras == other.extras + && TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) + && TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) + && TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) + && TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); +} +bool Parameter::operator==(const Parameter& other) const +{ + if (this->bool_value != other.bool_value + || this->has_number_value != other.has_number_value) + return false; + + if (this->has_number_value) + if(!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value)) + return false; + + if (this->json_double_value.size() != other.json_double_value.size()) + return false; + for (auto& it : this->json_double_value) + { + auto otherIt = other.json_double_value.find(it.first); + if (otherIt==other.json_double_value.end()) + return false; + + if(!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) + return false; + } + + if (!Equals(this->number_array, other.number_array)) + return false; + + if (this->string_value != other.string_value) + return false; + + return true; +} +bool PerspectiveCamera::operator==(const PerspectiveCamera& other) const +{ + return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) + && this->extensions == other.extensions + && this->extras == other.extras + && TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) + && TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) + && TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); +} +bool Primitive::operator==(const Primitive& other) const +{ + return this->attributes == other.attributes + && this->extras == other.extras + && this->indices == other.indices + && this->material == other.material + && this->mode == other.mode + && this->targets == other.targets; +} +bool Sampler::operator==(const Sampler& other) const +{ + return this->extras == other.extras + && this->magFilter == other.magFilter + && this->minFilter == other.minFilter + && this->name == other.name + && this->wrapR == other.wrapR + && this->wrapS == other.wrapS + && this->wrapT == other.wrapT; +} +bool Scene::operator==(const Scene& other) const +{ + return this->extensions == other.extensions + && this->extras == other.extras + && this->name == other.name + && this->nodes == other.nodes;; +} +bool Skin::operator==(const Skin& other) const +{ + return this->inverseBindMatrices == other.inverseBindMatrices + && this->joints == other.joints + && this->name == other.name + && this->skeleton == other.skeleton; +} +bool Texture::operator==(const Texture& other) const +{ + return this->extensions == other.extensions + && this->extras == other.extras + && this->name == other.name + && this->sampler == other.sampler + && this->source == other.source; +} +bool Value::operator==(const Value& other) const +{ + return Equals(*this, other); +} + static void swap4(unsigned int *val) { #ifdef TINYGLTF_LITTLE_ENDIAN (void)val; @@ -2821,10 +3161,10 @@ static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err, double zfar = 0.0; // = invalid ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera"); - camera->aspectRatio = float(aspectRatio); - camera->zfar = float(zfar); - camera->yfov = float(yfov); - camera->znear = float(znear); + camera->aspectRatio = aspectRatio; + camera->zfar = zfar; + camera->yfov = yfov; + camera->znear = znear; ParseExtensionsProperty(&camera->extensions, err, o); ParseExtrasProperty(&(camera->extras), o); @@ -2860,10 +3200,10 @@ static bool ParseOrthographicCamera(OrthographicCamera *camera, ParseExtensionsProperty(&camera->extensions, err, o); ParseExtrasProperty(&(camera->extras), o); - camera->xmag = float(xmag); - camera->ymag = float(ymag); - camera->zfar = float(zfar); - camera->znear = float(znear); + camera->xmag = xmag; + camera->ymag = ymag; + camera->zfar = zfar; + camera->znear = znear; // TODO(syoyo): Validate parameter values. @@ -3873,8 +4213,9 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) { type = "MAT4"; break; } - + SerializeStringProperty("type", type, o); + if(!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o); if (accessor.extras.Type() != NULL_TYPE) { SerializeValue("extras", accessor.extras, o); @@ -3905,7 +4246,7 @@ static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) { } static void SerializeGltfAnimation(Animation &animation, json &o) { - SerializeStringProperty("name", animation.name, o); + if(!animation.name.empty()) SerializeStringProperty("name", animation.name, o); json channels; for (unsigned int i = 0; i < animation.channels.size(); ++i) { json channel; @@ -4094,7 +4435,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) { } static void SerializeGltfLight(Light &light, json &o) { - SerializeStringProperty("name", light.name, o); + if(!light.name.empty()) SerializeStringProperty("name", light.name, o); SerializeNumberArrayProperty("color", light.color, o); SerializeStringProperty("type", light.type, o); } @@ -4129,7 +4470,7 @@ static void SerializeGltfNode(Node &node, json &o) { } SerializeExtensionMap(node.extensions, o); - SerializeStringProperty("name", node.name, o); + if(!node.name.empty()) SerializeStringProperty("name", node.name, o); SerializeNumberArrayProperty("children", node.children, o); } @@ -4227,10 +4568,12 @@ static void SerializeGltfTexture(Texture &texture, json &o) { SerializeExtensionMap(texture.extensions, o); } -static void WriteGltfFile(const std::string &output, +static bool WriteGltfFile(const std::string &output, const std::string &content) { std::ofstream gltfFile(output.c_str()); + if(!gltfFile.is_open()) return false; gltfFile << content << std::endl; + return true; } bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, @@ -4453,8 +4796,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, SerializeValue("extras", model->extras, output); } - WriteGltfFile(filename, output.dump()); - return true; + return WriteGltfFile(filename, output.dump()); } } // namespace tinygltf