- Update XML output written by assimp dump. cameras & lights missing, document scheme as well. No guarantees that I won't change it in future without further notice (currently WIP, format to be freezed with our next release).

- Add dump comparison tool to assimp_cmd. It serves as the workhorse of the regression suite.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@598 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2010-03-13 00:04:21 +00:00
parent 30ca88e782
commit ff53e84749
6 changed files with 1900 additions and 650 deletions

View File

@ -1 +1,894 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file CompareDump.cpp
* @brief Implementation of the 'assimp cmpdmp', which compares
* two model dumps for equality. It plays an important role
* in the regression test suite.
*/
#include "Main.h"
const char* AICMD_MSG_CMPDUMP_HELP =
"assimp cmpdump <actual> <expected>\n"
"\tCompare two short dumps produced with \'assimp dump <..> -s\' for equality.\n"
;
#include "../../code/assbin_chunks.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
#include "generic_inserter.hpp"
// get << for aiString
template <typename char_t, typename traits_t>
void mysprint(std::basic_ostream<char_t, traits_t>& os, const aiString& vec) {
os << "[length: \'" << std::dec << vec.length << "\' content: \'" << vec.data << "\']";
}
template <typename char_t, typename traits_t>
std::basic_ostream<char_t, traits_t>& operator<< (std::basic_ostream<char_t, traits_t>& os, const aiString& vec) {
return generic_inserter(mysprint<char_t,traits_t>, os, vec);
}
class sliced_chunk_iterator;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class compare_fails_exception
///
/// @brief Sentinel exception to return quickly from deeply nested control paths
////////////////////////////////////////////////////////////////////////////////////////////////////
class compare_fails_exception : public virtual std::exception {
public:
enum {MAX_ERR_LEN = 4096};
/* public c'tors */
compare_fails_exception(const char* msg) {
strncpy(mywhat,msg,MAX_ERR_LEN-1);
strcat(mywhat,"\n");
}
/* public member functions */
const char* what() const throw() {
return mywhat;
}
private:
char mywhat[MAX_ERR_LEN+1];
};
#define MY_FLT_EPSILON 1e-1f
#define MY_DBL_EPSILON 1e-1
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class comparer_context
///
/// @brief Record our way through the files to be compared and dump useful information if we fail.
////////////////////////////////////////////////////////////////////////////////////////////////////
class comparer_context {
friend class sliced_chunk_iterator;
public:
/* construct given two file handles to compare */
comparer_context(FILE* actual,FILE* expect)
: actual(actual)
, expect(expect)
, cnt_chunks(0)
{
assert(actual);
assert(expect);
fseek(actual,0,SEEK_END);
lengths.push(std::make_pair(static_cast<uint32_t>(ftell(actual)),0));
fseek(actual,0,SEEK_SET);
history.push_back(HistoryEntry("---",PerChunkCounter()));
}
public:
/* set new scope */
void push_elem(const char* msg) {
const std::string s = msg;
PerChunkCounter::const_iterator it = history.back().second.find(s);
if(it != history.back().second.end()) {
++history.back().second[s];
}
else history.back().second[s] = 1;
history.push_back(HistoryEntry(s,PerChunkCounter()));
}
/* leave current scope */
void pop_elem() {
assert(history.size());
history.pop_back();
}
/* push current chunk length and start offset on top of stack */
void push_length(uint32_t nl, uint32_t start) {
lengths.push(std::make_pair(nl,start));
++cnt_chunks;
}
/* pop the chunk length stack */
void pop_length() {
assert(lengths.size());
lengths.pop();
}
/* access the current chunk length */
uint32_t get_latest_chunk_length() {
assert(lengths.size());
return lengths.top().first;
}
/* access the current chunk start offset */
uint32_t get_latest_chunk_start() {
assert(lengths.size());
return lengths.top().second;
}
/* total number of chunk headers passed so far*/
uint32_t get_num_chunks() {
return cnt_chunks;
}
/* get ACTUAL file desc. != NULL */
FILE* get_actual() const {
return actual;
}
/* get EXPECT file desc. != NULL */
FILE* get_expect() const {
return expect;
}
/* compare next T from both streams, name occurs in error messages */
template<typename T> T cmp(const std::string& name) {
T a,e;
read(a,e);
if(a != e) {
std::stringstream ss;
failure((ss<< "Expected " << e << ", but actual is " << a,
ss.str()),name);
}
// std::cout << name << " " << std::hex << a << std::endl;
return a;
}
/* compare next num T's from both streams, name occurs in error messages */
template<typename T> void cmp(size_t num,const std::string& name) {
for(size_t n = 0; n < num; ++n) {
std::stringstream ss;
cmp<T>((ss<<name<<"["<<n<<"]",ss.str()));
// std::cout << name << " " << std::hex << a << std::endl;
}
}
/* Bounds of an aiVector3D array (separate function
* because partial specializations of member functions are illegal--)*/
template<typename T> void cmp_bounds(const std::string& name) {
cmp<T> (name+".<minimum-value>");
cmp<T> (name+".<maximum-value>");
}
private:
/* Report failure */
void failure(const std::string& err, const std::string& name) {
std::stringstream ss;
throw compare_fails_exception((ss
<< "Files are different at "
<< history.back().first
<< "."
<< name
<< ".\nError is: "
<< err
<< ".\nCurrent position in scene hierarchy is "
<< print_hierarchy(),ss.str().c_str()
));
}
/** print our 'stack' */
std::string print_hierarchy() {
std::stringstream ss;
ss << std::endl;
const char* last = history.back().first.c_str();
std::string pad;
for(ChunkHistory::reverse_iterator rev = ++history.rbegin(),
end = history.rend(); rev < end; ++rev, pad += " ")
{
ss << pad << (*rev).first << "(Index: " << (*rev).second[last]-1 << ")" << std::endl;
last = (*rev).first.c_str();
}
return ss.str();
}
/* read from both streams simult.*/
template <typename T> void read(T& filla,T& fille) {
if(1 != fread(&filla,sizeof(T),1,actual)) {
throw compare_fails_exception("Unexpected EOF reading ACTUAL");
}
if(1 != fread(&fille,sizeof(T),1,expect)) {
throw compare_fails_exception("Unexpected EOF reading EXPECT");
}
}
private:
FILE *const actual, *const expect;
typedef std::map<std::string,unsigned int> PerChunkCounter;
typedef std::pair<std::string,PerChunkCounter> HistoryEntry;
typedef std::deque<HistoryEntry> ChunkHistory;
ChunkHistory history;
typedef std::stack<std::pair<uint32_t,uint32_t> > LengthStack;
LengthStack lengths;
uint32_t cnt_chunks;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/* specialization for aiString (it needs separate handling because its on-disk representation
* differs from its binary representation in memory and can't be treated as an array of n T's.*/
template <> void comparer_context :: read<aiString>(aiString& filla,aiString& fille) {
uint32_t lena,lene;
read(lena,lene);
if(lena && 1 != fread(&filla.data,lena,1,actual)) {
throw compare_fails_exception("Unexpected EOF reading ACTUAL");
}
if(lene && 1 != fread(&fille.data,lene,1,expect)) {
throw compare_fails_exception("Unexpected EOF reading ACTUAL");
}
fille.data[fille.length=static_cast<unsigned int>(lene)] = '\0';
filla.data[filla.length=static_cast<unsigned int>(lena)] = '\0';
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/* Specialization for float, uses epsilon for comparisons*/
template<> float comparer_context :: cmp<float>(const std::string& name)
{
float a,e,t;
read(a,e);
if((t=fabs(a-e)) > MY_FLT_EPSILON) {
std::stringstream ss;
failure((ss<< "Expected " << e << ", but actual is "
<< a << " (delta is " << t << ")", ss.str()),name);
}
return a;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/* Specialization for double, uses epsilon for comparisons*/
template<> double comparer_context :: cmp<double>(const std::string& name)
{
double a,e,t;
read(a,e);
if((t=fabs(a-e)) > MY_DBL_EPSILON) {
std::stringstream ss;
failure((ss<< "Expected " << e << ", but actual is "
<< a << " (delta is " << t << ")", ss.str()),name);
}
return a;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/* Specialization for aiVector3D */
template<> aiVector3D comparer_context :: cmp<aiVector3D >(const std::string& name)
{
const float x = cmp<float>(name+".x");
const float y = cmp<float>(name+".y");
const float z = cmp<float>(name+".z");
return aiVector3D(x,y,z);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/* Specialization for aiColor4D */
template<> aiColor4D comparer_context :: cmp<aiColor4D >(const std::string& name)
{
const float r = cmp<float>(name+".r");
const float g = cmp<float>(name+".g");
const float b = cmp<float>(name+".b");
const float a = cmp<float>(name+".a");
return aiColor4D(r,g,b,a);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/* Specialization for aiQuaternion */
template<> aiQuaternion comparer_context :: cmp<aiQuaternion >(const std::string& name)
{
const float w = cmp<float>(name+".w");
const float x = cmp<float>(name+".x");
const float y = cmp<float>(name+".y");
const float z = cmp<float>(name+".z");
return aiQuaternion(w,x,y,z);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/* Specialization for aiQuatKey */
template<> aiQuatKey comparer_context :: cmp<aiQuatKey >(const std::string& name)
{
const double mTime = cmp<double>(name+".mTime");
const aiQuaternion mValue = cmp<aiQuaternion>(name+".mValue");
return aiQuatKey(mTime,mValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/* Specialization for aiVectorKey */
template<> aiVectorKey comparer_context :: cmp<aiVectorKey >(const std::string& name)
{
const double mTime = cmp<double>(name+".mTime");
const aiVector3D mValue = cmp<aiVector3D>(name+".mValue");
return aiVectorKey(mTime,mValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/* Specialization for aiMatrix4x4 */
template<> aiMatrix4x4 comparer_context :: cmp<aiMatrix4x4 >(const std::string& name)
{
aiMatrix4x4 res;
for(unsigned int i = 0; i < 4; ++i) {
for(unsigned int j = 0; j < 4; ++j) {
std::stringstream ss;
res[i][j] = cmp<float>(name+(ss<<".m"<<i<<j,ss.str()));
}
}
return res;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/* Specialization for aiVertexWeight */
template<> aiVertexWeight comparer_context :: cmp<aiVertexWeight >(const std::string& name)
{
const unsigned int mVertexId = cmp<unsigned int>(name+".mVertexId");
const float mWeight = cmp<float>(name+".mWeight");
return aiVertexWeight(mVertexId,mWeight);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class sliced_chunk_iterator
///
/// @brief Helper to iterate easily through corresponding chunks of two dumps simultaneously.
///
/// Not a *real* iterator, doesn't fully conform to the isocpp iterator spec
////////////////////////////////////////////////////////////////////////////////////////////////////
class sliced_chunk_iterator {
friend class sliced_chunk_reader;
sliced_chunk_iterator(comparer_context& ctx, long end)
: ctx(ctx)
, endit(false)
, next(std::numeric_limits<long>::max())
, end(end)
{
load_next();
}
public:
~sliced_chunk_iterator() {
fseek(ctx.get_actual(),end,SEEK_SET);
fseek(ctx.get_expect(),end,SEEK_SET);
}
public:
/* get current chunk head */
typedef std::pair<uint32_t,uint32_t> Chunk;
const Chunk& operator*() {
return current;
}
/* get to next chunk head */
const sliced_chunk_iterator& operator++() {
cleanup();
load_next();
return *this;
}
/* */
bool is_end() const {
return endit;
}
private:
/* get to the end of *this* chunk */
void cleanup() {
if(next != std::numeric_limits<long>::max()) {
fseek(ctx.get_actual(),next,SEEK_SET);
fseek(ctx.get_expect(),next,SEEK_SET);
ctx.pop_length();
}
}
/* advance to the next chunk */
void load_next() {
Chunk actual;
size_t res=0;
const long cur = ftell(ctx.get_expect());
if(end-cur<8) {
current = std::make_pair(0u,0u);
endit = true;
return;
}
res|=fread(&current.first,4,1,ctx.get_expect());
res|=fread(&current.second,4,1,ctx.get_expect()) <<1u;
res|=fread(&actual.first,4,1,ctx.get_actual()) <<2u;
res|=fread(&actual.second,4,1,ctx.get_actual()) <<3u;
if(res!=0xf) {
ctx.failure("I/OError reading chunk head, dumps are not well-defined","<ChunkHead>");
}
if (current.first != actual.first) {
std::stringstream ss;
ctx.failure((ss
<<"Chunk headers do not match. EXPECT: "
<< std::hex << current.first
<<" ACTUAL: "
<< /*std::hex */actual.first,
ss.str()),
"<ChunkHead>");
}
if (current.first != actual.first) {
std::stringstream ss;
ctx.failure((ss
<<"Chunk lenghts do not match. EXPECT: "
<<current.second
<<" ACTUAL: "
<< actual.second,
ss.str()),
"<ChunkHead>");
}
next = cur+current.second+8;
ctx.push_length(current.second,cur+8);
}
comparer_context& ctx;
Chunk current;
bool endit;
long next,end;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class sliced_chunk_reader
///
/// @brief Helper to iterate easily through corresponding chunks of two dumps simultaneously.
////////////////////////////////////////////////////////////////////////////////////////////////////
class sliced_chunk_reader {
public:
//
sliced_chunk_reader(comparer_context& ctx)
: ctx(ctx)
{}
//
~sliced_chunk_reader() {
}
public:
sliced_chunk_iterator begin() const {
return sliced_chunk_iterator(ctx,ctx.get_latest_chunk_length()+
ctx.get_latest_chunk_start());
}
private:
comparer_context& ctx;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class scoped_chunk
///
/// @brief Utility to simplify usage of comparer_context.push_elem/pop_elem
////////////////////////////////////////////////////////////////////////////////////////////////////
class scoped_chunk {
public:
//
scoped_chunk(comparer_context& ctx,const char* msg)
: ctx(ctx)
{
ctx.push_elem(msg);
}
//
~scoped_chunk()
{
ctx.pop_elem();
}
private:
comparer_context& ctx;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyMaterialProperty(comparer_context& comp) {
scoped_chunk chunk(comp,"aiMaterialProperty");
comp.cmp<aiString>("mKey");
comp.cmp<uint32_t>("mSemantic");
comp.cmp<uint32_t>("mIndex");
const uint32_t length = comp.cmp<uint32_t>("mDataLength");
const aiPropertyTypeInfo type = static_cast<aiPropertyTypeInfo>(
comp.cmp<uint32_t>("mType"));
switch (type)
{
case aiPTI_Float:
comp.cmp<float>(length/4,"mData");
break;
case aiPTI_String:
comp.cmp<aiString>("mData");
break;
case aiPTI_Integer:
comp.cmp<uint32_t>(length/4,"mData");
break;
case aiPTI_Buffer:
comp.cmp<uint8_t>(length,"mData");
break;
};
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyMaterial(comparer_context& comp) {
scoped_chunk chunk(comp,"aiMaterial");
comp.cmp<uint32_t>("aiMaterial::mNumProperties");
sliced_chunk_reader reader(comp);
for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
if ((*it).first == ASSBIN_CHUNK_AIMATERIALPROPERTY) {
CompareOnTheFlyMaterialProperty(comp);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyBone(comparer_context& comp) {
scoped_chunk chunk(comp,"aiBone");
comp.cmp<aiString>("mName");
comp.cmp<uint32_t>("mNumWeights");
comp.cmp<aiMatrix4x4>("mOffsetMatrix");
comp.cmp_bounds<aiVertexWeight>("mWeights");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyNodeAnim(comparer_context& comp) {
scoped_chunk chunk(comp,"aiNodeAnim");
comp.cmp<aiString>("mNodeName");
comp.cmp<uint32_t>("mNumPositionKeys");
comp.cmp<uint32_t>("mNumRotationKeys");
comp.cmp<uint32_t>("mNumScalingKeys");
comp.cmp<uint32_t>("mPreState");
comp.cmp<uint32_t>("mPostState");
comp.cmp_bounds<aiVector3D>("mPositionKeys");
comp.cmp_bounds<aiVector3D>("mRotationKeys");
comp.cmp_bounds<aiVector3D>("mScalingKeys");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyMesh(comparer_context& comp) {
scoped_chunk chunk(comp,"aiMesh");
comp.cmp<uint32_t>("mPrimitiveTypes");
comp.cmp<uint32_t>("mNumVertices");
const uint32_t nf = comp.cmp<uint32_t>("mNumFaces");
comp.cmp<uint32_t>("mNumBones");
comp.cmp<uint32_t>("mMaterialIndex");
const uint32_t present = comp.cmp<uint32_t>("<vertex-components-present>");
if(present & ASSBIN_MESH_HAS_POSITIONS) {
comp.cmp_bounds<aiVector3D>("mVertices");
}
if(present & ASSBIN_MESH_HAS_NORMALS) {
comp.cmp_bounds<aiVector3D>("mNormals");
}
if(present & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS) {
comp.cmp_bounds<aiVector3D>("mTangents");
comp.cmp_bounds<aiVector3D>("mBitangents");
}
for(unsigned int i = 0; present & ASSBIN_MESH_HAS_COLOR(i); ++i) {
std::stringstream ss;
comp.cmp_bounds<aiColor4D>((ss<<"mColors["<<i<<"]",ss.str()));
}
for(unsigned int i = 0; present & ASSBIN_MESH_HAS_TEXCOORD(i); ++i) {
std::stringstream ss;
comp.cmp<uint32_t>((ss<<"mNumUVComponents["<<i<<"]",ss.str()));
comp.cmp_bounds<aiVector3D>((ss.clear(),ss<<"mTextureCoords["<<i<<"]",ss.str()));
}
for(unsigned int i = 0; i< ((nf+511)/512); ++i) {
std::stringstream ss;
comp.cmp<uint32_t>((ss<<"mFaces["<<i*512<<"-"<<std::min(static_cast<
uint32_t>((i+1)*512),nf)<<"]",ss.str()));
}
sliced_chunk_reader reader(comp);
for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
if ((*it).first == ASSBIN_CHUNK_AIBONE) {
CompareOnTheFlyBone(comp);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyCamera(comparer_context& comp) {
scoped_chunk chunk(comp,"aiCamera");
comp.cmp<aiString>("mName");
comp.cmp<aiVector3D>("mPosition");
comp.cmp<aiVector3D>("mLookAt");
comp.cmp<aiVector3D>("mUp");
comp.cmp<float>("mHorizontalFOV");
comp.cmp<float>("mClipPlaneNear");
comp.cmp<float>("mClipPlaneFar");
comp.cmp<float>("mAspect");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyLight(comparer_context& comp) {
scoped_chunk chunk(comp,"aiLight");
comp.cmp<aiString>("mName");
const aiLightSourceType type = static_cast<aiLightSourceType>(
comp.cmp<uint32_t>("mType"));
if(type==aiLightSource_DIRECTIONAL) {
comp.cmp<float>("mAttenuationConstant");
comp.cmp<float>("mAttenuationLinear");
comp.cmp<float>("mAttenuationQuadratic");
}
comp.cmp<aiVector3D>("mColorDiffuse");
comp.cmp<aiVector3D>("mColorSpecular");
comp.cmp<aiVector3D>("mColorAmbient");
if(type==aiLightSource_SPOT) {
comp.cmp<float>("mAngleInnerCone");
comp.cmp<float>("mAngleOuterCone");
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyAnimation(comparer_context& comp) {
scoped_chunk chunk(comp,"aiAnimation");
comp.cmp<aiString>("mName");
comp.cmp<double>("mDuration");
comp.cmp<double>("mTicksPerSecond");
comp.cmp<uint32_t>("mNumChannels");
sliced_chunk_reader reader(comp);
for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
if ((*it).first == ASSBIN_CHUNK_AINODEANIM) {
CompareOnTheFlyNodeAnim(comp);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyTexture(comparer_context& comp) {
scoped_chunk chunk(comp,"aiTexture");
const uint32_t w = comp.cmp<uint32_t>("mWidth");
const uint32_t h = comp.cmp<uint32_t>("mHeight");
comp.cmp<char>("achFormatHint[0]");
comp.cmp<char>("achFormatHint[1]");
comp.cmp<char>("achFormatHint[2]");
comp.cmp<char>("achFormatHint[3]");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyNode(comparer_context& comp) {
scoped_chunk chunk(comp,"aiNode");
comp.cmp<aiString>("mName");
comp.cmp<aiMatrix4x4>("mTransformation");
comp.cmp<uint32_t>("mNumChildren");
comp.cmp<uint32_t>(comp.cmp<uint32_t>("mNumMeshes"),"mMeshes");
sliced_chunk_reader reader(comp);
for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
if ((*it).first == ASSBIN_CHUNK_AINODE) {
CompareOnTheFlyNode(comp);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFlyScene(comparer_context& comp) {
scoped_chunk chunk(comp,"aiScene");
comp.cmp<uint32_t>("mFlags");
comp.cmp<uint32_t>("mNumMeshes");
comp.cmp<uint32_t>("mNumMaterials");
comp.cmp<uint32_t>("mNumAnimations");
comp.cmp<uint32_t>("mNumTextures");
comp.cmp<uint32_t>("mNumLights");
comp.cmp<uint32_t>("mNumCameras");
sliced_chunk_reader reader(comp);
for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
if ((*it).first == ASSBIN_CHUNK_AIMATERIAL) {
CompareOnTheFlyMaterial(comp);
}
else if ((*it).first == ASSBIN_CHUNK_AITEXTURE) {
CompareOnTheFlyTexture(comp);
}
else if ((*it).first == ASSBIN_CHUNK_AIMESH) {
CompareOnTheFlyMesh(comp);
}
else if ((*it).first == ASSBIN_CHUNK_AIANIMATION) {
CompareOnTheFlyAnimation(comp);
}
else if ((*it).first == ASSBIN_CHUNK_AICAMERA) {
CompareOnTheFlyCamera(comp);
}
else if ((*it).first == ASSBIN_CHUNK_AILIGHT) {
CompareOnTheFlyLight(comp);
}
else if ((*it).first == ASSBIN_CHUNK_AINODE) {
CompareOnTheFlyNode(comp);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CompareOnTheFly(comparer_context& comp)
{
sliced_chunk_reader reader(comp);
for(sliced_chunk_iterator it = reader.begin(); !it.is_end(); ++it) {
if ((*it).first == ASSBIN_CHUNK_AISCENE) {
CompareOnTheFlyScene(comp);
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CheckHeader(comparer_context& comp)
{
fseek(comp.get_actual(),ASSBIN_HEADER_LENGTH,SEEK_CUR);
fseek(comp.get_expect(),ASSBIN_HEADER_LENGTH,SEEK_CUR);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
int Assimp_CompareDump (const char* const* params, unsigned int num)
{
// --help
if (num == 1 && !strcmp( params[0], "-h") || !strcmp( params[0], "--help") || !strcmp( params[0], "-?") ) {
printf("%s",AICMD_MSG_CMPDUMP_HELP);
return 0;
}
// assimp cmpdump actual expected
if (num < 1) {
std::cout << "assimp cmpdump: Invalid number of arguments. "
"See \'assimp cmpdump --help\'\r\n" << std::endl;
return 1;
}
if(!strcmp(params[0],params[1])) {
std::cout << "assimp cmpdump: same file, same content." << std::endl;
return 0;
}
FILE* actual = fopen(params[0],"rb"), *expected = fopen(params[1],"rb");
if (!actual) {
std::cout << "assimp cmpdump: Failure reading ACTUAL data from " <<
params[0] << std::endl;
return -5;
}
if (!expected) {
std::cout << "assimp cmpdump: Failure reading EXPECT data from " <<
params[1] << std::endl;
return -6;
}
comparer_context comp(actual,expected);
try {
CheckHeader(comp);
CompareOnTheFly(comp);
}
catch(const compare_fails_exception& ex) {
printf("%s",ex.what());
return -1;
}
catch(...) {
// we don't bother checking too rigourously here, so
// we might end up here ...
std::cout << "Unknown failure, are the input files well-defined?";
return -3;
}
std::cout << "Success (totally " << std::dec << comp.get_num_chunks() <<
" chunks)" << std::endl;
return 0;
}

View File

@ -46,11 +46,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Main.h"
const char* AICMD_MSG_DUMP_HELP_E =
"todo assimp extract help";
"assimp extract <model> [<out>] [-t<n>] [-f<fmt>] [-ba] [-s] [common parameters]\n"
"\t -ba Writes BMP's with alpha channel\n"
"\t -t<n> Zero-based index of the texture to be extracted \n"
"\t -f<f> Specify the file format if <out> is ommitted \n"
"\t[See the assimp_cmd docs for a full list of all common parameters] \n"
"\t -cfast Fast post processing preset, runs just a few important steps \n"
"\t -cdefault Default post processing: runs all recommended steps\n"
"\t -cfull Fires almost all post processing steps \n"
;
#define AI_EXTRACT_WRITE_BMP_ALPHA 0x1
#include "Compiler/pushpack1.h"
// -----------------------------------------------------------------------------------
@ -136,7 +142,7 @@ int SaveAsBMP (FILE* file, const aiTexel* data, unsigned int width, unsigned int
header.bfSize = header.bfOffBits+width*height*numc;
header.bfReserved1 = header.bfReserved2 = 0;
::fwrite(&header,sizeof(BITMAPFILEHEADER),1,file);
fwrite(&header,sizeof(BITMAPFILEHEADER),1,file);
BITMAPINFOHEADER info;
info.biSize = 40;
@ -151,13 +157,13 @@ int SaveAsBMP (FILE* file, const aiTexel* data, unsigned int width, unsigned int
info.biClrUsed = 0;
info.biClrImportant = 0;
::fwrite(&info,sizeof(BITMAPINFOHEADER),1,file);
fwrite(&info,sizeof(BITMAPINFOHEADER),1,file);
unsigned char* temp = buffer+info.biSizeImage;
const unsigned int row = width*numc;
for (int y = 0; temp -= row,y < info.biHeight;++y) {
::fwrite(temp,row,1,file);
fwrite(temp,row,1,file);
}
// delete the buffer
@ -179,11 +185,11 @@ int SaveAsTGA (FILE* file, const aiTexel* data, unsigned int width, unsigned int
head.descriptor |= (1u<<5);
head.imagetype = 2; // actually it's RGBA
::fwrite(&head,sizeof(TGA_HEADER),1,file);
fwrite(&head,sizeof(TGA_HEADER),1,file);
for (unsigned int y = 0; y < height; ++y) {
for (unsigned int x = 0; x < width; ++x) {
::fwrite(data + y*width+x,4,1,file);
fwrite(data + y*width+x,4,1,file);
}
}
@ -212,7 +218,7 @@ int DoExport(const aiTexture* tx, FILE* p, const std::string& extension,
// -----------------------------------------------------------------------------------
// Implementation of the assimp extract utility
int Assimp_Extract (const char** params, unsigned int num)
int Assimp_Extract (const char* const* params, unsigned int num)
{
if (num < 1) {
printf("assimp extract: Invalid number of arguments. See \'assimp extract --help\'\n");
@ -220,7 +226,7 @@ int Assimp_Extract (const char** params, unsigned int num)
}
// --help
if (!::strcmp( params[0], "-h") || !::strcmp( params[0], "--help") || !::strcmp( params[0], "-?") ) {
if (!strcmp( params[0], "-h") || !strcmp( params[0], "--help") || !strcmp( params[0], "-?") ) {
printf("%s",AICMD_MSG_DUMP_HELP_E);
return 0;
}
@ -246,28 +252,30 @@ int Assimp_Extract (const char** params, unsigned int num)
for (unsigned int i = (out[0] == '-' ? 1 : 2); i < num;++i) {
if (!params[i])continue;
if (!::strncmp( params[i], "-f",2)) {
if (!strncmp( params[i], "-f",2)) {
extension = std::string(params[i]+2);
}
else if ( !::strncmp( params[i], "--format=",9)) {
else if ( !strncmp( params[i], "--format=",9)) {
extension = std::string(params[i]+9);
}
else if ( !::strcmp( params[i], "--nosuffix") || !::strcmp(params[i],"-s")) {
else if ( !strcmp( params[i], "--nosuffix") || !strcmp(params[i],"-s")) {
nosuffix = true;
}
else if ( !::strncmp( params[i], "--texture=",10)) {
else if ( !strncmp( params[i], "--texture=",10)) {
texIdx = ::strtol10(params[i]+10);
}
else if ( !::strncmp( params[i], "-t",2)) {
else if ( !strncmp( params[i], "-t",2)) {
texIdx = ::strtol10(params[i]+2);
}
else if ( !::strcmp( params[i], "-ba") || !::strcmp( params[i], "--bmp-with-alpha")) {
else if ( !strcmp( params[i], "-ba") || !strcmp( params[i], "--bmp-with-alpha")) {
flags |= AI_EXTRACT_WRITE_BMP_ALPHA;
}
#if 0
else {
printf("Unknown parameter: %s\n",params[i]);
return 10;
}
#endif
}
for (std::string::iterator it = extension.begin();it != extension.end();++it)
*it = ::tolower(*it);
@ -310,10 +318,10 @@ int Assimp_Extract (const char** params, unsigned int num)
}
// now write all output textures
for (unsigned int i = 0; i < scene->mNumTextures;++i)
{
if (texIdx != 0xffffffff && texIdx != i)
for (unsigned int i = 0; i < scene->mNumTextures;++i) {
if (texIdx != 0xffffffff && texIdx != i) {
continue;
}
const aiTexture* tex = scene->mTextures[i];
std::string out_cpy = out, out_ext = extension;
@ -347,7 +355,7 @@ int Assimp_Extract (const char** params, unsigned int num)
int m;
if (!tex->mHeight) {
m = (1 != ::fwrite(tex->pcData,tex->mWidth,1,p));
m = (1 != fwrite(tex->pcData,tex->mWidth,1,p));
}
else m = DoExport(tex,p,extension,flags);
::fclose(p);

View File

@ -63,6 +63,7 @@ const char* AICMD_MSG_HELP =
"\t\tknowext - Check whether a file extension is recognized by Assimp\n"
"\t\textract - Extract an embedded texture from a model\n"
"\t\tdump - Convert a model to binary or XML dumps (ASSBIN/ASSXML)\n"
"\t\tcmpdump - Compare two file dumps produced with \'assimp dump <file> -s ...\'\n"
"\n\n\tUse \'assimp <verb> --help\' to get detailed help for a command.\n"
;
@ -73,14 +74,13 @@ const char* AICMD_MSG_HELP =
int main (int argc, char* argv[])
{
if (argc <= 1) {
printf("assimp: No command specified. Use \'assimp help\' for a detailed command list\n");
return 0;
}
// assimp version
// Display version information
if (! ::strcmp(argv[1], "version")) {
if (! strcmp(argv[1], "version")) {
const unsigned int flags = aiGetCompileFlags();
printf(AICMD_MSG_ABOUT,
aiGetVersionMajor(),
@ -103,7 +103,14 @@ int main (int argc, char* argv[])
return 0;
}
// assimp cmpdump
// Compare two mini model dumps (regression suite)
if (! strcmp(argv[1], "cmpdump")) {
return Assimp_CompareDump (&argv[2],argc-2);
}
// construct a global Assimp::Importer instance
// because all further tools rely on it
Assimp::Importer imp;
globalImporter = &imp;
@ -138,13 +145,13 @@ int main (int argc, char* argv[])
// assimp dump
// Dump a model to a file
if (! strcmp(argv[1], "dump")) {
return Assimp_Dump ((const char**)&argv[2],argc-2);
return Assimp_Dump (&argv[2],argc-2);
}
// assimp extract
// Extract an embedded texture from a file
if (! strcmp(argv[1], "extract")) {
return Assimp_Extract ((const char**)&argv[2],argc-2);
return Assimp_Extract (&argv[2],argc-2);
}
printf("Unrecognized command. Use \'assimp help\' for a detailed command list\n");
@ -153,7 +160,9 @@ int main (int argc, char* argv[])
// ------------------------------------------------------------------------------
// Import a specific file
const aiScene* ImportModel(const ImportData& imp, const std::string& path)
const aiScene* ImportModel(
const ImportData& imp,
const std::string& path)
{
// Attach log streams
if (imp.log) {
@ -172,12 +181,12 @@ const aiScene* ImportModel(const ImportData& imp, const std::string& path)
// Now validate this flag combination
if(!globalImporter->ValidateFlags(imp.ppFlags)) {
::printf("ERROR: Unsupported post-processing flags \n");
printf("ERROR: Unsupported post-processing flags \n");
return NULL;
}
printf("Validating postprocessing flags ... OK\n");
if (imp.showLog)
::printf("-----------------------------------------------------------------\n");
printf("-----------------------------------------------------------------\n");
// do the actual import, measure time
const clock_t first = clock();
@ -205,7 +214,9 @@ const aiScene* ImportModel(const ImportData& imp, const std::string& path)
// ------------------------------------------------------------------------------
// Process standard arguments
int ProcessStandardArguments(ImportData& fill, const char** params,
int ProcessStandardArguments(
ImportData& fill,
const char* const * params,
unsigned int num)
{
// -ptv --pretransform-vertices
@ -237,11 +248,11 @@ int ProcessStandardArguments(ImportData& fill, const char** params,
for (unsigned int i = 0; i < num;++i)
{
if (!params[i]) { // could happen if some args have already been processed
continue;
}
//if (!params[i]) { // could happen if some args have already been processed
// continue;
//}
bool has = true;
// bool has = true;
if (! strcmp(params[i], "-ptv") || ! strcmp(params[i], "--pretransform-vertices")) {
fill.ppFlags |= aiProcess_PreTransformVertices;
}
@ -350,10 +361,10 @@ int ProcessStandardArguments(ImportData& fill, const char** params,
fill.logFile = "assimp-log.txt";
}
else has = false;
if (has) {
params[i] = NULL;
}
//else has = false;
//if (has) {
// params[i] = NULL;
//}
}
if (fill.logFile.length() || fill.showLog || fill.verbose)

View File

@ -71,8 +71,7 @@ using namespace Assimp;
extern Assimp::Importer* globalImporter;
// ------------------------------------------------------------------------------
/** @brief Defines common import parameters
*/
/** Defines common import parameters */
struct ImportData
{
ImportData()
@ -101,39 +100,52 @@ struct ImportData
};
// ------------------------------------------------------------------------------
/** @brief Process standard arguments
/** Process standard arguments
*
* @param fill Filled by function
* @param params Command line parameters to be processed
* @param num NUmber of params
* @return 0 for success
*/
int ProcessStandardArguments(ImportData& fill, const char** params,
* @return 0 for success */
int ProcessStandardArguments(ImportData& fill,
const char* const* params,
unsigned int num);
// ------------------------------------------------------------------------------
/** @brief Import a specific model file
/** Import a specific model file
* @param imp Import configuration to be used
* @param path Path to the file to be opened
*/
const aiScene* ImportModel(const ImportData& imp, const std::string& path);
* @param path Path to the file to be opened */
const aiScene* ImportModel(
const ImportData& imp,
const std::string& path);
// ------------------------------------------------------------------------------
/** @brief assimp dump utility
/** assimp_dump utility
* @param params Command line parameters to 'assimp dumb'
* @param Number of params
* @return 0 for success
*/
int Assimp_Dump (const char** params, unsigned int num);
* @return 0 for success*/
int Assimp_Dump (
const char* const* params,
unsigned int num);
// ------------------------------------------------------------------------------
/** @brief assimp extract utility
/** assimp_extract utility
* @param params Command line parameters to 'assimp extract'
* @param Number of params
* @return 0 for success
*/
int Assimp_Extract (const char** params, unsigned int num);
* @return 0 for success*/
int Assimp_Extract (
const char* const* params,
unsigned int num);
// ------------------------------------------------------------------------------
/** assimp_cmpdump utility
* @param params Command line parameters to 'assimp cmpdump'
* @param Number of params
* @return 0 for success*/
int Assimp_CompareDump (
const char* const* params,
unsigned int num);
// ------------------------------------------------------------------------------
/** @brief assimp info utility

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,113 @@
/* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or organization
* obtaining a copy of the software and accompanying documentation covered by
* this license (the "Software") to use, reproduce, display, distribute,
* execute, and transmit the Software, and to prepare derivative works of the
* Software, and to permit third-parties to whom the Software is furnished to
* do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including
* the above license grant, this restriction and the following disclaimer,
* must be included in all copies of the Software, in whole or in part, and
* all derivative works of the Software, unless such copies or derivative
* works are solely in the form of machine-executable object code generated by
* a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE. */
#ifndef HEADER_GENERIC_INSERTER_HPP_INCLUDED
#define HEADER_GENERIC_INSERTER_HPP_INCLUDED
#include <ostream>
#include <new> // bad_alloc
template <typename char_type, typename traits_type, typename argument_type>
std::basic_ostream<char_type, traits_type>& generic_inserter(void (*print)(std::basic_ostream<char_type, traits_type>& os, argument_type const& arg), std::basic_ostream<char_type, traits_type>& os, argument_type const& arg)
{
using namespace ::std;
ios_base::iostate err = ios_base::goodbit;
try
{
typename basic_ostream<char_type, traits_type>::sentry sentry(os);
if (sentry)
{
print(os, arg);
err = os.rdstate();
os.width(0); // Reset width in case the user didn't do it.
}
}
catch (bad_alloc const&)
{
err |= ios_base::badbit; // bad_alloc is considered fatal
ios_base::iostate const exception_mask = os.exceptions();
// Two cases: 1.) badbit is not set; 2.) badbit is set
if (((exception_mask & ios_base::failbit) != 0) && // failbit shall throw
((exception_mask & ios_base::badbit) == 0)) // badbit shall not throw
{
// Do not throw unless failbit is set.
// If it is set throw ios_base::failure because we don't know what caused the failbit to be set.
os.setstate(err);
}
else if (exception_mask & ios_base::badbit)
{
try
{
// This will set the badbit and throw ios_base::failure.
os.setstate(err);
}
catch (ios_base::failure const&)
{
// Do nothing since we want bad_alloc to be rethrown.
}
throw;
}
// else: no exception must get out!
}
catch (...)
{
err |= ios_base::failbit; // Any other exception is considered "only" as a failure.
ios_base::iostate const exception_mask = os.exceptions();
// badbit is considered more important
if (((exception_mask & ios_base::badbit) != 0) && // badbit shall throw
((err & ios_base::badbit) != 0)) // badbit is set
{
// Throw ios_base::failure because we don't know what caused the badbit to be set.
os.setstate(err);
}
else if ((exception_mask & ios_base::failbit) != 0)
{
try
{
// This will set the failbit and throw the exception ios_base::failure.
os.setstate(err);
}
catch (ios_base::failure const&)
{
// Do nothing since we want the original exception to be rethrown.
}
throw;
}
// else: no exception must get out!
}
// Needed in the case that no exception has been thrown but the stream state has changed.
if (err)
os.setstate(err);
return os;
}
#endif // HEADER_GENERIC_INSERTER_HPP_INCLUDED