60 #ifndef TINY_OBJ_LOADER_H_ 61 #define TINY_OBJ_LOADER_H_ 70 #if __cplusplus > 199711L 71 #define TINYOBJ_OVERRIDE override 73 #define TINYOBJ_OVERRIDE 77 #pragma clang diagnostic push 78 #if __has_warning("-Wzero-as-null-pointer-constant") 79 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" 82 #pragma clang diagnostic ignored "-Wpadded" 141 #ifdef TINYOBJLOADER_USE_DOUBLE 241 #ifdef TINY_OBJ_LOADER_PYTHON_BINDING 243 std::array<double, 3> GetDiffuse() {
244 std::array<double, 3> values;
245 values[0] = double(
diffuse[0]);
246 values[1] = double(
diffuse[1]);
247 values[2] = double(
diffuse[2]);
252 std::array<double, 3> GetSpecular() {
253 std::array<double, 3> values;
261 std::array<double, 3> GetTransmittance() {
262 std::array<double, 3> values;
270 std::array<double, 3> GetEmission() {
271 std::array<double, 3> values;
279 std::array<double, 3> GetAmbient() {
280 std::array<double, 3> values;
281 values[0] = double(
ambient[0]);
282 values[1] = double(
ambient[1]);
283 values[2] = double(
ambient[2]);
288 void SetDiffuse(std::array<double, 3> &a) {
294 void SetAmbient(std::array<double, 3> &a) {
300 void SetSpecular(std::array<double, 3> &a) {
306 void SetTransmittance(std::array<double, 3> &a) {
312 std::string GetCustomParameter(
const std::string &key) {
313 std::map<std::string, std::string>::const_iterator it =
319 return std::string();
355 std::vector<unsigned char>
437 void (*
usemtl_cb)(
void *user_data,
const char *name,
int material_id);
442 void (*
group_cb)(
void *user_data,
const char **names,
int num_names);
461 virtual bool operator()(
const std::string &matId,
462 std::vector<material_t> *materials,
463 std::map<std::string, int> *matMap, std::string *warn,
464 std::string *err) = 0;
474 : m_mtlBaseDir(mtl_basedir) {}
476 virtual bool operator()(
const std::string &matId,
477 std::vector<material_t> *materials,
478 std::map<std::string, int> *matMap, std::string *warn,
482 std::string m_mtlBaseDir;
491 : m_inStream(inStream) {}
493 virtual bool operator()(
const std::string &matId,
494 std::vector<material_t> *materials,
495 std::map<std::string, int> *matMap, std::string *warn,
499 std::istream &m_inStream;
553 bool ParseFromString(
const std::string &obj_text,
const std::string &mtl_text,
559 bool Valid()
const {
return valid_; }
563 const std::vector<shape_t> &
GetShapes()
const {
return shapes_; }
565 const std::vector<material_t> &
GetMaterials()
const {
return materials_; }
570 const std::string &
Warning()
const {
return warning_; }
575 const std::string &
Error()
const {
return error_; }
581 std::vector<shape_t> shapes_;
582 std::vector<material_t> materials_;
584 std::string warning_;
602 bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
603 std::vector<material_t> *materials, std::string *warn,
604 std::string *err,
const char *filename,
605 const char *mtl_basedir = NULL,
bool triangulate =
true,
606 bool default_vcols_fallback =
true);
615 void *user_data = NULL,
616 MaterialReader *readMatFn = NULL,
617 std::string *warn = NULL, std::string *err = NULL);
623 bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
624 std::vector<material_t> *materials, std::string *warn,
625 std::string *err, std::istream *inStream,
626 MaterialReader *readMatFn = NULL,
bool triangulate =
true,
627 bool default_vcols_fallback =
true);
630 void LoadMtl(std::map<std::string, int> *material_map,
631 std::vector<material_t> *materials, std::istream *inStream,
632 std::string *warning, std::string *err);
643 const char *linebuf);
649 #endif // TINY_OBJ_LOADER_H_ 651 #ifdef TINYOBJLOADER_IMPLEMENTATION 664 #ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT 666 #ifdef TINYOBJLOADER_DONOT_INCLUDE_MAPBOX_EARCUT 671 #pragma clang diagnostic push 672 #pragma clang diagnostic ignored "-Weverything" 676 #include "mapbox/earcut.hpp" 679 #pragma clang diagnostic pop 684 #endif // TINYOBJLOADER_USE_MAPBOX_EARCUT 690 struct vertex_index_t {
691 int v_idx, vt_idx, vn_idx;
692 vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
693 explicit vertex_index_t(
int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
694 vertex_index_t(
int vidx,
int vtidx,
int vnidx)
695 : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
704 std::vector<vertex_index_t> vertex_indices;
706 face_t() : smoothing_group_id(0), pad_(0) {}
714 std::vector<vertex_index_t> vertex_indices;
722 std::vector<vertex_index_t> vertex_indices;
726 tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {}
733 std::vector<real_t> v;
734 std::vector<real_t> vn;
735 std::vector<real_t> vt;
741 std::vector<face_t> faceGroup;
742 std::vector<__line_t> lineGroup;
743 std::vector<__points_t> pointsGroup;
751 bool IsEmpty()
const {
752 return faceGroup.empty() && lineGroup.empty() && pointsGroup.empty();
760 static std::istream &safeGetline(std::istream &is, std::string &t) {
769 std::istream::sentry se(is,
true);
770 std::streambuf *sb = is.rdbuf();
774 int c = sb->sbumpc();
779 if (sb->sgetc() ==
'\n') sb->sbumpc();
783 if (t.empty()) is.setstate(std::ios::eofbit);
786 t +=
static_cast<char>(c);
794 #define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) 795 #define IS_DIGIT(x) \ 796 (static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10)) 797 #define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) 800 static inline bool fixIndex(
int idx,
int n,
int *ret) {
823 static inline std::string parseString(
const char **token) {
825 (*token) += strspn((*token),
" \t");
826 size_t e = strcspn((*token),
" \t\r");
827 s = std::string((*token), &(*token)[e]);
832 static inline int parseInt(
const char **token) {
833 (*token) += strspn((*token),
" \t");
834 int i = atoi((*token));
835 (*token) += strcspn((*token),
" \t\r");
866 static bool tryParseDouble(
const char *s,
const char *s_end,
double *result) {
871 double mantissa = 0.0;
885 char const *curr = s;
890 bool end_not_reached =
false;
891 bool leading_decimal_dots =
false;
898 if (*curr ==
'+' || *curr ==
'-') {
901 if ((curr != s_end) && (*curr ==
'.')) {
903 leading_decimal_dots =
true;
905 }
else if (IS_DIGIT(*curr)) {
906 }
else if (*curr ==
'.') {
908 leading_decimal_dots =
true;
914 end_not_reached = (curr != s_end);
915 if (!leading_decimal_dots) {
916 while (end_not_reached && IS_DIGIT(*curr)) {
918 mantissa +=
static_cast<int>(*curr - 0x30);
921 end_not_reached = (curr != s_end);
925 if (read == 0)
goto fail;
929 if (!end_not_reached)
goto assemble;
935 end_not_reached = (curr != s_end);
936 while (end_not_reached && IS_DIGIT(*curr)) {
937 static const double pow_lut[] = {
938 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001,
940 const int lut_entries =
sizeof pow_lut /
sizeof pow_lut[0];
943 mantissa +=
static_cast<int>(*curr - 0x30) *
944 (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read));
947 end_not_reached = (curr != s_end);
949 }
else if (*curr ==
'e' || *curr ==
'E') {
954 if (!end_not_reached)
goto assemble;
957 if (*curr ==
'e' || *curr ==
'E') {
960 end_not_reached = (curr != s_end);
961 if (end_not_reached && (*curr ==
'+' || *curr ==
'-')) {
964 }
else if (IS_DIGIT(*curr)) {
971 end_not_reached = (curr != s_end);
972 while (end_not_reached && IS_DIGIT(*curr)) {
975 if (exponent > (2147483647/10)) {
980 exponent +=
static_cast<int>(*curr - 0x30);
983 end_not_reached = (curr != s_end);
985 exponent *= (exp_sign ==
'+' ? 1 : -1);
986 if (read == 0)
goto fail;
990 *result = (sign ==
'+' ? 1 : -1) *
991 (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent)
998 static inline real_t parseReal(
const char **token,
double default_value = 0.0) {
999 (*token) += strspn((*token),
" \t");
1000 const char *end = (*token) + strcspn((*token),
" \t\r");
1001 double val = default_value;
1002 tryParseDouble((*token), end, &val);
1008 static inline bool parseReal(
const char **token,
real_t *out) {
1009 (*token) += strspn((*token),
" \t");
1010 const char *end = (*token) + strcspn((*token),
" \t\r");
1012 bool ret = tryParseDouble((*token), end, &val);
1021 static inline void parseReal2(
real_t *x,
real_t *y,
const char **token,
1022 const double default_x = 0.0,
1023 const double default_y = 0.0) {
1024 (*x) = parseReal(token, default_x);
1025 (*y) = parseReal(token, default_y);
1029 const char **token,
const double default_x = 0.0,
1030 const double default_y = 0.0,
1031 const double default_z = 0.0) {
1032 (*x) = parseReal(token, default_x);
1033 (*y) = parseReal(token, default_y);
1034 (*z) = parseReal(token, default_z);
1038 const char **token,
const double default_x = 0.0,
1039 const double default_y = 0.0,
1040 const double default_z = 0.0,
1041 const double default_w = 1.0) {
1042 (*x) = parseReal(token, default_x);
1043 (*y) = parseReal(token, default_y);
1044 (*z) = parseReal(token, default_z);
1045 (*w) = parseReal(token, default_w);
1052 const double default_x = 0.0,
1053 const double default_y = 0.0,
1054 const double default_z = 0.0) {
1055 (*x) = parseReal(token, default_x);
1056 (*y) = parseReal(token, default_y);
1057 (*z) = parseReal(token, default_z);
1059 const bool found_color =
1060 parseReal(token, r) && parseReal(token, g) && parseReal(token, b);
1063 (*r) = (*g) = (*b) = 1.0;
1069 static inline bool parseOnOff(
const char **token,
bool default_value =
true) {
1070 (*token) += strspn((*token),
" \t");
1071 const char *end = (*token) + strcspn((*token),
" \t\r");
1073 bool ret = default_value;
1074 if ((0 == strncmp((*token),
"on", 2))) {
1076 }
else if ((0 == strncmp((*token),
"off", 3))) {
1086 (*token) += strspn((*token),
" \t");
1087 const char *end = (*token) + strcspn((*token),
" \t\r");
1090 if ((0 == strncmp((*token),
"cube_top", strlen(
"cube_top")))) {
1092 }
else if ((0 == strncmp((*token),
"cube_bottom", strlen(
"cube_bottom")))) {
1094 }
else if ((0 == strncmp((*token),
"cube_left", strlen(
"cube_left")))) {
1096 }
else if ((0 == strncmp((*token),
"cube_right", strlen(
"cube_right")))) {
1098 }
else if ((0 == strncmp((*token),
"cube_front", strlen(
"cube_front")))) {
1100 }
else if ((0 == strncmp((*token),
"cube_back", strlen(
"cube_back")))) {
1102 }
else if ((0 == strncmp((*token),
"sphere", strlen(
"sphere")))) {
1110 static tag_sizes parseTagTriple(
const char **token) {
1113 (*token) += strspn((*token),
" \t");
1114 ts.num_ints = atoi((*token));
1115 (*token) += strcspn((*token),
"/ \t\r");
1116 if ((*token)[0] !=
'/') {
1122 (*token) += strspn((*token),
" \t");
1123 ts.num_reals = atoi((*token));
1124 (*token) += strcspn((*token),
"/ \t\r");
1125 if ((*token)[0] !=
'/') {
1130 ts.num_strings = parseInt(token);
1136 static bool parseTriple(
const char **token,
int vsize,
int vnsize,
int vtsize,
1137 vertex_index_t *ret) {
1142 vertex_index_t vi(-1);
1144 if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) {
1148 (*token) += strcspn((*token),
"/ \t\r");
1149 if ((*token)[0] !=
'/') {
1156 if ((*token)[0] ==
'/') {
1158 if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) {
1161 (*token) += strcspn((*token),
"/ \t\r");
1167 if (!fixIndex(atoi((*token)), vtsize, &(vi.vt_idx))) {
1171 (*token) += strcspn((*token),
"/ \t\r");
1172 if ((*token)[0] !=
'/') {
1179 if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) {
1182 (*token) += strcspn((*token),
"/ \t\r");
1190 static vertex_index_t parseRawTriple(
const char **token) {
1191 vertex_index_t vi(static_cast<int>(0));
1193 vi.v_idx = atoi((*token));
1194 (*token) += strcspn((*token),
"/ \t\r");
1195 if ((*token)[0] !=
'/') {
1201 if ((*token)[0] ==
'/') {
1203 vi.vn_idx = atoi((*token));
1204 (*token) += strcspn((*token),
"/ \t\r");
1209 vi.vt_idx = atoi((*token));
1210 (*token) += strcspn((*token),
"/ \t\r");
1211 if ((*token)[0] !=
'/') {
1217 vi.vn_idx = atoi((*token));
1218 (*token) += strcspn((*token),
"/ \t\r");
1223 const char *linebuf) {
1225 bool found_texname =
false;
1226 std::string texture_name;
1228 const char *token = linebuf;
1230 while (!IS_NEW_LINE((*token))) {
1231 token += strspn(token,
" \t");
1232 if ((0 == strncmp(token,
"-blendu", 7)) && IS_SPACE((token[7]))) {
1234 texopt->blendu = parseOnOff(&token,
true);
1235 }
else if ((0 == strncmp(token,
"-blendv", 7)) && IS_SPACE((token[7]))) {
1237 texopt->blendv = parseOnOff(&token,
true);
1238 }
else if ((0 == strncmp(token,
"-clamp", 6)) && IS_SPACE((token[6]))) {
1240 texopt->clamp = parseOnOff(&token,
true);
1241 }
else if ((0 == strncmp(token,
"-boost", 6)) && IS_SPACE((token[6]))) {
1243 texopt->sharpness = parseReal(&token, 1.0);
1244 }
else if ((0 == strncmp(token,
"-bm", 3)) && IS_SPACE((token[3]))) {
1246 texopt->bump_multiplier = parseReal(&token, 1.0);
1247 }
else if ((0 == strncmp(token,
"-o", 2)) && IS_SPACE((token[2]))) {
1249 parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]),
1250 &(texopt->origin_offset[2]), &token);
1251 }
else if ((0 == strncmp(token,
"-s", 2)) && IS_SPACE((token[2]))) {
1253 parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]),
1254 &token, 1.0, 1.0, 1.0);
1255 }
else if ((0 == strncmp(token,
"-t", 2)) && IS_SPACE((token[2]))) {
1257 parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]),
1258 &(texopt->turbulence[2]), &token);
1259 }
else if ((0 == strncmp(token,
"-type", 5)) && IS_SPACE((token[5]))) {
1262 }
else if ((0 == strncmp(token,
"-texres", 7)) && IS_SPACE((token[7]))) {
1265 texopt->texture_resolution = parseInt(&token);
1266 }
else if ((0 == strncmp(token,
"-imfchan", 8)) && IS_SPACE((token[8]))) {
1268 token += strspn(token,
" \t");
1269 const char *end = token + strcspn(token,
" \t\r");
1270 if ((end - token) == 1) {
1271 texopt->imfchan = (*token);
1274 }
else if ((0 == strncmp(token,
"-mm", 3)) && IS_SPACE((token[3]))) {
1276 parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0);
1277 }
else if ((0 == strncmp(token,
"-colorspace", 11)) &&
1278 IS_SPACE((token[11]))) {
1280 texopt->colorspace = parseString(&token);
1284 size_t len = strcspn(token,
" \t\r");
1285 texture_name = std::string(token, token + len);
1288 token += strspn(token,
" \t");
1292 texture_name = std::string(token);
1293 token += texture_name.length();
1296 found_texname =
true;
1300 if (found_texname) {
1301 (*texname) = texture_name;
1308 static void InitTexOpt(texture_option_t *texopt,
const bool is_bump) {
1310 texopt->imfchan =
'l';
1312 texopt->imfchan =
'm';
1314 texopt->bump_multiplier =
static_cast<real_t>(1.0);
1315 texopt->clamp =
false;
1316 texopt->blendu =
true;
1317 texopt->blendv =
true;
1318 texopt->sharpness =
static_cast<real_t>(1.0);
1319 texopt->brightness =
static_cast<real_t>(0.0);
1320 texopt->contrast =
static_cast<real_t>(1.0);
1321 texopt->origin_offset[0] =
static_cast<real_t>(0.0);
1322 texopt->origin_offset[1] =
static_cast<real_t>(0.0);
1323 texopt->origin_offset[2] =
static_cast<real_t>(0.0);
1324 texopt->scale[0] =
static_cast<real_t>(1.0);
1325 texopt->scale[1] =
static_cast<real_t>(1.0);
1326 texopt->scale[2] =
static_cast<real_t>(1.0);
1327 texopt->turbulence[0] =
static_cast<real_t>(0.0);
1328 texopt->turbulence[1] =
static_cast<real_t>(0.0);
1329 texopt->turbulence[2] =
static_cast<real_t>(0.0);
1330 texopt->texture_resolution = -1;
1334 static void InitMaterial(material_t *material) {
1335 InitTexOpt(&material->ambient_texopt,
false);
1336 InitTexOpt(&material->diffuse_texopt,
false);
1337 InitTexOpt(&material->specular_texopt,
false);
1338 InitTexOpt(&material->specular_highlight_texopt,
false);
1339 InitTexOpt(&material->bump_texopt,
true);
1340 InitTexOpt(&material->displacement_texopt,
false);
1341 InitTexOpt(&material->alpha_texopt,
false);
1342 InitTexOpt(&material->reflection_texopt,
false);
1343 InitTexOpt(&material->roughness_texopt,
false);
1344 InitTexOpt(&material->metallic_texopt,
false);
1345 InitTexOpt(&material->sheen_texopt,
false);
1346 InitTexOpt(&material->emissive_texopt,
false);
1347 InitTexOpt(&material->normal_texopt,
1349 material->name =
"";
1350 material->ambient_texname =
"";
1351 material->diffuse_texname =
"";
1352 material->specular_texname =
"";
1353 material->specular_highlight_texname =
"";
1354 material->bump_texname =
"";
1355 material->displacement_texname =
"";
1356 material->reflection_texname =
"";
1357 material->alpha_texname =
"";
1358 for (
int i = 0; i < 3; i++) {
1359 material->ambient[i] =
static_cast<real_t>(0.0);
1360 material->diffuse[i] =
static_cast<real_t>(0.0);
1361 material->specular[i] =
static_cast<real_t>(0.0);
1362 material->transmittance[i] =
static_cast<real_t>(0.0);
1363 material->emission[i] =
static_cast<real_t>(0.0);
1365 material->illum = 0;
1366 material->dissolve =
static_cast<real_t>(1.0);
1367 material->shininess =
static_cast<real_t>(1.0);
1368 material->ior =
static_cast<real_t>(1.0);
1370 material->roughness =
static_cast<real_t>(0.0);
1371 material->metallic =
static_cast<real_t>(0.0);
1372 material->sheen =
static_cast<real_t>(0.0);
1373 material->clearcoat_thickness =
static_cast<real_t>(0.0);
1374 material->clearcoat_roughness =
static_cast<real_t>(0.0);
1375 material->anisotropy_rotation =
static_cast<real_t>(0.0);
1376 material->anisotropy =
static_cast<real_t>(0.0);
1377 material->roughness_texname =
"";
1378 material->metallic_texname =
"";
1379 material->sheen_texname =
"";
1380 material->emissive_texname =
"";
1381 material->normal_texname =
"";
1383 material->unknown_parameter.clear();
1387 template <
typename T>
1388 static int pnpoly(
int nvert, T *vertx, T *verty, T testx, T testy) {
1390 for (i = 0, j = nvert - 1; i < nvert; j = i++) {
1391 if (((verty[i] > testy) != (verty[j] > testy)) &&
1393 (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) +
1401 static bool exportGroupsToShape(shape_t *shape,
const PrimGroup &prim_group,
1402 const std::vector<tag_t> &tags,
1403 const int material_id,
const std::string &name,
1404 bool triangulate,
const std::vector<real_t> &v,
1405 std::string *warn) {
1406 if (prim_group.IsEmpty()) {
1413 if (!prim_group.faceGroup.empty()) {
1415 for (
size_t i = 0; i < prim_group.faceGroup.size(); i++) {
1416 const face_t &face = prim_group.faceGroup[i];
1418 size_t npolys = face.vertex_indices.size();
1423 (*warn) +=
"Degenerated face found\n.";
1430 vertex_index_t i0 = face.vertex_indices[0];
1431 vertex_index_t i1 = face.vertex_indices[1];
1432 vertex_index_t i2 = face.vertex_indices[2];
1433 vertex_index_t i3 = face.vertex_indices[3];
1435 size_t vi0 = size_t(i0.v_idx);
1436 size_t vi1 = size_t(i1.v_idx);
1437 size_t vi2 = size_t(i2.v_idx);
1438 size_t vi3 = size_t(i3.v_idx);
1440 if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) ||
1441 ((3 * vi2 + 2) >= v.size()) || ((3 * vi3 + 2) >= v.size())) {
1445 (*warn) +=
"Face with invalid vertex index found.\n";
1450 real_t v0x = v[vi0 * 3 + 0];
1451 real_t v0y = v[vi0 * 3 + 1];
1452 real_t v0z = v[vi0 * 3 + 2];
1453 real_t v1x = v[vi1 * 3 + 0];
1454 real_t v1y = v[vi1 * 3 + 1];
1455 real_t v1z = v[vi1 * 3 + 2];
1456 real_t v2x = v[vi2 * 3 + 0];
1457 real_t v2y = v[vi2 * 3 + 1];
1458 real_t v2z = v[vi2 * 3 + 2];
1459 real_t v3x = v[vi3 * 3 + 0];
1460 real_t v3y = v[vi3 * 3 + 1];
1461 real_t v3z = v[vi3 * 3 + 2];
1488 real_t sqr02 = e02x * e02x + e02y * e02y + e02z * e02z;
1489 real_t sqr13 = e13x * e13x + e13y * e13y + e13z * e13z;
1491 index_t idx0, idx1, idx2, idx3;
1493 idx0.vertex_index = i0.v_idx;
1494 idx0.normal_index = i0.vn_idx;
1495 idx0.texcoord_index = i0.vt_idx;
1496 idx1.vertex_index = i1.v_idx;
1497 idx1.normal_index = i1.vn_idx;
1498 idx1.texcoord_index = i1.vt_idx;
1499 idx2.vertex_index = i2.v_idx;
1500 idx2.normal_index = i2.vn_idx;
1501 idx2.texcoord_index = i2.vt_idx;
1502 idx3.vertex_index = i3.v_idx;
1503 idx3.normal_index = i3.vn_idx;
1504 idx3.texcoord_index = i3.vt_idx;
1506 if (sqr02 < sqr13) {
1508 shape->mesh.indices.push_back(idx0);
1509 shape->mesh.indices.push_back(idx1);
1510 shape->mesh.indices.push_back(idx2);
1512 shape->mesh.indices.push_back(idx0);
1513 shape->mesh.indices.push_back(idx2);
1514 shape->mesh.indices.push_back(idx3);
1517 shape->mesh.indices.push_back(idx0);
1518 shape->mesh.indices.push_back(idx1);
1519 shape->mesh.indices.push_back(idx3);
1521 shape->mesh.indices.push_back(idx1);
1522 shape->mesh.indices.push_back(idx2);
1523 shape->mesh.indices.push_back(idx3);
1527 shape->mesh.num_face_vertices.push_back(3);
1528 shape->mesh.num_face_vertices.push_back(3);
1530 shape->mesh.material_ids.push_back(material_id);
1531 shape->mesh.material_ids.push_back(material_id);
1533 shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
1534 shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
1537 vertex_index_t i0 = face.vertex_indices[0];
1538 vertex_index_t i1(-1);
1539 vertex_index_t i2 = face.vertex_indices[1];
1542 size_t axes[2] = {1, 2};
1543 for (
size_t k = 0; k < npolys; ++k) {
1544 i0 = face.vertex_indices[(k + 0) % npolys];
1545 i1 = face.vertex_indices[(k + 1) % npolys];
1546 i2 = face.vertex_indices[(k + 2) % npolys];
1547 size_t vi0 = size_t(i0.v_idx);
1548 size_t vi1 = size_t(i1.v_idx);
1549 size_t vi2 = size_t(i2.v_idx);
1551 if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) ||
1552 ((3 * vi2 + 2) >= v.size())) {
1557 real_t v0x = v[vi0 * 3 + 0];
1558 real_t v0y = v[vi0 * 3 + 1];
1559 real_t v0z = v[vi0 * 3 + 2];
1560 real_t v1x = v[vi1 * 3 + 0];
1561 real_t v1y = v[vi1 * 3 + 1];
1562 real_t v1z = v[vi1 * 3 + 2];
1563 real_t v2x = v[vi2 * 3 + 0];
1564 real_t v2y = v[vi2 * 3 + 1];
1565 real_t v2z = v[vi2 * 3 + 2];
1572 real_t cx = std::fabs(e0y * e1z - e0z * e1y);
1573 real_t cy = std::fabs(e0z * e1x - e0x * e1z);
1574 real_t cz = std::fabs(e0x * e1y - e0y * e1x);
1575 const real_t epsilon = std::numeric_limits<real_t>::epsilon();
1578 if (cx > epsilon || cy > epsilon || cz > epsilon) {
1581 if (cx > cy && cx > cz) {
1586 if (cz > cx && cz > cy) {
1595 #ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT 1596 using Point = std::array<real_t, 2>;
1600 std::vector<std::vector<Point> > polygon;
1602 std::vector<Point> polyline;
1605 for (
size_t k = 0; k < npolys; k++) {
1606 i0 = face.vertex_indices[k];
1607 size_t vi0 = size_t(i0.v_idx);
1609 assert(((3 * vi0 + 2) < v.size()));
1611 real_t v0x = v[vi0 * 3 + axes[0]];
1612 real_t v0y = v[vi0 * 3 + axes[1]];
1614 polyline.push_back({v0x, v0y});
1617 polygon.push_back(polyline);
1618 std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(polygon);
1621 assert(indices.size() % 3 == 0);
1624 for (
size_t k = 0; k < indices.size() / 3; k++) {
1626 index_t idx0, idx1, idx2;
1627 idx0.vertex_index = face.vertex_indices[indices[3 * k + 0]].v_idx;
1629 face.vertex_indices[indices[3 * k + 0]].vn_idx;
1630 idx0.texcoord_index =
1631 face.vertex_indices[indices[3 * k + 0]].vt_idx;
1632 idx1.vertex_index = face.vertex_indices[indices[3 * k + 1]].v_idx;
1634 face.vertex_indices[indices[3 * k + 1]].vn_idx;
1635 idx1.texcoord_index =
1636 face.vertex_indices[indices[3 * k + 1]].vt_idx;
1637 idx2.vertex_index = face.vertex_indices[indices[3 * k + 2]].v_idx;
1639 face.vertex_indices[indices[3 * k + 2]].vn_idx;
1640 idx2.texcoord_index =
1641 face.vertex_indices[indices[3 * k + 2]].vt_idx;
1643 shape->mesh.indices.push_back(idx0);
1644 shape->mesh.indices.push_back(idx1);
1645 shape->mesh.indices.push_back(idx2);
1647 shape->mesh.num_face_vertices.push_back(3);
1648 shape->mesh.material_ids.push_back(material_id);
1649 shape->mesh.smoothing_group_ids.push_back(
1650 face.smoothing_group_id);
1654 #else // Built-in ear clipping triangulation 1657 face_t remainingFace = face;
1658 size_t guess_vert = 0;
1659 vertex_index_t ind[3];
1665 size_t remainingIterations = face.vertex_indices.size();
1666 size_t previousRemainingVertices =
1667 remainingFace.vertex_indices.size();
1669 while (remainingFace.vertex_indices.size() > 3 &&
1670 remainingIterations > 0) {
1674 npolys = remainingFace.vertex_indices.size();
1675 if (guess_vert >= npolys) {
1676 guess_vert -= npolys;
1679 if (previousRemainingVertices != npolys) {
1681 previousRemainingVertices = npolys;
1682 remainingIterations = npolys;
1686 remainingIterations--;
1689 for (
size_t k = 0; k < 3; k++) {
1690 ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys];
1691 size_t vi = size_t(ind[k].v_idx);
1692 if (((vi * 3 + axes[0]) >= v.size()) ||
1693 ((vi * 3 + axes[1]) >= v.size())) {
1695 vx[k] =
static_cast<real_t>(0.0);
1696 vy[k] =
static_cast<real_t>(0.0);
1698 vx[k] = v[vi * 3 + axes[0]];
1699 vy[k] = v[vi * 3 + axes[1]];
1706 real_t e0x = vx[1] - vx[0];
1707 real_t e0y = vy[1] - vy[0];
1708 real_t e1x = vx[2] - vx[1];
1709 real_t e1y = vy[2] - vy[1];
1715 real_t area = (vx[0] * vy[1] - vy[0] * vx[1]) * static_cast<real_t>(0.5);
1718 if (
cross * area < static_cast<real_t>(0.0)) {
1726 bool overlap =
false;
1727 for (
size_t otherVert = 3; otherVert < npolys; ++otherVert) {
1728 size_t idx = (guess_vert + otherVert) % npolys;
1730 if (idx >= remainingFace.vertex_indices.size()) {
1736 size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx);
1738 if (((ovi * 3 + axes[0]) >= v.size()) ||
1739 ((ovi * 3 + axes[1]) >= v.size())) {
1744 real_t tx = v[ovi * 3 + axes[0]];
1745 real_t ty = v[ovi * 3 + axes[1]];
1746 if (pnpoly(3, vx, vy, tx, ty)) {
1761 index_t idx0, idx1, idx2;
1762 idx0.vertex_index = ind[0].v_idx;
1763 idx0.normal_index = ind[0].vn_idx;
1764 idx0.texcoord_index = ind[0].vt_idx;
1765 idx1.vertex_index = ind[1].v_idx;
1766 idx1.normal_index = ind[1].vn_idx;
1767 idx1.texcoord_index = ind[1].vt_idx;
1768 idx2.vertex_index = ind[2].v_idx;
1769 idx2.normal_index = ind[2].vn_idx;
1770 idx2.texcoord_index = ind[2].vt_idx;
1772 shape->mesh.indices.push_back(idx0);
1773 shape->mesh.indices.push_back(idx1);
1774 shape->mesh.indices.push_back(idx2);
1776 shape->mesh.num_face_vertices.push_back(3);
1777 shape->mesh.material_ids.push_back(material_id);
1778 shape->mesh.smoothing_group_ids.push_back(
1779 face.smoothing_group_id);
1783 size_t removed_vert_index = (guess_vert + 1) % npolys;
1784 while (removed_vert_index + 1 < npolys) {
1785 remainingFace.vertex_indices[removed_vert_index] =
1786 remainingFace.vertex_indices[removed_vert_index + 1];
1787 removed_vert_index += 1;
1789 remainingFace.vertex_indices.pop_back();
1794 if (remainingFace.vertex_indices.size() == 3) {
1795 i0 = remainingFace.vertex_indices[0];
1796 i1 = remainingFace.vertex_indices[1];
1797 i2 = remainingFace.vertex_indices[2];
1799 index_t idx0, idx1, idx2;
1800 idx0.vertex_index = i0.v_idx;
1801 idx0.normal_index = i0.vn_idx;
1802 idx0.texcoord_index = i0.vt_idx;
1803 idx1.vertex_index = i1.v_idx;
1804 idx1.normal_index = i1.vn_idx;
1805 idx1.texcoord_index = i1.vt_idx;
1806 idx2.vertex_index = i2.v_idx;
1807 idx2.normal_index = i2.vn_idx;
1808 idx2.texcoord_index = i2.vt_idx;
1810 shape->mesh.indices.push_back(idx0);
1811 shape->mesh.indices.push_back(idx1);
1812 shape->mesh.indices.push_back(idx2);
1814 shape->mesh.num_face_vertices.push_back(3);
1815 shape->mesh.material_ids.push_back(material_id);
1816 shape->mesh.smoothing_group_ids.push_back(
1817 face.smoothing_group_id);
1823 for (
size_t k = 0; k < npolys; k++) {
1825 idx.vertex_index = face.vertex_indices[k].v_idx;
1826 idx.normal_index = face.vertex_indices[k].vn_idx;
1827 idx.texcoord_index = face.vertex_indices[k].vt_idx;
1828 shape->mesh.indices.push_back(idx);
1831 shape->mesh.num_face_vertices.push_back(
1832 static_cast<unsigned char>(npolys));
1833 shape->mesh.material_ids.push_back(material_id);
1834 shape->mesh.smoothing_group_ids.push_back(
1835 face.smoothing_group_id);
1839 shape->mesh.tags = tags;
1843 if (!prim_group.lineGroup.empty()) {
1845 for (
size_t i = 0; i < prim_group.lineGroup.size(); i++) {
1846 for (
size_t j = 0; j < prim_group.lineGroup[i].vertex_indices.size();
1848 const vertex_index_t &vi = prim_group.lineGroup[i].vertex_indices[j];
1851 idx.vertex_index = vi.v_idx;
1852 idx.normal_index = vi.vn_idx;
1853 idx.texcoord_index = vi.vt_idx;
1855 shape->lines.indices.push_back(idx);
1858 shape->lines.num_line_vertices.push_back(
1859 int(prim_group.lineGroup[i].vertex_indices.size()));
1864 if (!prim_group.pointsGroup.empty()) {
1866 for (
size_t i = 0; i < prim_group.pointsGroup.size(); i++) {
1867 for (
size_t j = 0; j < prim_group.pointsGroup[i].vertex_indices.size();
1869 const vertex_index_t &vi = prim_group.pointsGroup[i].vertex_indices[j];
1872 idx.vertex_index = vi.v_idx;
1873 idx.normal_index = vi.vn_idx;
1874 idx.texcoord_index = vi.vt_idx;
1876 shape->points.indices.push_back(idx);
1886 static void SplitString(
const std::string &s,
char delim,
char escape,
1887 std::vector<std::string> &elems) {
1890 bool escaping =
false;
1891 for (
size_t i = 0; i < s.size(); ++i) {
1895 }
else if (ch == escape) {
1898 }
else if (ch == delim) {
1899 if (!token.empty()) {
1900 elems.push_back(token);
1908 elems.push_back(token);
1911 static std::string JoinPath(
const std::string &dir,
1912 const std::string &filename) {
1917 char lastChar = *dir.rbegin();
1918 if (lastChar !=
'/') {
1919 return dir + std::string(
"/") + filename;
1921 return dir + filename;
1926 void LoadMtl(std::map<std::string, int> *material_map,
1927 std::vector<material_t> *materials, std::istream *inStream,
1928 std::string *warning, std::string *err) {
1932 material_t material;
1933 InitMaterial(&material);
1937 bool has_tr =
false;
1941 bool has_kd =
false;
1943 std::stringstream warn_ss;
1946 std::string linebuf;
1947 while (inStream->peek() != -1) {
1948 safeGetline(*inStream, linebuf);
1952 if (linebuf.size() > 0) {
1953 linebuf = linebuf.substr(0, linebuf.find_last_not_of(
" \t") + 1);
1957 if (linebuf.size() > 0) {
1958 if (linebuf[linebuf.size() - 1] ==
'\n')
1959 linebuf.erase(linebuf.size() - 1);
1961 if (linebuf.size() > 0) {
1962 if (linebuf[linebuf.size() - 1] ==
'\r')
1963 linebuf.erase(linebuf.size() - 1);
1967 if (linebuf.empty()) {
1972 const char *token = linebuf.c_str();
1973 token += strspn(token,
" \t");
1976 if (token[0] ==
'\0')
continue;
1978 if (token[0] ==
'#')
continue;
1981 if ((0 == strncmp(token,
"newmtl", 6)) && IS_SPACE((token[6]))) {
1983 if (!material.name.empty()) {
1984 material_map->insert(std::pair<std::string, int>(
1985 material.name, static_cast<int>(materials->size())));
1986 materials->push_back(material);
1990 InitMaterial(&material);
1998 std::stringstream sstr;
2000 material.name = sstr.str();
2006 if (token[0] ==
'K' && token[1] ==
'a' && IS_SPACE((token[2]))) {
2009 parseReal3(&r, &g, &b, &token);
2010 material.ambient[0] = r;
2011 material.ambient[1] = g;
2012 material.ambient[2] = b;
2017 if (token[0] ==
'K' && token[1] ==
'd' && IS_SPACE((token[2]))) {
2020 parseReal3(&r, &g, &b, &token);
2021 material.diffuse[0] = r;
2022 material.diffuse[1] = g;
2023 material.diffuse[2] = b;
2029 if (token[0] ==
'K' && token[1] ==
's' && IS_SPACE((token[2]))) {
2032 parseReal3(&r, &g, &b, &token);
2033 material.specular[0] = r;
2034 material.specular[1] = g;
2035 material.specular[2] = b;
2040 if ((token[0] ==
'K' && token[1] ==
't' && IS_SPACE((token[2]))) ||
2041 (token[0] ==
'T' && token[1] ==
'f' && IS_SPACE((token[2])))) {
2044 parseReal3(&r, &g, &b, &token);
2045 material.transmittance[0] = r;
2046 material.transmittance[1] = g;
2047 material.transmittance[2] = b;
2052 if (token[0] ==
'N' && token[1] ==
'i' && IS_SPACE((token[2]))) {
2054 material.ior = parseReal(&token);
2059 if (token[0] ==
'K' && token[1] ==
'e' && IS_SPACE(token[2])) {
2062 parseReal3(&r, &g, &b, &token);
2063 material.emission[0] = r;
2064 material.emission[1] = g;
2065 material.emission[2] = b;
2070 if (token[0] ==
'N' && token[1] ==
's' && IS_SPACE(token[2])) {
2072 material.shininess = parseReal(&token);
2077 if (0 == strncmp(token,
"illum", 5) && IS_SPACE(token[5])) {
2079 material.illum = parseInt(&token);
2084 if ((token[0] ==
'd' && IS_SPACE(token[1]))) {
2086 material.dissolve = parseReal(&token);
2089 warn_ss <<
"Both `d` and `Tr` parameters defined for \"" 2091 <<
"\". Use the value of `d` for dissolve (line " << line_no
2097 if (token[0] ==
'T' && token[1] ==
'r' && IS_SPACE(token[2])) {
2101 warn_ss <<
"Both `d` and `Tr` parameters defined for \"" 2103 <<
"\". Use the value of `d` for dissolve (line " << line_no
2109 material.dissolve =
static_cast<real_t>(1.0) - parseReal(&token);
2116 if (token[0] ==
'P' && token[1] ==
'r' && IS_SPACE(token[2])) {
2118 material.roughness = parseReal(&token);
2123 if (token[0] ==
'P' && token[1] ==
'm' && IS_SPACE(token[2])) {
2125 material.metallic = parseReal(&token);
2130 if (token[0] ==
'P' && token[1] ==
's' && IS_SPACE(token[2])) {
2132 material.sheen = parseReal(&token);
2137 if (token[0] ==
'P' && token[1] ==
'c' && IS_SPACE(token[2])) {
2139 material.clearcoat_thickness = parseReal(&token);
2144 if ((0 == strncmp(token,
"Pcr", 3)) && IS_SPACE(token[3])) {
2146 material.clearcoat_roughness = parseReal(&token);
2151 if ((0 == strncmp(token,
"aniso", 5)) && IS_SPACE(token[5])) {
2153 material.anisotropy = parseReal(&token);
2158 if ((0 == strncmp(token,
"anisor", 6)) && IS_SPACE(token[6])) {
2160 material.anisotropy_rotation = parseReal(&token);
2165 if ((0 == strncmp(token,
"map_Ka", 6)) && IS_SPACE(token[6])) {
2168 &(material.ambient_texopt), token);
2173 if ((0 == strncmp(token,
"map_Kd", 6)) && IS_SPACE(token[6])) {
2176 &(material.diffuse_texopt), token);
2181 material.diffuse[0] =
static_cast<real_t>(0.6);
2182 material.diffuse[1] =
static_cast<real_t>(0.6);
2183 material.diffuse[2] =
static_cast<real_t>(0.6);
2190 if ((0 == strncmp(token,
"map_Ks", 6)) && IS_SPACE(token[6])) {
2193 &(material.specular_texopt), token);
2198 if ((0 == strncmp(token,
"map_Ns", 6)) && IS_SPACE(token[6])) {
2201 &(material.specular_highlight_texopt), token);
2206 if ((0 == strncmp(token,
"map_bump", 8)) && IS_SPACE(token[8])) {
2209 &(material.bump_texopt), token);
2214 if ((0 == strncmp(token,
"map_Bump", 8)) && IS_SPACE(token[8])) {
2217 &(material.bump_texopt), token);
2222 if ((0 == strncmp(token,
"bump", 4)) && IS_SPACE(token[4])) {
2225 &(material.bump_texopt), token);
2230 if ((0 == strncmp(token,
"map_d", 5)) && IS_SPACE(token[5])) {
2232 material.alpha_texname = token;
2234 &(material.alpha_texopt), token);
2239 if ((0 == strncmp(token,
"disp", 4)) && IS_SPACE(token[4])) {
2242 &(material.displacement_texopt), token);
2247 if ((0 == strncmp(token,
"refl", 4)) && IS_SPACE(token[4])) {
2250 &(material.reflection_texopt), token);
2255 if ((0 == strncmp(token,
"map_Pr", 6)) && IS_SPACE(token[6])) {
2258 &(material.roughness_texopt), token);
2263 if ((0 == strncmp(token,
"map_Pm", 6)) && IS_SPACE(token[6])) {
2266 &(material.metallic_texopt), token);
2271 if ((0 == strncmp(token,
"map_Ps", 6)) && IS_SPACE(token[6])) {
2274 &(material.sheen_texopt), token);
2279 if ((0 == strncmp(token,
"map_Ke", 6)) && IS_SPACE(token[6])) {
2282 &(material.emissive_texopt), token);
2287 if ((0 == strncmp(token,
"norm", 4)) && IS_SPACE(token[4])) {
2290 &(material.normal_texopt), token);
2295 const char *_space = strchr(token,
' ');
2297 _space = strchr(token,
'\t');
2300 std::ptrdiff_t len = _space - token;
2301 std::string key(token, static_cast<size_t>(len));
2302 std::string value = _space + 1;
2303 material.unknown_parameter.insert(
2304 std::pair<std::string, std::string>(key, value));
2308 material_map->insert(std::pair<std::string, int>(
2309 material.name, static_cast<int>(materials->size())));
2310 materials->push_back(material);
2313 (*warning) = warn_ss.str();
2318 std::vector<material_t> *materials,
2319 std::map<std::string, int> *matMap,
2320 std::string *warn, std::string *err) {
2321 if (!m_mtlBaseDir.empty()) {
2329 std::vector<std::string> paths;
2330 std::istringstream f(m_mtlBaseDir);
2333 while (getline(f, s, sep)) {
2337 for (
size_t i = 0; i < paths.size(); i++) {
2338 std::string filepath = JoinPath(paths[i], matId);
2340 std::ifstream matIStream(filepath.c_str());
2342 LoadMtl(matMap, materials, &matIStream, warn, err);
2348 std::stringstream ss;
2349 ss <<
"Material file [ " << matId
2350 <<
" ] not found in a path : " << m_mtlBaseDir <<
"\n";
2352 (*warn) += ss.str();
2357 std::string filepath = matId;
2358 std::ifstream matIStream(filepath.c_str());
2360 LoadMtl(matMap, materials, &matIStream, warn, err);
2365 std::stringstream ss;
2366 ss <<
"Material file [ " << filepath
2367 <<
" ] not found in a path : " << m_mtlBaseDir <<
"\n";
2369 (*warn) += ss.str();
2377 std::vector<material_t> *materials,
2378 std::map<std::string, int> *matMap,
2379 std::string *warn, std::string *err) {
2383 std::stringstream ss;
2384 ss <<
"Material stream in error state. \n";
2386 (*warn) += ss.str();
2391 LoadMtl(matMap, materials, &m_inStream, warn, err);
2396 bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
2397 std::vector<material_t> *materials, std::string *warn,
2398 std::string *err,
const char *filename,
const char *mtl_basedir,
2399 bool triangulate,
bool default_vcols_fallback) {
2400 attrib->vertices.clear();
2401 attrib->normals.clear();
2402 attrib->texcoords.clear();
2403 attrib->colors.clear();
2406 std::stringstream errss;
2408 std::ifstream ifs(filename);
2410 errss <<
"Cannot open file [" << filename <<
"]\n";
2412 (*err) = errss.str();
2417 std::string baseDir = mtl_basedir ? mtl_basedir :
"";
2418 if (!baseDir.empty()) {
2420 const char dirsep =
'/';
2422 const char dirsep =
'\\';
2424 if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep;
2426 MaterialFileReader matFileReader(baseDir);
2428 return LoadObj(attrib, shapes, materials, warn, err, &ifs, &matFileReader,
2429 triangulate, default_vcols_fallback);
2432 bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
2433 std::vector<material_t> *materials, std::string *warn,
2434 std::string *err, std::istream *inStream,
2435 MaterialReader *readMatFn ,
bool triangulate,
2436 bool default_vcols_fallback) {
2437 std::stringstream errss;
2439 std::vector<real_t> v;
2440 std::vector<real_t> vn;
2441 std::vector<real_t> vt;
2442 std::vector<real_t> vc;
2443 std::vector<skin_weight_t> vw;
2444 std::vector<tag_t> tags;
2445 PrimGroup prim_group;
2449 std::set<std::string> material_filenames;
2450 std::map<std::string, int> material_map;
2454 unsigned int current_smoothing_id =
2457 int greatest_v_idx = -1;
2458 int greatest_vn_idx = -1;
2459 int greatest_vt_idx = -1;
2463 bool found_all_colors =
true;
2465 size_t line_num = 0;
2466 std::string linebuf;
2467 while (inStream->peek() != -1) {
2468 safeGetline(*inStream, linebuf);
2473 if (linebuf.size() > 0) {
2474 if (linebuf[linebuf.size() - 1] ==
'\n')
2475 linebuf.erase(linebuf.size() - 1);
2477 if (linebuf.size() > 0) {
2478 if (linebuf[linebuf.size() - 1] ==
'\r')
2479 linebuf.erase(linebuf.size() - 1);
2483 if (linebuf.empty()) {
2488 const char *token = linebuf.c_str();
2489 token += strspn(token,
" \t");
2492 if (token[0] ==
'\0')
continue;
2494 if (token[0] ==
'#')
continue;
2497 if (token[0] ==
'v' && IS_SPACE((token[1]))) {
2502 found_all_colors &= parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token);
2508 if (found_all_colors || default_vcols_fallback) {
2518 if (token[0] ==
'v' && token[1] ==
'n' && IS_SPACE((token[2]))) {
2521 parseReal3(&x, &y, &z, &token);
2529 if (token[0] ==
'v' && token[1] ==
't' && IS_SPACE((token[2]))) {
2532 parseReal2(&x, &y, &token);
2539 if (token[0] ==
'v' && token[1] ==
'w' && IS_SPACE((token[2]))) {
2548 vid = parseInt(&token);
2554 while (!IS_NEW_LINE(token[0])) {
2558 parseReal2(&j, &w, &token, -1.0);
2560 if (j < static_cast<real_t>(0)) {
2562 std::stringstream ss;
2563 ss <<
"Failed parse `vw' line. joint_id is negative. " 2565 << line_num <<
".)\n";
2571 joint_and_weight_t jw;
2573 jw.joint_id = int(j);
2576 sw.weightValues.push_back(jw);
2578 size_t n = strspn(token,
" \t\r");
2586 if (token[0] ==
'l' && IS_SPACE((token[1]))) {
2591 while (!IS_NEW_LINE(token[0])) {
2593 if (!parseTriple(&token, static_cast<int>(v.size() / 3),
2594 static_cast<int>(vn.size() / 3),
2595 static_cast<int>(vt.size() / 2), &vi)) {
2597 std::stringstream ss;
2598 ss <<
"Failed parse `l' line(e.g. zero value for vertex index. " 2600 << line_num <<
".)\n";
2606 line.vertex_indices.push_back(vi);
2608 size_t n = strspn(token,
" \t\r");
2612 prim_group.lineGroup.push_back(line);
2618 if (token[0] ==
'p' && IS_SPACE((token[1]))) {
2623 while (!IS_NEW_LINE(token[0])) {
2625 if (!parseTriple(&token, static_cast<int>(v.size() / 3),
2626 static_cast<int>(vn.size() / 3),
2627 static_cast<int>(vt.size() / 2), &vi)) {
2629 std::stringstream ss;
2630 ss <<
"Failed parse `p' line(e.g. zero value for vertex index. " 2632 << line_num <<
".)\n";
2638 pts.vertex_indices.push_back(vi);
2640 size_t n = strspn(token,
" \t\r");
2644 prim_group.pointsGroup.push_back(pts);
2650 if (token[0] ==
'f' && IS_SPACE((token[1]))) {
2652 token += strspn(token,
" \t");
2656 face.smoothing_group_id = current_smoothing_id;
2657 face.vertex_indices.reserve(3);
2659 while (!IS_NEW_LINE(token[0])) {
2661 if (!parseTriple(&token, static_cast<int>(v.size() / 3),
2662 static_cast<int>(vn.size() / 3),
2663 static_cast<int>(vt.size() / 2), &vi)) {
2665 std::stringstream ss;
2666 ss <<
"Failed parse `f' line(e.g. zero value for face index. line " 2667 << line_num <<
".)\n";
2673 greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx;
2675 greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx;
2677 greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx;
2679 face.vertex_indices.push_back(vi);
2680 size_t n = strspn(token,
" \t\r");
2685 prim_group.faceGroup.push_back(face);
2691 if ((0 == strncmp(token,
"usemtl", 6))) {
2693 std::string namebuf = parseString(&token);
2695 int newMaterialId = -1;
2696 std::map<std::string, int>::const_iterator it =
2697 material_map.find(namebuf);
2698 if (it != material_map.end()) {
2699 newMaterialId = it->second;
2703 (*warn) +=
"material [ '" + namebuf +
"' ] not found in .mtl\n";
2707 if (newMaterialId != material) {
2711 exportGroupsToShape(&shape, prim_group, tags, material, name,
2712 triangulate, v, warn);
2713 prim_group.faceGroup.clear();
2714 material = newMaterialId;
2721 if ((0 == strncmp(token,
"mtllib", 6)) && IS_SPACE((token[6]))) {
2725 std::vector<std::string> filenames;
2726 SplitString(std::string(token),
' ',
'\\', filenames);
2728 if (filenames.empty()) {
2730 std::stringstream ss;
2731 ss <<
"Looks like empty filename for mtllib. Use default " 2733 << line_num <<
".)\n";
2735 (*warn) += ss.str();
2739 for (
size_t s = 0; s < filenames.size(); s++) {
2740 if (material_filenames.count(filenames[s]) > 0) {
2745 std::string warn_mtl;
2746 std::string err_mtl;
2747 bool ok = (*readMatFn)(filenames[s].c_str(), materials,
2748 &material_map, &warn_mtl, &err_mtl);
2749 if (warn && (!warn_mtl.empty())) {
2750 (*warn) += warn_mtl;
2753 if (err && (!err_mtl.empty())) {
2759 material_filenames.insert(filenames[s]);
2767 "Failed to load material file(s). Use default " 2778 if (token[0] ==
'g' && IS_SPACE((token[1]))) {
2780 bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name,
2781 triangulate, v, warn);
2784 if (shape.mesh.indices.size() > 0) {
2785 shapes->push_back(shape);
2793 std::vector<std::string> names;
2795 while (!IS_NEW_LINE(token[0])) {
2796 std::string str = parseString(&token);
2797 names.push_back(str);
2798 token += strspn(token,
" \t\r");
2803 if (names.size() < 2) {
2806 std::stringstream ss;
2807 ss <<
"Empty group name. line: " << line_num <<
"\n";
2808 (*warn) += ss.str();
2812 std::stringstream ss;
2819 for (
size_t i = 2; i < names.size(); i++) {
2820 ss <<
" " << names[i];
2830 if (token[0] ==
'o' && IS_SPACE((token[1]))) {
2832 bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name,
2833 triangulate, v, warn);
2836 if (shape.mesh.indices.size() > 0 || shape.lines.indices.size() > 0 ||
2837 shape.points.indices.size() > 0) {
2838 shapes->push_back(shape);
2847 std::stringstream ss;
2854 if (token[0] ==
't' && IS_SPACE(token[1])) {
2855 const int max_tag_nums = 8192;
2860 tag.name = parseString(&token);
2862 tag_sizes ts = parseTagTriple(&token);
2864 if (ts.num_ints < 0) {
2867 if (ts.num_ints > max_tag_nums) {
2868 ts.num_ints = max_tag_nums;
2871 if (ts.num_reals < 0) {
2874 if (ts.num_reals > max_tag_nums) {
2875 ts.num_reals = max_tag_nums;
2878 if (ts.num_strings < 0) {
2881 if (ts.num_strings > max_tag_nums) {
2882 ts.num_strings = max_tag_nums;
2885 tag.intValues.resize(static_cast<size_t>(ts.num_ints));
2887 for (
size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
2888 tag.intValues[i] = parseInt(&token);
2891 tag.floatValues.resize(static_cast<size_t>(ts.num_reals));
2892 for (
size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) {
2893 tag.floatValues[i] = parseReal(&token);
2896 tag.stringValues.resize(static_cast<size_t>(ts.num_strings));
2897 for (
size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
2898 tag.stringValues[i] = parseString(&token);
2901 tags.push_back(tag);
2906 if (token[0] ==
's' && IS_SPACE(token[1])) {
2911 token += strspn(token,
" \t");
2913 if (token[0] ==
'\0') {
2917 if (token[0] ==
'\r' || token[1] ==
'\n') {
2921 if (strlen(token) >= 3 && token[0] ==
'o' && token[1] ==
'f' &&
2923 current_smoothing_id = 0;
2926 int smGroupId = parseInt(&token);
2927 if (smGroupId < 0) {
2930 current_smoothing_id = 0;
2932 current_smoothing_id =
static_cast<unsigned int>(smGroupId);
2943 if (!found_all_colors && !default_vcols_fallback) {
2947 if (greatest_v_idx >= static_cast<int>(v.size() / 3)) {
2949 std::stringstream ss;
2950 ss <<
"Vertex indices out of bounds (line " << line_num <<
".)\n\n";
2951 (*warn) += ss.str();
2954 if (greatest_vn_idx >= static_cast<int>(vn.size() / 3)) {
2956 std::stringstream ss;
2957 ss <<
"Vertex normal indices out of bounds (line " << line_num <<
".)\n\n";
2958 (*warn) += ss.str();
2961 if (greatest_vt_idx >= static_cast<int>(vt.size() / 2)) {
2963 std::stringstream ss;
2964 ss <<
"Vertex texcoord indices out of bounds (line " << line_num <<
".)\n\n";
2965 (*warn) += ss.str();
2969 bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name,
2970 triangulate, v, warn);
2975 if (ret || shape.mesh.indices
2977 shapes->push_back(shape);
2982 (*err) += errss.str();
2985 attrib->vertices.swap(v);
2986 attrib->vertex_weights.swap(v);
2987 attrib->normals.swap(vn);
2988 attrib->texcoords.swap(vt);
2989 attrib->texcoord_ws.swap(vt);
2990 attrib->colors.swap(vc);
2991 attrib->skin_weights.swap(vw);
2998 MaterialReader *readMatFn ,
3000 std::string *err ) {
3001 std::stringstream errss;
3004 std::set<std::string> material_filenames;
3005 std::map<std::string, int> material_map;
3006 int material_id = -1;
3008 std::vector<index_t> indices;
3009 std::vector<material_t> materials;
3010 std::vector<std::string> names;
3012 std::vector<const char *> names_out;
3014 std::string linebuf;
3015 while (inStream.peek() != -1) {
3016 safeGetline(inStream, linebuf);
3019 if (linebuf.size() > 0) {
3020 if (linebuf[linebuf.size() - 1] ==
'\n')
3021 linebuf.erase(linebuf.size() - 1);
3023 if (linebuf.size() > 0) {
3024 if (linebuf[linebuf.size() - 1] ==
'\r')
3025 linebuf.erase(linebuf.size() - 1);
3029 if (linebuf.empty()) {
3034 const char *token = linebuf.c_str();
3035 token += strspn(token,
" \t");
3038 if (token[0] ==
'\0')
continue;
3040 if (token[0] ==
'#')
continue;
3043 if (token[0] ==
'v' && IS_SPACE((token[1]))) {
3047 parseV(&x, &y, &z, &w, &token);
3048 if (callback.vertex_cb) {
3049 callback.vertex_cb(user_data, x, y, z, w);
3055 if (token[0] ==
'v' && token[1] ==
'n' && IS_SPACE((token[2]))) {
3058 parseReal3(&x, &y, &z, &token);
3059 if (callback.normal_cb) {
3060 callback.normal_cb(user_data, x, y, z);
3066 if (token[0] ==
'v' && token[1] ==
't' && IS_SPACE((token[2]))) {
3069 parseReal3(&x, &y, &z, &token);
3070 if (callback.texcoord_cb) {
3071 callback.texcoord_cb(user_data, x, y, z);
3077 if (token[0] ==
'f' && IS_SPACE((token[1]))) {
3079 token += strspn(token,
" \t");
3082 while (!IS_NEW_LINE(token[0])) {
3083 vertex_index_t vi = parseRawTriple(&token);
3086 idx.vertex_index = vi.v_idx;
3087 idx.normal_index = vi.vn_idx;
3088 idx.texcoord_index = vi.vt_idx;
3090 indices.push_back(idx);
3091 size_t n = strspn(token,
" \t\r");
3095 if (callback.index_cb && indices.size() > 0) {
3096 callback.index_cb(user_data, &indices.at(0),
3097 static_cast<int>(indices.size()));
3104 if ((0 == strncmp(token,
"usemtl", 6)) && IS_SPACE((token[6]))) {
3106 std::stringstream ss;
3108 std::string namebuf = ss.str();
3110 int newMaterialId = -1;
3111 std::map<std::string, int>::const_iterator it =
3112 material_map.find(namebuf);
3113 if (it != material_map.end()) {
3114 newMaterialId = it->second;
3117 if (warn && (!callback.usemtl_cb)) {
3118 (*warn) +=
"material [ " + namebuf +
" ] not found in .mtl\n";
3122 if (newMaterialId != material_id) {
3123 material_id = newMaterialId;
3126 if (callback.usemtl_cb) {
3127 callback.usemtl_cb(user_data, namebuf.c_str(), material_id);
3134 if ((0 == strncmp(token,
"mtllib", 6)) && IS_SPACE((token[6]))) {
3138 std::vector<std::string> filenames;
3139 SplitString(std::string(token),
' ',
'\\', filenames);
3141 if (filenames.empty()) {
3144 "Looks like empty filename for mtllib. Use default " 3149 for (
size_t s = 0; s < filenames.size(); s++) {
3150 if (material_filenames.count(filenames[s]) > 0) {
3155 std::string warn_mtl;
3156 std::string err_mtl;
3157 bool ok = (*readMatFn)(filenames[s].c_str(), &materials,
3158 &material_map, &warn_mtl, &err_mtl);
3160 if (warn && (!warn_mtl.empty())) {
3161 (*warn) += warn_mtl;
3164 if (err && (!err_mtl.empty())) {
3170 material_filenames.insert(filenames[s]);
3178 "Failed to load material file(s). Use default " 3182 if (callback.mtllib_cb) {
3183 callback.mtllib_cb(user_data, &materials.at(0),
3184 static_cast<int>(materials.size()));
3194 if (token[0] ==
'g' && IS_SPACE((token[1]))) {
3197 while (!IS_NEW_LINE(token[0])) {
3198 std::string str = parseString(&token);
3199 names.push_back(str);
3200 token += strspn(token,
" \t\r");
3203 assert(names.size() > 0);
3205 if (callback.group_cb) {
3206 if (names.size() > 1) {
3208 names_out.resize(names.size() - 1);
3209 for (
size_t j = 0; j < names_out.size(); j++) {
3210 names_out[j] = names[j + 1].c_str();
3212 callback.group_cb(user_data, &names_out.at(0),
3213 static_cast<int>(names_out.size()));
3216 callback.group_cb(user_data, NULL, 0);
3224 if (token[0] ==
'o' && IS_SPACE((token[1]))) {
3228 std::stringstream ss;
3230 std::string object_name = ss.str();
3232 if (callback.object_cb) {
3233 callback.object_cb(user_data, object_name.c_str());
3240 if (token[0] ==
't' && IS_SPACE(token[1])) {
3244 std::stringstream ss;
3246 tag.name = ss.str();
3248 token += tag.name.size() + 1;
3250 tag_sizes ts = parseTagTriple(&token);
3252 tag.intValues.resize(static_cast<size_t>(ts.num_ints));
3254 for (
size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
3255 tag.intValues[i] = atoi(token);
3256 token += strcspn(token,
"/ \t\r") + 1;
3259 tag.floatValues.resize(static_cast<size_t>(ts.num_reals));
3260 for (
size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) {
3261 tag.floatValues[i] = parseReal(&token);
3262 token += strcspn(token,
"/ \t\r") + 1;
3265 tag.stringValues.resize(static_cast<size_t>(ts.num_strings));
3266 for (
size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
3267 std::stringstream ss;
3269 tag.stringValues[i] = ss.str();
3270 token += tag.stringValues[i].size() + 1;
3273 tags.push_back(tag);
3281 (*err) += errss.str();
3288 const ObjReaderConfig &config) {
3289 std::string mtl_search_path;
3291 if (config.mtl_search_path.empty()) {
3296 size_t pos = filename.find_last_of(
"/\\");
3297 if (pos != std::string::npos) {
3298 mtl_search_path = filename.substr(0, pos);
3301 mtl_search_path = config.mtl_search_path;
3304 valid_ =
LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_,
3305 filename.c_str(), mtl_search_path.c_str(),
3306 config.triangulate, config.vertex_color);
3312 const std::string &mtl_text,
3313 const ObjReaderConfig &config) {
3314 std::stringbuf obj_buf(obj_text);
3315 std::stringbuf mtl_buf(mtl_text);
3317 std::istream obj_ifs(&obj_buf);
3318 std::istream mtl_ifs(&mtl_buf);
3320 MaterialStreamReader mtl_ss(mtl_ifs);
3322 valid_ =
LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_,
3323 &obj_ifs, &mtl_ss, config.triangulate, config.vertex_color);
3329 #pragma clang diagnostic pop
void(* group_cb)(void *user_data, const char **names, int num_names)
void(* object_cb)(void *user_data, const char *name)
std::string specular_highlight_texname
std::vector< joint_and_weight_t > weightValues
std::string specular_texname
const std::vector< material_t > & GetMaterials() const
const attrib_t & GetAttrib() const
texture_option_t alpha_texopt
std::vector< real_t > texcoords
bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, const char *linebuf)
Parse texture name and texture option for custom texture parameter through material::unknown_paramete...
std::string triangulation_method
const std::vector< shape_t > & GetShapes() const
MaterialFileReader(const std::string &mtl_basedir)
std::vector< index_t > indices
std::vector< std::string > stringValues
virtual bool operator()(const std::string &matId, std::vector< material_t > *materials, std::map< std::string, int > *matMap, std::string *warn, std::string *err) TINYOBJ_OVERRIDE
bool Valid() const
.obj was loaded or parsed correctly.
const std::string & Error() const
Error message(filled when Load or Parse failed)
real_t clearcoat_roughness
real_t anisotropy_rotation
void(* index_cb)(void *user_data, index_t *indices, int num_indices)
void(* mtllib_cb)(void *user_data, const material_t *materials, int num_materials)
virtual ~MaterialReader()
std::string reflection_texname
std::vector< skin_weight_t > skin_weights
texture_option_t displacement_texopt
std::vector< real_t > floatValues
std::string ambient_texname
virtual ~MaterialStreamReader() TINYOBJ_OVERRIDE
Wavefront .obj reader class(v2 API)
std::vector< index_t > indices
const std::string & Warning() const
Warning message(may be filled after Load or Parse)
std::string mtl_search_path
Search path to .mtl file.
bool ParseFromString(const std::string &obj_text, const std::string &mtl_text, const ObjReaderConfig &config=ObjReaderConfig())
Parse .obj from a text string.
texture_option_t roughness_texopt
std::string sheen_texname
std::vector< int > material_ids
texture_option_t specular_texopt
std::vector< int > num_line_vertices
void LoadMtl(std::map< std::string, int > *material_map, std::vector< material_t > *materials, std::istream *inStream, std::string *warning, std::string *err)
Loads materials into std::map.
std::vector< unsigned int > smoothing_group_ids
texture_option_t diffuse_texopt
real_t clearcoat_thickness
virtual bool operator()(const std::string &matId, std::vector< material_t > *materials, std::map< std::string, int > *matMap, std::string *warn, std::string *err) TINYOBJ_OVERRIDE
std::string diffuse_texname
virtual bool operator()(const std::string &matId, std::vector< material_t > *materials, std::map< std::string, int > *matMap, std::string *warn, std::string *err)=0
void(* usemtl_cb)(void *user_data, const char *name, int material_id)
texture_option_t normal_texopt
std::vector< real_t > vertex_weights
std::vector< real_t > normals
std::string roughness_texname
std::vector< unsigned char > num_face_vertices
__host__ __device__ float3 cross(const float3 &a, const float3 &b)
calculate the cross product between vectors a and b.
std::vector< real_t > texcoord_ws
std::vector< tag_t > tags
texture_option_t sheen_texopt
std::string metallic_texname
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, void *user_data=NULL, MaterialReader *readMatFn=NULL, std::string *warn=NULL, std::string *err=NULL)
Loads .obj from a file with custom user callback.
texture_option_t emissive_texopt
std::map< std::string, std::string > unknown_parameter
const std::vector< real_t > & GetVertices() const
std::vector< int > intValues
texture_option_t ambient_texopt
bool LoadObj(attrib_t *attrib, std::vector< shape_t > *shapes, std::vector< material_t > *materials, std::string *warn, std::string *err, const char *filename, const char *mtl_basedir=NULL, bool triangulate=true, bool default_vcols_fallback=true)
==>>========= Legacy v1 API =============================================
std::string alpha_texname
std::string normal_texname
std::string displacement_texname
void(* texcoord_cb)(void *user_data, real_t x, real_t y, real_t z)
std::vector< real_t > vertices
std::string emissive_texname
texture_option_t specular_highlight_texopt
texture_option_t metallic_texopt
std::vector< index_t > indices
virtual ~MaterialFileReader() TINYOBJ_OVERRIDE
bool vertex_color
Parse vertex color.
texture_option_t bump_texopt
texture_option_t reflection_texopt
void(* vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w)
const std::vector< real_t > & GetVertexWeights() const
MaterialStreamReader(std::istream &inStream)
bool ParseFromFile(const std::string &filename, const ObjReaderConfig &config=ObjReaderConfig())
Load .obj and .mtl from a file.
std::vector< real_t > colors
void(* normal_cb)(void *user_data, real_t x, real_t y, real_t z)