Compare commits

...

26 Commits

Author SHA1 Message Date
Syoyo Fujita
5e8a7fd602 Merge pull request #456 from haroonq/patch-1
Allow BufferView indices to be unspecified.
2023-10-10 21:24:51 +09:00
haroonq
8098a9e8ed Allow BufferView indices to be unspecified.
Allow BufferView indices for element array buffers to be unspecified to support some extensions.

Note that this is similar to how invalid array buffers are handled in order to support, for example, sparse morph targets.
2023-10-09 10:30:25 +00:00
Syoyo Fujita
e0b393c695 Merge pull request #455 from nyalldawson/fix_message
Fix incorrect component type shown in warning message
2023-09-12 20:28:35 +09:00
Nyall Dawson
c35819f0b7 Fix incorrect component type shown in warning message 2023-09-12 08:46:14 +10:00
Syoyo Fujita
4b9cfc8c1e Remove unused code. Fixes #454 2023-09-07 22:03:46 +09:00
Syoyo Fujita
c40c9c223e Merge pull request #453 from nyalldawson/fix_draco
Fix build with draco
2023-09-07 21:35:55 +09:00
Nyall Dawson
0067b4d941 Fix build with draco 2023-09-07 08:20:28 +10:00
Syoyo Fujita
35735bb686 Merge pull request #452 from nyalldawson/permissive_align
Relax bin chunk end alignment check in permissive mode
2023-09-07 06:46:11 +09:00
Nyall Dawson
4d119d7268 Relax bin chunk end alignment check in permissive mode 2023-09-07 07:08:26 +10:00
Syoyo Fujita
fe6a18269f Merge pull request #450 from nyalldawson/fix_win
Fix msvc build -- STRICT is a msvc macro name
2023-09-07 05:59:12 +09:00
Nyall Dawson
bbc1eaeecf Fix msvc build -- STRICT is a msvc macro name 2023-09-07 06:28:37 +10:00
Syoyo Fujita
62cc92566e Merge pull request #451 from nyalldawson/mingw
Add mingw msys2 workflow
2023-09-06 20:55:14 +09:00
Nyall Dawson
b2aca1ecef Add mingw msys2 workflow 2023-09-06 08:26:34 +10:00
Nyall Dawson
5a7b8278cd Fix warning when building without draco support 2023-09-03 09:04:56 +10:00
Syoyo Fujita
3d445cc65d Merge pull request #449 from emimvi/consistent_byteOffset
Always use size_t for byte offsets
2023-09-03 02:20:35 +09:00
Syoyo Fujita
51530ee500 Merge pull request #447 from nyalldawson/draco_fix
Handle the situation where the recorded component type does not match the required type for the actual number of stored points
2023-09-03 02:17:12 +09:00
emimvi
759976e087 Consistently use size_t for all byteOffset's 2023-09-02 09:39:53 +02:00
Nyall Dawson
6e3d666cf3 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:

d9172461e2/CesiumGltfReader/src/decodeDraco.cpp (L101)
2023-09-02 10:16:04 +10:00
emimvi
bf7120f8a0 Serialize byteOffset as size_t, avoiding cast
Fixes silently writing an overflowed int in the output file.
2023-09-02 00:11:41 +02:00
Syoyo Fujita
acf1e8a2b1 Merge pull request #445 from nyalldawson/permissive
Be tolerant when encountering emissiveFactor with array length 4
2023-09-01 23:10:29 +09:00
Nyall Dawson
8c85d5e387 Add method to set parsing strictness 2023-08-28 12:56:09 +10:00
Nyall Dawson
02e8b8da1e Raise a warning when encountering emissiveFactor with array length
of 4 instead of aborting the model loading
2023-08-28 12:46:44 +10:00
Syoyo Fujita
ddc76f7724 Merge pull request #446 from nyalldawson/fix_error
Fix misleading error message
2023-08-24 22:11:50 +09:00
Nyall Dawson
8e9aadf569 Fix misleading error message
Avoids a confusing "Must have 4 bytes or more bytes, but got 4."
error.
2023-08-24 11:48:09 +10:00
Syoyo Fujita
0eaa23fbfc Merge pull request #442 from turanszkij/release-1
added project link to readme
2023-08-07 18:32:30 +09:00
Turánszki János
2a5dc852cc added project link to readme 2023-08-07 06:59:35 +02:00
3 changed files with 156 additions and 41 deletions

45
.github/workflows/mingw-w64-msys2.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: MSYS2 MinGW-w64 Windows 64bit Build
on:
push:
branches:
- release
- devel
paths:
- 'tiny_gltf.*'
- 'CMakeLists.txt'
- '.github/workflows/mingw-w64-msys2.yml'
pull_request:
workflow_dispatch:
jobs:
mingw-w64-msys2-build:
name: MSYS2 MinGW-w64 Windows Build
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v3
- name: Install core & build dependencies
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
install: base-devel
pacboy: >-
cc:p cmake:p ninja:p
update: true
release: false
- name: Configure
run: |
cmake \
-G"Ninja" \
-S . \
-B build
- name: Build
run: |
cmake --build build

View File

@@ -109,6 +109,7 @@ WASI build example is located in [wasm](wasm) .
* [SanityEngine](https://github.com/DethRaid/SanityEngine) - A C++/D3D12 renderer focused on the personal and professional development of its developer
* [Open3D](http://www.open3d.org/) - A Modern Library for 3D Data Processing
* [Supernova Engine](https://github.com/supernovaengine/supernova) - Game engine for 2D and 3D projects with Lua or C++ in data oriented design.
* [Wicked Engine<img src="https://github.com/turanszkij/WickedEngine/blob/master/Content/logo_small.png" width="28px" align="center"/>](https://github.com/turanszkij/WickedEngine) - 3D engine with modern graphics
* Your projects here! (Please send PR)
## TODOs

View File

@@ -195,6 +195,11 @@ typedef enum {
OBJECT_TYPE
} Type;
typedef enum {
Permissive,
Strict
} ParseStrictness;
static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
return 1;
@@ -832,7 +837,7 @@ struct Accessor {
int count;
bool isSparse;
struct {
int byteOffset;
size_t byteOffset;
int bufferView;
int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
Value extras;
@@ -842,7 +847,7 @@ struct Accessor {
} indices;
struct {
int bufferView;
int byteOffset;
size_t byteOffset;
Value extras;
ExtensionMap extensions;
std::string extras_json_string;
@@ -1463,6 +1468,11 @@ class TinyGLTF {
bool embedImages, bool embedBuffers,
bool prettyPrint, bool writeBinary);
///
/// Sets the parsing strictness.
///
void SetParseStrictness(ParseStrictness strictness);
///
/// Set callback to use for loading image data
///
@@ -1552,6 +1562,8 @@ class TinyGLTF {
size_t bin_size_ = 0;
bool is_binary_ = false;
ParseStrictness strictness_ = ParseStrictness::Strict;
bool serialize_default_values_ = false; ///< Serialize default values?
bool store_original_json_for_extras_and_extensions_ = false;
@@ -2553,6 +2565,10 @@ static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
return true;
}
void TinyGLTF::SetParseStrictness(ParseStrictness strictness) {
strictness_ = strictness;
}
void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
LoadImageData = func;
load_image_user_data_ = user_data;
@@ -4632,24 +4648,26 @@ static bool ParseSparseAccessor(
const detail::json &indices_obj = detail::GetValue(indices_iterator);
const detail::json &values_obj = detail::GetValue(values_iterator);
int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
int indices_buffer_view = 0, component_type = 0;
size_t indices_byte_offset = 0;
if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
"bufferView", true, "SparseAccessor")) {
return false;
}
ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
ParseUnsignedProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
false);
if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
true, "SparseAccessor")) {
return false;
}
int values_buffer_view = 0, values_byte_offset = 0;
int values_buffer_view = 0;
size_t values_byte_offset = 0;
if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
true, "SparseAccessor")) {
return false;
}
ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
ParseUnsignedProperty(&values_byte_offset, err, values_obj, "byteOffset",
false);
sparse->count = count;
@@ -4857,8 +4875,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;
@@ -4890,6 +4909,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<draco::PointIndex::ValueType>(
std::numeric_limits<uint8_t>::max())) {
supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
} else if (
numPoint < static_cast<draco::PointIndex::ValueType>(
std::numeric_limits<uint16_t>::max())) {
supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
} else {
supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
}
if (supposedComponentType > model->accessors[primitive->indices].componentType) {
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";
}
model->accessors[primitive->indices].componentType = supposedComponentType;
}
}
int32_t componentSize = GetComponentSizeInBytes(
model->accessors[primitive->indices].componentType);
Buffer decodedIndexBuffer;
@@ -4955,9 +5001,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;
@@ -5006,18 +5054,22 @@ 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;
(void)strictness;
#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();
@@ -5030,8 +5082,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));
}
@@ -5192,15 +5245,24 @@ static bool ParsePbrMetallicRoughness(
return true;
}
static bool ParseMaterial(Material *material, std::string *err,
static bool ParseMaterial(Material *material, 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(&material->name, err, o, "name", /* required */ false);
if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
"emissiveFactor",
/* required */ false)) {
if (material->emissiveFactor.size() != 3) {
if (strictness==ParseStrictness::Permissive && material->emissiveFactor.size() == 4) {
if (warn) {
(*warn) +=
"Array length of `emissiveFactor` parameter in "
"material must be 3, but got 4\n";
}
material->emissiveFactor.resize(3);
}
else if (material->emissiveFactor.size() != 3) {
if (err) {
(*err) +=
"Array length of `emissiveFactor` parameter in "
@@ -6053,8 +6115,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;
}
@@ -6082,20 +6145,22 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
return false;
}
auto bufferView =
const auto bufferView =
model->accessors[size_t(primitive.indices)].bufferView;
if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
if (bufferView < 0) {
// skip, bufferView could be null(-1) for certain extensions
} else if (size_t(bufferView) >= model->bufferViews.size()) {
if (err) {
(*err) += "accessor[" + std::to_string(primitive.indices) +
"] invalid bufferView";
}
return false;
} else {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
// we could optionally check if accessors' bufferView type is Scalar, as
// it should be
}
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
// we could optionally check if accessors' bufferView type is Scalar, as
// it should be
}
for (auto &attribute : primitive.attributes) {
@@ -6198,8 +6263,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Material material;
ParseStringProperty(&material.name, err, o, "name", false);
if (!ParseMaterial(&material, err, o,
store_original_json_for_extras_and_extensions_)) {
if (!ParseMaterial(&material, err, warn, o,
store_original_json_for_extras_and_extensions_,
strictness_)) {
return false;
}
@@ -6644,8 +6710,8 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
if (err) {
(*err) =
"Insufficient storage space for Chunk1(BIN data). At least Chunk1 "
"Must have 4 bytes or more bytes, but got " +
std::to_string((header_and_json_size + 12ull) - uint64_t(length)) +
"Must have 4 or more bytes, but got " +
std::to_string((header_and_json_size + 8ull) - uint64_t(length)) +
".\n";
}
return false;
@@ -6669,10 +6735,17 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
}
if ((chunk1_length % 4) != 0) {
if (err) {
(*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
if (strictness_==ParseStrictness::Permissive) {
if (warn) {
(*warn) += "BIN Chunk end is not aligned to a 4-byte boundary.\n";
}
}
else {
if (err) {
(*err) = "BIN Chunk end is not aligned to a 4-byte boundary.";
}
return false;
}
return false;
}
if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
@@ -6697,10 +6770,6 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
bin_size_ = size_t(chunk1_length);
}
// Extract JSON string.
std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
chunk0_length);
is_binary_ = true;
bool ret = LoadFromString(model, err, warn,
@@ -7098,7 +7167,7 @@ static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
if (accessor.byteOffset != 0)
SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
SerializeNumberProperty<size_t>("byteOffset", accessor.byteOffset, o);
SerializeNumberProperty<int>("componentType", accessor.componentType, o);
SerializeNumberProperty<size_t>("count", accessor.count, o);
@@ -7169,7 +7238,7 @@ static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
detail::json indices;
SerializeNumberProperty<int>("bufferView",
accessor.sparse.indices.bufferView, indices);
SerializeNumberProperty<int>("byteOffset",
SerializeNumberProperty<size_t>("byteOffset",
accessor.sparse.indices.byteOffset, indices);
SerializeNumberProperty<int>(
"componentType", accessor.sparse.indices.componentType, indices);
@@ -7180,7 +7249,7 @@ static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
detail::json values;
SerializeNumberProperty<int>("bufferView",
accessor.sparse.values.bufferView, values);
SerializeNumberProperty<int>("byteOffset",
SerializeNumberProperty<size_t>("byteOffset",
accessor.sparse.values.byteOffset, values);
SerializeExtrasAndExtensions(accessor.sparse.values, values);
detail::JsonAddMember(sparse, "values", std::move(values));