mesh.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  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. void DumpJson(FILE* out = stdout) const {
  284. fprintf(out, " \"%s\": { ", name.c_str());
  285. if (map_Kd.empty()) {
  286. fprintf(out, "\"Kd\": [%hu, %hu, %hu] }",
  287. Quantize(Kd[0], 0, 1, 255),
  288. Quantize(Kd[1], 0, 1, 255),
  289. Quantize(Kd[2], 0, 1, 255));
  290. } else {
  291. fprintf(out, "\"map_Kd\": \"%s\" }", map_Kd.c_str());
  292. }
  293. }
  294. };
  295. typedef std::vector<Material> MaterialList;
  296. class WavefrontMtlFile {
  297. public:
  298. explicit WavefrontMtlFile(FILE* fp) {
  299. ParseFile(fp);
  300. }
  301. const MaterialList& materials() const {
  302. return materials_;
  303. }
  304. private:
  305. // TODO: factor this parsing stuff out.
  306. void ParseFile(FILE* fp) {
  307. // TODO: don't use a fixed-size buffer.
  308. const size_t kLineBufferSize = 256;
  309. char buffer[kLineBufferSize];
  310. unsigned int line_num = 1;
  311. while (fgets(buffer, kLineBufferSize, fp) != NULL) {
  312. char* stripped = StripLeadingWhitespace(buffer);
  313. TerminateAtNewlineOrComment(stripped);
  314. ParseLine(stripped, line_num++);
  315. }
  316. }
  317. void ParseLine(const char* line, unsigned int line_num) {
  318. switch (*line) {
  319. case 'K':
  320. ParseColor(line + 1, line_num);
  321. break;
  322. case 'm':
  323. if (0 == strncmp(line + 1, "ap_Kd", 5)) {
  324. ParseMapKd(line + 6, line_num);
  325. }
  326. break;
  327. case 'n':
  328. if (0 == strncmp(line + 1, "ewmtl", 5)) {
  329. ParseNewmtl(line + 6, line_num);
  330. }
  331. default:
  332. break;
  333. }
  334. }
  335. void ParseColor(const char* line, unsigned int line_num) {
  336. switch (*line) {
  337. case 'd': {
  338. ShortFloatList floats;
  339. floats.ParseLine(line + 1);
  340. float* Kd = current_->Kd;
  341. Kd[0] = floats[0];
  342. Kd[1] = floats[1];
  343. Kd[2] = floats[2];
  344. break;
  345. }
  346. default:
  347. break;
  348. }
  349. }
  350. void ParseMapKd(const char* line, unsigned int line_num) {
  351. current_->map_Kd = StripLeadingWhitespace(line);
  352. }
  353. void ParseNewmtl(const char* line, unsigned int line_num) {
  354. materials_.push_back(Material());
  355. current_ = &materials_.back();
  356. ToLower(StripLeadingWhitespace(line), &current_->name);
  357. }
  358. Material* current_;
  359. MaterialList materials_;
  360. };
  361. typedef std::map<std::string, DrawBatch> MaterialBatches;
  362. // TODO: consider splitting this into a low-level parser and a high-level
  363. // object.
  364. class WavefrontObjFile {
  365. public:
  366. explicit WavefrontObjFile(FILE* fp) {
  367. current_batch_ = &material_batches_[""];
  368. current_batch_->Init(&positions_, &texcoords_, &normals_);
  369. current_group_line_ = 0;
  370. line_to_groups_.insert(std::make_pair(0, "default"));
  371. ParseFile(fp);
  372. }
  373. const MaterialList& materials() const {
  374. return materials_;
  375. }
  376. const MaterialBatches& material_batches() const {
  377. return material_batches_;
  378. }
  379. const std::string& LineToGroup(unsigned int line) const {
  380. typedef LineToGroups::const_iterator Iterator;
  381. typedef std::pair<Iterator, Iterator> EqualRange;
  382. EqualRange equal_range = line_to_groups_.equal_range(line);
  383. const std::string* best_group = NULL;
  384. int best_count = 0;
  385. for (Iterator iter = equal_range.first; iter != equal_range.second;
  386. ++iter) {
  387. const std::string& group = iter->second;
  388. const int count = group_counts_.find(group)->second;
  389. if (!best_group || (count < best_count)) {
  390. best_group = &group;
  391. best_count = count;
  392. }
  393. }
  394. if (!best_group) {
  395. ErrorLine("no suitable group found", line);
  396. }
  397. return *best_group;
  398. }
  399. void DumpDebug() const {
  400. printf("positions size: " PRIuS "\n"
  401. "texcoords size: " PRIuS "\n"
  402. "normals size: " PRIuS "\n",
  403. positions_.size(), texcoords_.size(), normals_.size());
  404. }
  405. private:
  406. WavefrontObjFile() { } // For testing.
  407. void ParseFile(FILE* fp) {
  408. // TODO: don't use a fixed-size buffer.
  409. const size_t kLineBufferSize = 256;
  410. char buffer[kLineBufferSize] = { 0 };
  411. unsigned int line_num = 1;
  412. while (fgets(buffer, kLineBufferSize, fp) != NULL) {
  413. char* stripped = StripLeadingWhitespace(buffer);
  414. TerminateAtNewlineOrComment(stripped);
  415. ParseLine(stripped, line_num++);
  416. }
  417. }
  418. void ParseLine(const char* line, unsigned int line_num) {
  419. switch (*line) {
  420. case 'v':
  421. ParseAttrib(line + 1, line_num);
  422. break;
  423. case 'f':
  424. ParseFace(line + 1, line_num);
  425. break;
  426. case 'g':
  427. if (isspace(line[1])) {
  428. ParseGroup(line + 2, line_num);
  429. } else {
  430. goto unknown;
  431. }
  432. break;
  433. case '\0':
  434. case '#':
  435. break; // Do nothing for comments or blank lines.
  436. case 'p':
  437. WarnLine("point unsupported", line_num);
  438. break;
  439. case 'l':
  440. WarnLine("line unsupported", line_num);
  441. break;
  442. case 'u':
  443. if (0 == strncmp(line + 1, "semtl", 5)) {
  444. ParseUsemtl(line + 6, line_num);
  445. } else {
  446. goto unknown;
  447. }
  448. break;
  449. case 'm':
  450. if (0 == strncmp(line + 1, "tllib", 5)) {
  451. ParseMtllib(line + 6, line_num);
  452. } else {
  453. goto unknown;
  454. }
  455. break;
  456. case 's':
  457. ParseSmoothingGroup(line + 1, line_num);
  458. break;
  459. unknown:
  460. default:
  461. WarnLine("unknown keyword", line_num);
  462. break;
  463. }
  464. }
  465. void ParseAttrib(const char* line, unsigned int line_num) {
  466. ShortFloatList floats;
  467. floats.ParseLine(line + 1);
  468. if (isspace(*line)) {
  469. ParsePosition(floats, line_num);
  470. } else if (*line == 't') {
  471. ParseTexCoord(floats, line_num);
  472. } else if (*line == 'n') {
  473. ParseNormal(floats, line_num);
  474. } else {
  475. WarnLine("unknown attribute format", line_num);
  476. }
  477. }
  478. void ParsePosition(const ShortFloatList& floats, unsigned int line_num) {
  479. if (floats.size() != positionDim() &&
  480. floats.size() != 6) { // ignore r g b for now.
  481. ErrorLine("bad position", line_num);
  482. }
  483. floats.AppendNTo(&positions_, positionDim());
  484. }
  485. void ParseTexCoord(const ShortFloatList& floats, unsigned int line_num) {
  486. if ((floats.size() < 1) || (floats.size() > 3)) {
  487. // TODO: correctly handle 3-D texcoords intead of just
  488. // truncating.
  489. ErrorLine("bad texcoord", line_num);
  490. }
  491. floats.AppendNTo(&texcoords_, texcoordDim());
  492. }
  493. void ParseNormal(const ShortFloatList& floats, unsigned int line_num) {
  494. if (floats.size() != normalDim()) {
  495. ErrorLine("bad normal", line_num);
  496. }
  497. // Normalize to avoid out-of-bounds quantization. This should be
  498. // optional, in case someone wants to be using the normal magnitude as
  499. // something meaningful.
  500. const float x = floats[0];
  501. const float y = floats[1];
  502. const float z = floats[2];
  503. const float scale = 1.0/sqrt(x*x + y*y + z*z);
  504. if (isfinite(scale)) {
  505. normals_.push_back(scale * x);
  506. normals_.push_back(scale * y);
  507. normals_.push_back(scale * z);
  508. } else {
  509. normals_.push_back(0);
  510. normals_.push_back(0);
  511. normals_.push_back(0);
  512. }
  513. }
  514. // Parses faces and converts to triangle fans. This is not a
  515. // particularly good tesselation in general case, but it is really
  516. // simple, and is perfectly fine for triangles and quads.
  517. void ParseFace(const char* line, unsigned int line_num) {
  518. // Also handle face outlines as faces.
  519. if (*line == 'o') ++line;
  520. // TODO: instead of storing these indices as-is, it might make
  521. // sense to flatten them right away. This can reduce memory
  522. // consumption and improve access locality, especially since .OBJ
  523. // face indices are so needlessly large.
  524. int indices[9] = { 0 };
  525. // The first index acts as the pivot for the triangle fan.
  526. line = ParseIndices(line, line_num, indices + 0, indices + 1, indices + 2);
  527. if (line == NULL) {
  528. ErrorLine("bad first index", line_num);
  529. }
  530. line = ParseIndices(line, line_num, indices + 3, indices + 4, indices + 5);
  531. if (line == NULL) {
  532. ErrorLine("bad second index", line_num);
  533. }
  534. // After the first two indices, each index introduces a new
  535. // triangle to the fan.
  536. while ((line = ParseIndices(line, line_num,
  537. indices + 6, indices + 7, indices + 8))) {
  538. current_batch_->AddTriangle(current_group_line_, indices);
  539. // The most recent vertex is reused for the next triangle.
  540. indices[3] = indices[6];
  541. indices[4] = indices[7];
  542. indices[5] = indices[8];
  543. indices[6] = indices[7] = indices[8] = 0;
  544. }
  545. }
  546. // Parse a single group of indices, separated by slashes ('/').
  547. // TODO: convert negative indices (that is, relative to the end of
  548. // the current vertex positions) to more conventional positive
  549. // indices.
  550. const char* ParseIndices(const char* line, unsigned int line_num,
  551. int* position_index, int* texcoord_index,
  552. int* normal_index) {
  553. const char* endptr = NULL;
  554. *position_index = strtoint(line, &endptr);
  555. if (*position_index == 0) {
  556. return NULL;
  557. }
  558. if (endptr != NULL && *endptr == '/') {
  559. *texcoord_index = strtoint(endptr + 1, &endptr);
  560. } else {
  561. *texcoord_index = *normal_index = 0;
  562. }
  563. if (endptr != NULL && *endptr == '/') {
  564. *normal_index = strtoint(endptr + 1, &endptr);
  565. } else {
  566. *normal_index = 0;
  567. }
  568. return endptr;
  569. }
  570. // .OBJ files can specify multiple groups for a set of faces. This
  571. // implementation finds the "most unique" group for a set of faces
  572. // and uses that for the batch. In the first pass, we use the line
  573. // number of the "g" command to tag the faces. Afterwards, after we
  574. // collect group populations, we can go back and give them real
  575. // names.
  576. void ParseGroup(const char* line, unsigned int line_num) {
  577. std::string token;
  578. while ((line = ConsumeFirstToken(line, &token))) {
  579. ToLowerInplace(&token);
  580. group_counts_[token]++;
  581. line_to_groups_.insert(std::make_pair(line_num, token));
  582. }
  583. current_group_line_ = line_num;
  584. }
  585. void ParseSmoothingGroup(const char* line, unsigned int line_num) {
  586. static bool once = true;
  587. if (once) {
  588. WarnLine("s ignored", line_num);
  589. once = false;
  590. }
  591. }
  592. void ParseMtllib(const char* line, unsigned int line_num) {
  593. FILE* fp = fopen(StripLeadingWhitespace(line), "r");
  594. if (!fp) {
  595. WarnLine("mtllib not found", line_num);
  596. return;
  597. }
  598. WavefrontMtlFile mtlfile(fp);
  599. fclose(fp);
  600. materials_ = mtlfile.materials();
  601. for (size_t i = 0; i < materials_.size(); ++i) {
  602. DrawBatch& draw_batch = material_batches_[materials_[i].name];
  603. draw_batch.Init(&positions_, &texcoords_, &normals_);
  604. }
  605. }
  606. void ParseUsemtl(const char* line, unsigned int line_num) {
  607. std::string usemtl;
  608. ToLower(StripLeadingWhitespace(line), &usemtl);
  609. MaterialBatches::iterator iter = material_batches_.find(usemtl);
  610. if (iter == material_batches_.end()) {
  611. ErrorLine("material not found", line_num);
  612. }
  613. current_batch_ = &iter->second;
  614. }
  615. void WarnLine(const char* why, unsigned int line_num) const {
  616. fprintf(stderr, "WARNING: %s at line %u\n", why, line_num);
  617. }
  618. void ErrorLine(const char* why, unsigned int line_num) const {
  619. fprintf(stderr, "ERROR: %s at line %u\n", why, line_num);
  620. exit(-1);
  621. }
  622. AttribList positions_;
  623. AttribList texcoords_;
  624. AttribList normals_;
  625. MaterialList materials_;
  626. // Currently, batch by texture (i.e. map_Kd).
  627. MaterialBatches material_batches_;
  628. DrawBatch* current_batch_;
  629. typedef std::multimap<unsigned int, std::string> LineToGroups;
  630. LineToGroups line_to_groups_;
  631. std::map<std::string, int> group_counts_;
  632. unsigned int current_group_line_;
  633. };
  634. #endif // WEBGL_LOADER_MESH_H_
粤ICP备19079148号