/** * @file llbvhloader.cpp * @brief Translates a BVH files to LindenLabAnimation format. * * $LicenseInfo:firstyear=2004&license=viewergpl$ * * Copyright (c) 2004-2007, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "linden_common.h" #include "llbvhloader.h" #include #include "lldatapacker.h" #include "lldir.h" #include "llkeyframemotion.h" #include "llquantize.h" #include "llstl.h" #include "llapr.h" 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 //------------------------------------------------------------------------ const char *LLBVHLoader::ST_OK = "Ok"; const char *LLBVHLoader::ST_EOF = "Premature end of file."; const char *LLBVHLoader::ST_NO_CONSTRAINT = "Can't read constraint definition."; const char *LLBVHLoader::ST_NO_FILE = "Can't open BVH file."; const char *LLBVHLoader::ST_NO_HIER = "Invalid HIERARCHY header."; const char *LLBVHLoader::ST_NO_JOINT = "Can't find ROOT or JOINT."; const char *LLBVHLoader::ST_NO_NAME = "Can't get JOINT name."; const char *LLBVHLoader::ST_NO_OFFSET = "Can't find OFFSET."; const char *LLBVHLoader::ST_NO_CHANNELS = "Can't find CHANNELS."; const char *LLBVHLoader::ST_NO_ROTATION = "Can't get rotation order."; const char *LLBVHLoader::ST_NO_AXIS = "Can't get rotation axis."; const char *LLBVHLoader::ST_NO_MOTION = "Can't find MOTION."; const char *LLBVHLoader::ST_NO_FRAMES = "Can't get number of frames."; const char *LLBVHLoader::ST_NO_FRAME_TIME = "Can't get frame time."; const char *LLBVHLoader::ST_NO_POS = "Can't get position values."; const char *LLBVHLoader::ST_NO_ROT = "Can't get rotation values."; const char *LLBVHLoader::ST_NO_XLT_FILE = "Can't open translation file."; const char *LLBVHLoader::ST_NO_XLT_HEADER = "Can't read translation header."; const char *LLBVHLoader::ST_NO_XLT_NAME = "Can't read translation names."; const char *LLBVHLoader::ST_NO_XLT_IGNORE = "Can't read translation ignore value."; const char *LLBVHLoader::ST_NO_XLT_RELATIVE = "Can't read translation relative value."; const char *LLBVHLoader::ST_NO_XLT_OUTNAME = "Can't read translation outname value."; const char *LLBVHLoader::ST_NO_XLT_MATRIX = "Can't read translation matrix."; const char *LLBVHLoader::ST_NO_XLT_MERGECHILD = "Can't get mergechild name."; const char *LLBVHLoader::ST_NO_XLT_MERGEPARENT = "Can't get mergeparent name."; const char *LLBVHLoader::ST_NO_XLT_PRIORITY = "Can't get priority value."; const char *LLBVHLoader::ST_NO_XLT_LOOP = "Can't get loop value."; const char *LLBVHLoader::ST_NO_XLT_EASEIN = "Can't get easeIn values."; const char *LLBVHLoader::ST_NO_XLT_EASEOUT = "Can't get easeOut values."; const char *LLBVHLoader::ST_NO_XLT_HAND = "Can't get hand morph value."; const 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 //-------------------------------------------------------------------- std::string path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName); 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 ) /* Flawfinder: ignore */ 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 && LLStringUtil::compareInsensitive(token, "emote")==0) { char emote_str[1024]; /* Flawfinder: ignore */ if ( sscanf(mLine, " %*s = %1023s", emote_str) != 1 ) /* Flawfinder: ignore */ return ST_NO_XLT_EMOTE; mEmoteName.assign( emote_str ); // llinfos << "NOTE: Emote: " << mEmoteName.c_str() << llendl; continue; } //---------------------------------------------------------------- // check for global priority setting //---------------------------------------------------------------- if (loadingGlobals && LLStringUtil::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 && LLStringUtil::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 ) /* Flawfinder: ignore */ { mLoop = (LLStringUtil::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 && LLStringUtil::compareInsensitive(token, "easein")==0) { F32 duration; char type[128]; /* Flawfinder: ignore */ if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 ) /* Flawfinder: ignore */ return ST_NO_XLT_EASEIN; mEaseIn = duration; continue; } //---------------------------------------------------------------- // check for global easeOut setting //---------------------------------------------------------------- if (loadingGlobals && LLStringUtil::compareInsensitive(token, "easeout")==0) { F32 duration; char type[128]; /* Flawfinder: ignore */ if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 ) /* Flawfinder: ignore */ return ST_NO_XLT_EASEOUT; mEaseOut = duration; continue; } //---------------------------------------------------------------- // check for global handMorph setting //---------------------------------------------------------------- if (loadingGlobals && LLStringUtil::compareInsensitive(token, "hand")==0) { S32 handMorph; if (sscanf(mLine, " %*s = %d", &handMorph) != 1) return ST_NO_XLT_HAND; mHand = handMorph; continue; } if (loadingGlobals && LLStringUtil::compareInsensitive(token, "constraint")==0) { Constraint constraint; // try reading optional target direction if(sscanf( /* Flawfinder: ignore */ 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( /* Flawfinder: ignore */ 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 && LLStringUtil::compareInsensitive(token, "planar_constraint")==0) { Constraint constraint; // try reading optional target direction if(sscanf( /* Flawfinder: ignore */ 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( /* Flawfinder: ignore */ 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 ( LLStringUtil::compareInsensitive(token, "ignore")==0 ) { char trueFalse[128]; /* Flawfinder: ignore */ if ( sscanf(mLine, " %*s = %127s", trueFalse) != 1 ) /* Flawfinder: ignore */ return ST_NO_XLT_IGNORE; trans->mIgnore = (LLStringUtil::compareInsensitive(trueFalse, "true")==0); continue; } //---------------------------------------------------------------- // check for relativepos flag //---------------------------------------------------------------- if ( LLStringUtil::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 ) /* Flawfinder: ignore */ { if ( LLStringUtil::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 ( LLStringUtil::compareInsensitive(token, "relativerot")==0 ) { //F32 x, y, z; char relpos[128]; /* Flawfinder: ignore */ if ( sscanf(mLine, " %*s = %127s", relpos) == 1 ) /* Flawfinder: ignore */ { if ( LLStringUtil::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 ( LLStringUtil::compareInsensitive(token, "outname")==0 ) { char outName[128]; /* Flawfinder: ignore */ if ( sscanf(mLine, " %*s = %127s", outName) != 1 ) /* Flawfinder: ignore */ return ST_NO_XLT_OUTNAME; trans->mOutName = outName; continue; } //---------------------------------------------------------------- // check for frame matrix value //---------------------------------------------------------------- if ( LLStringUtil::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 ( LLStringUtil::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 ( LLStringUtil::compareInsensitive(token, "mergeparent")==0 ) { char mergeParentName[128]; /* Flawfinder: ignore */ if ( sscanf(mLine, " %*s = %127s", mergeParentName) != 1 ) /* Flawfinder: ignore */ return ST_NO_XLT_MERGEPARENT; trans->mMergeParentName = mergeParentName; continue; } //---------------------------------------------------------------- // check for mergechild value //---------------------------------------------------------------- if ( LLStringUtil::compareInsensitive(token, "mergechild")==0 ) { char mergeChildName[128]; /* Flawfinder: ignore */ if ( sscanf(mLine, " %*s = %127s", mergeChildName) != 1 ) /* Flawfinder: ignore */ return ST_NO_XLT_MERGECHILD; trans->mMergeChildName = mergeChildName; continue; } //---------------------------------------------------------------- // check for per-joint priority //---------------------------------------------------------------- if ( LLStringUtil::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 > tokenizer; boost::char_separator sep("\r\n"); tokenizer tokens(str, sep); tokenizer::iterator iter = tokens.begin(); mLineNumber = 0; mJoints.clear(); std::vector 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 ) /* Flawfinder: ignore */ { 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; imKeys.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) { // *FIX: 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); double diff_max = 0; KeyVector::iterator ki_max = ki; 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; // Test if the rotation has changed significantly since the very first frame. If false // for all frames, then we'll just throw out this joint's rotation entirely. 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; // Draw a line between the last good keyframe and current. Test the distance between the last frame (current-1, i.e. ki_prev) // and the line. If it's greater than some threshold, then it represents a significant frame and we want to include it. if (rot_test >= rot_threshold || (ki+1 == joint->mKeys.end() && numRotFramesConsidered > 2)) { // Add the current test keyframe (which is technically the previous key, i.e. ki_prev). numRotFramesConsidered = 2; ki_last_good_rot = ki_prev; joint->mNumRotKeys++; // Add another keyframe between the last good keyframe and current, at whatever point was the most "significant" (i.e. // had the largest deviation from the earlier tests). Note that a more robust approach would be test all intermediate // keyframes against the line between the last good keyframe and current, but we're settling for this other method // because it's significantly faster. if (diff_max > 0) { if (ki_max->mIgnoreRot == TRUE) { ki_max->mIgnoreRot = FALSE; joint->mNumRotKeys++; } diff_max = 0; } } else { // This keyframe isn't significant enough, throw it away. ki_prev->mIgnoreRot = TRUE; numRotFramesConsidered++; // Store away the keyframe that has the largest deviation from the interpolated line, for insertion later. if (rot_test > diff_max) { diff_max = rot_test; ki_max = ki; } } } 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, "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, "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; }