You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

899 lines
26 KiB

  1. /*
  2. ---------------------------------------------------------------------------
  3. Open Asset Import Library (assimp)
  4. ---------------------------------------------------------------------------
  5. Copyright (c) 2006-2012, assimp team
  6. All rights reserved.
  7. Redistribution and use of this software in source and binary forms,
  8. with or without modification, are permitted provided that the following
  9. conditions are met:
  10. * Redistributions of source code must retain the above
  11. copyright notice, this list of conditions and the
  12. following disclaimer.
  13. * Redistributions in binary form must reproduce the above
  14. copyright notice, this list of conditions and the
  15. following disclaimer in the documentation and/or other
  16. materials provided with the distribution.
  17. * Neither the name of the assimp team, nor the names of its
  18. contributors may be used to endorse or promote products
  19. derived from this software without specific prior
  20. written permission of the assimp team.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. ---------------------------------------------------------------------------
  33. */
  34. /** @file CompareDump.cpp
  35. * @brief Implementation of the 'assimp cmpdmp', which compares
  36. * two model dumps for equality. It plays an important role
  37. * in the regression test suite.
  38. */
  39. #include "Main.h"
  40. const char* AICMD_MSG_CMPDUMP_HELP =
  41. "assimp cmpdump <actual> <expected>\n"
  42. "\tCompare two short dumps produced with \'assimp dump <..> -s\' for equality.\n"
  43. ;
  44. #include "../../code/assbin_chunks.h"
  45. ////////////////////////////////////////////////////////////////////////////////////////////////////
  46. #include "generic_inserter.hpp"
  47. // get << for aiString
  48. template <typename char_t, typename traits_t>
  49. void mysprint(std::basic_ostream<char_t, traits_t>& os, const aiString& vec) {
  50. os << "[length: \'" << std::dec << vec.length << "\' content: \'" << vec.data << "\']";
  51. }
  52. template <typename char_t, typename traits_t>
  53. std::basic_ostream<char_t, traits_t>& operator<< (std::basic_ostream<char_t, traits_t>& os, const aiString& vec) {
  54. return generic_inserter(mysprint<char_t,traits_t>, os, vec);
  55. }
  56. class sliced_chunk_iterator;
  57. ////////////////////////////////////////////////////////////////////////////////////////////////////
  58. /// @class compare_fails_exception
  59. ///
  60. /// @brief Sentinel exception to return quickly from deeply nested control paths
  61. ////////////////////////////////////////////////////////////////////////////////////////////////////
  62. class compare_fails_exception : public virtual std::exception {
  63. public:
  64. enum {MAX_ERR_LEN = 4096};
  65. /* public c'tors */
  66. compare_fails_exception(const char* msg) {
  67. strncpy(mywhat,msg,MAX_ERR_LEN-1);
  68. strcat(mywhat,"\n");
  69. }
  70. /* public member functions */
  71. const char* what() const throw() {
  72. return mywhat;
  73. }
  74. private:
  75. char mywhat[MAX_ERR_LEN+1];
  76. };
  77. #define MY_FLT_EPSILON 1e-1f
  78. #define MY_DBL_EPSILON 1e-1
  79. ////////////////////////////////////////////////////////////////////////////////////////////////////
  80. /// @class comparer_context
  81. ///
  82. /// @brief Record our way through the files to be compared and dump useful information if we fail.
  83. ////////////////////////////////////////////////////////////////////////////////////////////////////
  84. class comparer_context {
  85. friend class sliced_chunk_iterator;
  86. public:
  87. /* construct given two file handles to compare */
  88. comparer_context(FILE* actual,FILE* expect)
  89. : actual(actual)
  90. , expect(expect)
  91. , cnt_chunks(0)
  92. {
  93. ai_assert(actual);
  94. ai_assert(expect);
  95. fseek(actual,0,SEEK_END);
  96. lengths.push(std::make_pair(static_cast<uint32_t>(ftell(actual)),0));
  97. fseek(actual,0,SEEK_SET);
  98. history.push_back(HistoryEntry("---",PerChunkCounter()));
  99. }
  100. public:
  101. /* set new scope */
  102. void push_elem(const char* msg) {
  103. const std::string s = msg;
  104. PerChunkCounter::const_iterator it = history.back().second.find(s);
  105. if(it != history.back().second.end()) {
  106. ++history.back().second[s];
  107. }
  108. else history.back().second[s] = 1;
  109. history.push_back(HistoryEntry(s,PerChunkCounter()));
  110. }
  111. /* leave current scope */
  112. void pop_elem() {
  113. ai_assert(history.size());
  114. history.pop_back();
  115. }
  116. /* push current chunk length and start offset on top of stack */
  117. void push_length(uint32_t nl, uint32_t start) {
  118. lengths.push(std::make_pair(nl,start));
  119. ++cnt_chunks;
  120. }
  121. /* pop the chunk length stack */
  122. void pop_length() {
  123. ai_assert(lengths.size());
  124. lengths.pop();
  125. }
  126. /* access the current chunk length */
  127. uint32_t get_latest_chunk_length() {
  128. ai_assert(lengths.size());
  129. return lengths.top().first;
  130. }
  131. /* access the current chunk start offset */
  132. uint32_t get_latest_chunk_start() {
  133. ai_assert(lengths.size());
  134. return lengths.top().second;
  135. }
  136. /* total number of chunk headers passed so far*/
  137. uint32_t get_num_chunks() {
  138. return cnt_chunks;
  139. }
  140. /* get ACTUAL file desc. != NULL */
  141. FILE* get_actual() const {
  142. return actual;
  143. }
  144. /* get EXPECT file desc. != NULL */
  145. FILE* get_expect() const {
  146. return expect;
  147. }
  148. /* compare next T from both streams, name occurs in error messages */
  149. template<typename T> T cmp(const std::string& name) {
  150. T a,e;
  151. read(a,e);
  152. if(a != e) {
  153. std::stringstream ss;
  154. failure((ss<< "Expected " << e << ", but actual is " << a,
  155. ss.str()),name);
  156. }
  157. // std::cout << name << " " << std::hex << a << std::endl;
  158. return a;
  159. }
  160. /* compare next num T's from both streams, name occurs in error messages */
  161. template<typename T> void cmp(size_t num,const std::string& name) {
  162. for(size_t n = 0; n < num; ++n) {
  163. std::stringstream ss;
  164. cmp<T>((ss<<name<<"["<<n<<"]",ss.str()));
  165. // std::cout << name << " " << std::hex << a << std::endl;
  166. }
  167. }
  168. /* Bounds of an aiVector3D array (separate function
  169. * because partial specializations of member functions are illegal--)*/
  170. template<typename T> void cmp_bounds(const std::string& name) {
  171. cmp<T> (name+".<minimum-value>");
  172. cmp<T> (name+".<maximum-value>");
  173. }
  174. private:
  175. /* Report failure */
  176. void failure(const std::string& err, const std::string& name) {
  177. std::stringstream ss;
  178. throw compare_fails_exception((ss
  179. << "Files are different at "
  180. << history.back().first
  181. << "."
  182. << name
  183. << ".\nError is: "
  184. << err
  185. << ".\nCurrent position in scene hierarchy is "
  186. << print_hierarchy(),ss.str().c_str()
  187. ));
  188. }
  189. /** print our 'stack' */
  190. std::string print_hierarchy() {
  191. std::stringstream ss;
  192. ss << std::endl;
  193. const char* last = history.back().first.c_str();
  194. std::string pad;
  195. for(ChunkHistory::reverse_iterator rev = ++history.rbegin(),
  196. end = history.rend(); rev < end; ++rev, pad += " ")
  197. {
  198. ss << pad << (*rev).first << "(Index: " << (*rev).second[last]-1 << ")" << std::endl;
  199. last = (*rev).first.c_str();
  200. }
  201. return ss.str();
  202. }
  203. /* read from both streams simult.*/
  204. template <typename T> void read(T& filla,T& fille) {
  205. if(1 != fread(&filla,sizeof(T),1,actual)) {
  206. throw compare_fails_exception("Unexpected EOF reading ACTUAL");
  207. }
  208. if(1 != fread(&fille,sizeof(T),1,expect)) {
  209. throw compare_fails_exception("Unexpected EOF reading EXPECT");
  210. }
  211. }
  212. private:
  213. FILE *const actual, *const expect;
  214. typedef std::map<std::string,unsigned int> PerChunkCounter;
  215. typedef std::pair<std::string,PerChunkCounter> HistoryEntry;
  216. typedef std::deque<HistoryEntry> ChunkHistory;
  217. ChunkHistory history;
  218. typedef std::stack<std::pair<uint32_t,uint32_t> > LengthStack;
  219. LengthStack lengths;
  220. uint32_t cnt_chunks;
  221. };
  222. ////////////////////////////////////////////////////////////////////////////////////////////////////
  223. /* specialization for aiString (it needs separate handling because its on-disk representation
  224. * differs from its binary representation in memory and can't be treated as an array of n T's.*/
  225. template <> void comparer_context :: read<aiString>(aiString& filla,aiString& fille) {
  226. uint32_t lena,lene;
  227. read(lena,lene);
  228. if(lena && 1 != fread(&filla.data,lena,1,actual)) {
  229. throw compare_fails_exception("Unexpected EOF reading ACTUAL");
  230. }
  231. if(lene && 1 != fread(&fille.data,lene,1,expect)) {
  232. throw compare_fails_exception("Unexpected EOF reading ACTUAL");
  233. }
  234. fille.data[fille.length=static_cast<unsigned int>(lene)] = '\0';
  235. filla.data[filla.length=static_cast<unsigned int>(lena)] = '\0';
  236. }
  237. ////////////////////////////////////////////////////////////////////////////////////////////////////
  238. /* Specialization for float, uses epsilon for comparisons*/
  239. template<> float comparer_context :: cmp<float>(const std::string& name)
  240. {
  241. float a,e,t;
  242. read(a,e);
  243. if((t=fabs(a-e)) > MY_FLT_EPSILON) {
  244. std::stringstream ss;
  245. failure((ss<< "Expected " << e << ", but actual is "
  246. << a << " (delta is " << t << ")", ss.str()),name);
  247. }
  248. return a;
  249. }
  250. ////////////////////////////////////////////////////////////////////////////////////////////////////
  251. /* Specialization for double, uses epsilon for comparisons*/
  252. template<> double comparer_context :: cmp<double>(const std::string& name)
  253. {
  254. double a,e,t;
  255. read(a,e);
  256. if((t=fabs(a-e)) > MY_DBL_EPSILON) {
  257. std::stringstream ss;
  258. failure((ss<< "Expected " << e << ", but actual is "
  259. << a << " (delta is " << t << ")", ss.str()),name);
  260. }
  261. return a;
  262. }
  263. ////////////////////////////////////////////////////////////////////////////////////////////////////
  264. /* Specialization for aiVector3D */
  265. template<> aiVector3D comparer_context :: cmp<aiVector3D >(const std::string& name)
  266. {
  267. const float x = cmp<float>(name+".x");
  268. const float y = cmp<float>(name+".y");
  269. const float z = cmp<float>(name+".z");
  270. return aiVector3D(x,y,z);
  271. }
  272. ////////////////////////////////////////////////////////////////////////////////////////////////////
  273. /* Specialization for aiColor4D */
  274. template<> aiColor4D comparer_context :: cmp<aiColor4D >(const std::string& name)
  275. {
  276. const float r = cmp<float>(name+".r");
  277. const float g = cmp<float>(name+".g");
  278. const float b = cmp<float>(name+".b");
  279. const float a = cmp<float>(name+".a");
  280. return aiColor4D(r,g,b,a);
  281. }
  282. ////////////////////////////////////////////////////////////////////////////////////////////////////
  283. /* Specialization for aiQuaternion */
  284. template<> aiQuaternion comparer_context :: cmp<aiQuaternion >(const std::string& name)
  285. {
  286. const float w = cmp<float>(name+".w");
  287. const float x = cmp<float>(name+".x");
  288. const float y = cmp<float>(name+".y");
  289. const float z = cmp<float>(name+".z");
  290. return aiQuaternion(w,x,y,z);
  291. }
  292. ////////////////////////////////////////////////////////////////////////////////////////////////////
  293. /* Specialization for aiQuatKey */
  294. template<> aiQuatKey comparer_context :: cmp<aiQuatKey >(const std::string& name)
  295. {
  296. const double mTime = cmp<double>(name+".mTime");
  297. const aiQuaternion mValue = cmp<aiQuaternion>(name+".mValue");
  298. return aiQuatKey(mTime,mValue);
  299. }
  300. ////////////////////////////////////////////////////////////////////////////////////////////////////
  301. /* Specialization for aiVectorKey */
  302. template<> aiVectorKey comparer_context :: cmp<aiVectorKey >(const std::string& name)
  303. {
  304. const double mTime = cmp<double>(name+".mTime");
  305. const aiVector3D mValue = cmp<aiVector3D>(name+".mValue");
  306. return aiVectorKey(mTime,mValue);
  307. }
  308. ////////////////////////////////////////////////////////////////////////////////////////////////////
  309. /* Specialization for aiMatrix4x4 */
  310. template<> aiMatrix4x4 comparer_context :: cmp<aiMatrix4x4 >(const std::string& name)
  311. {
  312. aiMatrix4x4 res;
  313. for(unsigned int i = 0; i < 4; ++i) {
  314. for(unsigned int j = 0; j < 4; ++j) {
  315. std::stringstream ss;
  316. res[i][j] = cmp<float>(name+(ss<<".m"<<i<<j,ss.str()));
  317. }
  318. }
  319. return res;
  320. }
  321. ////////////////////////////////////////////////////////////////////////////////////////////////////
  322. /* Specialization for aiVertexWeight */
  323. template<> aiVertexWeight comparer_context :: cmp<aiVertexWeight >(const std::string& name)
  324. {
  325. const unsigned int mVertexId = cmp<unsigned int>(name+".mVertexId");
  326. const float mWeight = cmp<float>(name+".mWeight");
  327. return aiVertexWeight(mVertexId,mWeight);
  328. }
  329. ////////////////////////////////////////////////////////////////////////////////////////////////////
  330. /// @class sliced_chunk_iterator
  331. ///
  332. /// @brief Helper to iterate easily through corresponding chunks of two dumps simultaneously.
  333. ///
  334. /// Not a *real* iterator, doesn't fully conform to the isocpp iterator spec
  335. ////////////////////////////////////////////////////////////////////////////////////////////////////
  336. class sliced_chunk_iterator {
  337. friend class sliced_chunk_reader;
  338. sliced_chunk_iterator(comparer_context& ctx, long end)
  339. : ctx(ctx)
  340. , endit(false)
  341. , next(std::numeric_limits<long>::max())
  342. , end(end)
  343. {
  344. load_next();
  345. }
  346. public:
  347. ~sliced_chunk_iterator() {
  348. fseek(ctx.get_actual(),end,SEEK_SET);
  349. fseek(ctx.get_expect(),end,SEEK_SET);
  350. }
  351. public:
  352. /* get current chunk head */
  353. typedef std::pair<uint32_t,uint32_t> Chunk;
  354. const Chunk& operator*() {
  355. return current;
  356. }
  357. /* get to next chunk head */
  358. const sliced_chunk_iterator& operator++() {
  359. cleanup();
  360. load_next();
  361. return *this;
  362. }
  363. /* */
  364. bool is_end() const {
  365. return endit;
  366. }
  367. private:
  368. /* get to the end of *this* chunk */
  369. void cleanup() {
  370. if(next != std::numeric_limits<long>::max()) {
  371. fseek(ctx.get_actual(),next,SEEK_SET);
  372. fseek(ctx.get_expect(),next,SEEK_SET);
  373. ctx.pop_length();
  374. }
  375. }
  376. /* advance to the next chunk */
  377. void load_next() {
  378. Chunk actual;
  379. size_t res=0;
  380. const long cur = ftell(ctx.get_expect());
  381. if(end-cur<8) {
  382. current = std::make_pair(0u,0u);
  383. endit = true;
  384. return;
  385. }
  386. res|=fread(&current.first,4,1,ctx.get_expect());
  387. res|=fread(&current.second,4,1,ctx.get_expect()) <<1u;
  388. res|=fread(&actual.first,4,1,ctx.get_actual()) <<2u;
  389. res|=fread(&actual.second,4,1,ctx.get_actual()) <<3u;
  390. if(res!=0xf) {
  391. ctx.failure("I/OError reading chunk head, dumps are not well-defined","<ChunkHead>");
  392. }
  393. if (current.first != actual.first) {
  394. std::stringstream ss;
  395. ctx.failure((ss
  396. <<"Chunk headers do not match. EXPECT: "
  397. << std::hex << current.first
  398. <<" ACTUAL: "
  399. << /*std::hex */actual.first,
  400. ss.str()),
  401. "<ChunkHead>");
  402. }
  403. if (current.first != actual.first) {
  404. std::stringstream ss;
  405. ctx.failure((ss
  406. <<"Chunk lenghts do not match. EXPECT: "
  407. <<current.second
  408. <<" ACTUAL: "
  409. << actual.second,
  410. ss.str()),
  411. "<ChunkHead>");
  412. }
  413. next = cur+current.second+8;
  414. ctx.push_length(current.second,cur+8);
  415. }
  416. comparer_context& ctx;
  417. Chunk current;
  418. bool endit;
  419. long next,end;
  420. };
  421. ////////////////////////////////////////////////////////////////////////////////////////////////////
  422. /// @class sliced_chunk_reader
  423. ///
  424. /// @brief Helper to iterate easily through corresponding chunks of two dumps simultaneously.
  425. ////////////////////////////////////////////////////////////////////////////////////////////////////
  426. class sliced_chunk_reader {
  427. public:
  428. //
  429. sliced_chunk_reader(comparer_context& ctx)
  430. : ctx(ctx)
  431. {}
  432. //
  433. ~sliced_chunk_reader() {
  434. }
  435. public:
  436. sliced_chunk_iterator begin() const {
  437. return sliced_chunk_iterator(ctx,ctx.get_latest_chunk_length()+
  438. ctx.get_latest_chunk_start());
  439. }
  440. private:
  441. comparer_context& ctx;
  442. };
  443. ////////////////////////////////////////////////////////////////////////////////////////////////////
  444. /// @class scoped_chunk
  445. ///
  446. /// @brief Utility to simplify usage of comparer_context.push_elem/pop_elem
  447. ////////////////////////////////////////////////////////////////////////////////////////////////////
  448. class scoped_chunk {
  449. public:
  450. //
  451. scoped_chunk(comparer_context& ctx,const char* msg)
  452. : ctx(ctx)
  453. {
  454. ctx.push_elem(msg);
  455. }
  456. //
  457. ~scoped_chunk()
  458. {
  459. ctx.pop_elem();
  460. }
  461. private:
  462. comparer_context& ctx;
  463. };
  464. ////////////////////////////////////////////////////////////////////////////////////////////////////
  465. void CompareOnTheFlyMaterialProperty(comparer_context& comp) {
  466. scoped_chunk chunk(comp,"aiMaterialProperty");
  467. comp.cmp<aiString>("mKey");
  468. comp.cmp<uint32_t>("mSemantic");
  469. comp.cmp<uint32_t>("mIndex");
  470. const uint32_t length = comp.cmp<uint32_t>("mDataLength");
  471. const aiPropertyTypeInfo type = static_cast<aiPropertyTypeInfo>(
  472. comp.cmp<uint32_t>("mType"));
  473. switch (type)
  474. {
  475. case aiPTI_Float:
  476. comp.cmp<float>(length/4,"mData");
  477. break;
  478. case aiPTI_String:
  479. comp.cmp<aiString>("mData");
  480. break;
  481. case aiPTI_Integer:
  482. comp.cmp<uint32_t>(length/4,"mData");
  483. break;
  484. case aiPTI_Buffer:
  485. comp.cmp<uint8_t>(length,"mData");
  486. break;
  487. default:
  488. break;
  489. };
  490. }
  491. ////////////////////////////////////////////////////////////////////////////////////////////////////
  492. void CompareOnTheFlyMaterial(comparer_context& comp) {
  493. scoped_chunk chunk(comp,"aiMaterial");
  494. comp.cmp<uint32_t>("aiMaterial::mNumProperties");
  495. sliced_chunk_reader reader(comp);
  496. for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
  497. if ((*it).first == ASSBIN_CHUNK_AIMATERIALPROPERTY) {
  498. CompareOnTheFlyMaterialProperty(comp);
  499. }
  500. }
  501. }
  502. ////////////////////////////////////////////////////////////////////////////////////////////////////
  503. void CompareOnTheFlyBone(comparer_context& comp) {
  504. scoped_chunk chunk(comp,"aiBone");
  505. comp.cmp<aiString>("mName");
  506. comp.cmp<uint32_t>("mNumWeights");
  507. comp.cmp<aiMatrix4x4>("mOffsetMatrix");
  508. comp.cmp_bounds<aiVertexWeight>("mWeights");
  509. }
  510. ////////////////////////////////////////////////////////////////////////////////////////////////////
  511. void CompareOnTheFlyNodeAnim(comparer_context& comp) {
  512. scoped_chunk chunk(comp,"aiNodeAnim");
  513. comp.cmp<aiString>("mNodeName");
  514. comp.cmp<uint32_t>("mNumPositionKeys");
  515. comp.cmp<uint32_t>("mNumRotationKeys");
  516. comp.cmp<uint32_t>("mNumScalingKeys");
  517. comp.cmp<uint32_t>("mPreState");
  518. comp.cmp<uint32_t>("mPostState");
  519. comp.cmp_bounds<aiVectorKey>("mPositionKeys");
  520. comp.cmp_bounds<aiQuatKey>("mRotationKeys");
  521. comp.cmp_bounds<aiVectorKey>("mScalingKeys");
  522. }
  523. ////////////////////////////////////////////////////////////////////////////////////////////////////
  524. void CompareOnTheFlyMesh(comparer_context& comp) {
  525. scoped_chunk chunk(comp,"aiMesh");
  526. comp.cmp<uint32_t>("mPrimitiveTypes");
  527. comp.cmp<uint32_t>("mNumVertices");
  528. const uint32_t nf = comp.cmp<uint32_t>("mNumFaces");
  529. comp.cmp<uint32_t>("mNumBones");
  530. comp.cmp<uint32_t>("mMaterialIndex");
  531. const uint32_t present = comp.cmp<uint32_t>("<vertex-components-present>");
  532. if(present & ASSBIN_MESH_HAS_POSITIONS) {
  533. comp.cmp_bounds<aiVector3D>("mVertices");
  534. }
  535. if(present & ASSBIN_MESH_HAS_NORMALS) {
  536. comp.cmp_bounds<aiVector3D>("mNormals");
  537. }
  538. if(present & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS) {
  539. comp.cmp_bounds<aiVector3D>("mTangents");
  540. comp.cmp_bounds<aiVector3D>("mBitangents");
  541. }
  542. for(unsigned int i = 0; present & ASSBIN_MESH_HAS_COLOR(i); ++i) {
  543. std::stringstream ss;
  544. comp.cmp_bounds<aiColor4D>((ss<<"mColors["<<i<<"]",ss.str()));
  545. }
  546. for(unsigned int i = 0; present & ASSBIN_MESH_HAS_TEXCOORD(i); ++i) {
  547. std::stringstream ss;
  548. comp.cmp<uint32_t>((ss<<"mNumUVComponents["<<i<<"]",ss.str()));
  549. comp.cmp_bounds<aiVector3D>((ss.clear(),ss<<"mTextureCoords["<<i<<"]",ss.str()));
  550. }
  551. for(unsigned int i = 0; i< ((nf+511)/512); ++i) {
  552. std::stringstream ss;
  553. comp.cmp<uint32_t>((ss<<"mFaces["<<i*512<<"-"<<std::min(static_cast<
  554. uint32_t>((i+1)*512),nf)<<"]",ss.str()));
  555. }
  556. sliced_chunk_reader reader(comp);
  557. for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
  558. if ((*it).first == ASSBIN_CHUNK_AIBONE) {
  559. CompareOnTheFlyBone(comp);
  560. }
  561. }
  562. }
  563. ////////////////////////////////////////////////////////////////////////////////////////////////////
  564. void CompareOnTheFlyCamera(comparer_context& comp) {
  565. scoped_chunk chunk(comp,"aiCamera");
  566. comp.cmp<aiString>("mName");
  567. comp.cmp<aiVector3D>("mPosition");
  568. comp.cmp<aiVector3D>("mLookAt");
  569. comp.cmp<aiVector3D>("mUp");
  570. comp.cmp<float>("mHorizontalFOV");
  571. comp.cmp<float>("mClipPlaneNear");
  572. comp.cmp<float>("mClipPlaneFar");
  573. comp.cmp<float>("mAspect");
  574. }
  575. ////////////////////////////////////////////////////////////////////////////////////////////////////
  576. void CompareOnTheFlyLight(comparer_context& comp) {
  577. scoped_chunk chunk(comp,"aiLight");
  578. comp.cmp<aiString>("mName");
  579. const aiLightSourceType type = static_cast<aiLightSourceType>(
  580. comp.cmp<uint32_t>("mType"));
  581. if(type==aiLightSource_DIRECTIONAL) {
  582. comp.cmp<float>("mAttenuationConstant");
  583. comp.cmp<float>("mAttenuationLinear");
  584. comp.cmp<float>("mAttenuationQuadratic");
  585. }
  586. comp.cmp<aiVector3D>("mColorDiffuse");
  587. comp.cmp<aiVector3D>("mColorSpecular");
  588. comp.cmp<aiVector3D>("mColorAmbient");
  589. if(type==aiLightSource_SPOT) {
  590. comp.cmp<float>("mAngleInnerCone");
  591. comp.cmp<float>("mAngleOuterCone");
  592. }
  593. }
  594. ////////////////////////////////////////////////////////////////////////////////////////////////////
  595. void CompareOnTheFlyAnimation(comparer_context& comp) {
  596. scoped_chunk chunk(comp,"aiAnimation");
  597. comp.cmp<aiString>("mName");
  598. comp.cmp<double>("mDuration");
  599. comp.cmp<double>("mTicksPerSecond");
  600. comp.cmp<uint32_t>("mNumChannels");
  601. sliced_chunk_reader reader(comp);
  602. for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
  603. if ((*it).first == ASSBIN_CHUNK_AINODEANIM) {
  604. CompareOnTheFlyNodeAnim(comp);
  605. }
  606. }
  607. }
  608. ////////////////////////////////////////////////////////////////////////////////////////////////////
  609. void CompareOnTheFlyTexture(comparer_context& comp) {
  610. scoped_chunk chunk(comp,"aiTexture");
  611. const uint32_t w = comp.cmp<uint32_t>("mWidth");
  612. const uint32_t h = comp.cmp<uint32_t>("mHeight");
  613. (void)w; (void)h;
  614. comp.cmp<char>("achFormatHint[0]");
  615. comp.cmp<char>("achFormatHint[1]");
  616. comp.cmp<char>("achFormatHint[2]");
  617. comp.cmp<char>("achFormatHint[3]");
  618. }
  619. ////////////////////////////////////////////////////////////////////////////////////////////////////
  620. void CompareOnTheFlyNode(comparer_context& comp) {
  621. scoped_chunk chunk(comp,"aiNode");
  622. comp.cmp<aiString>("mName");
  623. comp.cmp<aiMatrix4x4>("mTransformation");
  624. comp.cmp<uint32_t>("mNumChildren");
  625. comp.cmp<uint32_t>(comp.cmp<uint32_t>("mNumMeshes"),"mMeshes");
  626. sliced_chunk_reader reader(comp);
  627. for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
  628. if ((*it).first == ASSBIN_CHUNK_AINODE) {
  629. CompareOnTheFlyNode(comp);
  630. }
  631. }
  632. }
  633. ////////////////////////////////////////////////////////////////////////////////////////////////////
  634. void CompareOnTheFlyScene(comparer_context& comp) {
  635. scoped_chunk chunk(comp,"aiScene");
  636. comp.cmp<uint32_t>("mFlags");
  637. comp.cmp<uint32_t>("mNumMeshes");
  638. comp.cmp<uint32_t>("mNumMaterials");
  639. comp.cmp<uint32_t>("mNumAnimations");
  640. comp.cmp<uint32_t>("mNumTextures");
  641. comp.cmp<uint32_t>("mNumLights");
  642. comp.cmp<uint32_t>("mNumCameras");
  643. sliced_chunk_reader reader(comp);
  644. for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
  645. if ((*it).first == ASSBIN_CHUNK_AIMATERIAL) {
  646. CompareOnTheFlyMaterial(comp);
  647. }
  648. else if ((*it).first == ASSBIN_CHUNK_AITEXTURE) {
  649. CompareOnTheFlyTexture(comp);
  650. }
  651. else if ((*it).first == ASSBIN_CHUNK_AIMESH) {
  652. CompareOnTheFlyMesh(comp);
  653. }
  654. else if ((*it).first == ASSBIN_CHUNK_AIANIMATION) {
  655. CompareOnTheFlyAnimation(comp);
  656. }
  657. else if ((*it).first == ASSBIN_CHUNK_AICAMERA) {
  658. CompareOnTheFlyCamera(comp);
  659. }
  660. else if ((*it).first == ASSBIN_CHUNK_AILIGHT) {
  661. CompareOnTheFlyLight(comp);
  662. }
  663. else if ((*it).first == ASSBIN_CHUNK_AINODE) {
  664. CompareOnTheFlyNode(comp);
  665. }
  666. }
  667. }
  668. ////////////////////////////////////////////////////////////////////////////////////////////////////
  669. void CompareOnTheFly(comparer_context& comp)
  670. {
  671. sliced_chunk_reader reader(comp);
  672. for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
  673. if ((*it).first == ASSBIN_CHUNK_AISCENE) {
  674. CompareOnTheFlyScene(comp);
  675. break;
  676. }
  677. }
  678. }
  679. ////////////////////////////////////////////////////////////////////////////////////////////////////
  680. void CheckHeader(comparer_context& comp)
  681. {
  682. fseek(comp.get_actual(),ASSBIN_HEADER_LENGTH,SEEK_CUR);
  683. fseek(comp.get_expect(),ASSBIN_HEADER_LENGTH,SEEK_CUR);
  684. }
  685. ////////////////////////////////////////////////////////////////////////////////////////////////////
  686. int Assimp_CompareDump (const char* const* params, unsigned int num)
  687. {
  688. // --help
  689. if ((num == 1 && !strcmp( params[0], "-h")) || !strcmp( params[0], "--help") || !strcmp( params[0], "-?") ) {
  690. printf("%s",AICMD_MSG_CMPDUMP_HELP);
  691. return 0;
  692. }
  693. // assimp cmpdump actual expected
  694. if (num < 1) {
  695. std::cout << "assimp cmpdump: Invalid number of arguments. "
  696. "See \'assimp cmpdump --help\'\r\n" << std::endl;
  697. return 1;
  698. }
  699. if(!strcmp(params[0],params[1])) {
  700. std::cout << "assimp cmpdump: same file, same content." << std::endl;
  701. return 0;
  702. }
  703. FILE* actual = fopen(params[0],"rb"), *expected = fopen(params[1],"rb");
  704. if (!actual) {
  705. std::cout << "assimp cmpdump: Failure reading ACTUAL data from " <<
  706. params[0] << std::endl;
  707. return -5;
  708. }
  709. if (!expected) {
  710. std::cout << "assimp cmpdump: Failure reading EXPECT data from " <<
  711. params[1] << std::endl;
  712. return -6;
  713. }
  714. comparer_context comp(actual,expected);
  715. try {
  716. CheckHeader(comp);
  717. CompareOnTheFly(comp);
  718. }
  719. catch(const compare_fails_exception& ex) {
  720. printf("%s",ex.what());
  721. return -1;
  722. }
  723. catch(...) {
  724. // we don't bother checking too rigourously here, so
  725. // we might end up here ...
  726. std::cout << "Unknown failure, are the input files well-defined?";
  727. return -3;
  728. }
  729. std::cout << "Success (totally " << std::dec << comp.get_num_chunks() <<
  730. " chunks)" << std::endl;
  731. return 0;
  732. }