polyimport.cpp 19 KB

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