mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-09 19:53:49 +00:00
Compare commits
21 Commits
int-minmax
...
fix-negati
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
688ba8a60e | ||
|
|
d07b976d59 | ||
|
|
08a7dd8dad | ||
|
|
87da1e775f | ||
|
|
151495558e | ||
|
|
84e15260ba | ||
|
|
03cdef430d | ||
|
|
fb4bd82e2c | ||
|
|
3ddf8301f6 | ||
|
|
298c37a954 | ||
|
|
b702de755f | ||
|
|
58ceed1d89 | ||
|
|
d1b6b3a5aa | ||
|
|
c56d069726 | ||
|
|
d1e32865f1 | ||
|
|
869a9d273c | ||
|
|
1208f057d6 | ||
|
|
f684dde9f1 | ||
|
|
f161782001 | ||
|
|
654bcf733c | ||
|
|
2c521b3432 |
@@ -6,7 +6,8 @@ SET(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
option(TINYGLTF_BUILD_LOADER_EXAMPLE "Build loader_example" ON)
|
||||
option(TINYGLTF_BUILD_GL_EXAMPLES "Build GL exampels(requires glfw, OpenGL, etc)" OFF)
|
||||
option(TINYGLTF_BUILD_VALIDATOR_EXAMPLE "Build validator exampe" OFF)
|
||||
option(TINYGLTF_BUILD_VALIDATOR_EXAMPLE "Build validator example" OFF)
|
||||
option(TINYGLTF_BUILD_BUILDER_EXAMPLE "Build glTF builder example" OFF)
|
||||
|
||||
if (TINYGLTF_BUILD_LOADER_EXAMPLE)
|
||||
ADD_EXECUTABLE ( loader_example
|
||||
@@ -22,6 +23,11 @@ endif (TINYGLTF_BUILD_GL_EXAMPLES)
|
||||
if (TINYGLTF_BUILD_VALIDATOR_EXAMPLE)
|
||||
ADD_SUBDIRECTORY ( examples/validator )
|
||||
endif (TINYGLTF_BUILD_VALIDATOR_EXAMPLE)
|
||||
|
||||
if (TINYGLTF_BUILD_BUILDER_EXAMPLE)
|
||||
ADD_SUBDIRECTORY ( examples/build-gltf )
|
||||
endif (TINYGLTF_BUILD_BUILDER_EXAMPLE)
|
||||
|
||||
#
|
||||
# TinuGLTF is a header-only library, so no library build. just install header files.
|
||||
#
|
||||
|
||||
@@ -77,6 +77,7 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
|
||||
* [glview](examples/glview) : Simple glTF geometry viewer.
|
||||
* [validator](examples/validator) : Simple glTF validator with JSON schema.
|
||||
* [basic](examples/basic) : Basic glTF viewer with texturing support.
|
||||
* [build-gltf](examples/build-gltf) : Build simple glTF scene from a scratch.
|
||||
|
||||
## Projects using TinyGLTF
|
||||
|
||||
@@ -90,6 +91,7 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
|
||||
* [GlslViewer](https://github.com/patriciogonzalezvivo/glslViewer) - live GLSL coding for MacOS and Linux
|
||||
* [Vulkan-Samples](https://github.com/KhronosGroup/Vulkan-Samples) - The Vulkan Samples is collection of resources to help you develop optimized Vulkan applications.
|
||||
* [TDME2](https://github.com/andreasdr/tdme2) - TDME2 - ThreeDeeMiniEngine2 is a lightweight 3D engine including tools suited for 3D game development using C++11
|
||||
* [SanityEngine](https://github.com/DethRaid/SanityEngine) - A C++/D3D12 renderer focused on the personal and proessional development of its developer
|
||||
* Your projects here! (Please send PR)
|
||||
|
||||
## TODOs
|
||||
@@ -168,6 +170,7 @@ if (!ret) {
|
||||
* `TINYGLTF_ANDROID_LOAD_FROM_ASSETS`: Load all files from packaged app assets instead of the regular file system. **Note:** You must pass a valid asset manager from your android app to `tinygltf::asset_manager` beforehand.
|
||||
* `TINYGLTF_ENABLE_DRACO`: Enable Draco compression. User must provide include path and link correspnding libraries in your project file.
|
||||
* `TINYGLTF_NO_INCLUDE_JSON `: Disable including `json.hpp` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
|
||||
* `TINYGLTF_NO_INCLUDE_RAPIDJSON `: Disable including RapidJson's header files from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
|
||||
* `TINYGLTF_NO_INCLUDE_STB_IMAGE `: Disable including `stb_image.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
|
||||
* `TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE `: Disable including `stb_image_write.h` from within `tiny_gltf.h` because it has been already included before or you want to include it using custom path before including `tiny_gltf.h`.
|
||||
* `TINYGLTF_USE_RAPIDJSON` : Use RapidJSON as a JSON parser/serializer. RapidJSON files are not included in TinyGLTF repo. Please set an include path to RapidJSON if you enable this featrure.
|
||||
|
||||
2
examples/build-gltf/Makefile
Normal file
2
examples/build-gltf/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
all:
|
||||
$(CXX) -o create_triangle_gltf -I../../ create_triangle_gltf.cpp
|
||||
121
examples/build-gltf/create_triangle_gltf.cpp
Normal file
121
examples/build-gltf/create_triangle_gltf.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
// An example of how to generate a gltf file from scratch. This example
|
||||
// was translated from the pygltlib documentation in the pypi project page,
|
||||
// which in turn is based on the Khronos Sample Models at:
|
||||
//
|
||||
// https://github.com/KhronosGroup/glTF-Sample-Models
|
||||
//
|
||||
// This example is released under the MIT license.
|
||||
//
|
||||
// 2021-02-25 Thu
|
||||
// Dov Grobgeld <dov.grobgeld@gmail.com>
|
||||
|
||||
|
||||
// Define these only in *one* .cc file.
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
|
||||
// #define TINYGLTF_NOEXCEPTION // optional. disable exception handling.
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Create a model with a single mesh and save it as a gltf file
|
||||
tinygltf::Model m;
|
||||
tinygltf::Scene scene;
|
||||
tinygltf::Mesh mesh;
|
||||
tinygltf::Primitive primitive;
|
||||
tinygltf::Node node;
|
||||
tinygltf::Buffer buffer;
|
||||
tinygltf::BufferView bufferView1;
|
||||
tinygltf::BufferView bufferView2;
|
||||
tinygltf::Accessor accessor1;
|
||||
tinygltf::Accessor accessor2;
|
||||
tinygltf::Asset asset;
|
||||
|
||||
// This is the raw data buffer.
|
||||
buffer.data = {
|
||||
// 6 bytes of indices and two bytes of padding
|
||||
0x00,0x00,0x01,0x00,0x02,0x00,0x00,0x00,
|
||||
// 36 bytes of floating point numbers
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,
|
||||
0x00,0x00,0x00,0x00};
|
||||
|
||||
// "The indices of the vertices (ELEMENT_ARRAY_BUFFER) take up 6 bytes in the
|
||||
// start of the buffer.
|
||||
bufferView1.buffer = 0;
|
||||
bufferView1.byteOffset=0;
|
||||
bufferView1.byteLength=6;
|
||||
bufferView1.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
|
||||
|
||||
// The vertices take up 36 bytes (3 vertices * 3 floating points * 4 bytes)
|
||||
// at position 8 in the buffer and are of type ARRAY_BUFFER
|
||||
bufferView2.buffer = 0;
|
||||
bufferView2.byteOffset=8;
|
||||
bufferView2.byteLength=36;
|
||||
bufferView2.target = TINYGLTF_TARGET_ARRAY_BUFFER;
|
||||
|
||||
// Describe the layout of bufferView1, the indices of the vertices
|
||||
accessor1.bufferView = 0;
|
||||
accessor1.byteOffset = 0;
|
||||
accessor1.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
|
||||
accessor1.count = 3;
|
||||
accessor1.type = TINYGLTF_TYPE_SCALAR;
|
||||
accessor1.maxValues.push_back(2);
|
||||
accessor1.minValues.push_back(0);
|
||||
|
||||
// Describe the layout of bufferView2, the vertices themself
|
||||
accessor2.bufferView = 1;
|
||||
accessor2.byteOffset = 0;
|
||||
accessor2.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
||||
accessor2.count = 3;
|
||||
accessor2.type = TINYGLTF_TYPE_VEC3;
|
||||
accessor2.maxValues = {1.0, 1.0, 0.0};
|
||||
accessor2.minValues = {0.0, 0.0, 0.0};
|
||||
|
||||
// Build the mesh primitive and add it to the mesh
|
||||
primitive.indices = 0; // The index of the accessor for the vertex indices
|
||||
primitive.attributes["POSITION"] = 1; // The index of the accessor for positions
|
||||
primitive.material = 0;
|
||||
primitive.mode = TINYGLTF_MODE_TRIANGLES;
|
||||
mesh.primitives.push_back(primitive);
|
||||
|
||||
// Other tie ups
|
||||
node.mesh = 0;
|
||||
scene.nodes.push_back(0); // Default scene
|
||||
|
||||
// Define the asset. The version is required
|
||||
asset.version = "2.0";
|
||||
asset.generator = "tinygltf";
|
||||
|
||||
// Now all that remains is to tie back all the loose objects into the
|
||||
// our single model.
|
||||
m.scenes.push_back(scene);
|
||||
m.meshes.push_back(mesh);
|
||||
m.nodes.push_back(node);
|
||||
m.buffers.push_back(buffer);
|
||||
m.bufferViews.push_back(bufferView1);
|
||||
m.bufferViews.push_back(bufferView2);
|
||||
m.accessors.push_back(accessor1);
|
||||
m.accessors.push_back(accessor2);
|
||||
m.asset = asset;
|
||||
|
||||
// Create a simple material
|
||||
tinygltf::Material mat;
|
||||
mat.pbrMetallicRoughness.baseColorFactor = {1.0f, 0.9f, 0.9f, 1.0f};
|
||||
mat.doubleSided = true;
|
||||
m.materials.push_back(mat);
|
||||
|
||||
// Save it to a file
|
||||
tinygltf::TinyGLTF gltf;
|
||||
gltf.WriteGltfSceneToFile(&m, "triangle.gltf",
|
||||
true, // embedImages
|
||||
true, // embedBuffers
|
||||
true, // pretty print
|
||||
false); // write binary
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -610,7 +610,7 @@ void Viewer::buildSamplerDescs() {
|
||||
|
||||
samplerDesc.AddressU = toTextureAddressMode(glTFSampler.wrapS);
|
||||
samplerDesc.AddressV = toTextureAddressMode(glTFSampler.wrapT);
|
||||
samplerDesc.AddressW = toTextureAddressMode(glTFSampler.wrapR);
|
||||
samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
samplerDesc.MaxLOD = 256;
|
||||
|
||||
samplerDescs_.push_back(samplerDesc);
|
||||
@@ -1289,4 +1289,4 @@ void Viewer::drawNode(uint64_t nodeIndex) {
|
||||
for (auto childNodeIndex : glTFNode.children) {
|
||||
drawNode(childNodeIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
267
models/regression/unassigned-skeleton.gltf
Normal file
267
models/regression/unassigned-skeleton.gltf
Normal file
File diff suppressed because one or more lines are too long
@@ -432,11 +432,40 @@ TEST_CASE("serialize-empty-material", "[issue-294]") {
|
||||
nlohmann::json j = nlohmann::json::parse(os.str());
|
||||
|
||||
REQUIRE(1 == j["materials"].size());
|
||||
REQUIRE(j["asset"].is_null());
|
||||
REQUIRE(j["materials"][0].is_object());
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("empty-skeleton-id", "[issue-321]") {
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF ctx;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/regression/unassigned-skeleton.gltf");
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
REQUIRE(true == ret);
|
||||
|
||||
REQUIRE(model.skins.size() == 1);
|
||||
REQUIRE(model.skins[0].skeleton == -1); // unassigned
|
||||
|
||||
std::stringstream os;
|
||||
|
||||
ctx.WriteGltfSceneToStream(&model, os, false, false);
|
||||
|
||||
// use nlohmann json
|
||||
nlohmann::json j = nlohmann::json::parse(os.str());
|
||||
|
||||
// Ensure `skeleton` property is not written to .gltf(was serialized as -1)
|
||||
REQUIRE(1 == j["skins"].size());
|
||||
REQUIRE(j["skins"][0].is_object());
|
||||
REQUIRE(j["skins"][0].count("skeleton") == 0);
|
||||
|
||||
}
|
||||
|
||||
#ifndef TINYGLTF_NO_FS
|
||||
TEST_CASE("expandpath-utf-8", "[pr-226]") {
|
||||
|
||||
|
||||
53
tiny_gltf.h
53
tiny_gltf.h
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 - 2020 Syoyo Fujita, Aurélien Chatelain and many
|
||||
// Copyright (c) 2015 - Present Syoyo Fujita, Aurélien Chatelain and many
|
||||
// contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -298,7 +298,7 @@ class Value {
|
||||
|
||||
DEFAULT_METHODS(Value)
|
||||
|
||||
char Type() const { return static_cast<const char>(type_); }
|
||||
char Type() const { return static_cast<char>(type_); }
|
||||
|
||||
bool IsBool() const { return (type_ == BOOL_TYPE); }
|
||||
|
||||
@@ -614,7 +614,7 @@ struct Sampler {
|
||||
int wrapT =
|
||||
TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
|
||||
// "REPEAT"], default "REPEAT"
|
||||
int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension
|
||||
//int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used.
|
||||
|
||||
Value extras;
|
||||
ExtensionMap extensions;
|
||||
@@ -627,8 +627,7 @@ struct Sampler {
|
||||
: minFilter(-1),
|
||||
magFilter(-1),
|
||||
wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
|
||||
wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT),
|
||||
wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
|
||||
wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
|
||||
DEFAULT_METHODS(Sampler)
|
||||
bool operator==(const Sampler &) const;
|
||||
};
|
||||
@@ -1072,7 +1071,7 @@ struct Buffer {
|
||||
};
|
||||
|
||||
struct Asset {
|
||||
std::string version; // required
|
||||
std::string version = "2.0"; // required
|
||||
std::string generator;
|
||||
std::string minVersion;
|
||||
std::string copyright;
|
||||
@@ -1260,7 +1259,7 @@ bool FileExists(const std::string &abs_filename, void *);
|
||||
|
||||
///
|
||||
/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
|
||||
/// `C:\Users\tinygltf\AppData`)
|
||||
/// `C:\\Users\\tinygltf\\AppData`)
|
||||
///
|
||||
/// @param[in] filepath File path string. Assume UTF-8
|
||||
/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
|
||||
@@ -1539,6 +1538,7 @@ class TinyGLTF {
|
||||
#ifndef TINYGLTF_USE_RAPIDJSON
|
||||
#include "json.hpp"
|
||||
#else
|
||||
#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
|
||||
#include "document.h"
|
||||
#include "prettywriter.h"
|
||||
#include "rapidjson.h"
|
||||
@@ -1546,6 +1546,7 @@ class TinyGLTF {
|
||||
#include "writer.h"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef TINYGLTF_ENABLE_DRACO
|
||||
#include "draco/compression/decode.h"
|
||||
@@ -1933,8 +1934,9 @@ bool Sampler::operator==(const Sampler &other) const {
|
||||
return this->extensions == other.extensions && 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;
|
||||
|
||||
//this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
|
||||
}
|
||||
bool Scene::operator==(const Scene &other) const {
|
||||
return this->extensions == other.extensions && this->extras == other.extras &&
|
||||
@@ -4470,6 +4472,7 @@ static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
|
||||
static bool ParseDracoExtension(Primitive *primitive, Model *model,
|
||||
std::string *err,
|
||||
const Value &dracoExtensionValue) {
|
||||
(void)err;
|
||||
auto bufferViewValue = dracoExtensionValue.Get("bufferView");
|
||||
if (!bufferViewValue.IsInt()) return false;
|
||||
auto attributesValue = dracoExtensionValue.Get("attributes");
|
||||
@@ -4530,7 +4533,6 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model,
|
||||
|
||||
int dracoAttributeIndex = attribute.second.Get<int>();
|
||||
const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
|
||||
const auto pBuffer = pAttribute->buffer();
|
||||
const auto componentType =
|
||||
model->accessors[primitiveAttribute->second].componentType;
|
||||
|
||||
@@ -5089,12 +5091,12 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
|
||||
int magFilter = -1;
|
||||
int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
|
||||
int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
|
||||
int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
|
||||
//int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
|
||||
ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
|
||||
ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
|
||||
ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
|
||||
ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
|
||||
ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
|
||||
//ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
|
||||
|
||||
// TODO(syoyo): Check the value is alloed one.
|
||||
// (e.g. we allow 9728(NEAREST), but don't allow 9727)
|
||||
@@ -5103,7 +5105,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
|
||||
sampler->magFilter = magFilter;
|
||||
sampler->wrapS = wrapS;
|
||||
sampler->wrapT = wrapT;
|
||||
sampler->wrapR = wrapR;
|
||||
//sampler->wrapR = wrapR;
|
||||
|
||||
ParseExtensionsProperty(&(sampler->extensions), err, o);
|
||||
ParseExtrasProperty(&(sampler->extras), o);
|
||||
@@ -6760,10 +6762,15 @@ static void SerializeGltfAsset(Asset &asset, json &o) {
|
||||
SerializeStringProperty("copyright", asset.copyright, o);
|
||||
}
|
||||
|
||||
if (!asset.version.empty()) {
|
||||
SerializeStringProperty("version", asset.version, o);
|
||||
if (asset.version.empty()) {
|
||||
// Just in case
|
||||
// `version` must be defined
|
||||
asset.version = "2.0";
|
||||
}
|
||||
|
||||
// TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
|
||||
SerializeStringProperty("version", asset.version, o);
|
||||
|
||||
if (asset.extras.Keys().size()) {
|
||||
SerializeValue("extras", asset.extras, o);
|
||||
}
|
||||
@@ -7161,7 +7168,7 @@ static void SerializeGltfSampler(Sampler &sampler, json &o) {
|
||||
if (sampler.minFilter != -1) {
|
||||
SerializeNumberProperty("minFilter", sampler.minFilter, o);
|
||||
}
|
||||
SerializeNumberProperty("wrapR", sampler.wrapR, o);
|
||||
//SerializeNumberProperty("wrapR", sampler.wrapR, o);
|
||||
SerializeNumberProperty("wrapS", sampler.wrapS, o);
|
||||
SerializeNumberProperty("wrapT", sampler.wrapT, o);
|
||||
|
||||
@@ -7236,11 +7243,17 @@ static void SerializeGltfScene(Scene &scene, json &o) {
|
||||
}
|
||||
|
||||
static void SerializeGltfSkin(Skin &skin, json &o) {
|
||||
if (skin.inverseBindMatrices != -1)
|
||||
SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
|
||||
|
||||
// required
|
||||
SerializeNumberArrayProperty<int>("joints", skin.joints, o);
|
||||
SerializeNumberProperty("skeleton", skin.skeleton, o);
|
||||
|
||||
if (skin.inverseBindMatrices >= 0) {
|
||||
SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
|
||||
}
|
||||
|
||||
if (skin.skeleton >= 0) {
|
||||
SerializeNumberProperty("skeleton", skin.skeleton, o);
|
||||
}
|
||||
|
||||
if (skin.name.size()) {
|
||||
SerializeStringProperty("name", skin.name, o);
|
||||
}
|
||||
@@ -7471,7 +7484,7 @@ static void SerializeGltfModel(Model *model, json &o) {
|
||||
}
|
||||
|
||||
// Extensions used
|
||||
if (model->extensionsUsed.size()) {
|
||||
if (extensionsUsed.size()) {
|
||||
SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user