From 6e3d666cf376c81929643c9455688d6aba43e10a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 24 Aug 2023 13:25:34 +1000 Subject: [PATCH] When in permissive mode, handle the situation where the recorded component type does not match the required type for the actual number of stored points This situation arises when decoding certain malformed files, most notably it's seen in glb tiles from Google Earth's 3d tileset. It's a port of the workaround used by Cesium native here: https://github.com/CesiumGS/cesium-native/blob/d9172461e2d53f19d050ebc39f24a4f4ef5b9224/CesiumGltfReader/src/decodeDraco.cpp#L101 --- tiny_gltf.h | 57 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 3e807cc..d6ea042 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -4873,8 +4873,9 @@ static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh, } static bool ParseDracoExtension(Primitive *primitive, Model *model, - std::string *err, - const Value &dracoExtensionValue) { + std::string *err, std::string *warn, + const Value &dracoExtensionValue, + ParseStrictness strictness) { (void)err; auto bufferViewValue = dracoExtensionValue.Get("bufferView"); if (!bufferViewValue.IsInt()) return false; @@ -4906,6 +4907,33 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, // create new bufferView for indices if (primitive->indices >= 0) { + if (strictness == ParseStrictness::PERMISSIVE) { + const draco::PointIndex::ValueType numPoint = mesh->num_points(); + // handle the situation where the stored component type does not match the + // required type for the actual number of stored points + int supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + if (numPoint < static_cast( + std::numeric_limits::max())) { + supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + } else if ( + numPoint < static_cast( + std::numeric_limits::max())) { + supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + } else { + supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT; + } + + if (supposedComponentType > model->accessors[primitive->indices].componentType) { + model->accessors[primitive->indices].componentType = supposedComponentType; + if (warn) { + (*warn) += + "GLTF component type " + std::to_string(model->accessors[primitive->indices].componentType) + + " is not sufficient for number of stored points," + " treating as " + std::to_string(supposedComponentType) + "\n"; + } + } + } + int32_t componentSize = GetComponentSizeInBytes( model->accessors[primitive->indices].componentType); Buffer decodedIndexBuffer; @@ -4971,9 +4999,11 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, } #endif -static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, +static bool ParsePrimitive(Primitive *primitive, Model *model, + std::string *err, std::string *warn, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { + bool store_original_json_for_extras_and_extensions, + ParseStrictness strictness) { int material = -1; ParseIntegerProperty(&material, err, o, "material", false); primitive->material = material; @@ -5022,18 +5052,21 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, auto dracoExtension = primitive->extensions.find("KHR_draco_mesh_compression"); if (dracoExtension != primitive->extensions.end()) { - ParseDracoExtension(primitive, model, err, dracoExtension->second); + ParseDracoExtension(primitive, model, err, warn, dracoExtension->second, strictness); } #else (void)model; + (void)warn; #endif return true; } -static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, +static bool ParseMesh(Mesh *mesh, Model *model, + std::string *err, std::string *warn, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { + bool store_original_json_for_extras_and_extensions, + ParseStrictness strictness) { ParseStringProperty(&mesh->name, err, o, "name", false); mesh->primitives.clear(); @@ -5046,8 +5079,9 @@ static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, detail::ArrayBegin(detail::GetValue(primObject)); i != primEnd; ++i) { Primitive primitive; - if (ParsePrimitive(&primitive, model, err, *i, - store_original_json_for_extras_and_extensions)) { + if (ParsePrimitive(&primitive, model, err, warn, *i, + store_original_json_for_extras_and_extensions, + strictness)) { // Only add the primitive if the parsing succeeds. mesh->primitives.emplace_back(std::move(primitive)); } @@ -6078,8 +6112,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, return false; } Mesh mesh; - if (!ParseMesh(&mesh, model, err, o, - store_original_json_for_extras_and_extensions_)) { + if (!ParseMesh(&mesh, model, err, warn, o, + store_original_json_for_extras_and_extensions_, + strictness_)) { return false; }