EncoderArguments.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. #include "Base.h"
  2. #include "EncoderArguments.h"
  3. #include "StringUtil.h"
  4. #ifdef WIN32
  5. #define PATH_MAX _MAX_PATH
  6. #define realpath(A,B) _fullpath(B,A,PATH_MAX)
  7. #endif
  8. // The encoder version number should be incremented when a feature is added to the encoder.
  9. // The encoder version is not the same as the GPB version.
  10. #define ENCODER_VERSION "2.0.0"
  11. #define HEIGHTMAP_SIZE_MAX 2049
  12. namespace gameplay
  13. {
  14. static EncoderArguments* __instance;
  15. extern int __logVerbosity = 1;
  16. EncoderArguments::EncoderArguments(size_t argc, const char** argv) :
  17. _normalMap(false),
  18. _parseError(false),
  19. _fontSize(0),
  20. _fontPreview(false),
  21. _fontFormat(Font::BITMAP),
  22. _textOutput(false),
  23. _optimizeAnimations(false),
  24. _animationGrouping(ANIMATIONGROUP_PROMPT),
  25. _outputMaterial(false)
  26. {
  27. __instance = this;
  28. memset(_heightmapResolution, 0, sizeof(int) * 2);
  29. if (argc > 1)
  30. {
  31. // read the options
  32. std::vector<std::string> arguments;
  33. for (size_t i = 1; i < argc; ++i)
  34. {
  35. arguments.push_back(argv[i]);
  36. }
  37. size_t index = 0;
  38. for (size_t i = 0; i < arguments.size(); ++i)
  39. {
  40. if (arguments[i][0] == '-')
  41. {
  42. readOption(arguments, &i);
  43. index = i + 1;
  44. }
  45. }
  46. if (arguments.size() - index == 2)
  47. {
  48. setInputfilePath(arguments[index]);
  49. setOutputfilePath(arguments[index + 1]);
  50. }
  51. else if (arguments.size() - index == 1)
  52. {
  53. setInputfilePath(arguments[index]);
  54. }
  55. }
  56. else
  57. {
  58. _parseError = true;
  59. }
  60. }
  61. EncoderArguments::~EncoderArguments(void)
  62. {
  63. }
  64. EncoderArguments* EncoderArguments::getInstance()
  65. {
  66. return __instance;
  67. }
  68. const std::string& EncoderArguments::getFilePath() const
  69. {
  70. return _filePath;
  71. }
  72. const char* EncoderArguments::getFilePathPointer() const
  73. {
  74. return _filePath.c_str();
  75. }
  76. std::string EncoderArguments::getOutputDirPath() const
  77. {
  78. if (_fileOutputPath.size() > 0)
  79. {
  80. int pos = _fileOutputPath.find_last_of('/');
  81. return (pos == -1 ? _fileOutputPath : _fileOutputPath.substr(0, pos));
  82. }
  83. else
  84. {
  85. int pos = _filePath.find_last_of('/');
  86. return (pos == -1 ? _filePath : _filePath.substr(0, pos));
  87. }
  88. }
  89. std::string EncoderArguments::getOutputFileExtension() const
  90. {
  91. switch (getFileFormat())
  92. {
  93. case FILEFORMAT_PNG:
  94. case FILEFORMAT_RAW:
  95. if (_normalMap)
  96. return ".png";
  97. default:
  98. return ".gpb";
  99. }
  100. }
  101. std::string EncoderArguments::getOutputFilePath() const
  102. {
  103. if (_fileOutputPath.size() > 0)
  104. {
  105. // Output file explicitly set
  106. return _fileOutputPath;
  107. }
  108. else
  109. {
  110. // Generate an output file path
  111. int pos = _filePath.find_last_of('.');
  112. std::string outputFilePath(pos > 0 ? _filePath.substr(0, pos) : _filePath);
  113. // Modify the original file name if the output extension can be the same as the input
  114. if (_normalMap)
  115. {
  116. outputFilePath.append("_normalmap");
  117. }
  118. outputFilePath.append(getOutputFileExtension());
  119. return outputFilePath;
  120. }
  121. }
  122. const std::vector<std::string>& EncoderArguments::getGroupAnimationNodeId() const
  123. {
  124. return _groupAnimationNodeId;
  125. }
  126. const std::vector<std::string>& EncoderArguments::getGroupAnimationAnimationId() const
  127. {
  128. return _groupAnimationAnimationId;
  129. }
  130. bool EncoderArguments::containsGroupNodeId(const std::string& nodeId) const
  131. {
  132. return find(_groupAnimationNodeId.begin(), _groupAnimationNodeId.end(), nodeId) != _groupAnimationNodeId.end();
  133. }
  134. const std::string EncoderArguments::getAnimationId(const std::string& nodeId) const
  135. {
  136. for (size_t i = 0, size = _groupAnimationNodeId.size(); i < size; ++i)
  137. {
  138. if (_groupAnimationNodeId[i].compare(nodeId) == 0)
  139. {
  140. return _groupAnimationAnimationId[i];
  141. }
  142. }
  143. return "";
  144. }
  145. EncoderArguments::AnimationGroupOption EncoderArguments::getAnimationGrouping() const
  146. {
  147. return _animationGrouping;
  148. }
  149. const std::vector<EncoderArguments::HeightmapOption>& EncoderArguments::getHeightmapOptions() const
  150. {
  151. return _heightmaps;
  152. }
  153. unsigned int EncoderArguments::tangentBinormalIdCount() const
  154. {
  155. return _tangentBinormalId.size();
  156. }
  157. bool EncoderArguments::isGenerateTangentBinormalId(const std::string& id) const
  158. {
  159. return _tangentBinormalId.find(id) != _tangentBinormalId.end();
  160. }
  161. bool EncoderArguments::normalMapGeneration() const
  162. {
  163. return _normalMap;
  164. }
  165. void EncoderArguments::getHeightmapResolution(int* x, int* y) const
  166. {
  167. *x = _heightmapResolution[0];
  168. *y = _heightmapResolution[1];
  169. }
  170. const Vector3& EncoderArguments::getHeightmapWorldSize() const
  171. {
  172. return _heightmapWorldSize;
  173. }
  174. bool EncoderArguments::parseErrorOccured() const
  175. {
  176. return _parseError;
  177. }
  178. bool EncoderArguments::fileExists() const
  179. {
  180. if (_filePath.length() > 0)
  181. {
  182. struct stat buf;
  183. if (stat(_filePath.c_str(), &buf) != -1)
  184. {
  185. return true;
  186. }
  187. }
  188. return false;
  189. }
  190. void splitString(const char* str, std::vector<std::string>* tokens)
  191. {
  192. // Split node id list into tokens
  193. unsigned int length = strlen(str);
  194. char* temp = new char[length + 1];
  195. strcpy(temp, str);
  196. char* tok = strtok(temp, ",");
  197. while (tok)
  198. {
  199. tokens->push_back(tok);
  200. tok = strtok(NULL, ",");
  201. }
  202. delete[] temp;
  203. }
  204. void EncoderArguments::printUsage() const
  205. {
  206. LOG(1, "Usage: gameplay-encoder [options] <input filepath> <output filepath>\n\n" \
  207. "Supported file extensions:\n" \
  208. " .fbx\t(FBX)\n" \
  209. " .ttf\t(TrueType Font)\n" \
  210. "\n" \
  211. "General Options:\n" \
  212. " -v <verbosity>\tVerbosity level (0-4).\n" \
  213. "\n" \
  214. "FBX file options:\n" \
  215. " -i <id>\tFilter by node ID.\n" \
  216. " -t\t\tWrite text/xml.\n" \
  217. " -g:auto\tAutomatically group animation channels into a new animation.\n" \
  218. " -g:none\tDo not prompt to group animations.\n" \
  219. " -g <node id> <animation id>\n" \
  220. "\t\tGroup all animation channels targeting the nodes into a \n" \
  221. "\t\tnew animation.\n" \
  222. " -m\t\tOutput material file for scene.\n" \
  223. " -tb <node id>\n" \
  224. "\t\tGenerates tangents and binormals for the given node.\n" \
  225. " -oa\n" \
  226. "\t\tOptimizes animations by analyzing animation channel data and\n" \
  227. "\t\tremoving any channels that contain default/identity values\n" \
  228. "\t\tand removing any duplicate contiguous keyframes, which are \n" \
  229. "\t\tcommon when exporting baked animation data.\n" \
  230. " -h <size> \"<node ids>\" <filename>\n" \
  231. "\t\tGenerates a single heightmap image using meshes from the \n" \
  232. "\t\tspecified nodes. \n" \
  233. "\t\t<size> is two comma-separated numbers in the format \"X,Y\", \n" \
  234. "\t\tindicating the dimensions of the produced heightmap image.\n" \
  235. "\t\t<node ids> should be in quotes with a space between each id.\n" \
  236. "\t\tFilename is the name of the image (PNG) to be saved.\n" \
  237. "\t\tMultiple -h arguments can be supplied to generate more than one \n" \
  238. "\t\theightmap. For 24-bit packed height data use -hp instead of -h.\n" \
  239. "\n" \
  240. "Normal map generation options:\n" \
  241. " -n\t\tGenerate normal map (requires input file of type PNG or RAW)\n" \
  242. " -s\t\tSize/resolution of the input heightmap image \n" \
  243. " \t\t(required for RAW files)\n" \
  244. " -w <size>\tSpecifies the size of an input terrain heightmap file in world\n" \
  245. "\t\tunits, along the X, Y and Z axes. <size> should be three \n" \
  246. "\t\tcomma-separated numbers in the format \"X,Y,Z\". The Y value \n" \
  247. "\t\trepresents the maximum possible height value of a full \n" \
  248. "\t\tintensity heightmap pixel.\n" \
  249. "\n" \
  250. " Normal map generation can be used to create object-space normal maps from \n" \
  251. " heightmap images. Heightmaps must be in either PNG format (where the \n" \
  252. " intensity of each pixel represents a height value), or in RAW format \n" \
  253. " (8 or 16-bit), which is a common headerless format supported by most \n" \
  254. " terrain generation tools.\n" \
  255. "\n" \
  256. "TTF file options:\n" \
  257. " -s <size>\tSize of the bitmap font. (in pixels).\n" \
  258. " -p\t\tOutput font preview.\n" \
  259. " -f Format of font. -f:b (BITMAP), -f:d (DISTANCE_FIELD).\n" \
  260. "\n" \
  261. "Encoder version: " ENCODER_VERSION "\n" \
  262. "\n");
  263. exit(8);
  264. }
  265. bool EncoderArguments::fontPreviewEnabled() const
  266. {
  267. return _fontPreview;
  268. }
  269. Font::FontFormat EncoderArguments::getFontFormat() const
  270. {
  271. return _fontFormat;
  272. }
  273. bool EncoderArguments::textOutputEnabled() const
  274. {
  275. return _textOutput;
  276. }
  277. bool EncoderArguments::optimizeAnimationsEnabled() const
  278. {
  279. return _optimizeAnimations;
  280. }
  281. bool EncoderArguments::outputMaterialEnabled() const
  282. {
  283. return _outputMaterial;
  284. }
  285. const char* EncoderArguments::getNodeId() const
  286. {
  287. if (_nodeId.length() == 0)
  288. {
  289. return NULL;
  290. }
  291. return _nodeId.c_str();
  292. }
  293. unsigned int EncoderArguments::getFontSize() const
  294. {
  295. return _fontSize;
  296. }
  297. EncoderArguments::FileFormat EncoderArguments::getFileFormat() const
  298. {
  299. if (_filePath.length() < 5)
  300. {
  301. return FILEFORMAT_UNKNOWN;
  302. }
  303. // Extract the extension
  304. std::string ext = "";
  305. size_t pos = _filePath.find_last_of(".");
  306. if (pos != std::string::npos)
  307. {
  308. ext = _filePath.substr(pos + 1);
  309. }
  310. for (size_t i = 0; i < ext.size(); ++i)
  311. ext[i] = (char)tolower(ext[i]);
  312. // Match every supported extension with its format constant
  313. if (ext.compare("dae") == 0)
  314. {
  315. return FILEFORMAT_DAE;
  316. }
  317. if (ext.compare("fbx") == 0)
  318. {
  319. return FILEFORMAT_FBX;
  320. }
  321. if (ext.compare("ttf") == 0)
  322. {
  323. return FILEFORMAT_TTF;
  324. }
  325. if (ext.compare("gpb") == 0)
  326. {
  327. return FILEFORMAT_GPB;
  328. }
  329. if (ext.compare("png") == 0)
  330. {
  331. return FILEFORMAT_PNG;
  332. }
  333. if (ext.compare("raw") == 0)
  334. {
  335. return FILEFORMAT_RAW;
  336. }
  337. return FILEFORMAT_UNKNOWN;
  338. }
  339. void EncoderArguments::readOption(const std::vector<std::string>& options, size_t* index)
  340. {
  341. const std::string& str = options[*index];
  342. if (str.length() == 0 && str[0] != '-')
  343. {
  344. return;
  345. }
  346. switch (str[1])
  347. {
  348. case 'f':
  349. if (str.compare("-f:b") == 0)
  350. {
  351. _fontFormat = Font::BITMAP;
  352. }
  353. else if (str.compare("-f:d") == 0)
  354. {
  355. _fontFormat = Font::DISTANCE_FIELD;
  356. }
  357. break;
  358. case 'g':
  359. if (str.compare("-groupAnimations:auto") == 0 || str.compare("-g:auto") == 0)
  360. {
  361. _animationGrouping = ANIMATIONGROUP_AUTO;
  362. }
  363. else if (str.compare("-groupAnimations:off") == 0 || str.compare("-g:off") == 0)
  364. {
  365. _animationGrouping = ANIMATIONGROUP_OFF;
  366. }
  367. else if (str.compare("-groupAnimations") == 0 || str.compare("-g") == 0)
  368. {
  369. // read two strings, make sure not to go out of bounds
  370. if ((*index + 2) >= options.size())
  371. {
  372. LOG(1, "Error: -g requires 2 arguments.\n");
  373. _parseError = true;
  374. return;
  375. }
  376. (*index)++;
  377. _groupAnimationNodeId.push_back(options[*index]);
  378. (*index)++;
  379. _groupAnimationAnimationId.push_back(options[*index]);
  380. }
  381. break;
  382. case 'i':
  383. // Node ID
  384. (*index)++;
  385. if (*index < options.size())
  386. {
  387. _nodeId.assign(options[*index]);
  388. }
  389. else
  390. {
  391. LOG(1, "Error: missing arguemnt for -%c.\n", str[1]);
  392. _parseError = true;
  393. return;
  394. }
  395. break;
  396. case 'o':
  397. // Optimization flag
  398. if (str == "-oa")
  399. {
  400. // Optimize animations
  401. _optimizeAnimations = true;
  402. }
  403. break;
  404. case 'h':
  405. {
  406. bool isHighPrecision = str.compare("-hp") == 0;
  407. if (str.compare("-heightmap") == 0 || str.compare("-h") == 0 || isHighPrecision)
  408. {
  409. (*index)++;
  410. if (*index < (options.size() + 2))
  411. {
  412. _heightmaps.resize(_heightmaps.size() + 1);
  413. HeightmapOption& heightmap = _heightmaps.back();
  414. heightmap.isHighPrecision = isHighPrecision;
  415. // Read heightmap size
  416. std::vector<std::string> parts;
  417. splitString(options[*index].c_str(), &parts);
  418. if (parts.size() != 2)
  419. {
  420. LOG(1, "Error: invalid size argument for -h|-heightmap.\n");
  421. _parseError = true;
  422. return;
  423. }
  424. heightmap.width = atoi(parts[0].c_str());
  425. heightmap.height = atoi(parts[1].c_str());
  426. // Put some artificial bounds on heightmap dimensions
  427. if (heightmap.width <= 0 || heightmap.height <= 0 || heightmap.width > HEIGHTMAP_SIZE_MAX || heightmap.height > HEIGHTMAP_SIZE_MAX)
  428. {
  429. LOG(1, "Error: size argument for -h|-heightmap must be between (1,1) and (%d,%d).\n", (int)HEIGHTMAP_SIZE_MAX, (int)HEIGHTMAP_SIZE_MAX);
  430. _parseError = true;
  431. return;
  432. }
  433. // Split node id list into tokens
  434. (*index)++;
  435. splitString(options[*index].c_str(), &heightmap.nodeIds);
  436. // Store output filename
  437. (*index)++;
  438. heightmap.filename = options[*index];
  439. if (heightmap.filename.empty())
  440. {
  441. LOG(1, "Error: missing filename argument for -h|-heightmap.\n");
  442. _parseError = true;
  443. return;
  444. }
  445. // Ensure the output filename has a .png extention
  446. if (heightmap.filename.length() > 5)
  447. {
  448. const char* ext = heightmap.filename.c_str() + (heightmap.filename.length() - 4);
  449. if (ext[0] != '.' || tolower(ext[1]) != 'p' || tolower(ext[2]) != 'n' || tolower(ext[3]) != 'g')
  450. heightmap.filename += ".png";
  451. }
  452. else
  453. heightmap.filename += ".png";
  454. }
  455. else
  456. {
  457. LOG(1, "Error: missing argument for -h|-heightmap.\n");
  458. _parseError = true;
  459. return;
  460. }
  461. }
  462. }
  463. break;
  464. case 'm':
  465. if (str.compare("-m") == 0)
  466. {
  467. // generate a material file
  468. _outputMaterial = true;
  469. }
  470. break;
  471. case 'n':
  472. _normalMap = true;
  473. break;
  474. case 'w':
  475. {
  476. // Read world size
  477. (*index)++;
  478. if (*index >= options.size())
  479. {
  480. LOG(1, "Error: missing world size argument for -w.\n");
  481. _parseError = true;
  482. return;
  483. }
  484. std::vector<std::string> parts;
  485. splitString(options[*index].c_str(), &parts);
  486. if (parts.size() != 3)
  487. {
  488. LOG(1, "Error: invalid world size argument for -w.\n");
  489. _parseError = true;
  490. return;
  491. }
  492. _heightmapWorldSize.x = (float)atof(parts[0].c_str());
  493. _heightmapWorldSize.y = (float)atof(parts[1].c_str());
  494. _heightmapWorldSize.z = (float)atof(parts[2].c_str());
  495. if (_heightmapWorldSize.x == 0 || _heightmapWorldSize.y == 0 || _heightmapWorldSize.z == 0)
  496. {
  497. LOG(1, "Error: invalid world size argument for -w.\n");
  498. _parseError = true;
  499. return;
  500. }
  501. }
  502. break;
  503. case 'p':
  504. _fontPreview = true;
  505. break;
  506. case 's':
  507. if (_normalMap)
  508. {
  509. (*index)++;
  510. if (*index >= options.size())
  511. {
  512. LOG(1, "Error: missing argument for -s.\n");
  513. _parseError = true;
  514. return;
  515. }
  516. // Heightmap size
  517. std::vector<std::string> parts;
  518. splitString(options[*index].c_str(), &parts);
  519. if (parts.size() != 2 ||
  520. (_heightmapResolution[0] = atoi(parts[0].c_str())) <= 0 ||
  521. (_heightmapResolution[1] = atoi(parts[1].c_str())) <= 0)
  522. {
  523. LOG(1, "Error: invalid argument for -s.\n");
  524. _parseError = true;
  525. return;
  526. }
  527. }
  528. else
  529. {
  530. // Font Size
  531. // old format was -s##
  532. if (str.length() > 2)
  533. {
  534. char n = str[2];
  535. if (n > '0' && n <= '9')
  536. {
  537. const char* number = str.c_str() + 2;
  538. _fontSize = atoi(number);
  539. break;
  540. }
  541. }
  542. (*index)++;
  543. if (*index < options.size())
  544. {
  545. _fontSize = atoi(options[*index].c_str());
  546. }
  547. else
  548. {
  549. LOG(1, "Error: missing arguemnt for -%c.\n", str[1]);
  550. _parseError = true;
  551. return;
  552. }
  553. }
  554. break;
  555. case 't':
  556. if (str.compare("-t") == 0)
  557. {
  558. _textOutput = true;
  559. }
  560. else if (str.compare("-tb") == 0)
  561. {
  562. if ((*index + 1) >= options.size())
  563. {
  564. LOG(1, "Error: -tb requires 1 argument.\n");
  565. _parseError = true;
  566. return;
  567. }
  568. (*index)++;
  569. std::string nodeId = options[*index];
  570. if (nodeId.length() > 0)
  571. {
  572. _tangentBinormalId.insert(nodeId);
  573. }
  574. }
  575. break;
  576. case 'v':
  577. (*index)++;
  578. if (*index < options.size())
  579. {
  580. __logVerbosity = atoi(options[*index].c_str());
  581. if (__logVerbosity < 0)
  582. __logVerbosity = 0;
  583. else if (__logVerbosity > 4)
  584. __logVerbosity = 4;
  585. }
  586. break;
  587. default:
  588. break;
  589. }
  590. }
  591. void EncoderArguments::setInputfilePath(const std::string& inputPath)
  592. {
  593. _filePath.assign(getRealPath(inputPath));
  594. }
  595. void EncoderArguments::setOutputfilePath(const std::string& outputPath)
  596. {
  597. std::string ext = getOutputFileExtension();
  598. if (outputPath.size() > 0 && outputPath[0] != '\0')
  599. {
  600. std::string realPath = getRealPath(outputPath);
  601. if (endsWith(realPath.c_str(), ext.c_str()))
  602. {
  603. _fileOutputPath.assign(realPath);
  604. }
  605. else if (endsWith(outputPath.c_str(), "/"))
  606. {
  607. std::string filenameNoExt = getFilenameNoExt(getFilenameFromFilePath(_filePath));
  608. _fileOutputPath.assign(outputPath);
  609. _fileOutputPath.append(filenameNoExt);
  610. _fileOutputPath.append(ext);
  611. }
  612. else
  613. {
  614. std::string filenameNoExt = getFilenameNoExt(getFilenameFromFilePath(realPath));
  615. int pos = realPath.find_last_of("/");
  616. if (pos)
  617. {
  618. _fileOutputPath = realPath.substr(0, pos);
  619. _fileOutputPath.append("/");
  620. _fileOutputPath.append(filenameNoExt);
  621. _fileOutputPath.append(ext);
  622. }
  623. }
  624. }
  625. }
  626. std::string EncoderArguments::getRealPath(const std::string& filepath)
  627. {
  628. char path[PATH_MAX + 1]; /* not sure about the "+ 1" */
  629. realpath(filepath.c_str(), path);
  630. replace_char(path, '\\', '/');
  631. return std::string(path);
  632. }
  633. void EncoderArguments::replace_char(char* str, char oldChar, char newChar)
  634. {
  635. for (; *str != '\0'; ++str)
  636. {
  637. if (*str == oldChar)
  638. {
  639. *str = newChar;
  640. }
  641. }
  642. }
  643. std::string concat(const std::string& a, const char* b)
  644. {
  645. std::string str(a);
  646. str.append(b);
  647. return str;
  648. }
  649. void unittestsEncoderArguments()
  650. {
  651. std::string dir = EncoderArguments::getRealPath(".");
  652. std::string exePath = EncoderArguments::getRealPath(".");
  653. exePath.append("/gameplay-encoder.exe");
  654. const char* exe = exePath.c_str();
  655. {
  656. const char* argv[] = {exe, "-g", "root", "movements", "C:\\Git\\gaming\\GamePlay\\gameplay-encoder\\res\\duck.fbx"};
  657. EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
  658. assert(equals(args.getAnimationId("root"), ("movements")));
  659. assert(equals(args.getGroupAnimationNodeId()[0], ("root")));
  660. assert(equals(args.getOutputFilePath(), "C:/Git/gaming/GamePlay/gameplay-encoder/res/duck.gpb"));
  661. }
  662. {
  663. // Test with only input file name (relative)
  664. const char* argv[] = {exe, "input.fbx"};
  665. EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
  666. assert(equals(args.getFilePath(), concat(dir, "/input.fbx")));
  667. assert(equals(args.getOutputFilePath(), concat(dir, "/input.gpb")));
  668. equals(args.getOutputDirPath(), dir);
  669. }
  670. {
  671. // Test specifying a relative output path
  672. const char* argv[] = {exe, "input.fbx", "output.gpb"};
  673. EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
  674. assert(equals(args.getFilePath(), concat(dir, "/input.fbx")));
  675. assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
  676. }
  677. {
  678. // Test specifying a relative output path
  679. const char* argv[] = {exe, "input.fbx", "output.gpb"};
  680. EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
  681. assert(equals(args.getFilePath(), concat(dir, "/input.fbx")));
  682. assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
  683. }
  684. {
  685. // Test specifying a relative output path to a directory
  686. const char* argv[] = {exe, "input.fbx", "stuff/output.gpb"};
  687. EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
  688. assert(equals(args.getFilePath(), concat(dir, "/input.fbx")));
  689. assert(equals(args.getOutputFilePath(), concat(dir, "/stuff/output.gpb")));
  690. }
  691. {
  692. // Test parsing some arguments
  693. const char* argv[] = {exe, "test.fbx", "-t", "input.fbx", "output.gpb"};
  694. EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
  695. assert(equals(args.getFilePath(), concat(dir, "/input.fbx")));
  696. assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
  697. assert(args.textOutputEnabled());
  698. }
  699. {
  700. // Test output file with no file extension
  701. const char* argv[] = {exe, "input.fbx", "output"};
  702. EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
  703. assert(equals(args.getFilePath(), concat(dir, "/input.fbx")));
  704. assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
  705. }
  706. {
  707. // Test output file with wrong file extension
  708. const char* argv[] = {exe, "input.fbx", "output.fbx"};
  709. EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
  710. assert(equals(args.getFilePath(), concat(dir, "/input.fbx")));
  711. assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
  712. }
  713. }
  714. }