mirror of
https://git.mirrors.martin98.com/https://github.com/syoyo/tinygltf.git
synced 2025-08-11 17:19:02 +08:00
Merge branch 'feature/serialize-gltf' of https://github.com/sketchfab/tinygltf into sketchfab-feature/serialize-gltf
This commit is contained in:
commit
9a1ea7e300
@ -43,4 +43,4 @@ script:
|
||||
- export CXX="${CXX}-${COMPILER_VERSION}"
|
||||
- ${CC} -v
|
||||
- ${CXX} ${EXTRA_CXXFLAGS} -Wall -g -o loader_example loader_example.cc
|
||||
- ./loader_example box.gltf
|
||||
- ./loader_example ./models/Cube/Cube.gltf
|
||||
|
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Header-only tiny glTF 2.0 loader.
|
||||
// Header-only tiny glTF 2.0 loader and serializer.
|
||||
//
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
@ -240,6 +240,10 @@ class Value {
|
||||
return keys;
|
||||
}
|
||||
|
||||
size_t Size() const {
|
||||
return (IsArray() ? ArrayLen() : Keys().size());
|
||||
}
|
||||
|
||||
protected:
|
||||
int type_;
|
||||
|
||||
@ -318,7 +322,11 @@ struct Skin {
|
||||
int skeleton; // The index of the node used as a skeleton root
|
||||
std::vector<int> joints; // Indices of skeleton nodes
|
||||
|
||||
Skin() { inverseBindMatrices = -1; }
|
||||
Skin()
|
||||
{
|
||||
inverseBindMatrices = -1;
|
||||
skeleton = -1;
|
||||
}
|
||||
};
|
||||
|
||||
struct Sampler {
|
||||
@ -526,6 +534,7 @@ class Model {
|
||||
|
||||
int defaultScene;
|
||||
std::vector<std::string> extensionsUsed;
|
||||
std::vector<std::string> extensionsRequired;
|
||||
|
||||
Asset asset;
|
||||
|
||||
@ -579,6 +588,8 @@ class TinyGLTFLoader {
|
||||
const std::string &base_dir = "",
|
||||
unsigned int check_sections = REQUIRE_ALL);
|
||||
|
||||
bool WriteGltfSceneToFile(Model *model, const std::string &filename/*, bool embedImages, bool embedBuffers, bool writeBinary*/);
|
||||
|
||||
private:
|
||||
/// Loads glTF asset from string(memory).
|
||||
/// `length` = strlen(str);
|
||||
@ -1293,70 +1304,6 @@ static bool ParseStringIntProperty(std::map<std::string, int> *ret,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseKHRBinaryExtension(const picojson::object &o, std::string *err,
|
||||
double *buffer_view, std::string *mime_type,
|
||||
int *image_width, int *image_height) {
|
||||
picojson::object j = o;
|
||||
|
||||
if (j.find("extensions") == j.end()) {
|
||||
if (err) {
|
||||
(*err) += "`extensions' property is missing.\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(j["extensions"].is<picojson::object>())) {
|
||||
if (err) {
|
||||
(*err) += "Invalid `extensions' property.\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
picojson::object ext = j["extensions"].get<picojson::object>();
|
||||
|
||||
if (ext.find("KHR_binary_glTF") == ext.end()) {
|
||||
if (err) {
|
||||
(*err) +=
|
||||
"`KHR_binary_glTF' property is missing in extension property.\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(ext["KHR_binary_glTF"].is<picojson::object>())) {
|
||||
if (err) {
|
||||
(*err) += "Invalid `KHR_binary_glTF' property.\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
picojson::object k = ext["KHR_binary_glTF"].get<picojson::object>();
|
||||
|
||||
if (!ParseNumberProperty(buffer_view, err, k, "bufferView", true,
|
||||
"KHR_binary_glTF")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mime_type) {
|
||||
ParseStringProperty(mime_type, err, k, "mimeType", false);
|
||||
}
|
||||
|
||||
if (image_width) {
|
||||
double width = 0.0;
|
||||
if (ParseNumberProperty(&width, err, k, "width", false)) {
|
||||
(*image_width) = static_cast<int>(width);
|
||||
}
|
||||
}
|
||||
|
||||
if (image_height) {
|
||||
double height = 0.0;
|
||||
if (ParseNumberProperty(&height, err, k, "height", false)) {
|
||||
(*image_height) = static_cast<int>(height);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseJSONProperty(std::map<std::string, double> *ret,
|
||||
std::string *err, const picojson::object &o,
|
||||
const std::string &property, bool required) {
|
||||
@ -1446,34 +1393,26 @@ static bool ParseImage(Image *image, std::string *err,
|
||||
return false;
|
||||
}
|
||||
|
||||
// There should be "extensions" property.
|
||||
// "extensions":{"KHR_binary_glTF":{"bufferView": "id", ...
|
||||
|
||||
double buffer_view = -1.0;
|
||||
std::string mime_type;
|
||||
int image_width;
|
||||
int image_height;
|
||||
bool ret = ParseKHRBinaryExtension(o, err, &buffer_view, &mime_type,
|
||||
&image_width, &image_height);
|
||||
if (!ret) {
|
||||
if (!ParseNumberProperty(&buffer_view, err, o, "bufferView", true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uri.compare("data:,") == 0) {
|
||||
// ok
|
||||
} else {
|
||||
if (err) {
|
||||
(*err) += "Invalid URI for binary data.\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::string mime_type;
|
||||
ParseStringProperty(&mime_type, err, o, "mimeType", false);
|
||||
|
||||
double width = 0.0;
|
||||
ParseNumberProperty(&width, err, o, "width", false);
|
||||
|
||||
double height = 0.0;
|
||||
ParseNumberProperty(&height, err, o, "height", false);
|
||||
|
||||
// Just only save some information here. Loading actual image data from
|
||||
// bufferView is done in other place.
|
||||
image->bufferView = static_cast<int>(buffer_view);
|
||||
image->mimeType = mime_type;
|
||||
image->width = image_width;
|
||||
image->height = image_height;
|
||||
image->width = static_cast<int>(width);
|
||||
image->height = static_cast<int>(height);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1519,9 +1458,7 @@ static bool ParseTexture(Texture *texture, std::string *err,
|
||||
double source = -1.0;
|
||||
ParseNumberProperty(&sampler, err, o, "sampler", false);
|
||||
|
||||
if (!ParseNumberProperty(&source, err, o, "source", true)) {
|
||||
return false;
|
||||
}
|
||||
ParseNumberProperty(&source, err, o, "source", false);
|
||||
|
||||
texture->sampler = static_cast<int>(sampler);
|
||||
texture->source = static_cast<int>(source);
|
||||
@ -1539,9 +1476,18 @@ static bool ParseBuffer(Buffer *buffer, std::string *err,
|
||||
return false;
|
||||
}
|
||||
|
||||
// In glTF 2.0, uri is not mandatory anymore
|
||||
std::string uri;
|
||||
ParseStringProperty(&uri, err, o, "uri", false, "Buffer");
|
||||
|
||||
// having an empty uri for a non embedded image should not be valid
|
||||
if(!is_binary && uri.empty())
|
||||
{
|
||||
if (err) {
|
||||
(*err) += "'uri' is missing from non binary glTF file buffer.\n";
|
||||
}
|
||||
}
|
||||
|
||||
picojson::object::const_iterator type = o.find("type");
|
||||
if (type != o.end()) {
|
||||
if (type->second.is<std::string>()) {
|
||||
@ -1555,15 +1501,14 @@ static bool ParseBuffer(Buffer *buffer, std::string *err,
|
||||
size_t bytes = static_cast<size_t>(byteLength);
|
||||
if (is_binary) {
|
||||
// Still binary glTF accepts external dataURI. First try external resources.
|
||||
bool loaded = false;
|
||||
if (IsDataURI(uri)) {
|
||||
loaded = DecodeDataURI(&buffer->data, uri, bytes, true);
|
||||
} else {
|
||||
// Assume external .bin file.
|
||||
loaded = LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true);
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
if(!uri.empty())
|
||||
{
|
||||
// External .bin file.
|
||||
LoadExternalFile(&buffer->data, err, uri, basedir, bytes, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// load data from (embedded) binary data
|
||||
|
||||
if ((bin_size == 0) || (bin_data == NULL)) {
|
||||
@ -1584,18 +1529,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uri.compare("data:,") == 0) {
|
||||
// @todo { check uri }
|
||||
buffer->data.resize(static_cast<size_t>(byteLength));
|
||||
memcpy(&(buffer->data.at(0)), bin_data,
|
||||
static_cast<size_t>(byteLength));
|
||||
|
||||
} else {
|
||||
if (err) {
|
||||
(*err) += "Invalid URI for binary data in `Buffer'.\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Read buffer data
|
||||
buffer->data.resize(static_cast<size_t>(byteLength));
|
||||
memcpy(&(buffer->data.at(0)), bin_data,
|
||||
static_cast<size_t>(byteLength));
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -1772,6 +1709,25 @@ static bool ParsePrimitive(Primitive *primitive, std::string *err,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look for morph targets
|
||||
picojson::object::const_iterator targetsObject = o.find("targets");
|
||||
if ((targetsObject != o.end()) && (targetsObject->second).is<picojson::array>()) {
|
||||
const picojson::array &targetArray =
|
||||
(targetsObject->second).get<picojson::array>();
|
||||
for (size_t i = 0; i < targetArray.size(); i++) {
|
||||
std::map<std::string, int> targetAttribues;
|
||||
|
||||
const picojson::object &dict = targetArray[i].get<picojson::object>();
|
||||
picojson::object::const_iterator dictIt(dict.begin());
|
||||
picojson::object::const_iterator dictItEnd(dict.end());
|
||||
|
||||
for (; dictIt != dictItEnd; ++dictIt) {
|
||||
targetAttribues[dictIt->first] = static_cast<int>(dictIt->second.get<double>());
|
||||
}
|
||||
primitive->targets.push_back(targetAttribues);
|
||||
}
|
||||
}
|
||||
|
||||
ParseExtrasProperty(&(primitive->extras), o);
|
||||
|
||||
return true;
|
||||
@ -2104,7 +2060,7 @@ static bool ParseSkin(Skin *skin, std::string *err, const picojson::object &o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double skeleton;
|
||||
double skeleton = -1.0;
|
||||
ParseNumberProperty(&skeleton, err, o, "skeleton", false, "Skin");
|
||||
skin->skeleton = static_cast<int>(skeleton);
|
||||
|
||||
@ -2185,6 +2141,7 @@ bool TinyGLTFLoader::LoadFromString(Model *model, std::string *err,
|
||||
model->meshes.clear();
|
||||
model->nodes.clear();
|
||||
model->extensionsUsed.clear();
|
||||
model->extensionsRequired.clear();
|
||||
model->defaultScene = -1;
|
||||
|
||||
// 0. Parse Asset
|
||||
@ -2203,6 +2160,13 @@ bool TinyGLTFLoader::LoadFromString(Model *model, std::string *err,
|
||||
model->extensionsUsed.push_back(root[i].get<std::string>());
|
||||
}
|
||||
}
|
||||
if (v.contains("extensionsRequired") && v.get("extensionsRequired").is<picojson::array>()) {
|
||||
const picojson::array &root = v.get("extensionsRequired").get<picojson::array>();
|
||||
for(unsigned int i=0; i< root.size(); ++i)
|
||||
{
|
||||
model->extensionsRequired.push_back(root[i].get<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Parse Buffer
|
||||
if (v.contains("buffers") && v.get("buffers").is<picojson::array>()) {
|
||||
@ -2545,7 +2509,7 @@ bool TinyGLTFLoader::LoadBinaryFromMemory(Model *model, std::string *err,
|
||||
model_length);
|
||||
|
||||
is_binary_ = true;
|
||||
bin_data_ = bytes + 20 + model_length;
|
||||
bin_data_ = bytes + 20 + model_length + 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
|
||||
bin_size_ =
|
||||
length - (20 + model_length); // extract header + JSON scene data.
|
||||
|
||||
@ -2590,6 +2554,585 @@ bool TinyGLTFLoader::LoadBinaryFromFile(Model *model, std::string *err,
|
||||
return ret;
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// GLTF Serialization
|
||||
///////////////////////
|
||||
|
||||
typedef std::pair<std::string, picojson::value> json_object_pair;
|
||||
|
||||
template<typename T>
|
||||
static void SerializeNumberProperty(const std::string &key, T number, picojson::object &obj)
|
||||
{
|
||||
obj.insert(json_object_pair(key, picojson::value(static_cast<double>(number))));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void SerializeNumberArrayProperty(const std::string &key, const std::vector<T> &value, picojson::object &obj)
|
||||
{
|
||||
picojson::object o;
|
||||
picojson::array vals;
|
||||
|
||||
for(unsigned int i = 0; i < value.size() ; ++i)
|
||||
{
|
||||
vals.push_back(picojson::value(static_cast<double>(value[i])));
|
||||
}
|
||||
|
||||
obj.insert(json_object_pair(key, picojson::value(vals)));
|
||||
|
||||
}
|
||||
|
||||
static void SerializeStringProperty(const std::string &key, const std::string &value, picojson::object &obj)
|
||||
{
|
||||
picojson::value strVal(value);
|
||||
obj.insert(json_object_pair(key, strVal));
|
||||
}
|
||||
|
||||
static void SerializeStringArrayProperty(const std::string &key, const std::vector<std::string> &value, picojson::object &obj)
|
||||
{
|
||||
picojson::object o;
|
||||
picojson::array vals;
|
||||
|
||||
for(unsigned int i = 0; i < value.size() ; ++i)
|
||||
{
|
||||
vals.push_back(picojson::value(value[i]));
|
||||
}
|
||||
|
||||
obj.insert(json_object_pair(key, picojson::value(vals)));
|
||||
}
|
||||
|
||||
static void SerializeValue(const std::string &key, const Value &value, picojson::object &obj)
|
||||
{
|
||||
if(value.IsArray())
|
||||
{
|
||||
picojson::array jsonValue;
|
||||
for(unsigned int i = 0; i < value.ArrayLen(); ++i)
|
||||
{
|
||||
Value elementValue = value.Get(int(i));
|
||||
if(elementValue.IsString())
|
||||
jsonValue.push_back(picojson::value(elementValue.Get<std::string>()));
|
||||
}
|
||||
obj.insert(json_object_pair(key, picojson::value(jsonValue)));
|
||||
}
|
||||
else
|
||||
{
|
||||
picojson::object jsonValue;
|
||||
std::vector<std::string> valueKeys;
|
||||
for(unsigned int i = 0; i < valueKeys.size(); ++i)
|
||||
{
|
||||
Value elementValue = value.Get(valueKeys[i]);
|
||||
if(elementValue.IsInt())
|
||||
jsonValue.insert(json_object_pair(valueKeys[i], picojson::value(static_cast<double>(elementValue.Get<int>()))));
|
||||
}
|
||||
|
||||
obj.insert(json_object_pair(key, picojson::value(jsonValue)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void SerializeGltfBufferData(const std::vector<unsigned char> &data, const std::string &binFilePath)
|
||||
{
|
||||
std::ofstream output(binFilePath.c_str(), std::ofstream::binary);
|
||||
output.write(reinterpret_cast<const char*>(&data[0]), ssize_t(data.size()));
|
||||
output.close();
|
||||
}
|
||||
|
||||
static void SerializeParameterMap(ParameterMap ¶m, picojson::object &o)
|
||||
{
|
||||
for(ParameterMap::iterator paramIt = param.begin(); paramIt != param.end(); ++paramIt)
|
||||
{
|
||||
if(paramIt->second.number_array.size())
|
||||
{
|
||||
SerializeNumberArrayProperty<double>(paramIt->first, paramIt->second.number_array, o);
|
||||
}
|
||||
else if(paramIt->second.json_double_value.size())
|
||||
{
|
||||
picojson::object json_double_value;
|
||||
|
||||
for(std::map<std::string, double>::iterator it=paramIt->second.json_double_value.begin(); it != paramIt->second.json_double_value.end(); ++it)
|
||||
{
|
||||
json_double_value.insert(json_object_pair(it->first, picojson::value(it->second)));
|
||||
}
|
||||
|
||||
o.insert(json_object_pair(paramIt->first, picojson::value(json_double_value)));
|
||||
}
|
||||
else if(!paramIt->second.string_value.empty())
|
||||
{
|
||||
SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
|
||||
}
|
||||
else
|
||||
{
|
||||
o.insert(json_object_pair(paramIt->first, picojson::value(paramIt->second.bool_value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfAccessor(Accessor &accessor, picojson::object &o)
|
||||
{
|
||||
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
|
||||
|
||||
if(accessor.byteOffset != 0.0 )
|
||||
SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
|
||||
|
||||
SerializeNumberProperty<int>("componentType", accessor.componentType, o);
|
||||
SerializeNumberProperty<size_t>("count", accessor.count, o);
|
||||
SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
|
||||
SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
|
||||
std::string type;
|
||||
switch(accessor.type)
|
||||
{
|
||||
case TINYGLTF_TYPE_SCALAR:
|
||||
type = "SCALAR";
|
||||
break;
|
||||
case TINYGLTF_TYPE_VEC2:
|
||||
type = "VEC2";
|
||||
break;
|
||||
case TINYGLTF_TYPE_VEC3:
|
||||
type = "VEC3";
|
||||
break;
|
||||
case TINYGLTF_TYPE_VEC4:
|
||||
type = "VEC4";
|
||||
break;
|
||||
case TINYGLTF_TYPE_MAT2:
|
||||
type = "MAT2";
|
||||
break;
|
||||
case TINYGLTF_TYPE_MAT3:
|
||||
type = "MAT3";
|
||||
break;
|
||||
case TINYGLTF_TYPE_MAT4:
|
||||
type = "MAT4";
|
||||
break;
|
||||
}
|
||||
|
||||
SerializeStringProperty("type", type, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfAnimationChannel(AnimationChannel &channel, picojson::object &o)
|
||||
{
|
||||
SerializeNumberProperty("sampler", channel.sampler, o);
|
||||
picojson::object target;
|
||||
SerializeNumberProperty("node", channel.target_node, target);
|
||||
SerializeStringProperty("path", channel.target_path, target);
|
||||
|
||||
o.insert(json_object_pair("target", picojson::value(target)));
|
||||
}
|
||||
|
||||
static void SerializeGltfAnimationSampler(AnimationSampler &sampler, picojson::object &o)
|
||||
{
|
||||
SerializeNumberProperty("input", sampler.input, o);
|
||||
SerializeNumberProperty("output", sampler.output, o);
|
||||
SerializeStringProperty("interpolation", sampler.interpolation, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfAnimation(Animation &animation, picojson::object &o)
|
||||
{
|
||||
SerializeStringProperty("name", animation.name, o);
|
||||
picojson::array channels;
|
||||
for(unsigned int i = 0; i < animation.channels.size(); ++i)
|
||||
{
|
||||
picojson::object channel;
|
||||
AnimationChannel gltfChannel = animation.channels[i];
|
||||
SerializeGltfAnimationChannel(gltfChannel, channel);
|
||||
channels.push_back(picojson::value(channel));
|
||||
}
|
||||
o.insert(json_object_pair("channels", picojson::value(channels)));
|
||||
|
||||
picojson::array samplers;
|
||||
for(unsigned int i = 0; i < animation.samplers.size(); ++i)
|
||||
{
|
||||
picojson::object sampler;
|
||||
AnimationSampler gltfSampler = animation.samplers[i];
|
||||
SerializeGltfAnimationSampler(gltfSampler, sampler);
|
||||
samplers.push_back(picojson::value(sampler));
|
||||
}
|
||||
|
||||
o.insert(json_object_pair("samplers", picojson::value(samplers)));
|
||||
}
|
||||
|
||||
static void SerializeGltfAsset(Asset &asset, picojson::object &o)
|
||||
{
|
||||
if(!asset.generator.empty())
|
||||
{
|
||||
SerializeStringProperty("generator", asset.generator, o);
|
||||
}
|
||||
|
||||
if(!asset.version.empty())
|
||||
{
|
||||
SerializeStringProperty("version", asset.version, o);
|
||||
}
|
||||
|
||||
if(asset.extras.Keys().size())
|
||||
{
|
||||
SerializeValue("extras", asset.extras, o);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfBuffer(Buffer &buffer, picojson::object &o, const std::string &binFilePath)
|
||||
{
|
||||
SerializeGltfBufferData(buffer.data, binFilePath);
|
||||
SerializeNumberProperty("byteLength", buffer.data.size(), o);
|
||||
SerializeStringProperty("uri", binFilePath, o);
|
||||
|
||||
if(buffer.name.size())
|
||||
SerializeStringProperty("name", buffer.name, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfBufferView(BufferView &bufferView, picojson::object &o)
|
||||
{
|
||||
SerializeNumberProperty("buffer", bufferView.buffer, o);
|
||||
SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
|
||||
SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
|
||||
SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
|
||||
SerializeNumberProperty("target", bufferView.target, o);
|
||||
|
||||
if(bufferView.name.size())
|
||||
{
|
||||
SerializeStringProperty("name", bufferView.name, o);
|
||||
}
|
||||
}
|
||||
|
||||
// Only external textures are serialized for now
|
||||
static void SerializeGltfImage(Image &image, picojson::object &o)
|
||||
{
|
||||
SerializeStringProperty("uri", image.uri, o);
|
||||
|
||||
if(image.name.size())
|
||||
{
|
||||
SerializeStringProperty("name", image.name, o);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfMaterial(Material &material, picojson::object &o)
|
||||
{
|
||||
if(material.extPBRValues.size())
|
||||
{
|
||||
// Serialize PBR specular/glossiness material
|
||||
picojson::object values;
|
||||
SerializeParameterMap(material.extPBRValues, values);
|
||||
|
||||
picojson::object extension;
|
||||
o.insert(json_object_pair("extensions", picojson::value(extension)));
|
||||
}
|
||||
|
||||
if(material.values.size())
|
||||
{
|
||||
picojson::object pbrMetallicRoughness;
|
||||
SerializeParameterMap(material.values, pbrMetallicRoughness);
|
||||
o.insert(json_object_pair("pbrMetallicRoughness", picojson::value(pbrMetallicRoughness)));
|
||||
}
|
||||
|
||||
picojson::object additionalValues;
|
||||
SerializeParameterMap(material.additionalValues, o);
|
||||
|
||||
if(material.name.size())
|
||||
{
|
||||
SerializeStringProperty("name", material.name, o);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfMesh(Mesh &mesh, picojson::object &o)
|
||||
{
|
||||
picojson::array primitives;
|
||||
for(unsigned int i=0; i < mesh.primitives.size(); ++i)
|
||||
{
|
||||
picojson::object primitive;
|
||||
picojson::object attributes;
|
||||
Primitive gltfPrimitive = mesh.primitives[i];
|
||||
for(std::map<std::string, int>::iterator attrIt = gltfPrimitive.attributes.begin(); attrIt != gltfPrimitive.attributes.end(); ++attrIt)
|
||||
{
|
||||
SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
|
||||
}
|
||||
|
||||
primitive.insert(json_object_pair("attributes", picojson::value(attributes)));
|
||||
SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
|
||||
SerializeNumberProperty<int>("material", gltfPrimitive.material, primitive);
|
||||
SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
|
||||
|
||||
// Morph targets
|
||||
if(gltfPrimitive.targets.size())
|
||||
{
|
||||
picojson::array targets;
|
||||
for(unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k)
|
||||
{
|
||||
picojson::object targetAttributes;
|
||||
std::map<std::string, int> targetData = gltfPrimitive.targets[k];
|
||||
for(std::map<std::string, int>::iterator attrIt = targetData.begin(); attrIt != targetData.end(); ++attrIt)
|
||||
{
|
||||
SerializeNumberProperty<int>(attrIt->first, attrIt->second, targetAttributes);
|
||||
}
|
||||
|
||||
targets.push_back(picojson::value(targetAttributes));
|
||||
}
|
||||
primitive.insert(json_object_pair("targets", picojson::value(targets)));
|
||||
}
|
||||
|
||||
primitives.push_back(picojson::value(primitive));
|
||||
}
|
||||
|
||||
o.insert(json_object_pair("primitives", picojson::value(primitives)));
|
||||
if(mesh.weights.size())
|
||||
{
|
||||
SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
|
||||
}
|
||||
|
||||
if(mesh.name.size())
|
||||
{
|
||||
SerializeStringProperty("name", mesh.name, o);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfNode(Node &node, picojson::object &o)
|
||||
{
|
||||
if(node.translation.size() > 0)
|
||||
{
|
||||
SerializeNumberArrayProperty<double>("translation", node.translation, o);
|
||||
}
|
||||
if(node.rotation.size() > 0)
|
||||
{
|
||||
SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
|
||||
}
|
||||
if(node.scale.size() > 0)
|
||||
{
|
||||
SerializeNumberArrayProperty<double>("scale", node.scale, o);
|
||||
}
|
||||
if(node.matrix.size() > 0)
|
||||
{
|
||||
SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
|
||||
}
|
||||
if(node.mesh != -1)
|
||||
{
|
||||
SerializeNumberProperty<int>("mesh", node.mesh, o);
|
||||
}
|
||||
|
||||
if(node.skin != -1)
|
||||
{
|
||||
SerializeNumberProperty<int>("skin", node.skin, o);
|
||||
}
|
||||
|
||||
SerializeStringProperty("name", node.name, o);
|
||||
SerializeNumberArrayProperty<int>("children", node.children, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfSampler(Sampler &sampler, picojson::object &o)
|
||||
{
|
||||
SerializeNumberProperty("magFilter", sampler.magFilter, o);
|
||||
SerializeNumberProperty("minFilter", sampler.minFilter, o);
|
||||
SerializeNumberProperty("wrapS", sampler.wrapS, o);
|
||||
SerializeNumberProperty("wrapT", sampler.wrapT, o);
|
||||
}
|
||||
|
||||
static void SerializeGltfScene(Scene &scene, picojson::object &o)
|
||||
{
|
||||
SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
|
||||
|
||||
if(scene.name.size())
|
||||
{
|
||||
SerializeStringProperty("name", scene.name, o);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfSkin(Skin &skin, picojson::object &o)
|
||||
{
|
||||
if(skin.inverseBindMatrices != -1)
|
||||
SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
|
||||
|
||||
SerializeNumberArrayProperty<int>("joints", skin.joints, o);
|
||||
SerializeNumberProperty("skeleton", skin.skeleton, o);
|
||||
if(skin.name.size())
|
||||
{
|
||||
SerializeStringProperty("name", skin.name, o);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeGltfTexture(Texture &texture, picojson::object &o)
|
||||
{
|
||||
SerializeNumberProperty("sampler", texture.sampler, o);
|
||||
SerializeNumberProperty("source", texture.source, o);
|
||||
|
||||
if(texture.extras.Size())
|
||||
{
|
||||
picojson::object extras;
|
||||
SerializeValue("extras", texture.extras, o);
|
||||
o.insert(json_object_pair("extras", picojson::value(extras)));
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteGltfFile(const std::string& output, const std::string& content)
|
||||
{
|
||||
std::ofstream gltfFile(output.c_str());
|
||||
gltfFile << content << std::endl;
|
||||
}
|
||||
|
||||
bool TinyGLTFLoader::WriteGltfSceneToFile(Model *model, const std::string &filename/*, bool embedImages, bool embedBuffers, bool writeBinary*/)
|
||||
{
|
||||
picojson::object output;
|
||||
|
||||
//ACCESSORS
|
||||
picojson::array accessors;
|
||||
for(unsigned int i=0; i < model->accessors.size(); ++i)
|
||||
{
|
||||
picojson::object accessor;
|
||||
SerializeGltfAccessor(model->accessors[i], accessor);
|
||||
accessors.push_back(picojson::value(accessor));
|
||||
}
|
||||
output.insert(json_object_pair("accessors", picojson::value(accessors)));
|
||||
|
||||
//ANIMATIONS
|
||||
if(model->animations.size())
|
||||
{
|
||||
picojson::array animations;
|
||||
for(unsigned int i=0; i < model->animations.size(); ++i)
|
||||
{
|
||||
if(model->animations[i].channels.size())
|
||||
{
|
||||
picojson::object animation;
|
||||
SerializeGltfAnimation(model->animations[i], animation);
|
||||
animations.push_back(picojson::value(animation));
|
||||
}
|
||||
}
|
||||
output.insert(json_object_pair("animations", picojson::value(animations)));
|
||||
}
|
||||
|
||||
//ASSET
|
||||
picojson::object asset;
|
||||
SerializeGltfAsset(model->asset, asset);
|
||||
output.insert(json_object_pair("asset", picojson::value(asset)));
|
||||
|
||||
std::string binFilePath = filename;
|
||||
std::string ext = ".bin";
|
||||
std::string::size_type pos = binFilePath.rfind('.', binFilePath.length());
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
binFilePath = binFilePath.substr(0, pos) + ext;
|
||||
}
|
||||
else
|
||||
{
|
||||
binFilePath = "./" + binFilePath + ".bin";
|
||||
}
|
||||
|
||||
//BUFFERS (We expect only one buffer here)
|
||||
picojson::array buffers;
|
||||
for(unsigned int i=0; i < model->buffers.size(); ++i)
|
||||
{
|
||||
picojson::object buffer;
|
||||
SerializeGltfBuffer(model->buffers[i], buffer, binFilePath);
|
||||
buffers.push_back(picojson::value(buffer));
|
||||
}
|
||||
output.insert(json_object_pair("buffers", picojson::value(buffers)));
|
||||
|
||||
//BUFFERVIEWS
|
||||
picojson::array bufferViews;
|
||||
for(unsigned int i=0; i < model->bufferViews.size(); ++i)
|
||||
{
|
||||
picojson::object bufferView;
|
||||
SerializeGltfBufferView(model->bufferViews[i], bufferView);
|
||||
bufferViews.push_back(picojson::value(bufferView));
|
||||
}
|
||||
output.insert(json_object_pair("bufferViews", picojson::value(bufferViews)));
|
||||
|
||||
//Extensions used
|
||||
if(model->extensionsUsed.size())
|
||||
{
|
||||
SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, output);
|
||||
}
|
||||
|
||||
//Extensions required
|
||||
if(model->extensionsRequired.size())
|
||||
{
|
||||
SerializeStringArrayProperty("extensionsRequired", model->extensionsRequired, output);
|
||||
}
|
||||
|
||||
//IMAGES
|
||||
picojson::array images;
|
||||
for(unsigned int i=0; i < model->images.size(); ++i)
|
||||
{
|
||||
picojson::object image;
|
||||
SerializeGltfImage(model->images[i], image);
|
||||
images.push_back(picojson::value(image));
|
||||
}
|
||||
output.insert(json_object_pair("images", picojson::value(images)));
|
||||
|
||||
//MATERIALS
|
||||
picojson::array materials;
|
||||
for(unsigned int i=0; i < model->materials.size(); ++i)
|
||||
{
|
||||
picojson::object material;
|
||||
SerializeGltfMaterial(model->materials[i], material);
|
||||
materials.push_back(picojson::value(material));
|
||||
}
|
||||
output.insert(json_object_pair("materials", picojson::value(materials)));
|
||||
|
||||
//MESHES
|
||||
picojson::array meshes;
|
||||
for(unsigned int i=0; i < model->meshes.size(); ++i)
|
||||
{
|
||||
picojson::object mesh;
|
||||
SerializeGltfMesh(model->meshes[i], mesh);
|
||||
meshes.push_back(picojson::value(mesh));
|
||||
}
|
||||
output.insert(json_object_pair("meshes", picojson::value(meshes)));
|
||||
|
||||
//NODES
|
||||
picojson::array nodes;
|
||||
for(unsigned int i=0; i < model->nodes.size(); ++i)
|
||||
{
|
||||
picojson::object node;
|
||||
SerializeGltfNode(model->nodes[i], node);
|
||||
nodes.push_back(picojson::value(node));
|
||||
}
|
||||
output.insert(json_object_pair("nodes", picojson::value(nodes)));
|
||||
|
||||
//SCENE
|
||||
SerializeNumberProperty<int>("scene", model->defaultScene, output);
|
||||
|
||||
//SCENES
|
||||
picojson::array scenes;
|
||||
for(unsigned int i=0; i < model->scenes.size(); ++i)
|
||||
{
|
||||
picojson::object currentScene;
|
||||
SerializeGltfScene(model->scenes[i], currentScene);
|
||||
scenes.push_back(picojson::value(currentScene));
|
||||
}
|
||||
output.insert(json_object_pair("scenes", picojson::value(scenes)));
|
||||
|
||||
//SKINS
|
||||
if(model->skins.size())
|
||||
{
|
||||
picojson::array skins;
|
||||
for(unsigned int i=0; i < model->skins.size(); ++i)
|
||||
{
|
||||
picojson::object skin;
|
||||
SerializeGltfSkin(model->skins[i], skin);
|
||||
skins.push_back(picojson::value(picojson::value(skin)));
|
||||
}
|
||||
output.insert(json_object_pair("skins", picojson::value(skins)));
|
||||
}
|
||||
|
||||
//TEXTURES
|
||||
picojson::array textures;
|
||||
for(unsigned int i=0; i < model->textures.size(); ++i)
|
||||
{
|
||||
picojson::object texture;
|
||||
SerializeGltfTexture(model->textures[i], texture);
|
||||
textures.push_back(picojson::value(texture));
|
||||
}
|
||||
output.insert(json_object_pair("textures", picojson::value(textures)));
|
||||
|
||||
//SAMPLERS
|
||||
picojson::array samplers;
|
||||
for(unsigned int i=0; i < model->samplers.size(); ++i)
|
||||
{
|
||||
picojson::object sampler;
|
||||
SerializeGltfSampler(model->samplers[i], sampler);
|
||||
samplers.push_back(picojson::value(sampler));
|
||||
}
|
||||
output.insert(json_object_pair("samplers", picojson::value(samplers)));
|
||||
|
||||
WriteGltfFile(filename, picojson::value(output).serialize());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace tinygltf
|
||||
|
||||
#endif // TINYGLTF_LOADER_IMPLEMENTATION
|
||||
|
Loading…
x
Reference in New Issue
Block a user