in shared/ext/tiny_gltf.h [5243:5907]
bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
const char *json_str,
unsigned int json_str_length,
const std::string &base_dir,
unsigned int check_sections) {
if (json_str_length < 4) {
if (err) {
(*err) = "JSON string too short.\n";
}
return false;
}
JsonDocument v;
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
defined(_CPPUNWIND)) && \
!defined(TINYGLTF_NOEXCEPTION)
try {
JsonParse(v, json_str, json_str_length, true);
} catch (const std::exception &e) {
if (err) {
(*err) = e.what();
}
return false;
}
#else
{
JsonParse(v, json_str, json_str_length);
if (!IsObject(v)) {
// Assume parsing was failed.
if (err) {
(*err) = "Failed to parse JSON object\n";
}
return false;
}
}
#endif
if (!IsObject(v)) {
// root is not an object.
if (err) {
(*err) = "Root element is not a JSON object\n";
}
return false;
}
{
bool version_found = false;
json_const_iterator it;
if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
auto &itObj = GetValue(it);
json_const_iterator version_it;
std::string versionStr;
if (FindMember(itObj, "version", version_it) &&
GetString(GetValue(version_it), versionStr)) {
version_found = true;
}
}
if (version_found) {
// OK
} else if (check_sections & REQUIRE_VERSION) {
if (err) {
(*err) += "\"asset\" object not found in .gltf or not an object type\n";
}
return false;
}
}
// scene is not mandatory.
// FIXME Maybe a better way to handle it than removing the code
auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
json_const_iterator it;
return FindMember(_v, name, it) && IsArray(GetValue(it));
};
{
if ((check_sections & REQUIRE_SCENES) &&
!IsArrayMemberPresent(v, "scenes")) {
if (err) {
(*err) += "\"scenes\" object not found in .gltf or not an array type\n";
}
return false;
}
}
{
if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
if (err) {
(*err) += "\"nodes\" object not found in .gltf\n";
}
return false;
}
}
{
if ((check_sections & REQUIRE_ACCESSORS) &&
!IsArrayMemberPresent(v, "accessors")) {
if (err) {
(*err) += "\"accessors\" object not found in .gltf\n";
}
return false;
}
}
{
if ((check_sections & REQUIRE_BUFFERS) &&
!IsArrayMemberPresent(v, "buffers")) {
if (err) {
(*err) += "\"buffers\" object not found in .gltf\n";
}
return false;
}
}
{
if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
!IsArrayMemberPresent(v, "bufferViews")) {
if (err) {
(*err) += "\"bufferViews\" object not found in .gltf\n";
}
return false;
}
}
model->buffers.clear();
model->bufferViews.clear();
model->accessors.clear();
model->meshes.clear();
model->cameras.clear();
model->nodes.clear();
model->extensionsUsed.clear();
model->extensionsRequired.clear();
model->extensions.clear();
model->defaultScene = -1;
// 1. Parse Asset
{
json_const_iterator it;
if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
const json &root = GetValue(it);
ParseAsset(&model->asset, err, root,
store_original_json_for_extras_and_extensions_);
}
}
#ifdef TINYGLTF_USE_CPP14
auto ForEachInArray = [](const json &_v, const char *member,
const auto &cb) -> bool
#else
// The std::function<> implementation can be less efficient because it will
// allocate heap when the size of the captured lambda is above 16 bytes with
// clang and gcc, but it does not require C++14.
auto ForEachInArray = [](const json &_v, const char *member,
const std::function<bool(const json &)> &cb) -> bool
#endif
{
json_const_iterator itm;
if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
const json &root = GetValue(itm);
auto it = ArrayBegin(root);
auto end = ArrayEnd(root);
for (; it != end; ++it) {
if (!cb(*it)) return false;
}
}
return true;
};
// 2. Parse extensionUsed
{
ForEachInArray(v, "extensionsUsed", [&](const json &o) {
std::string str;
GetString(o, str);
model->extensionsUsed.emplace_back(std::move(str));
return true;
});
}
{
ForEachInArray(v, "extensionsRequired", [&](const json &o) {
std::string str;
GetString(o, str);
model->extensionsRequired.emplace_back(std::move(str));
return true;
});
}
// 3. Parse Buffer
{
bool success = ForEachInArray(v, "buffers", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`buffers' does not contain an JSON object.";
}
return false;
}
Buffer buffer;
if (!ParseBuffer(&buffer, err, o,
store_original_json_for_extras_and_extensions_, &fs,
base_dir, is_binary_, bin_data_, bin_size_)) {
return false;
}
model->buffers.emplace_back(std::move(buffer));
return true;
});
if (!success) {
return false;
}
}
// 4. Parse BufferView
{
bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`bufferViews' does not contain an JSON object.";
}
return false;
}
BufferView bufferView;
if (!ParseBufferView(&bufferView, err, o,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->bufferViews.emplace_back(std::move(bufferView));
return true;
});
if (!success) {
return false;
}
}
// 5. Parse Accessor
{
bool success = ForEachInArray(v, "accessors", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`accessors' does not contain an JSON object.";
}
return false;
}
Accessor accessor;
if (!ParseAccessor(&accessor, err, o,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->accessors.emplace_back(std::move(accessor));
return true;
});
if (!success) {
return false;
}
}
// 6. Parse Mesh
{
bool success = ForEachInArray(v, "meshes", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`meshes' does not contain an JSON object.";
}
return false;
}
Mesh mesh;
if (!ParseMesh(&mesh, model, err, o,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->meshes.emplace_back(std::move(mesh));
return true;
});
if (!success) {
return false;
}
}
// Assign missing bufferView target types
// - Look for missing Mesh indices
// - Look for missing Mesh attributes
for (auto &mesh : model->meshes) {
for (auto &primitive : mesh.primitives) {
if (primitive.indices >
-1) // has indices from parsing step, must be Element Array Buffer
{
if (size_t(primitive.indices) >= model->accessors.size()) {
if (err) {
(*err) += "primitive indices accessor out of bounds";
}
return false;
}
auto bufferView =
model->accessors[size_t(primitive.indices)].bufferView;
if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
if (err) {
(*err) += "accessor[" + std::to_string(primitive.indices) +
"] invalid bufferView";
}
return false;
}
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
// we could optionally check if acessors' bufferView type is Scalar, as
// it should be
}
for (auto &attribute : primitive.attributes) {
model->bufferViews[size_t(model->accessors[size_t(attribute.second)].bufferView)]
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
}
}
}
// 7. Parse Node
{
bool success = ForEachInArray(v, "nodes", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`nodes' does not contain an JSON object.";
}
return false;
}
Node node;
if (!ParseNode(&node, err, o,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->nodes.emplace_back(std::move(node));
return true;
});
if (!success) {
return false;
}
}
// 8. Parse scenes.
{
bool success = ForEachInArray(v, "scenes", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`scenes' does not contain an JSON object.";
}
return false;
}
std::vector<int> nodes;
ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
Scene scene;
scene.nodes = std::move(nodes);
ParseStringProperty(&scene.name, err, o, "name", false);
ParseExtensionsProperty(&scene.extensions, err, o);
ParseExtrasProperty(&scene.extras, o);
if (store_original_json_for_extras_and_extensions_) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
model->extensions_json_string = JsonToString(GetValue(it));
}
}
{
json_const_iterator it;
if (FindMember(o, "extras", it)) {
model->extras_json_string = JsonToString(GetValue(it));
}
}
}
model->scenes.emplace_back(std::move(scene));
return true;
});
if (!success) {
return false;
}
}
// 9. Parse default scenes.
{
json_const_iterator rootIt;
int iVal;
if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
model->defaultScene = iVal;
}
}
// 10. Parse Material
{
bool success = ForEachInArray(v, "materials", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`materials' does not contain an JSON object.";
}
return false;
}
Material material;
ParseStringProperty(&material.name, err, o, "name", false);
if (!ParseMaterial(&material, err, o,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->materials.emplace_back(std::move(material));
return true;
});
if (!success) {
return false;
}
}
// 11. Parse Image
{
int idx = 0;
bool success = ForEachInArray(v, "images", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
}
return false;
}
Image image;
if (!ParseImage(&image, idx, err, warn, o,
store_original_json_for_extras_and_extensions_, base_dir,
&fs, &this->LoadImageData, load_image_user_data_)) {
return false;
}
if (image.bufferView != -1) {
// Load image from the buffer view.
if (size_t(image.bufferView) >= model->bufferViews.size()) {
if (err) {
std::stringstream ss;
ss << "image[" << idx << "] bufferView \"" << image.bufferView
<< "\" not found in the scene." << std::endl;
(*err) += ss.str();
}
return false;
}
const BufferView &bufferView =
model->bufferViews[size_t(image.bufferView)];
if (size_t(bufferView.buffer) >= model->buffers.size()) {
if (err) {
std::stringstream ss;
ss << "image[" << idx << "] buffer \"" << bufferView.buffer
<< "\" not found in the scene." << std::endl;
(*err) += ss.str();
}
return false;
}
const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
if (*LoadImageData == nullptr) {
if (err) {
(*err) += "No LoadImageData callback specified.\n";
}
return false;
}
bool ret = LoadImageData(
&image, idx, err, warn, image.width, image.height,
&buffer.data[bufferView.byteOffset],
static_cast<int>(bufferView.byteLength), load_image_user_data_);
if (!ret) {
return false;
}
}
model->images.emplace_back(std::move(image));
++idx;
return true;
});
if (!success) {
return false;
}
}
// 12. Parse Texture
{
bool success = ForEachInArray(v, "textures", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`textures' does not contain an JSON object.";
}
return false;
}
Texture texture;
if (!ParseTexture(&texture, err, o,
store_original_json_for_extras_and_extensions_,
base_dir)) {
return false;
}
model->textures.emplace_back(std::move(texture));
return true;
});
if (!success) {
return false;
}
}
// 13. Parse Animation
{
bool success = ForEachInArray(v, "animations", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`animations' does not contain an JSON object.";
}
return false;
}
Animation animation;
if (!ParseAnimation(&animation, err, o,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->animations.emplace_back(std::move(animation));
return true;
});
if (!success) {
return false;
}
}
// 14. Parse Skin
{
bool success = ForEachInArray(v, "skins", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`skins' does not contain an JSON object.";
}
return false;
}
Skin skin;
if (!ParseSkin(&skin, err, o,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->skins.emplace_back(std::move(skin));
return true;
});
if (!success) {
return false;
}
}
// 15. Parse Sampler
{
bool success = ForEachInArray(v, "samplers", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`samplers' does not contain an JSON object.";
}
return false;
}
Sampler sampler;
if (!ParseSampler(&sampler, err, o,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->samplers.emplace_back(std::move(sampler));
return true;
});
if (!success) {
return false;
}
}
// 16. Parse Camera
{
bool success = ForEachInArray(v, "cameras", [&](const json &o) {
if (!IsObject(o)) {
if (err) {
(*err) += "`cameras' does not contain an JSON object.";
}
return false;
}
Camera camera;
if (!ParseCamera(&camera, err, o,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->cameras.emplace_back(std::move(camera));
return true;
});
if (!success) {
return false;
}
}
// 17. Parse Extensions
ParseExtensionsProperty(&model->extensions, err, v);
// 18. Specific extension implementations
{
json_const_iterator rootIt;
if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
const json &root = GetValue(rootIt);
json_const_iterator it(ObjectBegin(root));
json_const_iterator itEnd(ObjectEnd(root));
for (; it != itEnd; ++it) {
// parse KHR_lights_punctual extension
std::string key(GetKey(it));
if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
const json &object = GetValue(it);
json_const_iterator itLight;
if (FindMember(object, "lights", itLight)) {
const json &lights = GetValue(itLight);
if (!IsArray(lights)) {
continue;
}
auto arrayIt(ArrayBegin(lights));
auto arrayItEnd(ArrayEnd(lights));
for (; arrayIt != arrayItEnd; ++arrayIt) {
Light light;
if (!ParseLight(&light, err, *arrayIt,
store_original_json_for_extras_and_extensions_)) {
return false;
}
model->lights.emplace_back(std::move(light));
}
}
}
}
}
}
// 19. Parse Extras
ParseExtrasProperty(&model->extras, v);
if (store_original_json_for_extras_and_extensions_) {
model->extras_json_string = JsonToString(v["extras"]);
model->extensions_json_string = JsonToString(v["extensions"]);
}
return true;
}