diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..0fd988d9
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,15 @@
+# These are supported funding model platforms
+
+github: syoyo # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+polar: # Replace with a single Polar username
+buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
+thanks_dev: # Replace with a single thanks.dev username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/README.md b/README.md
index 2bf40db1..8bfcdc2b 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Tiny but powerful single file wavefront obj loader written in C++03. No dependen
`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-)
-If you are looking for C89 version, please see https://github.com/syoyo/tinyobjloader-c .
+If you are looking for C99 version, please see https://github.com/syoyo/tinyobjloader-c .
Version notice
--------------
@@ -74,8 +74,9 @@ TinyObjLoader is successfully used in ...
* metal-ray-tracer - Writing ray-tracer using Metal Performance Shaders https://github.com/sergeyreznik/metal-ray-tracer https://sergeyreznik.github.io/metal-ray-tracer/index.html
* Supernova Engine - 2D and 3D projects with Lua or C++ in data oriented design: https://github.com/supernovaengine/supernova
* AGE (Arc Game Engine) - An open-source engine for building 2D & 3D real-time rendering and interactive contents: https://github.com/MohitSethi99/ArcGameEngine
-* [Wicked Engine
](https://github.com/turanszkij/WickedEngine) - 3D engine with modern graphics
-* Your project here! (Letting us know via github issue is welcome!)
+* [Wicked Engine
](https://github.com/turanszkij/WickedEngine) - 3D engine with modern graphics
+* [Lumina Game Engine](https://github.com/MrDrElliot/LuminaEngine) - A modern, high-performance game engine built with Vulkan
+* Your project here! (Plese send PR)
### Old version(v0.9.x)
@@ -250,7 +251,7 @@ You can enable `double(64bit)` precision by using `TINYOBJLOADER_USE_DOUBLE` def
When you enable `triangulation`(default is enabled),
TinyObjLoader triangulate polygons(faces with 4 or more vertices).
-Built-in trinagulation code may not work well in some polygon shape.
+Built-in triangulation code may not work well in some polygon shape.
You can define `TINYOBJLOADER_USE_MAPBOX_EARCUT` for robust triangulation using `mapbox/earcut.hpp`.
This requires C++11 compiler though. And you need to copy `mapbox/earcut.hpp` to your project.
@@ -260,7 +261,7 @@ If you have your own `mapbox/earcut.hpp` file incuded in your project, you can d
```c++
#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
-// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust trinagulation. Requires C++11
+// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust triangulation. Requires C++11
//#define TINYOBJLOADER_USE_MAPBOX_EARCUT
#include "tiny_obj_loader.h"
@@ -332,7 +333,7 @@ for (size_t s = 0; s < shapes.size(); s++) {
```c++
#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
-// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust trinagulation. Requires C++11
+// Optional. define TINYOBJLOADER_USE_MAPBOX_EARCUT gives robust triangulation. Requires C++11
//#define TINYOBJLOADER_USE_MAPBOX_EARCUT
#include "tiny_obj_loader.h"
diff --git a/models/cube_w_BOM.mtl b/models/cube_w_BOM.mtl
new file mode 100644
index 00000000..96255b54
--- /dev/null
+++ b/models/cube_w_BOM.mtl
@@ -0,0 +1,24 @@
+newmtl white
+Ka 0 0 0
+Kd 1 1 1
+Ks 0 0 0
+
+newmtl red
+Ka 0 0 0
+Kd 1 0 0
+Ks 0 0 0
+
+newmtl green
+Ka 0 0 0
+Kd 0 1 0
+Ks 0 0 0
+
+newmtl blue
+Ka 0 0 0
+Kd 0 0 1
+Ks 0 0 0
+
+newmtl light
+Ka 20 20 20
+Kd 1 1 1
+Ks 0 0 0
diff --git a/models/cube_w_BOM.obj b/models/cube_w_BOM.obj
new file mode 100644
index 00000000..3c395f04
--- /dev/null
+++ b/models/cube_w_BOM.obj
@@ -0,0 +1,32 @@
+mtllib cube_w_BOM.mtl
+
+v 0.000000 2.000000 2.000000
+v 0.000000 0.000000 2.000000
+v 2.000000 0.000000 2.000000
+v 2.000000 2.000000 2.000000
+v 0.000000 2.000000 0.000000
+v 0.000000 0.000000 0.000000
+v 2.000000 0.000000 0.000000
+v 2.000000 2.000000 0.000000
+# 8 vertices
+
+g front cube
+usemtl white
+f 1 2 3 4
+# two white spaces between 'back' and 'cube'
+g back cube
+# expects white material
+f 8 7 6 5
+g right cube
+usemtl red
+f 4 3 7 8
+g top cube
+usemtl white
+f 5 1 4 8
+g left cube
+usemtl green
+f 5 6 2 1
+g bottom cube
+usemtl white
+f 2 6 7 3
+# 6 elements
diff --git a/models/issue-389-comment.obj b/models/issue-389-comment.obj
new file mode 100644
index 00000000..cf16d926
--- /dev/null
+++ b/models/issue-389-comment.obj
@@ -0,0 +1,44 @@
+g Part 1
+v 0.0576127 0.0488792 0.0423
+v 0.0576127 0.0488792 0
+v -0.0483158 0.0488792 0
+v -0.0483158 0.0488792 0.0423
+v -0.0483158 -0.0139454 0
+v -0.0483158 -0.0139454 0.0423
+v 0.0576127 -0.0139454 0
+v 0.0576127 -0.0139454 0.0423
+vn 0 1 0
+vn -1 0 0
+vn 0 -1 0
+vn 1 0 0
+vn 0 0 1
+vn 0 0 -1
+o mesh0
+f 1//1 2//1 3//1
+f 3//1 4//1 1//1
+o mesh1
+f 4//2 3//2 5//2
+f 5//2 6//2 4//2
+o mesh2
+f 6//3 5//3 7//3
+f 7//3 8//3 6//3
+o mesh3
+f 8//4 7//4 2//4
+f 2//4 1//4 8//4
+o mesh4
+f 8//5 1//5 4//5
+f 4//5 6//5 8//5
+o mesh5
+f 5//6 3//6 2//6
+f 2//6 7//6 5//6
+
+# Zusätzliche Linien (aus der Oberseite)
+o lines
+v 0.0576127 0.0488792 0.0423 # Startpunkt Linie 1 (Ecke 1 Oberseite)
+v 0.0576127 0.0488792 0.2423 # Endpunkt Linie 1 (2m Höhe)
+v -0.0483158 -0.0139454 0.0423 # Startpunkt Linie 2 (Ecke 6 Oberseite)
+v -0.0483158 -0.0139454 0.2423 # Endpunkt Linie 2 (2m Höhe)
+
+# Linien
+l 1 9 # Linie 1
+l 6 10 # Linie 2
diff --git a/models/issue-391.mtl b/models/issue-391.mtl
new file mode 100644
index 00000000..c23ced4b
--- /dev/null
+++ b/models/issue-391.mtl
@@ -0,0 +1,4 @@
+newmtl has_kd
+Kd 1 0 0
+newmtl has_map
+map_Kd test.png
\ No newline at end of file
diff --git a/models/issue-391.obj b/models/issue-391.obj
new file mode 100644
index 00000000..06d8774b
--- /dev/null
+++ b/models/issue-391.obj
@@ -0,0 +1,9 @@
+mtllib issue-391.mtl
+v 0 0 0
+v 1 0 0
+v 0 1 0
+vn 0 0 1
+usemtl has_map
+f 1//1 2//1 3//1
+usemtl has_kd
+f 1//1 2//1 3//1
\ No newline at end of file
diff --git a/tests/tester.cc b/tests/tester.cc
index 2f336c76..b5c4d0db 100644
--- a/tests/tester.cc
+++ b/tests/tester.cc
@@ -1408,6 +1408,118 @@ void test_face_missing_issue295() {
TEST_CHECK((3 * 28) == shapes[0].mesh.indices.size()); // 28 triangle faces x 3
}
+void test_comment_issue389() {
+ tinyobj::attrib_t attrib;
+ std::vector shapes;
+ std::vector materials;
+
+ std::string warn;
+ std::string err;
+ bool ret = tinyobj::LoadObj(
+ &attrib, &shapes, &materials, &warn, &err,
+ "../models/issue-389-comment.obj",
+ gMtlBasePath, /* triangualte */false);
+
+ TEST_CHECK(warn.empty());
+
+ if (!warn.empty()) {
+ std::cout << "WARN: " << warn << std::endl;
+ }
+
+ if (!err.empty()) {
+ std::cerr << "ERR: " << err << std::endl;
+ }
+
+ TEST_CHECK(true == ret);
+}
+
+void test_default_kd_for_multiple_materials_issue391() {
+ tinyobj::attrib_t attrib;
+ std::vector shapes;
+ std::vector materials;
+
+ std::string warn;
+ std::string err;
+ bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
+ "../models/issue-391.obj", gMtlBasePath);
+ if (!warn.empty()) {
+ std::cout << "WARN: " << warn << std::endl;
+ }
+
+ if (!err.empty()) {
+ std::cerr << "ERR: " << err << std::endl;
+ }
+
+ const tinyobj::real_t kGrey[] = {0.6, 0.6, 0.6};
+ const tinyobj::real_t kRed[] = {1.0, 0.0, 0.0};
+
+ TEST_CHECK(true == ret);
+ TEST_CHECK(2 == materials.size());
+ for (size_t i = 0; i < materials.size(); ++i) {
+ const tinyobj::material_t& material = materials[i];
+ if (material.name == "has_map") {
+ for (int i = 0; i < 3; ++i) TEST_CHECK(material.diffuse[i] == kGrey[i]);
+ } else if (material.name == "has_kd") {
+ for (int i = 0; i < 3; ++i) TEST_CHECK(material.diffuse[i] == kRed[i]);
+ } else {
+ std::cerr << "Unexpected material found!" << std::endl;
+ TEST_CHECK(false);
+ }
+ }
+}
+
+void test_removeUtf8Bom() {
+ // Basic input with BOM
+ std::string withBOM = "\xEF\xBB\xBFhello world";
+ TEST_CHECK(tinyobj::removeUtf8Bom(withBOM) == "hello world");
+
+ // Input without BOM
+ std::string noBOM = "hello world";
+ TEST_CHECK(tinyobj::removeUtf8Bom(noBOM) == "hello world");
+
+ // Leaves short string unchanged
+ std::string shortStr = "\xEF";
+ TEST_CHECK(tinyobj::removeUtf8Bom(shortStr) == shortStr);
+
+ std::string shortStr2 = "\xEF\xBB";
+ TEST_CHECK(tinyobj::removeUtf8Bom(shortStr2) == shortStr2);
+
+ // BOM only returns empty string
+ std::string justBom = "\xEF\xBB\xBF";
+ TEST_CHECK(tinyobj::removeUtf8Bom(justBom) == "");
+
+ // Empty string
+ std::string emptyStr = "";
+ TEST_CHECK(tinyobj::removeUtf8Bom(emptyStr) == "");
+}
+
+void test_loadObj_with_BOM() {
+ tinyobj::attrib_t attrib;
+ std::vector shapes;
+ std::vector materials;
+
+ std::string warn;
+ std::string err;
+ bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
+ "../models/cube_w_BOM.obj", gMtlBasePath);
+
+ if (!warn.empty()) {
+ std::cout << "WARN: " << warn << std::endl;
+ }
+
+ if (!err.empty()) {
+ std::cerr << "ERR: " << err << std::endl;
+ }
+
+ TEST_CHECK(true == ret);
+ TEST_CHECK(6 == shapes.size());
+ TEST_CHECK(0 == shapes[0].name.compare("front cube"));
+ TEST_CHECK(0 == shapes[1].name.compare("back cube")); // multiple whitespaces
+ // are aggregated as
+ // single white space.
+}
+
+
// Fuzzer test.
// Just check if it does not crash.
// Disable by default since Windows filesystem can't create filename of afl
@@ -1511,8 +1623,14 @@ TEST_LIST = {
test_mtl_filename_with_whitespace_issue46},
{"test_face_missing_issue295",
test_face_missing_issue295},
+ {"test_comment_issue389",
+ test_comment_issue389},
{"test_invalid_relative_vertex_index",
test_invalid_relative_vertex_index},
{"test_invalid_texture_vertex_index",
test_invalid_texture_vertex_index},
+ {"default_kd_for_multiple_materials_issue391",
+ test_default_kd_for_multiple_materials_issue391},
+ {"test_removeUtf8Bom", test_removeUtf8Bom},
+ {"test_loadObj_with_BOM", test_loadObj_with_BOM},
{NULL, NULL}};
diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h
index c23acc0d..a927864e 100644
--- a/tiny_obj_loader.h
+++ b/tiny_obj_loader.h
@@ -810,6 +810,17 @@ static inline std::string toString(const T &t) {
return ss.str();
}
+static inline std::string removeUtf8Bom(const std::string& input) {
+ // UTF-8 BOM = 0xEF,0xBB,0xBF
+ if (input.size() >= 3 &&
+ static_cast(input[0]) == 0xEF &&
+ static_cast(input[1]) == 0xBB &&
+ static_cast(input[2]) == 0xBF) {
+ return input.substr(3); // Skip BOM
+ }
+ return input;
+}
+
struct warning_context {
std::string *warn;
size_t line_number;
@@ -2110,6 +2121,9 @@ void LoadMtl(std::map *material_map,
if (linebuf.empty()) {
continue;
}
+ if (line_no == 1) {
+ linebuf = removeUtf8Bom(linebuf);
+ }
// Skip leading space.
const char *token = linebuf.c_str();
@@ -2134,6 +2148,7 @@ void LoadMtl(std::map *material_map,
has_d = false;
has_tr = false;
+ has_kd = false;
// set new mtl name
token += 7;
@@ -2636,6 +2651,9 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes,
if (linebuf.empty()) {
continue;
}
+ if (line_num == 1) {
+ linebuf = removeUtf8Bom(linebuf);
+ }
// Skip leading space.
const char *token = linebuf.c_str();
@@ -2708,7 +2726,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes,
sw.vertex_id = vid;
- while (!IS_NEW_LINE(token[0])) {
+ while (!IS_NEW_LINE(token[0]) && token[0] != '#') {
real_t j, w;
// joint_id should not be negative, weight may be negative
// TODO(syoyo): # of elements check
@@ -2749,7 +2767,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes,
__line_t line;
- while (!IS_NEW_LINE(token[0])) {
+ while (!IS_NEW_LINE(token[0]) && token[0] != '#') {
vertex_index_t vi;
if (!parseTriple(&token, static_cast(v.size() / 3),
static_cast(vn.size() / 3),
@@ -2780,7 +2798,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes,
__points_t pts;
- while (!IS_NEW_LINE(token[0])) {
+ while (!IS_NEW_LINE(token[0]) && token[0] != '#') {
vertex_index_t vi;
if (!parseTriple(&token, static_cast(v.size() / 3),
static_cast(vn.size() / 3),
@@ -2815,7 +2833,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes,
face.smoothing_group_id = current_smoothing_id;
face.vertex_indices.reserve(3);
- while (!IS_NEW_LINE(token[0])) {
+ while (!IS_NEW_LINE(token[0]) && token[0] != '#') {
vertex_index_t vi;
if (!parseTriple(&token, static_cast(v.size() / 3),
static_cast(vn.size() / 3),
@@ -2951,7 +2969,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes,
std::vector names;
- while (!IS_NEW_LINE(token[0])) {
+ while (!IS_NEW_LINE(token[0]) && token[0] != '#') {
std::string str = parseString(&token);
names.push_back(str);
token += strspn(token, " \t\r"); // skip tag
@@ -3245,7 +3263,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
token += strspn(token, " \t");
indices.clear();
- while (!IS_NEW_LINE(token[0])) {
+ while (!IS_NEW_LINE(token[0]) && token[0] != '#') {
vertex_index_t vi = parseRawTriple(&token);
index_t idx;
@@ -3360,7 +3378,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
if (token[0] == 'g' && IS_SPACE((token[1]))) {
names.clear();
- while (!IS_NEW_LINE(token[0])) {
+ while (!IS_NEW_LINE(token[0]) && token[0] != '#') {
std::string str = parseString(&token);
names.push_back(str);
token += strspn(token, " \t\r"); // skip tag