polyimport.cpp 17 KB

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