mesh.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. // Copyright 2011 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"); you
  4. // may not use this file except in compliance with the License. You
  5. // may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  12. // implied. See the License for the specific language governing
  13. // permissions and limitations under the License.
  14. #ifndef WEBGL_LOADER_MESH_H_
  15. #define WEBGL_LOADER_MESH_H_
  16. #include <float.h>
  17. #include <limits.h>
  18. #include <math.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <map>
  22. #include <string>
  23. #include <utility>
  24. #include <vector>
  25. #include "base.h"
  26. #include "bounds.h"
  27. #include "stream.h"
  28. #include "utf8.h"
  29. // A short list of floats, useful for parsing a single vector
  30. // attribute.
  31. class ShortFloatList {
  32. public:
  33. // MeshLab can create position attributes with
  34. // color coordinates like: v x y z r g b
  35. static const size_t kMaxNumFloats = 6;
  36. ShortFloatList()
  37. : size_(0)
  38. {
  39. clear();
  40. }
  41. void clear() {
  42. for (size_t i = 0; i < kMaxNumFloats; ++i) {
  43. a_[i] = 0.f;
  44. }
  45. }
  46. // Parse up to kMaxNumFloats from C string.
  47. // TODO: this should instead return endptr, since size
  48. // is recoverable.
  49. size_t ParseLine(const char* line) {
  50. for (size_ = 0; size_ != kMaxNumFloats; ++size_) {
  51. char* endptr = NULL;
  52. a_[size_] = strtof(line, &endptr);
  53. if (endptr == NULL || line == endptr) break;
  54. line = endptr;
  55. }
  56. return size_;
  57. }
  58. float operator[](size_t idx) const {
  59. return a_[idx];
  60. }
  61. void AppendTo(AttribList* attribs) const {
  62. AppendNTo(attribs, size_);
  63. }
  64. void AppendNTo(AttribList* attribs, const size_t sz) const {
  65. attribs->insert(attribs->end(), a_, a_ + sz);
  66. }
  67. bool empty() const { return size_ == 0; }
  68. size_t size() const { return size_; }
  69. private:
  70. float a_[kMaxNumFloats];
  71. size_t size_;
  72. };
  73. class IndexFlattener {
  74. public:
  75. explicit IndexFlattener(size_t num_positions)
  76. : count_(0),
  77. table_(num_positions) {
  78. }
  79. int count() const { return count_; }
  80. void reserve(size_t size) {
  81. table_.reserve(size);
  82. }
  83. // Returns a pair of: < flattened index, newly inserted >.
  84. std::pair<int, bool> GetFlattenedIndex(int position_index,
  85. int texcoord_index,
  86. int normal_index) {
  87. if (position_index >= static_cast<int>(table_.size())) {
  88. table_.resize(position_index + 1);
  89. }
  90. // First, optimistically look up position_index in the table.
  91. IndexType& index = table_[position_index];
  92. if (index.position_or_flat == kIndexUnknown) {
  93. // This is the first time we've seen this position in the table,
  94. // so fill it. Since the table is indexed by position, we can
  95. // use the position_or_flat_index field to store the flat index.
  96. const int flat_index = count_++;
  97. index.position_or_flat = flat_index;
  98. index.texcoord = texcoord_index;
  99. index.normal = normal_index;
  100. return std::make_pair(flat_index, true);
  101. } else if (index.position_or_flat == kIndexNotInTable) {
  102. // There are multiple flattened indices at this position index,
  103. // so resort to the map.
  104. return GetFlattenedIndexFromMap(position_index,
  105. texcoord_index,
  106. normal_index);
  107. } else if (index.texcoord == texcoord_index &&
  108. index.normal == normal_index) {
  109. // The other indices match, so we can use the value cached in
  110. // the table.
  111. return std::make_pair(index.position_or_flat, false);
  112. }
  113. // The other indices don't match, so we mark this table entry,
  114. // and insert both the old and new indices into the map.
  115. const IndexType old_index(position_index, index.texcoord, index.normal);
  116. map_.insert(std::make_pair(old_index, index.position_or_flat));
  117. index.position_or_flat = kIndexNotInTable;
  118. const IndexType new_index(position_index, texcoord_index, normal_index);
  119. const int flat_index = count_++;
  120. map_.insert(std::make_pair(new_index, flat_index));
  121. return std::make_pair(flat_index, true);
  122. }
  123. private:
  124. std::pair<int, bool> GetFlattenedIndexFromMap(int position_index,
  125. int texcoord_index,
  126. int normal_index) {
  127. IndexType index(position_index, texcoord_index, normal_index);
  128. MapType::iterator iter = map_.lower_bound(index);
  129. if (iter == map_.end() || iter->first != index) {
  130. const int flat_index = count_++;
  131. map_.insert(iter, std::make_pair(index, flat_index));
  132. return std::make_pair(flat_index, true);
  133. } else {
  134. return std::make_pair(iter->second, false);
  135. }
  136. }
  137. static const int kIndexUnknown = -1;
  138. static const int kIndexNotInTable = -2;
  139. struct IndexType {
  140. IndexType()
  141. : position_or_flat(kIndexUnknown),
  142. texcoord(kIndexUnknown),
  143. normal(kIndexUnknown)
  144. { }
  145. IndexType(int position_index, int texcoord_index, int normal_index)
  146. : position_or_flat(position_index),
  147. texcoord(texcoord_index),
  148. normal(normal_index)
  149. { }
  150. // I'm being tricky/lazy here. The table_ stores the flattened
  151. // index in the first field, since it is indexed by position. The
  152. // map_ stores position and uses this struct as a key to lookup the
  153. // flattened index.
  154. int position_or_flat;
  155. int texcoord;
  156. int normal;
  157. // An ordering for std::map.
  158. bool operator<(const IndexType& that) const {
  159. if (position_or_flat == that.position_or_flat) {
  160. if (texcoord == that.texcoord) {
  161. return normal < that.normal;
  162. } else {
  163. return texcoord < that.texcoord;
  164. }
  165. } else {
  166. return position_or_flat < that.position_or_flat;
  167. }
  168. }
  169. bool operator==(const IndexType& that) const {
  170. return position_or_flat == that.position_or_flat &&
  171. texcoord == that.texcoord && normal == that.normal;
  172. }
  173. bool operator!=(const IndexType& that) const {
  174. return !operator==(that);
  175. }
  176. };
  177. typedef std::map<IndexType, int> MapType;
  178. int count_;
  179. std::vector<IndexType> table_;
  180. MapType map_;
  181. };
  182. static inline size_t positionDim() { return 3; }
  183. static inline size_t texcoordDim() { return 2; }
  184. static inline size_t normalDim() { return 3; }
  185. // TODO(wonchun): Make a c'tor to properly initialize.
  186. struct GroupStart {
  187. size_t offset; // offset into draw_mesh_.indices.
  188. unsigned int group_line;
  189. int min_index, max_index; // range into attribs.
  190. webgl_loader::Bounds bounds;
  191. };
  192. class DrawBatch {
  193. public:
  194. DrawBatch()
  195. : flattener_(0),
  196. current_group_line_(0xFFFFFFFF) {
  197. }
  198. const std::vector<GroupStart>& group_starts() const {
  199. return group_starts_;
  200. }
  201. void Init(AttribList* positions, AttribList* texcoords, AttribList* normals) {
  202. positions_ = positions;
  203. texcoords_ = texcoords;
  204. normals_ = normals;
  205. flattener_.reserve(1024);
  206. }
  207. void AddTriangle(unsigned int group_line, int* indices) {
  208. if (group_line != current_group_line_) {
  209. current_group_line_ = group_line;
  210. GroupStart group_start;
  211. group_start.offset = draw_mesh_.indices.size();
  212. group_start.group_line = group_line;
  213. group_start.min_index = INT_MAX;
  214. group_start.max_index = INT_MIN;
  215. group_start.bounds.Clear();
  216. group_starts_.push_back(group_start);
  217. }
  218. GroupStart& group = group_starts_.back();
  219. for (size_t i = 0; i < 9; i += 3) {
  220. // .OBJ files use 1-based indexing.
  221. const int position_index = indices[i + 0] - 1;
  222. const int texcoord_index = indices[i + 1] - 1;
  223. const int normal_index = indices[i + 2] - 1;
  224. const std::pair<int, bool> flattened = flattener_.GetFlattenedIndex(
  225. position_index, texcoord_index, normal_index);
  226. const int flat_index = flattened.first;
  227. CHECK(flat_index >= 0);
  228. draw_mesh_.indices.push_back(flat_index);
  229. if (flattened.second) {
  230. // This is a new index. Keep track of index ranges and vertex
  231. // bounds.
  232. if (flat_index > group.max_index) {
  233. group.max_index = flat_index;
  234. }
  235. if (flat_index < group.min_index) {
  236. group.min_index = flat_index;
  237. }
  238. const size_t new_loc = draw_mesh_.attribs.size();
  239. CHECK(8*size_t(flat_index) == new_loc);
  240. for (size_t i = 0; i < positionDim(); ++i) {
  241. draw_mesh_.attribs.push_back(
  242. positions_->at(positionDim() * position_index + i));
  243. }
  244. if (texcoord_index == -1) {
  245. for (size_t i = 0; i < texcoordDim(); ++i) {
  246. draw_mesh_.attribs.push_back(0);
  247. }
  248. } else {
  249. for (size_t i = 0; i < texcoordDim(); ++i) {
  250. draw_mesh_.attribs.push_back(
  251. texcoords_->at(texcoordDim() * texcoord_index + i));
  252. }
  253. }
  254. if (normal_index == -1) {
  255. for (size_t i = 0; i < normalDim(); ++i) {
  256. draw_mesh_.attribs.push_back(0);
  257. }
  258. } else {
  259. for (size_t i = 0; i < normalDim(); ++i) {
  260. draw_mesh_.attribs.push_back(
  261. normals_->at(normalDim() * normal_index + i));
  262. }
  263. }
  264. // TODO: is the covariance body useful for anything?
  265. group.bounds.EncloseAttrib(&draw_mesh_.attribs[new_loc]);
  266. }
  267. }
  268. }
  269. const DrawMesh& draw_mesh() const {
  270. return draw_mesh_;
  271. }
  272. private:
  273. AttribList* positions_, *texcoords_, *normals_;
  274. DrawMesh draw_mesh_;
  275. IndexFlattener flattener_;
  276. unsigned int current_group_line_;
  277. std::vector<GroupStart> group_starts_;
  278. };
  279. struct Material {
  280. std::string name;
  281. float Kd[3];
  282. std::string map_Kd;
  283. std::string d;
  284. void DumpJson(FILE* out = stdout) const {
  285. fprintf(out, " \"%s\": { ", name.c_str());
  286. if (!d.empty()) {
  287. fprintf(out, "\"d\": %s ,", d.c_str());
  288. }
  289. if (map_Kd.empty()) {
  290. fprintf(out, "\"Kd\": [%hu, %hu, %hu] }",
  291. Quantize(Kd[0], 0, 1, 255),
  292. Quantize(Kd[1], 0, 1, 255),
  293. Quantize(Kd[2], 0, 1, 255));
  294. } else {
  295. fprintf(out, "\"map_Kd\": \"%s\" }", map_Kd.c_str());
  296. }
  297. }
  298. };
  299. typedef std::vector<Material> MaterialList;
  300. class WavefrontMtlFile {
  301. public:
  302. explicit WavefrontMtlFile(FILE* fp) {
  303. ParseFile(fp);
  304. }
  305. const MaterialList& materials() const {
  306. return materials_;
  307. }
  308. private:
  309. // TODO: factor this parsing stuff out.
  310. void ParseFile(FILE* fp) {
  311. // TODO: don't use a fixed-size buffer.
  312. const size_t kLineBufferSize = 256;
  313. char buffer[kLineBufferSize];
  314. unsigned int line_num = 1;
  315. while (fgets(buffer, kLineBufferSize, fp) != NULL) {
  316. char* stripped = StripLeadingWhitespace(buffer);
  317. TerminateAtNewlineOrComment(stripped);
  318. ParseLine(stripped, line_num++);
  319. }
  320. }
  321. void ParseLine(const char* line, unsigned int line_num) {
  322. switch (*line) {
  323. case 'K':
  324. ParseColor(line + 1, line_num);
  325. break;
  326. case 'd':
  327. ParseD(line + 1, line_num);
  328. break;
  329. case 'm':
  330. if (0 == strncmp(line + 1, "ap_Kd", 5)) {
  331. ParseMapKd(line + 6, line_num);
  332. }
  333. break;
  334. case 'n':
  335. if (0 == strncmp(line + 1, "ewmtl", 5)) {
  336. ParseNewmtl(line + 6, line_num);
  337. }
  338. default:
  339. break;
  340. }
  341. }
  342. void ParseColor(const char* line, unsigned int line_num) {
  343. switch (*line) {
  344. case 'd': {
  345. ShortFloatList floats;
  346. floats.ParseLine(line + 1);
  347. float* Kd = current_->Kd;
  348. Kd[0] = floats[0];
  349. Kd[1] = floats[1];
  350. Kd[2] = floats[2];
  351. break;
  352. }
  353. default:
  354. break;
  355. }
  356. }
  357. void ParseD(const char* line, unsigned int line_num) {
  358. current_->d = StripLeadingWhitespace(line);
  359. }
  360. void ParseMapKd(const char* line, unsigned int line_num) {
  361. current_->map_Kd = StripLeadingWhitespace(line);
  362. }
  363. void ParseNewmtl(const char* line, unsigned int line_num) {
  364. materials_.push_back(Material());
  365. current_ = &materials_.back();
  366. ToLower(StripLeadingWhitespace(line), &current_->name);
  367. }
  368. Material* current_;
  369. MaterialList materials_;
  370. };
  371. typedef std::map<std::string, DrawBatch> MaterialBatches;
  372. // TODO: consider splitting this into a low-level parser and a high-level
  373. // object.
  374. class WavefrontObjFile {
  375. public:
  376. explicit WavefrontObjFile(FILE* fp) {
  377. current_batch_ = &material_batches_[""];
  378. current_batch_->Init(&positions_, &texcoords_, &normals_);
  379. current_group_line_ = 0;
  380. line_to_groups_.insert(std::make_pair(0, "default"));
  381. ParseFile(fp);
  382. }
  383. const MaterialList& materials() const {
  384. return materials_;
  385. }
  386. const MaterialBatches& material_batches() const {
  387. return material_batches_;
  388. }
  389. const std::string& LineToGroup(unsigned int line) const {
  390. typedef LineToGroups::const_iterator Iterator;
  391. typedef std::pair<Iterator, Iterator> EqualRange;
  392. EqualRange equal_range = line_to_groups_.equal_range(line);
  393. const std::string* best_group = NULL;
  394. int best_count = 0;
  395. for (Iterator iter = equal_range.first; iter != equal_range.second;
  396. ++iter) {
  397. const std::string& group = iter->second;
  398. const int count = group_counts_.find(group)->second;
  399. if (!best_group || (count < best_count)) {
  400. best_group = &group;
  401. best_count = count;
  402. }
  403. }
  404. if (!best_group) {
  405. ErrorLine("no suitable group found", line);
  406. }
  407. return *best_group;
  408. }
  409. void DumpDebug() const {
  410. printf("positions size: " PRIuS "\n"
  411. "texcoords size: " PRIuS "\n"
  412. "normals size: " PRIuS "\n",
  413. positions_.size(), texcoords_.size(), normals_.size());
  414. }
  415. private:
  416. WavefrontObjFile() { } // For testing.
  417. void ParseFile(FILE* fp) {
  418. // TODO: don't use a fixed-size buffer.
  419. const size_t kLineBufferSize = 256;
  420. char buffer[kLineBufferSize] = { 0 };
  421. unsigned int line_num = 1;
  422. while (fgets(buffer, kLineBufferSize, fp) != NULL) {
  423. char* stripped = StripLeadingWhitespace(buffer);
  424. TerminateAtNewlineOrComment(stripped);
  425. ParseLine(stripped, line_num++);
  426. }
  427. }
  428. void ParseLine(const char* line, unsigned int line_num) {
  429. switch (*line) {
  430. case 'v':
  431. ParseAttrib(line + 1, line_num);
  432. break;
  433. case 'f':
  434. ParseFace(line + 1, line_num);
  435. break;
  436. case 'g':
  437. if (isspace(line[1])) {
  438. ParseGroup(line + 2, line_num);
  439. } else {
  440. goto unknown;
  441. }
  442. break;
  443. case '\0':
  444. case '#':
  445. break; // Do nothing for comments or blank lines.
  446. case 'p':
  447. WarnLine("point unsupported", line_num);
  448. break;
  449. case 'l':
  450. WarnLine("line unsupported", line_num);
  451. break;
  452. case 'u':
  453. if (0 == strncmp(line + 1, "semtl", 5)) {
  454. ParseUsemtl(line + 6, line_num);
  455. } else {
  456. goto unknown;
  457. }
  458. break;
  459. case 'm':
  460. if (0 == strncmp(line + 1, "tllib", 5)) {
  461. ParseMtllib(line + 6, line_num);
  462. } else {
  463. goto unknown;
  464. }
  465. break;
  466. case 's':
  467. ParseSmoothingGroup(line + 1, line_num);
  468. break;
  469. unknown:
  470. default:
  471. WarnLine("unknown keyword", line_num);
  472. break;
  473. }
  474. }
  475. void ParseAttrib(const char* line, unsigned int line_num) {
  476. ShortFloatList floats;
  477. floats.ParseLine(line + 1);
  478. if (isspace(*line)) {
  479. ParsePosition(floats, line_num);
  480. } else if (*line == 't') {
  481. ParseTexCoord(floats, line_num);
  482. } else if (*line == 'n') {
  483. ParseNormal(floats, line_num);
  484. } else {
  485. WarnLine("unknown attribute format", line_num);
  486. }
  487. }
  488. void ParsePosition(const ShortFloatList& floats, unsigned int line_num) {
  489. if (floats.size() != positionDim() &&
  490. floats.size() != 6) { // ignore r g b for now.
  491. ErrorLine("bad position", line_num);
  492. }
  493. floats.AppendNTo(&positions_, positionDim());
  494. }
  495. void ParseTexCoord(const ShortFloatList& floats, unsigned int line_num) {
  496. if ((floats.size() < 1) || (floats.size() > 3)) {
  497. // TODO: correctly handle 3-D texcoords intead of just
  498. // truncating.
  499. ErrorLine("bad texcoord", line_num);
  500. }
  501. floats.AppendNTo(&texcoords_, texcoordDim());
  502. }
  503. void ParseNormal(const ShortFloatList& floats, unsigned int line_num) {
  504. if (floats.size() != normalDim()) {
  505. ErrorLine("bad normal", line_num);
  506. }
  507. // Normalize to avoid out-of-bounds quantization. This should be
  508. // optional, in case someone wants to be using the normal magnitude as
  509. // something meaningful.
  510. const float x = floats[0];
  511. const float y = floats[1];
  512. const float z = floats[2];
  513. const float scale = 1.0/sqrt(x*x + y*y + z*z);
  514. if (isfinite(scale)) {
  515. normals_.push_back(scale * x);
  516. normals_.push_back(scale * y);
  517. normals_.push_back(scale * z);
  518. } else {
  519. normals_.push_back(0);
  520. normals_.push_back(0);
  521. normals_.push_back(0);
  522. }
  523. }
  524. // Parses faces and converts to triangle fans. This is not a
  525. // particularly good tesselation in general case, but it is really
  526. // simple, and is perfectly fine for triangles and quads.
  527. void ParseFace(const char* line, unsigned int line_num) {
  528. // Also handle face outlines as faces.
  529. if (*line == 'o') ++line;
  530. // TODO: instead of storing these indices as-is, it might make
  531. // sense to flatten them right away. This can reduce memory
  532. // consumption and improve access locality, especially since .OBJ
  533. // face indices are so needlessly large.
  534. int indices[9] = { 0 };
  535. // The first index acts as the pivot for the triangle fan.
  536. line = ParseIndices(line, line_num, indices + 0, indices + 1, indices + 2);
  537. if (line == NULL) {
  538. ErrorLine("bad first index", line_num);
  539. }
  540. line = ParseIndices(line, line_num, indices + 3, indices + 4, indices + 5);
  541. if (line == NULL) {
  542. ErrorLine("bad second index", line_num);
  543. }
  544. // After the first two indices, each index introduces a new
  545. // triangle to the fan.
  546. while ((line = ParseIndices(line, line_num,
  547. indices + 6, indices + 7, indices + 8))) {
  548. current_batch_->AddTriangle(current_group_line_, indices);
  549. // The most recent vertex is reused for the next triangle.
  550. indices[3] = indices[6];
  551. indices[4] = indices[7];
  552. indices[5] = indices[8];
  553. indices[6] = indices[7] = indices[8] = 0;
  554. }
  555. }
  556. // Parse a single group of indices, separated by slashes ('/').
  557. // TODO: convert negative indices (that is, relative to the end of
  558. // the current vertex positions) to more conventional positive
  559. // indices.
  560. const char* ParseIndices(const char* line, unsigned int line_num,
  561. int* position_index, int* texcoord_index,
  562. int* normal_index) {
  563. const char* endptr = NULL;
  564. *position_index = strtoint(line, &endptr);
  565. if (*position_index == 0) {
  566. return NULL;
  567. }
  568. if (endptr != NULL && *endptr == '/') {
  569. *texcoord_index = strtoint(endptr + 1, &endptr);
  570. } else {
  571. *texcoord_index = *normal_index = 0;
  572. }
  573. if (endptr != NULL && *endptr == '/') {
  574. *normal_index = strtoint(endptr + 1, &endptr);
  575. } else {
  576. *normal_index = 0;
  577. }
  578. return endptr;
  579. }
  580. // .OBJ files can specify multiple groups for a set of faces. This
  581. // implementation finds the "most unique" group for a set of faces
  582. // and uses that for the batch. In the first pass, we use the line
  583. // number of the "g" command to tag the faces. Afterwards, after we
  584. // collect group populations, we can go back and give them real
  585. // names.
  586. void ParseGroup(const char* line, unsigned int line_num) {
  587. std::string token;
  588. while ((line = ConsumeFirstToken(line, &token))) {
  589. ToLowerInplace(&token);
  590. group_counts_[token]++;
  591. line_to_groups_.insert(std::make_pair(line_num, token));
  592. }
  593. current_group_line_ = line_num;
  594. }
  595. void ParseSmoothingGroup(const char* line, unsigned int line_num) {
  596. static bool once = true;
  597. if (once) {
  598. WarnLine("s ignored", line_num);
  599. once = false;
  600. }
  601. }
  602. void ParseMtllib(const char* line, unsigned int line_num) {
  603. FILE* fp = fopen(StripLeadingWhitespace(line), "r");
  604. if (!fp) {
  605. WarnLine("mtllib not found", line_num);
  606. return;
  607. }
  608. WavefrontMtlFile mtlfile(fp);
  609. fclose(fp);
  610. materials_ = mtlfile.materials();
  611. for (size_t i = 0; i < materials_.size(); ++i) {
  612. DrawBatch& draw_batch = material_batches_[materials_[i].name];
  613. draw_batch.Init(&positions_, &texcoords_, &normals_);
  614. }
  615. }
  616. void ParseUsemtl(const char* line, unsigned int line_num) {
  617. std::string usemtl;
  618. ToLower(StripLeadingWhitespace(line), &usemtl);
  619. MaterialBatches::iterator iter = material_batches_.find(usemtl);
  620. if (iter == material_batches_.end()) {
  621. ErrorLine("material not found", line_num);
  622. }
  623. current_batch_ = &iter->second;
  624. }
  625. void WarnLine(const char* why, unsigned int line_num) const {
  626. fprintf(stderr, "WARNING: %s at line %u\n", why, line_num);
  627. }
  628. void ErrorLine(const char* why, unsigned int line_num) const {
  629. fprintf(stderr, "ERROR: %s at line %u\n", why, line_num);
  630. exit(-1);
  631. }
  632. AttribList positions_;
  633. AttribList texcoords_;
  634. AttribList normals_;
  635. MaterialList materials_;
  636. // Currently, batch by texture (i.e. map_Kd).
  637. MaterialBatches material_batches_;
  638. DrawBatch* current_batch_;
  639. typedef std::multimap<unsigned int, std::string> LineToGroups;
  640. LineToGroups line_to_groups_;
  641. std::map<std::string, int> group_counts_;
  642. unsigned int current_group_line_;
  643. };
  644. #endif // WEBGL_LOADER_MESH_H_
粤ICP备19079148号