From 186322bed665c80e1ce3dca2cc1319b31dbccdd4 Mon Sep 17 00:00:00 2001 From: Emanuel Schrade Date: Mon, 6 Nov 2017 11:14:41 +0100 Subject: [PATCH] Parse KHR_Lights_cmn extension --- tiny_gltf.h | 159 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 128 insertions(+), 31 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 3df699c..80597f2 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -505,6 +505,7 @@ class Node { std::vector weights; // The weights of the instantiated Morph Target Value extras; + ParameterMap extLightsValues; // KHR_lights_cmn extension }; typedef struct { @@ -532,6 +533,12 @@ struct Scene { ParameterMap extras; }; +struct Light { + std::string name; + std::vector color; + std::string type; +}; + class Model { public: Model() {} @@ -550,6 +557,7 @@ class Model { std::vector samplers; std::vector cameras; std::vector scenes; + std::vector lights; int defaultScene; std::vector extensionsUsed; @@ -1838,6 +1846,48 @@ static bool ParseMesh(Mesh *mesh, std::string *err, const picojson::object &o) { return true; } +static bool ParseParameterProperty(Parameter *param, std::string *err, + const picojson::object &o, + const std::string &prop, bool required) { + double num_val; + + // A parameter value can either be a string or an array of either a boolean or + // a number. Booleans of any kind aren't supported here. Granted, it + // complicates the Parameter structure and breaks it semantically in the sense + // that the client probably works off the assumption that if the string is + // empty the vector is used, etc. Would a tagged union work? + if (ParseStringProperty(¶m->string_value, err, o, prop, false)) { + // Found string property. + return true; + } else if (ParseNumberArrayProperty(¶m->number_array, err, o, prop, + false)) { + // Found a number array. + return true; + } else if (ParseNumberProperty(&num_val, err, o, prop, false)) { + param->number_array.push_back(num_val); + return true; + } else if (ParseJSONProperty(¶m->json_double_value, err, o, prop, + false)) { + return true; + } else if (ParseBooleanProperty(¶m->bool_value, err, o, prop, false)) { + return true; + } else { + if (required) { + if (err) { + (*err) += "parameter must be a string or number / number array.\n"; + } + } + return false; + } +} + +static bool ParseLight(Light *light, std::string *err, const picojson::object &o) { + ParseStringProperty(&light->name, err, o, "name", false); + ParseNumberArrayProperty(&light->color, err, o, "color", false); + ParseStringProperty(&light->type, err, o, "type", false); + return true; +} + static bool ParseNode(Node *node, std::string *err, const picojson::object &o) { ParseStringProperty(&node->name, err, o, "name", false); @@ -1881,42 +1931,36 @@ static bool ParseNode(Node *node, std::string *err, const picojson::object &o) { ParseExtrasProperty(&(node->extras), o); - return true; -} + picojson::object::const_iterator extensions_object = o.find("extensions"); + if ((extensions_object != o.end()) && + (extensions_object->second).is()) { + const picojson::object &values_object = + (extensions_object->second).get(); -static bool ParseParameterProperty(Parameter *param, std::string *err, - const picojson::object &o, - const std::string &prop, bool required) { - double num_val; + picojson::object::const_iterator it(values_object.begin()); + picojson::object::const_iterator itEnd(values_object.end()); - // A parameter value can either be a string or an array of either a boolean or - // a number. Booleans of any kind aren't supported here. Granted, it - // complicates the Parameter structure and breaks it semantically in the sense - // that the client probably works off the assumption that if the string is - // empty the vector is used, etc. Would a tagged union work? - if (ParseStringProperty(¶m->string_value, err, o, prop, false)) { - // Found string property. - return true; - } else if (ParseNumberArrayProperty(¶m->number_array, err, o, prop, - false)) { - // Found a number array. - return true; - } else if (ParseNumberProperty(&num_val, err, o, prop, false)) { - param->number_array.push_back(num_val); - return true; - } else if (ParseJSONProperty(¶m->json_double_value, err, o, prop, - false)) { - return true; - } else if (ParseBooleanProperty(¶m->bool_value, err, o, prop, false)) { - return true; - } else { - if (required) { - if (err) { - (*err) += "parameter must be a string or number / number array.\n"; + for (; it != itEnd; it++) { + if (it->first == "KHR_lights_cmn" && + (it->second).is()) { + const picojson::object &values_object = + (it->second).get(); + + picojson::object::const_iterator itVal(values_object.begin()); + picojson::object::const_iterator itValEnd(values_object.end()); + + for (; itVal != itValEnd; itVal++) { + Parameter param; + if (ParseParameterProperty(¶m, err, values_object, itVal->first, + false)) { + node->extLightsValues[itVal->first] = param; + } + } } } - return false; } + + return true; } static bool ParseMaterial(Material *material, std::string *err, @@ -2722,6 +2766,35 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, const char *str, model->cameras.push_back(camera); } } + + // 15. Parse Extensions + if (v.contains("extensions") && v.get("extensions").is()) { + const picojson::object &root = v.get("extensions").get(); + picojson::object::const_iterator it(root.begin()); + picojson::object::const_iterator itEnd(root.end()); + for (; it != itEnd; ++it) { + // parse KHR_lights_cmn extension + if (it->first == "KHR_lights_cmn" && it->second.is()) { + const picojson::object &object = it->second.get(); + picojson::object::const_iterator it(object.find("lights")); + picojson::object::const_iterator itEnd(object.end()); + if (it == itEnd) + continue; + + const picojson::array &lights = it->second.get(); + picojson::array::const_iterator arrayIt(lights.begin()); + picojson::array::const_iterator arrayItEnd(lights.end()); + for (; arrayIt != arrayItEnd; ++arrayIt) { + Light light; + if (!ParseLight(&light, err, arrayIt->get())) { + return false; + } + model->lights.push_back(light); + } + } + } + + } return true; } @@ -3172,6 +3245,12 @@ static void SerializeGltfMesh(Mesh &mesh, picojson::object &o) { } } +static void SerializeGltfLight(Light &light, picojson::object &o) { + SerializeStringProperty("name", light.name, o); + SerializeNumberArrayProperty("color", light.color, o); + SerializeStringProperty("type", light.type, o); +} + static void SerializeGltfNode(Node &node, picojson::object &o) { if (node.translation.size() > 0) { SerializeNumberArrayProperty("translation", node.translation, o); @@ -3197,6 +3276,15 @@ static void SerializeGltfNode(Node &node, picojson::object &o) { SerializeNumberProperty("camera", node.camera, o); } + if (node.extLightsValues.size()) { + picojson::object values; + SerializeParameterMap(node.extLightsValues, values); + picojson::object lightsExt; + lightsExt.insert(json_object_pair("KHR_lights_cmn", picojson::value(values))); + o.insert(json_object_pair("extensions", picojson::value(lightsExt))); + } + + SerializeStringProperty("name", node.name, o); SerializeNumberArrayProperty("children", node.children, o); } @@ -3443,6 +3531,15 @@ bool TinyGLTF::WriteGltfSceneToFile( } output.insert(json_object_pair("cameras", picojson::value(cameras))); + // LIGHTS + picojson::array lights; + for (unsigned int i = 0; i < model->lights.size(); ++i) { + picojson::object light; + SerializeGltfLight(model->lights[i], light); + lights.push_back(picojson::value(light)); + } + output.insert(json_object_pair("lights", picojson::value(lights))); + WriteGltfFile(filename, picojson::value(output).serialize()); return true; }