CCObjLoader.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. //
  2. // Copyright 2012-2015, Syoyo Fujita.
  3. //
  4. // Licensed under 2-clause BSD license.
  5. //
  6. //
  7. // version 0.9.13: Report "Material file not found message" in `err`(#46)
  8. // version 0.9.12: Fix groups being ignored if they have 'usemtl' just before 'g' (#44)
  9. // version 0.9.11: Invert `Tr` parameter(#43)
  10. // version 0.9.10: Fix seg fault on windows.
  11. // version 0.9.9 : Replace atof() with custom parser.
  12. // version 0.9.8 : Fix multi-materials(per-face material ID).
  13. // version 0.9.7 : Support multi-materials(per-face material ID) per
  14. // object/group.
  15. // version 0.9.6 : Support Ni(index of refraction) mtl parameter.
  16. // Parse transmittance material parameter correctly.
  17. // version 0.9.5 : Parse multiple group name.
  18. // Add support of specifying the base path to load material file.
  19. // version 0.9.4 : Initial support of group tag(g)
  20. // version 0.9.3 : Fix parsing triple 'x/y/z'
  21. // version 0.9.2 : Add more .mtl load support
  22. // version 0.9.1 : Add initial .mtl load support
  23. // version 0.9.0 : Initial
  24. //
  25. #include <cstdlib>
  26. #include <cstring>
  27. #include <cassert>
  28. #include <cmath>
  29. #include <cstddef>
  30. #include <string>
  31. #include <vector>
  32. #include <map>
  33. #include <fstream>
  34. #include <sstream>
  35. #include "platform/CCFileUtils.h"
  36. #include "base/ccUtils.h"
  37. #include "3d/CCObjLoader.h"
  38. namespace tinyobj {
  39. #define TINYOBJ_SSCANF_BUFFER_SIZE (4096)
  40. struct vertex_index {
  41. int v_idx, vt_idx, vn_idx;
  42. vertex_index(){};
  43. vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx){};
  44. vertex_index(int vidx, int vtidx, int vnidx)
  45. : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx){};
  46. };
  47. // for std::map
  48. static inline bool operator<(const vertex_index &a, const vertex_index &b) {
  49. if (a.v_idx != b.v_idx)
  50. return (a.v_idx < b.v_idx);
  51. if (a.vn_idx != b.vn_idx)
  52. return (a.vn_idx < b.vn_idx);
  53. if (a.vt_idx != b.vt_idx)
  54. return (a.vt_idx < b.vt_idx);
  55. return false;
  56. }
  57. struct obj_shape {
  58. std::vector<float> v;
  59. std::vector<float> vn;
  60. std::vector<float> vt;
  61. };
  62. static inline bool isSpace(const char c) { return (c == ' ') || (c == '\t'); }
  63. static inline bool isNewLine(const char c) {
  64. return (c == '\r') || (c == '\n') || (c == '\0');
  65. }
  66. // Make index zero-base, and also support relative index.
  67. static inline int fixIndex(int idx, int n) {
  68. if (idx > 0) return idx - 1;
  69. if (idx == 0) return 0;
  70. return n + idx; // negative value = relative
  71. }
  72. static inline std::string parseString(const char *&token) {
  73. std::string s;
  74. token += strspn(token, " \t");
  75. size_t e = strcspn(token, " \t\r");
  76. s = std::string(token, &token[e]);
  77. token += e;
  78. return s;
  79. }
  80. static inline int parseInt(const char *&token) {
  81. token += strspn(token, " \t");
  82. int i = atoi(token);
  83. token += strcspn(token, " \t\r");
  84. return i;
  85. }
  86. // Tries to parse a floating point number located at s.
  87. //
  88. // s_end should be a location in the string where reading should absolutely
  89. // stop. For example at the end of the string, to prevent buffer overflows.
  90. //
  91. // Parses the following EBNF grammar:
  92. // sign = "+" | "-" ;
  93. // END = ? anything not in digit ?
  94. // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
  95. // integer = [sign] , digit , {digit} ;
  96. // decimal = integer , ["." , integer] ;
  97. // float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ;
  98. //
  99. // Valid strings are for example:
  100. // -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2
  101. //
  102. // If the parsing is a success, result is set to the parsed value and true
  103. // is returned.
  104. //
  105. // The function is greedy and will parse until any of the following happens:
  106. // - a non-conforming character is encountered.
  107. // - s_end is reached.
  108. //
  109. // The following situations triggers a failure:
  110. // - s >= s_end.
  111. // - parse failure.
  112. //
  113. static bool tryParseDouble(const char *s, const char *s_end, double *result)
  114. {
  115. if (s >= s_end)
  116. {
  117. return false;
  118. }
  119. double mantissa = 0.0;
  120. // This exponent is base 2 rather than 10.
  121. // However the exponent we parse is supposed to be one of ten,
  122. // thus we must take care to convert the exponent/and or the
  123. // mantissa to a * 2^E, where a is the mantissa and E is the
  124. // exponent.
  125. // To get the final double we will use ldexp, it requires the
  126. // exponent to be in base 2.
  127. int exponent = 0;
  128. // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED
  129. // TO JUMP OVER DEFINITIONS.
  130. char sign = '+';
  131. char exp_sign = '+';
  132. char const *curr = s;
  133. // How many characters were read in a loop.
  134. int read = 0;
  135. // Tells whether a loop terminated due to reaching s_end.
  136. bool end_not_reached = false;
  137. /*
  138. BEGIN PARSING.
  139. */
  140. // Find out what sign we've got.
  141. if (*curr == '+' || *curr == '-')
  142. {
  143. sign = *curr;
  144. curr++;
  145. }
  146. else if (isdigit(*curr)) { /* Pass through. */ }
  147. else
  148. {
  149. goto fail;
  150. }
  151. // Read the integer part.
  152. while ((end_not_reached = (curr != s_end)) && isdigit(*curr))
  153. {
  154. mantissa *= 10;
  155. mantissa += static_cast<int>(*curr - 0x30);
  156. curr++; read++;
  157. }
  158. // We must make sure we actually got something.
  159. if (read == 0)
  160. goto fail;
  161. // We allow numbers of form "#", "###" etc.
  162. if (!end_not_reached)
  163. goto assemble;
  164. // Read the decimal part.
  165. if (*curr == '.')
  166. {
  167. curr++;
  168. read = 1;
  169. while ((end_not_reached = (curr != s_end)) && isdigit(*curr))
  170. {
  171. // NOTE: Don't use powf here, it will absolutely murder precision.
  172. mantissa += static_cast<int>(*curr - 0x30) * pow(10.0, -read);
  173. read++; curr++;
  174. }
  175. }
  176. else if (*curr == 'e' || *curr == 'E') {}
  177. else
  178. {
  179. goto assemble;
  180. }
  181. if (!end_not_reached)
  182. goto assemble;
  183. // Read the exponent part.
  184. if (*curr == 'e' || *curr == 'E')
  185. {
  186. curr++;
  187. // Figure out if a sign is present and if it is.
  188. if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-'))
  189. {
  190. exp_sign = *curr;
  191. curr++;
  192. }
  193. else if (isdigit(*curr)) { /* Pass through. */ }
  194. else
  195. {
  196. // Empty E is not allowed.
  197. goto fail;
  198. }
  199. read = 0;
  200. while ((end_not_reached = (curr != s_end)) && isdigit(*curr))
  201. {
  202. exponent *= 10;
  203. exponent += static_cast<int>(*curr - 0x30);
  204. curr++; read++;
  205. }
  206. exponent *= (exp_sign == '+'? 1 : -1);
  207. if (read == 0)
  208. goto fail;
  209. }
  210. assemble:
  211. *result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
  212. return true;
  213. fail:
  214. return false;
  215. }
  216. static inline float parseFloat(const char *&token) {
  217. token += strspn(token, " \t");
  218. #ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER
  219. float f = (float)utils::atof(token);
  220. token += strcspn(token, " \t\r");
  221. #else
  222. const char *end = token + strcspn(token, " \t\r");
  223. double val = 0.0;
  224. tryParseDouble(token, end, &val);
  225. float f = static_cast<float>(val);
  226. token = end;
  227. #endif
  228. return f;
  229. }
  230. static inline void parseFloat2(float &x, float &y, const char *&token) {
  231. x = parseFloat(token);
  232. y = parseFloat(token);
  233. }
  234. static inline void parseFloat3(float &x, float &y, float &z,
  235. const char *&token) {
  236. x = parseFloat(token);
  237. y = parseFloat(token);
  238. z = parseFloat(token);
  239. }
  240. // Parse triples: i, i/j/k, i//k, i/j
  241. static vertex_index parseTriple(const char *&token, int vsize, int vnsize,
  242. int vtsize) {
  243. vertex_index vi(-1);
  244. vi.v_idx = fixIndex(atoi(token), vsize);
  245. token += strcspn(token, "/ \t\r");
  246. if (token[0] != '/') {
  247. return vi;
  248. }
  249. token++;
  250. // i//k
  251. if (token[0] == '/') {
  252. token++;
  253. vi.vn_idx = fixIndex(atoi(token), vnsize);
  254. token += strcspn(token, "/ \t\r");
  255. return vi;
  256. }
  257. // i/j/k or i/j
  258. vi.vt_idx = fixIndex(atoi(token), vtsize);
  259. token += strcspn(token, "/ \t\r");
  260. if (token[0] != '/') {
  261. return vi;
  262. }
  263. // i/j/k
  264. token++; // skip '/'
  265. vi.vn_idx = fixIndex(atoi(token), vnsize);
  266. token += strcspn(token, "/ \t\r");
  267. return vi;
  268. }
  269. static unsigned int
  270. updateVertex(std::map<vertex_index, unsigned int> &vertexCache,
  271. std::vector<float> &positions, std::vector<float> &normals,
  272. std::vector<float> &texcoords,
  273. const std::vector<float> &in_positions,
  274. const std::vector<float> &in_normals,
  275. const std::vector<float> &in_texcoords, const vertex_index &i) {
  276. const std::map<vertex_index, unsigned int>::iterator it = vertexCache.find(i);
  277. if (it != vertexCache.end()) {
  278. // found cache
  279. return it->second;
  280. }
  281. assert(in_positions.size() > (unsigned int)(3 * i.v_idx + 2));
  282. positions.push_back(in_positions[3 * i.v_idx + 0]);
  283. positions.push_back(in_positions[3 * i.v_idx + 1]);
  284. positions.push_back(in_positions[3 * i.v_idx + 2]);
  285. if (i.vn_idx >= 0) {
  286. normals.push_back(in_normals[3 * i.vn_idx + 0]);
  287. normals.push_back(in_normals[3 * i.vn_idx + 1]);
  288. normals.push_back(in_normals[3 * i.vn_idx + 2]);
  289. }
  290. if (i.vt_idx >= 0) {
  291. texcoords.push_back(in_texcoords[2 * i.vt_idx + 0]);
  292. texcoords.push_back(in_texcoords[2 * i.vt_idx + 1]);
  293. }
  294. unsigned int idx = static_cast<unsigned int>(positions.size() / 3 - 1);
  295. vertexCache[i] = idx;
  296. return idx;
  297. }
  298. void InitMaterial(material_t &material) {
  299. material.name = "";
  300. material.ambient_texname = "";
  301. material.diffuse_texname = "";
  302. material.specular_texname = "";
  303. material.normal_texname = "";
  304. for (int i = 0; i < 3; i++) {
  305. material.ambient[i] = 0.f;
  306. material.diffuse[i] = 0.f;
  307. material.specular[i] = 0.f;
  308. material.transmittance[i] = 0.f;
  309. material.emission[i] = 0.f;
  310. }
  311. material.illum = 0;
  312. material.dissolve = 1.f;
  313. material.shininess = 1.f;
  314. material.ior = 1.f;
  315. material.unknown_parameter.clear();
  316. }
  317. static bool exportFaceGroupToShape(
  318. shape_t &shape, std::map<vertex_index, unsigned int> vertexCache,
  319. const std::vector<float> &in_positions,
  320. const std::vector<float> &in_normals,
  321. const std::vector<float> &in_texcoords,
  322. const std::vector<std::vector<vertex_index> > &faceGroup,
  323. const int material_id, const std::string &name, bool clearCache) {
  324. if (faceGroup.empty()) {
  325. return false;
  326. }
  327. // Flatten vertices and indices
  328. for (size_t i = 0, size = faceGroup.size(); i < size; ++i) {
  329. const std::vector<vertex_index> &face = faceGroup[i];
  330. vertex_index i0 = face[0];
  331. vertex_index i1(-1);
  332. vertex_index i2 = face[1];
  333. size_t npolys = face.size();
  334. // Polygon -> triangle fan conversion
  335. for (size_t k = 2; k < npolys; k++) {
  336. i1 = i2;
  337. i2 = face[k];
  338. unsigned int v0 = updateVertex(
  339. vertexCache, shape.mesh.positions, shape.mesh.normals,
  340. shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0);
  341. unsigned int v1 = updateVertex(
  342. vertexCache, shape.mesh.positions, shape.mesh.normals,
  343. shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1);
  344. unsigned int v2 = updateVertex(
  345. vertexCache, shape.mesh.positions, shape.mesh.normals,
  346. shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2);
  347. shape.mesh.indices.push_back(v0);
  348. shape.mesh.indices.push_back(v1);
  349. shape.mesh.indices.push_back(v2);
  350. shape.mesh.material_ids.push_back(material_id);
  351. }
  352. }
  353. shape.name = name;
  354. if (clearCache)
  355. vertexCache.clear();
  356. return true;
  357. }
  358. static std::string& replacePathSeperator(std::string& path)
  359. {
  360. for (std::string::size_type i = 0, size = path.size(); i < size; ++i) {
  361. if (path[i] == '\\')
  362. path[i] = '/';
  363. }
  364. return path;
  365. }
  366. std::string LoadMtl(std::map<std::string, int> &material_map,
  367. std::vector<material_t> &materials,
  368. std::istream &inStream) {
  369. std::stringstream err;
  370. // Create a default material anyway.
  371. material_t material;
  372. InitMaterial(material);
  373. int maxchars = 8192; // Alloc enough size.
  374. std::vector<char> buf(maxchars); // Alloc enough size.
  375. while (inStream.peek() != -1) {
  376. inStream.getline(&buf[0], maxchars);
  377. std::string linebuf(&buf[0]);
  378. // Trim newline '\r\n' or '\n'
  379. if (linebuf.size() > 0) {
  380. if (linebuf[linebuf.size() - 1] == '\n')
  381. linebuf.erase(linebuf.size() - 1);
  382. }
  383. if (linebuf.size() > 0) {
  384. if (linebuf[linebuf.size() - 1] == '\r')
  385. linebuf.erase(linebuf.size() - 1);
  386. }
  387. // Skip if empty line.
  388. if (linebuf.empty()) {
  389. continue;
  390. }
  391. // Skip leading space.
  392. const char *token = linebuf.c_str();
  393. token += strspn(token, " \t");
  394. assert(token);
  395. if (token[0] == '\0')
  396. continue; // empty line
  397. if (token[0] == '#')
  398. continue; // comment line
  399. // new mtl
  400. if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) {
  401. // flush previous material.
  402. if (!material.name.empty()) {
  403. material_map.insert(
  404. std::pair<std::string, int>(material.name, static_cast<int>(materials.size())));
  405. materials.push_back(material);
  406. }
  407. // initial temporary material
  408. InitMaterial(material);
  409. // set new mtl name
  410. char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
  411. token += 7;
  412. #ifdef _MSC_VER
  413. sscanf_s(token, "%s", namebuf, _countof(namebuf));
  414. #else
  415. sscanf(token, "%s", namebuf);
  416. #endif
  417. material.name = namebuf;
  418. continue;
  419. }
  420. // ambient
  421. if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) {
  422. token += 2;
  423. float r, g, b;
  424. parseFloat3(r, g, b, token);
  425. material.ambient[0] = r;
  426. material.ambient[1] = g;
  427. material.ambient[2] = b;
  428. continue;
  429. }
  430. // diffuse
  431. if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) {
  432. token += 2;
  433. float r, g, b;
  434. parseFloat3(r, g, b, token);
  435. material.diffuse[0] = r;
  436. material.diffuse[1] = g;
  437. material.diffuse[2] = b;
  438. continue;
  439. }
  440. // specular
  441. if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) {
  442. token += 2;
  443. float r, g, b;
  444. parseFloat3(r, g, b, token);
  445. material.specular[0] = r;
  446. material.specular[1] = g;
  447. material.specular[2] = b;
  448. continue;
  449. }
  450. // transmittance
  451. if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) {
  452. token += 2;
  453. float r, g, b;
  454. parseFloat3(r, g, b, token);
  455. material.transmittance[0] = r;
  456. material.transmittance[1] = g;
  457. material.transmittance[2] = b;
  458. continue;
  459. }
  460. // ior(index of refraction)
  461. if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) {
  462. token += 2;
  463. material.ior = parseFloat(token);
  464. continue;
  465. }
  466. // emission
  467. if (token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) {
  468. token += 2;
  469. float r, g, b;
  470. parseFloat3(r, g, b, token);
  471. material.emission[0] = r;
  472. material.emission[1] = g;
  473. material.emission[2] = b;
  474. continue;
  475. }
  476. // shininess
  477. if (token[0] == 'N' && token[1] == 's' && isSpace(token[2])) {
  478. token += 2;
  479. material.shininess = parseFloat(token);
  480. continue;
  481. }
  482. // illum model
  483. if (0 == strncmp(token, "illum", 5) && isSpace(token[5])) {
  484. token += 6;
  485. material.illum = parseInt(token);
  486. continue;
  487. }
  488. // dissolve
  489. if ((token[0] == 'd' && isSpace(token[1]))) {
  490. token += 1;
  491. material.dissolve = parseFloat(token);
  492. continue;
  493. }
  494. if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) {
  495. token += 2;
  496. // Invert value of Tr(assume Tr is in range [0, 1])
  497. material.dissolve = 1.0f - parseFloat(token);
  498. continue;
  499. }
  500. // ambient texture
  501. if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6])) {
  502. token += 7;
  503. material.ambient_texname = token;
  504. replacePathSeperator(material.ambient_texname);
  505. continue;
  506. }
  507. // diffuse texture
  508. if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6])) {
  509. token += 7;
  510. material.diffuse_texname = token;
  511. replacePathSeperator(material.diffuse_texname);
  512. continue;
  513. }
  514. // specular texture
  515. if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6])) {
  516. token += 7;
  517. material.specular_texname = token;
  518. replacePathSeperator(material.specular_texname);
  519. continue;
  520. }
  521. // normal texture
  522. if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) {
  523. token += 7;
  524. material.normal_texname = token;
  525. replacePathSeperator(material.normal_texname);
  526. continue;
  527. }
  528. // unknown parameter
  529. const char *_space = strchr(token, ' ');
  530. if (!_space) {
  531. _space = strchr(token, '\t');
  532. }
  533. if (_space) {
  534. std::ptrdiff_t len = _space - token;
  535. std::string key(token, len);
  536. std::string value = _space + 1;
  537. material.unknown_parameter.insert(
  538. std::pair<std::string, std::string>(key, value));
  539. }
  540. }
  541. // flush last material.
  542. material_map.insert(
  543. std::pair<std::string, int>(material.name, static_cast<int>(materials.size())));
  544. materials.push_back(material);
  545. return err.str();
  546. }
  547. std::string MaterialFileReader::operator()(const std::string &matId,
  548. std::vector<material_t> &materials,
  549. std::map<std::string, int> &matMap) {
  550. std::string filepath;
  551. if (!m_mtlBasePath.empty()) {
  552. filepath = std::string(m_mtlBasePath) + matId;
  553. } else {
  554. filepath = matId;
  555. }
  556. std::string err = "";
  557. std::istringstream matIStream(cocos2d::FileUtils::getInstance()->getStringFromFile(filepath));
  558. if (!matIStream) {
  559. std::stringstream ss;
  560. ss << "WARN: Material file [ " << filepath << " ] not found. Created a default material.";
  561. err += ss.str();
  562. }
  563. err += LoadMtl(matMap, materials, matIStream);
  564. return err;
  565. }
  566. std::string LoadObj(std::vector<shape_t> &shapes,
  567. std::vector<material_t> &materials, // [output]
  568. const char *filename, const char *mtl_basepath) {
  569. shapes.clear();
  570. std::stringstream err;
  571. std::istringstream ifs(cocos2d::FileUtils::getInstance()->getStringFromFile(filename));
  572. if (!ifs) {
  573. err << "Cannot open file [" << filename << "]" << std::endl;
  574. return err.str();
  575. }
  576. std::string basePath;
  577. if (mtl_basepath) {
  578. basePath = mtl_basepath;
  579. }
  580. MaterialFileReader matFileReader(basePath);
  581. return LoadObj(shapes, materials, ifs, matFileReader);
  582. }
  583. std::string LoadObj(std::vector<shape_t> &shapes,
  584. std::vector<material_t> &materials, // [output]
  585. std::istream &inStream, MaterialReader &readMatFn) {
  586. std::stringstream err;
  587. std::vector<float> v;
  588. std::vector<float> vn;
  589. std::vector<float> vt;
  590. std::vector<std::vector<vertex_index> > faceGroup;
  591. std::string name;
  592. // material
  593. std::map<std::string, int> material_map;
  594. std::map<vertex_index, unsigned int> vertexCache;
  595. int material = -1;
  596. shape_t shape;
  597. int maxchars = 8192; // Alloc enough size.
  598. std::vector<char> buf(maxchars); // Alloc enough size.
  599. while (inStream.peek() != -1) {
  600. inStream.getline(&buf[0], maxchars);
  601. std::string linebuf(&buf[0]);
  602. // Trim newline '\r\n' or '\n'
  603. if (linebuf.size() > 0) {
  604. if (linebuf[linebuf.size() - 1] == '\n')
  605. linebuf.erase(linebuf.size() - 1);
  606. }
  607. if (linebuf.size() > 0) {
  608. if (linebuf[linebuf.size() - 1] == '\r')
  609. linebuf.erase(linebuf.size() - 1);
  610. }
  611. // Skip if empty line.
  612. if (linebuf.empty()) {
  613. continue;
  614. }
  615. // Skip leading space.
  616. const char *token = linebuf.c_str();
  617. token += strspn(token, " \t");
  618. assert(token);
  619. if (token[0] == '\0')
  620. continue; // empty line
  621. if (token[0] == '#')
  622. continue; // comment line
  623. // vertex
  624. if (token[0] == 'v' && isSpace((token[1]))) {
  625. token += 2;
  626. float x, y, z;
  627. parseFloat3(x, y, z, token);
  628. v.push_back(x);
  629. v.push_back(y);
  630. v.push_back(z);
  631. continue;
  632. }
  633. // normal
  634. if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2]))) {
  635. token += 3;
  636. float x, y, z;
  637. parseFloat3(x, y, z, token);
  638. vn.push_back(x);
  639. vn.push_back(y);
  640. vn.push_back(z);
  641. continue;
  642. }
  643. // texcoord
  644. if (token[0] == 'v' && token[1] == 't' && isSpace((token[2]))) {
  645. token += 3;
  646. float x, y;
  647. parseFloat2(x, y, token);
  648. vt.push_back(x);
  649. vt.push_back(y);
  650. continue;
  651. }
  652. // face
  653. if (token[0] == 'f' && isSpace((token[1]))) {
  654. token += 2;
  655. token += strspn(token, " \t");
  656. std::vector<vertex_index> face;
  657. auto first = static_cast<int>(v.size() / 3);
  658. auto second = static_cast<int>(vn.size() / 3);
  659. auto third = static_cast<int>(vt.size() / 2);
  660. while (!isNewLine(token[0])) {
  661. vertex_index vi =
  662. parseTriple(token, first, second, third);
  663. face.push_back(vi);
  664. size_t n = strspn(token, " \t\r");
  665. token += n;
  666. }
  667. faceGroup.push_back(face);
  668. continue;
  669. }
  670. // use mtl
  671. if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) {
  672. char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
  673. token += 7;
  674. #ifdef _MSC_VER
  675. sscanf_s(token, "%s", namebuf, _countof(namebuf));
  676. #else
  677. sscanf(token, "%s", namebuf);
  678. #endif
  679. // Create face group per material.
  680. bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt,
  681. faceGroup, material, name, true);
  682. if (ret) {
  683. shapes.push_back(shape);
  684. }
  685. shape = shape_t();
  686. faceGroup.clear();
  687. if (material_map.find(namebuf) != material_map.end()) {
  688. material = material_map[namebuf];
  689. } else {
  690. // { error!! material not found }
  691. material = -1;
  692. }
  693. continue;
  694. }
  695. // load mtl
  696. if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) {
  697. char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
  698. token += 7;
  699. #ifdef _MSC_VER
  700. sscanf_s(token, "%s", namebuf, _countof(namebuf));
  701. #else
  702. sscanf(token, "%s", namebuf);
  703. #endif
  704. std::string err_mtl = readMatFn(namebuf, materials, material_map);
  705. if (!err_mtl.empty()) {
  706. faceGroup.clear(); // for safety
  707. return err_mtl;
  708. }
  709. continue;
  710. }
  711. // group name
  712. if (token[0] == 'g' && isSpace((token[1]))) {
  713. // flush previous face group.
  714. bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt,
  715. faceGroup, material, name, true);
  716. if (ret) {
  717. shapes.push_back(shape);
  718. }
  719. shape = shape_t();
  720. // material = -1;
  721. faceGroup.clear();
  722. std::vector<std::string> names;
  723. while (!isNewLine(token[0])) {
  724. std::string str = parseString(token);
  725. names.push_back(str);
  726. token += strspn(token, " \t\r"); // skip tag
  727. }
  728. assert(names.size() > 0);
  729. // names[0] must be 'g', so skip the 0th element.
  730. if (names.size() > 1) {
  731. name = names[1];
  732. } else {
  733. name = "";
  734. }
  735. continue;
  736. }
  737. // object name
  738. if (token[0] == 'o' && isSpace((token[1]))) {
  739. // flush previous face group.
  740. bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt,
  741. faceGroup, material, name, true);
  742. if (ret) {
  743. shapes.push_back(shape);
  744. }
  745. // material = -1;
  746. faceGroup.clear();
  747. shape = shape_t();
  748. // @todo { multiple object name? }
  749. char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
  750. token += 2;
  751. #ifdef _MSC_VER
  752. sscanf_s(token, "%s", namebuf, _countof(namebuf));
  753. #else
  754. sscanf(token, "%s", namebuf);
  755. #endif
  756. name = std::string(namebuf);
  757. continue;
  758. }
  759. // Ignore unknown command.
  760. }
  761. bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup,
  762. material, name, true);
  763. if (ret) {
  764. shapes.push_back(shape);
  765. }
  766. faceGroup.clear(); // for safety
  767. return err.str();
  768. }
  769. }