ObjFileParser.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. #include "ObjFileParser.h"
  2. #include "ObjFileMtlImporter.h"
  3. #include "ObjTools.h"
  4. #include "ObjFileData.h"
  5. #include "DefaultIOSystem.h"
  6. #include "../include/IOStream.h"
  7. #include "../include/aiTypes.h"
  8. #include "../include/aiAssert.h"
  9. #include "fast_atof.h"
  10. #include <iostream>
  11. #include <vector>
  12. #include <cassert>
  13. namespace Assimp
  14. {
  15. // -------------------------------------------------------------------
  16. const std::string ObjFileParser::DEFAULT_MATERIAL = "defaultmaterial";
  17. // -------------------------------------------------------------------
  18. ObjFileParser::ObjFileParser(std::vector<char> &Data,
  19. const std::string &strAbsPath,
  20. const std::string &strModelName) :
  21. m_strAbsPath(strAbsPath),
  22. m_DataIt(Data.begin()),
  23. m_DataItEnd(Data.end()),
  24. m_pModel(NULL),
  25. m_uiLine(0)
  26. {
  27. // Create the model instance to store all the data
  28. m_pModel = new ObjFile::Model();
  29. m_pModel->m_ModelName = strModelName;
  30. m_pModel->m_pDefaultMaterial = new ObjFile::Material();
  31. m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL );
  32. m_pModel->m_MaterialLib.push_back( DEFAULT_MATERIAL );
  33. m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial;
  34. // Start parsing the file
  35. parseFile();
  36. }
  37. // -------------------------------------------------------------------
  38. ObjFileParser::~ObjFileParser()
  39. {
  40. // empty
  41. }
  42. // -------------------------------------------------------------------
  43. ObjFile::Model *ObjFileParser::GetModel() const
  44. {
  45. return m_pModel;
  46. }
  47. // -------------------------------------------------------------------
  48. void ObjFileParser::parseFile()
  49. {
  50. if (m_DataIt == m_DataItEnd)
  51. return;
  52. while (m_DataIt != m_DataItEnd)
  53. {
  54. switch (*m_DataIt)
  55. {
  56. case 'v': // Parse a vertex texture coordinate
  57. {
  58. ++m_DataIt;
  59. if (*m_DataIt == ' ')
  60. {
  61. // Read in vertex definition
  62. getVector3(m_pModel->m_Vertices);
  63. }
  64. else if (*m_DataIt == 't')
  65. {
  66. // Read in texture coordinate (2D)
  67. ++m_DataIt;
  68. getVector2(m_pModel->m_TextureCoord);
  69. }
  70. else if (*m_DataIt == 'n')
  71. {
  72. // Read in normal vector definition
  73. ++m_DataIt;
  74. getVector3(m_pModel->m_Normals);
  75. }
  76. }
  77. break;
  78. case 'f': // Parse a face
  79. {
  80. getFace();
  81. }
  82. break;
  83. case '#': // Parse a comment
  84. {
  85. getComment();
  86. }
  87. break;
  88. case 'u': // Parse a material desc. setter
  89. {
  90. getMaterialDesc();
  91. }
  92. break;
  93. case 'm': // Parse a material library
  94. {
  95. getMaterialLib();
  96. }
  97. break;
  98. case 'g': // Parse group name
  99. {
  100. getGroupName();
  101. }
  102. break;
  103. case 's': // Parse group number
  104. {
  105. getGroupNumber();
  106. }
  107. break;
  108. case 'o': // Parse object name
  109. {
  110. getObjectName();
  111. }
  112. break;
  113. default:
  114. {
  115. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  116. }
  117. break;
  118. }
  119. }
  120. }
  121. // -------------------------------------------------------------------
  122. // Copy the next word in a temporary buffer
  123. void ObjFileParser::copyNextWord(char *pBuffer, size_t length)
  124. {
  125. size_t index = 0;
  126. m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
  127. while (!isSpace(*m_DataIt) && m_DataIt != m_DataItEnd)
  128. {
  129. pBuffer[index] = *m_DataIt;
  130. index++;
  131. if (index == length-1)
  132. break;
  133. ++m_DataIt;
  134. }
  135. pBuffer[index] = '\0';
  136. }
  137. // -------------------------------------------------------------------
  138. // Copy the next line into a temporary buffer
  139. void ObjFileParser::copyNextLine(char *pBuffer, size_t length)
  140. {
  141. size_t index = 0;
  142. while (m_DataIt != m_DataItEnd)
  143. {
  144. if (*m_DataIt == '\n' || *m_DataIt == '\r')
  145. break;
  146. assert (index+1 <= length);
  147. pBuffer[ index ] = *m_DataIt;
  148. ++index;
  149. ++m_DataIt;
  150. }
  151. pBuffer[ index ] = '\0';
  152. }
  153. // -------------------------------------------------------------------
  154. // Get values for a new 3D vector instance
  155. void ObjFileParser::getVector3(std::vector<aiVector3D*> &point3d_array)
  156. {
  157. float x, y, z;
  158. copyNextWord(m_buffer, BUFFERSIZE);
  159. x = (float) fast_atof(m_buffer);
  160. copyNextWord(m_buffer, BUFFERSIZE);
  161. y = (float) fast_atof(m_buffer);
  162. copyNextWord(m_buffer, BUFFERSIZE);
  163. z = (float) fast_atof(m_buffer);
  164. point3d_array.push_back(new aiVector3D(x,y,z));
  165. //skipLine();
  166. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  167. }
  168. // -------------------------------------------------------------------
  169. // Get values for a new 2D vector instance
  170. void ObjFileParser::getVector2( std::vector<aiVector2D*> &point2d_array )
  171. {
  172. float x, y;
  173. copyNextWord(m_buffer, BUFFERSIZE);
  174. x = (float) fast_atof(m_buffer);
  175. copyNextWord(m_buffer, BUFFERSIZE);
  176. y = (float) fast_atof(m_buffer);
  177. point2d_array.push_back(new aiVector2D(x, y));
  178. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  179. }
  180. // -------------------------------------------------------------------
  181. // Get values for a new face instance
  182. void ObjFileParser::getFace()
  183. {
  184. copyNextLine(m_buffer, BUFFERSIZE);
  185. if (m_DataIt == m_DataItEnd)
  186. return;
  187. char *pPtr = m_buffer;
  188. char *pEnd = &pPtr[BUFFERSIZE];
  189. pPtr = getNextToken<char*>(pPtr, pEnd);
  190. if (pPtr == '\0')
  191. return;
  192. std::vector<unsigned int> *pIndices = new std::vector<unsigned int>;
  193. std::vector<unsigned int> *pTexID = new std::vector<unsigned int>;
  194. std::vector<unsigned int> *pNormalID = new std::vector<unsigned int>;
  195. bool vt = (!m_pModel->m_TextureCoord.empty());
  196. bool vn = (!m_pModel->m_Normals.empty());
  197. int iStep = 0, iPos = 0;
  198. while (pPtr != pEnd)
  199. {
  200. iStep = 1;
  201. if (*pPtr == '\0')
  202. break;
  203. if (*pPtr=='\r')
  204. break;
  205. if (*pPtr=='/' )
  206. {
  207. if (iPos == 0)
  208. {
  209. //if there are no texturecoordinates in the obj file but normals
  210. if (!vt && vn)
  211. iPos = 1;
  212. }
  213. iPos++;
  214. }
  215. else if (isSpace(*pPtr))
  216. {
  217. iPos = 0;
  218. }
  219. else
  220. {
  221. //OBJ USES 1 Base ARRAYS!!!!
  222. const int iVal = atoi(pPtr);
  223. int tmp = iVal;
  224. while ((tmp = tmp / 10)!=0)
  225. ++iStep;
  226. if (0 != iVal)
  227. {
  228. // Store parsed index
  229. if (0 == iPos)
  230. {
  231. pIndices->push_back(iVal-1);
  232. }
  233. else if (1 == iPos)
  234. {
  235. pTexID->push_back(iVal-1);
  236. }
  237. else if (2 == iPos)
  238. {
  239. pNormalID->push_back(iVal-1);
  240. }
  241. else
  242. {
  243. reportErrorTokenInFace();
  244. }
  245. }
  246. }
  247. for (int i=0; i<iStep; i++)
  248. ++pPtr;
  249. }
  250. ObjFile::Face *face = new ObjFile::Face(pIndices, pNormalID, pTexID);
  251. // Set active material, if one set
  252. if (NULL != m_pModel->m_pCurrentMaterial)
  253. face->m_pMaterial = m_pModel->m_pCurrentMaterial;
  254. else
  255. face->m_pMaterial = m_pModel->m_pDefaultMaterial;
  256. // Create a default object, if nothing there
  257. if ( NULL == m_pModel->m_pCurrent )
  258. createObject("defaultobject");
  259. // Store the new instance
  260. m_pModel->m_pCurrent->m_Faces.push_back(face);
  261. // Assign face to mesh
  262. if ( NULL == m_pModel->m_pCurrentMesh )
  263. {
  264. m_pModel->m_pCurrentMesh = new ObjFile::Mesh();
  265. m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh );
  266. }
  267. m_pModel->m_pCurrentMesh->m_Faces.push_back( face );
  268. if ( !face->m_pVertices->empty() )
  269. m_pModel->m_pCurrentMesh->m_uiNumIndices += face->m_pVertices->size();
  270. // Skip the rest of the line
  271. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  272. }
  273. // -------------------------------------------------------------------
  274. // Get values for a new material description
  275. void ObjFileParser::getMaterialDesc()
  276. {
  277. // Get next data for material data
  278. m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
  279. if (m_DataIt == m_DataItEnd)
  280. return;
  281. char *pStart = &(*m_DataIt);
  282. while ( !isSpace(*m_DataIt) && m_DataIt != m_DataItEnd )
  283. ++m_DataIt;
  284. // Get name
  285. std::string strName(pStart, &(*m_DataIt));
  286. if ( strName.empty() )
  287. return;
  288. // Search for material
  289. std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strName );
  290. if ( it == m_pModel->m_MaterialMap.end() )
  291. {
  292. // Not found, use default material
  293. m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
  294. m_pModel->m_pCurrentMesh = new ObjFile::Mesh();
  295. m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh );
  296. m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( DEFAULT_MATERIAL );
  297. }
  298. else
  299. {
  300. // Found, using detected material
  301. m_pModel->m_pCurrentMaterial = (*it).second;
  302. // Create a new mesh for a new material
  303. m_pModel->m_pCurrentMesh = new ObjFile::Mesh();
  304. m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh );
  305. m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strName );
  306. }
  307. // Skip rest of line
  308. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  309. }
  310. // -------------------------------------------------------------------
  311. // Get a comment, values will be skipped
  312. void ObjFileParser::getComment()
  313. {
  314. while (true)
  315. {
  316. if ('\n' == (*m_DataIt) || m_DataIt == m_DataItEnd)
  317. {
  318. ++m_DataIt;
  319. break;
  320. }
  321. else
  322. {
  323. ++m_DataIt;
  324. }
  325. }
  326. }
  327. // -------------------------------------------------------------------
  328. // Get material library from file.
  329. void ObjFileParser::getMaterialLib()
  330. {
  331. // Translate tuple
  332. m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
  333. if (m_DataIt == m_DataItEnd)
  334. return;
  335. char *pStart = &(*m_DataIt);
  336. while (!isSpace(*m_DataIt))
  337. m_DataIt++;
  338. // Check for existence
  339. DefaultIOSystem IOSystem;
  340. std::string strMatName(pStart, &(*m_DataIt));
  341. std::string absName = m_strAbsPath + IOSystem.getOsSeparator() + strMatName;
  342. if ( !IOSystem.Exists(absName) )
  343. {
  344. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  345. return;
  346. }
  347. // Extract the extention
  348. std::string strExt("");
  349. extractExtension( strMatName, strExt );
  350. static const std::string mat = "mtl";
  351. // Load the material library
  352. DefaultIOSystem FileSystem;
  353. IOStream *pFile = FileSystem.Open(absName);
  354. if (0L != pFile)
  355. {
  356. // Import material library data from file
  357. size_t size = pFile->FileSize();
  358. std::vector<char> buffer;
  359. buffer.resize( size );
  360. size_t read_size = pFile->Read( &buffer[ 0 ], sizeof( char ), size );
  361. FileSystem.Close( pFile );
  362. // Importing the material library
  363. ObjFileMtlImporter mtlImporter( buffer, absName, m_pModel );
  364. }
  365. // Skip rest of line
  366. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  367. }
  368. // -------------------------------------------------------------------
  369. // Set a new material definition as the current material.
  370. void ObjFileParser::getNewMaterial()
  371. {
  372. m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
  373. m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
  374. char *pStart = &(*m_DataIt);
  375. std::string strMat(pStart, *m_DataIt);
  376. while (isSpace(*m_DataIt))
  377. m_DataIt++;
  378. std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strMat );
  379. if (it == m_pModel->m_MaterialMap.end())
  380. {
  381. // Show a warning, if material was not found
  382. std::string strWarn ("Unsupported material requested: ");
  383. strWarn += strMat;
  384. std::cerr << "Warning : " << strWarn << std::endl;
  385. m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
  386. }
  387. else
  388. {
  389. // Set new material
  390. m_pModel->m_pCurrentMaterial = (*it).second;
  391. m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strMat );
  392. }
  393. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  394. }
  395. // -------------------------------------------------------------------
  396. int ObjFileParser::getMaterialIndex( const std::string &strMaterialName )
  397. {
  398. int mat_index = -1;
  399. if ( strMaterialName.empty() )
  400. return mat_index;
  401. for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index)
  402. {
  403. if ( strMaterialName == m_pModel->m_MaterialLib[ index ])
  404. {
  405. mat_index = index;
  406. break;
  407. }
  408. }
  409. return mat_index;
  410. }
  411. // -------------------------------------------------------------------
  412. // Getter for a group name.
  413. void ObjFileParser::getGroupName()
  414. {
  415. // Get next word from data buffer
  416. m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
  417. m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
  418. // Store groupname in group library
  419. char *pStart = &(*m_DataIt);
  420. while (!isSpace(*m_DataIt))
  421. m_DataIt++;
  422. std::string strGroupName(pStart, &(*m_DataIt));
  423. // Change active group, if necessary
  424. if (m_pModel->m_strActiveGroup != strGroupName)
  425. {
  426. // Search for already existing entry
  427. ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(&strGroupName);
  428. // New group name, creating a new entry
  429. ObjFile::Object *pObject = m_pModel->m_pCurrent;
  430. if (it == m_pModel->m_Groups.end())
  431. {
  432. std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
  433. m_pModel->m_Groups[ &strGroupName ] = pFaceIDArray;
  434. m_pModel->m_pGroupFaceIDs = (pFaceIDArray);
  435. }
  436. else
  437. {
  438. m_pModel->m_pGroupFaceIDs = (*it).second;
  439. }
  440. m_pModel->m_strActiveGroup = strGroupName;
  441. }
  442. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  443. }
  444. // -------------------------------------------------------------------
  445. // Not supported
  446. void ObjFileParser::getGroupNumber()
  447. {
  448. // Not used
  449. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  450. }
  451. // -------------------------------------------------------------------
  452. // Stores values for a new object instance, name will be used to
  453. // identify it.
  454. void ObjFileParser::getObjectName()
  455. {
  456. m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
  457. if (m_DataIt == m_DataItEnd)
  458. return;
  459. char *pStart = &(*m_DataIt);
  460. while (!isSpace(*m_DataIt))
  461. m_DataIt++;
  462. std::string strObjectName(pStart, &(*m_DataIt));
  463. if (!strObjectName.empty())
  464. {
  465. // Reset current object
  466. m_pModel->m_pCurrent = NULL;
  467. // Search for actual object
  468. for (std::vector<ObjFile::Object*>::const_iterator it = m_pModel->m_Objects.begin();
  469. it != m_pModel->m_Objects.end();
  470. ++it)
  471. {
  472. if ((*it)->m_strObjName == strObjectName)
  473. {
  474. m_pModel->m_pCurrent = *it;
  475. break;
  476. }
  477. }
  478. // Allocate a new object, if current one wasn´t found before
  479. if (m_pModel->m_pCurrent == NULL)
  480. {
  481. createObject(strObjectName);
  482. /*m_pModel->m_pCurrent = new ObjFile::Object();
  483. m_pModel->m_pCurrent->m_strObjName = strObjectName;
  484. m_pModel->m_Objects.push_back(m_pModel->m_pCurrent);*/
  485. }
  486. }
  487. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  488. }
  489. // -------------------------------------------------------------------
  490. // Creates a new object instance
  491. void ObjFileParser::createObject(const std::string &strObjectName)
  492. {
  493. ai_assert (NULL != m_pModel);
  494. ai_assert (!strObjectName.empty());
  495. m_pModel->m_pCurrent = new ObjFile::Object();
  496. m_pModel->m_pCurrent->m_strObjName = strObjectName;
  497. m_pModel->m_Objects.push_back(m_pModel->m_pCurrent);
  498. }
  499. // -------------------------------------------------------------------
  500. // Shows an error in parsing process.
  501. void ObjFileParser::reportErrorTokenInFace()
  502. {
  503. std::string strErr("");
  504. m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
  505. std::cerr << "Not supported token in face desc. detected : " << strErr << std::endl;
  506. }
  507. // -------------------------------------------------------------------
  508. // Extracts the extention from a filename
  509. void ObjFileParser::extractExtension(const std::string strFile,
  510. std::string &strExt)
  511. {
  512. strExt = "";
  513. if (strFile.empty())
  514. return;
  515. std::string::size_type pos = strFile.find_last_of(".");
  516. if (pos == std::string::npos)
  517. return;
  518. strExt = strFile.substr(pos, strFile.size() - pos);
  519. }
  520. // -------------------------------------------------------------------
  521. } // Namespace Assimp