diff --git a/xs/src/tiny_obj_loader.h b/xs/src/tiny_obj_loader.h index 7f704d89d..9e43a7ddf 100644 --- a/xs/src/tiny_obj_loader.h +++ b/xs/src/tiny_obj_loader.h @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2012-2016 Syoyo Fujita and many contributors. +Copyright (c) 2012-2018 Syoyo Fujita and many contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,12 @@ THE SOFTWARE. */ // +// version 1.2.0 : Hardened implementation(#175) +// version 1.1.1 : Support smoothing groups(#162) +// version 1.1.0 : Support parsing vertex color(#144) +// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) +// version 1.0.7 : Support multiple tex options(#126) +// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) // version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) // version 1.0.4 : Support multiple filenames for 'mtllib'(#112) // version 1.0.3 : Support parsing texture options(#85) @@ -47,13 +53,23 @@ THE SOFTWARE. namespace tinyobj { +#ifdef __clang__ +#pragma clang diagnostic push +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + +#pragma clang diagnostic ignored "-Wpadded" + +#endif + // https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... // // -blendu on | off # set horizontal texture blending // (default on) // -blendv on | off # set vertical texture blending // (default on) -// -boost float_value # boost mip-map sharpness +// -boost real_value # boost mip-map sharpness // -mm base_value gain_value # modify texture map values (default // 0 1) // # base_value = brightness, @@ -96,6 +112,14 @@ namespace tinyobj { // separately // cube_left | cube_right +#ifdef TINYOBJLOADER_USE_DOUBLE +//#pragma message "using double" +typedef double real_t; +#else +//#pragma message "using float" +typedef float real_t; +#endif + typedef enum { TEXTURE_TYPE_NONE, // default TEXTURE_TYPE_SPHERE, @@ -108,32 +132,32 @@ typedef enum { } texture_type_t; typedef struct { - texture_type_t type; // -type (default TEXTURE_TYPE_NONE) - float sharpness; // -boost (default 1.0?) - float brightness; // base_value in -mm option (default 0) - float contrast; // gain_value in -mm option (default 1) - float origin_offset[3]; // -o u [v [w]] (default 0 0 0) - float scale[3]; // -s u [v [w]] (default 1 1 1) - float turbulence[3]; // -t u [v [w]] (default 0 0 0) + texture_type_t type; // -type (default TEXTURE_TYPE_NONE) + real_t sharpness; // -boost (default 1.0?) + real_t brightness; // base_value in -mm option (default 0) + real_t contrast; // gain_value in -mm option (default 1) + real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) + real_t scale[3]; // -s u [v [w]] (default 1 1 1) + real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) // int texture_resolution; // -texres resolution (default = ?) TODO bool clamp; // -clamp (default false) char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') bool blendu; // -blendu (default on) bool blendv; // -blendv (default on) - float bump_multiplier; // -bm (for bump maps only, default 1.0) + real_t bump_multiplier; // -bm (for bump maps only, default 1.0) } texture_option_t; typedef struct { std::string name; - float ambient[3]; - float diffuse[3]; - float specular[3]; - float transmittance[3]; - float emission[3]; - float shininess; - float ior; // index of refraction - float dissolve; // 1 == opaque; 0 == fully transparent + real_t ambient[3]; + real_t diffuse[3]; + real_t specular[3]; + real_t transmittance[3]; + real_t emission[3]; + real_t shininess; + real_t ior; // index of refraction + real_t dissolve; // 1 == opaque; 0 == fully transparent // illumination model (see http://www.fileformat.info/format/material/) int illum; @@ -143,9 +167,10 @@ typedef struct { std::string diffuse_texname; // map_Kd std::string specular_texname; // map_Ks std::string specular_highlight_texname; // map_Ns - std::string bump_texname; // map_bump, bump + std::string bump_texname; // map_bump, map_Bump, bump std::string displacement_texname; // disp std::string alpha_texname; // map_d + std::string reflection_texname; // refl texture_option_t ambient_texopt; texture_option_t diffuse_texopt; @@ -154,18 +179,18 @@ typedef struct { texture_option_t bump_texopt; texture_option_t displacement_texopt; texture_option_t alpha_texopt; + texture_option_t reflection_texopt; // PBR extension // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr - float roughness; // [0, 1] default 0 - float metallic; // [0, 1] default 0 - float sheen; // [0, 1] default 0 - float clearcoat_thickness; // [0, 1] default 0 - float clearcoat_roughness; // [0, 1] default 0 - float anisotropy; // aniso. [0, 1] default 0 - float anisotropy_rotation; // anisor. [0, 1] default 0 - float pad0; - float pad1; + real_t roughness; // [0, 1] default 0 + real_t metallic; // [0, 1] default 0 + real_t sheen; // [0, 1] default 0 + real_t clearcoat_thickness; // [0, 1] default 0 + real_t clearcoat_roughness; // [0, 1] default 0 + real_t anisotropy; // aniso. [0, 1] default 0 + real_t anisotropy_rotation; // anisor. [0, 1] default 0 + real_t pad0; std::string roughness_texname; // map_Pr std::string metallic_texname; // map_Pm std::string sheen_texname; // map_Ps @@ -187,7 +212,7 @@ typedef struct { std::string name; std::vector intValues; - std::vector floatValues; + std::vector floatValues; std::vector stringValues; } tag_t; @@ -205,7 +230,10 @@ typedef struct { // face. 3 = polygon, 4 = quad, // ... Up to 255. std::vector material_ids; // per-face material ID - std::vector tags; // SubD tag + std::vector smoothing_group_ids; // per-face smoothing group + // ID(0 = off. positive value + // = group id) + std::vector tags; // SubD tag } mesh_t; typedef struct { @@ -215,19 +243,20 @@ typedef struct { // Vertex attributes typedef struct { - std::vector vertices; // 'v' - std::vector normals; // 'vn' - std::vector texcoords; // 'vt' + std::vector vertices; // 'v' + std::vector normals; // 'vn' + std::vector texcoords; // 'vt' + std::vector colors; // extension: vertex colors } attrib_t; typedef struct callback_t_ { // W is optional and set to 1 if there is no `w` item in `v` line - void (*vertex_cb)(void *user_data, float x, float y, float z, float w); - void (*normal_cb)(void *user_data, float x, float y, float z); + void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w); + void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z); // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in // `vt` line. - void (*texcoord_cb)(void *user_data, float x, float y, float z); + void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z); // called per 'f' line. num_indices is the number of face indices(e.g. 3 for // triangle, 4 for quad) @@ -344,6 +373,7 @@ void LoadMtl(std::map *material_map, #include #include #include +#include #include #include @@ -352,27 +382,36 @@ namespace tinyobj { MaterialReader::~MaterialReader() {} -#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) - -struct vertex_index { +struct vertex_index_t { int v_idx, vt_idx, vn_idx; - vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} - explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} - vertex_index(int vidx, int vtidx, int vnidx) + vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} + explicit vertex_index_t(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} + vertex_index_t(int vidx, int vtidx, int vnidx) : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} }; +// Internal data structure for face representation +// index + smoothing group. +struct face_t { + unsigned int + smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off. + int pad_; + std::vector vertex_indices; // face vertex indices. + + face_t() : smoothing_group_id(0) {} +}; + struct tag_sizes { - tag_sizes() : num_ints(0), num_floats(0), num_strings(0) {} + tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} int num_ints; - int num_floats; + int num_reals; int num_strings; }; struct obj_shape { - std::vector v; - std::vector vn; - std::vector vt; + std::vector v; + std::vector vn; + std::vector vt; }; // See @@ -389,22 +428,26 @@ static std::istream &safeGetline(std::istream &is, std::string &t) { std::istream::sentry se(is, true); std::streambuf *sb = is.rdbuf(); - for (;;) { - int c = sb->sbumpc(); - switch (c) { - case '\n': - return is; - case '\r': - if (sb->sgetc() == '\n') sb->sbumpc(); - return is; - case EOF: - // Also handle the case when the last line has no line ending - if (t.empty()) is.setstate(std::ios::eofbit); - return is; - default: - t += static_cast(c); + if (se) { + for (;;) { + int c = sb->sbumpc(); + switch (c) { + case '\n': + return is; + case '\r': + if (sb->sgetc() == '\n') sb->sbumpc(); + return is; + case EOF: + // Also handle the case when the last line has no line ending + if (t.empty()) is.setstate(std::ios::eofbit); + return is; + default: + t += static_cast(c); + } } } + + return is; } #define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) @@ -413,10 +456,27 @@ static std::istream &safeGetline(std::istream &is, std::string &t) { #define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) // Make index zero-base, and also support relative index. -static inline int fixIndex(int idx, int n) { - if (idx > 0) return idx - 1; - if (idx == 0) return 0; - return n + idx; // negative value = relative +static inline bool fixIndex(int idx, int n, int *ret) { + if (!ret) { + return false; + } + + if (idx > 0) { + (*ret) = idx - 1; + return true; + } + + if (idx == 0) { + // zero is not allowed according to the spec. + return false; + } + + if (idx < 0) { + (*ret) = n + idx; // negative value = relative + return true; + } + + return false; // never reach here. } static inline std::string parseString(const char **token) { @@ -569,49 +629,80 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { } assemble: - *result = - (sign == '+' ? 1 : -1) * - (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) : mantissa); + *result = (sign == '+' ? 1 : -1) * + (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) + : mantissa); return true; fail: return false; } -static inline float parseFloat(const char **token, double default_value = 0.0) { +static inline real_t parseReal(const char **token, double default_value = 0.0) { (*token) += strspn((*token), " \t"); const char *end = (*token) + strcspn((*token), " \t\r"); double val = default_value; tryParseDouble((*token), end, &val); - float f = static_cast(val); + real_t f = static_cast(val); (*token) = end; return f; } -static inline void parseFloat2(float *x, float *y, const char **token, - const double default_x = 0.0, - const double default_y = 0.0) { - (*x) = parseFloat(token, default_x); - (*y) = parseFloat(token, default_y); +static inline bool parseReal(const char **token, real_t *out) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val; + bool ret = tryParseDouble((*token), end, &val); + if (ret) { + real_t f = static_cast(val); + (*out) = f; + } + (*token) = end; + return ret; } -static inline void parseFloat3(float *x, float *y, float *z, const char **token, - const double default_x = 0.0, - const double default_y = 0.0, - const double default_z = 0.0) { - (*x) = parseFloat(token, default_x); - (*y) = parseFloat(token, default_y); - (*z) = parseFloat(token, default_z); +static inline void parseReal2(real_t *x, real_t *y, const char **token, + const double default_x = 0.0, + const double default_y = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); } -static inline void parseV(float *x, float *y, float *z, float *w, +static inline void parseReal3(real_t *x, real_t *y, real_t *z, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); +} + +static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, const char **token, const double default_x = 0.0, const double default_y = 0.0, const double default_z = 0.0, const double default_w = 1.0) { - (*x) = parseFloat(token, default_x); - (*y) = parseFloat(token, default_y); - (*z) = parseFloat(token, default_z); - (*w) = parseFloat(token, default_w); + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + (*w) = parseReal(token, default_w); +} + +// Extension: parse vertex with colors(6 items) +static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, + real_t *r, real_t *g, real_t *b, + const char **token, + const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + + (*r) = parseReal(token, 1.0); + (*g) = parseReal(token, 1.0); + (*b) = parseReal(token, 1.0); + + return true; } static inline bool parseOnOff(const char **token, bool default_value = true) { @@ -658,63 +749,85 @@ static inline texture_type_t parseTextureType( static tag_sizes parseTagTriple(const char **token) { tag_sizes ts; + (*token) += strspn((*token), " \t"); ts.num_ints = atoi((*token)); (*token) += strcspn((*token), "/ \t\r"); if ((*token)[0] != '/') { return ts; } - (*token)++; - ts.num_floats = atoi((*token)); + (*token)++; // Skip '/' + + (*token) += strspn((*token), " \t"); + ts.num_reals = atoi((*token)); (*token) += strcspn((*token), "/ \t\r"); if ((*token)[0] != '/') { return ts; } - (*token)++; + (*token)++; // Skip '/' - ts.num_strings = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r") + 1; + ts.num_strings = parseInt(token); return ts; } // Parse triples with index offsets: i, i/j/k, i//k, i/j -static vertex_index parseTriple(const char **token, int vsize, int vnsize, - int vtsize) { - vertex_index vi(-1); +static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize, + vertex_index_t *ret) { + if (!ret) { + return false; + } + + vertex_index_t vi(-1); + + if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) { + return false; + } - vi.v_idx = fixIndex(atoi((*token)), vsize); (*token) += strcspn((*token), "/ \t\r"); if ((*token)[0] != '/') { - return vi; + (*ret) = vi; + return true; } (*token)++; // i//k if ((*token)[0] == '/') { (*token)++; - vi.vn_idx = fixIndex(atoi((*token)), vnsize); + if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { + return false; + } (*token) += strcspn((*token), "/ \t\r"); - return vi; + (*ret) = vi; + return true; } // i/j/k or i/j - vi.vt_idx = fixIndex(atoi((*token)), vtsize); + if (!fixIndex(atoi((*token)), vtsize, &(vi.vt_idx))) { + return false; + } + (*token) += strcspn((*token), "/ \t\r"); if ((*token)[0] != '/') { - return vi; + (*ret) = vi; + return true; } // i/j/k (*token)++; // skip '/' - vi.vn_idx = fixIndex(atoi((*token)), vnsize); + if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { + return false; + } (*token) += strcspn((*token), "/ \t\r"); - return vi; + + (*ret) = vi; + + return true; } // Parse raw triples: i, i/j/k, i//k, i/j -static vertex_index parseRawTriple(const char **token) { - vertex_index vi(static_cast(0)); // 0 is an invalid index in OBJ +static vertex_index_t parseRawTriple(const char **token) { + vertex_index_t vi(static_cast(0)); // 0 is an invalid index in OBJ vi.v_idx = atoi((*token)); (*token) += strcspn((*token), "/ \t\r"); @@ -758,27 +871,28 @@ static bool ParseTextureNameAndOption(std::string *texname, } else { texopt->imfchan = 'm'; } - texopt->bump_multiplier = 1.0f; + texopt->bump_multiplier = static_cast(1.0); texopt->clamp = false; texopt->blendu = true; texopt->blendv = true; - texopt->sharpness = 1.0f; - texopt->brightness = 0.0f; - texopt->contrast = 1.0f; - texopt->origin_offset[0] = 0.0f; - texopt->origin_offset[1] = 0.0f; - texopt->origin_offset[2] = 0.0f; - texopt->scale[0] = 1.0f; - texopt->scale[1] = 1.0f; - texopt->scale[2] = 1.0f; - texopt->turbulence[0] = 0.0f; - texopt->turbulence[1] = 0.0f; - texopt->turbulence[2] = 0.0f; + texopt->sharpness = static_cast(1.0); + texopt->brightness = static_cast(0.0); + texopt->contrast = static_cast(1.0); + texopt->origin_offset[0] = static_cast(0.0); + texopt->origin_offset[1] = static_cast(0.0); + texopt->origin_offset[2] = static_cast(0.0); + texopt->scale[0] = static_cast(1.0); + texopt->scale[1] = static_cast(1.0); + texopt->scale[2] = static_cast(1.0); + texopt->turbulence[0] = static_cast(0.0); + texopt->turbulence[1] = static_cast(0.0); + texopt->turbulence[2] = static_cast(0.0); texopt->type = TEXTURE_TYPE_NONE; const char *token = linebuf; // Assume line ends with NULL while (!IS_NEW_LINE((*token))) { + token += strspn(token, " \t"); // skip space if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { token += 8; texopt->blendu = parseOnOff(&token, /* default */ true); @@ -790,22 +904,22 @@ static bool ParseTextureNameAndOption(std::string *texname, texopt->clamp = parseOnOff(&token, /* default */ true); } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { token += 7; - texopt->sharpness = parseFloat(&token, 1.0); + texopt->sharpness = parseReal(&token, 1.0); } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { token += 4; - texopt->bump_multiplier = parseFloat(&token, 1.0); + texopt->bump_multiplier = parseReal(&token, 1.0); } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { token += 3; - parseFloat3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), - &(texopt->origin_offset[2]), &token); + parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), + &(texopt->origin_offset[2]), &token); } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { token += 3; - parseFloat3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), - &token, 1.0, 1.0, 1.0); + parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), + &token, 1.0, 1.0, 1.0); } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { token += 3; - parseFloat3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), - &(texopt->turbulence[2]), &token); + parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), + &(texopt->turbulence[2]), &token); } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { token += 5; texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); @@ -819,15 +933,21 @@ static bool ParseTextureNameAndOption(std::string *texname, token = end; } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { token += 4; - parseFloat2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); + parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); } else { - // Assume texture filename - token += strspn(token, " \t"); // skip space + // Assume texture filename +#if 0 size_t len = strcspn(token, " \t\r"); // untile next space texture_name = std::string(token, token + len); token += len; token += strspn(token, " \t"); // skip space +#else + // Read filename until line end to parse filename containing whitespace + // TODO(syoyo): Support parsing texture option flag after the filename. + texture_name = std::string(token); + token += texture_name.length(); +#endif found_texname = true; } @@ -849,26 +969,27 @@ static void InitMaterial(material_t *material) { material->specular_highlight_texname = ""; material->bump_texname = ""; material->displacement_texname = ""; + material->reflection_texname = ""; material->alpha_texname = ""; for (int i = 0; i < 3; i++) { - material->ambient[i] = 0.f; - material->diffuse[i] = 0.f; - material->specular[i] = 0.f; - material->transmittance[i] = 0.f; - material->emission[i] = 0.f; + material->ambient[i] = static_cast(0.0); + material->diffuse[i] = static_cast(0.0); + material->specular[i] = static_cast(0.0); + material->transmittance[i] = static_cast(0.0); + material->emission[i] = static_cast(0.0); } material->illum = 0; - material->dissolve = 1.f; - material->shininess = 1.f; - material->ior = 1.f; + material->dissolve = static_cast(1.0); + material->shininess = static_cast(1.0); + material->ior = static_cast(1.0); - material->roughness = 0.f; - material->metallic = 0.f; - material->sheen = 0.f; - material->clearcoat_thickness = 0.f; - material->clearcoat_roughness = 0.f; - material->anisotropy_rotation = 0.f; - material->anisotropy = 0.f; + material->roughness = static_cast(0.0); + material->metallic = static_cast(0.0); + material->sheen = static_cast(0.0); + material->clearcoat_thickness = static_cast(0.0); + material->clearcoat_roughness = static_cast(0.0); + material->anisotropy_rotation = static_cast(0.0); + material->anisotropy = static_cast(0.0); material->roughness_texname = ""; material->metallic_texname = ""; material->sheen_texname = ""; @@ -878,60 +999,256 @@ static void InitMaterial(material_t *material) { material->unknown_parameter.clear(); } -static bool exportFaceGroupToShape( - shape_t *shape, const std::vector > &faceGroup, - const std::vector &tags, const int material_id, - const std::string &name, bool triangulate) { +// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html +template +static int pnpoly(int nvert, T *vertx, T *verty, T testx, + T testy) { + int i, j, c = 0; + for (i = 0, j = nvert - 1; i < nvert; j = i++) { + if (((verty[i] > testy) != (verty[j] > testy)) && + (testx < + (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + + vertx[i])) + c = !c; + } + return c; +} + +// TODO(syoyo): refactor function. +static bool exportFaceGroupToShape(shape_t *shape, + const std::vector &faceGroup, + const std::vector &tags, + const int material_id, + const std::string &name, bool triangulate, + const std::vector &v) { if (faceGroup.empty()) { return false; } // Flatten vertices and indices for (size_t i = 0; i < faceGroup.size(); i++) { - const std::vector &face = faceGroup[i]; + const face_t &face = faceGroup[i]; - vertex_index i0 = face[0]; - vertex_index i1(-1); - vertex_index i2 = face[1]; + size_t npolys = face.vertex_indices.size(); - size_t npolys = face.size(); + if (npolys < 3) { + // Face must have 3+ vertices. + continue; + } + + vertex_index_t i0 = face.vertex_indices[0]; + vertex_index_t i1(-1); + vertex_index_t i2 = face.vertex_indices[1]; if (triangulate) { - // Polygon -> triangle fan conversion - for (size_t k = 2; k < npolys; k++) { - i1 = i2; - i2 = face[k]; + // find the two axes to work in + size_t axes[2] = {1, 2}; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + i2 = face.vertex_indices[(k + 2) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + size_t vi2 = size_t(i2.v_idx); - index_t idx0, idx1, idx2; - idx0.vertex_index = i0.v_idx; - idx0.normal_index = i0.vn_idx; - idx0.texcoord_index = i0.vt_idx; - idx1.vertex_index = i1.v_idx; - idx1.normal_index = i1.vn_idx; - idx1.texcoord_index = i1.vt_idx; - idx2.vertex_index = i2.v_idx; - idx2.normal_index = i2.vn_idx; - idx2.texcoord_index = i2.vt_idx; + if (((3 * vi0 + 2) >= v.size()) || + ((3 * vi1 + 2) >= v.size()) || + ((3 * vi2 + 2) >= v.size())) { + // Invalid triangle. + // FIXME(syoyo): Is it ok to simply skip this invalid triangle? + continue; + } + real_t v0x = v[vi0 * 3 + 0]; + real_t v0y = v[vi0 * 3 + 1]; + real_t v0z = v[vi0 * 3 + 2]; + real_t v1x = v[vi1 * 3 + 0]; + real_t v1y = v[vi1 * 3 + 1]; + real_t v1z = v[vi1 * 3 + 2]; + real_t v2x = v[vi2 * 3 + 0]; + real_t v2y = v[vi2 * 3 + 1]; + real_t v2z = v[vi2 * 3 + 2]; + real_t e0x = v1x - v0x; + real_t e0y = v1y - v0y; + real_t e0z = v1z - v0z; + real_t e1x = v2x - v1x; + real_t e1y = v2y - v1y; + real_t e1z = v2z - v1z; + real_t cx = std::fabs(e0y * e1z - e0z * e1y); + real_t cy = std::fabs(e0z * e1x - e0x * e1z); + real_t cz = std::fabs(e0x * e1y - e0y * e1x); + const real_t epsilon = std::numeric_limits::epsilon(); + if (cx > epsilon || cy > epsilon || cz > epsilon) { + // found a corner + if (cx > cy && cx > cz) { + } else { + axes[0] = 0; + if (cz > cx && cz > cy) axes[1] = 1; + } + break; + } + } - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); + real_t area = 0; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + if (((vi0 * 3 + axes[0]) >= v.size()) || + ((vi0 * 3 + axes[1]) >= v.size()) || + ((vi1 * 3 + axes[0]) >= v.size()) || + ((vi1 * 3 + axes[1]) >= v.size())) { + // Invalid index. + continue; + } + real_t v0x = v[vi0 * 3 + axes[0]]; + real_t v0y = v[vi0 * 3 + axes[1]]; + real_t v1x = v[vi1 * 3 + axes[0]]; + real_t v1y = v[vi1 * 3 + axes[1]]; + area += (v0x * v1y - v0y * v1x) * static_cast(0.5); + } - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); + int maxRounds = + 10; // arbitrary max loop count to protect against unexpected errors + + face_t remainingFace = face; // copy + size_t guess_vert = 0; + vertex_index_t ind[3]; + real_t vx[3]; + real_t vy[3]; + while (remainingFace.vertex_indices.size() > 3 && maxRounds > 0) { + npolys = remainingFace.vertex_indices.size(); + if (guess_vert >= npolys) { + maxRounds -= 1; + guess_vert -= npolys; + } + for (size_t k = 0; k < 3; k++) { + ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; + size_t vi = size_t(ind[k].v_idx); + if (((vi * 3 + axes[0]) >= v.size()) || + ((vi * 3 + axes[1]) >= v.size())) { + // ??? + vx[k] = static_cast(0.0); + vy[k] = static_cast(0.0); + } else { + vx[k] = v[vi * 3 + axes[0]]; + vy[k] = v[vi * 3 + axes[1]]; + } + } + real_t e0x = vx[1] - vx[0]; + real_t e0y = vy[1] - vy[0]; + real_t e1x = vx[2] - vx[1]; + real_t e1y = vy[2] - vy[1]; + real_t cross = e0x * e1y - e0y * e1x; + // if an internal angle + if (cross * area < static_cast(0.0)) { + guess_vert += 1; + continue; + } + + // check all other verts in case they are inside this triangle + bool overlap = false; + for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { + size_t idx = (guess_vert + otherVert) % npolys; + + if (idx >= remainingFace.vertex_indices.size()) { + // ??? + continue; + } + + size_t ovi = size_t( + remainingFace.vertex_indices[idx] + .v_idx); + + if (((ovi * 3 + axes[0]) >= v.size()) || + ((ovi * 3 + axes[1]) >= v.size())) { + // ??? + continue; + } + real_t tx = v[ovi * 3 + axes[0]]; + real_t ty = v[ovi * 3 + axes[1]]; + if (pnpoly(3, vx, vy, tx, ty)) { + overlap = true; + break; + } + } + + if (overlap) { + guess_vert += 1; + continue; + } + + // this triangle is an ear + { + index_t idx0, idx1, idx2; + idx0.vertex_index = ind[0].v_idx; + idx0.normal_index = ind[0].vn_idx; + idx0.texcoord_index = ind[0].vt_idx; + idx1.vertex_index = ind[1].v_idx; + idx1.normal_index = ind[1].vn_idx; + idx1.texcoord_index = ind[1].vt_idx; + idx2.vertex_index = ind[2].v_idx; + idx2.normal_index = ind[2].vn_idx; + idx2.texcoord_index = ind[2].vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } + + // remove v1 from the list + size_t removed_vert_index = (guess_vert + 1) % npolys; + while (removed_vert_index + 1 < npolys) { + remainingFace.vertex_indices[removed_vert_index] = + remainingFace.vertex_indices[removed_vert_index + 1]; + removed_vert_index += 1; + } + remainingFace.vertex_indices.pop_back(); + } + + if (remainingFace.vertex_indices.size() == 3) { + i0 = remainingFace.vertex_indices[0]; + i1 = remainingFace.vertex_indices[1]; + i2 = remainingFace.vertex_indices[2]; + { + index_t idx0, idx1, idx2; + idx0.vertex_index = i0.v_idx; + idx0.normal_index = i0.vn_idx; + idx0.texcoord_index = i0.vt_idx; + idx1.vertex_index = i1.v_idx; + idx1.normal_index = i1.vn_idx; + idx1.texcoord_index = i1.vt_idx; + idx2.vertex_index = i2.v_idx; + idx2.normal_index = i2.vn_idx; + idx2.texcoord_index = i2.vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } } } else { for (size_t k = 0; k < npolys; k++) { index_t idx; - idx.vertex_index = face[k].v_idx; - idx.normal_index = face[k].vn_idx; - idx.texcoord_index = face[k].vt_idx; + idx.vertex_index = face.vertex_indices[k].v_idx; + idx.normal_index = face.vertex_indices[k].vn_idx; + idx.texcoord_index = face.vertex_indices[k].vt_idx; shape->mesh.indices.push_back(idx); } shape->mesh.num_face_vertices.push_back( static_cast(npolys)); shape->mesh.material_ids.push_back(material_id); // per face + shape->mesh.smoothing_group_ids.push_back( + face.smoothing_group_id); // per face } } @@ -1015,22 +1332,20 @@ void LoadMtl(std::map *material_map, has_tr = false; // set new mtl name - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - material.name = namebuf; + { + std::stringstream sstr; + sstr << token; + material.name = sstr.str(); + } continue; } // ambient if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); + real_t r, g, b; + parseReal3(&r, &g, &b, &token); material.ambient[0] = r; material.ambient[1] = g; material.ambient[2] = b; @@ -1040,8 +1355,8 @@ void LoadMtl(std::map *material_map, // diffuse if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); + real_t r, g, b; + parseReal3(&r, &g, &b, &token); material.diffuse[0] = r; material.diffuse[1] = g; material.diffuse[2] = b; @@ -1051,8 +1366,8 @@ void LoadMtl(std::map *material_map, // specular if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); + real_t r, g, b; + parseReal3(&r, &g, &b, &token); material.specular[0] = r; material.specular[1] = g; material.specular[2] = b; @@ -1063,8 +1378,8 @@ void LoadMtl(std::map *material_map, if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); + real_t r, g, b; + parseReal3(&r, &g, &b, &token); material.transmittance[0] = r; material.transmittance[1] = g; material.transmittance[2] = b; @@ -1074,15 +1389,15 @@ void LoadMtl(std::map *material_map, // ior(index of refraction) if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { token += 2; - material.ior = parseFloat(&token); + material.ior = parseReal(&token); continue; } // emission if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { token += 2; - float r, g, b; - parseFloat3(&r, &g, &b, &token); + real_t r, g, b; + parseReal3(&r, &g, &b, &token); material.emission[0] = r; material.emission[1] = g; material.emission[2] = b; @@ -1092,7 +1407,7 @@ void LoadMtl(std::map *material_map, // shininess if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { token += 2; - material.shininess = parseFloat(&token); + material.shininess = parseReal(&token); continue; } @@ -1106,7 +1421,7 @@ void LoadMtl(std::map *material_map, // dissolve if ((token[0] == 'd' && IS_SPACE(token[1]))) { token += 1; - material.dissolve = parseFloat(&token); + material.dissolve = parseReal(&token); if (has_tr) { ss << "WARN: Both `d` and `Tr` parameters defined for \"" @@ -1127,7 +1442,7 @@ void LoadMtl(std::map *material_map, // We invert value of Tr(assume Tr is in range [0, 1]) // NOTE: Interpretation of Tr is application(exporter) dependent. For // some application(e.g. 3ds max obj exporter), Tr = d(Issue 43) - material.dissolve = 1.0f - parseFloat(&token); + material.dissolve = static_cast(1.0) - parseReal(&token); } has_tr = true; continue; @@ -1136,49 +1451,49 @@ void LoadMtl(std::map *material_map, // PBR: roughness if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { token += 2; - material.roughness = parseFloat(&token); + material.roughness = parseReal(&token); continue; } // PBR: metallic if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { token += 2; - material.metallic = parseFloat(&token); + material.metallic = parseReal(&token); continue; } // PBR: sheen if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { token += 2; - material.sheen = parseFloat(&token); + material.sheen = parseReal(&token); continue; } // PBR: clearcoat thickness if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { token += 2; - material.clearcoat_thickness = parseFloat(&token); + material.clearcoat_thickness = parseReal(&token); continue; } // PBR: clearcoat roughness if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { token += 4; - material.clearcoat_roughness = parseFloat(&token); + material.clearcoat_roughness = parseReal(&token); continue; } // PBR: anisotropy if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { token += 6; - material.anisotropy = parseFloat(&token); + material.anisotropy = parseReal(&token); continue; } // PBR: anisotropy rotation if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { token += 7; - material.anisotropy_rotation = parseFloat(&token); + material.anisotropy_rotation = parseReal(&token); continue; } @@ -1227,6 +1542,15 @@ void LoadMtl(std::map *material_map, continue; } + // bump texture + if ((0 == strncmp(token, "map_Bump", 8)) && IS_SPACE(token[8])) { + token += 9; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token, + /* is_bump */ true); + continue; + } + // bump texture if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { token += 5; @@ -1255,6 +1579,15 @@ void LoadMtl(std::map *material_map, continue; } + // reflection map + if ((0 == strncmp(token, "refl", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.reflection_texname), + &(material.reflection_texopt), token, + /* is_bump */ false); + continue; + } + // PBR: roughness texture if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { token += 7; @@ -1389,6 +1722,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, attrib->vertices.clear(); attrib->normals.clear(); attrib->texcoords.clear(); + attrib->colors.clear(); shapes->clear(); std::stringstream errss; @@ -1405,6 +1739,13 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, std::string baseDir; if (mtl_basedir) { baseDir = mtl_basedir; +#ifndef _WIN32 + const char dirsep = '/'; +#else + const char dirsep = '\\'; +#endif + if (baseDir[baseDir.length() - 1] != dirsep) + baseDir += dirsep; } MaterialFileReader matFileReader(baseDir); @@ -1418,17 +1759,22 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, bool triangulate) { std::stringstream errss; - std::vector v; - std::vector vn; - std::vector vt; + std::vector v; + std::vector vn; + std::vector vt; + std::vector vc; std::vector tags; - std::vector > faceGroup; + std::vector faceGroup; std::string name; // material std::map material_map; int material = -1; + // smoothing group id + unsigned int current_smoothing_id = + 0; // Initial value. 0 means no smoothing. + shape_t shape; std::string linebuf; @@ -1462,19 +1808,24 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // vertex if (token[0] == 'v' && IS_SPACE((token[1]))) { token += 2; - float x, y, z; - parseFloat3(&x, &y, &z, &token); + real_t x, y, z; + real_t r, g, b; + parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); v.push_back(x); v.push_back(y); v.push_back(z); + + vc.push_back(r); + vc.push_back(g); + vc.push_back(b); continue; } // normal if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { token += 3; - float x, y, z; - parseFloat3(&x, &y, &z, &token); + real_t x, y, z; + parseReal3(&x, &y, &z, &token); vn.push_back(x); vn.push_back(y); vn.push_back(z); @@ -1484,8 +1835,8 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // texcoord if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { token += 3; - float x, y; - parseFloat2(&x, &y, &token); + real_t x, y; + parseReal2(&x, &y, &token); vt.push_back(x); vt.push_back(y); continue; @@ -1496,34 +1847,39 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, token += 2; token += strspn(token, " \t"); - std::vector face; - face.reserve(3); + face_t face; + + face.smoothing_group_id = current_smoothing_id; + face.vertex_indices.reserve(3); while (!IS_NEW_LINE(token[0])) { - vertex_index vi = parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2)); - face.push_back(vi); + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + (*err) = "Failed parse `f' line(e.g. zero value for face index).\n"; + } + return false; + } + + face.vertex_indices.push_back(vi); size_t n = strspn(token, " \t\r"); token += n; } // replace with emplace_back + std::move on C++11 - faceGroup.push_back(std::vector()); - faceGroup[faceGroup.size() - 1].swap(face); + faceGroup.push_back(face); continue; } // use mtl if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif + std::stringstream ss; + ss << token; + std::string namebuf = ss.str(); int newMaterialId = -1; if (material_map.find(namebuf) != material_map.end()) { @@ -1537,7 +1893,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // this time. // just clear `faceGroup` after `exportFaceGroupToShape()` call. exportFaceGroupToShape(&shape, faceGroup, tags, material, name, - triangulate); + triangulate, v); faceGroup.clear(); material = newMaterialId; } @@ -1592,8 +1948,10 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, if (token[0] == 'g' && IS_SPACE((token[1]))) { // flush previous face group. bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, - triangulate); - if (ret) { + triangulate, v); + (void)ret; // return value not used. + + if (shape.mesh.indices.size() > 0) { shapes->push_back(shape); } @@ -1627,7 +1985,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, if (token[0] == 'o' && IS_SPACE((token[1]))) { // flush previous face group. bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, - triangulate); + triangulate, v); if (ret) { shapes->push_back(shape); } @@ -1637,69 +1995,105 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, shape = shape_t(); // @todo { multiple object name? } - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - name = std::string(namebuf); + std::stringstream ss; + ss << token; + name = ss.str(); continue; } if (token[0] == 't' && IS_SPACE(token[1])) { + const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize. tag_t tag; - char namebuf[4096]; token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - tag.name = std::string(namebuf); - token += tag.name.size() + 1; + tag.name = parseString(&token); tag_sizes ts = parseTagTriple(&token); + if (ts.num_ints < 0) { + ts.num_ints = 0; + } + if (ts.num_ints > max_tag_nums) { + ts.num_ints = max_tag_nums; + } + + if (ts.num_reals < 0) { + ts.num_reals = 0; + } + if (ts.num_reals > max_tag_nums) { + ts.num_reals = max_tag_nums; + } + + if (ts.num_strings < 0) { + ts.num_strings = 0; + } + if (ts.num_strings > max_tag_nums) { + ts.num_strings = max_tag_nums; + } + tag.intValues.resize(static_cast(ts.num_ints)); for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { - tag.intValues[i] = atoi(token); - token += strcspn(token, "/ \t\r") + 1; + tag.intValues[i] = parseInt(&token); } - tag.floatValues.resize(static_cast(ts.num_floats)); - for (size_t i = 0; i < static_cast(ts.num_floats); ++i) { - tag.floatValues[i] = parseFloat(&token); - token += strcspn(token, "/ \t\r") + 1; + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); } tag.stringValues.resize(static_cast(ts.num_strings)); for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { - char stringValueBuffer[4096]; - -#ifdef _MSC_VER - sscanf_s(token, "%s", stringValueBuffer, - (unsigned)_countof(stringValueBuffer)); -#else - std::sscanf(token, "%s", stringValueBuffer); -#endif - tag.stringValues[i] = stringValueBuffer; - token += tag.stringValues[i].size() + 1; + tag.stringValues[i] = parseString(&token); } tags.push_back(tag); + + continue; } + if (token[0] == 's' && IS_SPACE(token[1])) { + // smoothing group id + token += 2; + + // skip space. + token += strspn(token, " \t"); // skip space + + if (token[0] == '\0') { + continue; + } + + if (token[0] == '\r' || token[1] == '\n') { + continue; + } + + if (strlen(token) >= 3) { + if (token[0] == 'o' && token[1] == 'f' && token[2] == 'f') { + current_smoothing_id = 0; + } + } else { + // assume number + int smGroupId = parseInt(&token); + if (smGroupId < 0) { + // parse error. force set to 0. + // FIXME(syoyo): Report warning. + current_smoothing_id = 0; + } else { + current_smoothing_id = static_cast(smGroupId); + } + } + + continue; + } // smoothing group id + // Ignore unknown command. } bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, - triangulate); + triangulate, v); // exportFaceGroupToShape return false when `usemtl` is called in the last // line. // we also add `shape` to `shapes` when `shape.mesh` has already some @@ -1716,6 +2110,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, attrib->vertices.swap(v); attrib->normals.swap(vn); attrib->texcoords.swap(vt); + attrib->colors.swap(vc); return true; } @@ -1768,7 +2163,8 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, // vertex if (token[0] == 'v' && IS_SPACE((token[1]))) { token += 2; - float x, y, z, w; // w is optional. default = 1.0 + // TODO(syoyo): Support parsing vertex color extension. + real_t x, y, z, w; // w is optional. default = 1.0 parseV(&x, &y, &z, &w, &token); if (callback.vertex_cb) { callback.vertex_cb(user_data, x, y, z, w); @@ -1779,8 +2175,8 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, // normal if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { token += 3; - float x, y, z; - parseFloat3(&x, &y, &z, &token); + real_t x, y, z; + parseReal3(&x, &y, &z, &token); if (callback.normal_cb) { callback.normal_cb(user_data, x, y, z); } @@ -1790,8 +2186,8 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, // texcoord if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { token += 3; - float x, y, z; // y and z are optional. default = 0.0 - parseFloat3(&x, &y, &z, &token); + real_t x, y, z; // y and z are optional. default = 0.0 + parseReal3(&x, &y, &z, &token); if (callback.texcoord_cb) { callback.texcoord_cb(user_data, x, y, z); } @@ -1805,7 +2201,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, indices.clear(); while (!IS_NEW_LINE(token[0])) { - vertex_index vi = parseRawTriple(&token); + vertex_index_t vi = parseRawTriple(&token); index_t idx; idx.vertex_index = vi.v_idx; @@ -1827,14 +2223,10 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, // use mtl if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, - static_cast(_countof(namebuf))); -#else - std::sscanf(token, "%s", namebuf); -#endif + std::stringstream ss; + ss << token; + std::string namebuf = ss.str(); int newMaterialId = -1; if (material_map.find(namebuf) != material_map.end()) { @@ -1848,7 +2240,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, } if (callback.usemtl_cb) { - callback.usemtl_cb(user_data, namebuf, material_id); + callback.usemtl_cb(user_data, namebuf.c_str(), material_id); } continue; @@ -1942,14 +2334,11 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, // object name if (token[0] == 'o' && IS_SPACE((token[1]))) { // @todo { multiple object name? } - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - std::string object_name = std::string(namebuf); + + std::stringstream ss; + ss << token; + std::string object_name = ss.str(); if (callback.object_cb) { callback.object_cb(user_data, object_name.c_str()); @@ -1962,14 +2351,10 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, if (token[0] == 't' && IS_SPACE(token[1])) { tag_t tag; - char namebuf[4096]; token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - std::sscanf(token, "%s", namebuf); -#endif - tag.name = std::string(namebuf); + std::stringstream ss; + ss << token; + tag.name = ss.str(); token += tag.name.size() + 1; @@ -1982,23 +2367,17 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, token += strcspn(token, "/ \t\r") + 1; } - tag.floatValues.resize(static_cast(ts.num_floats)); - for (size_t i = 0; i < static_cast(ts.num_floats); ++i) { - tag.floatValues[i] = parseFloat(&token); + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); token += strcspn(token, "/ \t\r") + 1; } tag.stringValues.resize(static_cast(ts.num_strings)); for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { - char stringValueBuffer[4096]; - -#ifdef _MSC_VER - sscanf_s(token, "%s", stringValueBuffer, - (unsigned)_countof(stringValueBuffer)); -#else - std::sscanf(token, "%s", stringValueBuffer); -#endif - tag.stringValues[i] = stringValueBuffer; + std::stringstream ss; + ss << token; + tag.stringValues[i] = ss.str(); token += tag.stringValues[i].size() + 1; } @@ -2015,6 +2394,10 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, return true; } + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif } // namespace tinyobj #endif