42#ifndef TINY_OBJ_LOADER_H_
43#define TINY_OBJ_LOADER_H_
100#ifdef TINYOBJLOADER_USE_DOUBLE
102 typedef double real_t;
105 typedef float real_t;
111 TEXTURE_TYPE_CUBE_TOP,
112 TEXTURE_TYPE_CUBE_BOTTOM,
113 TEXTURE_TYPE_CUBE_FRONT,
114 TEXTURE_TYPE_CUBE_BACK,
115 TEXTURE_TYPE_CUBE_LEFT,
116 TEXTURE_TYPE_CUBE_RIGHT
124 real_t origin_offset[3];
126 real_t turbulence[3];
132 real_t bump_multiplier;
141 real_t transmittance[3];
151 std::string ambient_texname;
152 std::string diffuse_texname;
153 std::string specular_texname;
154 std::string specular_highlight_texname;
155 std::string bump_texname;
156 std::string displacement_texname;
157 std::string alpha_texname;
172 real_t clearcoat_thickness;
173 real_t clearcoat_roughness;
175 real_t anisotropy_rotation;
178 std::string roughness_texname;
179 std::string metallic_texname;
180 std::string sheen_texname;
181 std::string emissive_texname;
182 std::string normal_texname;
192 std::map<std::string, std::string> unknown_parameter;
198 std::vector<int> intValues;
199 std::vector<real_t> floatValues;
200 std::vector<std::string> stringValues;
212 std::vector<index_t> indices;
213 std::vector<unsigned char> num_face_vertices;
216 std::vector<int> material_ids;
217 std::vector<tag_t> tags;
227 std::vector<real_t> vertices;
228 std::vector<real_t> normals;
229 std::vector<real_t> texcoords;
234 void (*vertex_cb)(
void *user_data, real_t x, real_t y, real_t z, real_t w);
235 void (*normal_cb)(
void *user_data, real_t x, real_t y, real_t z);
239 void (*texcoord_cb)(
void *user_data, real_t x, real_t y, real_t z);
244 void (*index_cb)(
void *user_data,
index_t *indices,
int num_indices);
248 void (*usemtl_cb)(
void *user_data,
const char *name,
int material_id);
250 void (*mtllib_cb)(
void *user_data,
const material_t *materials,
253 void (*group_cb)(
void *user_data,
const char **names,
int num_names);
254 void (*object_cb)(
void *user_data,
const char *name);
272 virtual bool operator()(
const std::string &matId,
273 std::vector<material_t> *materials,
274 std::map<std::string, int> *matMap,
275 std::string *err) = 0;
281 : m_mtlBaseDir(mtl_basedir) {}
283 virtual bool operator()(
const std::string &matId,
284 std::vector<material_t> *materials,
285 std::map<std::string, int> *matMap, std::string *err);
288 std::string m_mtlBaseDir;
294 : m_inStream(inStream) {}
296 virtual bool operator()(
const std::string &matId,
297 std::vector<material_t> *materials,
298 std::map<std::string, int> *matMap, std::string *err);
301 std::istream &m_inStream;
314bool LoadObj(
attrib_t *attrib, std::vector<shape_t> *shapes,
315 std::vector<material_t> *materials, std::string *err,
316 const char *filename,
const char *mtl_basedir = NULL,
317 bool triangulate =
true);
325bool LoadObjWithCallback(std::istream &inStream,
const callback_t &callback,
326 void *user_data = NULL,
328 std::string *err = NULL);
334bool LoadObj(
attrib_t *attrib, std::vector<shape_t> *shapes,
335 std::vector<material_t> *materials, std::string *err,
337 bool triangulate =
true);
340void LoadMtl(std::map<std::string, int> *material_map,
341 std::vector<material_t> *materials, std::istream *inStream,
342 std::string *warning);
348#ifdef TINYOBJLOADER_IMPLEMENTATION
362MaterialReader::~MaterialReader() {}
364#define TINYOBJ_SSCANF_BUFFER_SIZE (4096)
367 int v_idx, vt_idx, vn_idx;
368 vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
369 explicit vertex_index(
int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
370 vertex_index(
int vidx,
int vtidx,
int vnidx)
371 : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
375 tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {}
382 std::vector<real_t> v;
383 std::vector<real_t> vn;
384 std::vector<real_t> vt;
389static std::istream &safeGetline(std::istream &is, std::string &t) {
398 std::istream::sentry se(is,
true);
399 std::streambuf *sb = is.rdbuf();
402 int c = sb->sbumpc();
407 if (sb->sgetc() ==
'\n') sb->sbumpc();
411 if (t.empty()) is.setstate(std::ios::eofbit);
414 t +=
static_cast<char>(c);
419#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
421 (static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
422#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
425static inline int fixIndex(
int idx,
int n) {
426 if (idx > 0)
return idx - 1;
427 if (idx == 0)
return 0;
431static inline std::string parseString(
const char **token) {
433 (*token) += strspn((*token),
" \t");
434 size_t e = strcspn((*token),
" \t\r");
435 s = std::string((*token), &(*token)[e]);
440static inline int parseInt(
const char **token) {
441 (*token) += strspn((*token),
" \t");
442 int i = atoi((*token));
443 (*token) += strcspn((*token),
" \t\r");
474static bool tryParseDouble(
const char *s,
const char *s_end,
double *result) {
479 double mantissa = 0.0;
493 char const *curr = s;
498 bool end_not_reached =
false;
505 if (*curr ==
'+' || *curr ==
'-') {
508 }
else if (IS_DIGIT(*curr)) {
514 end_not_reached = (curr != s_end);
515 while (end_not_reached && IS_DIGIT(*curr)) {
517 mantissa +=
static_cast<int>(*curr - 0x30);
520 end_not_reached = (curr != s_end);
524 if (read == 0)
goto fail;
526 if (!end_not_reached)
goto assemble;
532 end_not_reached = (curr != s_end);
533 while (end_not_reached && IS_DIGIT(*curr)) {
534 static const double pow_lut[] = {
535 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001,
537 const int lut_entries =
sizeof pow_lut /
sizeof pow_lut[0];
540 mantissa +=
static_cast<int>(*curr - 0x30) *
541 (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read));
544 end_not_reached = (curr != s_end);
546 }
else if (*curr ==
'e' || *curr ==
'E') {
551 if (!end_not_reached)
goto assemble;
554 if (*curr ==
'e' || *curr ==
'E') {
557 end_not_reached = (curr != s_end);
558 if (end_not_reached && (*curr ==
'+' || *curr ==
'-')) {
561 }
else if (IS_DIGIT(*curr)) {
568 end_not_reached = (curr != s_end);
569 while (end_not_reached && IS_DIGIT(*curr)) {
571 exponent +=
static_cast<int>(*curr - 0x30);
574 end_not_reached = (curr != s_end);
576 exponent *= (exp_sign ==
'+' ? 1 : -1);
577 if (read == 0)
goto fail;
582 (
sign ==
'+' ? 1 : -1) *
583 (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) : mantissa);
589static inline real_t parseReal(
const char **token,
double default_value = 0.0) {
590 (*token) += strspn((*token),
" \t");
591 const char *end = (*token) + strcspn((*token),
" \t\r");
592 double val = default_value;
593 tryParseDouble((*token), end, &val);
594 real_t f =
static_cast<real_t
>(val);
599static inline void parseReal2(real_t *x, real_t *y,
const char **token,
600 const double default_x = 0.0,
601 const double default_y = 0.0) {
602 (*x) = parseReal(token, default_x);
603 (*y) = parseReal(token, default_y);
606static inline void parseReal3(real_t *x, real_t *y, real_t *z,
const char **token,
607 const double default_x = 0.0,
608 const double default_y = 0.0,
609 const double default_z = 0.0) {
610 (*x) = parseReal(token, default_x);
611 (*y) = parseReal(token, default_y);
612 (*z) = parseReal(token, default_z);
615static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w,
616 const char **token,
const double default_x = 0.0,
617 const double default_y = 0.0,
618 const double default_z = 0.0,
619 const double default_w = 1.0) {
620 (*x) = parseReal(token, default_x);
621 (*y) = parseReal(token, default_y);
622 (*z) = parseReal(token, default_z);
623 (*w) = parseReal(token, default_w);
626static inline bool parseOnOff(
const char **token,
bool default_value =
true) {
627 (*token) += strspn((*token),
" \t");
628 const char *end = (*token) + strcspn((*token),
" \t\r");
630 bool ret = default_value;
631 if ((0 == strncmp((*token),
"on", 2))) {
633 }
else if ((0 == strncmp((*token),
"off", 3))) {
641static inline texture_type_t parseTextureType(
642 const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) {
643 (*token) += strspn((*token),
" \t");
644 const char *end = (*token) + strcspn((*token),
" \t\r");
645 texture_type_t ty = default_value;
647 if ((0 == strncmp((*token),
"cube_top", strlen(
"cube_top")))) {
648 ty = TEXTURE_TYPE_CUBE_TOP;
649 }
else if ((0 == strncmp((*token),
"cube_bottom", strlen(
"cube_bottom")))) {
650 ty = TEXTURE_TYPE_CUBE_BOTTOM;
651 }
else if ((0 == strncmp((*token),
"cube_left", strlen(
"cube_left")))) {
652 ty = TEXTURE_TYPE_CUBE_LEFT;
653 }
else if ((0 == strncmp((*token),
"cube_right", strlen(
"cube_right")))) {
654 ty = TEXTURE_TYPE_CUBE_RIGHT;
655 }
else if ((0 == strncmp((*token),
"cube_front", strlen(
"cube_front")))) {
656 ty = TEXTURE_TYPE_CUBE_FRONT;
657 }
else if ((0 == strncmp((*token),
"cube_back", strlen(
"cube_back")))) {
658 ty = TEXTURE_TYPE_CUBE_BACK;
659 }
else if ((0 == strncmp((*token),
"sphere", strlen(
"sphere")))) {
660 ty = TEXTURE_TYPE_SPHERE;
667static tag_sizes parseTagTriple(
const char **token) {
670 ts.num_ints = atoi((*token));
671 (*token) += strcspn((*token),
"/ \t\r");
672 if ((*token)[0] !=
'/') {
677 ts.num_reals = atoi((*token));
678 (*token) += strcspn((*token),
"/ \t\r");
679 if ((*token)[0] !=
'/') {
684 ts.num_strings = atoi((*token));
685 (*token) += strcspn((*token),
"/ \t\r") + 1;
691static vertex_index parseTriple(
const char **token,
int vsize,
int vnsize,
695 vi.v_idx = fixIndex(atoi((*token)), vsize);
696 (*token) += strcspn((*token),
"/ \t\r");
697 if ((*token)[0] !=
'/') {
703 if ((*token)[0] ==
'/') {
705 vi.vn_idx = fixIndex(atoi((*token)), vnsize);
706 (*token) += strcspn((*token),
"/ \t\r");
711 vi.vt_idx = fixIndex(atoi((*token)), vtsize);
712 (*token) += strcspn((*token),
"/ \t\r");
713 if ((*token)[0] !=
'/') {
719 vi.vn_idx = fixIndex(atoi((*token)), vnsize);
720 (*token) += strcspn((*token),
"/ \t\r");
725static vertex_index parseRawTriple(
const char **token) {
726 vertex_index vi(
static_cast<int>(0));
728 vi.v_idx = atoi((*token));
729 (*token) += strcspn((*token),
"/ \t\r");
730 if ((*token)[0] !=
'/') {
736 if ((*token)[0] ==
'/') {
738 vi.vn_idx = atoi((*token));
739 (*token) += strcspn((*token),
"/ \t\r");
744 vi.vt_idx = atoi((*token));
745 (*token) += strcspn((*token),
"/ \t\r");
746 if ((*token)[0] !=
'/') {
752 vi.vn_idx = atoi((*token));
753 (*token) += strcspn((*token),
"/ \t\r");
757static bool ParseTextureNameAndOption(std::string *texname,
758 texture_option_t *texopt,
759 const char *linebuf,
const bool is_bump) {
761 bool found_texname =
false;
762 std::string texture_name;
766 texopt->imfchan =
'l';
768 texopt->imfchan =
'm';
770 texopt->bump_multiplier = 1.0f;
771 texopt->clamp =
false;
772 texopt->blendu =
true;
773 texopt->blendv =
true;
774 texopt->sharpness = 1.0f;
775 texopt->brightness = 0.0f;
776 texopt->contrast = 1.0f;
777 texopt->origin_offset[0] = 0.0f;
778 texopt->origin_offset[1] = 0.0f;
779 texopt->origin_offset[2] = 0.0f;
780 texopt->scale[0] = 1.0f;
781 texopt->scale[1] = 1.0f;
782 texopt->scale[2] = 1.0f;
783 texopt->turbulence[0] = 0.0f;
784 texopt->turbulence[1] = 0.0f;
785 texopt->turbulence[2] = 0.0f;
786 texopt->type = TEXTURE_TYPE_NONE;
788 const char *token = linebuf;
790 while (!IS_NEW_LINE((*token))) {
791 if ((0 == strncmp(token,
"-blendu", 7)) && IS_SPACE((token[7]))) {
793 texopt->blendu = parseOnOff(&token,
true);
794 }
else if ((0 == strncmp(token,
"-blendv", 7)) && IS_SPACE((token[7]))) {
796 texopt->blendv = parseOnOff(&token,
true);
797 }
else if ((0 == strncmp(token,
"-clamp", 6)) && IS_SPACE((token[6]))) {
799 texopt->clamp = parseOnOff(&token,
true);
800 }
else if ((0 == strncmp(token,
"-boost", 6)) && IS_SPACE((token[6]))) {
802 texopt->sharpness = parseReal(&token, 1.0);
803 }
else if ((0 == strncmp(token,
"-bm", 3)) && IS_SPACE((token[3]))) {
805 texopt->bump_multiplier = parseReal(&token, 1.0);
806 }
else if ((0 == strncmp(token,
"-o", 2)) && IS_SPACE((token[2]))) {
808 parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]),
809 &(texopt->origin_offset[2]), &token);
810 }
else if ((0 == strncmp(token,
"-s", 2)) && IS_SPACE((token[2]))) {
812 parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]),
813 &token, 1.0, 1.0, 1.0);
814 }
else if ((0 == strncmp(token,
"-t", 2)) && IS_SPACE((token[2]))) {
816 parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]),
817 &(texopt->turbulence[2]), &token);
818 }
else if ((0 == strncmp(token,
"-type", 5)) && IS_SPACE((token[5]))) {
820 texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE);
821 }
else if ((0 == strncmp(token,
"-imfchan", 8)) && IS_SPACE((token[8]))) {
823 token += strspn(token,
" \t");
824 const char *end = token + strcspn(token,
" \t\r");
825 if ((end - token) == 1) {
826 texopt->imfchan = (*token);
829 }
else if ((0 == strncmp(token,
"-mm", 3)) && IS_SPACE((token[3]))) {
831 parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0);
834 token += strspn(token,
" \t");
835 size_t len = strcspn(token,
" \t\r");
836 texture_name = std::string(token, token + len);
839 token += strspn(token,
" \t");
841 found_texname =
true;
846 (*texname) = texture_name;
853static void InitMaterial(material_t *material) {
855 material->ambient_texname =
"";
856 material->diffuse_texname =
"";
857 material->specular_texname =
"";
858 material->specular_highlight_texname =
"";
859 material->bump_texname =
"";
860 material->displacement_texname =
"";
861 material->alpha_texname =
"";
862 for (
int i = 0; i < 3; i++) {
863 material->ambient[i] = 0.f;
864 material->diffuse[i] = 0.f;
865 material->specular[i] = 0.f;
866 material->transmittance[i] = 0.f;
867 material->emission[i] = 0.f;
870 material->dissolve = 1.f;
871 material->shininess = 1.f;
874 material->roughness = 0.f;
875 material->metallic = 0.f;
876 material->sheen = 0.f;
877 material->clearcoat_thickness = 0.f;
878 material->clearcoat_roughness = 0.f;
879 material->anisotropy_rotation = 0.f;
880 material->anisotropy = 0.f;
881 material->roughness_texname =
"";
882 material->metallic_texname =
"";
883 material->sheen_texname =
"";
884 material->emissive_texname =
"";
885 material->normal_texname =
"";
887 material->unknown_parameter.clear();
890static bool exportFaceGroupToShape(
891 shape_t *shape,
const std::vector<std::vector<vertex_index> > &faceGroup,
892 const std::vector<tag_t> &tags,
const int material_id,
893 const std::string &name,
bool triangulate) {
894 if (faceGroup.empty()) {
899 for (
size_t i = 0; i < faceGroup.size(); i++) {
900 const std::vector<vertex_index> &face = faceGroup[i];
902 vertex_index i0 = face[0];
904 vertex_index i2 = face[1];
906 size_t npolys = face.size();
910 for (
size_t k = 2; k < npolys; k++) {
914 index_t idx0, idx1, idx2;
915 idx0.vertex_index = i0.v_idx;
916 idx0.normal_index = i0.vn_idx;
917 idx0.texcoord_index = i0.vt_idx;
918 idx1.vertex_index = i1.v_idx;
919 idx1.normal_index = i1.vn_idx;
920 idx1.texcoord_index = i1.vt_idx;
921 idx2.vertex_index = i2.v_idx;
922 idx2.normal_index = i2.vn_idx;
923 idx2.texcoord_index = i2.vt_idx;
925 shape->mesh.indices.push_back(idx0);
926 shape->mesh.indices.push_back(idx1);
927 shape->mesh.indices.push_back(idx2);
929 shape->mesh.num_face_vertices.push_back(3);
930 shape->mesh.material_ids.push_back(material_id);
933 for (
size_t k = 0; k < npolys; k++) {
935 idx.vertex_index = face[k].v_idx;
936 idx.normal_index = face[k].vn_idx;
937 idx.texcoord_index = face[k].vt_idx;
938 shape->mesh.indices.push_back(idx);
941 shape->mesh.num_face_vertices.push_back(
942 static_cast<unsigned char>(npolys));
943 shape->mesh.material_ids.push_back(material_id);
948 shape->mesh.tags = tags;
955static void SplitString(
const std::string &s,
char delim,
956 std::vector<std::string> &elems) {
957 std::stringstream ss;
960 while (std::getline(ss, item, delim)) {
961 elems.push_back(item);
965void LoadMtl(std::map<std::string, int> *material_map,
966 std::vector<material_t> *materials, std::istream *inStream,
967 std::string *warning) {
970 InitMaterial(&material);
976 std::stringstream ss;
979 while (inStream->peek() != -1) {
980 safeGetline(*inStream, linebuf);
983 if (linebuf.size() > 0) {
984 linebuf = linebuf.substr(0, linebuf.find_last_not_of(
" \t") + 1);
988 if (linebuf.size() > 0) {
989 if (linebuf[linebuf.size() - 1] ==
'\n')
990 linebuf.erase(linebuf.size() - 1);
992 if (linebuf.size() > 0) {
993 if (linebuf[linebuf.size() - 1] ==
'\r')
994 linebuf.erase(linebuf.size() - 1);
998 if (linebuf.empty()) {
1003 const char *token = linebuf.c_str();
1004 token += strspn(token,
" \t");
1007 if (token[0] ==
'\0')
continue;
1009 if (token[0] ==
'#')
continue;
1012 if ((0 == strncmp(token,
"newmtl", 6)) && IS_SPACE((token[6]))) {
1014 if (!material.name.empty()) {
1015 material_map->insert(std::pair<std::string, int>(
1016 material.name,
static_cast<int>(materials->size())));
1017 materials->push_back(material);
1021 InitMaterial(&material);
1027 char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1030 sscanf_s(token,
"%s", namebuf, (
unsigned)_countof(namebuf));
1032 std::sscanf(token,
"%s", namebuf);
1034 material.name = namebuf;
1039 if (token[0] ==
'K' && token[1] ==
'a' && IS_SPACE((token[2]))) {
1042 parseReal3(&r, &g, &b, &token);
1043 material.ambient[0] = r;
1044 material.ambient[1] = g;
1045 material.ambient[2] = b;
1050 if (token[0] ==
'K' && token[1] ==
'd' && IS_SPACE((token[2]))) {
1053 parseReal3(&r, &g, &b, &token);
1054 material.diffuse[0] = r;
1055 material.diffuse[1] = g;
1056 material.diffuse[2] = b;
1061 if (token[0] ==
'K' && token[1] ==
's' && IS_SPACE((token[2]))) {
1064 parseReal3(&r, &g, &b, &token);
1065 material.specular[0] = r;
1066 material.specular[1] = g;
1067 material.specular[2] = b;
1072 if ((token[0] ==
'K' && token[1] ==
't' && IS_SPACE((token[2]))) ||
1073 (token[0] ==
'T' && token[1] ==
'f' && IS_SPACE((token[2])))) {
1076 parseReal3(&r, &g, &b, &token);
1077 material.transmittance[0] = r;
1078 material.transmittance[1] = g;
1079 material.transmittance[2] = b;
1084 if (token[0] ==
'N' && token[1] ==
'i' && IS_SPACE((token[2]))) {
1086 material.ior = parseReal(&token);
1091 if (token[0] ==
'K' && token[1] ==
'e' && IS_SPACE(token[2])) {
1094 parseReal3(&r, &g, &b, &token);
1095 material.emission[0] = r;
1096 material.emission[1] = g;
1097 material.emission[2] = b;
1102 if (token[0] ==
'N' && token[1] ==
's' && IS_SPACE(token[2])) {
1104 material.shininess = parseReal(&token);
1109 if (0 == strncmp(token,
"illum", 5) && IS_SPACE(token[5])) {
1111 material.illum = parseInt(&token);
1116 if ((token[0] ==
'd' && IS_SPACE(token[1]))) {
1118 material.dissolve = parseReal(&token);
1121 ss <<
"WARN: Both `d` and `Tr` parameters defined for \""
1122 << material.name <<
"\". Use the value of `d` for dissolve."
1128 if (token[0] ==
'T' && token[1] ==
'r' && IS_SPACE(token[2])) {
1132 ss <<
"WARN: Both `d` and `Tr` parameters defined for \""
1133 << material.name <<
"\". Use the value of `d` for dissolve."
1139 material.dissolve = 1.0f - parseReal(&token);
1146 if (token[0] ==
'P' && token[1] ==
'r' && IS_SPACE(token[2])) {
1148 material.roughness = parseReal(&token);
1153 if (token[0] ==
'P' && token[1] ==
'm' && IS_SPACE(token[2])) {
1155 material.metallic = parseReal(&token);
1160 if (token[0] ==
'P' && token[1] ==
's' && IS_SPACE(token[2])) {
1162 material.sheen = parseReal(&token);
1167 if (token[0] ==
'P' && token[1] ==
'c' && IS_SPACE(token[2])) {
1169 material.clearcoat_thickness = parseReal(&token);
1174 if ((0 == strncmp(token,
"Pcr", 3)) && IS_SPACE(token[3])) {
1176 material.clearcoat_roughness = parseReal(&token);
1181 if ((0 == strncmp(token,
"aniso", 5)) && IS_SPACE(token[5])) {
1183 material.anisotropy = parseReal(&token);
1188 if ((0 == strncmp(token,
"anisor", 6)) && IS_SPACE(token[6])) {
1190 material.anisotropy_rotation = parseReal(&token);
1195 if ((0 == strncmp(token,
"map_Ka", 6)) && IS_SPACE(token[6])) {
1197 ParseTextureNameAndOption(&(material.ambient_texname),
1198 &(material.ambient_texopt), token,
1204 if ((0 == strncmp(token,
"map_Kd", 6)) && IS_SPACE(token[6])) {
1206 ParseTextureNameAndOption(&(material.diffuse_texname),
1207 &(material.diffuse_texopt), token,
1213 if ((0 == strncmp(token,
"map_Ks", 6)) && IS_SPACE(token[6])) {
1215 ParseTextureNameAndOption(&(material.specular_texname),
1216 &(material.specular_texopt), token,
1222 if ((0 == strncmp(token,
"map_Ns", 6)) && IS_SPACE(token[6])) {
1224 ParseTextureNameAndOption(&(material.specular_highlight_texname),
1225 &(material.specular_highlight_texopt), token,
1231 if ((0 == strncmp(token,
"map_bump", 8)) && IS_SPACE(token[8])) {
1233 ParseTextureNameAndOption(&(material.bump_texname),
1234 &(material.bump_texopt), token,
1240 if ((0 == strncmp(token,
"bump", 4)) && IS_SPACE(token[4])) {
1242 ParseTextureNameAndOption(&(material.bump_texname),
1243 &(material.bump_texopt), token,
1249 if ((0 == strncmp(token,
"map_d", 5)) && IS_SPACE(token[5])) {
1251 material.alpha_texname = token;
1252 ParseTextureNameAndOption(&(material.alpha_texname),
1253 &(material.alpha_texopt), token,
1259 if ((0 == strncmp(token,
"disp", 4)) && IS_SPACE(token[4])) {
1261 ParseTextureNameAndOption(&(material.displacement_texname),
1262 &(material.displacement_texopt), token,
1268 if ((0 == strncmp(token,
"map_Pr", 6)) && IS_SPACE(token[6])) {
1270 ParseTextureNameAndOption(&(material.roughness_texname),
1271 &(material.roughness_texopt), token,
1277 if ((0 == strncmp(token,
"map_Pm", 6)) && IS_SPACE(token[6])) {
1279 ParseTextureNameAndOption(&(material.metallic_texname),
1280 &(material.metallic_texopt), token,
1286 if ((0 == strncmp(token,
"map_Ps", 6)) && IS_SPACE(token[6])) {
1288 ParseTextureNameAndOption(&(material.sheen_texname),
1289 &(material.sheen_texopt), token,
1295 if ((0 == strncmp(token,
"map_Ke", 6)) && IS_SPACE(token[6])) {
1297 ParseTextureNameAndOption(&(material.emissive_texname),
1298 &(material.emissive_texopt), token,
1304 if ((0 == strncmp(token,
"norm", 4)) && IS_SPACE(token[4])) {
1306 ParseTextureNameAndOption(
1307 &(material.normal_texname), &(material.normal_texopt), token,
1313 const char *_space = strchr(token,
' ');
1315 _space = strchr(token,
'\t');
1318 std::ptrdiff_t len = _space - token;
1319 std::string
key(token,
static_cast<size_t>(len));
1320 std::string value = _space + 1;
1321 material.unknown_parameter.insert(
1322 std::pair<std::string, std::string>(key, value));
1326 material_map->insert(std::pair<std::string, int>(
1327 material.name,
static_cast<int>(materials->size())));
1328 materials->push_back(material);
1331 (*warning) = ss.str();
1335bool MaterialFileReader::operator()(
const std::string &matId,
1336 std::vector<material_t> *materials,
1337 std::map<std::string, int> *matMap,
1339 std::string filepath;
1341 if (!m_mtlBaseDir.empty()) {
1342 filepath = std::string(m_mtlBaseDir) + matId;
1347 std::ifstream matIStream(filepath.c_str());
1349 std::stringstream ss;
1350 ss <<
"WARN: Material file [ " << filepath <<
" ] not found." << std::endl;
1357 std::string warning;
1358 LoadMtl(matMap, materials, &matIStream, &warning);
1360 if (!warning.empty()) {
1369bool MaterialStreamReader::operator()(
const std::string &matId,
1370 std::vector<material_t> *materials,
1371 std::map<std::string, int> *matMap,
1375 std::stringstream ss;
1376 ss <<
"WARN: Material stream in error state. " << std::endl;
1383 std::string warning;
1384 LoadMtl(matMap, materials, &m_inStream, &warning);
1386 if (!warning.empty()) {
1395bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
1396 std::vector<material_t> *materials, std::string *err,
1397 const char *filename,
const char *mtl_basedir,
bool trianglulate) {
1398 attrib->vertices.clear();
1399 attrib->normals.clear();
1400 attrib->texcoords.clear();
1403 std::stringstream errss;
1405 std::ifstream ifs(filename);
1407 errss <<
"Cannot open file [" << filename <<
"]" << std::endl;
1409 (*err) = errss.str();
1414 std::string baseDir;
1416 baseDir = mtl_basedir;
1418 MaterialFileReader matFileReader(baseDir);
1420 return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader,
1424bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
1425 std::vector<material_t> *materials, std::string *err,
1426 std::istream *inStream, MaterialReader *readMatFn ,
1428 std::stringstream errss;
1430 std::vector<real_t> v;
1431 std::vector<real_t> vn;
1432 std::vector<real_t> vt;
1433 std::vector<tag_t> tags;
1434 std::vector<std::vector<vertex_index> > faceGroup;
1438 std::map<std::string, int> material_map;
1443 std::string linebuf;
1444 while (inStream->peek() != -1) {
1445 safeGetline(*inStream, linebuf);
1448 if (linebuf.size() > 0) {
1449 if (linebuf[linebuf.size() - 1] ==
'\n')
1450 linebuf.erase(linebuf.size() - 1);
1452 if (linebuf.size() > 0) {
1453 if (linebuf[linebuf.size() - 1] ==
'\r')
1454 linebuf.erase(linebuf.size() - 1);
1458 if (linebuf.empty()) {
1463 const char *token = linebuf.c_str();
1464 token += strspn(token,
" \t");
1467 if (token[0] ==
'\0')
continue;
1469 if (token[0] ==
'#')
continue;
1472 if (token[0] ==
'v' && IS_SPACE((token[1]))) {
1475 parseReal3(&x, &y, &z, &token);
1483 if (token[0] ==
'v' && token[1] ==
'n' && IS_SPACE((token[2]))) {
1486 parseReal3(&x, &y, &z, &token);
1494 if (token[0] ==
'v' && token[1] ==
't' && IS_SPACE((token[2]))) {
1497 parseReal2(&x, &y, &token);
1504 if (token[0] ==
'f' && IS_SPACE((token[1]))) {
1506 token += strspn(token,
" \t");
1508 std::vector<vertex_index> face;
1511 while (!IS_NEW_LINE(token[0])) {
1512 vertex_index vi = parseTriple(&token,
static_cast<int>(v.size() / 3),
1513 static_cast<int>(vn.size() / 3),
1514 static_cast<int>(vt.size() / 2));
1516 size_t n = strspn(token,
" \t\r");
1521 faceGroup.push_back(std::vector<vertex_index>());
1522 faceGroup[faceGroup.size() - 1].swap(face);
1528 if ((0 == strncmp(token,
"usemtl", 6)) && IS_SPACE((token[6]))) {
1529 char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1532 sscanf_s(token,
"%s", namebuf, (
unsigned)_countof(namebuf));
1534 std::sscanf(token,
"%s", namebuf);
1537 int newMaterialId = -1;
1538 if (material_map.find(namebuf) != material_map.end()) {
1539 newMaterialId = material_map[namebuf];
1544 if (newMaterialId != material) {
1548 exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
1551 material = newMaterialId;
1558 if ((0 == strncmp(token,
"mtllib", 6)) && IS_SPACE((token[6]))) {
1562 std::vector<std::string> filenames;
1563 SplitString(std::string(token),
' ', filenames);
1565 if (filenames.empty()) {
1568 "WARN: Looks like empty filename for mtllib. Use default "
1573 for (
size_t s = 0; s < filenames.size(); s++) {
1574 std::string err_mtl;
1575 bool ok = (*readMatFn)(filenames[s].c_str(), materials,
1576 &material_map, &err_mtl);
1577 if (err && (!err_mtl.empty())) {
1590 "WARN: Failed to load material file(s). Use default "
1601 if (token[0] ==
'g' && IS_SPACE((token[1]))) {
1603 bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
1606 shapes->push_back(shape);
1614 std::vector<std::string> names;
1617 while (!IS_NEW_LINE(token[0])) {
1618 std::string str = parseString(&token);
1619 names.push_back(str);
1620 token += strspn(token,
" \t\r");
1623 assert(names.size() > 0);
1626 if (names.size() > 1) {
1636 if (token[0] ==
'o' && IS_SPACE((token[1]))) {
1638 bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
1641 shapes->push_back(shape);
1649 char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1652 sscanf_s(token,
"%s", namebuf, (
unsigned)_countof(namebuf));
1654 std::sscanf(token,
"%s", namebuf);
1656 name = std::string(namebuf);
1661 if (token[0] ==
't' && IS_SPACE(token[1])) {
1667 sscanf_s(token,
"%s", namebuf, (
unsigned)_countof(namebuf));
1669 std::sscanf(token,
"%s", namebuf);
1671 tag.name = std::string(namebuf);
1673 token += tag.name.size() + 1;
1675 tag_sizes ts = parseTagTriple(&token);
1677 tag.intValues.resize(
static_cast<size_t>(ts.num_ints));
1679 for (
size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
1680 tag.intValues[i] = atoi(token);
1681 token += strcspn(token,
"/ \t\r") + 1;
1684 tag.floatValues.resize(
static_cast<size_t>(ts.num_reals));
1685 for (
size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) {
1686 tag.floatValues[i] = parseReal(&token);
1687 token += strcspn(token,
"/ \t\r") + 1;
1690 tag.stringValues.resize(
static_cast<size_t>(ts.num_strings));
1691 for (
size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
1692 char stringValueBuffer[4096];
1695 sscanf_s(token,
"%s", stringValueBuffer,
1696 (
unsigned)_countof(stringValueBuffer));
1698 std::sscanf(token,
"%s", stringValueBuffer);
1700 tag.stringValues[i] = stringValueBuffer;
1701 token += tag.stringValues[i].size() + 1;
1704 tags.push_back(tag);
1710 bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
1716 if (ret || shape.mesh.indices.size()) {
1717 shapes->push_back(shape);
1722 (*err) += errss.str();
1725 attrib->vertices.swap(v);
1726 attrib->normals.swap(vn);
1727 attrib->texcoords.swap(vt);
1732bool LoadObjWithCallback(std::istream &inStream,
const callback_t &callback,
1734 MaterialReader *readMatFn ,
1735 std::string *err ) {
1736 std::stringstream errss;
1739 std::map<std::string, int> material_map;
1740 int material_id = -1;
1742 std::vector<index_t> indices;
1743 std::vector<material_t> materials;
1744 std::vector<std::string> names;
1747 std::vector<const char *> names_out;
1749 std::string linebuf;
1750 while (inStream.peek() != -1) {
1751 safeGetline(inStream, linebuf);
1754 if (linebuf.size() > 0) {
1755 if (linebuf[linebuf.size() - 1] ==
'\n')
1756 linebuf.erase(linebuf.size() - 1);
1758 if (linebuf.size() > 0) {
1759 if (linebuf[linebuf.size() - 1] ==
'\r')
1760 linebuf.erase(linebuf.size() - 1);
1764 if (linebuf.empty()) {
1769 const char *token = linebuf.c_str();
1770 token += strspn(token,
" \t");
1773 if (token[0] ==
'\0')
continue;
1775 if (token[0] ==
'#')
continue;
1778 if (token[0] ==
'v' && IS_SPACE((token[1]))) {
1781 parseV(&x, &y, &z, &w, &token);
1782 if (callback.vertex_cb) {
1783 callback.vertex_cb(user_data, x, y, z, w);
1789 if (token[0] ==
'v' && token[1] ==
'n' && IS_SPACE((token[2]))) {
1792 parseReal3(&x, &y, &z, &token);
1793 if (callback.normal_cb) {
1794 callback.normal_cb(user_data, x, y, z);
1800 if (token[0] ==
'v' && token[1] ==
't' && IS_SPACE((token[2]))) {
1803 parseReal3(&x, &y, &z, &token);
1804 if (callback.texcoord_cb) {
1805 callback.texcoord_cb(user_data, x, y, z);
1811 if (token[0] ==
'f' && IS_SPACE((token[1]))) {
1813 token += strspn(token,
" \t");
1816 while (!IS_NEW_LINE(token[0])) {
1817 vertex_index vi = parseRawTriple(&token);
1820 idx.vertex_index = vi.v_idx;
1821 idx.normal_index = vi.vn_idx;
1822 idx.texcoord_index = vi.vt_idx;
1824 indices.push_back(idx);
1825 size_t n = strspn(token,
" \t\r");
1829 if (callback.index_cb && indices.size() > 0) {
1830 callback.index_cb(user_data, &indices.at(0),
1831 static_cast<int>(indices.size()));
1838 if ((0 == strncmp(token,
"usemtl", 6)) && IS_SPACE((token[6]))) {
1839 char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1842 sscanf_s(token,
"%s", namebuf,
1843 static_cast<unsigned int>(_countof(namebuf)));
1845 std::sscanf(token,
"%s", namebuf);
1848 int newMaterialId = -1;
1849 if (material_map.find(namebuf) != material_map.end()) {
1850 newMaterialId = material_map[namebuf];
1855 if (newMaterialId != material_id) {
1856 material_id = newMaterialId;
1859 if (callback.usemtl_cb) {
1860 callback.usemtl_cb(user_data, namebuf, material_id);
1867 if ((0 == strncmp(token,
"mtllib", 6)) && IS_SPACE((token[6]))) {
1871 std::vector<std::string> filenames;
1872 SplitString(std::string(token),
' ', filenames);
1874 if (filenames.empty()) {
1877 "WARN: Looks like empty filename for mtllib. Use default "
1882 for (
size_t s = 0; s < filenames.size(); s++) {
1883 std::string err_mtl;
1884 bool ok = (*readMatFn)(filenames[s].c_str(), &materials,
1885 &material_map, &err_mtl);
1886 if (err && (!err_mtl.empty())) {
1899 "WARN: Failed to load material file(s). Use default "
1903 if (callback.mtllib_cb) {
1904 callback.mtllib_cb(user_data, &materials.at(0),
1905 static_cast<int>(materials.size()));
1915 if (token[0] ==
'g' && IS_SPACE((token[1]))) {
1918 while (!IS_NEW_LINE(token[0])) {
1919 std::string str = parseString(&token);
1920 names.push_back(str);
1921 token += strspn(token,
" \t\r");
1924 assert(names.size() > 0);
1927 if (names.size() > 1) {
1933 if (callback.group_cb) {
1934 if (names.size() > 1) {
1936 names_out.resize(names.size() - 1);
1937 for (
size_t j = 0; j < names_out.size(); j++) {
1938 names_out[j] = names[j + 1].c_str();
1940 callback.group_cb(user_data, &names_out.at(0),
1941 static_cast<int>(names_out.size()));
1944 callback.group_cb(user_data, NULL, 0);
1952 if (token[0] ==
'o' && IS_SPACE((token[1]))) {
1954 char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1957 sscanf_s(token,
"%s", namebuf, (
unsigned)_countof(namebuf));
1959 std::sscanf(token,
"%s", namebuf);
1961 std::string object_name = std::string(namebuf);
1963 if (callback.object_cb) {
1964 callback.object_cb(user_data, object_name.c_str());
1971 if (token[0] ==
't' && IS_SPACE(token[1])) {
1977 sscanf_s(token,
"%s", namebuf, (
unsigned)_countof(namebuf));
1979 std::sscanf(token,
"%s", namebuf);
1981 tag.name = std::string(namebuf);
1983 token += tag.name.size() + 1;
1985 tag_sizes ts = parseTagTriple(&token);
1987 tag.intValues.resize(
static_cast<size_t>(ts.num_ints));
1989 for (
size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
1990 tag.intValues[i] = atoi(token);
1991 token += strcspn(token,
"/ \t\r") + 1;
1994 tag.floatValues.resize(
static_cast<size_t>(ts.num_reals));
1995 for (
size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) {
1996 tag.floatValues[i] = parseReal(&token);
1997 token += strcspn(token,
"/ \t\r") + 1;
2000 tag.stringValues.resize(
static_cast<size_t>(ts.num_strings));
2001 for (
size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
2002 char stringValueBuffer[4096];
2005 sscanf_s(token,
"%s", stringValueBuffer,
2006 (
unsigned)_countof(stringValueBuffer));
2008 std::sscanf(token,
"%s", stringValueBuffer);
2010 tag.stringValues[i] = stringValueBuffer;
2011 token += tag.stringValues[i].size() + 1;
2014 tags.push_back(tag);
2022 (*err) += errss.str();
Definition tiny_obj_loader.h:278
Definition tiny_obj_loader.h:267
Definition tiny_obj_loader.h:291
GLM_FUNC_DECL vec< L, T, Q > sign(vec< L, T, Q > const &x)
Definition func_common.inl:295
GLM_FUNC_DECL GLM_CONSTEXPR genType e()
Definition constants.inl:102
@ key
the parser read a key of a value in an object
Definition tiny_obj_loader.h:226
Definition tiny_obj_loader.h:232
Definition tiny_obj_loader.h:205
Definition tiny_obj_loader.h:135
Definition tiny_obj_loader.h:211
Definition tiny_obj_loader.h:220
Definition tiny_obj_loader.h:195
Definition tiny_obj_loader.h:119