obj2sdf.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. /// obj2sdf will load a Wavefront .obj file that may contain many parts/materials
  2. /// it will split into separate obj files for each part/material and
  3. /// create an sdf file with visuals/collisions pointing to the new obj files
  4. /// this will make it easier to load complex obj files into pybullet
  5. /// see for example export in data/kitchens/fathirmutfak.sdf
  6. ///Bullet Continuous Collision Detection and Physics Library
  7. ///Erwin Coumans (C) 2018
  8. ///http://bulletphysics.org
  9. ///
  10. ///This software is provided 'as-is', without any express or implied warranty.
  11. ///In no event will the authors be held liable for any damages arising from the use of this software.
  12. ///Permission is granted to anyone to use this software for any purpose,
  13. ///including commercial applications, and to alter it and redistribute it freely,
  14. ///subject to the following restrictions:
  15. ///
  16. ///1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
  17. ///2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  18. ///3. This notice may not be removed or altered from any source distribution.
  19. #include <string.h>
  20. #include <stdio.h>
  21. #include <assert.h>
  22. #define ASSERT_EQ(a, b) assert((a) == (b));
  23. #include "Wavefront/tiny_obj_loader.h"
  24. #include <vector>
  25. #include "Bullet3Common/b3FileUtils.h"
  26. #include "../Utils/b3ResourcePath.h"
  27. #include "Bullet3Common/b3CommandLineArgs.h"
  28. #include "Bullet3Common/b3HashMap.h"
  29. #include "../Utils/b3BulletDefaultFileIO.h"
  30. using bt_tinyobj::index_t;
  31. struct ShapeContainer
  32. {
  33. std::string m_matName;
  34. std::string m_shapeName;
  35. bt_tinyobj::material_t material;
  36. std::vector<float> positions;
  37. std::vector<float> normals;
  38. std::vector<float> texcoords;
  39. std::vector<index_t> indices;
  40. b3AlignedObjectArray<int> m_shapeIndices;
  41. };
  42. b3HashMap<b3HashString, ShapeContainer> gMaterialNames;
  43. #define MAX_PATH_LEN 1024
  44. std::string StripExtension(const std::string& sPath)
  45. {
  46. for (std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++)
  47. {
  48. if (*i == '.')
  49. {
  50. return std::string(sPath.begin(), i.base() - 1);
  51. }
  52. // if we find a slash there is no extension
  53. if (*i == '\\' || *i == '/')
  54. break;
  55. }
  56. // we didn't find an extension
  57. return sPath;
  58. }
  59. int main(int argc, char* argv[])
  60. {
  61. b3CommandLineArgs args(argc, argv);
  62. char* fileName;
  63. args.GetCmdLineArgument("fileName", fileName);
  64. if (fileName == 0)
  65. {
  66. printf("required --fileName=\"name\"");
  67. exit(0);
  68. }
  69. std::string matLibName = StripExtension(fileName);
  70. printf("fileName = %s\n", fileName);
  71. if (fileName == 0)
  72. {
  73. printf("Please use --fileName=\"pathToObj\".");
  74. exit(0);
  75. }
  76. bool mergeMaterials = args.CheckCmdLineFlag("mergeMaterials");
  77. char fileNameWithPath[MAX_PATH_LEN];
  78. bool fileFound = (b3ResourcePath::findResourcePath(fileName, fileNameWithPath, MAX_PATH_LEN,0)) > 0;
  79. char materialPrefixPath[MAX_PATH_LEN];
  80. b3FileUtils::extractPath(fileNameWithPath, materialPrefixPath, MAX_PATH_LEN);
  81. std::vector<bt_tinyobj::shape_t> shapes;
  82. bt_tinyobj::attrib_t attribute;
  83. b3BulletDefaultFileIO fileIO;
  84. std::string err = bt_tinyobj::LoadObj(attribute, shapes, fileNameWithPath, materialPrefixPath, &fileIO);
  85. char sdfFileName[MAX_PATH_LEN];
  86. sprintf(sdfFileName, "%s%s.sdf", materialPrefixPath, "newsdf");
  87. FILE* sdfFile = fopen(sdfFileName, "w");
  88. if (sdfFile == 0)
  89. {
  90. printf("Fatal error: cannot create sdf file %s\n", sdfFileName);
  91. exit(0);
  92. }
  93. fprintf(sdfFile, "<sdf version='1.6'>\n\t<world name='default'>\n\t<gravity>0 0 -9.8</gravity>\n");
  94. for (int s = 0; s < (int)shapes.size(); s++)
  95. {
  96. bt_tinyobj::shape_t& shape = shapes[s];
  97. bt_tinyobj::material_t mat = shape.material;
  98. b3HashString key = mat.name.length() ? mat.name.c_str() : "";
  99. if (!gMaterialNames.find(key))
  100. {
  101. ShapeContainer container;
  102. container.m_matName = mat.name;
  103. container.m_shapeName = shape.name;
  104. container.material = mat;
  105. gMaterialNames.insert(key, container);
  106. }
  107. ShapeContainer* shapeC = gMaterialNames.find(key);
  108. if (shapeC)
  109. {
  110. shapeC->m_shapeIndices.push_back(s);
  111. int curPositions = shapeC->positions.size() / 3;
  112. int curNormals = shapeC->normals.size() / 3;
  113. int curTexcoords = shapeC->texcoords.size() / 2;
  114. int faceCount = shape.mesh.indices.size();
  115. int vertexCount = attribute.vertices.size();
  116. for (int v = 0; v < vertexCount; v++)
  117. {
  118. shapeC->positions.push_back(attribute.vertices[v]);
  119. }
  120. int numNormals = int(attribute.normals.size());
  121. for (int vn = 0; vn < numNormals; vn++)
  122. {
  123. shapeC->normals.push_back(attribute.normals[vn]);
  124. }
  125. int numTexCoords = int(attribute.texcoords.size());
  126. for (int vt = 0; vt < numTexCoords; vt++)
  127. {
  128. shapeC->texcoords.push_back(attribute.texcoords[vt]);
  129. }
  130. for (int face = 0; face < faceCount; face += 3)
  131. {
  132. if (face < 0 && face >= int(shape.mesh.indices.size()))
  133. {
  134. continue;
  135. }
  136. index_t index;
  137. for (int ii = 0; ii < 3; ii++)
  138. {
  139. index.vertex_index = shape.mesh.indices[face + ii].vertex_index + curPositions;
  140. index.normal_index = shape.mesh.indices[face + ii].normal_index + curNormals;
  141. index.texcoord_index = shape.mesh.indices[face + ii].texcoord_index + curTexcoords;
  142. shapeC->indices.push_back(index);
  143. }
  144. }
  145. }
  146. }
  147. printf("unique materials=%d\n", gMaterialNames.size());
  148. if (mergeMaterials)
  149. {
  150. for (int m = 0; m < gMaterialNames.size(); m++)
  151. {
  152. if (gMaterialNames.getAtIndex(m)->m_shapeIndices.size() == 0)
  153. continue;
  154. ShapeContainer* shapeCon = gMaterialNames.getAtIndex(m);
  155. printf("object name = %s\n", shapeCon->m_shapeName.c_str());
  156. char objSdfPartFileName[MAX_PATH_LEN];
  157. sprintf(objSdfPartFileName, "part%d.obj", m);
  158. char objFileName[MAX_PATH_LEN];
  159. if (strlen(materialPrefixPath) > 0)
  160. {
  161. sprintf(objFileName, "%s/part%d.obj", materialPrefixPath, m);
  162. }
  163. else
  164. {
  165. sprintf(objFileName, "part%d.obj", m);
  166. }
  167. FILE* f = fopen(objFileName, "w");
  168. if (f == 0)
  169. {
  170. printf("Fatal error: cannot create part obj file %s\n", objFileName);
  171. exit(0);
  172. }
  173. fprintf(f, "# Exported using automatic converter by Erwin Coumans\n");
  174. if (matLibName.length())
  175. {
  176. fprintf(f, "mtllib %s.mtl\n", matLibName.c_str());
  177. }
  178. else
  179. {
  180. fprintf(f, "mtllib bedroom.mtl\n");
  181. }
  182. int faceCount = shapeCon->indices.size();
  183. int vertexCount = shapeCon->positions.size();
  184. bt_tinyobj::material_t mat = shapeCon->material;
  185. if (shapeCon->m_matName.length())
  186. {
  187. const char* objName = shapeCon->m_matName.c_str();
  188. printf("mat.name = %s\n", objName);
  189. fprintf(f, "#object %s\n\n", objName);
  190. }
  191. for (int v = 0; v < vertexCount / 3; v++)
  192. {
  193. fprintf(f, "v %f %f %f\n", shapeCon->positions[v * 3 + 0], shapeCon->positions[v * 3 + 1], shapeCon->positions[v * 3 + 2]);
  194. }
  195. if (mat.name.length())
  196. {
  197. fprintf(f, "usemtl %s\n", mat.name.c_str());
  198. }
  199. else
  200. {
  201. fprintf(f, "usemtl wire_028089177\n");
  202. }
  203. fprintf(f, "\n");
  204. int numNormals = int(shapeCon->normals.size());
  205. for (int vn = 0; vn < numNormals / 3; vn++)
  206. {
  207. fprintf(f, "vn %f %f %f\n", shapeCon->normals[vn * 3 + 0], shapeCon->normals[vn * 3 + 1], shapeCon->normals[vn * 3 + 2]);
  208. }
  209. fprintf(f, "\n");
  210. int numTexCoords = int(shapeCon->texcoords.size());
  211. for (int vt = 0; vt < numTexCoords / 2; vt++)
  212. {
  213. fprintf(f, "vt %f %f\n", shapeCon->texcoords[vt * 2 + 0], shapeCon->texcoords[vt * 2 + 1]);
  214. }
  215. fprintf(f, "s off\n");
  216. for (int face = 0; face < faceCount; face += 3)
  217. {
  218. if (face < 0 && face >= int(shapeCon->indices.size()))
  219. {
  220. continue;
  221. }
  222. fprintf(f, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
  223. shapeCon->indices[face].vertex_index + 1, shapeCon->indices[face].texcoord_index + 1, shapeCon->indices[face].normal_index + 1,
  224. shapeCon->indices[face + 1].vertex_index + 1, shapeCon->indices[face + 1].texcoord_index + 1, shapeCon->indices[face + 1].normal_index + 1,
  225. shapeCon->indices[face + 2].vertex_index + 1, shapeCon->indices[face + 2].texcoord_index + 1, shapeCon->indices[face + 2].normal_index + 1);
  226. }
  227. fclose(f);
  228. float kdRed = mat.diffuse[0];
  229. float kdGreen = mat.diffuse[1];
  230. float kdBlue = mat.diffuse[2];
  231. float transparency = mat.transparency;
  232. fprintf(sdfFile,
  233. "\t\t<model name='%s'>\n"
  234. "\t\t\t<static>1</static>\n"
  235. "\t\t\t<pose >0 0 0 0 0 0</pose>\n"
  236. "\t\t\t<link name='link_d%d'>\n"
  237. "\t\t\t<inertial>\n"
  238. "\t\t\t<mass>0</mass>\n"
  239. "\t\t\t<inertia>\n"
  240. "\t\t\t<ixx>0.166667</ixx>\n"
  241. "\t\t\t<ixy>0</ixy>\n"
  242. "\t\t\t<ixz>0</ixz>\n"
  243. "\t\t\t<iyy>0.166667</iyy>\n"
  244. "\t\t\t<iyz>0</iyz>\n"
  245. "\t\t\t<izz>0.166667</izz>\n"
  246. "\t\t\t</inertia>\n"
  247. "\t\t\t</inertial>\n"
  248. "\t\t\t<collision concave='yes' name='collision_%d'>\n"
  249. "\t\t\t<geometry>\n"
  250. "\t\t\t<mesh>\n"
  251. "\t\t\t<scale>1 1 1</scale>\n"
  252. "\t\t\t\t<uri>%s</uri>\n"
  253. "\t\t\t</mesh>\n"
  254. "\t\t\t</geometry>\n"
  255. "\t\t\t </collision>\n"
  256. "\t\t\t<visual name='visual'>\n"
  257. "\t\t\t\t<geometry>\n"
  258. "\t\t\t\t<mesh>\n"
  259. "\t\t\t\t\t<scale>1 1 1</scale>\n"
  260. "\t\t\t\t\t<uri>%s</uri>\n"
  261. "\t\t\t\t</mesh>\n"
  262. "\t\t\t\t</geometry>\n"
  263. "\t\t\t<material>\n"
  264. "\t\t\t\t<ambient>1 0 0 1</ambient>\n"
  265. "\t\t\t\t<diffuse>%f %f %f %f</diffuse>\n"
  266. "\t\t\t\t<specular>0.1 0.1 0.1 1</specular>\n"
  267. "\t\t\t\t<emissive>0 0 0 0</emissive>\n"
  268. "\t\t\t </material>\n"
  269. "\t\t\t </visual>\n"
  270. "\t\t\t </link>\n"
  271. "\t\t\t</model>\n",
  272. objSdfPartFileName, m, m,
  273. objSdfPartFileName, objSdfPartFileName,
  274. kdRed, kdGreen, kdBlue, transparency);
  275. }
  276. }
  277. else
  278. {
  279. for (int s = 0; s < (int)shapes.size(); s++)
  280. {
  281. bt_tinyobj::shape_t& shape = shapes[s];
  282. if (shape.name.length())
  283. {
  284. printf("object name = %s\n", shape.name.c_str());
  285. }
  286. char objFileName[MAX_PATH_LEN];
  287. if (strlen(materialPrefixPath) > 0)
  288. {
  289. sprintf(objFileName, "%s/part%d.obj", materialPrefixPath, s);
  290. }
  291. else
  292. {
  293. sprintf(objFileName, "part%d.obj", s);
  294. }
  295. FILE* f = fopen(objFileName, "w");
  296. if (f == 0)
  297. {
  298. printf("Fatal error: cannot create part obj file %s\n", objFileName);
  299. exit(0);
  300. }
  301. fprintf(f, "# Exported using automatic converter by Erwin Coumans\n");
  302. if (matLibName.length())
  303. {
  304. fprintf(f, "mtllib %s.mtl\n", matLibName.c_str());
  305. }
  306. else
  307. {
  308. fprintf(f, "mtllib bedroom.mtl\n");
  309. }
  310. int faceCount = shape.mesh.indices.size();
  311. int vertexCount = attribute.vertices.size();
  312. bt_tinyobj::material_t mat = shape.material;
  313. if (shape.name.length())
  314. {
  315. const char* objName = shape.name.c_str();
  316. printf("mat.name = %s\n", objName);
  317. fprintf(f, "#object %s\n\n", objName);
  318. }
  319. for (int v = 0; v < vertexCount / 3; v++)
  320. {
  321. fprintf(f, "v %f %f %f\n", attribute.vertices[v * 3 + 0], attribute.vertices[v * 3 + 1], attribute.vertices[v * 3 + 2]);
  322. }
  323. if (mat.name.length())
  324. {
  325. fprintf(f, "usemtl %s\n", mat.name.c_str());
  326. }
  327. else
  328. {
  329. fprintf(f, "usemtl wire_028089177\n");
  330. }
  331. fprintf(f, "\n");
  332. int numNormals = int(attribute.normals.size());
  333. for (int vn = 0; vn < numNormals / 3; vn++)
  334. {
  335. fprintf(f, "vn %f %f %f\n", attribute.normals[vn * 3 + 0], attribute.normals[vn * 3 + 1], attribute.normals[vn * 3 + 2]);
  336. }
  337. fprintf(f, "\n");
  338. int numTexCoords = int(attribute.texcoords.size());
  339. for (int vt = 0; vt < numTexCoords / 2; vt++)
  340. {
  341. fprintf(f, "vt %f %f\n", attribute.texcoords[vt * 2 + 0], attribute.texcoords[vt * 2 + 1]);
  342. }
  343. fprintf(f, "s off\n");
  344. for (int face = 0; face < faceCount; face += 3)
  345. {
  346. if (face < 0 && face >= int(shape.mesh.indices.size()))
  347. {
  348. continue;
  349. }
  350. fprintf(f, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
  351. shape.mesh.indices[face].vertex_index + 1, shape.mesh.indices[face].texcoord_index + 1, shape.mesh.indices[face].normal_index + 1,
  352. shape.mesh.indices[face + 1].vertex_index + 1, shape.mesh.indices[face + 1].texcoord_index + 1, shape.mesh.indices[face + 1].normal_index + 1,
  353. shape.mesh.indices[face + 2].vertex_index + 1, shape.mesh.indices[face + 2].texcoord_index + 1, shape.mesh.indices[face + 2].normal_index + 1);
  354. }
  355. fclose(f);
  356. float kdRed = mat.diffuse[0];
  357. float kdGreen = mat.diffuse[1];
  358. float kdBlue = mat.diffuse[2];
  359. float transparency = mat.transparency;
  360. char objSdfPartFileName[MAX_PATH_LEN];
  361. sprintf(objSdfPartFileName, "part%d.obj", s);
  362. fprintf(sdfFile,
  363. "\t\t<model name='%s'>\n"
  364. "\t\t\t<static>1</static>\n"
  365. "\t\t\t<pose>0 0 0 0 0 0</pose>\n"
  366. "\t\t\t<link name='link_d%d'>\n"
  367. "\t\t\t<inertial>\n"
  368. "\t\t\t<mass>0</mass>\n"
  369. "\t\t\t<inertia>\n"
  370. "\t\t\t<ixx>0.166667</ixx>\n"
  371. "\t\t\t<ixy>0</ixy>\n"
  372. "\t\t\t<ixz>0</ixz>\n"
  373. "\t\t\t<iyy>0.166667</iyy>\n"
  374. "\t\t\t<iyz>0</iyz>\n"
  375. "\t\t\t<izz>0.166667</izz>\n"
  376. "\t\t\t</inertia>\n"
  377. "\t\t\t</inertial>\n"
  378. "\t\t\t<collision name='collision_%d'>\n"
  379. "\t\t\t<geometry>\n"
  380. "\t\t\t<mesh>\n"
  381. "\t\t\t<scale>1 1 1</scale>\n"
  382. "\t\t\t\t<uri>%s</uri>\n"
  383. "\t\t\t</mesh>\n"
  384. "\t\t\t</geometry>\n"
  385. "\t\t\t </collision>\n"
  386. "\t\t\t<visual name='visual'>\n"
  387. "\t\t\t\t<geometry>\n"
  388. "\t\t\t\t<mesh>\n"
  389. "\t\t\t\t\t<scale>1 1 1</scale>\n"
  390. "\t\t\t\t\t<uri>%s</uri>\n"
  391. "\t\t\t\t</mesh>\n"
  392. "\t\t\t\t</geometry>\n"
  393. "\t\t\t<material>\n"
  394. "\t\t\t\t<ambient>1 0 0 1</ambient>\n"
  395. "\t\t\t\t<diffuse>%f %f %f %f</diffuse>\n"
  396. "\t\t\t\t<specular>0.1 0.1 0.1 1</specular>\n"
  397. "\t\t\t\t<emissive>0 0 0 0</emissive>\n"
  398. "\t\t\t </material>\n"
  399. "\t\t\t </visual>\n"
  400. "\t\t\t </link>\n"
  401. "\t\t\t</model>\n",
  402. objSdfPartFileName, s, s,
  403. objSdfPartFileName, objSdfPartFileName,
  404. kdRed, kdGreen, kdBlue, transparency);
  405. }
  406. }
  407. fprintf(sdfFile, "\t</world>\n</sdf>\n");
  408. fclose(sdfFile);
  409. return 0;
  410. }