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-9d2fd5bffc1fpull/5/head
parent
7cb9438522
commit
6d2857ed4a
|
@ -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())
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue