EncoderArguments.cpp 22 KB

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