polyimport.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. #include "polyimport.h"
  2. #include "OSBasics.h"
  3. #include "PolyObject.h"
  4. #include "physfs.h"
  5. #ifdef WIN32
  6. #include "getopt.h"
  7. #define getopt getopt_a
  8. #else
  9. #include <unistd.h>
  10. #endif
  11. using namespace Polycode;
  12. const struct aiScene* scene = NULL;
  13. bool hasWeights = false;
  14. vector<aiBone*> bones;
  15. unsigned int numBones = 0;
  16. std::vector<String> materialsInFile;
  17. std::vector<String> meshesInFile;
  18. bool writeNormals = false;
  19. bool writeTangents = false;
  20. bool writeColors = false;
  21. bool writeBoneWeights = false;
  22. bool writeUVs = false;
  23. bool writeSecondaryUVs = false;
  24. bool hasMesh(String meshName) {
  25. for(int i=0; i < meshesInFile.size(); i++) {
  26. if(meshesInFile[i] == meshName) {
  27. return true;
  28. }
  29. }
  30. return false;
  31. }
  32. bool hasMaterial(String materialName) {
  33. for(int i=0; i < materialsInFile.size(); i++) {
  34. if(materialsInFile[i] == materialName) {
  35. return true;
  36. }
  37. }
  38. return false;
  39. }
  40. unsigned int addBone(aiBone *bone) {
  41. for(int i=0; i < bones.size(); i++) {
  42. if(bones[i]->mName == bone->mName)
  43. return i;
  44. }
  45. bones.push_back(bone);
  46. return bones.size()-1;
  47. }
  48. void addToMesh(String prefix, Polycode::Mesh *tmesh, const struct aiScene *sc, const struct aiNode* nd, bool swapZY, bool addSubmeshes, bool listOnly, ObjectEntry *parentSceneObject, String overrideMaterial, ObjectEntry *materialsParent, String assetPrefixPath, String baseFileName) {
  49. int i, nIgnoredPolygons = 0;
  50. unsigned int n = 0, t;
  51. // draw all meshes assigned to this node
  52. for (; n < nd->mNumMeshes; ++n) {
  53. if(!addSubmeshes) {
  54. tmesh = new Polycode::Mesh(Mesh::TRI_MESH);
  55. tmesh->indexedMesh = true;
  56. }
  57. const struct aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];
  58. Vector3 bBox;
  59. String meshFileName = String(nd->mName.data);
  60. if(meshFileName == "") {
  61. meshFileName = baseFileName;
  62. }
  63. int idx = 0;
  64. String baseMeshFileName = meshFileName;
  65. while(hasMesh(meshFileName)) {
  66. meshFileName = baseMeshFileName + String::IntToString(idx);
  67. idx++;
  68. }
  69. meshesInFile.push_back(meshFileName);
  70. if(listOnly) {
  71. if(!addSubmeshes) {
  72. printf("%s%s.mesh\n", prefix.c_str(), meshFileName.c_str());
  73. }
  74. } else {
  75. printf("Importing mesh:%s (%d vertices) (%d faces) \n", mesh->mName.data, mesh->mNumVertices, mesh->mNumFaces);
  76. }
  77. //apply_material(sc->mMaterials[mesh->mMaterialIndex]);
  78. for (t = 0; t < mesh->mNumVertices; ++t) {
  79. Vertex *vertex = new Vertex();
  80. int index = t;
  81. if(mesh->mColors[0] != NULL) {
  82. vertex->vertexColor.setColorRGBA(mesh->mColors[0][index].r, mesh->mColors[0][index].g, mesh->mColors[0][index].b, mesh->mColors[0][index].a);
  83. }
  84. if(mesh->mTangents != NULL) {
  85. if(swapZY)
  86. vertex->tangent = Vector3(mesh->mTangents[index].x, mesh->mTangents[index].z, -mesh->mTangents[index].y);
  87. else
  88. vertex->tangent = Vector3(mesh->mTangents[index].x, mesh->mTangents[index].y, mesh->mTangents[index].z);
  89. }
  90. if(mesh->mNormals != NULL) {
  91. if(swapZY)
  92. vertex->setNormal(mesh->mNormals[index].x, mesh->mNormals[index].z, -mesh->mNormals[index].y);
  93. else
  94. vertex->setNormal(mesh->mNormals[index].x, mesh->mNormals[index].y, mesh->mNormals[index].z);
  95. }
  96. if(mesh->HasTextureCoords(0))
  97. {
  98. vertex->setTexCoord(mesh->mTextureCoords[0][index].x, mesh->mTextureCoords[0][index].y);
  99. }
  100. if(mesh->HasTextureCoords(1))
  101. {
  102. vertex->setSecondaryTexCoord(mesh->mTextureCoords[1][index].x, mesh->mTextureCoords[1][index].y);
  103. }
  104. for( unsigned int a = 0; a < mesh->mNumBones; a++) {
  105. aiBone* bone = mesh->mBones[a];
  106. unsigned int boneIndex = addBone(bone);
  107. for( unsigned int b = 0; b < bone->mNumWeights; b++) {
  108. if(bone->mWeights[b].mVertexId == index) {
  109. vertex->addBoneAssignment(boneIndex, bone->mWeights[b].mWeight);
  110. hasWeights = true;
  111. }
  112. }
  113. }
  114. if(swapZY) {
  115. vertex->set(mesh->mVertices[index].x, mesh->mVertices[index].z, -mesh->mVertices[index].y);
  116. } else {
  117. vertex->set(mesh->mVertices[index].x, mesh->mVertices[index].y, mesh->mVertices[index].z);
  118. }
  119. if(fabs(vertex->x) > bBox.x) {
  120. bBox.x = fabs(vertex->x);
  121. }
  122. if(fabs(vertex->y) > bBox.y) {
  123. bBox.y = fabs(vertex->y);
  124. }
  125. if(fabs(vertex->z) > bBox.z) {
  126. bBox.z = fabs(vertex->z);
  127. }
  128. tmesh->addVertex(vertex);
  129. }
  130. for (t = 0; t < mesh->mNumFaces; ++t) {
  131. const struct aiFace* face = &mesh->mFaces[t];
  132. if (face->mNumIndices != 3) {
  133. nIgnoredPolygons++;
  134. continue;
  135. }
  136. for(i = 0; i < face->mNumIndices; i++) {
  137. int index = face->mIndices[i];
  138. tmesh->addIndex(index);
  139. }
  140. }
  141. if(!addSubmeshes && !listOnly) {
  142. String fileNameMesh = prefix+meshFileName+".mesh";
  143. OSFILE *outFile = OSBasics::open(fileNameMesh.c_str(), "wb");
  144. tmesh->saveToFile(outFile, writeNormals, writeTangents, writeColors, writeBoneWeights, writeUVs, writeSecondaryUVs);
  145. OSBasics::close(outFile);
  146. delete tmesh;
  147. ObjectEntry *meshEntry = parentSceneObject->addChild("child");
  148. meshEntry->addChild("id", String(nd->mName.data));
  149. meshEntry->addChild("tags", "");
  150. meshEntry->addChild("type", "SceneMesh");
  151. meshEntry->addChild("cR", "1");
  152. meshEntry->addChild("cG", "1");
  153. meshEntry->addChild("cB", "1");
  154. meshEntry->addChild("cA", "1");
  155. meshEntry->addChild("blendMode", "0");
  156. aiVector3D p;
  157. aiVector3D s;
  158. aiQuaternion r;
  159. nd->mTransformation.Decompose(s, r, p);
  160. meshEntry->addChild("sX", s.x);
  161. if(swapZY) {
  162. meshEntry->addChild("sY", s.z);
  163. meshEntry->addChild("sZ", s.y);
  164. } else {
  165. meshEntry->addChild("sY", s.y);
  166. meshEntry->addChild("sZ", s.z);
  167. }
  168. meshEntry->addChild("rX", r.x);
  169. if(swapZY) {
  170. meshEntry->addChild("rY", r.z);
  171. meshEntry->addChild("rZ", -r.y);
  172. } else {
  173. meshEntry->addChild("rY", r.y);
  174. meshEntry->addChild("rZ", r.z);
  175. }
  176. meshEntry->addChild("rW", r.w);
  177. meshEntry->addChild("pX", p.x);
  178. if(swapZY) {
  179. meshEntry->addChild("pY", p.z);
  180. meshEntry->addChild("pZ", -p.y);
  181. } else{
  182. meshEntry->addChild("pY", p.y);
  183. meshEntry->addChild("pZ", p.z);
  184. }
  185. bBox = bBox * 2.0;
  186. if(bBox.x == 0.0) {
  187. bBox.x = 0.001;
  188. }
  189. if(bBox.y == 0.0) {
  190. bBox.y = 0.001;
  191. }
  192. if(bBox.z == 0.0) {
  193. bBox.z = 0.001;
  194. }
  195. meshEntry->addChild("bbX", bBox.x);
  196. meshEntry->addChild("bbY", bBox.y);
  197. meshEntry->addChild("bbZ", bBox.z);
  198. ObjectEntry *sceneMeshEntry = meshEntry->addChild("SceneMesh");
  199. sceneMeshEntry->addChild("file", assetPrefixPath+fileNameMesh);
  200. String materialName = "Default";
  201. int materialIndex = mesh->mMaterialIndex;
  202. if(materialIndex < scene->mNumMaterials) {
  203. aiString name;
  204. scene->mMaterials[materialIndex]->Get(AI_MATKEY_NAME,name);
  205. if(name.length > 0) {
  206. materialName = String(name.data);
  207. }
  208. }
  209. if(materialsParent && materialName != "Default") {
  210. if(!hasMaterial(materialName)) {
  211. ObjectEntry *materialEntry = materialsParent->addChild("material");
  212. materialEntry->addChild("name", materialName);
  213. materialEntry->addChild("blendingMode", 0);
  214. materialEntry->addChild("blendingMode", 0);
  215. ObjectEntry *shaderEntry = materialEntry->addChild("shader");
  216. shaderEntry->addChild("name", "DefaultShaderNoTexture");
  217. materialsInFile.push_back(materialName);
  218. }
  219. }
  220. if(overrideMaterial != "") {
  221. sceneMeshEntry->addChild("material", overrideMaterial);
  222. } else {
  223. sceneMeshEntry->addChild("material", materialName);
  224. }
  225. }
  226. if (nIgnoredPolygons) {
  227. printf("Ignored %d non-triangular polygons\n", nIgnoredPolygons);
  228. }
  229. }
  230. // draw all children
  231. for (n = 0; n < nd->mNumChildren; ++n) {
  232. addToMesh(prefix, tmesh, sc, nd->mChildren[n], swapZY, addSubmeshes, listOnly, parentSceneObject, overrideMaterial, materialsParent, assetPrefixPath, baseFileName);
  233. }
  234. }
  235. int getBoneID(aiString name) {
  236. for(int i=0; i < bones.size(); i++) {
  237. if(bones[i]->mName == name) {
  238. return i;
  239. }
  240. }
  241. return 666;
  242. }
  243. void addToISkeleton(ISkeleton *skel, IBone *parent, const struct aiScene *sc, const struct aiNode* nd) {
  244. IBone *bone = new IBone();
  245. bone->parent = parent;
  246. bone->name = nd->mName;
  247. bone->t = nd->mTransformation;
  248. for(int i=0; i < bones.size(); i++) {
  249. if(bones[i]->mName == bone->name) {
  250. bone->bindMatrix = bones[i]->mOffsetMatrix;
  251. }
  252. }
  253. for (int n = 0; n < nd->mNumChildren; ++n) {
  254. addToISkeleton(skel, bone, sc, nd->mChildren[n]);
  255. }
  256. skel->addIBone(bone, getBoneID(bone->name));
  257. }
  258. int exportToFile(String prefix, bool swapZY, bool addSubmeshes, bool listOnly, bool exportEntity, bool generateMaterialFile, String overrideMaterial, String assetPrefixPath, String baseFileName) {
  259. Object materialObject;
  260. ObjectEntry *materialsParent = NULL;;
  261. if(generateMaterialFile) {
  262. materialObject.root.name = "polycode";
  263. materialsParent = materialObject.root.addChild("materials");
  264. }
  265. Object sceneObject;
  266. sceneObject.root.name = "entity";
  267. sceneObject.root.addChild("version", 2);
  268. ObjectEntry *parentEntry = sceneObject.root.addChild("root");
  269. parentEntry->addChild("id", "");
  270. parentEntry->addChild("tags", "");
  271. parentEntry->addChild("type", "Entity");
  272. parentEntry->addChild("cR", "1");
  273. parentEntry->addChild("cG", "1");
  274. parentEntry->addChild("cB", "1");
  275. parentEntry->addChild("cA", "1");
  276. parentEntry->addChild("blendMode", "0");
  277. parentEntry->addChild("sX", 1.0);
  278. parentEntry->addChild("sY", 1.0);
  279. parentEntry->addChild("sZ", 1.0);
  280. parentEntry->addChild("rX", 0.0);
  281. parentEntry->addChild("rY", 0.0);
  282. parentEntry->addChild("rZ", 0.0);
  283. parentEntry->addChild("rW", 1.0);
  284. parentEntry->addChild("pX", 0.0);
  285. parentEntry->addChild("pY", 0.0);
  286. parentEntry->addChild("pZ", 0.0);
  287. parentEntry->addChild("bbX", 0.0);
  288. parentEntry->addChild("bbY", 0.0);
  289. parentEntry->addChild("bbZ", 0.0);
  290. ObjectEntry *children = parentEntry->addChild("children");
  291. Polycode::Mesh *mesh = new Polycode::Mesh(Mesh::TRI_MESH);
  292. mesh->indexedMesh = true;
  293. addToMesh(prefix, mesh, scene, scene->mRootNode, swapZY, addSubmeshes, listOnly, children, overrideMaterial, materialsParent, assetPrefixPath, baseFileName);
  294. if(addSubmeshes) {
  295. String fileNameMesh;
  296. if(prefix != "") {
  297. fileNameMesh = prefix+".mesh";
  298. } else {
  299. fileNameMesh = "out.mesh";
  300. }
  301. if(listOnly) {
  302. printf("%s\n", fileNameMesh.c_str());
  303. } else {
  304. OSFILE *outFile = OSBasics::open(fileNameMesh.c_str(), "wb");
  305. mesh->saveToFile(outFile, writeNormals, writeTangents, writeColors, writeBoneWeights, writeUVs, writeSecondaryUVs);
  306. OSBasics::close(outFile);
  307. }
  308. }
  309. if(hasWeights) {
  310. if(listOnly) {
  311. printf("%s.skeleton\n", prefix.c_str());
  312. } else {
  313. printf("Mesh has weights, exporting skeleton...\n");
  314. }
  315. String fileNameSkel;
  316. if(prefix != "") {
  317. fileNameSkel = prefix+".skeleton";
  318. } else {
  319. fileNameSkel = "out.skeleton";
  320. }
  321. ISkeleton *skeleton = new ISkeleton();
  322. for (int n = 0; n < scene->mRootNode->mNumChildren; ++n) {
  323. if(scene->mRootNode->mChildren[n]->mNumChildren > 0) {
  324. addToISkeleton(skeleton, NULL, scene, scene->mRootNode->mChildren[n]);
  325. }
  326. }
  327. if(scene->HasAnimations()) {
  328. printf("Importing animations...\n");
  329. for(int i=0; i < scene->mNumAnimations;i++) {
  330. aiAnimation *a = scene->mAnimations[i];
  331. if(listOnly) {
  332. printf("%s%s.anim\n", prefix.c_str(), a->mName.data);
  333. } else {
  334. printf("Importing '%s' (%d tracks)\n", a->mName.data, a->mNumChannels);
  335. }
  336. IAnimation *anim = new IAnimation();
  337. anim->tps = a->mTicksPerSecond;
  338. anim->name = a->mName.data;
  339. anim->numTracks = a->mNumChannels;
  340. anim->length = a->mDuration/a->mTicksPerSecond;
  341. for(int c=0; c < a->mNumChannels; c++) {
  342. aiNodeAnim *nodeAnim = a->mChannels[c];
  343. ITrack *track = new ITrack();
  344. track->nodeAnim = nodeAnim;
  345. anim->tracks.push_back(track);
  346. }
  347. skeleton->addAnimation(anim);
  348. }
  349. } else {
  350. printf("No animations in file...\n");
  351. }
  352. if(!listOnly) {
  353. skeleton->saveToFile(fileNameSkel.c_str(), swapZY);
  354. }
  355. } else {
  356. if(!listOnly) {
  357. printf("No weight data, skipping skeleton export...\n");
  358. }
  359. }
  360. String matFileName = baseFileName+".mat";
  361. if(!listOnly && materialsParent) {
  362. materialObject.saveToXML(matFileName);
  363. }
  364. if(!listOnly && exportEntity) {
  365. if(materialsParent) {
  366. ObjectEntry *settings = sceneObject.root.addChild("settings");
  367. ObjectEntry *matFiles = settings->addChild("matFiles");
  368. ObjectEntry *matFile = matFiles->addChild("matFile");
  369. matFile->addChild("path", assetPrefixPath+matFileName);
  370. }
  371. String entityFileName = baseFileName+".entity";
  372. sceneObject.saveToXML(entityFileName);
  373. }
  374. if(mesh) {
  375. delete mesh;
  376. }
  377. return 1;
  378. }
  379. int main(int argc, char **argv) {
  380. bool argsValid = true;
  381. bool showHelp = false;
  382. bool swapZYAxis = false;
  383. bool generateTangents = false;
  384. bool addSubmeshes = false;
  385. bool listOnly = false;
  386. bool showAssimpDebug = false;
  387. bool generateNormals = false;
  388. bool exportEntity = false;
  389. bool generateMaterialFile = false;
  390. String overrideMaterial;
  391. String prefix;
  392. String assetPrefixPath;
  393. int opt;
  394. while ((opt = getopt(argc, argv, "engcwuvadlhp:stmfo:x:")) != -1) {
  395. switch ((char)opt) {
  396. case 'e':
  397. exportEntity = true;
  398. break;
  399. case 'n':
  400. writeNormals = true;
  401. break;
  402. case 'g':
  403. writeTangents = true;
  404. break;
  405. case 'c':
  406. writeColors = true;
  407. break;
  408. case 'w':
  409. writeBoneWeights = true;
  410. break;
  411. case 'u':
  412. writeUVs = true;
  413. break;
  414. case 'v':
  415. writeSecondaryUVs = true;
  416. break;
  417. case 's':
  418. swapZYAxis = true;
  419. break;
  420. case 't':
  421. generateTangents = true;
  422. break;
  423. case 'm':
  424. generateNormals = true;
  425. break;
  426. case 'a':
  427. addSubmeshes = true;
  428. break;
  429. case 'd':
  430. showAssimpDebug = true;
  431. break;
  432. case 'l':
  433. listOnly = true;
  434. break;
  435. case 'p':
  436. prefix = String(optarg);
  437. break;
  438. case 'h':
  439. showHelp = true;
  440. break;
  441. case 'f':
  442. generateMaterialFile = true;
  443. break;
  444. case 'o':
  445. overrideMaterial = String(optarg);
  446. break;
  447. case 'x':
  448. assetPrefixPath = String(optarg)+"/";
  449. break;
  450. default:
  451. argsValid = false;
  452. break;
  453. }
  454. }
  455. if(listOnly && argc < 3) {
  456. argsValid = false;
  457. }
  458. if(!listOnly) {
  459. printf("Polycode import tool v"POLYCODE_VERSION_STRING"\n");
  460. }
  461. if(!argsValid) {
  462. printf("Invalid arguments! Run with -h to see available options.\n\n");
  463. return 0;
  464. }
  465. if(showHelp || argc < 2) {
  466. printf("usage: polyimport [-adhlstngcwuvmef] [-o override_material] [-p output_prefix] [-x asset_path] source_file\n\n");
  467. printf("Misc options:\n");
  468. printf("d: Show Assimp debug info.\n");
  469. printf("h: Show this help.\n");
  470. printf("l: List output files, but do not convert.\n");
  471. printf("p: Specify a file prefix for exported files.\n");
  472. printf("\nMesh import options:\n");
  473. printf("a: Add all meshes to a single mesh.\n");
  474. printf("s: Swap Z/Y axis (e.g. import from Blender)\n");
  475. printf("m: Generate normals.\n");
  476. printf("t: Generate tangents.\n");
  477. printf("\nMesh export options:\n");
  478. printf("n: Export normals\n");
  479. printf("g: Export tangents\n");
  480. printf("c: Export colors\n");
  481. printf("w: Export bone weights\n");
  482. printf("u: Export UV coordinates\n");
  483. printf("v: Export secondary UV coordinates\n");
  484. printf("\nEntity export options:\n");
  485. printf("e: Export entity scene\n");
  486. printf("f: Generate material file\n");
  487. printf("o: Specify override material.\n");
  488. printf("x: Specify asset prefix path.\n");
  489. printf("\n");
  490. return 0;
  491. }
  492. PHYSFS_init(argv[0]);
  493. if(showAssimpDebug) {
  494. struct aiLogStream stream;
  495. stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT,NULL);
  496. aiAttachLogStream(&stream);
  497. }
  498. int inputArg = argc-1;
  499. if(!listOnly) {
  500. printf("Loading %s...\n", argv[inputArg]);
  501. }
  502. String baseFileName = String(argv[inputArg]);
  503. std::vector<String> parts = baseFileName.split("/");
  504. if(parts.size() > 1) {
  505. baseFileName = parts[parts.size()-1];
  506. }
  507. baseFileName = baseFileName.substr(0, baseFileName.find_last_of("."));
  508. scene = aiImportFile(argv[inputArg], aiProcess_JoinIdenticalVertices|
  509. aiProcess_Triangulate);
  510. if(scene) {
  511. if(generateTangents && !listOnly) {
  512. aiApplyPostProcessing(scene, aiProcess_CalcTangentSpace);
  513. }
  514. if(generateNormals && !listOnly) {
  515. aiApplyPostProcessing(scene, aiProcess_GenSmoothNormals);
  516. }
  517. exportToFile(prefix, swapZYAxis, addSubmeshes, listOnly, exportEntity, generateMaterialFile, overrideMaterial, assetPrefixPath, baseFileName);
  518. } else {
  519. printf("Error opening scene (%s)\n", aiGetErrorString());
  520. }
  521. aiReleaseImport(scene);
  522. return 1;
  523. }