Bugfix: collada loader now preserves empty data arrays to work around stupid exporters writing empty animation channels

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1246 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/5/head
ulfjorensen 2012-05-05 07:38:14 +00:00
parent 7cb9438522
commit 6d2857ed4a
2 changed files with 120 additions and 104 deletions

View File

@ -926,7 +926,7 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
const Collada::AnimationChannel& srcChannel = *cit; const Collada::AnimationChannel& srcChannel = *cit;
Collada::ChannelEntry entry; Collada::ChannelEntry entry;
// we except the animation target to be of type "nodeName/transformID.subElement". Ignore all others // we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others
// find the slash that separates the node name - there should be only one // find the slash that separates the node name - there should be only one
std::string::size_type slashPos = srcChannel.mTarget.find( '/'); std::string::size_type slashPos = srcChannel.mTarget.find( '/');
if( slashPos == std::string::npos) if( slashPos == std::string::npos)
@ -995,122 +995,134 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount) if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget)); throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget));
// find bounding times if( e.mTimeAccessor->mCount > 0 )
startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0)); {
endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0)); // find bounding times
startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0));
endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0));
}
} }
// create a local transformation chain of the node's transforms std::vector<aiMatrix4x4> resultTrafos;
std::vector<Collada::Transform> transforms = srcNode->mTransforms; if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 )
{
// create a local transformation chain of the node's transforms
std::vector<Collada::Transform> transforms = srcNode->mTransforms;
// now for every unique point in time, find or interpolate the key values for that time // now for every unique point in time, find or interpolate the key values for that time
// and apply them to the transform chain. Then the node's present transformation can be calculated. // and apply them to the transform chain. Then the node's present transformation can be calculated.
float time = startTime; float time = startTime;
std::vector<aiMatrix4x4> resultTrafos; while( 1)
while( 1) {
{ for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) {
{ Collada::ChannelEntry& e = *it;
Collada::ChannelEntry& e = *it;
// find the keyframe behind the current point in time // find the keyframe behind the current point in time
size_t pos = 0; size_t pos = 0;
float postTime = 0.f; float postTime = 0.f;
while( 1) while( 1)
{ {
if( pos >= e.mTimeAccessor->mCount) if( pos >= e.mTimeAccessor->mCount)
break; break;
postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
if( postTime >= time) if( postTime >= time)
break; break;
++pos; ++pos;
} }
pos = std::min( pos, e.mTimeAccessor->mCount-1); pos = std::min( pos, e.mTimeAccessor->mCount-1);
// read values from there // read values from there
float temp[16]; float temp[16];
for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c); temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c);
// if not exactly at the key time, interpolate with previous value set // if not exactly at the key time, interpolate with previous value set
if( postTime > time && pos > 0) if( postTime > time && pos > 0)
{ {
float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0);
float factor = (time - postTime) / (preTime - postTime); float factor = (time - postTime) / (preTime - postTime);
for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
{ {
float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c);
temp[c] += (v - temp[c]) * factor; temp[c] += (v - temp[c]) * factor;
} }
} }
// Apply values to current transformation // Apply values to current transformation
std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement);
} }
// Calculate resulting transformation // Calculate resulting transformation
aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms);
// out of lazyness: we store the time in matrix.d4 // out of lazyness: we store the time in matrix.d4
mat.d4 = time; mat.d4 = time;
resultTrafos.push_back( mat); resultTrafos.push_back( mat);
// find next point in time to evaluate. That's the closest frame larger than the current in any channel // find next point in time to evaluate. That's the closest frame larger than the current in any channel
float nextTime = 1e20f; float nextTime = 1e20f;
for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it) for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
{ {
Collada::ChannelEntry& e = *it; Collada::ChannelEntry& e = *it;
// find the next time value larger than the current // find the next time value larger than the current
size_t pos = 0; size_t pos = 0;
while( pos < e.mTimeAccessor->mCount) while( pos < e.mTimeAccessor->mCount)
{ {
float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
if( t > time) if( t > time)
{ {
nextTime = std::min( nextTime, t); nextTime = std::min( nextTime, t);
break; break;
} }
++pos; ++pos;
} }
} }
// no more keys on any channel after the current time -> we're done // no more keys on any channel after the current time -> we're done
if( nextTime > 1e19) if( nextTime > 1e19)
break; break;
// else construct next keyframe at this following time point // else construct next keyframe at this following time point
time = nextTime; time = nextTime;
} }
}
// there should be some keyframes // there should be some keyframes, but we aren't that fixated on valid input data
ai_assert( resultTrafos.size() > 0); // ai_assert( resultTrafos.size() > 0);
// build an animation channel for the given node out of these trafo keys // build an animation channel for the given node out of these trafo keys
aiNodeAnim* dstAnim = new aiNodeAnim; if( !resultTrafos.empty() )
dstAnim->mNodeName = nodeName; {
dstAnim->mNumPositionKeys = resultTrafos.size(); aiNodeAnim* dstAnim = new aiNodeAnim;
dstAnim->mNumRotationKeys= resultTrafos.size(); dstAnim->mNodeName = nodeName;
dstAnim->mNumScalingKeys = resultTrafos.size(); dstAnim->mNumPositionKeys = resultTrafos.size();
dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; dstAnim->mNumRotationKeys= resultTrafos.size();
dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; dstAnim->mNumScalingKeys = resultTrafos.size();
dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
for( size_t a = 0; a < resultTrafos.size(); ++a) for( size_t a = 0; a < resultTrafos.size(); ++a)
{ {
aiMatrix4x4 mat = resultTrafos[a]; aiMatrix4x4 mat = resultTrafos[a];
double time = double( mat.d4); // remember? time is stored in mat.d4 double time = double( mat.d4); // remember? time is stored in mat.d4
mat.d4 = 1.0f; mat.d4 = 1.0f;
dstAnim->mPositionKeys[a].mTime = time; dstAnim->mPositionKeys[a].mTime = time;
dstAnim->mRotationKeys[a].mTime = time; dstAnim->mRotationKeys[a].mTime = time;
dstAnim->mScalingKeys[a].mTime = time; dstAnim->mScalingKeys[a].mTime = time;
mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
} }
anims.push_back( dstAnim); anims.push_back( dstAnim);
} else
{
DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter.");
}
} }
if( !anims.empty()) if( !anims.empty())

View File

@ -1647,6 +1647,7 @@ void ColladaParser::ReadDataArray()
{ {
std::string elmName = mReader->getNodeName(); std::string elmName = mReader->getNodeName();
bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array"); bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array");
bool isEmptyElement = mReader->isEmptyElement();
// read attributes // read attributes
int indexID = GetAttribute( "id"); int indexID = GetAttribute( "id");
@ -1654,13 +1655,15 @@ void ColladaParser::ReadDataArray()
int indexCount = GetAttribute( "count"); int indexCount = GetAttribute( "count");
unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount); unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount);
const char* content = TestTextContent(); const char* content = TestTextContent();
if (content) { // some exporters write empty data arrays, silently skip over them
// read values and store inside an array in the data library // read values and store inside an array in the data library
mDataLibrary[id] = Data(); mDataLibrary[id] = Data();
Data& data = mDataLibrary[id]; Data& data = mDataLibrary[id];
data.mIsStringArray = isStringArray; data.mIsStringArray = isStringArray;
// some exporters write empty data arrays, but we need to conserve them anyways because others might reference them
if (content)
{
if( isStringArray) if( isStringArray)
{ {
data.mStrings.reserve( count); data.mStrings.reserve( count);
@ -1695,10 +1698,11 @@ void ColladaParser::ReadDataArray()
SkipSpacesAndLineEnd( &content); SkipSpacesAndLineEnd( &content);
} }
} }
// test for closing tag
TestClosing( elmName.c_str());
} }
// test for closing tag
if( !isEmptyElement )
TestClosing( elmName.c_str());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------