// equality operator for tinygltf::Model and types contained by it

// double comparison (although perhaps not necessary, as json reads/writes bytes directly? unsure..)
// initialize some variables for operator==() reliabiltiy
// change some floats to doubles
// intellisense define
// serialize accessor.name
// dont serialize some names if they're empty
// return false if writing gltf file fails
This commit is contained in:
Selmar Kok 2018-10-03 15:39:05 +02:00
parent 1d0bd6c64f
commit 31cb7f92d8

View File

@ -41,6 +41,7 @@
#include <cassert>
#include <cstdint>
#include <cstring>
#include <cstdlib>
#include <map>
#include <string>
#include <vector>
@ -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))<TINYGLTF_DOUBLE_EPS)
typedef enum {
NULL_TYPE = 0,
NUMBER_TYPE = 1,
@ -294,6 +298,8 @@ class Value {
size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
bool operator==(const tinygltf::Value& other) const;
protected:
int type_;
@ -338,12 +344,12 @@ TINYGLTF_VALUE_GET(Value::Object, object_value_)
using ColorValue = std::array<double, 4>;
struct Parameter {
bool bool_value;
bool bool_value = false;
bool has_number_value = false;
std::string string_value;
std::vector<double> number_array;
std::map<std::string, double> 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<AnimationChannel> channels;
std::vector<AnimationSampler> 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<std::map<std::string, int>> 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<double> color;
std::string type;
bool operator==(const Light&) const;
};
class Model {
public:
Model() {}
~Model() {}
bool operator==(const Model&) const;
std::vector<Accessor> accessors;
std::vector<Animation> animations;
@ -944,7 +979,7 @@ class TinyGLTF {
#endif // TINY_GLTF_H_
#ifdef TINYGLTF_IMPLEMENTATION
#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
#include <algorithm>
//#include <cassert>
#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<bool>() == other.Get<bool>();
case NUMBER_TYPE:
return one.Get<double>() == other.Get<double>();
case INT_TYPE:
return one.Get<int>() == other.Get<int>();
case OBJECT_TYPE:
{
auto oneObj = one.Get<tinygltf::Value::Object>();
auto otherObj = other.Get<tinygltf::Value::Object>();
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<std::string>() == other.Get<std::string>();
case BINARY_TYPE:
return one.Get<std::vector<unsigned char>>() == other.Get<std::vector<unsigned char>>();
default:
{
// unhandled type
return false;
}
}
return false;
}
// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
bool Equals(const std::vector<double>& one, const std::vector<double>& 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.
@ -3875,6 +4215,7 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) {
}
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<int>("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