From 1af7c1d7848f68e4e0197342926378c41e0dfc54 Mon Sep 17 00:00:00 2001 From: Johan Bowald Date: Tue, 16 Jul 2019 14:50:46 +0200 Subject: [PATCH] can write to streams --- tiny_gltf.h | 453 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 261 insertions(+), 192 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 30c2e55..0501265 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -1022,6 +1022,12 @@ class TinyGLTF { const std::string &base_dir = "", unsigned int check_sections = REQUIRE_VERSION); + /// + /// Write glTF to stream, buffers and images will be embeded + /// + bool WriteGltfSceneToStream(Model *model, std::ostream &stream, + bool prettyPrint, bool writeBinary); + /// /// Write glTF to file. /// @@ -1055,6 +1061,7 @@ class TinyGLTF { const char *str, const unsigned int length, const std::string &base_dir, unsigned int check_sections); + const unsigned char *bin_data_; size_t bin_size_; bool is_binary_; @@ -5257,52 +5264,10 @@ static void SerializeGltfTexture(Texture &texture, json &o) { SerializeExtensionMap(texture.extensions, o); } -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; -} - -static void WriteBinaryGltfFile(const std::string &output, - const std::string &content) { - std::ofstream gltfFile(output.c_str(), std::ios::binary); - - const std::string header = "glTF"; - const int version = 2; - const int padding_size = content.size() % 4; - - // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info, - // padding - const int length = 12 + 8 + int(content.size()) + padding_size; - - gltfFile.write(header.c_str(), header.size()); - gltfFile.write(reinterpret_cast(&version), sizeof(version)); - gltfFile.write(reinterpret_cast(&length), sizeof(length)); - - // JSON chunk info, then JSON data - const int model_length = int(content.size()) + padding_size; - const int model_format = 0x4E4F534A; - gltfFile.write(reinterpret_cast(&model_length), - sizeof(model_length)); - gltfFile.write(reinterpret_cast(&model_format), - sizeof(model_format)); - gltfFile.write(content.c_str(), content.size()); - - // Chunk must be multiplies of 4, so pad with spaces - if (padding_size > 0) { - const std::string padding = std::string(padding_size, ' '); - gltfFile.write(padding.c_str(), padding.size()); - } -} - -bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, - bool embedImages = false, - bool embedBuffers = false, - bool prettyPrint = true, - bool writeBinary = false) { - json output; +/// +/// Serialize all properties except buffers and images. +/// +static void SerializeGltfModel(Model *model, json &o) { // ACCESSORS json accessors; @@ -5311,7 +5276,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, SerializeGltfAccessor(model->accessors[i], accessor); accessors.push_back(accessor); } - output["accessors"] = accessors; + o["accessors"] = accessors; // ANIMATIONS if (model->animations.size()) { @@ -5323,14 +5288,259 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, animations.push_back(animation); } } - output["animations"] = animations; + o["animations"] = animations; } // ASSET json asset; SerializeGltfAsset(model->asset, asset); - output["asset"] = asset; + o["asset"] = asset; + + // BUFFERVIEWS + json bufferViews; + for (unsigned int i = 0; i < model->bufferViews.size(); ++i) { + json bufferView; + SerializeGltfBufferView(model->bufferViews[i], bufferView); + bufferViews.push_back(bufferView); + } + o["bufferViews"] = bufferViews; + + // Extensions used + if (model->extensionsUsed.size()) { + SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, + o); + } + + // Extensions required + if (model->extensionsRequired.size()) { + SerializeStringArrayProperty("extensionsRequired", + model->extensionsRequired, o); + } + + // MATERIALS + if (model->materials.size()) { + json materials; + for (unsigned int i = 0; i < model->materials.size(); ++i) { + json material; + SerializeGltfMaterial(model->materials[i], material); + materials.push_back(material); + } + o["materials"] = materials; + } + + // MESHES + if (model->meshes.size()) { + json meshes; + for (unsigned int i = 0; i < model->meshes.size(); ++i) { + json mesh; + SerializeGltfMesh(model->meshes[i], mesh); + meshes.push_back(mesh); + } + o["meshes"] = meshes; + } + + // NODES + if (model->nodes.size()) { + json nodes; + for (unsigned int i = 0; i < model->nodes.size(); ++i) { + json node; + SerializeGltfNode(model->nodes[i], node); + nodes.push_back(node); + } + o["nodes"] = nodes; + } + + // SCENE + if (model->defaultScene > -1) { + SerializeNumberProperty("scene", model->defaultScene, o); + } + + // SCENES + if (model->scenes.size()) { + json scenes; + for (unsigned int i = 0; i < model->scenes.size(); ++i) { + json currentScene; + SerializeGltfScene(model->scenes[i], currentScene); + scenes.push_back(currentScene); + } + o["scenes"] = scenes; + } + + // SKINS + if (model->skins.size()) { + json skins; + for (unsigned int i = 0; i < model->skins.size(); ++i) { + json skin; + SerializeGltfSkin(model->skins[i], skin); + skins.push_back(skin); + } + o["skins"] = skins; + } + + // TEXTURES + if (model->textures.size()) { + json textures; + for (unsigned int i = 0; i < model->textures.size(); ++i) { + json texture; + SerializeGltfTexture(model->textures[i], texture); + textures.push_back(texture); + } + o["textures"] = textures; + } + + // SAMPLERS + if (model->samplers.size()) { + json samplers; + for (unsigned int i = 0; i < model->samplers.size(); ++i) { + json sampler; + SerializeGltfSampler(model->samplers[i], sampler); + samplers.push_back(sampler); + } + o["samplers"] = samplers; + } + + // CAMERAS + if (model->cameras.size()) { + json cameras; + for (unsigned int i = 0; i < model->cameras.size(); ++i) { + json camera; + SerializeGltfCamera(model->cameras[i], camera); + cameras.push_back(camera); + } + o["cameras"] = cameras; + } + + // EXTENSIONS + SerializeExtensionMap(model->extensions, o); + + // LIGHTS as KHR_lights_cmn + if (model->lights.size()) { + json lights; + for (unsigned int i = 0; i < model->lights.size(); ++i) { + json light; + SerializeGltfLight(model->lights[i], light); + lights.push_back(light); + } + json khr_lights_cmn; + khr_lights_cmn["lights"] = lights; + json ext_j; + + if (o.find("extensions") != o.end()) { + ext_j = o["extensions"]; + } + + ext_j["KHR_lights_punctual"] = khr_lights_cmn; + + o["extensions"] = ext_j; + } + + // EXTRAS + if (model->extras.Type() != NULL_TYPE) { + SerializeValue("extras", model->extras, o); + } +} + +static bool WriteGltfStream(std::ostream &stream, + const std::string &content) { + stream << content << std::endl; + return true; +} + +static bool WriteGltfFile(const std::string &output, + const std::string &content) { + std::ofstream gltfFile(output.c_str()); + if (!gltfFile.is_open()) return false; + return WriteGltfStream(gltfFile, content); +} + +static void WriteBinaryGltfStream(std::ostream &stream, + const std::string &content) { + + const std::string header = "glTF"; + const int version = 2; + const int padding_size = content.size() % 4; + + // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info, + // padding + const int length = 12 + 8 + int(content.size()) + padding_size; + + stream.write(header.c_str(), header.size()); + stream.write(reinterpret_cast(&version), sizeof(version)); + stream.write(reinterpret_cast(&length), sizeof(length)); + + // JSON chunk info, then JSON data + const int model_length = int(content.size()) + padding_size; + const int model_format = 0x4E4F534A; + stream.write(reinterpret_cast(&model_length), + sizeof(model_length)); + stream.write(reinterpret_cast(&model_format), + sizeof(model_format)); + stream.write(content.c_str(), content.size()); + + // Chunk must be multiplies of 4, so pad with spaces + if (padding_size > 0) { + const std::string padding = std::string(padding_size, ' '); + stream.write(padding.c_str(), padding.size()); + } +} + +static void WriteBinaryGltfFile(const std::string &output, + const std::string &content) { + std::ofstream gltfFile(output.c_str(), std::ios::binary); + WriteBinaryGltfStream(gltfFile, content); +} + +bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, + bool prettyPrint = true, + bool writeBinary = false) { + json output; + + /// Serialize all properties except buffers and images. + SerializeGltfModel(model, output); + + // BUFFERS + std::vector usedUris; + json buffers; + for (unsigned int i = 0; i < model->buffers.size(); ++i) { + json buffer; + SerializeGltfBuffer(model->buffers[i], buffer); + buffers.push_back(buffer); + } + output["buffers"] = buffers; + + // IMAGES + if (model->images.size()) { + json images; + for (unsigned int i = 0; i < model->images.size(); ++i) { + json image; + + std::string dummystring = ""; + // UpdateImageObject need baseDir but only uses it if embededImages is enable, + // since we won't write separte images when writing to a stream we use a dummystring + UpdateImageObject(model->images[i], dummystring, int(i), false, + &this->WriteImageData, this->write_image_user_data_); + SerializeGltfImage(model->images[i], image); + images.push_back(image); + } + output["images"] = images; + } + + if (writeBinary) { + WriteBinaryGltfStream(stream, output.dump()); + } else { + WriteGltfStream(stream, output.dump(prettyPrint ? 2 : -1)); + } + + return true; +} + +bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, + bool embedImages = false, + bool embedBuffers = false, + bool prettyPrint = true, + bool writeBinary = false) { + json output; std::string defaultBinFilename = GetBaseFilename(filename); std::string defaultBinFileExt = ".bin"; std::string::size_type pos = @@ -5343,6 +5553,8 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, if (baseDir.empty()) { baseDir = "./"; } + /// Serialize all properties except buffers and images. + SerializeGltfModel(model, output); // BUFFERS std::vector usedUris; @@ -5382,27 +5594,6 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, } output["buffers"] = buffers; - // BUFFERVIEWS - json bufferViews; - for (unsigned int i = 0; i < model->bufferViews.size(); ++i) { - json bufferView; - SerializeGltfBufferView(model->bufferViews[i], bufferView); - bufferViews.push_back(bufferView); - } - output["bufferViews"] = bufferViews; - - // Extensions used - if (model->extensionsUsed.size()) { - SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, - output); - } - - // Extensions required - if (model->extensionsRequired.size()) { - SerializeStringArrayProperty("extensionsRequired", - model->extensionsRequired, output); - } - // IMAGES if (model->images.size()) { json images; @@ -5417,128 +5608,6 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, output["images"] = images; } - // MATERIALS - if (model->materials.size()) { - json materials; - for (unsigned int i = 0; i < model->materials.size(); ++i) { - json material; - SerializeGltfMaterial(model->materials[i], material); - materials.push_back(material); - } - output["materials"] = materials; - } - - // MESHES - if (model->meshes.size()) { - json meshes; - for (unsigned int i = 0; i < model->meshes.size(); ++i) { - json mesh; - SerializeGltfMesh(model->meshes[i], mesh); - meshes.push_back(mesh); - } - output["meshes"] = meshes; - } - - // NODES - if (model->nodes.size()) { - json nodes; - for (unsigned int i = 0; i < model->nodes.size(); ++i) { - json node; - SerializeGltfNode(model->nodes[i], node); - nodes.push_back(node); - } - output["nodes"] = nodes; - } - - // SCENE - if (model->defaultScene > -1) { - SerializeNumberProperty("scene", model->defaultScene, output); - } - - // SCENES - if (model->scenes.size()) { - json scenes; - for (unsigned int i = 0; i < model->scenes.size(); ++i) { - json currentScene; - SerializeGltfScene(model->scenes[i], currentScene); - scenes.push_back(currentScene); - } - output["scenes"] = scenes; - } - - // SKINS - if (model->skins.size()) { - json skins; - for (unsigned int i = 0; i < model->skins.size(); ++i) { - json skin; - SerializeGltfSkin(model->skins[i], skin); - skins.push_back(skin); - } - output["skins"] = skins; - } - - // TEXTURES - if (model->textures.size()) { - json textures; - for (unsigned int i = 0; i < model->textures.size(); ++i) { - json texture; - SerializeGltfTexture(model->textures[i], texture); - textures.push_back(texture); - } - output["textures"] = textures; - } - - // SAMPLERS - if (model->samplers.size()) { - json samplers; - for (unsigned int i = 0; i < model->samplers.size(); ++i) { - json sampler; - SerializeGltfSampler(model->samplers[i], sampler); - samplers.push_back(sampler); - } - output["samplers"] = samplers; - } - - // CAMERAS - if (model->cameras.size()) { - json cameras; - for (unsigned int i = 0; i < model->cameras.size(); ++i) { - json camera; - SerializeGltfCamera(model->cameras[i], camera); - cameras.push_back(camera); - } - output["cameras"] = cameras; - } - - // EXTENSIONS - SerializeExtensionMap(model->extensions, output); - - // LIGHTS as KHR_lights_cmn - if (model->lights.size()) { - json lights; - for (unsigned int i = 0; i < model->lights.size(); ++i) { - json light; - SerializeGltfLight(model->lights[i], light); - lights.push_back(light); - } - json khr_lights_cmn; - khr_lights_cmn["lights"] = lights; - json ext_j; - - if (output.find("extensions") != output.end()) { - ext_j = output["extensions"]; - } - - ext_j["KHR_lights_punctual"] = khr_lights_cmn; - - output["extensions"] = ext_j; - } - - // EXTRAS - if (model->extras.Type() != NULL_TYPE) { - SerializeValue("extras", model->extras, output); - } - if (writeBinary) { WriteBinaryGltfFile(filename, output.dump()); } else {