From 70f8dc7a4f4be217fea5439e474fc75e567c23c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sat, 10 Feb 2024 22:37:52 +0100 Subject: miscellaneous: BOOL (int) to real bool --- indra/llcharacter/llbvhloader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/llcharacter/llbvhloader.cpp') diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp index 5b1b28bf4f..567bdb8c95 100644 --- a/indra/llcharacter/llbvhloader.cpp +++ b/indra/llcharacter/llbvhloader.cpp @@ -1266,15 +1266,15 @@ BOOL LLBVHLoader::getLine(apr_file_t* fp) { if (apr_file_eof(fp) == APR_EOF) { - return FALSE; + return false; } if ( apr_file_gets(mLine, BVH_PARSER_LINE_SIZE, fp) == APR_SUCCESS) { mLineNumber++; - return TRUE; + return true; } - return FALSE; + return false; } // returns required size of output buffer @@ -1496,5 +1496,5 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp) } - return TRUE; + return true; } -- cgit v1.2.3 From 9e854b697a06abed2a0917fb6120445f176764f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Fri, 16 Feb 2024 19:29:51 +0100 Subject: misc: BOOL to bool --- indra/llcharacter/llbvhloader.cpp | 62 +++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'indra/llcharacter/llbvhloader.cpp') diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp index 567bdb8c95..44e95c4e7f 100644 --- a/indra/llcharacter/llbvhloader.cpp +++ b/indra/llcharacter/llbvhloader.cpp @@ -184,7 +184,7 @@ LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &error LL_DEBUGS("BVH") << "After translations and optimize" << LL_ENDL; dumpBVHInfo(); - mInitialized = TRUE; + mInitialized = true; } @@ -227,7 +227,7 @@ ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName) //-------------------------------------------------------------------- // load data one line at a time //-------------------------------------------------------------------- - BOOL loadingGlobals = FALSE; + bool loadingGlobals = false; while ( getLine(fp) ) { //---------------------------------------------------------------- @@ -251,7 +251,7 @@ ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName) if (strcmp(name, "GLOBALS")==0) { - loadingGlobals = TRUE; + loadingGlobals = true; continue; } } @@ -298,7 +298,7 @@ ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName) if ( sscanf(mLine, " %*s = %f %f", &loop_in, &loop_out) == 2 ) { - mLoop = TRUE; + mLoop = true; } else if ( sscanf(mLine, " %*s = %127s", trueFalse) == 1 ) /* Flawfinder: ignore */ { @@ -496,8 +496,8 @@ void LLBVHLoader::makeTranslation(std::string alias_name, std::string joint_name if (joint_name == "mPelvis") { - newTrans.mRelativePositionKey = TRUE; - newTrans.mRelativeRotationKey = TRUE; + newTrans.mRelativePositionKey = true; + newTrans.mRelativeRotationKey = true; } } @@ -609,7 +609,7 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 & //-------------------------------------------------------------------- // consume joints //-------------------------------------------------------------------- - while (TRUE) + while (true) { //---------------------------------------------------------------- // get next line @@ -969,7 +969,7 @@ void LLBVHLoader::applyTranslations() if ( trans.mIgnore ) { //LL_INFOS() << "NOTE: Ignoring " << joint->mName.c_str() << LL_ENDL; - joint->mIgnore = TRUE; + joint->mIgnore = true; continue; } @@ -983,10 +983,10 @@ void LLBVHLoader::applyTranslations() } //Allow joint position changes as of SL-318 - joint->mIgnorePositions = FALSE; + joint->mIgnorePositions = false; if (joint->mNumChannels == 3) { - joint->mIgnorePositions = TRUE; + joint->mIgnorePositions = true; } //---------------------------------------------------------------- @@ -995,13 +995,13 @@ void LLBVHLoader::applyTranslations() if ( trans.mRelativePositionKey ) { // LL_INFOS() << "NOTE: Removing 1st position offset from all keys for " << joint->mOutName.c_str() << LL_ENDL; - joint->mRelativePositionKey = TRUE; + joint->mRelativePositionKey = true; } if ( trans.mRelativeRotationKey ) { // LL_INFOS() << "NOTE: Removing 1st rotation from all keys for " << joint->mOutName.c_str() << LL_ENDL; - joint->mRelativeRotationKey = TRUE; + joint->mRelativeRotationKey = true; } if ( trans.mRelativePosition.magVec() > 0.0f ) @@ -1065,8 +1065,8 @@ void LLBVHLoader::optimize() for (Joint* joint : mJoints) { - BOOL pos_changed = FALSE; - BOOL rot_changed = FALSE; + bool pos_changed = false; + bool rot_changed = false; if ( ! joint->mIgnore ) { @@ -1079,7 +1079,7 @@ void LLBVHLoader::optimize() // no keys? if (first_key == joint->mKeys.end()) { - joint->mIgnore = TRUE; + joint->mIgnore = true; continue; } @@ -1092,13 +1092,13 @@ void LLBVHLoader::optimize() { // *FIX: use single frame to move pelvis // if only one keyframe force output for this joint - rot_changed = TRUE; + 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; + first_key->mIgnorePos = true; + first_key->mIgnoreRot = true; ++ki; } @@ -1119,7 +1119,7 @@ void LLBVHLoader::optimize() joint->mNumPosKeys++; if (dist_vec_squared(LLVector3(ki_prev->mPos), first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED) { - pos_changed = TRUE; + pos_changed = true; } } else @@ -1132,12 +1132,12 @@ void LLBVHLoader::optimize() if (dist_vec_squared(current_pos, first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED) { - pos_changed = TRUE; + pos_changed = true; } if (dist_vec_squared(interp_pos, test_pos) < POSITION_KEYFRAME_THRESHOLD_SQUARED) { - ki_prev->mIgnorePos = TRUE; + ki_prev->mIgnorePos = true; numPosFramesConsidered++; } else @@ -1158,7 +1158,7 @@ void LLBVHLoader::optimize() if (rot_test > ROTATION_MOTION_THRESHOLD) { - rot_changed = TRUE; + rot_changed = true; } } else @@ -1180,7 +1180,7 @@ void LLBVHLoader::optimize() rot_test = x_delta + y_delta; if (rot_test > ROTATION_MOTION_THRESHOLD) { - rot_changed = TRUE; + 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); @@ -1202,9 +1202,9 @@ void LLBVHLoader::optimize() // because it's significantly faster. if (diff_max > 0) { - if (ki_max->mIgnoreRot == TRUE) + if (ki_max->mIgnoreRot == true) { - ki_max->mIgnoreRot = FALSE; + ki_max->mIgnoreRot = false; joint->mNumRotKeys++; } diff_max = 0; @@ -1213,7 +1213,7 @@ void LLBVHLoader::optimize() else { // This keyframe isn't significant enough, throw it away. - ki_prev->mIgnoreRot = TRUE; + 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) @@ -1232,7 +1232,7 @@ void LLBVHLoader::optimize() if (!(pos_changed || rot_changed)) { //LL_INFOS() << "Ignoring joint " << joint->mName << LL_ENDL; - joint->mIgnore = TRUE; + joint->mIgnore = true; } } } @@ -1245,13 +1245,13 @@ void LLBVHLoader::reset() mDuration = 0.0f; mPriority = 2; - mLoop = FALSE; + mLoop = false; mLoopInPoint = 0.f; mLoopOutPoint = 0.f; mEaseIn = 0.3f; mEaseOut = 0.3f; mHand = 1; - mInitialized = FALSE; + mInitialized = false; mEmoteName = ""; mLineNumber = 0; @@ -1262,7 +1262,7 @@ void LLBVHLoader::reset() //------------------------------------------------------------------------ // LLBVHLoader::getLine() //------------------------------------------------------------------------ -BOOL LLBVHLoader::getLine(apr_file_t* fp) +bool LLBVHLoader::getLine(apr_file_t* fp) { if (apr_file_eof(fp) == APR_EOF) { @@ -1287,7 +1287,7 @@ U32 LLBVHLoader::getOutputSize() } // writes contents to datapacker -BOOL LLBVHLoader::serialize(LLDataPacker& dp) +bool LLBVHLoader::serialize(LLDataPacker& dp) { F32 time; -- cgit v1.2.3 From f9473e8afcb624cc1b101195bf15943ec372b56f Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Mon, 6 May 2024 16:52:34 +0200 Subject: secondlife/viewer#1333 BOOL to bool conversion leftovers: ternaries --- indra/llcharacter/llbvhloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcharacter/llbvhloader.cpp') diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp index 44e95c4e7f..a277014ca5 100644 --- a/indra/llcharacter/llbvhloader.cpp +++ b/indra/llcharacter/llbvhloader.cpp @@ -1202,7 +1202,7 @@ void LLBVHLoader::optimize() // because it's significantly faster. if (diff_max > 0) { - if (ki_max->mIgnoreRot == true) + if (ki_max->mIgnoreRot) { ki_max->mIgnoreRot = false; joint->mNumRotKeys++; -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/llcharacter/llbvhloader.cpp | 3000 ++++++++++++++++++------------------- 1 file changed, 1500 insertions(+), 1500 deletions(-) (limited to 'indra/llcharacter/llbvhloader.cpp') diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp index 988e352fdb..9dace08e6f 100644 --- a/indra/llcharacter/llbvhloader.cpp +++ b/indra/llcharacter/llbvhloader.cpp @@ -1,1500 +1,1500 @@ -/** - * @file llbvhloader.cpp - * @brief Translates a BVH files to LindenLabAnimation format. - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful,7 - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llbvhloader.h" - -#include -#include - -#include "lldatapacker.h" -#include "lldir.h" -#include "llkeyframemotion.h" -#include "llquantize.h" -#include "llstl.h" -#include "llapr.h" -#include "llsdserialize.h" - - -using namespace std; - -#define INCHES_TO_METERS 0.02540005f - -/// The .bvh does not have a formal spec, and different readers interpret things in their own way. -/// In OUR usage, frame 0 is used in optimization and is not considered to be part of the animation. -const S32 NUMBER_OF_IGNORED_FRAMES_AT_START = 1; -/// In our usage, the last frame is used only to indicate what the penultimate frame should be interpolated towards. -/// I.e., the animation only plays up to the start of the last frame. There is no hold or exptrapolation past that point.. -/// Thus there are two frame of the total that do not contribute to the total running time of the animation. -const S32 NUMBER_OF_UNPLAYED_FRAMES = NUMBER_OF_IGNORED_FRAMES_AT_START + 1; - -const F32 POSITION_KEYFRAME_THRESHOLD_SQUARED = 0.03f * 0.03f; -const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f; - -const F32 POSITION_MOTION_THRESHOLD_SQUARED = 0.001f * 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."; -const char *LLBVHLoader::ST_BAD_ROOT = "Illegal ROOT joint."; -*/ - -//------------------------------------------------------------------------ -// 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, ELoadStatus &loadStatus, S32 &errorLine, std::map& joint_alias_map ) -{ - reset(); - errorLine = 0; - mStatus = loadTranslationTable("anim.ini"); - loadStatus = mStatus; - LL_INFOS("BVH") << "Load Status 00 : " << loadStatus << LL_ENDL; - if (mStatus == E_ST_NO_XLT_FILE) - { - LL_WARNS("BVH") << "NOTE: No translation table found." << LL_ENDL; - loadStatus = mStatus; - return; - } - else - { - if (mStatus != E_ST_OK) - { - LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL; - errorLine = getLineNumber(); - loadStatus = mStatus; - return; - } - } - - // Recognize all names we've been told are legal. - for (std::map::value_type& alias_pair : joint_alias_map) - { - makeTranslation( alias_pair.first , alias_pair.second ); - } - - char error_text[128]; /* Flawfinder: ignore */ - S32 error_line; - mStatus = loadBVHFile(buffer, error_text, error_line); //Reads all joints in BVH file. - - LL_DEBUGS("BVH") << "============================================================" << LL_ENDL; - LL_DEBUGS("BVH") << "Raw data from file" << LL_ENDL; - dumpBVHInfo(); - - if (mStatus != E_ST_OK) - { - LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL; - loadStatus = mStatus; - errorLine = getLineNumber(); - return; - } - - applyTranslations(); //Maps between joints found in file and the aliased names. - optimize(); - - LL_DEBUGS("BVH") << "============================================================" << LL_ENDL; - LL_DEBUGS("BVH") << "After translations and optimize" << LL_ENDL; - dumpBVHInfo(); - - mInitialized = true; -} - - -LLBVHLoader::~LLBVHLoader() -{ - std::for_each(mJoints.begin(),mJoints.end(),DeletePointer()); - mJoints.clear(); -} - -//------------------------------------------------------------------------ -// LLBVHLoader::loadTranslationTable() -//------------------------------------------------------------------------ -ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName) -{ - //-------------------------------------------------------------------- - // open file - //-------------------------------------------------------------------- - std::string path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName); - - LLAPRFile infile ; - infile.open(path, LL_APR_R); - apr_file_t *fp = infile.getFileHandle(); - if (!fp) - return E_ST_NO_XLT_FILE; - - LL_INFOS("BVH") << "NOTE: Loading translation table: " << fileName << LL_ENDL; - - //-------------------------------------------------------------------- - // register file to be closed on function exit - //-------------------------------------------------------------------- - - //-------------------------------------------------------------------- - // load header - //-------------------------------------------------------------------- - if ( ! getLine(fp) ) - return E_ST_EOF; - if ( strncmp(mLine, "Translations 1.0", 16) ) - return E_ST_NO_XLT_HEADER; - - //-------------------------------------------------------------------- - // load data one line at a time - //-------------------------------------------------------------------- - bool loadingGlobals = false; - 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 E_ST_NO_XLT_NAME; - - if (strcmp(name, "GLOBALS")==0) - { - loadingGlobals = true; - 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 E_ST_NO_XLT_EMOTE; - - mEmoteName.assign( emote_str ); -// LL_INFOS() << "NOTE: Emote: " << mEmoteName.c_str() << LL_ENDL; - continue; - } - - - //---------------------------------------------------------------- - // check for global priority setting - //---------------------------------------------------------------- - if (loadingGlobals && LLStringUtil::compareInsensitive(token, "priority")==0) - { - S32 priority; - if ( sscanf(mLine, " %*s = %d", &priority) != 1 ) - return E_ST_NO_XLT_PRIORITY; - - mPriority = priority; -// LL_INFOS() << "NOTE: Priority: " << mPriority << LL_ENDL; - 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 E_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 E_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 E_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 E_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 E_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 E_ST_NO_CONSTRAINT; - } - } - else - { - // normalize direction - if (!constraint.mTargetDir.isExactlyZero()) - { - constraint.mTargetDir.normVec(); - } - - } - - constraint.mConstraintType = CONSTRAINT_TYPE_PLANE; - mConstraints.push_back(constraint); - continue; - } - } - - infile.close() ; - return E_ST_OK; -} -void LLBVHLoader::makeTranslation(std::string alias_name, std::string joint_name) -{ - //Translation &newTrans = (foomap.insert(value_type(alias_name, Translation()))).first(); - Translation &newTrans = mTranslations[ alias_name ]; //Uses []'s implicit call to ctor. - - newTrans.mOutName = joint_name; - LLMatrix3 fm; - LLVector3 vect1(0, 1, 0); - LLVector3 vect2(0, 0, 1); - LLVector3 vect3(1, 0, 0); - fm.setRows(vect1, vect2, vect3); - - newTrans.mFrameMatrix = fm; - -if (joint_name == "mPelvis") - { - newTrans.mRelativePositionKey = true; - newTrans.mRelativeRotationKey = true; - } - -} - -ELoadStatus LLBVHLoader::loadAliases(const char * filename) -{ - LLSD aliases_sd; - - std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,filename); - - llifstream input_stream; - input_stream.open(fullpath.c_str(), std::ios::in | std::ios::binary); - - if(input_stream.is_open()) - { - if ( LLSDSerialize::fromXML(aliases_sd, input_stream) ) - { - for(LLSD::map_iterator alias_iter = aliases_sd.beginMap(); - alias_iter != aliases_sd.endMap(); - ++alias_iter) - { - LLSD::String alias_name = alias_iter->first; - LLSD::String joint_name = alias_iter->second; - makeTranslation(alias_name, joint_name); - - } - } - else - { - return E_ST_NO_XLT_HEADER; - } - input_stream.close(); - } - else - { - LL_WARNS("BVH") << "Can't open joint alias file " << fullpath << LL_ENDL; - return E_ST_NO_XLT_FILE; - } - - return E_ST_OK; -} - -void LLBVHLoader::dumpBVHInfo() -{ - for (U32 j=0; jmName << LL_ENDL; - for (S32 i=0; imKeys.size()) // Check this in case file load failed. - { - Key &prevkey = joint->mKeys[llmax(i-1,0)]; - Key &key = joint->mKeys[i]; - if ((i==0) || - (key.mPos[0] != prevkey.mPos[0]) || - (key.mPos[1] != prevkey.mPos[1]) || - (key.mPos[2] != prevkey.mPos[2]) || - (key.mRot[0] != prevkey.mRot[0]) || - (key.mRot[1] != prevkey.mRot[1]) || - (key.mRot[2] != prevkey.mRot[2]) - ) - { - LL_DEBUGS("BVH") << "FRAME " << i - << " POS " << key.mPos[0] << "," << key.mPos[1] << "," << key.mPos[2] - << " ROT " << key.mRot[0] << "," << key.mRot[1] << "," << key.mRot[2] << LL_ENDL; - } - } - } - } - -} - -//------------------------------------------------------------------------ -// LLBVHLoader::loadBVHFile() -//------------------------------------------------------------------------ -ELoadStatus 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 E_ST_EOF; - line = (*(iter++)); - err_line++; - - if ( !strstr(line.c_str(), "HIERARCHY") ) - { -// LL_INFOS() << line << LL_ENDL; - return E_ST_NO_HIER; - } - - //-------------------------------------------------------------------- - // consume joints - //-------------------------------------------------------------------- - while (true) - { - //---------------------------------------------------------------- - // get next line - //---------------------------------------------------------------- - if (iter == tokens.end()) - return E_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 - iter++; // } - 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 E_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 E_ST_NO_NAME; - } - - //--------------------------------------------------------------- - // we require the root joint be "hip" - DEV-26188 - //--------------------------------------------------------------- - if (mJoints.size() == 0 ) - { - //The root joint of the BVH file must be hip (mPelvis) or an alias of mPelvis. - const char* FORCED_ROOT_NAME = "hip"; - - TranslationMap::iterator hip_joint = mTranslations.find( FORCED_ROOT_NAME ); - TranslationMap::iterator root_joint = mTranslations.find( jointName ); - if ( hip_joint == mTranslations.end() || root_joint == mTranslations.end() || root_joint->second.mOutName != hip_joint->second.mOutName ) - { - strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */ - return E_ST_BAD_ROOT; - } - } - - - //---------------------------------------------------------------- - // add a set of keyframes for this joint - //---------------------------------------------------------------- - mJoints.push_back( new Joint( jointName ) ); - Joint *joint = mJoints.back(); - LL_DEBUGS("BVH") << "Created joint " << jointName << LL_ENDL; - LL_DEBUGS("BVH") << "- index " << mJoints.size()-1 << LL_ENDL; - - S32 depth = 1; - for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--) - { - Joint *pjoint = mJoints[parent_joints[j]]; - LL_DEBUGS("BVH") << "- ancestor " << pjoint->mName << LL_ENDL; - if (depth > pjoint->mChildTreeMaxDepth) - { - pjoint->mChildTreeMaxDepth = depth; - } - depth++; - } - - //---------------------------------------------------------------- - // get next line - //---------------------------------------------------------------- - if (iter == tokens.end()) - { - return E_ST_EOF; - } - line = (*(iter++)); - err_line++; - - //---------------------------------------------------------------- - // it must be { - //---------------------------------------------------------------- - if ( !strstr(line.c_str(), "{") ) - { - strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ - return E_ST_NO_OFFSET; - } - else - { - parent_joints.push_back((S32)mJoints.size() - 1); - } - - //---------------------------------------------------------------- - // get next line - //---------------------------------------------------------------- - if (iter == tokens.end()) - { - return E_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 E_ST_NO_OFFSET; - } - - //---------------------------------------------------------------- - // get next line - //---------------------------------------------------------------- - if (iter == tokens.end()) - { - return E_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 E_ST_NO_CHANNELS; - } - - // Animating position (via mNumChannels = 6) is only supported for mPelvis. - int res = sscanf(line.c_str(), " CHANNELS %d", &joint->mNumChannels); - if ( res != 1 ) - { - // Assume default if not otherwise specified. - if (mJoints.size()==1) - { - joint->mNumChannels = 6; - } - else - { - joint->mNumChannels = 3; - } - } - - //---------------------------------------------------------------- - // 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 E_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 E_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 E_ST_NO_MOTION; - } - - //-------------------------------------------------------------------- - // get number of frames - //-------------------------------------------------------------------- - if (iter == tokens.end()) - { - return E_ST_EOF; - } - line = (*(iter++)); - err_line++; - - if ( !strstr(line.c_str(), "Frames:") ) - { - strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ - return E_ST_NO_FRAMES; - } - - if ( sscanf(line.c_str(), "Frames: %d", &mNumFrames) != 1 ) - { - strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ - return E_ST_NO_FRAMES; - } - - //-------------------------------------------------------------------- - // get frame time - //-------------------------------------------------------------------- - if (iter == tokens.end()) - { - return E_ST_EOF; - } - line = (*(iter++)); - err_line++; - - if ( !strstr(line.c_str(), "Frame Time:") ) - { - strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ - return E_ST_NO_FRAME_TIME; - } - - if ( sscanf(line.c_str(), "Frame Time: %f", &mFrameTime) != 1 ) - { - strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ - return E_ST_NO_FRAME_TIME; - } - - // If the user only supplies one animation frame (after the ignored reference frame 0), hold for mFrameTime. - // If the user supples exactly one total frame, it isn't clear if that is a pose or reference frame, and the - // behavior is not defined. In this case, retain historical undefined behavior. - mDuration = llmax((F32)(mNumFrames - NUMBER_OF_UNPLAYED_FRAMES), 1.0f) * mFrameTime; - if (!mLoop) - { - mLoopOutPoint = mDuration; - } - - //-------------------------------------------------------------------- - // load frames - //-------------------------------------------------------------------- - for (S32 i=0; i floats; - boost::char_separator whitespace_sep("\t "); - tokenizer float_tokens(line, whitespace_sep); - tokenizer::iterator float_token_iter = float_tokens.begin(); - while (float_token_iter != float_tokens.end()) - { - try - { - F32 val = boost::lexical_cast(*float_token_iter); - floats.push_back(val); - } - catch (const boost::bad_lexical_cast&) - { - strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ - return E_ST_NO_POS; - } - float_token_iter++; - } - LL_DEBUGS("BVH") << "Got " << floats.size() << " floats " << LL_ENDL; - for (U32 j=0; jmKeys.push_back( Key() ); - Key &key = joint->mKeys.back(); - - if (floats.size() < joint->mNumChannels) - { - strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ - return E_ST_NO_POS; - } - - // assume either numChannels == 6, in which case we have pos + rot, - // or numChannels == 3, in which case we have only rot. - if (joint->mNumChannels == 6) - { - key.mPos[0] = floats.front(); floats.pop_front(); - key.mPos[1] = floats.front(); floats.pop_front(); - key.mPos[2] = floats.front(); floats.pop_front(); - } - key.mRot[ joint->mOrder[0]-'X' ] = floats.front(); floats.pop_front(); - key.mRot[ joint->mOrder[1]-'X' ] = floats.front(); floats.pop_front(); - key.mRot[ joint->mOrder[2]-'X' ] = floats.front(); floats.pop_front(); - } - } - - return E_ST_OK; -} - - -//------------------------------------------------------------------------ -// LLBVHLoader::applyTranslation() -//------------------------------------------------------------------------ -void LLBVHLoader::applyTranslations() -{ - for (Joint* joint : mJoints) - { - //---------------------------------------------------------------- - // 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 ) - { - //LL_INFOS() << "NOTE: Ignoring " << joint->mName.c_str() << LL_ENDL; - joint->mIgnore = true; - continue; - } - - //---------------------------------------------------------------- - // Set the output name - //---------------------------------------------------------------- - if ( ! trans.mOutName.empty() ) - { - //LL_INFOS() << "NOTE: Changing " << joint->mName.c_str() << " to " << trans.mOutName.c_str() << LL_ENDL; - joint->mOutName = trans.mOutName; - } - - //Allow joint position changes as of SL-318 - joint->mIgnorePositions = false; - if (joint->mNumChannels == 3) - { - joint->mIgnorePositions = true; - } - - //---------------------------------------------------------------- - // Set the relativepos flags if necessary - //---------------------------------------------------------------- - if ( trans.mRelativePositionKey ) - { -// LL_INFOS() << "NOTE: Removing 1st position offset from all keys for " << joint->mOutName.c_str() << LL_ENDL; - joint->mRelativePositionKey = true; - } - - if ( trans.mRelativeRotationKey ) - { -// LL_INFOS() << "NOTE: Removing 1st rotation from all keys for " << joint->mOutName.c_str() << LL_ENDL; - joint->mRelativeRotationKey = true; - } - - if ( trans.mRelativePosition.magVec() > 0.0f ) - { - joint->mRelativePosition = trans.mRelativePosition; -// LL_INFOS() << "NOTE: Removing " << -// joint->mRelativePosition.mV[0] << " " << -// joint->mRelativePosition.mV[1] << " " << -// joint->mRelativePosition.mV[2] << -// " from all position keys in " << -// joint->mOutName.c_str() << LL_ENDL; - } - - //---------------------------------------------------------------- - // Set change of coordinate frame - //---------------------------------------------------------------- - joint->mFrameMatrix = trans.mFrameMatrix; - joint->mOffsetMatrix = trans.mOffsetMatrix; - - //---------------------------------------------------------------- - // Set mergeparent name - //---------------------------------------------------------------- - if ( ! trans.mMergeParentName.empty() ) - { -// LL_INFOS() << "NOTE: Merging " << joint->mOutName.c_str() << -// " with parent " << -// trans.mMergeParentName.c_str() << LL_ENDL; - joint->mMergeParentName = trans.mMergeParentName; - } - - //---------------------------------------------------------------- - // Set mergechild name - //---------------------------------------------------------------- - if ( ! trans.mMergeChildName.empty() ) - { -// LL_INFOS() << "NOTE: Merging " << joint->mName.c_str() << -// " with child " << trans.mMergeChildName.c_str() << LL_ENDL; - 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; - } - - for (Joint* joint : mJoints) - { - 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_squared(LLVector3(ki_prev->mPos), first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED) - { - 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_squared(current_pos, first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED) - { - pos_changed = true; - } - - if (dist_vec_squared(interp_pos, test_pos) < POSITION_KEYFRAME_THRESHOLD_SQUARED) - { - 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) - { - 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)) - { - //LL_INFOS() << "Ignoring joint " << joint->mName << LL_ENDL; - 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 = ""; - mLineNumber = 0; - mTranslations.clear(); - mConstraints.clear(); -} - -//------------------------------------------------------------------------ -// 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) -{ - F32 time; - - // count number of non-ignored joints - S32 numJoints = 0; - for (Joint* joint : mJoints) - { - 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 (Joint* joint : mJoints) - { - // 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; - - for (Joint* mjoint : mJoints) - { - if ( !joint->mMergeParentName.empty() && (mjoint->mName == joint->mMergeParentName) ) - { - mergeParent = mjoint; - } - if ( !joint->mMergeChildName.empty() && (mjoint->mName == joint->mMergeChildName) ) - { - mergeChild = mjoint; - } - } - - dp.packS32(joint->mNumRotKeys, "num_rot_keys"); - - LLQuaternion::Order order = bvhStringToOrder( joint->mOrder ); - S32 frame = 0; - for (Key& key : joint->mKeys) - { - - if ((frame == 0) && joint->mRelativeRotationKey) - { - first_frame_rot = mayaQ( key.mRot[0], key.mRot[1], key.mRot[2], order); - - fixup_rot.shortestArc(LLVector3::z_axis * first_frame_rot * frameRot, LLVector3::z_axis); - } - - if (key.mIgnoreRot) - { - frame++; - continue; - } - - time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts. - - 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( key.mRot[0], key.mRot[1], key.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"); - frame++; - } - - // output position keys if joint has motion. - if ( !joint->mIgnorePositions ) - { - dp.packS32(joint->mNumPosKeys, "num_pos_keys"); - - LLVector3 relPos = joint->mRelativePosition; - LLVector3 relKey; - - frame = 0; - for (Key& key : joint->mKeys) - { - if ((frame == 0) && joint->mRelativePositionKey) - { - relKey.setVec(key.mPos); - } - - if (key.mIgnorePos) - { - frame++; - continue; - } - - time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts. - - LLVector3 inPos = (LLVector3(key.mPos) - relKey) * ~first_frame_rot;// * fixup_rot; - LLVector3 outPos = inPos * frameRot * offsetRot; - - outPos *= INCHES_TO_METERS; - - //SL-318 Pelvis position can only move 5m. Limiting all joint position offsets to this dist. - 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 (Constraint& constraint : mConstraints) - { - U8 byte = constraint.mChainLength; - dp.packU8(byte, "chain_length"); - - byte = constraint.mConstraintType; - dp.packU8(byte, "constraint_type"); - dp.packBinaryDataFixed((U8*)constraint.mSourceJointName, 16, "source_volume"); - dp.packVector3(constraint.mSourceOffset, "source_offset"); - dp.packBinaryDataFixed((U8*)constraint.mTargetJointName, 16, "target_volume"); - dp.packVector3(constraint.mTargetOffset, "target_offset"); - dp.packVector3(constraint.mTargetDir, "target_dir"); - dp.packF32(constraint.mEaseInStart, "ease_in_start"); - dp.packF32(constraint.mEaseInStop, "ease_in_stop"); - dp.packF32(constraint.mEaseOutStart, "ease_out_start"); - dp.packF32(constraint.mEaseOutStop, "ease_out_stop"); - } - - - return true; -} +/** + * @file llbvhloader.cpp + * @brief Translates a BVH files to LindenLabAnimation format. + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful,7 + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llbvhloader.h" + +#include +#include + +#include "lldatapacker.h" +#include "lldir.h" +#include "llkeyframemotion.h" +#include "llquantize.h" +#include "llstl.h" +#include "llapr.h" +#include "llsdserialize.h" + + +using namespace std; + +#define INCHES_TO_METERS 0.02540005f + +/// The .bvh does not have a formal spec, and different readers interpret things in their own way. +/// In OUR usage, frame 0 is used in optimization and is not considered to be part of the animation. +const S32 NUMBER_OF_IGNORED_FRAMES_AT_START = 1; +/// In our usage, the last frame is used only to indicate what the penultimate frame should be interpolated towards. +/// I.e., the animation only plays up to the start of the last frame. There is no hold or exptrapolation past that point.. +/// Thus there are two frame of the total that do not contribute to the total running time of the animation. +const S32 NUMBER_OF_UNPLAYED_FRAMES = NUMBER_OF_IGNORED_FRAMES_AT_START + 1; + +const F32 POSITION_KEYFRAME_THRESHOLD_SQUARED = 0.03f * 0.03f; +const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f; + +const F32 POSITION_MOTION_THRESHOLD_SQUARED = 0.001f * 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."; +const char *LLBVHLoader::ST_BAD_ROOT = "Illegal ROOT joint."; +*/ + +//------------------------------------------------------------------------ +// 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, ELoadStatus &loadStatus, S32 &errorLine, std::map& joint_alias_map ) +{ + reset(); + errorLine = 0; + mStatus = loadTranslationTable("anim.ini"); + loadStatus = mStatus; + LL_INFOS("BVH") << "Load Status 00 : " << loadStatus << LL_ENDL; + if (mStatus == E_ST_NO_XLT_FILE) + { + LL_WARNS("BVH") << "NOTE: No translation table found." << LL_ENDL; + loadStatus = mStatus; + return; + } + else + { + if (mStatus != E_ST_OK) + { + LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL; + errorLine = getLineNumber(); + loadStatus = mStatus; + return; + } + } + + // Recognize all names we've been told are legal. + for (std::map::value_type& alias_pair : joint_alias_map) + { + makeTranslation( alias_pair.first , alias_pair.second ); + } + + char error_text[128]; /* Flawfinder: ignore */ + S32 error_line; + mStatus = loadBVHFile(buffer, error_text, error_line); //Reads all joints in BVH file. + + LL_DEBUGS("BVH") << "============================================================" << LL_ENDL; + LL_DEBUGS("BVH") << "Raw data from file" << LL_ENDL; + dumpBVHInfo(); + + if (mStatus != E_ST_OK) + { + LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL; + loadStatus = mStatus; + errorLine = getLineNumber(); + return; + } + + applyTranslations(); //Maps between joints found in file and the aliased names. + optimize(); + + LL_DEBUGS("BVH") << "============================================================" << LL_ENDL; + LL_DEBUGS("BVH") << "After translations and optimize" << LL_ENDL; + dumpBVHInfo(); + + mInitialized = true; +} + + +LLBVHLoader::~LLBVHLoader() +{ + std::for_each(mJoints.begin(),mJoints.end(),DeletePointer()); + mJoints.clear(); +} + +//------------------------------------------------------------------------ +// LLBVHLoader::loadTranslationTable() +//------------------------------------------------------------------------ +ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName) +{ + //-------------------------------------------------------------------- + // open file + //-------------------------------------------------------------------- + std::string path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName); + + LLAPRFile infile ; + infile.open(path, LL_APR_R); + apr_file_t *fp = infile.getFileHandle(); + if (!fp) + return E_ST_NO_XLT_FILE; + + LL_INFOS("BVH") << "NOTE: Loading translation table: " << fileName << LL_ENDL; + + //-------------------------------------------------------------------- + // register file to be closed on function exit + //-------------------------------------------------------------------- + + //-------------------------------------------------------------------- + // load header + //-------------------------------------------------------------------- + if ( ! getLine(fp) ) + return E_ST_EOF; + if ( strncmp(mLine, "Translations 1.0", 16) ) + return E_ST_NO_XLT_HEADER; + + //-------------------------------------------------------------------- + // load data one line at a time + //-------------------------------------------------------------------- + bool loadingGlobals = false; + 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 E_ST_NO_XLT_NAME; + + if (strcmp(name, "GLOBALS")==0) + { + loadingGlobals = true; + 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 E_ST_NO_XLT_EMOTE; + + mEmoteName.assign( emote_str ); +// LL_INFOS() << "NOTE: Emote: " << mEmoteName.c_str() << LL_ENDL; + continue; + } + + + //---------------------------------------------------------------- + // check for global priority setting + //---------------------------------------------------------------- + if (loadingGlobals && LLStringUtil::compareInsensitive(token, "priority")==0) + { + S32 priority; + if ( sscanf(mLine, " %*s = %d", &priority) != 1 ) + return E_ST_NO_XLT_PRIORITY; + + mPriority = priority; +// LL_INFOS() << "NOTE: Priority: " << mPriority << LL_ENDL; + 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 E_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 E_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 E_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 E_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 E_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 E_ST_NO_CONSTRAINT; + } + } + else + { + // normalize direction + if (!constraint.mTargetDir.isExactlyZero()) + { + constraint.mTargetDir.normVec(); + } + + } + + constraint.mConstraintType = CONSTRAINT_TYPE_PLANE; + mConstraints.push_back(constraint); + continue; + } + } + + infile.close() ; + return E_ST_OK; +} +void LLBVHLoader::makeTranslation(std::string alias_name, std::string joint_name) +{ + //Translation &newTrans = (foomap.insert(value_type(alias_name, Translation()))).first(); + Translation &newTrans = mTranslations[ alias_name ]; //Uses []'s implicit call to ctor. + + newTrans.mOutName = joint_name; + LLMatrix3 fm; + LLVector3 vect1(0, 1, 0); + LLVector3 vect2(0, 0, 1); + LLVector3 vect3(1, 0, 0); + fm.setRows(vect1, vect2, vect3); + + newTrans.mFrameMatrix = fm; + +if (joint_name == "mPelvis") + { + newTrans.mRelativePositionKey = true; + newTrans.mRelativeRotationKey = true; + } + +} + +ELoadStatus LLBVHLoader::loadAliases(const char * filename) +{ + LLSD aliases_sd; + + std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,filename); + + llifstream input_stream; + input_stream.open(fullpath.c_str(), std::ios::in | std::ios::binary); + + if(input_stream.is_open()) + { + if ( LLSDSerialize::fromXML(aliases_sd, input_stream) ) + { + for(LLSD::map_iterator alias_iter = aliases_sd.beginMap(); + alias_iter != aliases_sd.endMap(); + ++alias_iter) + { + LLSD::String alias_name = alias_iter->first; + LLSD::String joint_name = alias_iter->second; + makeTranslation(alias_name, joint_name); + + } + } + else + { + return E_ST_NO_XLT_HEADER; + } + input_stream.close(); + } + else + { + LL_WARNS("BVH") << "Can't open joint alias file " << fullpath << LL_ENDL; + return E_ST_NO_XLT_FILE; + } + + return E_ST_OK; +} + +void LLBVHLoader::dumpBVHInfo() +{ + for (U32 j=0; jmName << LL_ENDL; + for (S32 i=0; imKeys.size()) // Check this in case file load failed. + { + Key &prevkey = joint->mKeys[llmax(i-1,0)]; + Key &key = joint->mKeys[i]; + if ((i==0) || + (key.mPos[0] != prevkey.mPos[0]) || + (key.mPos[1] != prevkey.mPos[1]) || + (key.mPos[2] != prevkey.mPos[2]) || + (key.mRot[0] != prevkey.mRot[0]) || + (key.mRot[1] != prevkey.mRot[1]) || + (key.mRot[2] != prevkey.mRot[2]) + ) + { + LL_DEBUGS("BVH") << "FRAME " << i + << " POS " << key.mPos[0] << "," << key.mPos[1] << "," << key.mPos[2] + << " ROT " << key.mRot[0] << "," << key.mRot[1] << "," << key.mRot[2] << LL_ENDL; + } + } + } + } + +} + +//------------------------------------------------------------------------ +// LLBVHLoader::loadBVHFile() +//------------------------------------------------------------------------ +ELoadStatus 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 E_ST_EOF; + line = (*(iter++)); + err_line++; + + if ( !strstr(line.c_str(), "HIERARCHY") ) + { +// LL_INFOS() << line << LL_ENDL; + return E_ST_NO_HIER; + } + + //-------------------------------------------------------------------- + // consume joints + //-------------------------------------------------------------------- + while (true) + { + //---------------------------------------------------------------- + // get next line + //---------------------------------------------------------------- + if (iter == tokens.end()) + return E_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 + iter++; // } + 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 E_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 E_ST_NO_NAME; + } + + //--------------------------------------------------------------- + // we require the root joint be "hip" - DEV-26188 + //--------------------------------------------------------------- + if (mJoints.size() == 0 ) + { + //The root joint of the BVH file must be hip (mPelvis) or an alias of mPelvis. + const char* FORCED_ROOT_NAME = "hip"; + + TranslationMap::iterator hip_joint = mTranslations.find( FORCED_ROOT_NAME ); + TranslationMap::iterator root_joint = mTranslations.find( jointName ); + if ( hip_joint == mTranslations.end() || root_joint == mTranslations.end() || root_joint->second.mOutName != hip_joint->second.mOutName ) + { + strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */ + return E_ST_BAD_ROOT; + } + } + + + //---------------------------------------------------------------- + // add a set of keyframes for this joint + //---------------------------------------------------------------- + mJoints.push_back( new Joint( jointName ) ); + Joint *joint = mJoints.back(); + LL_DEBUGS("BVH") << "Created joint " << jointName << LL_ENDL; + LL_DEBUGS("BVH") << "- index " << mJoints.size()-1 << LL_ENDL; + + S32 depth = 1; + for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--) + { + Joint *pjoint = mJoints[parent_joints[j]]; + LL_DEBUGS("BVH") << "- ancestor " << pjoint->mName << LL_ENDL; + if (depth > pjoint->mChildTreeMaxDepth) + { + pjoint->mChildTreeMaxDepth = depth; + } + depth++; + } + + //---------------------------------------------------------------- + // get next line + //---------------------------------------------------------------- + if (iter == tokens.end()) + { + return E_ST_EOF; + } + line = (*(iter++)); + err_line++; + + //---------------------------------------------------------------- + // it must be { + //---------------------------------------------------------------- + if ( !strstr(line.c_str(), "{") ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return E_ST_NO_OFFSET; + } + else + { + parent_joints.push_back((S32)mJoints.size() - 1); + } + + //---------------------------------------------------------------- + // get next line + //---------------------------------------------------------------- + if (iter == tokens.end()) + { + return E_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 E_ST_NO_OFFSET; + } + + //---------------------------------------------------------------- + // get next line + //---------------------------------------------------------------- + if (iter == tokens.end()) + { + return E_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 E_ST_NO_CHANNELS; + } + + // Animating position (via mNumChannels = 6) is only supported for mPelvis. + int res = sscanf(line.c_str(), " CHANNELS %d", &joint->mNumChannels); + if ( res != 1 ) + { + // Assume default if not otherwise specified. + if (mJoints.size()==1) + { + joint->mNumChannels = 6; + } + else + { + joint->mNumChannels = 3; + } + } + + //---------------------------------------------------------------- + // 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 E_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 E_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 E_ST_NO_MOTION; + } + + //-------------------------------------------------------------------- + // get number of frames + //-------------------------------------------------------------------- + if (iter == tokens.end()) + { + return E_ST_EOF; + } + line = (*(iter++)); + err_line++; + + if ( !strstr(line.c_str(), "Frames:") ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return E_ST_NO_FRAMES; + } + + if ( sscanf(line.c_str(), "Frames: %d", &mNumFrames) != 1 ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return E_ST_NO_FRAMES; + } + + //-------------------------------------------------------------------- + // get frame time + //-------------------------------------------------------------------- + if (iter == tokens.end()) + { + return E_ST_EOF; + } + line = (*(iter++)); + err_line++; + + if ( !strstr(line.c_str(), "Frame Time:") ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return E_ST_NO_FRAME_TIME; + } + + if ( sscanf(line.c_str(), "Frame Time: %f", &mFrameTime) != 1 ) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return E_ST_NO_FRAME_TIME; + } + + // If the user only supplies one animation frame (after the ignored reference frame 0), hold for mFrameTime. + // If the user supples exactly one total frame, it isn't clear if that is a pose or reference frame, and the + // behavior is not defined. In this case, retain historical undefined behavior. + mDuration = llmax((F32)(mNumFrames - NUMBER_OF_UNPLAYED_FRAMES), 1.0f) * mFrameTime; + if (!mLoop) + { + mLoopOutPoint = mDuration; + } + + //-------------------------------------------------------------------- + // load frames + //-------------------------------------------------------------------- + for (S32 i=0; i floats; + boost::char_separator whitespace_sep("\t "); + tokenizer float_tokens(line, whitespace_sep); + tokenizer::iterator float_token_iter = float_tokens.begin(); + while (float_token_iter != float_tokens.end()) + { + try + { + F32 val = boost::lexical_cast(*float_token_iter); + floats.push_back(val); + } + catch (const boost::bad_lexical_cast&) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return E_ST_NO_POS; + } + float_token_iter++; + } + LL_DEBUGS("BVH") << "Got " << floats.size() << " floats " << LL_ENDL; + for (U32 j=0; jmKeys.push_back( Key() ); + Key &key = joint->mKeys.back(); + + if (floats.size() < joint->mNumChannels) + { + strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/ + return E_ST_NO_POS; + } + + // assume either numChannels == 6, in which case we have pos + rot, + // or numChannels == 3, in which case we have only rot. + if (joint->mNumChannels == 6) + { + key.mPos[0] = floats.front(); floats.pop_front(); + key.mPos[1] = floats.front(); floats.pop_front(); + key.mPos[2] = floats.front(); floats.pop_front(); + } + key.mRot[ joint->mOrder[0]-'X' ] = floats.front(); floats.pop_front(); + key.mRot[ joint->mOrder[1]-'X' ] = floats.front(); floats.pop_front(); + key.mRot[ joint->mOrder[2]-'X' ] = floats.front(); floats.pop_front(); + } + } + + return E_ST_OK; +} + + +//------------------------------------------------------------------------ +// LLBVHLoader::applyTranslation() +//------------------------------------------------------------------------ +void LLBVHLoader::applyTranslations() +{ + for (Joint* joint : mJoints) + { + //---------------------------------------------------------------- + // 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 ) + { + //LL_INFOS() << "NOTE: Ignoring " << joint->mName.c_str() << LL_ENDL; + joint->mIgnore = true; + continue; + } + + //---------------------------------------------------------------- + // Set the output name + //---------------------------------------------------------------- + if ( ! trans.mOutName.empty() ) + { + //LL_INFOS() << "NOTE: Changing " << joint->mName.c_str() << " to " << trans.mOutName.c_str() << LL_ENDL; + joint->mOutName = trans.mOutName; + } + + //Allow joint position changes as of SL-318 + joint->mIgnorePositions = false; + if (joint->mNumChannels == 3) + { + joint->mIgnorePositions = true; + } + + //---------------------------------------------------------------- + // Set the relativepos flags if necessary + //---------------------------------------------------------------- + if ( trans.mRelativePositionKey ) + { +// LL_INFOS() << "NOTE: Removing 1st position offset from all keys for " << joint->mOutName.c_str() << LL_ENDL; + joint->mRelativePositionKey = true; + } + + if ( trans.mRelativeRotationKey ) + { +// LL_INFOS() << "NOTE: Removing 1st rotation from all keys for " << joint->mOutName.c_str() << LL_ENDL; + joint->mRelativeRotationKey = true; + } + + if ( trans.mRelativePosition.magVec() > 0.0f ) + { + joint->mRelativePosition = trans.mRelativePosition; +// LL_INFOS() << "NOTE: Removing " << +// joint->mRelativePosition.mV[0] << " " << +// joint->mRelativePosition.mV[1] << " " << +// joint->mRelativePosition.mV[2] << +// " from all position keys in " << +// joint->mOutName.c_str() << LL_ENDL; + } + + //---------------------------------------------------------------- + // Set change of coordinate frame + //---------------------------------------------------------------- + joint->mFrameMatrix = trans.mFrameMatrix; + joint->mOffsetMatrix = trans.mOffsetMatrix; + + //---------------------------------------------------------------- + // Set mergeparent name + //---------------------------------------------------------------- + if ( ! trans.mMergeParentName.empty() ) + { +// LL_INFOS() << "NOTE: Merging " << joint->mOutName.c_str() << +// " with parent " << +// trans.mMergeParentName.c_str() << LL_ENDL; + joint->mMergeParentName = trans.mMergeParentName; + } + + //---------------------------------------------------------------- + // Set mergechild name + //---------------------------------------------------------------- + if ( ! trans.mMergeChildName.empty() ) + { +// LL_INFOS() << "NOTE: Merging " << joint->mName.c_str() << +// " with child " << trans.mMergeChildName.c_str() << LL_ENDL; + 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; + } + + for (Joint* joint : mJoints) + { + 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_squared(LLVector3(ki_prev->mPos), first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED) + { + 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_squared(current_pos, first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED) + { + pos_changed = true; + } + + if (dist_vec_squared(interp_pos, test_pos) < POSITION_KEYFRAME_THRESHOLD_SQUARED) + { + 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) + { + 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)) + { + //LL_INFOS() << "Ignoring joint " << joint->mName << LL_ENDL; + 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 = ""; + mLineNumber = 0; + mTranslations.clear(); + mConstraints.clear(); +} + +//------------------------------------------------------------------------ +// 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) +{ + F32 time; + + // count number of non-ignored joints + S32 numJoints = 0; + for (Joint* joint : mJoints) + { + 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 (Joint* joint : mJoints) + { + // 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; + + for (Joint* mjoint : mJoints) + { + if ( !joint->mMergeParentName.empty() && (mjoint->mName == joint->mMergeParentName) ) + { + mergeParent = mjoint; + } + if ( !joint->mMergeChildName.empty() && (mjoint->mName == joint->mMergeChildName) ) + { + mergeChild = mjoint; + } + } + + dp.packS32(joint->mNumRotKeys, "num_rot_keys"); + + LLQuaternion::Order order = bvhStringToOrder( joint->mOrder ); + S32 frame = 0; + for (Key& key : joint->mKeys) + { + + if ((frame == 0) && joint->mRelativeRotationKey) + { + first_frame_rot = mayaQ( key.mRot[0], key.mRot[1], key.mRot[2], order); + + fixup_rot.shortestArc(LLVector3::z_axis * first_frame_rot * frameRot, LLVector3::z_axis); + } + + if (key.mIgnoreRot) + { + frame++; + continue; + } + + time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts. + + 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( key.mRot[0], key.mRot[1], key.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"); + frame++; + } + + // output position keys if joint has motion. + if ( !joint->mIgnorePositions ) + { + dp.packS32(joint->mNumPosKeys, "num_pos_keys"); + + LLVector3 relPos = joint->mRelativePosition; + LLVector3 relKey; + + frame = 0; + for (Key& key : joint->mKeys) + { + if ((frame == 0) && joint->mRelativePositionKey) + { + relKey.setVec(key.mPos); + } + + if (key.mIgnorePos) + { + frame++; + continue; + } + + time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts. + + LLVector3 inPos = (LLVector3(key.mPos) - relKey) * ~first_frame_rot;// * fixup_rot; + LLVector3 outPos = inPos * frameRot * offsetRot; + + outPos *= INCHES_TO_METERS; + + //SL-318 Pelvis position can only move 5m. Limiting all joint position offsets to this dist. + 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 (Constraint& constraint : mConstraints) + { + U8 byte = constraint.mChainLength; + dp.packU8(byte, "chain_length"); + + byte = constraint.mConstraintType; + dp.packU8(byte, "constraint_type"); + dp.packBinaryDataFixed((U8*)constraint.mSourceJointName, 16, "source_volume"); + dp.packVector3(constraint.mSourceOffset, "source_offset"); + dp.packBinaryDataFixed((U8*)constraint.mTargetJointName, 16, "target_volume"); + dp.packVector3(constraint.mTargetOffset, "target_offset"); + dp.packVector3(constraint.mTargetDir, "target_dir"); + dp.packF32(constraint.mEaseInStart, "ease_in_start"); + dp.packF32(constraint.mEaseInStop, "ease_in_stop"); + dp.packF32(constraint.mEaseOutStart, "ease_out_start"); + dp.packF32(constraint.mEaseOutStop, "ease_out_stop"); + } + + + return true; +} -- cgit v1.2.3