diff options
| author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
|---|---|---|
| committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
| commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
| tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llcharacter/llbvhloader.cpp | |
Print done when done.
Diffstat (limited to 'indra/llcharacter/llbvhloader.cpp')
| -rw-r--r-- | indra/llcharacter/llbvhloader.cpp | 1489 |
1 files changed, 1489 insertions, 0 deletions
diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp new file mode 100644 index 0000000000..4d4ad39080 --- /dev/null +++ b/indra/llcharacter/llbvhloader.cpp @@ -0,0 +1,1489 @@ +/** + * @file llbvhloader.cpp + * @brief Translates a BVH files to LindenLabAnimation format. + * + * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "llbvhloader.h" +#include "lldatapacker.h" +#include "lldir.h" +#include "llkeyframemotion.h" +#include "llquantize.h" +#include "llstl.h" +#include "llapr.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <llquaternion.h> +#include <boost/tokenizer.hpp> + +using namespace std; + +#define INCHES_TO_METERS 0.02540005f + +const F32 POSITION_KEYFRAME_THRESHOLD = 0.03f; +const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f; + +const F32 POSITION_MOTION_THRESHOLD = 0.001f; +const F32 ROTATION_MOTION_THRESHOLD = 0.001f; + +char gInFile[1024]; /* Flawfinder: ignore */ +char gOutFile[1024]; /* Flawfinder: ignore */ + +//------------------------------------------------------------------------ +// Status Codes +//------------------------------------------------------------------------ +char *LLBVHLoader::ST_OK = "Ok"; +char *LLBVHLoader::ST_EOF = "Premature end of file."; +char *LLBVHLoader::ST_NO_CONSTRAINT = "Can't read constraint definition."; +char *LLBVHLoader::ST_NO_FILE = "Can't open BVH file."; +char *LLBVHLoader::ST_NO_HIER = "Invalid HIERARCHY header."; +char *LLBVHLoader::ST_NO_JOINT = "Can't find ROOT or JOINT."; +char *LLBVHLoader::ST_NO_NAME = "Can't get JOINT name."; +char *LLBVHLoader::ST_NO_OFFSET = "Can't find OFFSET."; +char *LLBVHLoader::ST_NO_CHANNELS = "Can't find CHANNELS."; +char *LLBVHLoader::ST_NO_ROTATION = "Can't get rotation order."; +char *LLBVHLoader::ST_NO_AXIS = "Can't get rotation axis."; +char *LLBVHLoader::ST_NO_MOTION = "Can't find MOTION."; +char *LLBVHLoader::ST_NO_FRAMES = "Can't get number of frames."; +char *LLBVHLoader::ST_NO_FRAME_TIME = "Can't get frame time."; +char *LLBVHLoader::ST_NO_POS = "Can't get position values."; +char *LLBVHLoader::ST_NO_ROT = "Can't get rotation values."; +char *LLBVHLoader::ST_NO_XLT_FILE = "Can't open translation file."; +char *LLBVHLoader::ST_NO_XLT_HEADER = "Can't read translation header."; +char *LLBVHLoader::ST_NO_XLT_NAME = "Can't read translation names."; +char *LLBVHLoader::ST_NO_XLT_IGNORE = "Can't read translation ignore value."; +char *LLBVHLoader::ST_NO_XLT_RELATIVE = "Can't read translation relative value."; +char *LLBVHLoader::ST_NO_XLT_OUTNAME = "Can't read translation outname value."; +char *LLBVHLoader::ST_NO_XLT_MATRIX = "Can't read translation matrix."; +char *LLBVHLoader::ST_NO_XLT_MERGECHILD = "Can't get mergechild name."; +char *LLBVHLoader::ST_NO_XLT_MERGEPARENT = "Can't get mergeparent name."; +char *LLBVHLoader::ST_NO_XLT_PRIORITY = "Can't get priority value."; +char *LLBVHLoader::ST_NO_XLT_LOOP = "Can't get loop value."; +char *LLBVHLoader::ST_NO_XLT_EASEIN = "Can't get easeIn values."; +char *LLBVHLoader::ST_NO_XLT_EASEOUT = "Can't get easeOut values."; +char *LLBVHLoader::ST_NO_XLT_HAND = "Can't get hand morph value."; +char *LLBVHLoader::ST_NO_XLT_EMOTE = "Can't read emote name."; + +//------------------------------------------------------------------------ +// find_next_whitespace() +//------------------------------------------------------------------------ +const char *find_next_whitespace(const char *p) +{ + while(*p && isspace(*p)) p++; + while(*p && !isspace(*p)) p++; + return p; +} + + +//------------------------------------------------------------------------ +// bvhStringToOrder() +// +// XYZ order in BVH files must be passed to mayaQ() as ZYX. +// This function reverses the input string before passing it on +// to StringToOrder(). +//------------------------------------------------------------------------ +LLQuaternion::Order bvhStringToOrder( char *str ) +{ + char order[4]; /* Flawfinder: ignore */ + order[0] = str[2]; + order[1] = str[1]; + order[2] = str[0]; + order[3] = 0; + LLQuaternion::Order retVal = StringToOrder( order ); + return retVal; +} + +//----------------------------------------------------------------------------- +// LLBVHLoader() +//----------------------------------------------------------------------------- +LLBVHLoader::LLBVHLoader(const char* buffer) +{ + reset(); + + mStatus = loadTranslationTable("anim.ini"); + + if (mStatus == LLBVHLoader::ST_NO_XLT_FILE) + { + llwarns << "NOTE: No translation table found." << llendl; + return; + } + else + { + if (mStatus != LLBVHLoader::ST_OK) + { + llwarns << "ERROR: [line: " << getLineNumber() << "] " << mStatus << llendl; + return; + } + } + + char error_text[128]; /* Flawfinder: ignore */ + S32 error_line; + mStatus = loadBVHFile(buffer, error_text, error_line); + if (mStatus != LLBVHLoader::ST_OK) + { + llwarns << "ERROR: [line: " << getLineNumber() << "] " << mStatus << llendl; + return; + } + + applyTranslations(); + optimize(); + + mInitialized = TRUE; +} + +LLBVHLoader::~LLBVHLoader() +{ + std::for_each(mJoints.begin(),mJoints.end(),DeletePointer()); +} + +//------------------------------------------------------------------------ +// LLBVHLoader::loadTranslationTable() +//------------------------------------------------------------------------ +LLBVHLoader::Status LLBVHLoader::loadTranslationTable(const char *fileName) +{ + mLineNumber = 0; + mTranslations.clear(); + mConstraints.clear(); + + //-------------------------------------------------------------------- + // open file + //-------------------------------------------------------------------- + char path[LL_MAX_PATH]; /* Flawfinder: ignore */ + + snprintf( path, sizeof(path), "%s", + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName).c_str()); /* Flawfinder: ignore */ + + + apr_file_t *fp = ll_apr_file_open(path, LL_APR_R); + if (!fp) + return ST_NO_XLT_FILE; + + llinfos << "NOTE: Loading translation table: " << fileName << llendl; + + //-------------------------------------------------------------------- + // register file to be closed on function exit + //-------------------------------------------------------------------- + FileCloser fileCloser(fp); + + //-------------------------------------------------------------------- + // load header + //-------------------------------------------------------------------- + if ( ! getLine(fp) ) + return ST_EOF; + if ( strncmp(mLine, "Translations 1.0", 16) ) + return ST_NO_XLT_HEADER; + + //-------------------------------------------------------------------- + // load data one line at a time + //-------------------------------------------------------------------- + BOOL loadingGlobals = FALSE; + Translation *trans = NULL; + while ( getLine(fp) ) + { + //---------------------------------------------------------------- + // check the 1st token on the line to determine if it's empty or a comment + //---------------------------------------------------------------- + char token[128]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " %127s", token) != 1 ) + continue; + + if (token[0] == '#') + continue; + + //---------------------------------------------------------------- + // check if a [jointName] or [GLOBALS] was specified. + //---------------------------------------------------------------- + if (token[0] == '[') + { + char name[128]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " [%127[^]]", name) != 1 ) + return ST_NO_XLT_NAME; + + if (strcmp(name, "GLOBALS")==0) + { + loadingGlobals = TRUE; + continue; + } + else + { + loadingGlobals = FALSE; + Translation &newTrans = mTranslations[ name ]; + trans = &newTrans; + continue; + } + } + + //---------------------------------------------------------------- + // check for optional emote + //---------------------------------------------------------------- + if (loadingGlobals && LLString::compareInsensitive(token, "emote")==0) + { + char emote_str[1024]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " %*s = %1023s", emote_str) != 1 ) + return ST_NO_XLT_EMOTE; + + mEmoteName.assign( emote_str ); +// llinfos << "NOTE: Emote: " << mEmoteName.c_str() << llendl; + continue; + } + + + //---------------------------------------------------------------- + // check for global priority setting + //---------------------------------------------------------------- + if (loadingGlobals && LLString::compareInsensitive(token, "priority")==0) + { + S32 priority; + if ( sscanf(mLine, " %*s = %d", &priority) != 1 ) + return ST_NO_XLT_PRIORITY; + + mPriority = priority; +// llinfos << "NOTE: Priority: " << mPriority << llendl; + continue; + } + + //---------------------------------------------------------------- + // check for global loop setting + //---------------------------------------------------------------- + if (loadingGlobals && LLString::compareInsensitive(token, "loop")==0) + { + char trueFalse[128]; /* Flawfinder: ignore */ + trueFalse[0] = '\0'; + + F32 loop_in = 0.f; + F32 loop_out = 1.f; + + if ( sscanf(mLine, " %*s = %f %f", &loop_in, &loop_out) == 2 ) + { + mLoop = TRUE; + } + else if ( sscanf(mLine, " %*s = %127s", trueFalse) == 1 ) + { + mLoop = (LLString::compareInsensitive(trueFalse, "true")==0); + } + else + { + return ST_NO_XLT_LOOP; + } + + mLoopInPoint = loop_in * mDuration; + mLoopOutPoint = loop_out * mDuration; + + continue; + } + + //---------------------------------------------------------------- + // check for global easeIn setting + //---------------------------------------------------------------- + if (loadingGlobals && LLString::compareInsensitive(token, "easein")==0) + { + F32 duration; + char type[128]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 ) + return ST_NO_XLT_EASEIN; + + mEaseIn = duration; + continue; + } + + //---------------------------------------------------------------- + // check for global easeOut setting + //---------------------------------------------------------------- + if (loadingGlobals && LLString::compareInsensitive(token, "easeout")==0) + { + F32 duration; + char type[128]; + if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 ) + return ST_NO_XLT_EASEOUT; + + mEaseOut = duration; + continue; + } + + //---------------------------------------------------------------- + // check for global handMorph setting + //---------------------------------------------------------------- + if (loadingGlobals && LLString::compareInsensitive(token, "hand")==0) + { + S32 handMorph; + if (sscanf(mLine, " %*s = %d", &handMorph) != 1) + return ST_NO_XLT_HAND; + + mHand = handMorph; + continue; + } + + if (loadingGlobals && LLString::compareInsensitive(token, "constraint")==0) + { + Constraint constraint; + + // try reading optional target direction + if(sscanf( + mLine, + " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f", + &constraint.mChainLength, + &constraint.mEaseInStart, + &constraint.mEaseInStop, + &constraint.mEaseOutStart, + &constraint.mEaseOutStop, + constraint.mSourceJointName, + &constraint.mSourceOffset.mV[VX], + &constraint.mSourceOffset.mV[VY], + &constraint.mSourceOffset.mV[VZ], + constraint.mTargetJointName, + &constraint.mTargetOffset.mV[VX], + &constraint.mTargetOffset.mV[VY], + &constraint.mTargetOffset.mV[VZ], + &constraint.mTargetDir.mV[VX], + &constraint.mTargetDir.mV[VY], + &constraint.mTargetDir.mV[VZ]) != 16) + { + if(sscanf( + mLine, + " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f", + &constraint.mChainLength, + &constraint.mEaseInStart, + &constraint.mEaseInStop, + &constraint.mEaseOutStart, + &constraint.mEaseOutStop, + constraint.mSourceJointName, + &constraint.mSourceOffset.mV[VX], + &constraint.mSourceOffset.mV[VY], + &constraint.mSourceOffset.mV[VZ], + constraint.mTargetJointName, + &constraint.mTargetOffset.mV[VX], + &constraint.mTargetOffset.mV[VY], + &constraint.mTargetOffset.mV[VZ]) != 13) + { + return ST_NO_CONSTRAINT; + } + } + else + { + // normalize direction + if (!constraint.mTargetDir.isExactlyZero()) + { + constraint.mTargetDir.normVec(); + } + + } + + constraint.mConstraintType = CONSTRAINT_TYPE_POINT; + mConstraints.push_back(constraint); + continue; + } + + if (loadingGlobals && LLString::compareInsensitive(token, "planar_constraint")==0) + { + Constraint constraint; + + // try reading optional target direction + if(sscanf( + mLine, + " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f", + &constraint.mChainLength, + &constraint.mEaseInStart, + &constraint.mEaseInStop, + &constraint.mEaseOutStart, + &constraint.mEaseOutStop, + constraint.mSourceJointName, + &constraint.mSourceOffset.mV[VX], + &constraint.mSourceOffset.mV[VY], + &constraint.mSourceOffset.mV[VZ], + constraint.mTargetJointName, + &constraint.mTargetOffset.mV[VX], + &constraint.mTargetOffset.mV[VY], + &constraint.mTargetOffset.mV[VZ], + &constraint.mTargetDir.mV[VX], + &constraint.mTargetDir.mV[VY], + &constraint.mTargetDir.mV[VZ]) != 16) + { + if(sscanf( + mLine, + " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f", + &constraint.mChainLength, + &constraint.mEaseInStart, + &constraint.mEaseInStop, + &constraint.mEaseOutStart, + &constraint.mEaseOutStop, + constraint.mSourceJointName, + &constraint.mSourceOffset.mV[VX], + &constraint.mSourceOffset.mV[VY], + &constraint.mSourceOffset.mV[VZ], + constraint.mTargetJointName, + &constraint.mTargetOffset.mV[VX], + &constraint.mTargetOffset.mV[VY], + &constraint.mTargetOffset.mV[VZ]) != 13) + { + return ST_NO_CONSTRAINT; + } + } + else + { + // normalize direction + if (!constraint.mTargetDir.isExactlyZero()) + { + constraint.mTargetDir.normVec(); + } + + } + + constraint.mConstraintType = CONSTRAINT_TYPE_PLANE; + mConstraints.push_back(constraint); + continue; + } + + + //---------------------------------------------------------------- + // at this point there must be a valid trans pointer + //---------------------------------------------------------------- + if ( ! trans ) + return ST_NO_XLT_NAME; + + //---------------------------------------------------------------- + // check for ignore flag + //---------------------------------------------------------------- + if ( LLString::compareInsensitive(token, "ignore")==0 ) + { + char trueFalse[128]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " %*s = %127s", trueFalse) != 1 ) + return ST_NO_XLT_IGNORE; + + trans->mIgnore = (LLString::compareInsensitive(trueFalse, "true")==0); + continue; + } + + //---------------------------------------------------------------- + // check for relativepos flag + //---------------------------------------------------------------- + if ( LLString::compareInsensitive(token, "relativepos")==0 ) + { + F32 x, y, z; + char relpos[128]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " %*s = %f %f %f", &x, &y, &z) == 3 ) + { + trans->mRelativePosition.setVec( x, y, z ); + } + else if ( sscanf(mLine, " %*s = %127s", relpos) == 1 ) + { + if ( LLString::compareInsensitive(relpos, "firstkey")==0 ) + { + trans->mRelativePositionKey = TRUE; + } + else + { + return ST_NO_XLT_RELATIVE; + } + } + else + { + return ST_NO_XLT_RELATIVE; + } + + continue; + } + + //---------------------------------------------------------------- + // check for relativerot flag + //---------------------------------------------------------------- + if ( LLString::compareInsensitive(token, "relativerot")==0 ) + { + //F32 x, y, z; + char relpos[128]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " %*s = %127s", relpos) == 1 ) + { + if ( LLString::compareInsensitive(relpos, "firstkey")==0 ) + { + trans->mRelativeRotationKey = TRUE; + } + else + { + return ST_NO_XLT_RELATIVE; + } + } + else + { + return ST_NO_XLT_RELATIVE; + } + + continue; + } + + //---------------------------------------------------------------- + // check for outname value + //---------------------------------------------------------------- + if ( LLString::compareInsensitive(token, "outname")==0 ) + { + char outName[128]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " %*s = %127s", outName) != 1 ) + return ST_NO_XLT_OUTNAME; + + trans->mOutName = outName; + continue; + } + + //---------------------------------------------------------------- + // check for frame matrix value + //---------------------------------------------------------------- + if ( LLString::compareInsensitive(token, "frame")==0 ) + { + LLMatrix3 fm; + if ( sscanf(mLine, " %*s = %f %f %f, %f %f %f, %f %f %f", + &fm.mMatrix[0][0], &fm.mMatrix[0][1], &fm.mMatrix[0][2], + &fm.mMatrix[1][0], &fm.mMatrix[1][1], &fm.mMatrix[1][2], + &fm.mMatrix[2][0], &fm.mMatrix[2][1], &fm.mMatrix[2][2] ) != 9 ) + return ST_NO_XLT_MATRIX; + + trans->mFrameMatrix = fm; + continue; + } + + //---------------------------------------------------------------- + // check for offset matrix value + //---------------------------------------------------------------- + if ( LLString::compareInsensitive(token, "offset")==0 ) + { + LLMatrix3 om; + if ( sscanf(mLine, " %*s = %f %f %f, %f %f %f, %f %f %f", + &om.mMatrix[0][0], &om.mMatrix[0][1], &om.mMatrix[0][2], + &om.mMatrix[1][0], &om.mMatrix[1][1], &om.mMatrix[1][2], + &om.mMatrix[2][0], &om.mMatrix[2][1], &om.mMatrix[2][2] ) != 9 ) + return ST_NO_XLT_MATRIX; + + trans->mOffsetMatrix = om; + continue; + } + + //---------------------------------------------------------------- + // check for mergeparent value + //---------------------------------------------------------------- + if ( LLString::compareInsensitive(token, "mergeparent")==0 ) + { + char mergeParentName[128]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " %*s = %127s", mergeParentName) != 1 ) + return ST_NO_XLT_MERGEPARENT; + + trans->mMergeParentName = mergeParentName; + continue; + } + + //---------------------------------------------------------------- + // check for mergechild value + //---------------------------------------------------------------- + if ( LLString::compareInsensitive(token, "mergechild")==0 ) + { + char mergeChildName[128]; /* Flawfinder: ignore */ + if ( sscanf(mLine, " %*s = %127s", mergeChildName) != 1 ) + return ST_NO_XLT_MERGECHILD; + + trans->mMergeChildName = mergeChildName; + continue; + } + + //---------------------------------------------------------------- + // check for per-joint priority + //---------------------------------------------------------------- + if ( LLString::compareInsensitive(token, "priority")==0 ) + { + S32 priority; + if ( sscanf(mLine, " %*s = %d", &priority) != 1 ) + return ST_NO_XLT_PRIORITY; + + trans->mPriorityModifier = priority; + continue; + } + + } + return ST_OK; +} + + +//------------------------------------------------------------------------ +// LLBVHLoader::loadBVHFile() +//------------------------------------------------------------------------ +LLBVHLoader::Status LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &err_line) +{ + std::string line; + + err_line = 0; + error_text[127] = '\0'; + + std::string str(buffer); + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("\r\n"); + tokenizer tokens(str, sep); + tokenizer::iterator iter = tokens.begin(); + + mLineNumber = 0; + mJoints.clear(); + + std::vector<S32> parent_joints; + + //-------------------------------------------------------------------- + // consume hierarchy + //-------------------------------------------------------------------- + if (iter == tokens.end()) + return ST_EOF; + line = (*(iter++)); + err_line++; + + if ( !strstr(line.c_str(), "HIERARCHY") ) + { +// llinfos << line << llendl; + return ST_NO_HIER; + } + + //-------------------------------------------------------------------- + // consume joints + //-------------------------------------------------------------------- + while (TRUE) + { + //---------------------------------------------------------------- + // get next line + //---------------------------------------------------------------- + if (iter == tokens.end()) + return ST_EOF; + line = (*(iter++)); + err_line++; + + //---------------------------------------------------------------- + // consume } + //---------------------------------------------------------------- + if ( strstr(line.c_str(), "}") ) + { + if (parent_joints.size() > 0) + { + parent_joints.pop_back(); + } + continue; + } + + //---------------------------------------------------------------- + // if MOTION, break out + //---------------------------------------------------------------- + if ( strstr(line.c_str(), "MOTION") ) + break; + + //---------------------------------------------------------------- + // it must be either ROOT or JOINT or EndSite + //---------------------------------------------------------------- + if ( strstr(line.c_str(), "ROOT") ) + { + } + else if ( strstr(line.c_str(), "JOINT") ) + { + } + else if ( strstr(line.c_str(), "End Site") ) + { + iter++; // { + iter++; // OFFSET + S32 depth = 0; + for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--) + { + Joint *joint = mJoints[parent_joints[j]]; + if (depth > joint->mChildTreeMaxDepth) + { + joint->mChildTreeMaxDepth = depth; + } + depth++; + } + continue; + } + else + { + strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */ + return ST_NO_JOINT; + } + + //---------------------------------------------------------------- + // get the joint name + //---------------------------------------------------------------- + char jointName[80]; /* Flawfinder: ignore */ + if ( sscanf(line.c_str(), "%*s %79s", jointName) != 1 ) + { + strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */ + return ST_NO_NAME; + } + + //---------------------------------------------------------------- + // add a set of keyframes for this joint + //---------------------------------------------------------------- + mJoints.push_back( new Joint( jointName ) ); + Joint *joint = mJoints.back(); + + S32 depth = 1; + for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--) + { + Joint *pjoint = mJoints[parent_joints[j]]; + if (depth > pjoint->mChildTreeMaxDepth) + { + pjoint->mChildTreeMaxDepth = depth; + } + depth++; + } + + //---------------------------------------------------------------- + // get next line + //---------------------------------------------------------------- + if (iter == tokens.end()) + { + return ST_EOF; + } + line = (*(iter++)); + err_line++; + + //---------------------------------------------------------------- + // it must be { + //---------------------------------------------------------------- + if ( !strstr(line.c_str(), "{") ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_OFFSET; + } + else + { + parent_joints.push_back((S32)mJoints.size() - 1); + } + + //---------------------------------------------------------------- + // get next line + //---------------------------------------------------------------- + if (iter == tokens.end()) + { + return ST_EOF; + } + line = (*(iter++)); + err_line++; + + //---------------------------------------------------------------- + // it must be OFFSET + //---------------------------------------------------------------- + if ( !strstr(line.c_str(), "OFFSET") ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_OFFSET; + } + + //---------------------------------------------------------------- + // get next line + //---------------------------------------------------------------- + if (iter == tokens.end()) + { + return ST_EOF; + } + line = (*(iter++)); + err_line++; + + //---------------------------------------------------------------- + // it must be CHANNELS + //---------------------------------------------------------------- + if ( !strstr(line.c_str(), "CHANNELS") ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_CHANNELS; + } + + //---------------------------------------------------------------- + // get rotation order + //---------------------------------------------------------------- + const char *p = line.c_str(); + for (S32 i=0; i<3; i++) + { + p = strstr(p, "rotation"); + if (!p) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_ROTATION; + } + + const char axis = *(p - 1); + if ((axis != 'X') && (axis != 'Y') && (axis != 'Z')) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_AXIS; + } + + joint->mOrder[i] = axis; + + p++; + } + } + + //-------------------------------------------------------------------- + // consume motion + //-------------------------------------------------------------------- + if ( !strstr(line.c_str(), "MOTION") ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_MOTION; + } + + //-------------------------------------------------------------------- + // get number of frames + //-------------------------------------------------------------------- + if (iter == tokens.end()) + { + return ST_EOF; + } + line = (*(iter++)); + err_line++; + + if ( !strstr(line.c_str(), "Frames:") ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_FRAMES; + } + + if ( sscanf(line.c_str(), "Frames: %d", &mNumFrames) != 1 ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_FRAMES; + } + + //-------------------------------------------------------------------- + // get frame time + //-------------------------------------------------------------------- + if (iter == tokens.end()) + { + return ST_EOF; + } + line = (*(iter++)); + err_line++; + + if ( !strstr(line.c_str(), "Frame Time:") ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_FRAME_TIME; + } + + if ( sscanf(line.c_str(), "Frame Time: %f", &mFrameTime) != 1 ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_FRAME_TIME; + } + + mDuration = (F32)mNumFrames * mFrameTime; + if (!mLoop) + { + mLoopOutPoint = mDuration; + } + + //-------------------------------------------------------------------- + // load frames + //-------------------------------------------------------------------- + for (S32 i=0; i<mNumFrames; i++) + { + // get next line + if (iter == tokens.end()) + { + return ST_EOF; + } + line = (*(iter++)); + err_line++; + + // read and store values + const char *p = line.c_str(); + for (U32 j=0; j<mJoints.size(); j++) + { + Joint *joint = mJoints[j]; + joint->mKeys.push_back( Key() ); + Key &key = joint->mKeys.back(); + + // get 3 pos values for root joint only + if (j==0) + { + if ( sscanf(p, "%f %f %f", key.mPos, key.mPos+1, key.mPos+2) != 3 ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_POS; + } + } + + // skip to next 3 values in the line + p = find_next_whitespace(p); + if (!p) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_ROT; + } + p = find_next_whitespace(++p); + if (!p) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_ROT; + } + p = find_next_whitespace(++p); + if (!p) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_ROT; + } + + // get 3 rot values for joint + F32 rot[3]; + if ( sscanf(p, " %f %f %f", rot, rot+1, rot+2) != 3 ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return ST_NO_ROT; + } + + p++; + + key.mRot[ joint->mOrder[0]-'X' ] = rot[0]; + key.mRot[ joint->mOrder[1]-'X' ] = rot[1]; + key.mRot[ joint->mOrder[2]-'X' ] = rot[2]; + } + } + + return ST_OK; +} + + +//------------------------------------------------------------------------ +// LLBVHLoader::applyTranslation() +//------------------------------------------------------------------------ +void LLBVHLoader::applyTranslations() +{ + JointVector::iterator ji; + for (ji = mJoints.begin(); ji != mJoints.end(); ++ji ) + { + Joint *joint = *ji; + //---------------------------------------------------------------- + // Look for a translation for this joint. + // If none, skip to next joint + //---------------------------------------------------------------- + TranslationMap::iterator ti = mTranslations.find( joint->mName ); + if ( ti == mTranslations.end() ) + { + continue; + } + + Translation &trans = ti->second; + + //---------------------------------------------------------------- + // Set the ignore flag if necessary + //---------------------------------------------------------------- + if ( trans.mIgnore ) + { + //llinfos << "NOTE: Ignoring " << joint->mName.c_str() << llendl; + joint->mIgnore = TRUE; + continue; + } + + //---------------------------------------------------------------- + // Set the output name + //---------------------------------------------------------------- + if ( ! trans.mOutName.empty() ) + { + //llinfos << "NOTE: Changing " << joint->mName.c_str() << " to " << trans.mOutName.c_str() << llendl; + joint->mOutName = trans.mOutName; + } + + //---------------------------------------------------------------- + // Set the ignorepos flag if necessary + //---------------------------------------------------------------- + if ( joint->mOutName == std::string("mPelvis") ) + { + joint->mIgnorePositions = FALSE; + } + + //---------------------------------------------------------------- + // Set the relativepos flags if necessary + //---------------------------------------------------------------- + if ( trans.mRelativePositionKey ) + { +// llinfos << "NOTE: Removing 1st position offset from all keys for " << joint->mOutName.c_str() << llendl; + joint->mRelativePositionKey = TRUE; + } + + if ( trans.mRelativeRotationKey ) + { +// llinfos << "NOTE: Removing 1st rotation from all keys for " << joint->mOutName.c_str() << llendl; + joint->mRelativeRotationKey = TRUE; + } + + if ( trans.mRelativePosition.magVec() > 0.0f ) + { + joint->mRelativePosition = trans.mRelativePosition; +// llinfos << "NOTE: Removing " << +// joint->mRelativePosition.mV[0] << " " << +// joint->mRelativePosition.mV[1] << " " << +// joint->mRelativePosition.mV[2] << +// " from all position keys in " << +// joint->mOutName.c_str() << llendl; + } + + //---------------------------------------------------------------- + // Set change of coordinate frame + //---------------------------------------------------------------- + joint->mFrameMatrix = trans.mFrameMatrix; + joint->mOffsetMatrix = trans.mOffsetMatrix; + + //---------------------------------------------------------------- + // Set mergeparent name + //---------------------------------------------------------------- + if ( ! trans.mMergeParentName.empty() ) + { +// llinfos << "NOTE: Merging " << joint->mOutName.c_str() << +// " with parent " << +// trans.mMergeParentName.c_str() << llendl; + joint->mMergeParentName = trans.mMergeParentName; + } + + //---------------------------------------------------------------- + // Set mergechild name + //---------------------------------------------------------------- + if ( ! trans.mMergeChildName.empty() ) + { +// llinfos << "NOTE: Merging " << joint->mName.c_str() << +// " with child " << trans.mMergeChildName.c_str() << llendl; + joint->mMergeChildName = trans.mMergeChildName; + } + + //---------------------------------------------------------------- + // Set joint priority + //---------------------------------------------------------------- + joint->mPriority = mPriority + trans.mPriorityModifier; + + } +} + +//----------------------------------------------------------------------------- +// LLBVHLoader::optimize() +//----------------------------------------------------------------------------- +void LLBVHLoader::optimize() +{ + //RN: assumes motion blend, which is the default now + if (!mLoop && mEaseIn + mEaseOut > mDuration && mDuration != 0.f) + { + F32 factor = mDuration / (mEaseIn + mEaseOut); + mEaseIn *= factor; + mEaseOut *= factor; + } + + JointVector::iterator ji; + for (ji = mJoints.begin(); ji != mJoints.end(); ++ji) + { + Joint *joint = *ji; + BOOL pos_changed = FALSE; + BOOL rot_changed = FALSE; + + if ( ! joint->mIgnore ) + { + joint->mNumPosKeys = 0; + joint->mNumRotKeys = 0; + LLQuaternion::Order order = bvhStringToOrder( joint->mOrder ); + + KeyVector::iterator first_key = joint->mKeys.begin(); + + // no keys? + if (first_key == joint->mKeys.end()) + { + joint->mIgnore = TRUE; + continue; + } + + LLVector3 first_frame_pos(first_key->mPos); + LLQuaternion first_frame_rot = mayaQ( first_key->mRot[0], first_key->mRot[1], first_key->mRot[2], order); + + // skip first key + KeyVector::iterator ki = joint->mKeys.begin(); + if (joint->mKeys.size() == 1) + { + //FIXME: use single frame to move pelvis + // if only one keyframe force output for this joint + rot_changed = TRUE; + } + else + { + // if more than one keyframe, use first frame as reference and skip to second + first_key->mIgnorePos = TRUE; + first_key->mIgnoreRot = TRUE; + ++ki; + } + + KeyVector::iterator ki_prev = ki; + KeyVector::iterator ki_last_good_pos = ki; + KeyVector::iterator ki_last_good_rot = ki; + S32 numPosFramesConsidered = 2; + S32 numRotFramesConsidered = 2; + + F32 rot_threshold = ROTATION_KEYFRAME_THRESHOLD / llmax((F32)joint->mChildTreeMaxDepth * 0.33f, 1.f); + + for (; ki != joint->mKeys.end(); ++ki) + { + if (ki_prev == ki_last_good_pos) + { + joint->mNumPosKeys++; + if (dist_vec(LLVector3(ki_prev->mPos), first_frame_pos) > POSITION_MOTION_THRESHOLD) + { + pos_changed = TRUE; + } + } + else + { + //check position for noticeable effect + LLVector3 test_pos(ki_prev->mPos); + LLVector3 last_good_pos(ki_last_good_pos->mPos); + LLVector3 current_pos(ki->mPos); + LLVector3 interp_pos = lerp(current_pos, last_good_pos, 1.f / (F32)numPosFramesConsidered); + + if (dist_vec(current_pos, first_frame_pos) > POSITION_MOTION_THRESHOLD) + { + pos_changed = TRUE; + } + + if (dist_vec(interp_pos, test_pos) < POSITION_KEYFRAME_THRESHOLD) + { + ki_prev->mIgnorePos = TRUE; + numPosFramesConsidered++; + } + else + { + numPosFramesConsidered = 2; + ki_last_good_pos = ki_prev; + joint->mNumPosKeys++; + } + } + + if (ki_prev == ki_last_good_rot) + { + joint->mNumRotKeys++; + LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order); + F32 x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot); + F32 y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot); + F32 rot_test = x_delta + y_delta; + + if (rot_test > ROTATION_MOTION_THRESHOLD) + { + rot_changed = TRUE; + } + } + else + { + //check rotation for noticeable effect + LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order); + LLQuaternion last_good_rot = mayaQ( ki_last_good_rot->mRot[0], ki_last_good_rot->mRot[1], ki_last_good_rot->mRot[2], order); + LLQuaternion current_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order); + LLQuaternion interp_rot = lerp(1.f / (F32)numRotFramesConsidered, current_rot, last_good_rot); + + F32 x_delta; + F32 y_delta; + F32 rot_test; + + x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot); + y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot); + rot_test = x_delta + y_delta; + + if (rot_test > ROTATION_MOTION_THRESHOLD) + { + rot_changed = TRUE; + } + + x_delta = dist_vec(LLVector3::x_axis * interp_rot, LLVector3::x_axis * test_rot); + y_delta = dist_vec(LLVector3::y_axis * interp_rot, LLVector3::y_axis * test_rot); + rot_test = x_delta + y_delta; + + if (rot_test < rot_threshold) + { + ki_prev->mIgnoreRot = TRUE; + numRotFramesConsidered++; + } + else + { + numRotFramesConsidered = 2; + ki_last_good_rot = ki_prev; + joint->mNumRotKeys++; + } + } + + ki_prev = ki; + } + } + + // don't output joints with no motion + if (!(pos_changed || rot_changed)) + { + //llinfos << "Ignoring joint " << joint->mName << llendl; + joint->mIgnore = TRUE; + } + } +} + +void LLBVHLoader::reset() +{ + mLineNumber = 0; + mNumFrames = 0; + mFrameTime = 0.0f; + mDuration = 0.0f; + + mPriority = 2; + mLoop = FALSE; + mLoopInPoint = 0.f; + mLoopOutPoint = 0.f; + mEaseIn = 0.3f; + mEaseOut = 0.3f; + mHand = 1; + mInitialized = FALSE; + + mEmoteName = ""; +} + +//------------------------------------------------------------------------ +// LLBVHLoader::getLine() +//------------------------------------------------------------------------ +BOOL LLBVHLoader::getLine(apr_file_t* fp) +{ + if (apr_file_eof(fp) == APR_EOF) + { + return FALSE; + } + if ( apr_file_gets(mLine, BVH_PARSER_LINE_SIZE, fp) == APR_SUCCESS) + { + mLineNumber++; + return TRUE; + } + + return FALSE; +} + +// returns required size of output buffer +U32 LLBVHLoader::getOutputSize() +{ + LLDataPackerBinaryBuffer dp; + serialize(dp); + + return dp.getCurrentSize(); +} + +// writes contents to datapacker +BOOL LLBVHLoader::serialize(LLDataPacker& dp) +{ + JointVector::iterator ji; + KeyVector::iterator ki; + F32 time; + + // count number of non-ignored joints + S32 numJoints = 0; + for (ji=mJoints.begin(); ji!=mJoints.end(); ++ji) + { + Joint *joint = *ji; + if ( ! joint->mIgnore ) + numJoints++; + } + + // print header + dp.packU16(KEYFRAME_MOTION_VERSION, "version"); + dp.packU16(KEYFRAME_MOTION_SUBVERSION, "sub_version"); + dp.packS32(mPriority, "base_priority"); + dp.packF32(mDuration, "duration"); + dp.packString(mEmoteName.c_str(), "emote_name"); + dp.packF32(mLoopInPoint, "loop_in_point"); + dp.packF32(mLoopOutPoint, "loop_out_point"); + dp.packS32(mLoop, "loop"); + dp.packF32(mEaseIn, "ease_in_duration"); + dp.packF32(mEaseOut, "ease_out_duration"); + dp.packU32(mHand, "hand_pose"); + dp.packU32(numJoints, "num_joints"); + + for ( ji = mJoints.begin(); + ji != mJoints.end(); + ++ji ) + { + Joint *joint = *ji; + // if ignored, skip it + if ( joint->mIgnore ) + continue; + + LLQuaternion first_frame_rot; + LLQuaternion fixup_rot; + + dp.packString(joint->mOutName.c_str(), "joint_name"); + dp.packS32(joint->mPriority, "joint_priority"); + + // compute coordinate frame rotation + LLQuaternion frameRot( joint->mFrameMatrix ); + LLQuaternion frameRotInv = ~frameRot; + + LLQuaternion offsetRot( joint->mOffsetMatrix ); + + // find mergechild and mergeparent joints, if specified + LLQuaternion mergeParentRot; + LLQuaternion mergeChildRot; + Joint *mergeParent = NULL; + Joint *mergeChild = NULL; + + JointVector::iterator mji; + for (mji=mJoints.begin(); mji!=mJoints.end(); ++mji) + { + Joint *mjoint = *mji; + if ( !joint->mMergeParentName.empty() && (mjoint->mName == joint->mMergeParentName) ) + { + mergeParent = *mji; + } + if ( !joint->mMergeChildName.empty() && (mjoint->mName == joint->mMergeChildName) ) + { + mergeChild = *mji; + } + } + + dp.packS32(joint->mNumRotKeys, "num_rot_keys"); + + LLQuaternion::Order order = bvhStringToOrder( joint->mOrder ); + S32 outcount = 0; + S32 frame = 1; + for ( ki = joint->mKeys.begin(); + ki != joint->mKeys.end(); + ++ki ) + { + if ((frame == 1) && joint->mRelativeRotationKey) + { + first_frame_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order); + + fixup_rot.shortestArc(LLVector3::z_axis * first_frame_rot * frameRot, LLVector3::z_axis); + } + + if (ki->mIgnoreRot) + { + frame++; + continue; + } + + time = (F32)frame * mFrameTime; + + if (mergeParent) + { + mergeParentRot = mayaQ( mergeParent->mKeys[frame-1].mRot[0], + mergeParent->mKeys[frame-1].mRot[1], + mergeParent->mKeys[frame-1].mRot[2], + bvhStringToOrder(mergeParent->mOrder) ); + LLQuaternion parentFrameRot( mergeParent->mFrameMatrix ); + LLQuaternion parentOffsetRot( mergeParent->mOffsetMatrix ); + mergeParentRot = ~parentFrameRot * mergeParentRot * parentFrameRot * parentOffsetRot; + } + else + { + mergeParentRot.loadIdentity(); + } + + if (mergeChild) + { + mergeChildRot = mayaQ( mergeChild->mKeys[frame-1].mRot[0], + mergeChild->mKeys[frame-1].mRot[1], + mergeChild->mKeys[frame-1].mRot[2], + bvhStringToOrder(mergeChild->mOrder) ); + LLQuaternion childFrameRot( mergeChild->mFrameMatrix ); + LLQuaternion childOffsetRot( mergeChild->mOffsetMatrix ); + mergeChildRot = ~childFrameRot * mergeChildRot * childFrameRot * childOffsetRot; + + } + else + { + mergeChildRot.loadIdentity(); + } + + LLQuaternion inRot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order); + + LLQuaternion outRot = frameRotInv* mergeChildRot * inRot * mergeParentRot * ~first_frame_rot * frameRot * offsetRot; + + U16 time_short = F32_to_U16(time, 0.f, mDuration); + dp.packU16(time_short, "time"); + U16 x, y, z; + LLVector3 rot_vec = outRot.packToVector3(); + rot_vec.quantize16(-1.f, 1.f, -1.f, 1.f); + x = F32_to_U16(rot_vec.mV[VX], -1.f, 1.f); + y = F32_to_U16(rot_vec.mV[VY], -1.f, 1.f); + z = F32_to_U16(rot_vec.mV[VZ], -1.f, 1.f); + dp.packU16(x, "rot_angle_x"); + dp.packU16(y, "rot_angle_y"); + dp.packU16(z, "rot_angle_z"); + outcount++; + frame++; + } + + // output position keys (only for 1st joint) + if ( ji == mJoints.begin() && !joint->mIgnorePositions ) + { + dp.packS32(joint->mNumPosKeys, "num_pos_keys"); + + LLVector3 relPos = joint->mRelativePosition; + LLVector3 relKey; + + frame = 1; + for ( ki = joint->mKeys.begin(); + ki != joint->mKeys.end(); + ++ki ) + { + if ((frame == 1) && joint->mRelativePositionKey) + { + relKey.setVec(ki->mPos); + } + + if (ki->mIgnorePos) + { + frame++; + continue; + } + + time = (F32)frame * mFrameTime; + + LLVector3 inPos = (LLVector3(ki->mPos) - relKey) * ~first_frame_rot;// * fixup_rot; + LLVector3 outPos = inPos * frameRot * offsetRot; + + outPos *= INCHES_TO_METERS; + + outPos -= relPos; + outPos.clamp(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + + U16 time_short = F32_to_U16(time, 0.f, mDuration); + dp.packU16(time_short, "time"); + + U16 x, y, z; + outPos.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + x = F32_to_U16(outPos.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + y = F32_to_U16(outPos.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + z = F32_to_U16(outPos.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET); + dp.packU16(x, "pos_x"); + dp.packU16(y, "pos_y"); + dp.packU16(z, "pos_z"); + + frame++; + } + } + else + { + dp.packS32(0, "num_pos_keys"); + } + } + + S32 num_constraints = (S32)mConstraints.size(); + dp.packS32(num_constraints, "num_constraints"); + + for (ConstraintVector::iterator constraint_it = mConstraints.begin(); + constraint_it != mConstraints.end(); + constraint_it++) + { + U8 byte = constraint_it->mChainLength; + dp.packU8(byte, "chain_lenght"); + + byte = constraint_it->mConstraintType; + dp.packU8(byte, "constraint_type"); + dp.packBinaryDataFixed((U8*)constraint_it->mSourceJointName, 16, "source_volume"); + dp.packVector3(constraint_it->mSourceOffset, "source_offset"); + dp.packBinaryDataFixed((U8*)constraint_it->mTargetJointName, 16, "target_volume"); + dp.packVector3(constraint_it->mTargetOffset, "target_offset"); + dp.packVector3(constraint_it->mTargetDir, "target_dir"); + dp.packF32(constraint_it->mEaseInStart, "ease_in_start"); + dp.packF32(constraint_it->mEaseInStop, "ease_in_stop"); + dp.packF32(constraint_it->mEaseOutStart, "ease_out_start"); + dp.packF32(constraint_it->mEaseOutStop, "ease_out_stop"); + } + + return TRUE; +} |
