polyimport.cpp 19 KB

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