summaryrefslogtreecommitdiff
path: root/indra/llcharacter
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcharacter')
-rw-r--r--indra/llcharacter/llanimationstates.cpp319
-rw-r--r--indra/llcharacter/llanimationstates.h227
-rw-r--r--indra/llcharacter/llbvhloader.cpp1489
-rw-r--r--indra/llcharacter/llbvhloader.h282
-rw-r--r--indra/llcharacter/llcharacter.cpp472
-rw-r--r--indra/llcharacter/llcharacter.h252
-rw-r--r--indra/llcharacter/lleditingmotion.cpp234
-rw-r--r--indra/llcharacter/lleditingmotion.h115
-rw-r--r--indra/llcharacter/llgesture.cpp356
-rw-r--r--indra/llcharacter/llgesture.h96
-rw-r--r--indra/llcharacter/llhandmotion.cpp202
-rw-r--r--indra/llcharacter/llhandmotion.h119
-rw-r--r--indra/llcharacter/llheadrotmotion.cpp505
-rw-r--r--indra/llcharacter/llheadrotmotion.h194
-rw-r--r--indra/llcharacter/lljoint.cpp504
-rw-r--r--indra/llcharacter/lljoint.h163
-rw-r--r--indra/llcharacter/lljointsolverrp3.cpp366
-rw-r--r--indra/llcharacter/lljointsolverrp3.h158
-rw-r--r--indra/llcharacter/lljointstate.h106
-rw-r--r--indra/llcharacter/llkeyframefallmotion.cpp123
-rw-r--r--indra/llcharacter/llkeyframefallmotion.h60
-rw-r--r--indra/llcharacter/llkeyframemotion.cpp2112
-rw-r--r--indra/llcharacter/llkeyframemotion.h437
-rw-r--r--indra/llcharacter/llkeyframemotionparam.cpp442
-rw-r--r--indra/llcharacter/llkeyframemotionparam.h138
-rw-r--r--indra/llcharacter/llkeyframestandmotion.cpp320
-rw-r--r--indra/llcharacter/llkeyframestandmotion.h98
-rw-r--r--indra/llcharacter/llkeyframewalkmotion.cpp379
-rw-r--r--indra/llcharacter/llkeyframewalkmotion.h158
-rw-r--r--indra/llcharacter/llmotion.cpp131
-rw-r--r--indra/llcharacter/llmotion.h237
-rw-r--r--indra/llcharacter/llmotioncontroller.cpp927
-rw-r--r--indra/llcharacter/llmotioncontroller.h207
-rw-r--r--indra/llcharacter/llmultigesture.cpp478
-rw-r--r--indra/llcharacter/llmultigesture.h213
-rw-r--r--indra/llcharacter/llpose.cpp544
-rw-r--r--indra/llcharacter/llpose.h120
-rw-r--r--indra/llcharacter/llstatemachine.cpp378
-rw-r--r--indra/llcharacter/llstatemachine.h130
-rw-r--r--indra/llcharacter/lltargetingmotion.cpp151
-rw-r--r--indra/llcharacter/lltargetingmotion.h98
-rw-r--r--indra/llcharacter/llvisualparam.cpp266
-rw-r--r--indra/llcharacter/llvisualparam.h131
43 files changed, 14437 insertions, 0 deletions
diff --git a/indra/llcharacter/llanimationstates.cpp b/indra/llcharacter/llanimationstates.cpp
new file mode 100644
index 0000000000..58ba252e04
--- /dev/null
+++ b/indra/llcharacter/llanimationstates.cpp
@@ -0,0 +1,319 @@
+/**
+ * @file llanimationstates.cpp
+ * @brief Implementation of animation state related functions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Agent Animation State
+//-----------------------------------------------------------------------------
+
+#include "linden_common.h"
+
+#include <string.h>
+
+#include "llanimationstates.h"
+#include "llstring.h"
+
+LLUUID AGENT_WALK_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_CROUCHWALK, ANIM_AGENT_TURNLEFT, ANIM_AGENT_TURNRIGHT};
+S32 NUM_AGENT_WALK_ANIMS = sizeof(AGENT_WALK_ANIMS) / sizeof(LLUUID);
+
+LLUUID AGENT_GUN_HOLD_ANIMS[] = {ANIM_AGENT_HOLD_RIFLE_R, ANIM_AGENT_HOLD_HANDGUN_R, ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_AGENT_HOLD_BOW_L};
+S32 NUM_AGENT_GUN_HOLD_ANIMS = sizeof(AGENT_GUN_HOLD_ANIMS) / sizeof(LLUUID);
+
+LLUUID AGENT_GUN_AIM_ANIMS[] = {ANIM_AGENT_AIM_RIFLE_R, ANIM_AGENT_AIM_HANDGUN_R, ANIM_AGENT_AIM_BAZOOKA_R, ANIM_AGENT_AIM_BOW_L};
+S32 NUM_AGENT_GUN_AIM_ANIMS = sizeof(AGENT_GUN_AIM_ANIMS) / sizeof(LLUUID);
+
+LLUUID AGENT_NO_ROTATE_ANIMS[] = {ANIM_AGENT_SIT_GROUND, ANIM_AGENT_SIT_GROUND_CONSTRAINED, ANIM_AGENT_STANDUP};
+S32 NUM_AGENT_NO_ROTATE_ANIMS = sizeof(AGENT_NO_ROTATE_ANIMS) / sizeof(LLUUID);
+
+LLUUID AGENT_STAND_ANIMS[] = {ANIM_AGENT_STAND, ANIM_AGENT_STAND_1, ANIM_AGENT_STAND_2, ANIM_AGENT_STAND_3, ANIM_AGENT_STAND_4};
+S32 NUM_AGENT_STAND_ANIMS = sizeof(AGENT_STAND_ANIMS) / sizeof(LLUUID);
+
+
+LLAnimationLibrary gAnimLibrary;
+
+//-----------------------------------------------------------------------------
+// LLAnimationLibrary()
+//-----------------------------------------------------------------------------
+LLAnimationLibrary::LLAnimationLibrary() :
+ mAnimStringTable(16384)
+{
+ //add animation names to animmap
+ mAnimMap[ANIM_AGENT_AFRAID]= mAnimStringTable.addString("express_afraid");
+ mAnimMap[ANIM_AGENT_AIM_BAZOOKA_R]= mAnimStringTable.addString("aim_r_bazooka");
+ mAnimMap[ANIM_AGENT_AIM_BOW_L]= mAnimStringTable.addString("aim_l_bow");
+ mAnimMap[ANIM_AGENT_AIM_HANDGUN_R]= mAnimStringTable.addString("aim_r_handgun");
+ mAnimMap[ANIM_AGENT_AIM_RIFLE_R]= mAnimStringTable.addString("aim_r_rifle");
+ mAnimMap[ANIM_AGENT_ANGRY]= mAnimStringTable.addString("express_anger");
+ mAnimMap[ANIM_AGENT_AWAY]= mAnimStringTable.addString("away");
+ mAnimMap[ANIM_AGENT_BACKFLIP]= mAnimStringTable.addString("backflip");
+ mAnimMap[ANIM_AGENT_BELLY_LAUGH]= mAnimStringTable.addString("express_laugh");
+ mAnimMap[ANIM_AGENT_BLOW_KISS]= mAnimStringTable.addString("blowkiss");
+ mAnimMap[ANIM_AGENT_BORED]= mAnimStringTable.addString("express_bored");
+ mAnimMap[ANIM_AGENT_BOW]= mAnimStringTable.addString("bow");
+ mAnimMap[ANIM_AGENT_BRUSH]= mAnimStringTable.addString("brush");
+ mAnimMap[ANIM_AGENT_BUSY]= mAnimStringTable.addString("busy");
+ mAnimMap[ANIM_AGENT_CLAP]= mAnimStringTable.addString("clap");
+ mAnimMap[ANIM_AGENT_COURTBOW]= mAnimStringTable.addString("courtbow");
+ mAnimMap[ANIM_AGENT_CROUCH]= mAnimStringTable.addString("crouch");
+ mAnimMap[ANIM_AGENT_CROUCHWALK]= mAnimStringTable.addString("crouchwalk");
+ mAnimMap[ANIM_AGENT_CRY]= mAnimStringTable.addString("express_cry");
+ mAnimMap[ANIM_AGENT_CUSTOMIZE]= mAnimStringTable.addString("turn_180");
+ mAnimMap[ANIM_AGENT_CUSTOMIZE_DONE]= mAnimStringTable.addString("turnback_180");
+ mAnimMap[ANIM_AGENT_DANCE1]= mAnimStringTable.addString("dance1");
+ mAnimMap[ANIM_AGENT_DANCE2]= mAnimStringTable.addString("dance2");
+ mAnimMap[ANIM_AGENT_DANCE3]= mAnimStringTable.addString("dance3");
+ mAnimMap[ANIM_AGENT_DANCE4]= mAnimStringTable.addString("dance4");
+ mAnimMap[ANIM_AGENT_DANCE5]= mAnimStringTable.addString("dance5");
+ mAnimMap[ANIM_AGENT_DANCE6]= mAnimStringTable.addString("dance6");
+ mAnimMap[ANIM_AGENT_DANCE7]= mAnimStringTable.addString("dance7");
+ mAnimMap[ANIM_AGENT_DANCE8]= mAnimStringTable.addString("dance8");
+ mAnimMap[ANIM_AGENT_DEAD]= mAnimStringTable.addString("dead");
+ mAnimMap[ANIM_AGENT_DRINK]= mAnimStringTable.addString("drink");
+ mAnimMap[ANIM_AGENT_EMBARRASSED]= mAnimStringTable.addString("express_embarrased");
+ mAnimMap[ANIM_AGENT_EXPRESS_AFRAID]= mAnimStringTable.addString("express_afraid_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_ANGER]= mAnimStringTable.addString("express_anger_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_BORED]= mAnimStringTable.addString("express_bored_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_CRY]= mAnimStringTable.addString("express_cry_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_DISDAIN]= mAnimStringTable.addString("express_disdain");
+ mAnimMap[ANIM_AGENT_EXPRESS_EMBARRASSED]= mAnimStringTable.addString("express_embarrassed_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_FROWN]= mAnimStringTable.addString("express_frown");
+ mAnimMap[ANIM_AGENT_EXPRESS_KISS]= mAnimStringTable.addString("express_kiss");
+ mAnimMap[ANIM_AGENT_EXPRESS_LAUGH]= mAnimStringTable.addString("express_laugh_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_OPEN_MOUTH]= mAnimStringTable.addString("express_open_mouth");
+ mAnimMap[ANIM_AGENT_EXPRESS_REPULSED]= mAnimStringTable.addString("express_repulsed_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_SAD]= mAnimStringTable.addString("express_sad_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_SHRUG]= mAnimStringTable.addString("express_shrug_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_SMILE]= mAnimStringTable.addString("express_smile");
+ mAnimMap[ANIM_AGENT_EXPRESS_SURPRISE]= mAnimStringTable.addString("express_surprise_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_TONGUE_OUT]= mAnimStringTable.addString("express_tongue_out");
+ mAnimMap[ANIM_AGENT_EXPRESS_TOOTHSMILE]= mAnimStringTable.addString("express_toothsmile");
+ mAnimMap[ANIM_AGENT_EXPRESS_WINK]= mAnimStringTable.addString("express_wink_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_WORRY]= mAnimStringTable.addString("express_worry_emote");
+ mAnimMap[ANIM_AGENT_FALLDOWN]= mAnimStringTable.addString("falldown");
+ mAnimMap[ANIM_AGENT_FEMALE_WALK]= mAnimStringTable.addString("female_walk");
+ mAnimMap[ANIM_AGENT_FINGER_WAG]= mAnimStringTable.addString("angry_fingerwag");
+ mAnimMap[ANIM_AGENT_FIST_PUMP]= mAnimStringTable.addString("fist_pump");
+ mAnimMap[ANIM_AGENT_FLY]= mAnimStringTable.addString("fly");
+ mAnimMap[ANIM_AGENT_FLYSLOW]= mAnimStringTable.addString("flyslow");
+ mAnimMap[ANIM_AGENT_HELLO]= mAnimStringTable.addString("hello");
+ mAnimMap[ANIM_AGENT_HOLD_BAZOOKA_R]= mAnimStringTable.addString("hold_r_bazooka");
+ mAnimMap[ANIM_AGENT_HOLD_BOW_L]= mAnimStringTable.addString("hold_l_bow");
+ mAnimMap[ANIM_AGENT_HOLD_HANDGUN_R]= mAnimStringTable.addString("hold_r_handgun");
+ mAnimMap[ANIM_AGENT_HOLD_RIFLE_R]= mAnimStringTable.addString("hold_r_rifle");
+ mAnimMap[ANIM_AGENT_HOLD_THROW_R]= mAnimStringTable.addString("hold_throw_r");
+ mAnimMap[ANIM_AGENT_HOVER]= mAnimStringTable.addString("hover");
+ mAnimMap[ANIM_AGENT_HOVER_DOWN]= mAnimStringTable.addString("hover_down");
+ mAnimMap[ANIM_AGENT_HOVER_UP]= mAnimStringTable.addString("hover_up");
+ mAnimMap[ANIM_AGENT_IMPATIENT]= mAnimStringTable.addString("impatient");
+ mAnimMap[ANIM_AGENT_JUMP]= mAnimStringTable.addString("jump");
+ mAnimMap[ANIM_AGENT_JUMP_FOR_JOY]= mAnimStringTable.addString("jumpforjoy");
+ mAnimMap[ANIM_AGENT_KISS_MY_BUTT]= mAnimStringTable.addString("kissmybutt");
+ mAnimMap[ANIM_AGENT_LAND]= mAnimStringTable.addString("land");
+ mAnimMap[ANIM_AGENT_LAUGH_SHORT]= mAnimStringTable.addString("laugh_short");
+ mAnimMap[ANIM_AGENT_MEDIUM_LAND]= mAnimStringTable.addString("soft_land");
+ mAnimMap[ANIM_AGENT_MOTORCYCLE_SIT]= mAnimStringTable.addString("motorcycle_sit");
+ mAnimMap[ANIM_AGENT_MUSCLE_BEACH]= mAnimStringTable.addString("musclebeach");
+ mAnimMap[ANIM_AGENT_NO]= mAnimStringTable.addString("no_head");
+ mAnimMap[ANIM_AGENT_NO_UNHAPPY]= mAnimStringTable.addString("no_unhappy");
+ mAnimMap[ANIM_AGENT_NYAH_NYAH]= mAnimStringTable.addString("nyanya");
+ mAnimMap[ANIM_AGENT_ONETWO_PUNCH]= mAnimStringTable.addString("punch_onetwo");
+ mAnimMap[ANIM_AGENT_PEACE]= mAnimStringTable.addString("peace");
+ mAnimMap[ANIM_AGENT_POINT_ME]= mAnimStringTable.addString("point_me");
+ mAnimMap[ANIM_AGENT_POINT_YOU]= mAnimStringTable.addString("point_you");
+ mAnimMap[ANIM_AGENT_PRE_JUMP]= mAnimStringTable.addString("prejump");
+ mAnimMap[ANIM_AGENT_PUNCH_LEFT]= mAnimStringTable.addString("punch_l");
+ mAnimMap[ANIM_AGENT_PUNCH_RIGHT]= mAnimStringTable.addString("punch_r");
+ mAnimMap[ANIM_AGENT_REPULSED]= mAnimStringTable.addString("express_repulsed");
+ mAnimMap[ANIM_AGENT_ROUNDHOUSE_KICK]= mAnimStringTable.addString("kick_roundhouse_r");
+ mAnimMap[ANIM_AGENT_RPS_COUNTDOWN]= mAnimStringTable.addString("rps_countdown");
+ mAnimMap[ANIM_AGENT_RPS_PAPER]= mAnimStringTable.addString("rps_paper");
+ mAnimMap[ANIM_AGENT_RPS_ROCK]= mAnimStringTable.addString("rps_rock");
+ mAnimMap[ANIM_AGENT_RPS_SCISSORS]= mAnimStringTable.addString("rps_scissors");
+ mAnimMap[ANIM_AGENT_RUN]= mAnimStringTable.addString("run");
+ mAnimMap[ANIM_AGENT_SAD]= mAnimStringTable.addString("express_sad");
+ mAnimMap[ANIM_AGENT_SALUTE]= mAnimStringTable.addString("salute");
+ mAnimMap[ANIM_AGENT_SHOOT_BOW_L]= mAnimStringTable.addString("shoot_l_bow");
+ mAnimMap[ANIM_AGENT_SHOUT]= mAnimStringTable.addString("shout");
+ mAnimMap[ANIM_AGENT_SHRUG]= mAnimStringTable.addString("express_shrug");
+ mAnimMap[ANIM_AGENT_SIT]= mAnimStringTable.addString("sit");
+ mAnimMap[ANIM_AGENT_SIT_FEMALE]= mAnimStringTable.addString("sit_female");
+ mAnimMap[ANIM_AGENT_SIT_GROUND]= mAnimStringTable.addString("sit_ground");
+ mAnimMap[ANIM_AGENT_SIT_GROUND_CONSTRAINED]= mAnimStringTable.addString("sit_ground_constrained");
+ mAnimMap[ANIM_AGENT_SIT_GENERIC]= mAnimStringTable.addString("sit_generic");
+ mAnimMap[ANIM_AGENT_SIT_TO_STAND]= mAnimStringTable.addString("sit_to_stand");
+ mAnimMap[ANIM_AGENT_SLEEP]= mAnimStringTable.addString("sleep");
+ mAnimMap[ANIM_AGENT_SMOKE_IDLE]= mAnimStringTable.addString("smoke_idle");
+ mAnimMap[ANIM_AGENT_SMOKE_INHALE]= mAnimStringTable.addString("smoke_inhale");
+ mAnimMap[ANIM_AGENT_SMOKE_THROW_DOWN]= mAnimStringTable.addString("smoke_throw_down");
+ mAnimMap[ANIM_AGENT_SNAPSHOT]= mAnimStringTable.addString("snapshot");
+ mAnimMap[ANIM_AGENT_STAND]= mAnimStringTable.addString("stand");
+ mAnimMap[ANIM_AGENT_STANDUP]= mAnimStringTable.addString("standup");
+ mAnimMap[ANIM_AGENT_STAND_1]= mAnimStringTable.addString("stand_1");
+ mAnimMap[ANIM_AGENT_STAND_2]= mAnimStringTable.addString("stand_2");
+ mAnimMap[ANIM_AGENT_STAND_3]= mAnimStringTable.addString("stand_3");
+ mAnimMap[ANIM_AGENT_STAND_4]= mAnimStringTable.addString("stand_4");
+ mAnimMap[ANIM_AGENT_STRETCH]= mAnimStringTable.addString("stretch");
+ mAnimMap[ANIM_AGENT_STRIDE]= mAnimStringTable.addString("stride");
+ mAnimMap[ANIM_AGENT_SURF]= mAnimStringTable.addString("surf");
+ mAnimMap[ANIM_AGENT_SURPRISE]= mAnimStringTable.addString("express_surprise");
+ mAnimMap[ANIM_AGENT_SWORD_STRIKE]= mAnimStringTable.addString("sword_strike_r");
+ mAnimMap[ANIM_AGENT_TALK]= mAnimStringTable.addString("talk");
+ mAnimMap[ANIM_AGENT_TANTRUM]= mAnimStringTable.addString("angry_tantrum");
+ mAnimMap[ANIM_AGENT_THROW_R]= mAnimStringTable.addString("throw_r");
+ mAnimMap[ANIM_AGENT_TRYON_SHIRT]= mAnimStringTable.addString("tryon_shirt");
+ mAnimMap[ANIM_AGENT_TURNLEFT]= mAnimStringTable.addString("turnleft");
+ mAnimMap[ANIM_AGENT_TURNRIGHT]= mAnimStringTable.addString("turnright");
+ mAnimMap[ANIM_AGENT_TYPE]= mAnimStringTable.addString("type");
+ mAnimMap[ANIM_AGENT_WALK]= mAnimStringTable.addString("walk");
+ mAnimMap[ANIM_AGENT_WHISPER]= mAnimStringTable.addString("whisper");
+ mAnimMap[ANIM_AGENT_WHISTLE]= mAnimStringTable.addString("whistle");
+ mAnimMap[ANIM_AGENT_WINK]= mAnimStringTable.addString("express_wink");
+ mAnimMap[ANIM_AGENT_WINK_HOLLYWOOD]= mAnimStringTable.addString("wink_hollywood");
+ mAnimMap[ANIM_AGENT_WORRY]= mAnimStringTable.addString("express_worry");
+ mAnimMap[ANIM_AGENT_YES]= mAnimStringTable.addString("yes_head");
+ mAnimMap[ANIM_AGENT_YES_HAPPY]= mAnimStringTable.addString("yes_happy");
+ mAnimMap[ANIM_AGENT_YOGA_FLOAT]= mAnimStringTable.addString("yoga_float");
+}
+
+//-----------------------------------------------------------------------------
+// ~LLAnimationLibrary()
+//-----------------------------------------------------------------------------
+LLAnimationLibrary::~LLAnimationLibrary()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Return the text name of an animation state
+//-----------------------------------------------------------------------------
+const char *LLAnimationLibrary::animStateToString( const LLUUID& state )
+{
+ if (state.isNull())
+ {
+ return NULL;
+ }
+ if (mAnimMap.count(state))
+ {
+ return mAnimMap[state];
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Return the animation state for a given name
+//-----------------------------------------------------------------------------
+LLUUID LLAnimationLibrary::stringToAnimState( const char *name, BOOL allow_ids )
+{
+ LLString lower_case_name(name);
+ LLString::toLower(lower_case_name);
+
+ char *true_name = mAnimStringTable.checkString(lower_case_name.c_str());
+
+ LLUUID id;
+ id.setNull();
+
+ if (true_name)
+ {
+ for (anim_map_t::iterator iter = mAnimMap.begin();
+ iter != mAnimMap.end(); iter++)
+ {
+ if (iter->second == true_name)
+ {
+ id = iter->first;
+ break;
+ }
+ }
+ }
+ else if (allow_ids)
+ {
+ // try to convert string to LLUUID
+ id.set(name, FALSE);
+ }
+
+ return id;
+}
+
+// Animation states that the user can trigger as part of a gesture
+const LLAnimStateEntry gUserAnimStates[] = {
+ LLAnimStateEntry("Afraid", "express_afraid", ANIM_AGENT_AFRAID),
+ LLAnimStateEntry("Angry", "express_anger", ANIM_AGENT_ANGRY),
+ LLAnimStateEntry("Away", "away", ANIM_AGENT_AWAY),
+ LLAnimStateEntry("Backflip", "backflip", ANIM_AGENT_BACKFLIP),
+ LLAnimStateEntry("Belly Laugh", "express_laugh", ANIM_AGENT_BELLY_LAUGH),
+ LLAnimStateEntry("BigSmile", "express_toothsmile", ANIM_AGENT_EXPRESS_TOOTHSMILE),
+ LLAnimStateEntry("Blow Kiss", "blowkiss", ANIM_AGENT_BLOW_KISS),
+ LLAnimStateEntry("Bored", "express_bored", ANIM_AGENT_BORED),
+ LLAnimStateEntry("Bow", "bow", ANIM_AGENT_BOW),
+ LLAnimStateEntry("Clap", "clap", ANIM_AGENT_CLAP),
+ LLAnimStateEntry("Court Bow", "courtbow", ANIM_AGENT_COURTBOW),
+ LLAnimStateEntry("Cry", "express_cry", ANIM_AGENT_CRY),
+ LLAnimStateEntry("Dance 1", "dance1", ANIM_AGENT_DANCE1),
+ LLAnimStateEntry("Dance 2", "dance2", ANIM_AGENT_DANCE2),
+ LLAnimStateEntry("Dance 3", "dance3", ANIM_AGENT_DANCE3),
+ LLAnimStateEntry("Dance 4", "dance4", ANIM_AGENT_DANCE4),
+ LLAnimStateEntry("Dance 5", "dance5", ANIM_AGENT_DANCE5),
+ LLAnimStateEntry("Dance 6", "dance6", ANIM_AGENT_DANCE6),
+ LLAnimStateEntry("Dance 7", "dance7", ANIM_AGENT_DANCE7),
+ LLAnimStateEntry("Dance 8", "dance8", ANIM_AGENT_DANCE8),
+ LLAnimStateEntry("Disdain", "express_disdain", ANIM_AGENT_EXPRESS_DISDAIN),
+ LLAnimStateEntry("Drink", "drink", ANIM_AGENT_DRINK),
+ LLAnimStateEntry("Embarrassed", "express_embarrased", ANIM_AGENT_EMBARRASSED),
+ LLAnimStateEntry("Finger Wag", "angry_fingerwag", ANIM_AGENT_FINGER_WAG),
+ LLAnimStateEntry("Fist Pump", "fist_pump", ANIM_AGENT_FIST_PUMP),
+ LLAnimStateEntry("Floating Yoga", "yoga_float", ANIM_AGENT_YOGA_FLOAT),
+ LLAnimStateEntry("Frown", "express_frown", ANIM_AGENT_EXPRESS_FROWN),
+ LLAnimStateEntry("Impatient", "impatient", ANIM_AGENT_IMPATIENT),
+ LLAnimStateEntry("Jump For Joy", "jumpforjoy", ANIM_AGENT_JUMP_FOR_JOY),
+ LLAnimStateEntry("Kiss My Butt", "kissmybutt", ANIM_AGENT_KISS_MY_BUTT),
+ LLAnimStateEntry("Kiss", "express_kiss", ANIM_AGENT_EXPRESS_KISS),
+ LLAnimStateEntry("Laugh", "laugh_short", ANIM_AGENT_LAUGH_SHORT),
+ LLAnimStateEntry("Muscle Beach", "musclebeach", ANIM_AGENT_MUSCLE_BEACH),
+ LLAnimStateEntry("No (Unhappy)", "no_unhappy", ANIM_AGENT_NO_UNHAPPY),
+ LLAnimStateEntry("No", "no_head", ANIM_AGENT_NO),
+ LLAnimStateEntry("Nya-nya-nya", "nyanya", ANIM_AGENT_NYAH_NYAH),
+ LLAnimStateEntry("One-Two Punch", "punch_onetwo", ANIM_AGENT_ONETWO_PUNCH),
+ LLAnimStateEntry("Open Mouth", "express_open_mouth", ANIM_AGENT_EXPRESS_OPEN_MOUTH),
+ LLAnimStateEntry("Peace", "peace", ANIM_AGENT_PEACE),
+ LLAnimStateEntry("Point at Other", "point_you", ANIM_AGENT_POINT_YOU),
+ LLAnimStateEntry("Point at Self", "point_me", ANIM_AGENT_POINT_ME),
+ LLAnimStateEntry("Punch Left", "punch_l", ANIM_AGENT_PUNCH_LEFT),
+ LLAnimStateEntry("Punch Right", "punch_r", ANIM_AGENT_PUNCH_RIGHT),
+ LLAnimStateEntry("RPS count", "rps_countdown", ANIM_AGENT_RPS_COUNTDOWN),
+ LLAnimStateEntry("RPS paper", "rps_paper", ANIM_AGENT_RPS_PAPER),
+ LLAnimStateEntry("RPS rock", "rps_rock", ANIM_AGENT_RPS_ROCK),
+ LLAnimStateEntry("RPS scissors", "rps_scissors", ANIM_AGENT_RPS_SCISSORS),
+ LLAnimStateEntry("Repulsed", "express_repulsed", ANIM_AGENT_EXPRESS_REPULSED),
+ LLAnimStateEntry("Roundhouse Kick", "kick_roundhouse_r", ANIM_AGENT_ROUNDHOUSE_KICK),
+ LLAnimStateEntry("Sad", "express_sad", ANIM_AGENT_SAD),
+ LLAnimStateEntry("Salute", "salute", ANIM_AGENT_SALUTE),
+ LLAnimStateEntry("Shout", "shout", ANIM_AGENT_SHOUT),
+ LLAnimStateEntry("Shrug", "express_shrug", ANIM_AGENT_SHRUG),
+ LLAnimStateEntry("Smile", "express_smile", ANIM_AGENT_EXPRESS_SMILE),
+ LLAnimStateEntry("Smoke Idle", "smoke_idle", ANIM_AGENT_SMOKE_IDLE),
+ LLAnimStateEntry("Smoke Inhale", "smoke_inhale", ANIM_AGENT_SMOKE_INHALE),
+ LLAnimStateEntry("Smoke Throw Down","smoke_throw_down", ANIM_AGENT_SMOKE_THROW_DOWN),
+ LLAnimStateEntry("Surprise", "express_surprise", ANIM_AGENT_SURPRISE),
+ LLAnimStateEntry("Sword Strike", "sword_strike_r", ANIM_AGENT_SWORD_STRIKE),
+ LLAnimStateEntry("Tantrum", "angry_tantrum", ANIM_AGENT_TANTRUM),
+ LLAnimStateEntry("TongueOut", "express_tongue_out", ANIM_AGENT_EXPRESS_TONGUE_OUT),
+ LLAnimStateEntry("Wave", "hello", ANIM_AGENT_HELLO),
+ LLAnimStateEntry("Whisper", "whisper", ANIM_AGENT_WHISPER),
+ LLAnimStateEntry("Whistle", "whistle", ANIM_AGENT_WHISTLE),
+ LLAnimStateEntry("Wink", "express_wink", ANIM_AGENT_WINK),
+ LLAnimStateEntry("Wink (Hollywood)","wink_hollywood", ANIM_AGENT_WINK_HOLLYWOOD),
+ LLAnimStateEntry("Worry", "express_worry", ANIM_AGENT_EXPRESS_WORRY),
+ LLAnimStateEntry("Yes (Happy)", "yes_happy", ANIM_AGENT_YES_HAPPY),
+ LLAnimStateEntry("Yes", "yes_head", ANIM_AGENT_YES),
+};
+
+const S32 gUserAnimStatesCount = sizeof(gUserAnimStates) / sizeof(gUserAnimStates[0]);
+
+
+
+// End
+
diff --git a/indra/llcharacter/llanimationstates.h b/indra/llcharacter/llanimationstates.h
new file mode 100644
index 0000000000..31fe0dfc11
--- /dev/null
+++ b/indra/llcharacter/llanimationstates.h
@@ -0,0 +1,227 @@
+/**
+ * @file llanimationstates.h
+ * @brief Implementation of animation state support.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLANIMATIONSTATES_H
+#define LL_LLANIMATIONSTATES_H
+
+#include <map>
+
+#include "string_table.h"
+#include "lluuid.h"
+
+//-----------------------------------------------------------------------------
+// These bit flags are generally used to track the animation state
+// of characters. The simulator and viewer share these flags to interpret
+// the Animation name/value attribute on agents.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Agent Animation State
+//-----------------------------------------------------------------------------
+const S32 MAX_CONCURRENT_ANIMS = 16;
+
+
+const LLUUID ANIM_AGENT_AFRAID = LLUUID("6b61c8e8-4747-0d75-12d7-e49ff207a4ca");
+const LLUUID ANIM_AGENT_AIM_BAZOOKA_R = LLUUID("b5b4a67d-0aee-30d2-72cd-77b333e932ef");
+const LLUUID ANIM_AGENT_AIM_BOW_L = LLUUID("46bb4359-de38-4ed8-6a22-f1f52fe8f506");
+const LLUUID ANIM_AGENT_AIM_HANDGUN_R = LLUUID("3147d815-6338-b932-f011-16b56d9ac18b");
+const LLUUID ANIM_AGENT_AIM_RIFLE_R = LLUUID("ea633413-8006-180a-c3ba-96dd1d756720");
+const LLUUID ANIM_AGENT_ANGRY = LLUUID("5747a48e-073e-c331-f6f3-7c2149613d3e");
+const LLUUID ANIM_AGENT_AWAY = LLUUID("fd037134-85d4-f241-72c6-4f42164fedee");
+const LLUUID ANIM_AGENT_BACKFLIP = LLUUID("c4ca6188-9127-4f31-0158-23c4e2f93304");
+const LLUUID ANIM_AGENT_BELLY_LAUGH = LLUUID("18b3a4b5-b463-bd48-e4b6-71eaac76c515");
+const LLUUID ANIM_AGENT_BLOW_KISS = LLUUID("db84829b-462c-ee83-1e27-9bbee66bd624");
+const LLUUID ANIM_AGENT_BORED = LLUUID("b906c4ba-703b-1940-32a3-0c7f7d791510");
+const LLUUID ANIM_AGENT_BOW = LLUUID("82e99230-c906-1403-4d9c-3889dd98daba");
+const LLUUID ANIM_AGENT_BRUSH = LLUUID("349a3801-54f9-bf2c-3bd0-1ac89772af01");
+const LLUUID ANIM_AGENT_BUSY = LLUUID("efcf670c-2d18-8128-973a-034ebc806b67");
+const LLUUID ANIM_AGENT_CLAP = LLUUID("9b0c1c4e-8ac7-7969-1494-28c874c4f668");
+const LLUUID ANIM_AGENT_COURTBOW = LLUUID("9ba1c942-08be-e43a-fb29-16ad440efc50");
+const LLUUID ANIM_AGENT_CROUCH = LLUUID("201f3fdf-cb1f-dbec-201f-7333e328ae7c");
+const LLUUID ANIM_AGENT_CROUCHWALK = LLUUID("47f5f6fb-22e5-ae44-f871-73aaaf4a6022");
+const LLUUID ANIM_AGENT_CRY = LLUUID("92624d3e-1068-f1aa-a5ec-8244585193ed");
+const LLUUID ANIM_AGENT_CUSTOMIZE = LLUUID("038fcec9-5ebd-8a8e-0e2e-6e71a0a1ac53");
+const LLUUID ANIM_AGENT_CUSTOMIZE_DONE = LLUUID("6883a61a-b27b-5914-a61e-dda118a9ee2c");
+const LLUUID ANIM_AGENT_DANCE1 = LLUUID("b68a3d7c-de9e-fc87-eec8-543d787e5b0d");
+const LLUUID ANIM_AGENT_DANCE2 = LLUUID("928cae18-e31d-76fd-9cc9-2f55160ff818");
+const LLUUID ANIM_AGENT_DANCE3 = LLUUID("30047778-10ea-1af7-6881-4db7a3a5a114");
+const LLUUID ANIM_AGENT_DANCE4 = LLUUID("951469f4-c7b2-c818-9dee-ad7eea8c30b7");
+const LLUUID ANIM_AGENT_DANCE5 = LLUUID("4bd69a1d-1114-a0b4-625f-84e0a5237155");
+const LLUUID ANIM_AGENT_DANCE6 = LLUUID("cd28b69b-9c95-bb78-3f94-8d605ff1bb12");
+const LLUUID ANIM_AGENT_DANCE7 = LLUUID("a54d8ee2-28bb-80a9-7f0c-7afbbe24a5d6");
+const LLUUID ANIM_AGENT_DANCE8 = LLUUID("b0dc417c-1f11-af36-2e80-7e7489fa7cdc");
+const LLUUID ANIM_AGENT_DEAD = LLUUID("57abaae6-1d17-7b1b-5f98-6d11a6411276");
+const LLUUID ANIM_AGENT_DRINK = LLUUID("0f86e355-dd31-a61c-fdb0-3a96b9aad05f");
+const LLUUID ANIM_AGENT_EMBARRASSED = LLUUID("514af488-9051-044a-b3fc-d4dbf76377c6");
+const LLUUID ANIM_AGENT_EXPRESS_AFRAID = LLUUID("aa2df84d-cf8f-7218-527b-424a52de766e");
+const LLUUID ANIM_AGENT_EXPRESS_ANGER = LLUUID("1a03b575-9634-b62a-5767-3a679e81f4de");
+const LLUUID ANIM_AGENT_EXPRESS_BORED = LLUUID("214aa6c1-ba6a-4578-f27c-ce7688f61d0d");
+const LLUUID ANIM_AGENT_EXPRESS_CRY = LLUUID("d535471b-85bf-3b4d-a542-93bea4f59d33");
+const LLUUID ANIM_AGENT_EXPRESS_DISDAIN = LLUUID("d4416ff1-09d3-300f-4183-1b68a19b9fc1");
+const LLUUID ANIM_AGENT_EXPRESS_EMBARRASSED = LLUUID("0b8c8211-d78c-33e8-fa28-c51a9594e424");
+const LLUUID ANIM_AGENT_EXPRESS_FROWN = LLUUID("fee3df48-fa3d-1015-1e26-a205810e3001");
+const LLUUID ANIM_AGENT_EXPRESS_KISS = LLUUID("1e8d90cc-a84e-e135-884c-7c82c8b03a14");
+const LLUUID ANIM_AGENT_EXPRESS_LAUGH = LLUUID("62570842-0950-96f8-341c-809e65110823");
+const LLUUID ANIM_AGENT_EXPRESS_OPEN_MOUTH = LLUUID("d63bc1f9-fc81-9625-a0c6-007176d82eb7");
+const LLUUID ANIM_AGENT_EXPRESS_REPULSED = LLUUID("f76cda94-41d4-a229-2872-e0296e58afe1");
+const LLUUID ANIM_AGENT_EXPRESS_SAD = LLUUID("eb6ebfb2-a4b3-a19c-d388-4dd5c03823f7");
+const LLUUID ANIM_AGENT_EXPRESS_SHRUG = LLUUID("a351b1bc-cc94-aac2-7bea-a7e6ebad15ef");
+const LLUUID ANIM_AGENT_EXPRESS_SMILE = LLUUID("b7c7c833-e3d3-c4e3-9fc0-131237446312");
+const LLUUID ANIM_AGENT_EXPRESS_SURPRISE = LLUUID("728646d9-cc79-08b2-32d6-937f0a835c24");
+const LLUUID ANIM_AGENT_EXPRESS_TONGUE_OUT = LLUUID("835965c6-7f2f-bda2-5deb-2478737f91bf");
+const LLUUID ANIM_AGENT_EXPRESS_TOOTHSMILE = LLUUID("b92ec1a5-e7ce-a76b-2b05-bcdb9311417e");
+const LLUUID ANIM_AGENT_EXPRESS_WINK = LLUUID("da020525-4d94-59d6-23d7-81fdebf33148");
+const LLUUID ANIM_AGENT_EXPRESS_WORRY = LLUUID("9c05e5c7-6f07-6ca4-ed5a-b230390c3950");
+const LLUUID ANIM_AGENT_FALLDOWN = LLUUID("666307d9-a860-572d-6fd4-c3ab8865c094");
+const LLUUID ANIM_AGENT_FEMALE_WALK = LLUUID("f5fc7433-043d-e819-8298-f519a119b688");
+const LLUUID ANIM_AGENT_FINGER_WAG = LLUUID("c1bc7f36-3ba0-d844-f93c-93be945d644f");
+const LLUUID ANIM_AGENT_FIST_PUMP = LLUUID("7db00ccd-f380-f3ee-439d-61968ec69c8a");
+const LLUUID ANIM_AGENT_FLY = LLUUID("aec4610c-757f-bc4e-c092-c6e9caf18daf");
+const LLUUID ANIM_AGENT_FLYSLOW = LLUUID("2b5a38b2-5e00-3a97-a495-4c826bc443e6");
+const LLUUID ANIM_AGENT_HELLO = LLUUID("9b29cd61-c45b-5689-ded2-91756b8d76a9");
+const LLUUID ANIM_AGENT_HOLD_BAZOOKA_R = LLUUID("ef62d355-c815-4816-2474-b1acc21094a6");
+const LLUUID ANIM_AGENT_HOLD_BOW_L = LLUUID("8b102617-bcba-037b-86c1-b76219f90c88");
+const LLUUID ANIM_AGENT_HOLD_HANDGUN_R = LLUUID("efdc1727-8b8a-c800-4077-975fc27ee2f2");
+const LLUUID ANIM_AGENT_HOLD_RIFLE_R = LLUUID("3d94bad0-c55b-7dcc-8763-033c59405d33");
+const LLUUID ANIM_AGENT_HOLD_THROW_R = LLUUID("7570c7b5-1f22-56dd-56ef-a9168241bbb6");
+const LLUUID ANIM_AGENT_HOVER = LLUUID("4ae8016b-31b9-03bb-c401-b1ea941db41d");
+const LLUUID ANIM_AGENT_HOVER_DOWN = LLUUID("20f063ea-8306-2562-0b07-5c853b37b31e");
+const LLUUID ANIM_AGENT_HOVER_UP = LLUUID("62c5de58-cb33-5743-3d07-9e4cd4352864");
+const LLUUID ANIM_AGENT_IMPATIENT = LLUUID("5ea3991f-c293-392e-6860-91dfa01278a3");
+const LLUUID ANIM_AGENT_JUMP = LLUUID("2305bd75-1ca9-b03b-1faa-b176b8a8c49e");
+const LLUUID ANIM_AGENT_JUMP_FOR_JOY = LLUUID("709ea28e-1573-c023-8bf8-520c8bc637fa");
+const LLUUID ANIM_AGENT_KISS_MY_BUTT = LLUUID("19999406-3a3a-d58c-a2ac-d72e555dcf51");
+const LLUUID ANIM_AGENT_LAND = LLUUID("7a17b059-12b2-41b1-570a-186368b6aa6f");
+const LLUUID ANIM_AGENT_LAUGH_SHORT = LLUUID("ca5b3f14-3194-7a2b-c894-aa699b718d1f");
+const LLUUID ANIM_AGENT_MEDIUM_LAND = LLUUID("f4f00d6e-b9fe-9292-f4cb-0ae06ea58d57");
+const LLUUID ANIM_AGENT_MOTORCYCLE_SIT = LLUUID("08464f78-3a8e-2944-cba5-0c94aff3af29");
+const LLUUID ANIM_AGENT_MUSCLE_BEACH = LLUUID("315c3a41-a5f3-0ba4-27da-f893f769e69b");
+const LLUUID ANIM_AGENT_NO = LLUUID("5a977ed9-7f72-44e9-4c4c-6e913df8ae74");
+const LLUUID ANIM_AGENT_NO_UNHAPPY = LLUUID("d83fa0e5-97ed-7eb2-e798-7bd006215cb4");
+const LLUUID ANIM_AGENT_NYAH_NYAH = LLUUID("f061723d-0a18-754f-66ee-29a44795a32f");
+const LLUUID ANIM_AGENT_ONETWO_PUNCH = LLUUID("eefc79be-daae-a239-8c04-890f5d23654a");
+const LLUUID ANIM_AGENT_PEACE = LLUUID("b312b10e-65ab-a0a4-8b3c-1326ea8e3ed9");
+const LLUUID ANIM_AGENT_POINT_ME = LLUUID("17c024cc-eef2-f6a0-3527-9869876d7752");
+const LLUUID ANIM_AGENT_POINT_YOU = LLUUID("ec952cca-61ef-aa3b-2789-4d1344f016de");
+const LLUUID ANIM_AGENT_PRE_JUMP = LLUUID("7a4e87fe-de39-6fcb-6223-024b00893244");
+const LLUUID ANIM_AGENT_PUNCH_LEFT = LLUUID("f3300ad9-3462-1d07-2044-0fef80062da0");
+const LLUUID ANIM_AGENT_PUNCH_RIGHT = LLUUID("c8e42d32-7310-6906-c903-cab5d4a34656");
+const LLUUID ANIM_AGENT_REPULSED = LLUUID("36f81a92-f076-5893-dc4b-7c3795e487cf");
+const LLUUID ANIM_AGENT_ROUNDHOUSE_KICK = LLUUID("49aea43b-5ac3-8a44-b595-96100af0beda");
+const LLUUID ANIM_AGENT_RPS_COUNTDOWN = LLUUID("35db4f7e-28c2-6679-cea9-3ee108f7fc7f");
+const LLUUID ANIM_AGENT_RPS_PAPER = LLUUID("0836b67f-7f7b-f37b-c00a-460dc1521f5a");
+const LLUUID ANIM_AGENT_RPS_ROCK = LLUUID("42dd95d5-0bc6-6392-f650-777304946c0f");
+const LLUUID ANIM_AGENT_RPS_SCISSORS = LLUUID("16803a9f-5140-e042-4d7b-d28ba247c325");
+const LLUUID ANIM_AGENT_RUN = LLUUID("05ddbff8-aaa9-92a1-2b74-8fe77a29b445");
+const LLUUID ANIM_AGENT_SAD = LLUUID("0eb702e2-cc5a-9a88-56a5-661a55c0676a");
+const LLUUID ANIM_AGENT_SALUTE = LLUUID("cd7668a6-7011-d7e2-ead8-fc69eff1a104");
+const LLUUID ANIM_AGENT_SHOOT_BOW_L = LLUUID("e04d450d-fdb5-0432-fd68-818aaf5935f8");
+const LLUUID ANIM_AGENT_SHOUT = LLUUID("6bd01860-4ebd-127a-bb3d-d1427e8e0c42");
+const LLUUID ANIM_AGENT_SHRUG = LLUUID("70ea714f-3a97-d742-1b01-590a8fcd1db5");
+const LLUUID ANIM_AGENT_SIT = LLUUID("1a5fe8ac-a804-8a5d-7cbd-56bd83184568");
+const LLUUID ANIM_AGENT_SIT_FEMALE = LLUUID("b1709c8d-ecd3-54a1-4f28-d55ac0840782");
+const LLUUID ANIM_AGENT_SIT_GENERIC = LLUUID("245f3c54-f1c0-bf2e-811f-46d8eeb386e7");
+const LLUUID ANIM_AGENT_SIT_GROUND = LLUUID("1c7600d6-661f-b87b-efe2-d7421eb93c86");
+const LLUUID ANIM_AGENT_SIT_GROUND_CONSTRAINED = LLUUID("1a2bd58e-87ff-0df8-0b4c-53e047b0bb6e");
+const LLUUID ANIM_AGENT_SIT_TO_STAND = LLUUID("a8dee56f-2eae-9e7a-05a2-6fb92b97e21e");
+const LLUUID ANIM_AGENT_SLEEP = LLUUID("f2bed5f9-9d44-39af-b0cd-257b2a17fe40");
+const LLUUID ANIM_AGENT_SMOKE_IDLE = LLUUID("d2f2ee58-8ad1-06c9-d8d3-3827ba31567a");
+const LLUUID ANIM_AGENT_SMOKE_INHALE = LLUUID("6802d553-49da-0778-9f85-1599a2266526");
+const LLUUID ANIM_AGENT_SMOKE_THROW_DOWN = LLUUID("0a9fb970-8b44-9114-d3a9-bf69cfe804d6");
+const LLUUID ANIM_AGENT_SNAPSHOT = LLUUID("eae8905b-271a-99e2-4c0e-31106afd100c");
+const LLUUID ANIM_AGENT_STAND = LLUUID("2408fe9e-df1d-1d7d-f4ff-1384fa7b350f");
+const LLUUID ANIM_AGENT_STANDUP = LLUUID("3da1d753-028a-5446-24f3-9c9b856d9422");
+const LLUUID ANIM_AGENT_STAND_1 = LLUUID("15468e00-3400-bb66-cecc-646d7c14458e");
+const LLUUID ANIM_AGENT_STAND_2 = LLUUID("370f3a20-6ca6-9971-848c-9a01bc42ae3c");
+const LLUUID ANIM_AGENT_STAND_3 = LLUUID("42b46214-4b44-79ae-deb8-0df61424ff4b");
+const LLUUID ANIM_AGENT_STAND_4 = LLUUID("f22fed8b-a5ed-2c93-64d5-bdd8b93c889f");
+const LLUUID ANIM_AGENT_STRETCH = LLUUID("80700431-74ec-a008-14f8-77575e73693f");
+const LLUUID ANIM_AGENT_STRIDE = LLUUID("1cb562b0-ba21-2202-efb3-30f82cdf9595");
+const LLUUID ANIM_AGENT_SURF = LLUUID("41426836-7437-7e89-025d-0aa4d10f1d69");
+const LLUUID ANIM_AGENT_SURPRISE = LLUUID("313b9881-4302-73c0-c7d0-0e7a36b6c224");
+const LLUUID ANIM_AGENT_SWORD_STRIKE = LLUUID("85428680-6bf9-3e64-b489-6f81087c24bd");
+const LLUUID ANIM_AGENT_TALK = LLUUID("5c682a95-6da4-a463-0bf6-0f5b7be129d1");
+const LLUUID ANIM_AGENT_TANTRUM = LLUUID("11000694-3f41-adc2-606b-eee1d66f3724");
+const LLUUID ANIM_AGENT_THROW_R = LLUUID("aa134404-7dac-7aca-2cba-435f9db875ca");
+const LLUUID ANIM_AGENT_TRYON_SHIRT = LLUUID("83ff59fe-2346-f236-9009-4e3608af64c1");
+const LLUUID ANIM_AGENT_TURNLEFT = LLUUID("56e0ba0d-4a9f-7f27-6117-32f2ebbf6135");
+const LLUUID ANIM_AGENT_TURNRIGHT = LLUUID("2d6daa51-3192-6794-8e2e-a15f8338ec30");
+const LLUUID ANIM_AGENT_TYPE = LLUUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");
+const LLUUID ANIM_AGENT_WALK = LLUUID("6ed24bd8-91aa-4b12-ccc7-c97c857ab4e0");
+const LLUUID ANIM_AGENT_WHISPER = LLUUID("7693f268-06c7-ea71-fa21-2b30d6533f8f");
+const LLUUID ANIM_AGENT_WHISTLE = LLUUID("b1ed7982-c68e-a982-7561-52a88a5298c0");
+const LLUUID ANIM_AGENT_WINK = LLUUID("869ecdad-a44b-671e-3266-56aef2e3ac2e");
+const LLUUID ANIM_AGENT_WINK_HOLLYWOOD = LLUUID("c0c4030f-c02b-49de-24ba-2331f43fe41c");
+const LLUUID ANIM_AGENT_WORRY = LLUUID("9f496bd2-589a-709f-16cc-69bf7df1d36c");
+const LLUUID ANIM_AGENT_YES = LLUUID("15dd911d-be82-2856-26db-27659b142875");
+const LLUUID ANIM_AGENT_YES_HAPPY = LLUUID("b8c8b2a3-9008-1771-3bfc-90924955ab2d");
+const LLUUID ANIM_AGENT_YOGA_FLOAT = LLUUID("42ecd00b-9947-a97c-400a-bbc9174c7aeb");
+
+extern LLUUID AGENT_WALK_ANIMS[];
+extern S32 NUM_AGENT_WALK_ANIMS;
+
+extern LLUUID AGENT_GUN_HOLD_ANIMS[];
+extern S32 NUM_AGENT_GUN_HOLD_ANIMS;
+
+extern LLUUID AGENT_GUN_AIM_ANIMS[];
+extern S32 NUM_AGENT_GUN_AIM_ANIMS;
+
+extern LLUUID AGENT_NO_ROTATE_ANIMS[];
+extern S32 NUM_AGENT_NO_ROTATE_ANIMS;
+
+extern LLUUID AGENT_STAND_ANIMS[];
+extern S32 NUM_AGENT_STAND_ANIMS;
+
+class LLAnimationLibrary
+{
+private:
+ LLStringTable mAnimStringTable;
+
+ typedef std::map<LLUUID, char *> anim_map_t;
+ anim_map_t mAnimMap;
+
+public:
+ LLAnimationLibrary();
+ ~LLAnimationLibrary();
+
+ //-----------------------------------------------------------------------------
+ // Return the text name of a single animation state,
+ // Return NULL if the state is invalid
+ //-----------------------------------------------------------------------------
+ const char *animStateToString( const LLUUID& state );
+
+ //-----------------------------------------------------------------------------
+ // Return the animation state for the given name.
+ // Retun NULL if the name is invalid.
+ //-----------------------------------------------------------------------------
+ LLUUID stringToAnimState( const char *name, BOOL allow_ids = TRUE );
+};
+
+struct LLAnimStateEntry
+{
+ LLAnimStateEntry(const char* label, const char* name, const LLUUID& id)
+ : mLabel(label),
+ mName(name),
+ mID(id)
+ { }
+
+ const char* mLabel;
+ const char* mName;
+ const LLUUID mID;
+};
+
+// Animation states that the user can trigger
+extern const LLAnimStateEntry gUserAnimStates[];
+extern const S32 gUserAnimStatesCount;
+extern LLAnimationLibrary gAnimLibrary;
+
+
+#endif // LL_LLANIMATIONSTATES_H
+
+
+
diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp
new file mode 100644
index 0000000000..4d4ad39080
--- /dev/null
+++ b/indra/llcharacter/llbvhloader.cpp
@@ -0,0 +1,1489 @@
+/**
+ * @file llbvhloader.cpp
+ * @brief Translates a BVH files to LindenLabAnimation format.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llbvhloader.h"
+#include "lldatapacker.h"
+#include "lldir.h"
+#include "llkeyframemotion.h"
+#include "llquantize.h"
+#include "llstl.h"
+#include "llapr.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <llquaternion.h>
+#include <boost/tokenizer.hpp>
+
+using namespace std;
+
+#define INCHES_TO_METERS 0.02540005f
+
+const F32 POSITION_KEYFRAME_THRESHOLD = 0.03f;
+const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f;
+
+const F32 POSITION_MOTION_THRESHOLD = 0.001f;
+const F32 ROTATION_MOTION_THRESHOLD = 0.001f;
+
+char gInFile[1024]; /* Flawfinder: ignore */
+char gOutFile[1024]; /* Flawfinder: ignore */
+
+//------------------------------------------------------------------------
+// Status Codes
+//------------------------------------------------------------------------
+char *LLBVHLoader::ST_OK = "Ok";
+char *LLBVHLoader::ST_EOF = "Premature end of file.";
+char *LLBVHLoader::ST_NO_CONSTRAINT = "Can't read constraint definition.";
+char *LLBVHLoader::ST_NO_FILE = "Can't open BVH file.";
+char *LLBVHLoader::ST_NO_HIER = "Invalid HIERARCHY header.";
+char *LLBVHLoader::ST_NO_JOINT = "Can't find ROOT or JOINT.";
+char *LLBVHLoader::ST_NO_NAME = "Can't get JOINT name.";
+char *LLBVHLoader::ST_NO_OFFSET = "Can't find OFFSET.";
+char *LLBVHLoader::ST_NO_CHANNELS = "Can't find CHANNELS.";
+char *LLBVHLoader::ST_NO_ROTATION = "Can't get rotation order.";
+char *LLBVHLoader::ST_NO_AXIS = "Can't get rotation axis.";
+char *LLBVHLoader::ST_NO_MOTION = "Can't find MOTION.";
+char *LLBVHLoader::ST_NO_FRAMES = "Can't get number of frames.";
+char *LLBVHLoader::ST_NO_FRAME_TIME = "Can't get frame time.";
+char *LLBVHLoader::ST_NO_POS = "Can't get position values.";
+char *LLBVHLoader::ST_NO_ROT = "Can't get rotation values.";
+char *LLBVHLoader::ST_NO_XLT_FILE = "Can't open translation file.";
+char *LLBVHLoader::ST_NO_XLT_HEADER = "Can't read translation header.";
+char *LLBVHLoader::ST_NO_XLT_NAME = "Can't read translation names.";
+char *LLBVHLoader::ST_NO_XLT_IGNORE = "Can't read translation ignore value.";
+char *LLBVHLoader::ST_NO_XLT_RELATIVE = "Can't read translation relative value.";
+char *LLBVHLoader::ST_NO_XLT_OUTNAME = "Can't read translation outname value.";
+char *LLBVHLoader::ST_NO_XLT_MATRIX = "Can't read translation matrix.";
+char *LLBVHLoader::ST_NO_XLT_MERGECHILD = "Can't get mergechild name.";
+char *LLBVHLoader::ST_NO_XLT_MERGEPARENT = "Can't get mergeparent name.";
+char *LLBVHLoader::ST_NO_XLT_PRIORITY = "Can't get priority value.";
+char *LLBVHLoader::ST_NO_XLT_LOOP = "Can't get loop value.";
+char *LLBVHLoader::ST_NO_XLT_EASEIN = "Can't get easeIn values.";
+char *LLBVHLoader::ST_NO_XLT_EASEOUT = "Can't get easeOut values.";
+char *LLBVHLoader::ST_NO_XLT_HAND = "Can't get hand morph value.";
+char *LLBVHLoader::ST_NO_XLT_EMOTE = "Can't read emote name.";
+
+//------------------------------------------------------------------------
+// find_next_whitespace()
+//------------------------------------------------------------------------
+const char *find_next_whitespace(const char *p)
+{
+ while(*p && isspace(*p)) p++;
+ while(*p && !isspace(*p)) p++;
+ return p;
+}
+
+
+//------------------------------------------------------------------------
+// bvhStringToOrder()
+//
+// XYZ order in BVH files must be passed to mayaQ() as ZYX.
+// This function reverses the input string before passing it on
+// to StringToOrder().
+//------------------------------------------------------------------------
+LLQuaternion::Order bvhStringToOrder( char *str )
+{
+ char order[4]; /* Flawfinder: ignore */
+ order[0] = str[2];
+ order[1] = str[1];
+ order[2] = str[0];
+ order[3] = 0;
+ LLQuaternion::Order retVal = StringToOrder( order );
+ return retVal;
+}
+
+//-----------------------------------------------------------------------------
+// LLBVHLoader()
+//-----------------------------------------------------------------------------
+LLBVHLoader::LLBVHLoader(const char* buffer)
+{
+ reset();
+
+ mStatus = loadTranslationTable("anim.ini");
+
+ if (mStatus == LLBVHLoader::ST_NO_XLT_FILE)
+ {
+ llwarns << "NOTE: No translation table found." << llendl;
+ return;
+ }
+ else
+ {
+ if (mStatus != LLBVHLoader::ST_OK)
+ {
+ llwarns << "ERROR: [line: " << getLineNumber() << "] " << mStatus << llendl;
+ return;
+ }
+ }
+
+ char error_text[128]; /* Flawfinder: ignore */
+ S32 error_line;
+ mStatus = loadBVHFile(buffer, error_text, error_line);
+ if (mStatus != LLBVHLoader::ST_OK)
+ {
+ llwarns << "ERROR: [line: " << getLineNumber() << "] " << mStatus << llendl;
+ return;
+ }
+
+ applyTranslations();
+ optimize();
+
+ mInitialized = TRUE;
+}
+
+LLBVHLoader::~LLBVHLoader()
+{
+ std::for_each(mJoints.begin(),mJoints.end(),DeletePointer());
+}
+
+//------------------------------------------------------------------------
+// LLBVHLoader::loadTranslationTable()
+//------------------------------------------------------------------------
+LLBVHLoader::Status LLBVHLoader::loadTranslationTable(const char *fileName)
+{
+ mLineNumber = 0;
+ mTranslations.clear();
+ mConstraints.clear();
+
+ //--------------------------------------------------------------------
+ // open file
+ //--------------------------------------------------------------------
+ char path[LL_MAX_PATH]; /* Flawfinder: ignore */
+
+ snprintf( path, sizeof(path), "%s",
+ gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName).c_str()); /* Flawfinder: ignore */
+
+
+ apr_file_t *fp = ll_apr_file_open(path, LL_APR_R);
+ if (!fp)
+ return ST_NO_XLT_FILE;
+
+ llinfos << "NOTE: Loading translation table: " << fileName << llendl;
+
+ //--------------------------------------------------------------------
+ // register file to be closed on function exit
+ //--------------------------------------------------------------------
+ FileCloser fileCloser(fp);
+
+ //--------------------------------------------------------------------
+ // load header
+ //--------------------------------------------------------------------
+ if ( ! getLine(fp) )
+ return ST_EOF;
+ if ( strncmp(mLine, "Translations 1.0", 16) )
+ return ST_NO_XLT_HEADER;
+
+ //--------------------------------------------------------------------
+ // load data one line at a time
+ //--------------------------------------------------------------------
+ BOOL loadingGlobals = FALSE;
+ Translation *trans = NULL;
+ while ( getLine(fp) )
+ {
+ //----------------------------------------------------------------
+ // check the 1st token on the line to determine if it's empty or a comment
+ //----------------------------------------------------------------
+ char token[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %127s", token) != 1 )
+ continue;
+
+ if (token[0] == '#')
+ continue;
+
+ //----------------------------------------------------------------
+ // check if a [jointName] or [GLOBALS] was specified.
+ //----------------------------------------------------------------
+ if (token[0] == '[')
+ {
+ char name[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " [%127[^]]", name) != 1 )
+ return ST_NO_XLT_NAME;
+
+ if (strcmp(name, "GLOBALS")==0)
+ {
+ loadingGlobals = TRUE;
+ continue;
+ }
+ else
+ {
+ loadingGlobals = FALSE;
+ Translation &newTrans = mTranslations[ name ];
+ trans = &newTrans;
+ continue;
+ }
+ }
+
+ //----------------------------------------------------------------
+ // check for optional emote
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "emote")==0)
+ {
+ char emote_str[1024]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %1023s", emote_str) != 1 )
+ return ST_NO_XLT_EMOTE;
+
+ mEmoteName.assign( emote_str );
+// llinfos << "NOTE: Emote: " << mEmoteName.c_str() << llendl;
+ continue;
+ }
+
+
+ //----------------------------------------------------------------
+ // check for global priority setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "priority")==0)
+ {
+ S32 priority;
+ if ( sscanf(mLine, " %*s = %d", &priority) != 1 )
+ return ST_NO_XLT_PRIORITY;
+
+ mPriority = priority;
+// llinfos << "NOTE: Priority: " << mPriority << llendl;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for global loop setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "loop")==0)
+ {
+ char trueFalse[128]; /* Flawfinder: ignore */
+ trueFalse[0] = '\0';
+
+ F32 loop_in = 0.f;
+ F32 loop_out = 1.f;
+
+ if ( sscanf(mLine, " %*s = %f %f", &loop_in, &loop_out) == 2 )
+ {
+ mLoop = TRUE;
+ }
+ else if ( sscanf(mLine, " %*s = %127s", trueFalse) == 1 )
+ {
+ mLoop = (LLString::compareInsensitive(trueFalse, "true")==0);
+ }
+ else
+ {
+ return ST_NO_XLT_LOOP;
+ }
+
+ mLoopInPoint = loop_in * mDuration;
+ mLoopOutPoint = loop_out * mDuration;
+
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for global easeIn setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "easein")==0)
+ {
+ F32 duration;
+ char type[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 )
+ return ST_NO_XLT_EASEIN;
+
+ mEaseIn = duration;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for global easeOut setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "easeout")==0)
+ {
+ F32 duration;
+ char type[128];
+ if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 )
+ return ST_NO_XLT_EASEOUT;
+
+ mEaseOut = duration;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for global handMorph setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "hand")==0)
+ {
+ S32 handMorph;
+ if (sscanf(mLine, " %*s = %d", &handMorph) != 1)
+ return ST_NO_XLT_HAND;
+
+ mHand = handMorph;
+ continue;
+ }
+
+ if (loadingGlobals && LLString::compareInsensitive(token, "constraint")==0)
+ {
+ Constraint constraint;
+
+ // try reading optional target direction
+ if(sscanf(
+ mLine,
+ " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f",
+ &constraint.mChainLength,
+ &constraint.mEaseInStart,
+ &constraint.mEaseInStop,
+ &constraint.mEaseOutStart,
+ &constraint.mEaseOutStop,
+ constraint.mSourceJointName,
+ &constraint.mSourceOffset.mV[VX],
+ &constraint.mSourceOffset.mV[VY],
+ &constraint.mSourceOffset.mV[VZ],
+ constraint.mTargetJointName,
+ &constraint.mTargetOffset.mV[VX],
+ &constraint.mTargetOffset.mV[VY],
+ &constraint.mTargetOffset.mV[VZ],
+ &constraint.mTargetDir.mV[VX],
+ &constraint.mTargetDir.mV[VY],
+ &constraint.mTargetDir.mV[VZ]) != 16)
+ {
+ if(sscanf(
+ mLine,
+ " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f",
+ &constraint.mChainLength,
+ &constraint.mEaseInStart,
+ &constraint.mEaseInStop,
+ &constraint.mEaseOutStart,
+ &constraint.mEaseOutStop,
+ constraint.mSourceJointName,
+ &constraint.mSourceOffset.mV[VX],
+ &constraint.mSourceOffset.mV[VY],
+ &constraint.mSourceOffset.mV[VZ],
+ constraint.mTargetJointName,
+ &constraint.mTargetOffset.mV[VX],
+ &constraint.mTargetOffset.mV[VY],
+ &constraint.mTargetOffset.mV[VZ]) != 13)
+ {
+ return ST_NO_CONSTRAINT;
+ }
+ }
+ else
+ {
+ // normalize direction
+ if (!constraint.mTargetDir.isExactlyZero())
+ {
+ constraint.mTargetDir.normVec();
+ }
+
+ }
+
+ constraint.mConstraintType = CONSTRAINT_TYPE_POINT;
+ mConstraints.push_back(constraint);
+ continue;
+ }
+
+ if (loadingGlobals && LLString::compareInsensitive(token, "planar_constraint")==0)
+ {
+ Constraint constraint;
+
+ // try reading optional target direction
+ if(sscanf(
+ mLine,
+ " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f",
+ &constraint.mChainLength,
+ &constraint.mEaseInStart,
+ &constraint.mEaseInStop,
+ &constraint.mEaseOutStart,
+ &constraint.mEaseOutStop,
+ constraint.mSourceJointName,
+ &constraint.mSourceOffset.mV[VX],
+ &constraint.mSourceOffset.mV[VY],
+ &constraint.mSourceOffset.mV[VZ],
+ constraint.mTargetJointName,
+ &constraint.mTargetOffset.mV[VX],
+ &constraint.mTargetOffset.mV[VY],
+ &constraint.mTargetOffset.mV[VZ],
+ &constraint.mTargetDir.mV[VX],
+ &constraint.mTargetDir.mV[VY],
+ &constraint.mTargetDir.mV[VZ]) != 16)
+ {
+ if(sscanf(
+ mLine,
+ " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f",
+ &constraint.mChainLength,
+ &constraint.mEaseInStart,
+ &constraint.mEaseInStop,
+ &constraint.mEaseOutStart,
+ &constraint.mEaseOutStop,
+ constraint.mSourceJointName,
+ &constraint.mSourceOffset.mV[VX],
+ &constraint.mSourceOffset.mV[VY],
+ &constraint.mSourceOffset.mV[VZ],
+ constraint.mTargetJointName,
+ &constraint.mTargetOffset.mV[VX],
+ &constraint.mTargetOffset.mV[VY],
+ &constraint.mTargetOffset.mV[VZ]) != 13)
+ {
+ return ST_NO_CONSTRAINT;
+ }
+ }
+ else
+ {
+ // normalize direction
+ if (!constraint.mTargetDir.isExactlyZero())
+ {
+ constraint.mTargetDir.normVec();
+ }
+
+ }
+
+ constraint.mConstraintType = CONSTRAINT_TYPE_PLANE;
+ mConstraints.push_back(constraint);
+ continue;
+ }
+
+
+ //----------------------------------------------------------------
+ // at this point there must be a valid trans pointer
+ //----------------------------------------------------------------
+ if ( ! trans )
+ return ST_NO_XLT_NAME;
+
+ //----------------------------------------------------------------
+ // check for ignore flag
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "ignore")==0 )
+ {
+ char trueFalse[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", trueFalse) != 1 )
+ return ST_NO_XLT_IGNORE;
+
+ trans->mIgnore = (LLString::compareInsensitive(trueFalse, "true")==0);
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for relativepos flag
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "relativepos")==0 )
+ {
+ F32 x, y, z;
+ char relpos[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %f %f %f", &x, &y, &z) == 3 )
+ {
+ trans->mRelativePosition.setVec( x, y, z );
+ }
+ else if ( sscanf(mLine, " %*s = %127s", relpos) == 1 )
+ {
+ if ( LLString::compareInsensitive(relpos, "firstkey")==0 )
+ {
+ trans->mRelativePositionKey = TRUE;
+ }
+ else
+ {
+ return ST_NO_XLT_RELATIVE;
+ }
+ }
+ else
+ {
+ return ST_NO_XLT_RELATIVE;
+ }
+
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for relativerot flag
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "relativerot")==0 )
+ {
+ //F32 x, y, z;
+ char relpos[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", relpos) == 1 )
+ {
+ if ( LLString::compareInsensitive(relpos, "firstkey")==0 )
+ {
+ trans->mRelativeRotationKey = TRUE;
+ }
+ else
+ {
+ return ST_NO_XLT_RELATIVE;
+ }
+ }
+ else
+ {
+ return ST_NO_XLT_RELATIVE;
+ }
+
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for outname value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "outname")==0 )
+ {
+ char outName[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", outName) != 1 )
+ return ST_NO_XLT_OUTNAME;
+
+ trans->mOutName = outName;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for frame matrix value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "frame")==0 )
+ {
+ LLMatrix3 fm;
+ if ( sscanf(mLine, " %*s = %f %f %f, %f %f %f, %f %f %f",
+ &fm.mMatrix[0][0], &fm.mMatrix[0][1], &fm.mMatrix[0][2],
+ &fm.mMatrix[1][0], &fm.mMatrix[1][1], &fm.mMatrix[1][2],
+ &fm.mMatrix[2][0], &fm.mMatrix[2][1], &fm.mMatrix[2][2] ) != 9 )
+ return ST_NO_XLT_MATRIX;
+
+ trans->mFrameMatrix = fm;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for offset matrix value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "offset")==0 )
+ {
+ LLMatrix3 om;
+ if ( sscanf(mLine, " %*s = %f %f %f, %f %f %f, %f %f %f",
+ &om.mMatrix[0][0], &om.mMatrix[0][1], &om.mMatrix[0][2],
+ &om.mMatrix[1][0], &om.mMatrix[1][1], &om.mMatrix[1][2],
+ &om.mMatrix[2][0], &om.mMatrix[2][1], &om.mMatrix[2][2] ) != 9 )
+ return ST_NO_XLT_MATRIX;
+
+ trans->mOffsetMatrix = om;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for mergeparent value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "mergeparent")==0 )
+ {
+ char mergeParentName[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", mergeParentName) != 1 )
+ return ST_NO_XLT_MERGEPARENT;
+
+ trans->mMergeParentName = mergeParentName;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for mergechild value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "mergechild")==0 )
+ {
+ char mergeChildName[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", mergeChildName) != 1 )
+ return ST_NO_XLT_MERGECHILD;
+
+ trans->mMergeChildName = mergeChildName;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for per-joint priority
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "priority")==0 )
+ {
+ S32 priority;
+ if ( sscanf(mLine, " %*s = %d", &priority) != 1 )
+ return ST_NO_XLT_PRIORITY;
+
+ trans->mPriorityModifier = priority;
+ continue;
+ }
+
+ }
+ return ST_OK;
+}
+
+
+//------------------------------------------------------------------------
+// LLBVHLoader::loadBVHFile()
+//------------------------------------------------------------------------
+LLBVHLoader::Status LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &err_line)
+{
+ std::string line;
+
+ err_line = 0;
+ error_text[127] = '\0';
+
+ std::string str(buffer);
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("\r\n");
+ tokenizer tokens(str, sep);
+ tokenizer::iterator iter = tokens.begin();
+
+ mLineNumber = 0;
+ mJoints.clear();
+
+ std::vector<S32> parent_joints;
+
+ //--------------------------------------------------------------------
+ // consume hierarchy
+ //--------------------------------------------------------------------
+ if (iter == tokens.end())
+ return ST_EOF;
+ line = (*(iter++));
+ err_line++;
+
+ if ( !strstr(line.c_str(), "HIERARCHY") )
+ {
+// llinfos << line << llendl;
+ return ST_NO_HIER;
+ }
+
+ //--------------------------------------------------------------------
+ // consume joints
+ //--------------------------------------------------------------------
+ while (TRUE)
+ {
+ //----------------------------------------------------------------
+ // get next line
+ //----------------------------------------------------------------
+ if (iter == tokens.end())
+ return ST_EOF;
+ line = (*(iter++));
+ err_line++;
+
+ //----------------------------------------------------------------
+ // consume }
+ //----------------------------------------------------------------
+ if ( strstr(line.c_str(), "}") )
+ {
+ if (parent_joints.size() > 0)
+ {
+ parent_joints.pop_back();
+ }
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // if MOTION, break out
+ //----------------------------------------------------------------
+ if ( strstr(line.c_str(), "MOTION") )
+ break;
+
+ //----------------------------------------------------------------
+ // it must be either ROOT or JOINT or EndSite
+ //----------------------------------------------------------------
+ if ( strstr(line.c_str(), "ROOT") )
+ {
+ }
+ else if ( strstr(line.c_str(), "JOINT") )
+ {
+ }
+ else if ( strstr(line.c_str(), "End Site") )
+ {
+ iter++; // {
+ iter++; // OFFSET
+ S32 depth = 0;
+ for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
+ {
+ Joint *joint = mJoints[parent_joints[j]];
+ if (depth > joint->mChildTreeMaxDepth)
+ {
+ joint->mChildTreeMaxDepth = depth;
+ }
+ depth++;
+ }
+ continue;
+ }
+ else
+ {
+ strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
+ return ST_NO_JOINT;
+ }
+
+ //----------------------------------------------------------------
+ // get the joint name
+ //----------------------------------------------------------------
+ char jointName[80]; /* Flawfinder: ignore */
+ if ( sscanf(line.c_str(), "%*s %79s", jointName) != 1 )
+ {
+ strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
+ return ST_NO_NAME;
+ }
+
+ //----------------------------------------------------------------
+ // add a set of keyframes for this joint
+ //----------------------------------------------------------------
+ mJoints.push_back( new Joint( jointName ) );
+ Joint *joint = mJoints.back();
+
+ S32 depth = 1;
+ for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
+ {
+ Joint *pjoint = mJoints[parent_joints[j]];
+ if (depth > pjoint->mChildTreeMaxDepth)
+ {
+ pjoint->mChildTreeMaxDepth = depth;
+ }
+ depth++;
+ }
+
+ //----------------------------------------------------------------
+ // get next line
+ //----------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ //----------------------------------------------------------------
+ // it must be {
+ //----------------------------------------------------------------
+ if ( !strstr(line.c_str(), "{") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_OFFSET;
+ }
+ else
+ {
+ parent_joints.push_back((S32)mJoints.size() - 1);
+ }
+
+ //----------------------------------------------------------------
+ // get next line
+ //----------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ //----------------------------------------------------------------
+ // it must be OFFSET
+ //----------------------------------------------------------------
+ if ( !strstr(line.c_str(), "OFFSET") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_OFFSET;
+ }
+
+ //----------------------------------------------------------------
+ // get next line
+ //----------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ //----------------------------------------------------------------
+ // it must be CHANNELS
+ //----------------------------------------------------------------
+ if ( !strstr(line.c_str(), "CHANNELS") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_CHANNELS;
+ }
+
+ //----------------------------------------------------------------
+ // get rotation order
+ //----------------------------------------------------------------
+ const char *p = line.c_str();
+ for (S32 i=0; i<3; i++)
+ {
+ p = strstr(p, "rotation");
+ if (!p)
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROTATION;
+ }
+
+ const char axis = *(p - 1);
+ if ((axis != 'X') && (axis != 'Y') && (axis != 'Z'))
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_AXIS;
+ }
+
+ joint->mOrder[i] = axis;
+
+ p++;
+ }
+ }
+
+ //--------------------------------------------------------------------
+ // consume motion
+ //--------------------------------------------------------------------
+ if ( !strstr(line.c_str(), "MOTION") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_MOTION;
+ }
+
+ //--------------------------------------------------------------------
+ // get number of frames
+ //--------------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ if ( !strstr(line.c_str(), "Frames:") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_FRAMES;
+ }
+
+ if ( sscanf(line.c_str(), "Frames: %d", &mNumFrames) != 1 )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_FRAMES;
+ }
+
+ //--------------------------------------------------------------------
+ // get frame time
+ //--------------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ if ( !strstr(line.c_str(), "Frame Time:") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_FRAME_TIME;
+ }
+
+ if ( sscanf(line.c_str(), "Frame Time: %f", &mFrameTime) != 1 )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_FRAME_TIME;
+ }
+
+ mDuration = (F32)mNumFrames * mFrameTime;
+ if (!mLoop)
+ {
+ mLoopOutPoint = mDuration;
+ }
+
+ //--------------------------------------------------------------------
+ // load frames
+ //--------------------------------------------------------------------
+ for (S32 i=0; i<mNumFrames; i++)
+ {
+ // get next line
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ // read and store values
+ const char *p = line.c_str();
+ for (U32 j=0; j<mJoints.size(); j++)
+ {
+ Joint *joint = mJoints[j];
+ joint->mKeys.push_back( Key() );
+ Key &key = joint->mKeys.back();
+
+ // get 3 pos values for root joint only
+ if (j==0)
+ {
+ if ( sscanf(p, "%f %f %f", key.mPos, key.mPos+1, key.mPos+2) != 3 )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_POS;
+ }
+ }
+
+ // skip to next 3 values in the line
+ p = find_next_whitespace(p);
+ if (!p)
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROT;
+ }
+ p = find_next_whitespace(++p);
+ if (!p)
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROT;
+ }
+ p = find_next_whitespace(++p);
+ if (!p)
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROT;
+ }
+
+ // get 3 rot values for joint
+ F32 rot[3];
+ if ( sscanf(p, " %f %f %f", rot, rot+1, rot+2) != 3 )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROT;
+ }
+
+ p++;
+
+ key.mRot[ joint->mOrder[0]-'X' ] = rot[0];
+ key.mRot[ joint->mOrder[1]-'X' ] = rot[1];
+ key.mRot[ joint->mOrder[2]-'X' ] = rot[2];
+ }
+ }
+
+ return ST_OK;
+}
+
+
+//------------------------------------------------------------------------
+// LLBVHLoader::applyTranslation()
+//------------------------------------------------------------------------
+void LLBVHLoader::applyTranslations()
+{
+ JointVector::iterator ji;
+ for (ji = mJoints.begin(); ji != mJoints.end(); ++ji )
+ {
+ Joint *joint = *ji;
+ //----------------------------------------------------------------
+ // Look for a translation for this joint.
+ // If none, skip to next joint
+ //----------------------------------------------------------------
+ TranslationMap::iterator ti = mTranslations.find( joint->mName );
+ if ( ti == mTranslations.end() )
+ {
+ continue;
+ }
+
+ Translation &trans = ti->second;
+
+ //----------------------------------------------------------------
+ // Set the ignore flag if necessary
+ //----------------------------------------------------------------
+ if ( trans.mIgnore )
+ {
+ //llinfos << "NOTE: Ignoring " << joint->mName.c_str() << llendl;
+ joint->mIgnore = TRUE;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // Set the output name
+ //----------------------------------------------------------------
+ if ( ! trans.mOutName.empty() )
+ {
+ //llinfos << "NOTE: Changing " << joint->mName.c_str() << " to " << trans.mOutName.c_str() << llendl;
+ joint->mOutName = trans.mOutName;
+ }
+
+ //----------------------------------------------------------------
+ // Set the ignorepos flag if necessary
+ //----------------------------------------------------------------
+ if ( joint->mOutName == std::string("mPelvis") )
+ {
+ joint->mIgnorePositions = FALSE;
+ }
+
+ //----------------------------------------------------------------
+ // Set the relativepos flags if necessary
+ //----------------------------------------------------------------
+ if ( trans.mRelativePositionKey )
+ {
+// llinfos << "NOTE: Removing 1st position offset from all keys for " << joint->mOutName.c_str() << llendl;
+ joint->mRelativePositionKey = TRUE;
+ }
+
+ if ( trans.mRelativeRotationKey )
+ {
+// llinfos << "NOTE: Removing 1st rotation from all keys for " << joint->mOutName.c_str() << llendl;
+ joint->mRelativeRotationKey = TRUE;
+ }
+
+ if ( trans.mRelativePosition.magVec() > 0.0f )
+ {
+ joint->mRelativePosition = trans.mRelativePosition;
+// llinfos << "NOTE: Removing " <<
+// joint->mRelativePosition.mV[0] << " " <<
+// joint->mRelativePosition.mV[1] << " " <<
+// joint->mRelativePosition.mV[2] <<
+// " from all position keys in " <<
+// joint->mOutName.c_str() << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // Set change of coordinate frame
+ //----------------------------------------------------------------
+ joint->mFrameMatrix = trans.mFrameMatrix;
+ joint->mOffsetMatrix = trans.mOffsetMatrix;
+
+ //----------------------------------------------------------------
+ // Set mergeparent name
+ //----------------------------------------------------------------
+ if ( ! trans.mMergeParentName.empty() )
+ {
+// llinfos << "NOTE: Merging " << joint->mOutName.c_str() <<
+// " with parent " <<
+// trans.mMergeParentName.c_str() << llendl;
+ joint->mMergeParentName = trans.mMergeParentName;
+ }
+
+ //----------------------------------------------------------------
+ // Set mergechild name
+ //----------------------------------------------------------------
+ if ( ! trans.mMergeChildName.empty() )
+ {
+// llinfos << "NOTE: Merging " << joint->mName.c_str() <<
+// " with child " << trans.mMergeChildName.c_str() << llendl;
+ joint->mMergeChildName = trans.mMergeChildName;
+ }
+
+ //----------------------------------------------------------------
+ // Set joint priority
+ //----------------------------------------------------------------
+ joint->mPriority = mPriority + trans.mPriorityModifier;
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLBVHLoader::optimize()
+//-----------------------------------------------------------------------------
+void LLBVHLoader::optimize()
+{
+ //RN: assumes motion blend, which is the default now
+ if (!mLoop && mEaseIn + mEaseOut > mDuration && mDuration != 0.f)
+ {
+ F32 factor = mDuration / (mEaseIn + mEaseOut);
+ mEaseIn *= factor;
+ mEaseOut *= factor;
+ }
+
+ JointVector::iterator ji;
+ for (ji = mJoints.begin(); ji != mJoints.end(); ++ji)
+ {
+ Joint *joint = *ji;
+ BOOL pos_changed = FALSE;
+ BOOL rot_changed = FALSE;
+
+ if ( ! joint->mIgnore )
+ {
+ joint->mNumPosKeys = 0;
+ joint->mNumRotKeys = 0;
+ LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
+
+ KeyVector::iterator first_key = joint->mKeys.begin();
+
+ // no keys?
+ if (first_key == joint->mKeys.end())
+ {
+ joint->mIgnore = TRUE;
+ continue;
+ }
+
+ LLVector3 first_frame_pos(first_key->mPos);
+ LLQuaternion first_frame_rot = mayaQ( first_key->mRot[0], first_key->mRot[1], first_key->mRot[2], order);
+
+ // skip first key
+ KeyVector::iterator ki = joint->mKeys.begin();
+ if (joint->mKeys.size() == 1)
+ {
+ //FIXME: use single frame to move pelvis
+ // if only one keyframe force output for this joint
+ rot_changed = TRUE;
+ }
+ else
+ {
+ // if more than one keyframe, use first frame as reference and skip to second
+ first_key->mIgnorePos = TRUE;
+ first_key->mIgnoreRot = TRUE;
+ ++ki;
+ }
+
+ KeyVector::iterator ki_prev = ki;
+ KeyVector::iterator ki_last_good_pos = ki;
+ KeyVector::iterator ki_last_good_rot = ki;
+ S32 numPosFramesConsidered = 2;
+ S32 numRotFramesConsidered = 2;
+
+ F32 rot_threshold = ROTATION_KEYFRAME_THRESHOLD / llmax((F32)joint->mChildTreeMaxDepth * 0.33f, 1.f);
+
+ for (; ki != joint->mKeys.end(); ++ki)
+ {
+ if (ki_prev == ki_last_good_pos)
+ {
+ joint->mNumPosKeys++;
+ if (dist_vec(LLVector3(ki_prev->mPos), first_frame_pos) > POSITION_MOTION_THRESHOLD)
+ {
+ pos_changed = TRUE;
+ }
+ }
+ else
+ {
+ //check position for noticeable effect
+ LLVector3 test_pos(ki_prev->mPos);
+ LLVector3 last_good_pos(ki_last_good_pos->mPos);
+ LLVector3 current_pos(ki->mPos);
+ LLVector3 interp_pos = lerp(current_pos, last_good_pos, 1.f / (F32)numPosFramesConsidered);
+
+ if (dist_vec(current_pos, first_frame_pos) > POSITION_MOTION_THRESHOLD)
+ {
+ pos_changed = TRUE;
+ }
+
+ if (dist_vec(interp_pos, test_pos) < POSITION_KEYFRAME_THRESHOLD)
+ {
+ ki_prev->mIgnorePos = TRUE;
+ numPosFramesConsidered++;
+ }
+ else
+ {
+ numPosFramesConsidered = 2;
+ ki_last_good_pos = ki_prev;
+ joint->mNumPosKeys++;
+ }
+ }
+
+ if (ki_prev == ki_last_good_rot)
+ {
+ joint->mNumRotKeys++;
+ LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
+ F32 x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
+ F32 y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
+ F32 rot_test = x_delta + y_delta;
+
+ if (rot_test > ROTATION_MOTION_THRESHOLD)
+ {
+ rot_changed = TRUE;
+ }
+ }
+ else
+ {
+ //check rotation for noticeable effect
+ LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
+ LLQuaternion last_good_rot = mayaQ( ki_last_good_rot->mRot[0], ki_last_good_rot->mRot[1], ki_last_good_rot->mRot[2], order);
+ LLQuaternion current_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
+ LLQuaternion interp_rot = lerp(1.f / (F32)numRotFramesConsidered, current_rot, last_good_rot);
+
+ F32 x_delta;
+ F32 y_delta;
+ F32 rot_test;
+
+ x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
+ y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
+ rot_test = x_delta + y_delta;
+
+ if (rot_test > ROTATION_MOTION_THRESHOLD)
+ {
+ rot_changed = TRUE;
+ }
+
+ x_delta = dist_vec(LLVector3::x_axis * interp_rot, LLVector3::x_axis * test_rot);
+ y_delta = dist_vec(LLVector3::y_axis * interp_rot, LLVector3::y_axis * test_rot);
+ rot_test = x_delta + y_delta;
+
+ if (rot_test < rot_threshold)
+ {
+ ki_prev->mIgnoreRot = TRUE;
+ numRotFramesConsidered++;
+ }
+ else
+ {
+ numRotFramesConsidered = 2;
+ ki_last_good_rot = ki_prev;
+ joint->mNumRotKeys++;
+ }
+ }
+
+ ki_prev = ki;
+ }
+ }
+
+ // don't output joints with no motion
+ if (!(pos_changed || rot_changed))
+ {
+ //llinfos << "Ignoring joint " << joint->mName << llendl;
+ joint->mIgnore = TRUE;
+ }
+ }
+}
+
+void LLBVHLoader::reset()
+{
+ mLineNumber = 0;
+ mNumFrames = 0;
+ mFrameTime = 0.0f;
+ mDuration = 0.0f;
+
+ mPriority = 2;
+ mLoop = FALSE;
+ mLoopInPoint = 0.f;
+ mLoopOutPoint = 0.f;
+ mEaseIn = 0.3f;
+ mEaseOut = 0.3f;
+ mHand = 1;
+ mInitialized = FALSE;
+
+ mEmoteName = "";
+}
+
+//------------------------------------------------------------------------
+// LLBVHLoader::getLine()
+//------------------------------------------------------------------------
+BOOL LLBVHLoader::getLine(apr_file_t* fp)
+{
+ if (apr_file_eof(fp) == APR_EOF)
+ {
+ return FALSE;
+ }
+ if ( apr_file_gets(mLine, BVH_PARSER_LINE_SIZE, fp) == APR_SUCCESS)
+ {
+ mLineNumber++;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// returns required size of output buffer
+U32 LLBVHLoader::getOutputSize()
+{
+ LLDataPackerBinaryBuffer dp;
+ serialize(dp);
+
+ return dp.getCurrentSize();
+}
+
+// writes contents to datapacker
+BOOL LLBVHLoader::serialize(LLDataPacker& dp)
+{
+ JointVector::iterator ji;
+ KeyVector::iterator ki;
+ F32 time;
+
+ // count number of non-ignored joints
+ S32 numJoints = 0;
+ for (ji=mJoints.begin(); ji!=mJoints.end(); ++ji)
+ {
+ Joint *joint = *ji;
+ if ( ! joint->mIgnore )
+ numJoints++;
+ }
+
+ // print header
+ dp.packU16(KEYFRAME_MOTION_VERSION, "version");
+ dp.packU16(KEYFRAME_MOTION_SUBVERSION, "sub_version");
+ dp.packS32(mPriority, "base_priority");
+ dp.packF32(mDuration, "duration");
+ dp.packString(mEmoteName.c_str(), "emote_name");
+ dp.packF32(mLoopInPoint, "loop_in_point");
+ dp.packF32(mLoopOutPoint, "loop_out_point");
+ dp.packS32(mLoop, "loop");
+ dp.packF32(mEaseIn, "ease_in_duration");
+ dp.packF32(mEaseOut, "ease_out_duration");
+ dp.packU32(mHand, "hand_pose");
+ dp.packU32(numJoints, "num_joints");
+
+ for ( ji = mJoints.begin();
+ ji != mJoints.end();
+ ++ji )
+ {
+ Joint *joint = *ji;
+ // if ignored, skip it
+ if ( joint->mIgnore )
+ continue;
+
+ LLQuaternion first_frame_rot;
+ LLQuaternion fixup_rot;
+
+ dp.packString(joint->mOutName.c_str(), "joint_name");
+ dp.packS32(joint->mPriority, "joint_priority");
+
+ // compute coordinate frame rotation
+ LLQuaternion frameRot( joint->mFrameMatrix );
+ LLQuaternion frameRotInv = ~frameRot;
+
+ LLQuaternion offsetRot( joint->mOffsetMatrix );
+
+ // find mergechild and mergeparent joints, if specified
+ LLQuaternion mergeParentRot;
+ LLQuaternion mergeChildRot;
+ Joint *mergeParent = NULL;
+ Joint *mergeChild = NULL;
+
+ JointVector::iterator mji;
+ for (mji=mJoints.begin(); mji!=mJoints.end(); ++mji)
+ {
+ Joint *mjoint = *mji;
+ if ( !joint->mMergeParentName.empty() && (mjoint->mName == joint->mMergeParentName) )
+ {
+ mergeParent = *mji;
+ }
+ if ( !joint->mMergeChildName.empty() && (mjoint->mName == joint->mMergeChildName) )
+ {
+ mergeChild = *mji;
+ }
+ }
+
+ dp.packS32(joint->mNumRotKeys, "num_rot_keys");
+
+ LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
+ S32 outcount = 0;
+ S32 frame = 1;
+ for ( ki = joint->mKeys.begin();
+ ki != joint->mKeys.end();
+ ++ki )
+ {
+ if ((frame == 1) && joint->mRelativeRotationKey)
+ {
+ first_frame_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
+
+ fixup_rot.shortestArc(LLVector3::z_axis * first_frame_rot * frameRot, LLVector3::z_axis);
+ }
+
+ if (ki->mIgnoreRot)
+ {
+ frame++;
+ continue;
+ }
+
+ time = (F32)frame * mFrameTime;
+
+ if (mergeParent)
+ {
+ mergeParentRot = mayaQ( mergeParent->mKeys[frame-1].mRot[0],
+ mergeParent->mKeys[frame-1].mRot[1],
+ mergeParent->mKeys[frame-1].mRot[2],
+ bvhStringToOrder(mergeParent->mOrder) );
+ LLQuaternion parentFrameRot( mergeParent->mFrameMatrix );
+ LLQuaternion parentOffsetRot( mergeParent->mOffsetMatrix );
+ mergeParentRot = ~parentFrameRot * mergeParentRot * parentFrameRot * parentOffsetRot;
+ }
+ else
+ {
+ mergeParentRot.loadIdentity();
+ }
+
+ if (mergeChild)
+ {
+ mergeChildRot = mayaQ( mergeChild->mKeys[frame-1].mRot[0],
+ mergeChild->mKeys[frame-1].mRot[1],
+ mergeChild->mKeys[frame-1].mRot[2],
+ bvhStringToOrder(mergeChild->mOrder) );
+ LLQuaternion childFrameRot( mergeChild->mFrameMatrix );
+ LLQuaternion childOffsetRot( mergeChild->mOffsetMatrix );
+ mergeChildRot = ~childFrameRot * mergeChildRot * childFrameRot * childOffsetRot;
+
+ }
+ else
+ {
+ mergeChildRot.loadIdentity();
+ }
+
+ LLQuaternion inRot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
+
+ LLQuaternion outRot = frameRotInv* mergeChildRot * inRot * mergeParentRot * ~first_frame_rot * frameRot * offsetRot;
+
+ U16 time_short = F32_to_U16(time, 0.f, mDuration);
+ dp.packU16(time_short, "time");
+ U16 x, y, z;
+ LLVector3 rot_vec = outRot.packToVector3();
+ rot_vec.quantize16(-1.f, 1.f, -1.f, 1.f);
+ x = F32_to_U16(rot_vec.mV[VX], -1.f, 1.f);
+ y = F32_to_U16(rot_vec.mV[VY], -1.f, 1.f);
+ z = F32_to_U16(rot_vec.mV[VZ], -1.f, 1.f);
+ dp.packU16(x, "rot_angle_x");
+ dp.packU16(y, "rot_angle_y");
+ dp.packU16(z, "rot_angle_z");
+ outcount++;
+ frame++;
+ }
+
+ // output position keys (only for 1st joint)
+ if ( ji == mJoints.begin() && !joint->mIgnorePositions )
+ {
+ dp.packS32(joint->mNumPosKeys, "num_pos_keys");
+
+ LLVector3 relPos = joint->mRelativePosition;
+ LLVector3 relKey;
+
+ frame = 1;
+ for ( ki = joint->mKeys.begin();
+ ki != joint->mKeys.end();
+ ++ki )
+ {
+ if ((frame == 1) && joint->mRelativePositionKey)
+ {
+ relKey.setVec(ki->mPos);
+ }
+
+ if (ki->mIgnorePos)
+ {
+ frame++;
+ continue;
+ }
+
+ time = (F32)frame * mFrameTime;
+
+ LLVector3 inPos = (LLVector3(ki->mPos) - relKey) * ~first_frame_rot;// * fixup_rot;
+ LLVector3 outPos = inPos * frameRot * offsetRot;
+
+ outPos *= INCHES_TO_METERS;
+
+ outPos -= relPos;
+ outPos.clamp(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+
+ U16 time_short = F32_to_U16(time, 0.f, mDuration);
+ dp.packU16(time_short, "time");
+
+ U16 x, y, z;
+ outPos.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ x = F32_to_U16(outPos.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ y = F32_to_U16(outPos.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ z = F32_to_U16(outPos.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ dp.packU16(x, "pos_x");
+ dp.packU16(y, "pos_y");
+ dp.packU16(z, "pos_z");
+
+ frame++;
+ }
+ }
+ else
+ {
+ dp.packS32(0, "num_pos_keys");
+ }
+ }
+
+ S32 num_constraints = (S32)mConstraints.size();
+ dp.packS32(num_constraints, "num_constraints");
+
+ for (ConstraintVector::iterator constraint_it = mConstraints.begin();
+ constraint_it != mConstraints.end();
+ constraint_it++)
+ {
+ U8 byte = constraint_it->mChainLength;
+ dp.packU8(byte, "chain_lenght");
+
+ byte = constraint_it->mConstraintType;
+ dp.packU8(byte, "constraint_type");
+ dp.packBinaryDataFixed((U8*)constraint_it->mSourceJointName, 16, "source_volume");
+ dp.packVector3(constraint_it->mSourceOffset, "source_offset");
+ dp.packBinaryDataFixed((U8*)constraint_it->mTargetJointName, 16, "target_volume");
+ dp.packVector3(constraint_it->mTargetOffset, "target_offset");
+ dp.packVector3(constraint_it->mTargetDir, "target_dir");
+ dp.packF32(constraint_it->mEaseInStart, "ease_in_start");
+ dp.packF32(constraint_it->mEaseInStop, "ease_in_stop");
+ dp.packF32(constraint_it->mEaseOutStart, "ease_out_start");
+ dp.packF32(constraint_it->mEaseOutStop, "ease_out_stop");
+ }
+
+ return TRUE;
+}
diff --git a/indra/llcharacter/llbvhloader.h b/indra/llcharacter/llbvhloader.h
new file mode 100644
index 0000000000..7e00e1d5f4
--- /dev/null
+++ b/indra/llcharacter/llbvhloader.h
@@ -0,0 +1,282 @@
+/**
+ * @file llbvhloader.h
+ * @brief Translates a BVH files to LindenLabAnimation format.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBVHLOADER_H
+#define LL_LLBVHLOADER_H
+
+#include <string>
+#include <vector>
+#include <map>
+#include <stdtypes.h>
+#include <stdio.h>
+#include "v3math.h"
+#include "m3math.h"
+#include "llmath.h"
+#include "llapr.h"
+
+const S32 BVH_PARSER_LINE_SIZE = 2048;
+const F32 MAX_ANIM_DURATION = 30.f;
+class LLDataPacker;
+
+//------------------------------------------------------------------------
+// FileCloser
+//------------------------------------------------------------------------
+class FileCloser
+{
+public:
+ FileCloser( apr_file_t *file )
+ {
+ mFile = file;
+ }
+
+ ~FileCloser()
+ {
+ apr_file_close(mFile);
+ }
+protected:
+ apr_file_t* mFile;
+};
+
+
+//------------------------------------------------------------------------
+// Key
+//------------------------------------------------------------------------
+struct Key
+{
+ Key()
+ {
+ mPos[0] = mPos[1] = mPos[2] = 0.0f;
+ mRot[0] = mRot[1] = mRot[2] = 0.0f;
+ mIgnorePos = false;
+ mIgnoreRot = false;
+ }
+
+ F32 mPos[3];
+ F32 mRot[3];
+ BOOL mIgnorePos;
+ BOOL mIgnoreRot;
+};
+
+
+//------------------------------------------------------------------------
+// KeyVector
+//------------------------------------------------------------------------
+typedef std::vector<Key> KeyVector;
+
+//------------------------------------------------------------------------
+// Joint
+//------------------------------------------------------------------------
+struct Joint
+{
+ Joint(const char *name)
+ {
+ mName = name;
+ mIgnore = FALSE;
+ mIgnorePositions = FALSE;
+ mRelativePositionKey = FALSE;
+ mRelativeRotationKey = FALSE;
+ mOutName = name;
+ mOrder[0] = 'X';
+ mOrder[1] = 'Y';
+ mOrder[2] = 'Z';
+ mOrder[3] = 0;
+ mNumPosKeys = 0;
+ mNumRotKeys = 0;
+ mChildTreeMaxDepth = 0;
+ mPriority = 0;
+ }
+
+ // Include aligned members first
+ LLMatrix3 mFrameMatrix;
+ LLMatrix3 mOffsetMatrix;
+ LLVector3 mRelativePosition;
+ //
+ std::string mName;
+ BOOL mIgnore;
+ BOOL mIgnorePositions;
+ BOOL mRelativePositionKey;
+ BOOL mRelativeRotationKey;
+ std::string mOutName;
+ std::string mMergeParentName;
+ std::string mMergeChildName;
+ char mOrder[4]; /* Flawfinder: ignore */
+ KeyVector mKeys;
+ S32 mNumPosKeys;
+ S32 mNumRotKeys;
+ S32 mChildTreeMaxDepth;
+ S32 mPriority;
+};
+
+
+typedef enum e_constraint_type
+{
+ CONSTRAINT_TYPE_POINT,
+ CONSTRAINT_TYPE_PLANE
+} EConstraintType;
+
+struct Constraint
+{
+ char mSourceJointName[16]; /* Flawfinder: ignore */
+ char mTargetJointName[16]; /* Flawfinder: ignore */
+ S32 mChainLength;
+ LLVector3 mSourceOffset;
+ LLVector3 mTargetOffset;
+ LLVector3 mTargetDir;
+ F32 mEaseInStart;
+ F32 mEaseInStop;
+ F32 mEaseOutStart;
+ F32 mEaseOutStop;
+ EConstraintType mConstraintType;
+};
+
+//------------------------------------------------------------------------
+// JointVector
+//------------------------------------------------------------------------
+typedef std::vector<Joint*> JointVector;
+
+//------------------------------------------------------------------------
+// ConstraintVector
+//------------------------------------------------------------------------
+typedef std::vector<Constraint> ConstraintVector;
+
+//------------------------------------------------------------------------
+// Translation
+//------------------------------------------------------------------------
+class Translation
+{
+public:
+ Translation()
+ {
+ mIgnore = FALSE;
+ mRelativePositionKey = FALSE;
+ mRelativeRotationKey = FALSE;
+ mPriorityModifier = 0;
+ }
+
+ std::string mOutName;
+ BOOL mIgnore;
+ BOOL mIgnorePositions;
+ BOOL mRelativePositionKey;
+ BOOL mRelativeRotationKey;
+ LLMatrix3 mFrameMatrix;
+ LLMatrix3 mOffsetMatrix;
+ LLVector3 mRelativePosition;
+ std::string mMergeParentName;
+ std::string mMergeChildName;
+ S32 mPriorityModifier;
+};
+
+//------------------------------------------------------------------------
+// TranslationMap
+//------------------------------------------------------------------------
+typedef std::map<std::string, Translation> TranslationMap;
+
+class LLBVHLoader
+{
+ friend class LLKeyframeMotion;
+public:
+ // Constructor
+ LLBVHLoader(const char* buffer);
+ ~LLBVHLoader();
+
+ // Status Codes
+ typedef char *Status;
+ static char *ST_OK;
+ static char *ST_EOF;
+ static char *ST_NO_CONSTRAINT;
+ static char *ST_NO_FILE;
+ static char *ST_NO_HIER;
+ static char *ST_NO_JOINT;
+ static char *ST_NO_NAME;
+ static char *ST_NO_OFFSET;
+ static char *ST_NO_CHANNELS;
+ static char *ST_NO_ROTATION;
+ static char *ST_NO_AXIS;
+ static char *ST_NO_MOTION;
+ static char *ST_NO_FRAMES;
+ static char *ST_NO_FRAME_TIME;
+ static char *ST_NO_POS;
+ static char *ST_NO_ROT;
+ static char *ST_NO_XLT_FILE;
+ static char *ST_NO_XLT_HEADER;
+ static char *ST_NO_XLT_NAME;
+ static char *ST_NO_XLT_IGNORE;
+ static char *ST_NO_XLT_RELATIVE;
+ static char *ST_NO_XLT_OUTNAME;
+ static char *ST_NO_XLT_MATRIX;
+ static char *ST_NO_XLT_MERGECHILD;
+ static char *ST_NO_XLT_MERGEPARENT;
+ static char *ST_NO_XLT_PRIORITY;
+ static char *ST_NO_XLT_LOOP;
+ static char *ST_NO_XLT_EASEIN;
+ static char *ST_NO_XLT_EASEOUT;
+ static char *ST_NO_XLT_HAND;
+ static char *ST_NO_XLT_EMOTE;
+
+ // Loads the specified translation table.
+ Status loadTranslationTable(const char *fileName);
+
+ // Load the specified BVH file.
+ // Returns status code.
+ Status loadBVHFile(const char *buffer, char *error_text, S32 &error_line);
+
+ // Applies translations to BVH data loaded.
+ void applyTranslations();
+
+ // Returns the number of lines scanned.
+ // Useful for error reporting.
+ S32 getLineNumber() { return mLineNumber; }
+
+ // returns required size of output buffer
+ U32 getOutputSize();
+
+ // writes contents to datapacker
+ BOOL serialize(LLDataPacker& dp);
+
+ // flags redundant keyframe data
+ void optimize();
+
+ void reset();
+
+ F32 getDuration() { return mDuration; }
+
+ BOOL isInitialized() { return mInitialized; }
+
+ Status getStatus() { return mStatus; }
+
+protected:
+ // Consumes one line of input from file.
+ BOOL getLine(apr_file_t *fp);
+
+ // parser state
+ char mLine[BVH_PARSER_LINE_SIZE]; /* Flawfinder: ignore */
+ S32 mLineNumber;
+
+ // parsed values
+ S32 mNumFrames;
+ F32 mFrameTime;
+ JointVector mJoints;
+ ConstraintVector mConstraints;
+ TranslationMap mTranslations;
+
+ S32 mPriority;
+ BOOL mLoop;
+ F32 mLoopInPoint;
+ F32 mLoopOutPoint;
+ F32 mEaseIn;
+ F32 mEaseOut;
+ S32 mHand;
+ std::string mEmoteName;
+
+ BOOL mInitialized;
+ Status mStatus;
+ // computed values
+ F32 mDuration;
+};
+
+#endif // LL_LLBVHLOADER_H
diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp
new file mode 100644
index 0000000000..ecca9a2526
--- /dev/null
+++ b/indra/llcharacter/llcharacter.cpp
@@ -0,0 +1,472 @@
+/**
+ * @file llcharacter.cpp
+ * @brief Implementation of LLCharacter class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+
+#include "linden_common.h"
+
+#include "llcharacter.h"
+#include "llstring.h"
+
+#define SKEL_HEADER "Linden Skeleton 1.0"
+
+LLStringTable LLCharacter::sVisualParamNames(1024);
+
+// helper functions
+BOOL larger_screen_area( LLCharacter* data_new, LLCharacter* data_tested )
+{
+ return data_new->getPixelArea() > data_tested->getPixelArea();
+}
+
+LLLinkedList< LLCharacter > LLCharacter::sInstances( larger_screen_area );
+
+
+//-----------------------------------------------------------------------------
+// LLCharacter()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLCharacter::LLCharacter()
+ :
+ mPreferredPelvisHeight( 0.f ),
+ mSex( SEX_FEMALE ),
+ mAppearanceSerialNum( 0 ),
+ mSkeletonSerialNum( 0 )
+{
+ mMotionController.setCharacter( this );
+ sInstances.addData(this);
+ mPauseRequest = new LLPauseRequestHandle();
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLCharacter()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLCharacter::~LLCharacter()
+{
+ for (LLVisualParam *param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ delete param;
+ }
+ sInstances.removeData(this);
+}
+
+
+//-----------------------------------------------------------------------------
+// getJoint()
+//-----------------------------------------------------------------------------
+LLJoint *LLCharacter::getJoint( const std::string &name )
+{
+ LLJoint *joint = NULL;
+
+ LLJoint *root = getRootJoint();
+ if (root)
+ {
+ joint = root->findJoint(name);
+ }
+
+ if (!joint)
+ {
+ llwarns << "Failed to find joint." << llendl;
+ }
+ return joint;
+}
+
+//-----------------------------------------------------------------------------
+// addMotion()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::addMotion( const LLUUID& id, LLMotionConstructor create )
+{
+ return mMotionController.addMotion(id, create);
+}
+
+//-----------------------------------------------------------------------------
+// removeMotion()
+//-----------------------------------------------------------------------------
+void LLCharacter::removeMotion( const LLUUID& id )
+{
+ mMotionController.removeMotion(id);
+}
+
+//-----------------------------------------------------------------------------
+// getMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLCharacter::createMotion( const LLUUID &id )
+{
+ return mMotionController.createMotion( id );
+}
+
+//-----------------------------------------------------------------------------
+// startMotion()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::startMotion(const LLUUID &id, F32 start_offset)
+{
+ return mMotionController.startMotion(id, start_offset);
+}
+
+
+//-----------------------------------------------------------------------------
+// stopMotion()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::stopMotion(const LLUUID& id, BOOL stop_immediate)
+{
+ return mMotionController.stopMotionLocally(id, stop_immediate);
+}
+
+//-----------------------------------------------------------------------------
+// isMotionActive()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::isMotionActive(const LLUUID& id)
+{
+ LLMotion *motionp = mMotionController.findMotion(id);
+ if (motionp)
+ {
+ return mMotionController.isMotionActive(motionp);
+ }
+
+ return FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// onDeactivateMotion()
+//-----------------------------------------------------------------------------
+void LLCharacter::requestStopMotion( LLMotion* motion)
+{
+// llinfos << "DEBUG: Char::onDeactivateMotion( " << motionName << " )" << llendl;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateMotion()
+//-----------------------------------------------------------------------------
+void LLCharacter::updateMotion(BOOL force_update)
+{
+ // unpause if we're forcing an update or
+ // number of outstanding pause requests has dropped
+ // to the initial one
+ if (mMotionController.isPaused() &&
+ (force_update || mPauseRequest->getNumRefs() == 1))
+ {
+ mMotionController.unpause();
+ }
+
+ mMotionController.updateMotion();
+
+ // pause once again, after forced update, if there are outstanding
+ // pause requests
+ if (force_update && mPauseRequest->getNumRefs() > 1)
+ {
+ mMotionController.pause();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// flushAllMotions()
+//-----------------------------------------------------------------------------
+void LLCharacter::flushAllMotions()
+{
+ mMotionController.flushAllMotions();
+}
+
+
+//-----------------------------------------------------------------------------
+// dumpCharacter()
+//-----------------------------------------------------------------------------
+void LLCharacter::dumpCharacter( LLJoint *joint )
+{
+ // handle top level entry into recursion
+ if (joint == NULL)
+ {
+ llinfos << "DEBUG: Dumping Character @" << this << llendl;
+ dumpCharacter( getRootJoint() );
+ llinfos << "DEBUG: Done." << llendl;
+ return;
+ }
+
+ // print joint info
+ llinfos << "DEBUG: " << joint->getName() << " (" << (joint->getParent()?joint->getParent()->getName():std::string("ROOT")) << ")" << llendl;
+
+ // recurse
+ for ( LLJoint *j = joint->mChildren.getFirstData();
+ j != NULL;
+ j = joint->mChildren.getNextData() )
+ {
+ dumpCharacter(j);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setAnimationData()
+//-----------------------------------------------------------------------------
+void LLCharacter::setAnimationData(std::string name, void *data)
+{
+ if(mAnimationData.getValue(name))
+ {
+ *mAnimationData[name] = data;
+ }
+ else
+ {
+ mAnimationData.addToHead(name, data);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getAnimationData()
+//-----------------------------------------------------------------------------
+void * LLCharacter::getAnimationData(std::string name)
+{
+ void **result = mAnimationData.getValue(name);
+ void *return_value; // Necessary to suppress VC6 warning. JC
+ if (!result)
+ {
+ return_value = NULL;
+ }
+ else
+ {
+ return_value = *result;
+ }
+
+ return return_value;
+}
+
+//-----------------------------------------------------------------------------
+// removeAnimationData()
+//-----------------------------------------------------------------------------
+void LLCharacter::removeAnimationData(std::string name)
+{
+ mAnimationData.remove(name);
+}
+
+//-----------------------------------------------------------------------------
+// setVisualParamWeight()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::setVisualParamWeight(LLVisualParam* which_param, F32 weight, BOOL set_by_user)
+{
+ S32 index = which_param->getID();
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ if (index_iter != mVisualParamIndexMap.end())
+ {
+ index_iter->second->setWeight(weight, set_by_user);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// setVisualParamWeight()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::setVisualParamWeight(const char* param_name, F32 weight, BOOL set_by_user)
+{
+ LLString tname(param_name);
+ LLString::toLower(tname);
+ char *tableptr = sVisualParamNames.checkString(tname);
+ VisualParamNameMap_t::iterator name_iter = mVisualParamNameMap.find(tableptr);
+ if (name_iter != mVisualParamNameMap.end())
+ {
+ name_iter->second->setWeight(weight, set_by_user);
+ return TRUE;
+ }
+ llwarns << "LLCharacter::setVisualParamWeight() Invalid visual parameter: " << param_name << llendl;
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// setVisualParamWeight()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::setVisualParamWeight(S32 index, F32 weight, BOOL set_by_user)
+{
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ if (index_iter != mVisualParamIndexMap.end())
+ {
+ index_iter->second->setWeight(weight, set_by_user);
+ return TRUE;
+ }
+ llwarns << "LLCharacter::setVisualParamWeight() Invalid visual parameter index: " << index << llendl;
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// getVisualParamWeight()
+//-----------------------------------------------------------------------------
+F32 LLCharacter::getVisualParamWeight(LLVisualParam *which_param)
+{
+ S32 index = which_param->getID();
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ if (index_iter != mVisualParamIndexMap.end())
+ {
+ return index_iter->second->getWeight();
+ }
+ else
+ {
+ llwarns << "LLCharacter::getVisualParamWeight() Invalid visual parameter*, index= " << index << llendl;
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getVisualParamWeight()
+//-----------------------------------------------------------------------------
+F32 LLCharacter::getVisualParamWeight(const char* param_name)
+{
+ LLString tname(param_name);
+ LLString::toLower(tname);
+ char *tableptr = sVisualParamNames.checkString(tname);
+ VisualParamNameMap_t::iterator name_iter = mVisualParamNameMap.find(tableptr);
+ if (name_iter != mVisualParamNameMap.end())
+ {
+ return name_iter->second->getWeight();
+ }
+ llwarns << "LLCharacter::getVisualParamWeight() Invalid visual parameter: " << param_name << llendl;
+ return 0.f;
+}
+
+//-----------------------------------------------------------------------------
+// getVisualParamWeight()
+//-----------------------------------------------------------------------------
+F32 LLCharacter::getVisualParamWeight(S32 index)
+{
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ if (index_iter != mVisualParamIndexMap.end())
+ {
+ return index_iter->second->getWeight();
+ }
+ else
+ {
+ llwarns << "LLCharacter::getVisualParamWeight() Invalid visual parameter index: " << index << llendl;
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearVisualParamWeights()
+//-----------------------------------------------------------------------------
+void LLCharacter::clearVisualParamWeights()
+{
+ for (LLVisualParam *param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ param->setWeight( param->getDefaultWeight(), FALSE );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getVisualParam()
+//-----------------------------------------------------------------------------
+LLVisualParam* LLCharacter::getVisualParam(const char *param_name)
+{
+ LLString tname(param_name);
+ LLString::toLower(tname);
+ char *tableptr = sVisualParamNames.checkString(tname);
+ VisualParamNameMap_t::iterator name_iter = mVisualParamNameMap.find(tableptr);
+ if (name_iter != mVisualParamNameMap.end())
+ {
+ return name_iter->second;
+ }
+ llwarns << "LLCharacter::getVisualParam() Invalid visual parameter: " << param_name << llendl;
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// addSharedVisualParam()
+//-----------------------------------------------------------------------------
+void LLCharacter::addSharedVisualParam(LLVisualParam *param)
+{
+ S32 index = param->getID();
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ LLVisualParam* current_param = 0;
+ if (index_iter != mVisualParamIndexMap.end())
+ current_param = index_iter->second;
+ if( current_param )
+ {
+ LLVisualParam* next_param = current_param;
+ while(next_param->getNextParam())
+ {
+ next_param = next_param->getNextParam();
+ }
+ next_param->setNextParam(param);
+ }
+ else
+ {
+ llwarns << "Shared visual parameter " << param->getName() << " does not already exist with ID " <<
+ param->getID() << llendl;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// addVisualParam()
+//-----------------------------------------------------------------------------
+void LLCharacter::addVisualParam(LLVisualParam *param)
+{
+ S32 index = param->getID();
+ // Add Index map
+ std::pair<VisualParamIndexMap_t::iterator, bool> idxres;
+ idxres = mVisualParamIndexMap.insert(VisualParamIndexMap_t::value_type(index, param));
+ if (!idxres.second)
+ {
+ llwarns << "Visual parameter " << param->getName() << " already exists with same ID as " <<
+ param->getName() << llendl;
+ VisualParamIndexMap_t::iterator index_iter = idxres.first;
+ index_iter->second = param;
+ }
+
+ if (param->getInfo())
+ {
+ // Add name map
+ LLString tname(param->getName());
+ LLString::toLower(tname);
+ char *tableptr = sVisualParamNames.addString(tname);
+ std::pair<VisualParamNameMap_t::iterator, bool> nameres;
+ nameres = mVisualParamNameMap.insert(VisualParamNameMap_t::value_type(tableptr, param));
+ if (!nameres.second)
+ {
+ // Already exists, copy param
+ VisualParamNameMap_t::iterator name_iter = nameres.first;
+ name_iter->second = param;
+ }
+ }
+ //llinfos << "Adding Visual Param '" << param->getName() << "' ( " << index << " )" << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// updateVisualParams()
+//-----------------------------------------------------------------------------
+void LLCharacter::updateVisualParams()
+{
+ for (LLVisualParam *param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->isAnimating())
+ {
+ continue;
+ }
+ // only apply parameters whose effective weight has changed
+ F32 effective_weight = ( param->getSex() & mSex ) ? param->getWeight() : param->getDefaultWeight();
+ if (effective_weight != param->getLastWeight())
+ {
+ param->apply( mSex );
+ }
+ }
+}
+
+LLAnimPauseRequest LLCharacter::requestPause()
+{
+ mMotionController.pause();
+ return mPauseRequest;
+}
+
diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h
new file mode 100644
index 0000000000..f2f106f360
--- /dev/null
+++ b/indra/llcharacter/llcharacter.h
@@ -0,0 +1,252 @@
+/**
+ * @file llcharacter.h
+ * @brief Implementation of LLCharacter class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCHARACTER_H
+#define LL_LLCHARACTER_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include <string>
+
+#include "lljoint.h"
+#include "llmotioncontroller.h"
+#include "llassoclist.h"
+#include "llvisualparam.h"
+#include "linked_lists.h"
+#include "string_table.h"
+#include "llmemory.h"
+
+class LLPolyMesh;
+
+class LLPauseRequestHandle : public LLThreadSafeRefCount
+{
+public:
+ LLPauseRequestHandle() {};
+};
+
+typedef LLPointer<LLPauseRequestHandle> LLAnimPauseRequest;
+
+//-----------------------------------------------------------------------------
+// class LLCharacter
+//-----------------------------------------------------------------------------
+class LLCharacter
+{
+public:
+ // Constructor
+ LLCharacter();
+
+ // Destructor
+ virtual ~LLCharacter();
+
+ //-------------------------------------------------------------------------
+ // LLCharacter Interface
+ // These functions must be implemented by subclasses.
+ //-------------------------------------------------------------------------
+
+ // get the prefix to be used to lookup motion data files
+ // from the viewer data directory
+ virtual const char *getAnimationPrefix() = 0;
+
+ // get the root joint of the character
+ virtual LLJoint *getRootJoint() = 0;
+
+ // get the specified joint
+ // default implementation does recursive search,
+ // subclasses may optimize/cache results.
+ virtual LLJoint *getJoint( const std::string &name );
+
+ // get the position of the character
+ virtual LLVector3 getCharacterPosition() = 0;
+
+ // get the rotation of the character
+ virtual LLQuaternion getCharacterRotation() = 0;
+
+ // get the velocity of the character
+ virtual LLVector3 getCharacterVelocity() = 0;
+
+ // get the angular velocity of the character
+ virtual LLVector3 getCharacterAngularVelocity() = 0;
+
+ // get the height & normal of the ground under a point
+ virtual void getGround(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm) = 0;
+
+ // allocate an array of joints for the character skeleton
+ // this must be overloaded to support joint subclasses,
+ // and is called implicitly from buildSkeleton().
+ // Note this must handle reallocation as it will be called
+ // each time buildSkeleton() is called.
+ virtual BOOL allocateCharacterJoints( U32 num ) = 0;
+
+ // skeleton joint accessor to support joint subclasses
+ virtual LLJoint *getCharacterJoint( U32 i ) = 0;
+
+ // get the physics time dilation for the simulator
+ virtual F32 getTimeDilation() = 0;
+
+ // gets current pixel area of this character
+ virtual F32 getPixelArea() = 0;
+
+ // gets the head mesh of the character
+ virtual LLPolyMesh* getHeadMesh() = 0;
+
+ // gets the upper body mesh of the character
+ virtual LLPolyMesh* getUpperBodyMesh() = 0;
+
+ // gets global coordinates from agent local coordinates
+ virtual LLVector3d getPosGlobalFromAgent(const LLVector3 &position) = 0;
+
+ // gets agent local coordinates from global coordinates
+ virtual LLVector3 getPosAgentFromGlobal(const LLVector3d &position) = 0;
+
+ // updates all visual parameters for this character
+ virtual void updateVisualParams();
+
+ virtual void addDebugText( const char* text ) = 0;
+
+ virtual const LLUUID& getID() = 0;
+ //-------------------------------------------------------------------------
+ // End Interface
+ //-------------------------------------------------------------------------
+ // registers a motion with the character
+ // returns true if successfull
+ BOOL addMotion( const LLUUID& id, LLMotionConstructor create );
+
+ void removeMotion( const LLUUID& id );
+
+ // returns an instance of a registered motion
+ LLMotion* createMotion( const LLUUID &id );
+
+ // start a motion
+ // returns true if successful, false if an error occurred
+ virtual BOOL startMotion( const LLUUID& id, F32 start_offset = 0.f);
+
+ // stop a motion
+ virtual BOOL stopMotion( const LLUUID& id, BOOL stop_immediate = FALSE );
+
+ // is this motion active?
+ BOOL isMotionActive( const LLUUID& id );
+
+ // Event handler for motion deactivation.
+ // Called when a motion has completely stopped and has been deactivated.
+ // Subclasses may optionally override this.
+ // The default implementation does nothing.
+ virtual void requestStopMotion( LLMotion* motion );
+
+ // periodic update function, steps the motion controller
+ void updateMotion(BOOL force_update = FALSE);
+
+ LLAnimPauseRequest requestPause();
+ BOOL areAnimationsPaused() { return mMotionController.isPaused(); }
+ void setAnimTimeFactor(F32 factor) { mMotionController.setTimeFactor(factor); }
+ void setTimeStep(F32 time_step) { mMotionController.setTimeStep(time_step); }
+ // Releases all motion instances which should result in
+ // no cached references to character joint data. This is
+ // useful if a character wants to rebuild it's skeleton.
+ virtual void flushAllMotions();
+
+ // dumps information for debugging
+ virtual void dumpCharacter( LLJoint *joint = NULL );
+
+ virtual F32 getPreferredPelvisHeight() { return mPreferredPelvisHeight; }
+
+ virtual LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset) { return LLVector3::zero; }
+
+ virtual LLJoint* findCollisionVolume(U32 volume_id) { return NULL; }
+
+ virtual S32 getCollisionVolumeID(std::string &name) { return -1; }
+
+ void setAnimationData(std::string name, void *data);
+
+ void *getAnimationData(std::string name);
+
+ void removeAnimationData(std::string name);
+
+ void addVisualParam(LLVisualParam *param);
+ void addSharedVisualParam(LLVisualParam *param);
+
+ BOOL setVisualParamWeight(LLVisualParam *which_param, F32 weight, BOOL set_by_user = FALSE );
+ BOOL setVisualParamWeight(const char* param_name, F32 weight, BOOL set_by_user = FALSE );
+ BOOL setVisualParamWeight(S32 index, F32 weight, BOOL set_by_user = FALSE );
+
+ // get visual param weight by param or name
+ F32 getVisualParamWeight(LLVisualParam *distortion);
+ F32 getVisualParamWeight(const char* param_name);
+ F32 getVisualParamWeight(S32 index);
+
+ // set all morph weights to 0
+ void clearVisualParamWeights();
+
+ // visual parameter accessors
+ LLVisualParam* getFirstVisualParam()
+ {
+ mCurIterator = mVisualParamIndexMap.begin();
+ return getNextVisualParam();
+ }
+ LLVisualParam* getNextVisualParam()
+ {
+ if (mCurIterator == mVisualParamIndexMap.end())
+ return 0;
+ return (mCurIterator++)->second;
+ }
+
+ LLVisualParam* getVisualParam(S32 id)
+ {
+ VisualParamIndexMap_t::iterator iter = mVisualParamIndexMap.find(id);
+ return (iter == mVisualParamIndexMap.end()) ? 0 : iter->second;
+ }
+ S32 getVisualParamID(LLVisualParam *id)
+ {
+ VisualParamIndexMap_t::iterator iter;
+ for (iter = mVisualParamIndexMap.begin(); iter != mVisualParamIndexMap.end(); iter++)
+ {
+ if (iter->second == id)
+ return iter->first;
+ }
+ return 0;
+ }
+ S32 getVisualParamCount() { return (S32)mVisualParamIndexMap.size(); }
+ LLVisualParam* getVisualParam(const char *name);
+
+
+ ESex getSex() { return mSex; }
+ void setSex( ESex sex ) { mSex = sex; }
+
+ U32 getAppearanceSerialNum() const { return mAppearanceSerialNum; }
+ void setAppearanceSerialNum( U32 num ) { mAppearanceSerialNum = num; }
+
+ U32 getSkeletonSerialNum() const { return mSkeletonSerialNum; }
+ void setSkeletonSerialNum( U32 num ) { mSkeletonSerialNum = num; }
+
+ static LLLinkedList< LLCharacter > sInstances;
+
+protected:
+ LLMotionController mMotionController;
+
+ LLAssocList<std::string, void *> mAnimationData;
+
+ F32 mPreferredPelvisHeight;
+ ESex mSex;
+ U32 mAppearanceSerialNum;
+ U32 mSkeletonSerialNum;
+ LLAnimPauseRequest mPauseRequest;
+
+
+private:
+ // visual parameter stuff
+ typedef std::map<S32, LLVisualParam *> VisualParamIndexMap_t;
+ VisualParamIndexMap_t mVisualParamIndexMap;
+ VisualParamIndexMap_t::iterator mCurIterator;
+ typedef std::map<char *, LLVisualParam *> VisualParamNameMap_t;
+ VisualParamNameMap_t mVisualParamNameMap;
+
+ static LLStringTable sVisualParamNames;
+};
+
+#endif // LL_LLCHARACTER_H
+
diff --git a/indra/llcharacter/lleditingmotion.cpp b/indra/llcharacter/lleditingmotion.cpp
new file mode 100644
index 0000000000..f6cfc0ab80
--- /dev/null
+++ b/indra/llcharacter/lleditingmotion.cpp
@@ -0,0 +1,234 @@
+/**
+ * @file lleditingmotion.cpp
+ * @brief Implementation of LLEditingMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "lleditingmotion.h"
+#include "llcharacter.h"
+#include "llhandmotion.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+const LLQuaternion EDIT_MOTION_WRIST_ROTATION(F_PI_BY_TWO * 0.7f, LLVector3(1.0f, 0.0f, 0.0f));
+const F32 TARGET_LAG_HALF_LIFE = 0.1f; // half-life of IK targeting
+const F32 TORSO_LAG_HALF_LIFE = 0.2f;
+const F32 MAX_TIME_DELTA = 2.f; //max two seconds a frame for calculating interpolation
+
+S32 LLEditingMotion::sHandPose = LLHandMotion::HAND_POSE_RELAXED_R;
+S32 LLEditingMotion::sHandPosePriority = 3;
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLEditingMotion::LLEditingMotion( const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+
+ // create kinematic chain
+ mParentJoint.addChild( &mShoulderJoint );
+ mShoulderJoint.addChild( &mElbowJoint );
+ mElbowJoint.addChild( &mWristJoint );
+
+ mName = "editing";
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLEditingMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLEditingMotion::~LLEditingMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLEditingMotion::onInitialize(LLCharacter *character)
+{
+ // save character for future use
+ mCharacter = character;
+
+ // make sure character skeleton is copacetic
+ if (!mCharacter->getJoint("mShoulderLeft") ||
+ !mCharacter->getJoint("mElbowLeft") ||
+ !mCharacter->getJoint("mWristLeft"))
+ {
+ llwarns << "Invalid skeleton for editing motion!" << llendl;
+ return STATUS_FAILURE;
+ }
+
+ // get the shoulder, elbow, wrist joints from the character
+ mParentState.setJoint( mCharacter->getJoint("mShoulderLeft")->getParent() );
+ mShoulderState.setJoint( mCharacter->getJoint("mShoulderLeft") );
+ mElbowState.setJoint( mCharacter->getJoint("mElbowLeft") );
+ mWristState.setJoint( mCharacter->getJoint("mWristLeft") );
+ mTorsoState.setJoint( mCharacter->getJoint("mTorso"));
+
+ if ( ! mParentState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get parent joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mWristOffset = LLVector3(0.0f, 0.2f, 0.0f);
+
+ // add joint states to the pose
+ mShoulderState.setUsage(LLJointState::ROT);
+ mElbowState.setUsage(LLJointState::ROT);
+ mTorsoState.setUsage(LLJointState::ROT);
+ mWristState.setUsage(LLJointState::ROT);
+ addJointState( &mShoulderState );
+ addJointState( &mElbowState );
+ addJointState( &mTorsoState );
+ addJointState( &mWristState );
+
+ // propagate joint positions to kinematic chain
+ mParentJoint.setPosition( mParentState.getJoint()->getWorldPosition() );
+ mShoulderJoint.setPosition( mShoulderState.getJoint()->getPosition() );
+ mElbowJoint.setPosition( mElbowState.getJoint()->getPosition() );
+ mWristJoint.setPosition( mWristState.getJoint()->getPosition() + mWristOffset );
+
+ // propagate current joint rotations to kinematic chain
+ mParentJoint.setRotation( mParentState.getJoint()->getWorldRotation() );
+ mShoulderJoint.setRotation( mShoulderState.getJoint()->getRotation() );
+ mElbowJoint.setRotation( mElbowState.getJoint()->getRotation() );
+
+ // connect the ikSolver to the chain
+ mIKSolver.setPoleVector( LLVector3( -1.0f, 1.0f, 0.0f ) );
+ // specifying the elbow's axis will prevent bad IK for the more
+ // singular configurations, but the axis is limb-specific -- Leviathan
+ mIKSolver.setBAxis( LLVector3( -0.682683f, 0.0f, -0.730714f ) );
+ mIKSolver.setupJoints( &mShoulderJoint, &mElbowJoint, &mWristJoint, &mTarget );
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLEditingMotion::onActivate()
+{
+ // propagate joint positions to kinematic chain
+ mParentJoint.setPosition( mParentState.getJoint()->getWorldPosition() );
+ mShoulderJoint.setPosition( mShoulderState.getJoint()->getPosition() );
+ mElbowJoint.setPosition( mElbowState.getJoint()->getPosition() );
+ mWristJoint.setPosition( mWristState.getJoint()->getPosition() + mWristOffset );
+
+ // propagate current joint rotations to kinematic chain
+ mParentJoint.setRotation( mParentState.getJoint()->getWorldRotation() );
+ mShoulderJoint.setRotation( mShoulderState.getJoint()->getRotation() );
+ mElbowJoint.setRotation( mElbowState.getJoint()->getRotation() );
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ LLVector3 focus_pt;
+ LLVector3* pointAtPt = (LLVector3*)mCharacter->getAnimationData("PointAtPoint");
+
+
+ BOOL result = TRUE;
+
+ if (!pointAtPt)
+ {
+ focus_pt = mLastSelectPt;
+ result = FALSE;
+ }
+ else
+ {
+ focus_pt = *pointAtPt;
+ mLastSelectPt = focus_pt;
+ }
+
+ focus_pt += mCharacter->getCharacterPosition();
+
+ // propagate joint positions to kinematic chain
+ mParentJoint.setPosition( mParentState.getJoint()->getWorldPosition() );
+ mShoulderJoint.setPosition( mShoulderState.getJoint()->getPosition() );
+ mElbowJoint.setPosition( mElbowState.getJoint()->getPosition() );
+ mWristJoint.setPosition( mWristState.getJoint()->getPosition() + mWristOffset );
+
+ // propagate current joint rotations to kinematic chain
+ mParentJoint.setRotation( mParentState.getJoint()->getWorldRotation() );
+ mShoulderJoint.setRotation( mShoulderState.getJoint()->getRotation() );
+ mElbowJoint.setRotation( mElbowState.getJoint()->getRotation() );
+
+ // update target position from character
+ LLVector3 target = focus_pt - mParentJoint.getPosition();
+ F32 target_dist = target.normVec();
+
+ LLVector3 edit_plane_normal(1.f / F_SQRT2, 1.f / F_SQRT2, 0.f);
+ edit_plane_normal.normVec();
+
+ edit_plane_normal.rotVec(mTorsoState.getJoint()->getWorldRotation());
+
+ F32 dot = edit_plane_normal * target;
+
+ if (dot < 0.f)
+ {
+ target = target + (edit_plane_normal * (dot * 2.f));
+ target.mV[VZ] += clamp_rescale(dot, 0.f, -1.f, 0.f, 5.f);
+ target.normVec();
+ }
+
+ target = target * target_dist;
+ if (!target.isFinite())
+ {
+ llerrs << "Non finite target in editing motion with target distance of " << target_dist <<
+ " and focus point " << focus_pt << llendl;
+ }
+
+ mTarget.setPosition( target + mParentJoint.getPosition());
+
+// llinfos << "Point At: " << mTarget.getPosition() << llendl;
+
+ // update the ikSolver
+ if (!mTarget.getPosition().isExactlyZero())
+ {
+ LLQuaternion shoulderRot = mShoulderJoint.getRotation();
+ LLQuaternion elbowRot = mElbowJoint.getRotation();
+ mIKSolver.solve();
+
+ // use blending...
+ F32 slerp_amt = LLCriticalDamp::getInterpolant(TARGET_LAG_HALF_LIFE);
+ shoulderRot = slerp(slerp_amt, mShoulderJoint.getRotation(), shoulderRot);
+ elbowRot = slerp(slerp_amt, mElbowJoint.getRotation(), elbowRot);
+
+ // now put blended values back into joints
+ llassert(shoulderRot.isFinite());
+ llassert(elbowRot.isFinite());
+ mShoulderState.setRotation(shoulderRot);
+ mElbowState.setRotation(elbowRot);
+ mWristState.setRotation(LLQuaternion::DEFAULT);
+ }
+
+ mCharacter->setAnimationData("Hand Pose", &sHandPose);
+ mCharacter->setAnimationData("Hand Pose Priority", &sHandPosePriority);
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLEditingMotion::onDeactivate()
+{
+}
+
+
+// End
diff --git a/indra/llcharacter/lleditingmotion.h b/indra/llcharacter/lleditingmotion.h
new file mode 100644
index 0000000000..b817a765b2
--- /dev/null
+++ b/indra/llcharacter/lleditingmotion.h
@@ -0,0 +1,115 @@
+/**
+ * @file lleditingmotion.h
+ * @brief Implementation of LLEditingMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEDITINGMOTION_H
+#define LL_LLEDITINGMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+#include "lljointsolverrp3.h"
+#include "v3dmath.h"
+
+#define EDITING_EASEIN_DURATION 0.0f
+#define EDITING_EASEOUT_DURATION 0.5f
+#define EDITING_PRIORITY LLJoint::HIGH_PRIORITY
+#define MIN_REQUIRED_PIXEL_AREA_EDITING 500.f
+
+//-----------------------------------------------------------------------------
+// class LLEditingMotion
+//-----------------------------------------------------------------------------
+class LLEditingMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLEditingMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLEditingMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLEditingMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return EDITING_EASEIN_DURATION; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return EDITING_EASEOUT_DURATION; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return EDITING_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_EDITING; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+ LLVector3 mWristOffset;
+
+ LLJointState mParentState;
+ LLJointState mShoulderState;
+ LLJointState mElbowState;
+ LLJointState mWristState;
+ LLJointState mTorsoState;
+
+ LLJoint mParentJoint;
+ LLJoint mShoulderJoint;
+ LLJoint mElbowJoint;
+ LLJoint mWristJoint;
+ LLJoint mTarget;
+ LLJointSolverRP3 mIKSolver;
+
+ static S32 sHandPose;
+ static S32 sHandPosePriority;
+ LLVector3 mLastSelectPt;
+};
+
+#endif // LL_LLKEYFRAMEMOTION_H
+
diff --git a/indra/llcharacter/llgesture.cpp b/indra/llcharacter/llgesture.cpp
new file mode 100644
index 0000000000..028c2f7fdf
--- /dev/null
+++ b/indra/llcharacter/llgesture.cpp
@@ -0,0 +1,356 @@
+/**
+ * @file llgesture.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "indra_constants.h"
+
+#include "llgesture.h"
+#include "llendianswizzle.h"
+#include "message.h"
+#include <boost/tokenizer.hpp>
+
+// for allocating serialization buffers - these need to be updated when members change
+const S32 LLGestureList::SERIAL_HEADER_SIZE = sizeof(S32);
+const S32 LLGesture::MAX_SERIAL_SIZE = sizeof(KEY) + sizeof(MASK) + 16 + 26 + 41 + 41;
+
+LLGesture::LLGesture()
+: mKey(KEY_NONE),
+ mMask(MASK_NONE),
+ mTrigger(),
+ mTriggerLower(),
+ mSoundItemID(),
+ mAnimation(),
+ mOutputString()
+{ }
+
+LLGesture::LLGesture(KEY key, MASK mask, const std::string &trigger,
+ const LLUUID &sound_item_id,
+ const std::string &animation,
+ const std::string &output_string)
+:
+ mKey(key),
+ mMask(mask),
+ mTrigger(trigger),
+ mTriggerLower(trigger),
+ mSoundItemID(sound_item_id),
+ mAnimation(animation),
+ mOutputString(output_string)
+{
+ mTriggerLower = utf8str_tolower(mTriggerLower);
+}
+
+LLGesture::LLGesture(U8 **buffer, S32 max_size)
+{
+ *buffer = deserialize(*buffer, max_size);
+}
+
+LLGesture::LLGesture(const LLGesture &rhs)
+{
+ mKey = rhs.mKey;
+ mMask = rhs.mMask;
+ mTrigger = rhs.mTrigger;
+ mTriggerLower = rhs.mTriggerLower;
+ mSoundItemID = rhs.mSoundItemID;
+ mAnimation = rhs.mAnimation;
+ mOutputString = rhs.mOutputString;
+}
+
+const LLGesture &LLGesture::operator =(const LLGesture &rhs)
+{
+ mKey = rhs.mKey;
+ mMask = rhs.mMask;
+ mTrigger = rhs.mTrigger;
+ mTriggerLower = rhs.mTriggerLower;
+ mSoundItemID = rhs.mSoundItemID;
+ mAnimation = rhs.mAnimation;
+ mOutputString = rhs.mOutputString;
+ return (*this);
+}
+
+
+BOOL LLGesture::trigger(KEY key, MASK mask)
+{
+ llwarns << "Parent class trigger called: you probably didn't mean this." << llendl;
+ return FALSE;
+}
+
+
+BOOL LLGesture::trigger(const LLString &trigger_string)
+{
+ llwarns << "Parent class trigger called: you probably didn't mean this." << llendl;
+ return FALSE;
+}
+
+// NOT endian-neutral
+U8 *LLGesture::serialize(U8 *buffer) const
+{
+ htonmemcpy(buffer, &mKey, MVT_S8, 1);
+ buffer += sizeof(mKey);
+ htonmemcpy(buffer, &mMask, MVT_U32, 4);
+ buffer += sizeof(mMask);
+ htonmemcpy(buffer, mSoundItemID.mData, MVT_LLUUID, 16);
+ buffer += 16;
+
+ memcpy(buffer, mTrigger.c_str(), mTrigger.length() + 1); /* Flawfinder: ignore */
+ buffer += mTrigger.length() + 1;
+ memcpy(buffer, mAnimation.c_str(), mAnimation.length() + 1); /* Flawfinder: ignore */
+ buffer += mAnimation.length() + 1;
+ memcpy(buffer, mOutputString.c_str(), mOutputString.length() + 1); /* Flawfinder: ignore */
+ buffer += mOutputString.length() + 1;
+
+ return buffer;
+}
+
+U8 *LLGesture::deserialize(U8 *buffer, S32 max_size)
+{
+ U8 *tmp = buffer;
+
+ if (tmp + sizeof(mKey) + sizeof(mMask) + 16 > buffer + max_size)
+ {
+ llwarns << "Attempt to read past end of buffer, bad data!!!!" << llendl;
+ return buffer;
+ }
+
+ htonmemcpy(&mKey, tmp, MVT_S8, 1);
+ tmp += sizeof(mKey);
+ htonmemcpy(&mMask, tmp, MVT_U32, 4);
+ tmp += sizeof(mMask);
+ htonmemcpy(mSoundItemID.mData, tmp, MVT_LLUUID, 16);
+ tmp += 16;
+
+ mTrigger.assign((char *)tmp);
+ mTriggerLower = mTrigger;
+ mTriggerLower = utf8str_tolower(mTriggerLower);
+ tmp += mTrigger.length() + 1;
+ mAnimation.assign((char *)tmp);
+ //RN: force animation names to lower case
+ // must do this for backwards compatibility
+ mAnimation = utf8str_tolower(mAnimation);
+ tmp += mAnimation.length() + 1;
+ mOutputString.assign((char *)tmp);
+ tmp += mOutputString.length() + 1;
+
+ if (tmp > buffer + max_size)
+ {
+ llwarns << "Read past end of buffer, bad data!!!!" << llendl;
+ return tmp;
+ }
+
+ return tmp;
+}
+
+S32 LLGesture::getMaxSerialSize()
+{
+ return MAX_SERIAL_SIZE;
+}
+
+//---------------------------------------------------------------------
+// LLGestureList
+//---------------------------------------------------------------------
+
+LLGestureList::LLGestureList()
+: mList(0)
+{
+ // add some gestures for debugging
+// LLGesture *gesture = NULL;
+/*
+ gesture = new LLGesture(KEY_F2, MASK_NONE, ":-)",
+ SND_CHIRP, "dance2", ":-)" );
+ mList.put(gesture);
+
+ gesture = new LLGesture(KEY_F3, MASK_NONE, "/dance",
+ SND_OBJECT_CREATE, "dance3", "(dances)" );
+ mList.put(gesture);
+
+ gesture = new LLGesture(KEY_F4, MASK_NONE, "/boogie",
+ LLUUID::null, "dance4", LLString::null );
+ mList.put(gesture);
+
+ gesture = new LLGesture(KEY_F5, MASK_SHIFT, "/tongue",
+ LLUUID::null, "Express_Tongue_Out", LLString::null );
+ mList.put(gesture);
+ */
+}
+
+LLGestureList::~LLGestureList()
+{
+ deleteAll();
+}
+
+
+void LLGestureList::deleteAll()
+{
+ S32 count = mList.count();
+ for (S32 i = 0; i < count; i++)
+ {
+ delete mList.get(i);
+ }
+ mList.reset();
+}
+
+// Iterates through space delimited tokens in string, triggering any gestures found.
+// Generates a revised string that has the found tokens replaced by their replacement strings
+// and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
+BOOL LLGestureList::triggerAndReviseString(const LLString &string, LLString* revised_string)
+{
+ LLString tokenized = string;
+
+ BOOL found_gestures = FALSE;
+ BOOL first_token = TRUE;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(string, sep);
+ tokenizer::iterator token_iter;
+
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ LLGesture* gesture = NULL;
+
+ if( !found_gestures ) // Only pay attention to the first gesture in the string.
+ {
+ LLString cur_token_lower = *token_iter;
+ LLString::toLower(cur_token_lower);
+
+ for (S32 i = 0; i < mList.count(); i++)
+ {
+ gesture = mList.get(i);
+ if (gesture->trigger(cur_token_lower))
+ {
+ if( !gesture->getOutputString().empty() )
+ {
+ if( !first_token )
+ {
+ revised_string->append( " " );
+ }
+
+ // Don't muck with the user's capitalization if we don't have to.
+ const std::string& output = gesture->getOutputString();
+ LLString output_lower = LLString(output.c_str());
+ LLString::toLower(output_lower);
+ if( cur_token_lower == output_lower )
+ {
+ revised_string->append(*token_iter);
+ }
+ else
+ {
+ revised_string->append(output.c_str());
+ }
+
+ }
+ found_gestures = TRUE;
+ break;
+ }
+ gesture = NULL;
+ }
+ }
+
+ if( !gesture )
+ {
+ if( !first_token )
+ {
+ revised_string->append( " " );
+ }
+ revised_string->append( *token_iter );
+ }
+
+ first_token = FALSE;
+ }
+ return found_gestures;
+}
+
+
+
+BOOL LLGestureList::trigger(KEY key, MASK mask)
+{
+ for (S32 i = 0; i < mList.count(); i++)
+ {
+ LLGesture* gesture = mList.get(i);
+ if( gesture )
+ {
+ if (gesture->trigger(key, mask))
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ llwarns << "NULL gesture in gesture list (" << i << ")" << llendl
+ }
+ }
+ return FALSE;
+}
+
+// NOT endian-neutral
+U8 *LLGestureList::serialize(U8 *buffer) const
+{
+ // a single S32 serves as the header that tells us how many to read
+ S32 count = mList.count();
+ htonmemcpy(buffer, &count, MVT_S32, 4);
+ buffer += sizeof(count);
+
+ for (S32 i = 0; i < count; i++)
+ {
+ buffer = mList[i]->serialize(buffer);
+ }
+
+ return buffer;
+}
+
+const S32 MAX_GESTURES = 4096;
+
+U8 *LLGestureList::deserialize(U8 *buffer, S32 max_size)
+{
+ deleteAll();
+
+ S32 count;
+ U8 *tmp = buffer;
+
+ if (tmp + sizeof(count) > buffer + max_size)
+ {
+ llwarns << "Invalid max_size" << llendl;
+ return buffer;
+ }
+
+ htonmemcpy(&count, tmp, MVT_S32, 4);
+
+ if (count > MAX_GESTURES)
+ {
+ llwarns << "Unreasonably large gesture list count in deserialize: " << count << llendl;
+ return tmp;
+ }
+
+ tmp += sizeof(count);
+
+ mList.reserve_block(count);
+
+ for (S32 i = 0; i < count; i++)
+ {
+ mList[i] = create_gesture(&tmp, max_size - (S32)(tmp - buffer));
+ if (tmp - buffer > max_size)
+ {
+ llwarns << "Deserialization read past end of buffer, bad data!!!!" << llendl;
+ return tmp;
+ }
+ }
+
+ return tmp;
+}
+
+// this is a helper for deserialize
+// it gets overridden by LLViewerGestureList to create LLViewerGestures
+// overridden by child class to use local LLGesture implementation
+LLGesture *LLGestureList::create_gesture(U8 **buffer, S32 max_size)
+{
+ return new LLGesture(buffer, max_size);
+}
+
+S32 LLGestureList::getMaxSerialSize()
+{
+ return SERIAL_HEADER_SIZE + (count() * LLGesture::getMaxSerialSize());
+}
diff --git a/indra/llcharacter/llgesture.h b/indra/llcharacter/llgesture.h
new file mode 100644
index 0000000000..64c752769b
--- /dev/null
+++ b/indra/llcharacter/llgesture.h
@@ -0,0 +1,96 @@
+/**
+ * @file llgesture.h
+ * @brief A gesture is a combination of a triggering chat phrase or
+ * key, a sound, an animation, and a chat string.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLGESTURE_H
+#define LL_LLGESTURE_H
+
+#include "llanimationstates.h"
+#include "lluuid.h"
+#include "llstring.h"
+#include "lldarray.h"
+
+class LLGesture
+{
+public:
+ LLGesture();
+ LLGesture(KEY key, MASK mask, const std::string &trigger,
+ const LLUUID &sound_item_id, const std::string &animation,
+ const std::string &output_string);
+
+ LLGesture(U8 **buffer, S32 max_size); // deserializes, advances buffer
+ LLGesture(const LLGesture &gesture);
+ const LLGesture &operator=(const LLGesture &rhs);
+
+ virtual ~LLGesture() {};
+
+ // Accessors
+ KEY getKey() const { return mKey; }
+ MASK getMask() const { return mMask; }
+ const std::string& getTrigger() const { return mTrigger; }
+ const LLUUID& getSound() const { return mSoundItemID; }
+ const std::string& getAnimation() const { return mAnimation; }
+ const std::string& getOutputString() const { return mOutputString; }
+
+ // Triggers if a key/mask matches it
+ virtual BOOL trigger(KEY key, MASK mask);
+
+ // Triggers if case-insensitive substring matches (assumes string is lowercase)
+ virtual BOOL trigger(const LLString &string);
+
+ // non-endian-neutral serialization
+ U8 *serialize(U8 *buffer) const;
+ U8 *deserialize(U8 *buffer, S32 max_size);
+ static S32 getMaxSerialSize();
+
+protected:
+ KEY mKey; // usually a function key
+ MASK mMask; // usually MASK_NONE, or MASK_SHIFT
+ std::string mTrigger; // string, no whitespace allowed
+ std::string mTriggerLower; // lowercase version of mTrigger
+ LLUUID mSoundItemID; // ItemID of sound to play, LLUUID::null if none
+ std::string mAnimation; // canonical name of animation or face animation
+ std::string mOutputString; // string to say
+
+ static const S32 MAX_SERIAL_SIZE;
+};
+
+class LLGestureList
+{
+public:
+ LLGestureList();
+ virtual ~LLGestureList();
+
+ // Triggers if a key/mask matches one in the list
+ BOOL trigger(KEY key, MASK mask);
+
+ // Triggers if substring matches and generates revised string.
+ BOOL triggerAndReviseString(const LLString &string, LLString* revised_string);
+
+ // Used for construction from UI
+ S32 count() const { return mList.count(); }
+ virtual LLGesture* get(S32 i) const { return mList.get(i); }
+ virtual void put(LLGesture* gesture) { mList.put( gesture ); }
+ void deleteAll();
+
+ // non-endian-neutral serialization
+ U8 *serialize(U8 *buffer) const;
+ U8 *deserialize(U8 *buffer, S32 max_size);
+ S32 getMaxSerialSize();
+
+protected:
+ // overridden by child class to use local LLGesture implementation
+ virtual LLGesture *create_gesture(U8 **buffer, S32 max_size);
+
+protected:
+ LLDynamicArray<LLGesture*> mList;
+
+ static const S32 SERIAL_HEADER_SIZE;
+};
+
+#endif
diff --git a/indra/llcharacter/llhandmotion.cpp b/indra/llcharacter/llhandmotion.cpp
new file mode 100644
index 0000000000..d574363f83
--- /dev/null
+++ b/indra/llcharacter/llhandmotion.cpp
@@ -0,0 +1,202 @@
+/**
+ * @file llhandmotion.cpp
+ * @brief Implementation of LLHandMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llhandmotion.h"
+#include "llcharacter.h"
+#include "m3math.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+
+const char *gHandPoseNames[LLHandMotion::NUM_HAND_POSES] = /* Flawfinder: ignore */
+{
+ "",
+ "Hands_Relaxed",
+ "Hands_Point",
+ "Hands_Fist",
+ "Hands_Relaxed_L",
+ "Hands_Point_L",
+ "Hands_Fist_L",
+ "Hands_Relaxed_R",
+ "Hands_Point_R",
+ "Hands_Fist_R",
+ "Hands_Salute_R",
+ "Hands_Typing",
+ "Hands_Peace_R",
+ "Hands_Spread_R"
+};
+
+const F32 HAND_MORPH_BLEND_TIME = 0.2f;
+
+//-----------------------------------------------------------------------------
+// LLHandMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLHandMotion::LLHandMotion(const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+ mLastTime = 0.f;
+ mCurrentPose = HAND_POSE_RELAXED;
+ mNewPose = HAND_POSE_RELAXED;
+ mName = "hand_motion";
+
+ //RN: flag hand joint as highest priority for now, until we implement a proper animation track
+ mJointSignature[0][LL_HAND_JOINT_NUM] = 0xff;
+ mJointSignature[1][LL_HAND_JOINT_NUM] = 0xff;
+ mJointSignature[2][LL_HAND_JOINT_NUM] = 0xff;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLHandMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLHandMotion::~LLHandMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLHandMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ return STATUS_SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLHandMotion::onActivate()
+{
+ LLPolyMesh *upperBodyMesh = mCharacter->getUpperBodyMesh();
+
+ if (upperBodyMesh)
+ {
+ // Note: 0 is the default
+ for (S32 i = 1; i < LLHandMotion::NUM_HAND_POSES; i++)
+ {
+ mCharacter->setVisualParamWeight(gHandPoseNames[i], 0.f);
+ }
+ mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], 1.f);
+ mCharacter->updateVisualParams();
+ }
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLHandMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ eHandPose *requestedHandPose;
+
+ F32 timeDelta = time - mLastTime;
+ mLastTime = time;
+
+ requestedHandPose = (eHandPose *)mCharacter->getAnimationData("Hand Pose");
+ // check to see if requested pose has changed
+ if (!requestedHandPose)
+ {
+ if (mNewPose != HAND_POSE_RELAXED && mNewPose != mCurrentPose)
+ {
+ mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f);
+ }
+ mNewPose = HAND_POSE_RELAXED;
+ }
+ else
+ {
+ // this is a new morph we didn't know about before
+ if (*requestedHandPose != mNewPose && mNewPose != mCurrentPose && mNewPose != HAND_POSE_SPREAD)
+ {
+ mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f);
+ }
+ mNewPose = *requestedHandPose;
+ }
+
+ mCharacter->removeAnimationData("Hand Pose");
+ mCharacter->removeAnimationData("Hand Pose Priority");
+
+// if (requestedHandPose)
+// llinfos << "Hand Pose " << *requestedHandPose << llendl;
+
+ // if we are still blending...
+ if (mCurrentPose != mNewPose)
+ {
+ F32 incomingWeight = 1.f;
+ F32 outgoingWeight = 0.f;
+
+ if (mNewPose != HAND_POSE_SPREAD)
+ {
+ incomingWeight = mCharacter->getVisualParamWeight(gHandPoseNames[mNewPose]);
+ incomingWeight += (timeDelta / HAND_MORPH_BLEND_TIME);
+ incomingWeight = llclamp(incomingWeight, 0.f, 1.f);
+ mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], incomingWeight);
+ }
+
+ if (mCurrentPose != HAND_POSE_SPREAD)
+ {
+ outgoingWeight = mCharacter->getVisualParamWeight(gHandPoseNames[mCurrentPose]);
+ outgoingWeight -= (timeDelta / HAND_MORPH_BLEND_TIME);
+ outgoingWeight = llclamp(outgoingWeight, 0.f, 1.f);
+ mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], outgoingWeight);
+ }
+
+ mCharacter->updateVisualParams();
+
+ if (incomingWeight == 1.f && outgoingWeight == 0.f)
+ {
+ mCurrentPose = mNewPose;
+ }
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLHandMotion::onDeactivate()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::getHandPoseName()
+//-----------------------------------------------------------------------------
+LLString LLHandMotion::getHandPoseName(eHandPose pose)
+{
+ if ((S32)pose < LLHandMotion::NUM_HAND_POSES && (S32)pose >= 0)
+ {
+ return gHandPoseNames[pose];
+ }
+ return "";
+}
+
+LLHandMotion::eHandPose LLHandMotion::getHandPose(LLString posename)
+{
+ for (S32 pose = 0; pose < LLHandMotion::NUM_HAND_POSES; ++pose)
+ {
+ if (gHandPoseNames[pose] == posename)
+ {
+ return (eHandPose)pose;
+ }
+ }
+ return (eHandPose)0;
+}
+
+// End
diff --git a/indra/llcharacter/llhandmotion.h b/indra/llcharacter/llhandmotion.h
new file mode 100644
index 0000000000..cef7361633
--- /dev/null
+++ b/indra/llcharacter/llhandmotion.h
@@ -0,0 +1,119 @@
+/**
+ * @file llhandmotion.h
+ * @brief Implementation of LLHandMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHANDMOTION_H
+#define LL_LLHANDMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+#include "lltimer.h"
+
+#define MIN_REQUIRED_PIXEL_AREA_HAND 10000.f;
+
+//-----------------------------------------------------------------------------
+// class LLHandMotion
+//-----------------------------------------------------------------------------
+class LLHandMotion :
+ public LLMotion
+{
+public:
+ typedef enum e_hand_pose
+ {
+ HAND_POSE_SPREAD,
+ HAND_POSE_RELAXED,
+ HAND_POSE_POINT,
+ HAND_POSE_FIST,
+ HAND_POSE_RELAXED_L,
+ HAND_POSE_POINT_L,
+ HAND_POSE_FIST_L,
+ HAND_POSE_RELAXED_R,
+ HAND_POSE_POINT_R,
+ HAND_POSE_FIST_R,
+ HAND_POSE_SALUTE_R,
+ HAND_POSE_TYPING,
+ HAND_POSE_PEACE_R,
+ HAND_POSE_PALM_R,
+ NUM_HAND_POSES
+ } eHandPose;
+
+ // Constructor
+ LLHandMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLHandMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLHandMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.0; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.0; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_HAND; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+ static LLString getHandPoseName(eHandPose pose);
+ static eHandPose getHandPose(LLString posename);
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+
+ LLCharacter *mCharacter;
+
+ F32 mLastTime;
+ eHandPose mCurrentPose;
+ eHandPose mNewPose;
+};
+#endif // LL_LLHANDMOTION_H
+
diff --git a/indra/llcharacter/llheadrotmotion.cpp b/indra/llcharacter/llheadrotmotion.cpp
new file mode 100644
index 0000000000..4ff7e6b582
--- /dev/null
+++ b/indra/llcharacter/llheadrotmotion.cpp
@@ -0,0 +1,505 @@
+/**
+ * @file llheadrotmotion.cpp
+ * @brief Implementation of LLHeadRotMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llheadrotmotion.h"
+#include "llcharacter.h"
+#include "llrand.h"
+#include "m3math.h"
+#include "v3dmath.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+const F32 TORSO_LAG = 0.35f; // torso rotation factor
+const F32 NECK_LAG = 0.5f; // neck rotation factor
+const F32 HEAD_LOOKAT_LAG_HALF_LIFE = 0.15f; // half-life of lookat targeting for head
+const F32 TORSO_LOOKAT_LAG_HALF_LIFE = 0.27f; // half-life of lookat targeting for torso
+const F32 EYE_LOOKAT_LAG_HALF_LIFE = 0.06f; // half-life of lookat targeting for eye
+const F32 HEAD_ROTATION_CONSTRAINT = F_PI_BY_TWO * 0.8f; // limit angle for head rotation
+
+const F32 MIN_HEAD_LOOKAT_DISTANCE = 0.3f; // minimum distance from head before we turn to look at it
+const F32 MAX_TIME_DELTA = 2.f; //max two seconds a frame for calculating interpolation
+const F32 EYE_JITTER_MIN_TIME = 0.3f; // min amount of time between eye "jitter" motions
+const F32 EYE_JITTER_MAX_TIME = 2.5f; // max amount of time between eye "jitter" motions
+const F32 EYE_JITTER_MAX_YAW = 0.08f; // max yaw of eye jitter motion
+const F32 EYE_JITTER_MAX_PITCH = 0.015f; // max pitch of eye jitter motion
+const F32 EYE_LOOK_AWAY_MIN_TIME = 5.f; // min amount of time between eye "look away" motions
+const F32 EYE_LOOK_AWAY_MAX_TIME = 15.f; // max amount of time between eye "look away" motions
+const F32 EYE_LOOK_BACK_MIN_TIME = 1.f; // min amount of time before looking back after looking away
+const F32 EYE_LOOK_BACK_MAX_TIME = 5.f; // max amount of time before looking back after looking away
+const F32 EYE_LOOK_AWAY_MAX_YAW = 0.15f; // max yaw of eye look away motion
+const F32 EYE_LOOK_AWAY_MAX_PITCH = 0.12f; // max pitch of look away motion
+const F32 EYE_ROT_LIMIT_ANGLE = F_PI_BY_TWO * 0.3f; //max angle in radians for eye rotation
+
+const F32 EYE_BLINK_MIN_TIME = 0.5f; // minimum amount of time between blinks
+const F32 EYE_BLINK_MAX_TIME = 8.f; // maximum amount of time between blinks
+const F32 EYE_BLINK_CLOSE_TIME = 0.03f; // how long the eye stays closed in a blink
+const F32 EYE_BLINK_SPEED = 0.015f; // seconds it takes for a eye open/close movement
+const F32 EYE_BLINK_TIME_DELTA = 0.005f; // time between one eye starting a blink and the other following
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLHeadRotMotion::LLHeadRotMotion(const LLUUID &id) :
+ LLMotion(id),
+ mCharacter(NULL),
+ mTorsoJoint(NULL),
+ mHeadJoint(NULL)
+{
+ mName = "head_rot";
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLHeadRotMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLHeadRotMotion::~LLHeadRotMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLHeadRotMotion::onInitialize(LLCharacter *character)
+{
+ if (!character)
+ return STATUS_FAILURE;
+ mCharacter = character;
+
+ mPelvisJoint = character->getJoint("mPelvis");
+ if ( ! mPelvisJoint )
+ {
+ llinfos << getName() << ": Can't get pelvis joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mRootJoint = character->getJoint("mRoot");
+ if ( ! mRootJoint )
+ {
+ llinfos << getName() << ": Can't get root joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mTorsoJoint = character->getJoint("mTorso");
+ if ( ! mTorsoJoint )
+ {
+ llinfos << getName() << ": Can't get torso joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mHeadJoint = character->getJoint("mHead");
+ if ( ! mHeadJoint )
+ {
+ llinfos << getName() << ": Can't get head joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mTorsoState.setJoint( character->getJoint("mTorso") );
+ if ( ! mTorsoState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get torso joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mNeckState.setJoint( character->getJoint("mNeck") );
+ if ( ! mNeckState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get neck joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mHeadState.setJoint( character->getJoint("mHead") );
+ if ( ! mHeadState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get head joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mTorsoState.setUsage(LLJointState::ROT);
+ mNeckState.setUsage(LLJointState::ROT);
+ mHeadState.setUsage(LLJointState::ROT);
+
+ addJointState( &mTorsoState );
+ addJointState( &mNeckState );
+ addJointState( &mHeadState );
+
+ mLastHeadRot.loadIdentity();
+
+ return STATUS_SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLHeadRotMotion::onActivate()
+{
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLHeadRotMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ LLQuaternion targetHeadRotWorld;
+ LLQuaternion currentRootRotWorld = mRootJoint->getWorldRotation();
+ LLQuaternion currentInvRootRotWorld = ~currentRootRotWorld;
+
+ F32 head_slerp_amt = LLCriticalDamp::getInterpolant(HEAD_LOOKAT_LAG_HALF_LIFE);
+ F32 torso_slerp_amt = LLCriticalDamp::getInterpolant(TORSO_LOOKAT_LAG_HALF_LIFE);
+
+ LLVector3* targetPos = (LLVector3*)mCharacter->getAnimationData("LookAtPoint");
+
+ if (targetPos)
+ {
+ LLVector3 headLookAt = *targetPos;
+
+// llinfos << "Look At: " << headLookAt + mHeadJoint->getWorldPosition() << llendl;
+
+ F32 lookatDistance = headLookAt.normVec();
+
+ if (lookatDistance < MIN_HEAD_LOOKAT_DISTANCE)
+ {
+ targetHeadRotWorld = mPelvisJoint->getWorldRotation();
+ }
+ else
+ {
+ LLVector3 root_up = LLVector3(0.f, 0.f, 1.f) * currentRootRotWorld;
+ LLVector3 left(root_up % headLookAt);
+ // if look_at has zero length, fail
+ // if look_at and skyward are parallel, fail
+ //
+ // Test both of these conditions with a cross product.
+
+ if (left.magVecSquared() < 0.15f)
+ {
+ LLVector3 root_at = LLVector3(1.f, 0.f, 0.f) * currentRootRotWorld;
+ root_at.mV[VZ] = 0.f;
+ root_at.normVec();
+
+ headLookAt = lerp(headLookAt, root_at, 0.4f);
+ headLookAt.normVec();
+
+ left = root_up % headLookAt;
+ }
+
+ // Make sure look_at and skyward and not parallel
+ // and neither are zero length
+ LLVector3 up(headLookAt % left);
+
+ targetHeadRotWorld = LLQuaternion(headLookAt, left, up);
+ }
+ }
+ else
+ {
+ targetHeadRotWorld = currentRootRotWorld;
+ }
+
+ LLQuaternion head_rot_local = targetHeadRotWorld * currentInvRootRotWorld;
+ head_rot_local.constrain(HEAD_ROTATION_CONSTRAINT);
+
+ // set final torso rotation
+ // Set torso target rotation such that it lags behind the head rotation
+ // by a fixed amount.
+ LLQuaternion torso_rot_local = nlerp(TORSO_LAG, LLQuaternion::DEFAULT, head_rot_local );
+ mTorsoState.setRotation( nlerp(torso_slerp_amt, mTorsoState.getRotation(), torso_rot_local) );
+
+ head_rot_local = nlerp(head_slerp_amt, mLastHeadRot, head_rot_local);
+ mLastHeadRot = head_rot_local;
+
+ // Set the head rotation.
+ LLQuaternion torsoRotLocal = mNeckState.getJoint()->getParent()->getWorldRotation() * currentInvRootRotWorld;
+ head_rot_local = head_rot_local * ~torsoRotLocal;
+ mNeckState.setRotation( nlerp(NECK_LAG, LLQuaternion::DEFAULT, head_rot_local) );
+ mHeadState.setRotation( nlerp(1.f - NECK_LAG, LLQuaternion::DEFAULT, head_rot_local));
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLHeadRotMotion::onDeactivate()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLEyeMotion::LLEyeMotion(const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+ mEyeJitterTime = 0.f;
+ mEyeJitterYaw = 0.f;
+ mEyeJitterPitch = 0.f;
+
+ mEyeLookAwayTime = 0.f;
+ mEyeLookAwayYaw = 0.f;
+ mEyeLookAwayPitch = 0.f;
+
+ mEyeBlinkTime = 0.f;
+ mEyesClosed = FALSE;
+
+ mHeadJoint = NULL;
+
+ mName = "eye_rot";
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLEyeMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLEyeMotion::~LLEyeMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLEyeMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ mHeadJoint = character->getJoint("mHead");
+ if ( ! mHeadJoint )
+ {
+ llinfos << getName() << ": Can't get head joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mLeftEyeState.setJoint( character->getJoint("mEyeLeft") );
+ if ( ! mLeftEyeState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get left eyeball joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mRightEyeState.setJoint( character->getJoint("mEyeRight") );
+ if ( ! mRightEyeState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get Right eyeball joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mLeftEyeState.setUsage(LLJointState::ROT);
+ mRightEyeState.setUsage(LLJointState::ROT);
+
+ addJointState( &mLeftEyeState );
+ addJointState( &mRightEyeState );
+
+ return STATUS_SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLEyeMotion::onActivate()
+{
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLEyeMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ // Compute eye rotation.
+ LLQuaternion target_eye_rot;
+ LLVector3 eye_look_at;
+ F32 vergence;
+
+ //calculate jitter
+ if (mEyeJitterTimer.getElapsedTimeF32() > mEyeJitterTime)
+ {
+ mEyeJitterTime = EYE_JITTER_MIN_TIME + frand(EYE_JITTER_MAX_TIME - EYE_JITTER_MIN_TIME);
+ mEyeJitterYaw = (frand(2.f) - 1.f) * EYE_JITTER_MAX_YAW;
+ mEyeJitterPitch = (frand(2.f) - 1.f) * EYE_JITTER_MAX_PITCH;
+ // make sure lookaway time count gets updated, because we're resetting the timer
+ mEyeLookAwayTime -= llmax(0.f, mEyeJitterTimer.getElapsedTimeF32());
+ mEyeJitterTimer.reset();
+ }
+ else if (mEyeJitterTimer.getElapsedTimeF32() > mEyeLookAwayTime)
+ {
+ if (frand(1.f) > 0.1f)
+ {
+ // blink while moving eyes some percentage of the time
+ mEyeBlinkTime = mEyeBlinkTimer.getElapsedTimeF32();
+ }
+ if (mEyeLookAwayYaw == 0.f && mEyeLookAwayPitch == 0.f)
+ {
+ mEyeLookAwayYaw = (frand(2.f) - 1.f) * EYE_LOOK_AWAY_MAX_YAW;
+ mEyeLookAwayPitch = (frand(2.f) - 1.f) * EYE_LOOK_AWAY_MAX_PITCH;
+ mEyeLookAwayTime = EYE_LOOK_BACK_MIN_TIME + frand(EYE_LOOK_BACK_MAX_TIME - EYE_LOOK_BACK_MIN_TIME);
+ }
+ else
+ {
+ mEyeLookAwayYaw = 0.f;
+ mEyeLookAwayPitch = 0.f;
+ mEyeLookAwayTime = EYE_LOOK_AWAY_MIN_TIME + frand(EYE_LOOK_AWAY_MAX_TIME - EYE_LOOK_AWAY_MIN_TIME);
+ }
+ }
+
+ // do blinking
+ if (!mEyesClosed && mEyeBlinkTimer.getElapsedTimeF32() >= mEyeBlinkTime)
+ {
+ F32 leftEyeBlinkMorph = mEyeBlinkTimer.getElapsedTimeF32() - mEyeBlinkTime;
+ F32 rightEyeBlinkMorph = leftEyeBlinkMorph - EYE_BLINK_TIME_DELTA;
+
+ leftEyeBlinkMorph = llclamp(leftEyeBlinkMorph / EYE_BLINK_SPEED, 0.f, 1.f);
+ rightEyeBlinkMorph = llclamp(rightEyeBlinkMorph / EYE_BLINK_SPEED, 0.f, 1.f);
+ mCharacter->setVisualParamWeight("Blink_Left", leftEyeBlinkMorph);
+ mCharacter->setVisualParamWeight("Blink_Right", rightEyeBlinkMorph);
+ mCharacter->updateVisualParams();
+
+ if (rightEyeBlinkMorph == 1.f)
+ {
+ mEyesClosed = TRUE;
+ mEyeBlinkTime = EYE_BLINK_CLOSE_TIME;
+ mEyeBlinkTimer.reset();
+ }
+ }
+ else if (mEyesClosed)
+ {
+ if (mEyeBlinkTimer.getElapsedTimeF32() >= mEyeBlinkTime)
+ {
+ F32 leftEyeBlinkMorph = mEyeBlinkTimer.getElapsedTimeF32() - mEyeBlinkTime;
+ F32 rightEyeBlinkMorph = leftEyeBlinkMorph - EYE_BLINK_TIME_DELTA;
+
+ leftEyeBlinkMorph = 1.f - llclamp(leftEyeBlinkMorph / EYE_BLINK_SPEED, 0.f, 1.f);
+ rightEyeBlinkMorph = 1.f - llclamp(rightEyeBlinkMorph / EYE_BLINK_SPEED, 0.f, 1.f);
+ mCharacter->setVisualParamWeight("Blink_Left", leftEyeBlinkMorph);
+ mCharacter->setVisualParamWeight("Blink_Right", rightEyeBlinkMorph);
+ mCharacter->updateVisualParams();
+
+ if (rightEyeBlinkMorph == 0.f)
+ {
+ mEyesClosed = FALSE;
+ mEyeBlinkTime = EYE_BLINK_MIN_TIME + frand(EYE_BLINK_MAX_TIME - EYE_BLINK_MIN_TIME);
+ mEyeBlinkTimer.reset();
+ }
+ }
+ }
+
+ BOOL has_eye_target = FALSE;
+ LLVector3* targetPos = (LLVector3*)mCharacter->getAnimationData("LookAtPoint");
+
+ if (targetPos)
+ {
+ LLVector3 skyward(0.f, 0.f, 1.f);
+ LLVector3 left;
+ LLVector3 up;
+
+ eye_look_at = *targetPos;
+ F32 lookAtDistance = eye_look_at.normVec();
+
+ left.setVec(skyward % eye_look_at);
+ up.setVec(eye_look_at % left);
+
+ target_eye_rot = LLQuaternion(eye_look_at, left, up);
+
+ // calculate vergence
+ F32 interocular_dist = (mLeftEyeState.getJoint()->getWorldPosition() - mRightEyeState.getJoint()->getWorldPosition()).magVec();
+ vergence = -atan2((interocular_dist / 2.f), lookAtDistance);
+ llclamp(vergence, -F_PI_BY_TWO, 0.f);
+ }
+ else
+ {
+ target_eye_rot = mHeadJoint->getWorldRotation();
+ vergence = 0.f;
+ }
+
+ //RN: subtract 4 degrees to account for foveal angular offset relative to pupil
+ vergence += 4.f * DEG_TO_RAD;
+
+ // calculate eye jitter
+ LLQuaternion eye_jitter_rot;
+
+ // vergence not too high...
+ if (vergence > -0.05f)
+ {
+ //...go ahead and jitter
+ eye_jitter_rot.setQuat(0.f, mEyeJitterPitch + mEyeLookAwayPitch, mEyeJitterYaw + mEyeLookAwayYaw);
+ }
+ else
+ {
+ //...or don't
+ eye_jitter_rot.loadIdentity();
+ }
+
+ // calculate vergence of eyes as an object gets closer to the avatar's head
+ LLQuaternion vergence_quat;
+
+ if (has_eye_target)
+ {
+ vergence_quat.setQuat(vergence, LLVector3(0.f, 0.f, 1.f));
+ }
+ else
+ {
+ vergence_quat.loadIdentity();
+ }
+
+ // calculate eye rotations
+ LLQuaternion left_eye_rot = target_eye_rot;
+ left_eye_rot = vergence_quat * eye_jitter_rot * left_eye_rot;
+
+ LLQuaternion right_eye_rot = target_eye_rot;
+ vergence_quat.transQuat();
+ right_eye_rot = vergence_quat * eye_jitter_rot * right_eye_rot;
+
+ //set final eye rotations
+ // start with left
+ LLQuaternion tQw = mLeftEyeState.getJoint()->getParent()->getWorldRotation();
+ LLQuaternion tQh = left_eye_rot * ~tQw;
+ tQh.constrain(EYE_ROT_LIMIT_ANGLE);
+ mLeftEyeState.setRotation( tQh );
+
+ // now do right eye
+ tQw = mRightEyeState.getJoint()->getParent()->getWorldRotation();
+ tQh = right_eye_rot * ~tQw;
+ tQh.constrain(EYE_ROT_LIMIT_ANGLE);
+ mRightEyeState.setRotation( tQh );
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLEyeMotion::onDeactivate()
+{
+ LLJoint* joint = mLeftEyeState.getJoint();
+ if (joint)
+ {
+ joint->setRotation(LLQuaternion::DEFAULT);
+ }
+
+ joint = mRightEyeState.getJoint();
+ if (joint)
+ {
+ joint->setRotation(LLQuaternion::DEFAULT);
+ }
+}
+
+// End
diff --git a/indra/llcharacter/llheadrotmotion.h b/indra/llcharacter/llheadrotmotion.h
new file mode 100644
index 0000000000..dc327cf44b
--- /dev/null
+++ b/indra/llcharacter/llheadrotmotion.h
@@ -0,0 +1,194 @@
+/**
+ * @file llheadrotmotion.h
+ * @brief Implementation of LLHeadRotMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHEADROTMOTION_H
+#define LL_LLHEADROTMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+#include "llframetimer.h"
+
+#define MIN_REQUIRED_PIXEL_AREA_HEAD_ROT 500.f;
+#define MIN_REQUIRED_PIXEL_AREA_EYE 25000.f;
+
+//-----------------------------------------------------------------------------
+// class LLHeadRotMotion
+//-----------------------------------------------------------------------------
+class LLHeadRotMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLHeadRotMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLHeadRotMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLHeadRotMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 1.f; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 1.f; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_HEAD_ROT; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+
+ LLJoint *mTorsoJoint;
+ LLJoint *mHeadJoint;
+ LLJoint *mRootJoint;
+ LLJoint *mPelvisJoint;
+
+ LLJointState mTorsoState;
+ LLJointState mNeckState;
+ LLJointState mHeadState;
+
+ LLQuaternion mLastHeadRot;
+};
+
+//-----------------------------------------------------------------------------
+// class LLEyeMotion
+//-----------------------------------------------------------------------------
+class LLEyeMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLEyeMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLEyeMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create( const LLUUID &id) { return new LLEyeMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.5f; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.5f; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_EYE; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+
+ LLJoint *mHeadJoint;
+ LLJointState mLeftEyeState;
+ LLJointState mRightEyeState;
+
+ LLFrameTimer mEyeJitterTimer;
+ F32 mEyeJitterTime;
+ F32 mEyeJitterYaw;
+ F32 mEyeJitterPitch;
+ F32 mEyeLookAwayTime;
+ F32 mEyeLookAwayYaw;
+ F32 mEyeLookAwayPitch;
+
+ // eye blinking
+ LLFrameTimer mEyeBlinkTimer;
+ F32 mEyeBlinkTime;
+ BOOL mEyesClosed;
+};
+
+#endif // LL_LLHEADROTMOTION_H
+
diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
new file mode 100644
index 0000000000..3924c06adc
--- /dev/null
+++ b/indra/llcharacter/lljoint.cpp
@@ -0,0 +1,504 @@
+/**
+ * @file lljoint.cpp
+ * @brief Implementation of LLJoint class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "lljoint.h"
+
+#include "llmath.h"
+
+S32 LLJoint::sNumUpdates = 0;
+S32 LLJoint::sNumTouches = 0;
+
+//-----------------------------------------------------------------------------
+// LLJoint()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLJoint::LLJoint()
+{
+ mName = "unnamed";
+ mParent = NULL;
+ mXform.setScaleChildOffset(TRUE);
+ mXform.setScale(LLVector3(1.0f, 1.0f, 1.0f));
+ mDirtyFlags = MATRIX_DIRTY | ROTATION_DIRTY | POSITION_DIRTY;
+ mUpdateXform = TRUE;
+ mJointNum = -1;
+ touch();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLJoint()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLJoint::LLJoint(const std::string &name, LLJoint *parent)
+{
+ mName = "unnamed";
+ mParent = NULL;
+ mXform.setScaleChildOffset(TRUE);
+ mXform.setScale(LLVector3(1.0f, 1.0f, 1.0f));
+ mDirtyFlags = MATRIX_DIRTY | ROTATION_DIRTY | POSITION_DIRTY;
+ mJointNum = 0;
+
+ setName(name);
+ if (parent)
+ parent->addChild( this );
+
+ touch();
+}
+
+//-----------------------------------------------------------------------------
+// ~LLJoint()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLJoint::~LLJoint()
+{
+ removeAllChildren();
+}
+
+
+//-----------------------------------------------------------------------------
+// setup()
+//-----------------------------------------------------------------------------
+void LLJoint::setup(const std::string &name, LLJoint *parent)
+{
+ setName(name);
+ if (parent)
+ parent->addChild( this );
+}
+
+//-----------------------------------------------------------------------------
+// touch()
+// Sets all dirty flags for all children, recursively.
+//-----------------------------------------------------------------------------
+void LLJoint::touch(U32 flags)
+{
+ if ((flags | mDirtyFlags) != mDirtyFlags)
+ {
+ sNumTouches++;
+ mDirtyFlags |= flags;
+ U32 child_flags = flags;
+ if (flags & ROTATION_DIRTY)
+ {
+ child_flags |= POSITION_DIRTY;
+ }
+
+ for ( LLJoint *joint = mChildren.getFirstData();
+ joint != NULL;
+ joint = mChildren.getNextData() )
+ {
+ joint->touch(child_flags);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getRoot()
+//-----------------------------------------------------------------------------
+LLJoint *LLJoint::getRoot()
+{
+ if ( getParent() == NULL )
+ {
+ return this;
+ }
+ return getParent()->getRoot();
+}
+
+
+//-----------------------------------------------------------------------------
+// findJoint()
+//-----------------------------------------------------------------------------
+LLJoint *LLJoint::findJoint( const std::string &name )
+{
+ if (name == getName())
+ return this;
+
+ for ( LLJoint *j = mChildren.getFirstData();
+ j != NULL;
+ j = mChildren.getNextData() )
+ {
+ LLJoint *found = j->findJoint(name);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
+
+//--------------------------------------------------------------------
+// addChild()
+//--------------------------------------------------------------------
+void LLJoint::addChild(LLJoint *joint)
+{
+ if (joint->mParent)
+ joint->mParent->removeChild(joint);
+
+ mChildren.addDataAtEnd(joint);
+ joint->mXform.setParent(&mXform);
+ joint->mParent = this;
+ joint->touch();
+}
+
+
+//--------------------------------------------------------------------
+// removeChild()
+//--------------------------------------------------------------------
+void LLJoint::removeChild(LLJoint *joint)
+{
+ this->mChildren.removeData(joint);
+ joint->mXform.setParent(NULL);
+ joint->mParent = NULL;
+ joint->touch();
+}
+
+
+//--------------------------------------------------------------------
+// removeAllChildren()
+//--------------------------------------------------------------------
+void LLJoint::removeAllChildren()
+{
+ for ( LLJoint *joint = mChildren.getFirstData();
+ joint != NULL;
+ joint = mChildren.getNextData() )
+ {
+ removeChild(joint);
+ }
+}
+
+
+//--------------------------------------------------------------------
+// getPosition()
+//--------------------------------------------------------------------
+const LLVector3& LLJoint::getPosition()
+{
+ return mXform.getPosition();
+}
+
+
+//--------------------------------------------------------------------
+// setPosition()
+//--------------------------------------------------------------------
+void LLJoint::setPosition( const LLVector3& pos )
+{
+ mXform.setPosition(pos);
+ touch(MATRIX_DIRTY | POSITION_DIRTY);
+}
+
+
+//--------------------------------------------------------------------
+// getWorldPosition()
+//--------------------------------------------------------------------
+LLVector3 LLJoint::getWorldPosition()
+{
+ updateWorldPRSParent();
+ return mXform.getWorldPosition();
+}
+
+//-----------------------------------------------------------------------------
+// getLastWorldPosition()
+//-----------------------------------------------------------------------------
+LLVector3 LLJoint::getLastWorldPosition()
+{
+ return mXform.getWorldPosition();
+}
+
+
+//--------------------------------------------------------------------
+// setWorldPosition()
+//--------------------------------------------------------------------
+void LLJoint::setWorldPosition( const LLVector3& pos )
+{
+ if (mParent == NULL)
+ {
+ this->setPosition( pos );
+ return;
+ }
+
+ LLMatrix4 temp_matrix = getWorldMatrix();
+ temp_matrix.mMatrix[VW][VX] = pos.mV[VX];
+ temp_matrix.mMatrix[VW][VY] = pos.mV[VY];
+ temp_matrix.mMatrix[VW][VZ] = pos.mV[VZ];
+
+ LLMatrix4 parentWorldMatrix = mParent->getWorldMatrix();
+ LLMatrix4 invParentWorldMatrix = parentWorldMatrix.invert();
+
+ temp_matrix *= invParentWorldMatrix;
+
+ LLVector3 localPos( temp_matrix.mMatrix[VW][VX],
+ temp_matrix.mMatrix[VW][VY],
+ temp_matrix.mMatrix[VW][VZ] );
+
+ setPosition( localPos );
+}
+
+
+//--------------------------------------------------------------------
+// mXform.getRotation()
+//--------------------------------------------------------------------
+const LLQuaternion& LLJoint::getRotation()
+{
+ return mXform.getRotation();
+}
+
+
+//--------------------------------------------------------------------
+// setRotation()
+//--------------------------------------------------------------------
+void LLJoint::setRotation( const LLQuaternion& rot )
+{
+ if (rot.isFinite())
+ {
+ mXform.setRotation(rot);
+ touch(MATRIX_DIRTY | ROTATION_DIRTY);
+ }
+}
+
+
+//--------------------------------------------------------------------
+// getWorldRotation()
+//--------------------------------------------------------------------
+LLQuaternion LLJoint::getWorldRotation()
+{
+ updateWorldPRSParent();
+
+ return mXform.getWorldRotation();
+}
+
+//-----------------------------------------------------------------------------
+// getLastWorldRotation()
+//-----------------------------------------------------------------------------
+LLQuaternion LLJoint::getLastWorldRotation()
+{
+ return mXform.getWorldRotation();
+}
+
+//--------------------------------------------------------------------
+// setWorldRotation()
+//--------------------------------------------------------------------
+void LLJoint::setWorldRotation( const LLQuaternion& rot )
+{
+ if (mParent == NULL)
+ {
+ this->setRotation( rot );
+ return;
+ }
+
+ LLMatrix4 temp_mat(rot);
+
+ LLMatrix4 parentWorldMatrix = mParent->getWorldMatrix();
+ parentWorldMatrix.mMatrix[VW][VX] = 0;
+ parentWorldMatrix.mMatrix[VW][VY] = 0;
+ parentWorldMatrix.mMatrix[VW][VZ] = 0;
+
+ LLMatrix4 invParentWorldMatrix = parentWorldMatrix.invert();
+
+ temp_mat *= invParentWorldMatrix;
+
+ setRotation(LLQuaternion(temp_mat));
+}
+
+
+//--------------------------------------------------------------------
+// getScale()
+//--------------------------------------------------------------------
+const LLVector3& LLJoint::getScale()
+{
+ return mXform.getScale();
+}
+
+//--------------------------------------------------------------------
+// setScale()
+//--------------------------------------------------------------------
+void LLJoint::setScale( const LLVector3& scale )
+{
+ mXform.setScale(scale);
+ touch();
+}
+
+
+
+//--------------------------------------------------------------------
+// getWorldMatrix()
+//--------------------------------------------------------------------
+const LLMatrix4 &LLJoint::getWorldMatrix()
+{
+ updateWorldMatrixParent();
+
+ return mXform.getWorldMatrix();
+}
+
+
+//--------------------------------------------------------------------
+// setWorldMatrix()
+//--------------------------------------------------------------------
+void LLJoint::setWorldMatrix( const LLMatrix4& mat )
+{
+llinfos << "WARNING: LLJoint::setWorldMatrix() not correctly implemented yet" << llendl;
+ // extract global translation
+ LLVector3 trans( mat.mMatrix[VW][VX],
+ mat.mMatrix[VW][VY],
+ mat.mMatrix[VW][VZ] );
+
+ // extract global rotation
+ LLQuaternion rot( mat );
+
+ setWorldPosition( trans );
+ setWorldRotation( rot );
+}
+
+//-----------------------------------------------------------------------------
+// updateWorldMatrixParent()
+//-----------------------------------------------------------------------------
+void LLJoint::updateWorldMatrixParent()
+{
+ if (mDirtyFlags & MATRIX_DIRTY)
+ {
+ LLJoint *parent = getParent();
+ if (parent)
+ {
+ parent->updateWorldMatrixParent();
+ }
+ updateWorldMatrix();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateWorldPRSParent()
+//-----------------------------------------------------------------------------
+void LLJoint::updateWorldPRSParent()
+{
+ if (mDirtyFlags & (ROTATION_DIRTY | POSITION_DIRTY))
+ {
+ LLJoint *parent = getParent();
+ if (parent)
+ {
+ parent->updateWorldPRSParent();
+ }
+
+ mXform.update();
+ mDirtyFlags &= ~(ROTATION_DIRTY | POSITION_DIRTY);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateWorldMatrixChildren()
+//-----------------------------------------------------------------------------
+void LLJoint::updateWorldMatrixChildren()
+{
+ if (mDirtyFlags & MATRIX_DIRTY)
+ {
+ updateWorldMatrix();
+ }
+ for (LLJoint *child = mChildren.getFirstData(); child; child = mChildren.getNextData())
+ {
+ child->updateWorldMatrixChildren();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateWorldMatrix()
+//-----------------------------------------------------------------------------
+void LLJoint::updateWorldMatrix()
+{
+ if (mDirtyFlags & MATRIX_DIRTY)
+ {
+ sNumUpdates++;
+ mXform.updateMatrix(FALSE);
+ mDirtyFlags = 0x0;
+ }
+}
+
+//--------------------------------------------------------------------
+// getSkinOffset()
+//--------------------------------------------------------------------
+const LLVector3 &LLJoint::getSkinOffset()
+{
+ return mSkinOffset;
+}
+
+
+//--------------------------------------------------------------------
+// setSkinOffset()
+//--------------------------------------------------------------------
+void LLJoint::setSkinOffset( const LLVector3& offset )
+{
+ mSkinOffset = offset;
+}
+
+//-----------------------------------------------------------------------------
+// setConstraintSilhouette()
+//-----------------------------------------------------------------------------
+void LLJoint::setConstraintSilhouette(LLDynamicArray<LLVector3>& silhouette)
+{
+ S32 i;
+
+ mConstraintSilhouette.reset();
+ for (i = 0; i < silhouette.count(); i++)
+ {
+ if (i % 2 == 1)
+ {
+ // skip normals
+ continue;
+ }
+ mConstraintSilhouette[i / 2] = silhouette[i];
+ }
+ LLQuaternion inv_parent_rotation = LLQuaternion::DEFAULT;
+
+ if (getParent())
+ {
+ inv_parent_rotation = ~getParent()->getWorldRotation();
+ }
+
+ for (i = 0; i < mConstraintSilhouette.count(); i++)
+ {
+ LLVector3 vert = mConstraintSilhouette[i];
+
+ vert -= getWorldPosition();
+ vert.normVec();
+ vert = vert * inv_parent_rotation;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clampRotation()
+//-----------------------------------------------------------------------------
+void LLJoint::clampRotation(LLQuaternion old_rot, LLQuaternion new_rot)
+{
+ LLVector3 main_axis(1.f, 0.f, 0.f);
+
+ for (LLJoint* joint = mChildren.getFirstData(); joint; joint = mChildren.getNextData())
+ {
+ if (joint->isAnimatable())
+ {
+ main_axis = joint->getPosition();
+ main_axis.normVec();
+ // only care about first animatable child
+ break;
+ }
+ }
+
+ // 2003.03.26 - This code was just using up cpu cycles. AB
+
+// LLVector3 old_axis = main_axis * old_rot;
+// LLVector3 new_axis = main_axis * new_rot;
+
+// for (S32 i = 0; i < mConstraintSilhouette.count() - 1; i++)
+// {
+// LLVector3 vert1 = mConstraintSilhouette[i];
+// LLVector3 vert2 = mConstraintSilhouette[i + 1];
+
+ // figure out how to clamp rotation to line on 3-sphere
+
+// }
+}
+
+// End
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
new file mode 100644
index 0000000000..2fc86e87df
--- /dev/null
+++ b/indra/llcharacter/lljoint.h
@@ -0,0 +1,163 @@
+/**
+ * @file lljoint.h
+ * @brief Implementation of LLJoint class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLJOINT_H
+#define LL_LLJOINT_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include <string>
+
+#include "linked_lists.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+#include "xform.h"
+#include "lldarray.h"
+
+const S32 LL_CHARACTER_MAX_JOINTS_PER_MESH = 15;
+const U32 LL_CHARACTER_MAX_JOINTS = 32; // must be divisible by 4!
+const U32 LL_HAND_JOINT_NUM = 31;
+const U32 LL_FACE_JOINT_NUM = 30;
+const S32 LL_CHARACTER_MAX_PRIORITY = 7;
+const F32 LL_MAX_PELVIS_OFFSET = 5.f;
+
+//-----------------------------------------------------------------------------
+// class LLJoint
+//-----------------------------------------------------------------------------
+class LLJoint
+{
+public:
+ // priority levels, from highest to lowest
+ enum JointPriority
+ {
+ USE_MOTION_PRIORITY = -1,
+ LOW_PRIORITY = 0,
+ MEDIUM_PRIORITY,
+ HIGH_PRIORITY,
+ HIGHER_PRIORITY,
+ HIGHEST_PRIORITY,
+ ADDITIVE_PRIORITY = LL_CHARACTER_MAX_PRIORITY
+ };
+
+ enum DirtyFlags
+ {
+ MATRIX_DIRTY = 0x1 << 0,
+ ROTATION_DIRTY = 0x1 << 1,
+ POSITION_DIRTY = 0x1 << 2,
+ ALL_DIRTY = 0x7
+ };
+protected:
+ std::string mName;
+
+ // parent joint
+ LLJoint *mParent;
+
+ // explicit transformation members
+ LLXformMatrix mXform;
+
+public:
+ U32 mDirtyFlags;
+ BOOL mWorldRotationDirty;
+ BOOL mUpdateXform;
+
+ // describes the skin binding pose
+ LLVector3 mSkinOffset;
+
+ S32 mJointNum;
+
+ LLDynamicArray<LLVector3> mConstraintSilhouette;
+
+ // child joints
+ LLLinkedList<LLJoint> mChildren;
+
+ // debug statics
+ static S32 sNumTouches;
+ static S32 sNumUpdates;
+
+public:
+ LLJoint();
+ LLJoint( const std::string &name, LLJoint *parent=NULL );
+
+ virtual ~LLJoint();
+
+ // set name and parent
+ void setup( const std::string &name, LLJoint *parent=NULL );
+
+ void touch(U32 flags = ALL_DIRTY);
+
+ // get/set name
+ const std::string &getName() { return mName; }
+ void setName( const std::string &name ) { mName = name; }
+
+ // getParent
+ LLJoint *getParent() { return mParent; }
+
+ // getRoot
+ LLJoint *getRoot();
+
+ // search for child joints by name
+ LLJoint *findJoint( const std::string &name );
+
+ // add/remove children
+ void addChild( LLJoint *joint );
+ void removeChild( LLJoint *joint );
+ void removeAllChildren();
+
+ // get/set local position
+ const LLVector3& getPosition();
+ void setPosition( const LLVector3& pos );
+
+ // get/set world position
+ LLVector3 getWorldPosition();
+ LLVector3 getLastWorldPosition();
+ void setWorldPosition( const LLVector3& pos );
+
+ // get/set local rotation
+ const LLQuaternion& getRotation();
+ void setRotation( const LLQuaternion& rot );
+
+ // get/set world rotation
+ LLQuaternion getWorldRotation();
+ LLQuaternion getLastWorldRotation();
+ void setWorldRotation( const LLQuaternion& rot );
+
+ // get/set local scale
+ const LLVector3& getScale();
+ void setScale( const LLVector3& scale );
+
+ // get/set world matrix
+ const LLMatrix4 &getWorldMatrix();
+ void setWorldMatrix( const LLMatrix4& mat );
+
+ void updateWorldMatrixChildren();
+ void updateWorldMatrixParent();
+
+ void updateWorldPRSParent();
+
+ void updateWorldMatrix();
+
+ // get/set skin offset
+ const LLVector3 &getSkinOffset();
+ void setSkinOffset( const LLVector3 &offset);
+
+ LLXformMatrix *getXform() { return &mXform; }
+
+ void setConstraintSilhouette(LLDynamicArray<LLVector3>& silhouette);
+
+ void clampRotation(LLQuaternion old_rot, LLQuaternion new_rot);
+
+ virtual BOOL isAnimatable() { return TRUE; }
+
+ S32 getJointNum() { return mJointNum; }
+ void setJointNum(S32 joint_num) { mJointNum = joint_num; }
+};
+#endif // LL_LLJOINT_H
+
diff --git a/indra/llcharacter/lljointsolverrp3.cpp b/indra/llcharacter/lljointsolverrp3.cpp
new file mode 100644
index 0000000000..60b836734a
--- /dev/null
+++ b/indra/llcharacter/lljointsolverrp3.cpp
@@ -0,0 +1,366 @@
+/**
+ * @file lljointsolverrp3.cpp
+ * @brief Implementation of LLJointSolverRP3 class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "lljointsolverrp3.h"
+
+#include <math.h>
+
+#include "llmath.h"
+
+#define F_EPSILON 0.00001f
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+LLJointSolverRP3::LLJointSolverRP3()
+{
+ mJointA = NULL;
+ mJointB = NULL;
+ mJointC = NULL;
+ mJointGoal = NULL;
+ mLengthAB = 1.0f;
+ mLengthBC = 1.0f;
+ mPoleVector.setVec( 1.0f, 0.0f, 0.0f );
+ mbUseBAxis = FALSE;
+ mTwist = 0.0f;
+ mFirstTime = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Destructor
+//-----------------------------------------------------------------------------
+/*virtual*/ LLJointSolverRP3::~LLJointSolverRP3()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// setupJoints()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::setupJoints( LLJoint* jointA,
+ LLJoint* jointB,
+ LLJoint* jointC,
+ LLJoint* jointGoal )
+{
+ mJointA = jointA;
+ mJointB = jointB;
+ mJointC = jointC;
+ mJointGoal = jointGoal;
+
+ mLengthAB = mJointB->getPosition().magVec();
+ mLengthBC = mJointC->getPosition().magVec();
+
+ mJointABaseRotation = jointA->getRotation();
+ mJointBBaseRotation = jointB->getRotation();
+}
+
+
+//-----------------------------------------------------------------------------
+// getPoleVector()
+//-----------------------------------------------------------------------------
+const LLVector3& LLJointSolverRP3::getPoleVector()
+{
+ return mPoleVector;
+}
+
+
+//-----------------------------------------------------------------------------
+// setPoleVector()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::setPoleVector( const LLVector3& poleVector )
+{
+ mPoleVector = poleVector;
+ mPoleVector.normVec();
+}
+
+
+//-----------------------------------------------------------------------------
+// setPoleVector()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::setBAxis( const LLVector3& bAxis )
+{
+ mBAxis = bAxis;
+ mBAxis.normVec();
+ mbUseBAxis = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// getTwist()
+//-----------------------------------------------------------------------------
+F32 LLJointSolverRP3::getTwist()
+{
+ return mTwist;
+}
+
+
+//-----------------------------------------------------------------------------
+// setTwist()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::setTwist( F32 twist )
+{
+ mTwist = twist;
+}
+
+
+//-----------------------------------------------------------------------------
+// solve()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::solve()
+{
+// llinfos << llendl;
+// llinfos << "LLJointSolverRP3::solve()" << llendl;
+
+ //-------------------------------------------------------------------------
+ // setup joints in their base rotations
+ //-------------------------------------------------------------------------
+ mJointA->setRotation( mJointABaseRotation );
+ mJointB->setRotation( mJointBBaseRotation );
+
+ //-------------------------------------------------------------------------
+ // get joint positions in world space
+ //-------------------------------------------------------------------------
+ LLVector3 aPos = mJointA->getWorldPosition();
+ LLVector3 bPos = mJointB->getWorldPosition();
+ LLVector3 cPos = mJointC->getWorldPosition();
+ LLVector3 gPos = mJointGoal->getWorldPosition();
+
+// llinfos << "bPosLocal = " << mJointB->getPosition() << llendl;
+// llinfos << "cPosLocal = " << mJointC->getPosition() << llendl;
+// llinfos << "bRotLocal = " << mJointB->getRotation() << llendl;
+// llinfos << "cRotLocal = " << mJointC->getRotation() << llendl;
+
+// llinfos << "aPos : " << aPos << llendl;
+// llinfos << "bPos : " << bPos << llendl;
+// llinfos << "cPos : " << cPos << llendl;
+// llinfos << "gPos : " << gPos << llendl;
+
+ //-------------------------------------------------------------------------
+ // get the poleVector in world space
+ //-------------------------------------------------------------------------
+ LLMatrix4 worldJointAParentMat;
+ if ( mJointA->getParent() )
+ {
+ worldJointAParentMat = mJointA->getParent()->getWorldMatrix();
+ }
+ LLVector3 poleVec = rotate_vector( mPoleVector, worldJointAParentMat );
+
+ //-------------------------------------------------------------------------
+ // compute the following:
+ // vector from A to B
+ // vector from B to C
+ // vector from A to C
+ // vector from A to G (goal)
+ //-------------------------------------------------------------------------
+ LLVector3 abVec = bPos - aPos;
+ LLVector3 bcVec = cPos - bPos;
+ LLVector3 acVec = cPos - aPos;
+ LLVector3 agVec = gPos - aPos;
+
+// llinfos << "abVec : " << abVec << llendl;
+// llinfos << "bcVec : " << bcVec << llendl;
+// llinfos << "acVec : " << acVec << llendl;
+// llinfos << "agVec : " << agVec << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute needed lengths of those vectors
+ //-------------------------------------------------------------------------
+ F32 abLen = abVec.magVec();
+ F32 bcLen = bcVec.magVec();
+ F32 agLen = agVec.magVec();
+
+// llinfos << "abLen : " << abLen << llendl;
+// llinfos << "bcLen : " << bcLen << llendl;
+// llinfos << "agLen : " << agLen << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute component vector of (A->B) orthogonal to (A->C)
+ //-------------------------------------------------------------------------
+ LLVector3 abacCompOrthoVec = abVec - acVec * ((abVec * acVec)/(acVec * acVec));
+
+// llinfos << "abacCompOrthoVec : " << abacCompOrthoVec << llendl
+
+ //-------------------------------------------------------------------------
+ // compute the normal of the original ABC plane (and store for later)
+ //-------------------------------------------------------------------------
+ LLVector3 abcNorm;
+ if (!mbUseBAxis)
+ {
+ if( are_parallel(abVec, bcVec, 0.001f) )
+ {
+ // the current solution is maxed out, so we use the axis that is
+ // orthogonal to both poleVec and A->B
+ if ( are_parallel(poleVec, abVec, 0.001f) )
+ {
+ // ACK! the problem is singular
+ if ( are_parallel(poleVec, agVec, 0.001f) )
+ {
+ // the solutions is also singular
+ return;
+ }
+ else
+ {
+ abcNorm = poleVec % agVec;
+ }
+ }
+ else
+ {
+ abcNorm = poleVec % abVec;
+ }
+ }
+ else
+ {
+ abcNorm = abVec % bcVec;
+ }
+ }
+ else
+ {
+ abcNorm = mBAxis * mJointB->getWorldRotation();
+ }
+
+ //-------------------------------------------------------------------------
+ // compute rotation of B
+ //-------------------------------------------------------------------------
+ // angle between A->B and B->C
+ F32 abbcAng = angle_between(abVec, bcVec);
+
+ // vector orthogonal to A->B and B->C
+ LLVector3 abbcOrthoVec = abVec % bcVec;
+ if (abbcOrthoVec.magVecSquared() < 0.001f)
+ {
+ abbcOrthoVec = poleVec % abVec;
+ abacCompOrthoVec = poleVec;
+ }
+ abbcOrthoVec.normVec();
+
+ F32 agLenSq = agLen * agLen;
+
+ // angle arm for extension
+ F32 cosTheta = (agLenSq - abLen*abLen - bcLen*bcLen) / (2.0f * abLen * bcLen);
+ if (cosTheta > 1.0f)
+ cosTheta = 1.0f;
+ else if (cosTheta < -1.0f)
+ cosTheta = -1.0f;
+
+ F32 theta = acos(cosTheta);
+
+ LLQuaternion bRot(theta - abbcAng, abbcOrthoVec);
+
+// llinfos << "abbcAng : " << abbcAng << llendl;
+// llinfos << "abbcOrthoVec : " << abbcOrthoVec << llendl;
+// llinfos << "agLenSq : " << agLenSq << llendl;
+// llinfos << "cosTheta : " << cosTheta << llendl;
+// llinfos << "theta : " << theta << llendl;
+// llinfos << "bRot : " << bRot << llendl;
+// llinfos << "theta abbcAng theta-abbcAng: " << theta*180.0/F_PI << " " << abbcAng*180.0f/F_PI << " " << (theta - abbcAng)*180.0f/F_PI << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute rotation that rotates new A->C to A->G
+ //-------------------------------------------------------------------------
+ // rotate B->C by bRot
+ bcVec = bcVec * bRot;
+
+ // update A->C
+ acVec = abVec + bcVec;
+
+ LLQuaternion cgRot;
+ cgRot.shortestArc( acVec, agVec );
+
+// llinfos << "bcVec : " << bcVec << llendl;
+// llinfos << "acVec : " << acVec << llendl;
+// llinfos << "cgRot : " << cgRot << llendl;
+
+ // update A->B and B->C with rotation from C to G
+ abVec = abVec * cgRot;
+ bcVec = bcVec * cgRot;
+ abcNorm = abcNorm * cgRot;
+ acVec = abVec + bcVec;
+
+ //-------------------------------------------------------------------------
+ // compute the normal of the APG plane
+ //-------------------------------------------------------------------------
+ if (are_parallel(agVec, poleVec, 0.001f))
+ {
+ // the solution plane is undefined ==> we're done
+ return;
+ }
+ LLVector3 apgNorm = poleVec % agVec;
+ apgNorm.normVec();
+
+ if (!mbUseBAxis)
+ {
+ //---------------------------------------------------------------------
+ // compute the normal of the new ABC plane
+ // (only necessary if we're NOT using mBAxis)
+ //---------------------------------------------------------------------
+ if( are_parallel(abVec, bcVec, 0.001f) )
+ {
+ // G is either too close or too far away
+ // we'll use the old ABCnormal
+ }
+ else
+ {
+ abcNorm = abVec % bcVec;
+ }
+ abcNorm.normVec();
+ }
+
+ //-------------------------------------------------------------------------
+ // calcuate plane rotation
+ //-------------------------------------------------------------------------
+ LLQuaternion pRot;
+ if ( are_parallel( abcNorm, apgNorm, 0.001f) )
+ {
+ if (abcNorm * apgNorm < 0.0f)
+ {
+ // we must be PI radians off ==> rotate by PI around agVec
+ pRot.setQuat(F_PI, agVec);
+ }
+ else
+ {
+ // we're done
+ }
+ }
+ else
+ {
+ pRot.shortestArc( abcNorm, apgNorm );
+ }
+
+// llinfos << "abcNorm = " << abcNorm << llendl;
+// llinfos << "apgNorm = " << apgNorm << llendl;
+// llinfos << "pRot = " << pRot << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute twist rotation
+ //-------------------------------------------------------------------------
+ LLQuaternion twistRot( mTwist, agVec );
+
+// llinfos << "twist : " << mTwist*180.0/F_PI << llendl;
+// llinfos << "agNormVec: " << agNormVec << llendl;
+// llinfos << "twistRot : " << twistRot << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute rotation of A
+ //-------------------------------------------------------------------------
+ LLQuaternion aRot = cgRot * pRot * twistRot;
+
+ //-------------------------------------------------------------------------
+ // apply the rotations
+ //-------------------------------------------------------------------------
+ mJointB->setWorldRotation( mJointB->getWorldRotation() * bRot );
+ mJointA->setWorldRotation( mJointA->getWorldRotation() * aRot );
+}
+
+
+// End
diff --git a/indra/llcharacter/lljointsolverrp3.h b/indra/llcharacter/lljointsolverrp3.h
new file mode 100644
index 0000000000..d1507351b6
--- /dev/null
+++ b/indra/llcharacter/lljointsolverrp3.h
@@ -0,0 +1,158 @@
+/**
+ * @file lljointsolverrp3.h
+ * @brief Implementation of LLJointSolverRP3 class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLJOINTSOLVERRP3_H
+#define LL_LLJOINTSOLVERRP3_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "lljoint.h"
+
+/* -some compilers don't like line continuation chars-
+//-----------------------------------------------------------------------------
+// class LLJointSolverRP3
+//
+// This class is a "poor man's" IK for simple 3 joint kinematic chains.
+// It is modeled after the 'ikRPSolver' in Maya.
+// This class takes 4 LLJoints:
+// jointA
+// jointB
+// jointC
+// jointGoal
+//
+// Such that jointA is the parent of jointB, jointB is the parent of jointC.
+// When invoked, this class modifies the rotations of jointA and jointB such
+// that the position of the jointC attempts to reach the position of jointGoal.
+//
+// At object initialization time, the distances between jointA - jointB and
+// jointB - jointC are cached. During evaluation these bone lengths are
+// preserved.
+//
+// A A
+// | |
+// | |
+// B B---CG A---B---C...G
+// \
+// \
+// CG
+//
+//
+// In addition a "poleVector" is specified that does two things:
+//
+// a) defines the plane in which the solution occurs, thus
+// reducing an infinite number of solutions, down to 2.
+//
+// b) disambiguates the resulting two solutions as follows:
+//
+// A A A--->poleVector
+// | \ \
+// | \ \
+// B vs. B ==> B
+// \ | |
+// \ | |
+// CG CG CG
+//
+// A "twist" setting allows the solution plane to be rotated about the
+// line between A and C. A handy animation feature.
+//
+// For "smarter" results for non-coplanar limbs, specify the joints axis
+// of bend in the B's local frame (see setBAxis())
+//-----------------------------------------------------------------------------
+*/
+
+class LLJointSolverRP3
+{
+protected:
+ LLJoint *mJointA;
+ LLJoint *mJointB;
+ LLJoint *mJointC;
+ LLJoint *mJointGoal;
+
+ F32 mLengthAB;
+ F32 mLengthBC;
+
+ LLVector3 mPoleVector;
+ LLVector3 mBAxis;
+ BOOL mbUseBAxis;
+
+ F32 mTwist;
+
+ BOOL mFirstTime;
+ LLMatrix4 mSavedJointAMat;
+ LLMatrix4 mSavedInvPlaneMat;
+
+ LLQuaternion mJointABaseRotation;
+ LLQuaternion mJointBBaseRotation;
+
+public:
+ //-------------------------------------------------------------------------
+ // Constructor/Destructor
+ //-------------------------------------------------------------------------
+ LLJointSolverRP3();
+ virtual ~LLJointSolverRP3();
+
+ //-------------------------------------------------------------------------
+ // setupJoints()
+ // This must be called one time to setup the solver.
+ // This must be called AFTER the skeleton has been created, all parent/child
+ // relationships are established, and after the joints are placed in
+ // a valid configuration (as distances between them will be cached).
+ //-------------------------------------------------------------------------
+ void setupJoints( LLJoint* jointA,
+ LLJoint* jointB,
+ LLJoint* jointC,
+ LLJoint* jointGoal );
+
+ //-------------------------------------------------------------------------
+ // getPoleVector()
+ // Returns the current pole vector.
+ //-------------------------------------------------------------------------
+ const LLVector3& getPoleVector();
+
+ //-------------------------------------------------------------------------
+ // setPoleVector()
+ // Sets the pole vector.
+ // The pole vector is defined relative to (in the space of) jointA's parent.
+ // The default pole vector is (1,0,0), and this is used if this function
+ // is never called.
+ // This vector is normalized when set.
+ //-------------------------------------------------------------------------
+ void setPoleVector( const LLVector3& poleVector );
+
+ //-------------------------------------------------------------------------
+ // setBAxis()
+ // Sets the joint's axis in B's local frame, and enable "smarter" solve().
+ // This allows for smarter IK when for twisted limbs.
+ //-------------------------------------------------------------------------
+ void setBAxis( const LLVector3& bAxis );
+
+ //-------------------------------------------------------------------------
+ // getTwist()
+ // Returns the current twist in radians.
+ //-------------------------------------------------------------------------
+ F32 getTwist();
+
+ //-------------------------------------------------------------------------
+ // setTwist()
+ // Sets the twist value.
+ // The default is 0.0.
+ //-------------------------------------------------------------------------
+ void setTwist( F32 twist );
+
+ //-------------------------------------------------------------------------
+ // solve()
+ // This is the "work" function.
+ // When called, the rotations of jointA and jointB will be modified
+ // such that jointC attempts to reach jointGoal.
+ //-------------------------------------------------------------------------
+ void solve();
+};
+
+#endif // LL_LLJOINTSOLVERRP3_H
+
diff --git a/indra/llcharacter/lljointstate.h b/indra/llcharacter/lljointstate.h
new file mode 100644
index 0000000000..82a2b345b0
--- /dev/null
+++ b/indra/llcharacter/lljointstate.h
@@ -0,0 +1,106 @@
+/**
+ * @file lljointstate.h
+ * @brief Implementation of LLJointState class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLJOINTSTATE_H
+#define LL_LLJOINTSTATE_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "lljoint.h"
+
+//-----------------------------------------------------------------------------
+// class LLJointState
+//-----------------------------------------------------------------------------
+class LLJointState
+{
+public:
+ enum BlendPhase
+ {
+ INACTIVE,
+ EASE_IN,
+ ACTIVE,
+ EASE_OUT
+ };
+protected:
+ // associated joint
+ LLJoint *mJoint;
+
+ // indicates which members are used
+ U32 mUsage;
+
+ // indicates weighted effect of this joint
+ F32 mWeight;
+
+ // transformation members
+ LLVector3 mPosition; // position relative to parent joint
+ LLQuaternion mRotation; // joint rotation relative to parent joint
+ LLVector3 mScale; // scale relative to rotated frame
+ LLJoint::JointPriority mPriority; // how important this joint state is relative to others
+public:
+ // Constructor
+ LLJointState()
+ {
+ mUsage = 0;
+ mJoint = NULL;
+ mUsage = 0;
+ mWeight = 0.f;
+ mPriority = LLJoint::USE_MOTION_PRIORITY;
+ }
+
+ LLJointState(LLJoint* joint)
+ {
+ mUsage = 0;
+ mJoint = joint;
+ mUsage = 0;
+ mWeight = 0.f;
+ mPriority = LLJoint::USE_MOTION_PRIORITY;
+ }
+
+ // Destructor
+ virtual ~LLJointState()
+ {
+ }
+
+ // joint that this state is applied to
+ LLJoint *getJoint() { return mJoint; }
+ BOOL setJoint( LLJoint *joint ) { mJoint = joint; return mJoint != NULL; }
+
+ // transform type (bitwise flags can be combined)
+ // Note that these are set automatically when various
+ // member setPos/setRot/setScale functions are called.
+ enum Usage
+ {
+ POS = 1,
+ ROT = 2,
+ SCALE = 4,
+ };
+ U32 getUsage() { return mUsage; }
+ void setUsage( U32 usage ) { mUsage = usage; }
+ F32 getWeight() { return mWeight; }
+ void setWeight( F32 weight ) { mWeight = weight; }
+
+ // get/set position
+ const LLVector3& getPosition() { return mPosition; }
+ void setPosition( const LLVector3& pos ) { llassert(mUsage & POS); mPosition = pos; }
+
+ // get/set rotation
+ const LLQuaternion& getRotation() { return mRotation; }
+ void setRotation( const LLQuaternion& rot ) { llassert(mUsage & ROT); mRotation = rot; }
+
+ // get/set scale
+ const LLVector3& getScale() { return mScale; }
+ void setScale( const LLVector3& scale ) { llassert(mUsage & SCALE); mScale = scale; }
+
+ // get/set priority
+ const LLJoint::JointPriority getPriority() { return mPriority; }
+ void setPriority( const LLJoint::JointPriority priority ) { mPriority = priority; }
+};
+
+#endif // LL_LLJOINTSTATE_H
+
diff --git a/indra/llcharacter/llkeyframefallmotion.cpp b/indra/llcharacter/llkeyframefallmotion.cpp
new file mode 100644
index 0000000000..248d22c22c
--- /dev/null
+++ b/indra/llcharacter/llkeyframefallmotion.cpp
@@ -0,0 +1,123 @@
+/**
+ * @file llkeyframefallmotion.cpp
+ * @brief Implementation of LLKeyframeFallMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llkeyframefallmotion.h"
+#include "llcharacter.h"
+#include "m3math.h"
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GO_TO_KEY_POSE 1
+#define MIN_TRACK_SPEED 0.01f
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeFallMotion::LLKeyframeFallMotion(const LLUUID &id) : LLKeyframeMotion(id)
+{
+ mVelocityZ = 0.f;
+ mCharacter = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeFallMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeFallMotion::~LLKeyframeFallMotion()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeFallMotion::onInitialize(LLCharacter *character)
+{
+ // save character pointer for later use
+ mCharacter = character;
+
+ // load keyframe data, setup pose and joint states
+ LLMotion::LLMotionInitStatus result = LLKeyframeMotion::onInitialize(character);
+
+ for (U32 jm=0; jm<mJointMotionList->mNumJointMotions; jm++)
+ {
+ if (!mJointStates[jm].getJoint())
+ continue;
+ if (mJointStates[jm].getJoint()->getName() == std::string("mPelvis"))
+ {
+ mPelvisStatep = &mJointStates[jm];
+ }
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeFallMotion::onActivate()
+{
+ LLVector3 ground_pos;
+ LLVector3 ground_normal;
+ LLQuaternion inverse_pelvis_rot;
+ LLVector3 fwd_axis(1.f, 0.f, 0.f);
+
+ mVelocityZ = -mCharacter->getCharacterVelocity().mV[VZ];
+ mCharacter->getGround( mCharacter->getCharacterPosition(), ground_pos, ground_normal);
+ ground_normal.normVec();
+
+ inverse_pelvis_rot = mCharacter->getCharacterRotation();
+ inverse_pelvis_rot.transQuat();
+
+ // find ground normal in pelvis space
+ ground_normal = ground_normal * inverse_pelvis_rot;
+
+ // calculate new foward axis
+ fwd_axis = fwd_axis - (ground_normal * (ground_normal * fwd_axis));
+ fwd_axis.normVec();
+ mRotationToGroundNormal = LLQuaternion(fwd_axis, ground_normal % fwd_axis, ground_normal);
+
+ return LLKeyframeMotion::onActivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeFallMotion::onUpdate(F32 activeTime, U8* joint_mask)
+{
+ BOOL result = LLKeyframeMotion::onUpdate(activeTime, joint_mask);
+ F32 slerp_amt = clamp_rescale(activeTime / getDuration(), 0.5f, 0.75f, 0.f, 1.f);
+
+ mPelvisStatep->setRotation(mPelvisStatep->getRotation() * slerp(slerp_amt, mRotationToGroundNormal, LLQuaternion()));
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion::getEaseInDuration()
+//-----------------------------------------------------------------------------
+F32 LLKeyframeFallMotion::getEaseInDuration()
+{
+ if (mVelocityZ == 0.f)
+ {
+ // we've already hit the ground
+ return 0.4f;
+ }
+
+ return mCharacter->getPreferredPelvisHeight() / mVelocityZ;
+}
+
+// End
diff --git a/indra/llcharacter/llkeyframefallmotion.h b/indra/llcharacter/llkeyframefallmotion.h
new file mode 100644
index 0000000000..5cc15fd38b
--- /dev/null
+++ b/indra/llcharacter/llkeyframefallmotion.h
@@ -0,0 +1,60 @@
+/**
+ * @file llkeyframefallmotion.h
+ * @brief Implementation of LLKeframeWalkMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKeyframeFallMotion_H
+#define LL_LLKeyframeFallMotion_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llkeyframemotion.h"
+#include "llcharacter.h"
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeFallMotion
+//-----------------------------------------------------------------------------
+class LLKeyframeFallMotion :
+ public LLKeyframeMotion
+{
+public:
+ // Constructor
+ LLKeyframeFallMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeFallMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLKeyframeFallMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ virtual F32 getEaseInDuration();
+ virtual BOOL onUpdate(F32 activeTime, U8* joint_mask);
+
+protected:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter* mCharacter;
+ F32 mVelocityZ;
+ LLJointState* mPelvisStatep;
+ LLQuaternion mRotationToGroundNormal;
+};
+
+#endif // LL_LLKeyframeFallMotion_H
+
diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp
new file mode 100644
index 0000000000..4dd976d5f1
--- /dev/null
+++ b/indra/llcharacter/llkeyframemotion.cpp
@@ -0,0 +1,2112 @@
+/**
+ * @file llkeyframemotion.cpp
+ * @brief Implementation of LLKeyframeMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llmath.h"
+#include "llanimationstates.h"
+#include "llassetstorage.h"
+#include "lldatapacker.h"
+#include "llcharacter.h"
+#include "llcriticaldamp.h"
+#include "lldir.h"
+#include "llendianswizzle.h"
+#include "llkeyframemotion.h"
+#include "llquantize.h"
+#include "llvfile.h"
+#include "m3math.h"
+#include "message.h"
+
+//-----------------------------------------------------------------------------
+// Static Definitions
+//-----------------------------------------------------------------------------
+LLVFS* LLKeyframeMotion::sVFS = NULL;
+LLKeyframeDataCache::LLKeyframeDataMap LLKeyframeDataCache::sKeyframeDataMap;
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+static F32 JOINT_LENGTH_K = 0.7f;
+static S32 MAX_ITERATIONS = 20;
+static S32 MIN_ITERATIONS = 1;
+static S32 MIN_ITERATION_COUNT = 2;
+static F32 MAX_PIXEL_AREA_CONSTRAINTS = 80000.f;
+static F32 MIN_PIXEL_AREA_CONSTRAINTS = 1000.f;
+static F32 MIN_ACCELERATION_SQUARED = 0.0005f * 0.0005f;
+
+static F32 MAX_CONSTRAINTS = 10;
+
+//-----------------------------------------------------------------------------
+// JointMotionList::dumpDiagInfo()
+//-----------------------------------------------------------------------------
+U32 LLKeyframeMotion::JointMotionList::dumpDiagInfo()
+{
+ S32 total_size = sizeof(JointMotionList);
+
+ for (U32 i = 0; i < mNumJointMotions; i++)
+ {
+ LLKeyframeMotion::JointMotion* joint_motion_p = &mJointMotionArray[i];
+
+ llinfos << "\tJoint " << joint_motion_p->mJointName << llendl;
+ if (joint_motion_p->mUsage & LLJointState::SCALE)
+ {
+ llinfos << "\t" << joint_motion_p->mScaleCurve.mNumKeys << " scale keys at "
+ << joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey) << " bytes" << llendl;
+
+ total_size += joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey);
+ }
+ if (joint_motion_p->mUsage & LLJointState::ROT)
+ {
+ llinfos << "\t" << joint_motion_p->mRotationCurve.mNumKeys << " rotation keys at "
+ << joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey) << " bytes" << llendl;
+
+ total_size += joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey);
+ }
+ if (joint_motion_p->mUsage & LLJointState::POS)
+ {
+ llinfos << "\t" << joint_motion_p->mPositionCurve.mNumKeys << " position keys at "
+ << joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey) << " bytes" << llendl;
+
+ total_size += joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey);
+ }
+ }
+ llinfos << "Size: " << total_size << " bytes" << llendl;
+
+ return total_size;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// ****Curve classes
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// ScaleCurve::ScaleCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::ScaleCurve::ScaleCurve()
+{
+ mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// ScaleCurve::~ScaleCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::ScaleCurve::~ScaleCurve()
+{
+ mKeys.deleteAllData();
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// getValue()
+//-----------------------------------------------------------------------------
+LLVector3 LLKeyframeMotion::ScaleCurve::getValue(F32 time, F32 duration)
+{
+ LLVector3 value;
+ F32 index_before, index_after;
+ ScaleKey* scale_before;
+ ScaleKey* scale_after;
+
+ mKeys.getInterval(time, index_before, index_after, scale_before, scale_after);
+ if (scale_before)
+ {
+ if (!scale_after)
+ {
+ scale_after = &mLoopInKey;
+ index_after = duration;
+ }
+
+ if (index_after == index_before)
+ {
+ value = scale_after->mScale;
+ }
+ else
+ {
+ F32 u = (time - index_before) / (index_after - index_before);
+ value = interp(u, *scale_before, *scale_after);
+ }
+ }
+ else
+ {
+ // before first key
+ if (scale_after)
+ {
+ value = scale_after->mScale;
+ }
+ // no keys?
+ else
+ {
+ value.clearVec();
+ }
+ }
+
+ return value;
+}
+
+//-----------------------------------------------------------------------------
+// interp()
+//-----------------------------------------------------------------------------
+LLVector3 LLKeyframeMotion::ScaleCurve::interp(F32 u, ScaleKey& before, ScaleKey& after)
+{
+ switch (mInterpolationType)
+ {
+ case IT_STEP:
+ return before.mScale;
+
+ default:
+ case IT_LINEAR:
+ case IT_SPLINE:
+ return lerp(before.mScale, after.mScale, u);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// RotationCurve::RotationCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::RotationCurve::RotationCurve()
+{
+ mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// RotationCurve::~RotationCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::RotationCurve::~RotationCurve()
+{
+ mKeys.deleteAllData();
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// RotationCurve::getValue()
+//-----------------------------------------------------------------------------
+LLQuaternion LLKeyframeMotion::RotationCurve::getValue(F32 time, F32 duration)
+{
+ LLQuaternion value;
+ F32 index_before, index_after;
+ RotationKey* rot_before;
+ RotationKey* rot_after;
+
+ mKeys.getInterval(time, index_before, index_after, rot_before, rot_after);
+
+ if (rot_before)
+ {
+ if (!rot_after)
+ {
+ rot_after = &mLoopInKey;
+ index_after = duration;
+ }
+
+ if (index_after == index_before)
+ {
+ value = rot_after->mRotation;
+ }
+ else
+ {
+ F32 u = (time - index_before) / (index_after - index_before);
+ value = interp(u, *rot_before, *rot_after);
+ }
+ }
+ else
+ {
+ // before first key
+ if (rot_after)
+ {
+ value = rot_after->mRotation;
+ }
+ // no keys?
+ else
+ {
+ value = LLQuaternion::DEFAULT;
+ }
+ }
+
+ return value;
+}
+
+//-----------------------------------------------------------------------------
+// interp()
+//-----------------------------------------------------------------------------
+LLQuaternion LLKeyframeMotion::RotationCurve::interp(F32 u, RotationKey& before, RotationKey& after)
+{
+ switch (mInterpolationType)
+ {
+ case IT_STEP:
+ return before.mRotation;
+
+ default:
+ case IT_LINEAR:
+ case IT_SPLINE:
+ return nlerp(u, before.mRotation, after.mRotation);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// PositionCurve::PositionCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::PositionCurve::PositionCurve()
+{
+ mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// PositionCurve::~PositionCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::PositionCurve::~PositionCurve()
+{
+ mKeys.deleteAllData();
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// PositionCurve::getValue()
+//-----------------------------------------------------------------------------
+LLVector3 LLKeyframeMotion::PositionCurve::getValue(F32 time, F32 duration)
+{
+ LLVector3 value;
+ F32 index_before, index_after;
+ PositionKey* pos_before;
+ PositionKey* pos_after;
+
+ mKeys.getInterval(time, index_before, index_after, pos_before, pos_after);
+
+ if (pos_before)
+ {
+ if (!pos_after)
+ {
+ pos_after = &mLoopInKey;
+ index_after = duration;
+ }
+
+ if (index_after == index_before)
+ {
+ value = pos_after->mPosition;
+ }
+ else
+ {
+ F32 u = (time - index_before) / (index_after - index_before);
+ value = interp(u, *pos_before, *pos_after);
+ }
+ }
+ else
+ {
+ // before first key
+ if (pos_after)
+ {
+ value = pos_after->mPosition;
+ }
+ // no keys?
+ else
+ {
+ value.clearVec();
+ }
+ }
+
+ llassert(value.isFinite());
+
+ return value;
+}
+
+//-----------------------------------------------------------------------------
+// interp()
+//-----------------------------------------------------------------------------
+LLVector3 LLKeyframeMotion::PositionCurve::interp(F32 u, PositionKey& before, PositionKey& after)
+{
+ switch (mInterpolationType)
+ {
+ case IT_STEP:
+ return before.mPosition;
+ default:
+ case IT_LINEAR:
+ case IT_SPLINE:
+ return lerp(before.mPosition, after.mPosition, u);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// JointMotion class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// JointMotion::update()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::JointMotion::update(LLJointState* joint_state, F32 time, F32 duration)
+{
+ // this value being 0 is the cause of https://jira.lindenlab.com/browse/SL-22678 but I haven't
+ // managed to get a stack to see how it got here. Testing for 0 here will stop the crash.
+ if ( joint_state == 0 )
+ {
+ return;
+ };
+
+ U32 usage = joint_state->getUsage();
+
+ //-------------------------------------------------------------------------
+ // update scale component of joint state
+ //-------------------------------------------------------------------------
+ if ((usage & LLJointState::SCALE) && mScaleCurve.mNumKeys)
+ {
+ joint_state->setScale( mScaleCurve.getValue( time, duration ) );
+ }
+
+ //-------------------------------------------------------------------------
+ // update rotation component of joint state
+ //-------------------------------------------------------------------------
+ if ((usage & LLJointState::ROT) && mRotationCurve.mNumKeys)
+ {
+ joint_state->setRotation( mRotationCurve.getValue( time, duration ) );
+ }
+
+ //-------------------------------------------------------------------------
+ // update position component of joint state
+ //-------------------------------------------------------------------------
+ if ((usage & LLJointState::POS) && mPositionCurve.mNumKeys)
+ {
+ joint_state->setPosition( mPositionCurve.getValue( time, duration ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::LLKeyframeMotion( const LLUUID &id) : LLMotion(id)
+{
+ mJointMotionList = NULL;
+ mJointStates = NULL;
+ mLastSkeletonSerialNum = 0;
+ mLastLoopedTime = 0.f;
+ mLastUpdateTime = 0.f;
+ mAssetStatus = ASSET_UNDEFINED;
+ mPelvisp = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::~LLKeyframeMotion()
+{
+ if (mJointStates)
+ {
+ delete [] mJointStates;
+ }
+ mConstraints.deleteAllData();
+}
+
+//-----------------------------------------------------------------------------
+// create()
+//-----------------------------------------------------------------------------
+LLMotion *LLKeyframeMotion::create(const LLUUID &id)
+{
+ return new LLKeyframeMotion(id);
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ LLUUID* character_id;
+
+ // asset already loaded?
+ switch(mAssetStatus)
+ {
+ case ASSET_NEEDS_FETCH:
+ // request asset
+ mAssetStatus = ASSET_FETCHED;
+
+ character_id = new LLUUID(mCharacter->getID());
+ gAssetStorage->getAssetData(mID,
+ LLAssetType::AT_ANIMATION,
+ onLoadComplete,
+ (void *)character_id,
+ FALSE);
+
+ return STATUS_HOLD;
+ case ASSET_FETCHED:
+ return STATUS_HOLD;
+ case ASSET_FETCH_FAILED:
+ return STATUS_FAILURE;
+ case ASSET_LOADED:
+ return STATUS_SUCCESS;
+ default:
+ // we don't know what state the asset is in yet, so keep going
+ // check keyframe cache first then static vfs then asset request
+ break;
+ }
+
+ LLKeyframeMotion::JointMotionList* joint_motion_list = LLKeyframeDataCache::getKeyframeData(getID());
+
+ if(joint_motion_list)
+ {
+ // motion already existed in cache, so grab it
+ mJointMotionList = joint_motion_list;
+
+ // don't forget to allocate joint states
+ mJointStates = new LLJointState[mJointMotionList->mNumJointMotions];
+
+ // set up joint states to point to character joints
+ for(U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ if (LLJoint *jointp = mCharacter->getJoint(mJointMotionList->mJointMotionArray[i].mJointName))
+ {
+ mJointStates[i].setJoint(jointp);
+ mJointStates[i].setUsage(mJointMotionList->mJointMotionArray[i].mUsage);
+ mJointStates[i].setPriority(joint_motion_list->mJointMotionArray[i].mPriority);
+ }
+ }
+ mAssetStatus = ASSET_LOADED;
+ setupPose();
+ return STATUS_SUCCESS;
+ }
+
+ //-------------------------------------------------------------------------
+ // Load named file by concatenating the character prefix with the motion name.
+ // Load data into a buffer to be parsed.
+ //-------------------------------------------------------------------------
+ U8 *anim_data;
+ S32 anim_file_size;
+
+ if (!sVFS)
+ {
+ llerrs << "Must call LLKeyframeMotion::setVFS() first before loading a keyframe file!" << llendl;
+ }
+
+ BOOL success = FALSE;
+ LLVFile* anim_file = new LLVFile(sVFS, mID, LLAssetType::AT_ANIMATION);
+ if (!anim_file || !anim_file->getSize())
+ {
+ delete anim_file;
+ anim_file = NULL;
+
+ // request asset over network on next call to load
+ mAssetStatus = ASSET_NEEDS_FETCH;
+
+ return STATUS_HOLD;
+ }
+ else
+ {
+ anim_file_size = anim_file->getSize();
+ anim_data = new U8[anim_file_size];
+ success = anim_file->read(anim_data, anim_file_size); /*Flawfinder: ignore*/
+ delete anim_file;
+ anim_file = NULL;
+ }
+
+ if (!success)
+ {
+ llwarns << "Can't open animation file " << mID << llendl;
+ mAssetStatus = ASSET_FETCH_FAILED;
+ return STATUS_FAILURE;
+ }
+
+ lldebugs << "Loading keyframe data for: " << getName() << ":" << getID() << " (" << anim_file_size << " bytes)" << llendl;
+
+ LLDataPackerBinaryBuffer dp(anim_data, anim_file_size);
+
+ if (!deserialize(dp))
+ {
+ llwarns << "Failed to decode asset for animation " << getName() << ":" << getID() << llendl;
+ mAssetStatus = ASSET_FETCH_FAILED;
+ return STATUS_FAILURE;
+ }
+
+ delete []anim_data;
+
+ mAssetStatus = ASSET_LOADED;
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// setupPose()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::setupPose()
+{
+ // add all valid joint states to the pose
+ U32 jm;
+ for (jm=0; jm<mJointMotionList->mNumJointMotions; jm++)
+ {
+ if ( mJointStates[jm].getJoint() )
+ {
+ addJointState( &mJointStates[jm] );
+ }
+ }
+
+ // initialize joint constraints
+ for (JointConstraintSharedData* shared_constraintp = mJointMotionList->mConstraints.getFirstData();
+ shared_constraintp;
+ shared_constraintp = mJointMotionList->mConstraints.getNextData())
+ {
+ JointConstraint* constraintp = new JointConstraint(shared_constraintp);
+ initializeConstraint(constraintp);
+ mConstraints.addData(constraintp);
+ }
+
+ if (mJointMotionList->mConstraints.getLength())
+ {
+ mPelvisp = mCharacter->getJoint("mPelvis");
+ if (!mPelvisp)
+ {
+ return FALSE;
+ }
+ }
+
+ // setup loop keys
+ setLoopIn(mJointMotionList->mLoopInPoint);
+ setLoopOut(mJointMotionList->mLoopOutPoint);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::onActivate()
+{
+ // If the keyframe anim has an associated emote, trigger it.
+ if( mEmoteName.length() > 0 )
+ {
+ mCharacter->startMotion( gAnimLibrary.stringToAnimState(mEmoteName.c_str()) );
+ }
+
+ mLastLoopedTime = 0.f;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ llassert(time >= 0.f);
+
+ if (mJointMotionList->mLoop)
+ {
+ if (mJointMotionList->mDuration == 0.0f)
+ {
+ time = 0.f;
+ mLastLoopedTime = 0.0f;
+ }
+ else if (mStopped)
+ {
+ mLastLoopedTime = llmin(mJointMotionList->mDuration, mLastLoopedTime + time - mLastUpdateTime);
+ }
+ else if (time > mJointMotionList->mLoopOutPoint)
+ {
+ if ((mJointMotionList->mLoopOutPoint - mJointMotionList->mLoopInPoint) == 0.f)
+ {
+ mLastLoopedTime = mJointMotionList->mLoopOutPoint;
+ }
+ else
+ {
+ mLastLoopedTime = mJointMotionList->mLoopInPoint +
+ fmod(time - mJointMotionList->mLoopOutPoint,
+ mJointMotionList->mLoopOutPoint - mJointMotionList->mLoopInPoint);
+ }
+ }
+ else
+ {
+ mLastLoopedTime = time;
+ }
+ }
+ else
+ {
+ mLastLoopedTime = time;
+ }
+
+ applyKeyframes(mLastLoopedTime);
+
+ applyConstraints(mLastLoopedTime, joint_mask);
+
+ mLastUpdateTime = time;
+
+ return mLastLoopedTime <= mJointMotionList->mDuration;
+}
+
+//-----------------------------------------------------------------------------
+// applyKeyframes()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::applyKeyframes(F32 time)
+{
+ U32 i;
+ for (i=0; i<mJointMotionList->mNumJointMotions; i++)
+ {
+ mJointMotionList->mJointMotionArray[i].update(
+ &mJointStates[i],
+ time,
+ mJointMotionList->mDuration );
+ }
+
+ LLJoint::JointPriority* pose_priority = (LLJoint::JointPriority* )mCharacter->getAnimationData("Hand Pose Priority");
+ if (pose_priority)
+ {
+ if (mJointMotionList->mMaxPriority >= *pose_priority)
+ {
+ mCharacter->setAnimationData("Hand Pose", &mJointMotionList->mHandPose);
+ mCharacter->setAnimationData("Hand Pose Priority", &mJointMotionList->mMaxPriority);
+ }
+ }
+ else
+ {
+ mCharacter->setAnimationData("Hand Pose", &mJointMotionList->mHandPose);
+ mCharacter->setAnimationData("Hand Pose Priority", &mJointMotionList->mMaxPriority);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// applyConstraints()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::applyConstraints(F32 time, U8* joint_mask)
+{
+ //TODO: investigate replacing spring simulation with critically damped motion
+
+ // re-init constraints if skeleton has changed
+ if (mCharacter->getSkeletonSerialNum() != mLastSkeletonSerialNum)
+ {
+ mLastSkeletonSerialNum = mCharacter->getSkeletonSerialNum();
+ for (JointConstraint* constraintp = mConstraints.getFirstData();
+ constraintp;
+ constraintp = mConstraints.getNextData())
+ {
+ initializeConstraint(constraintp);
+ }
+ }
+
+ // apply constraints
+ for (JointConstraint* constraintp = mConstraints.getFirstData();
+ constraintp;
+ constraintp = mConstraints.getNextData())
+ {
+ applyConstraint(constraintp, time, joint_mask);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::onDeactivate()
+{
+ for (JointConstraint* constraintp = mConstraints.getFirstData();
+ constraintp;
+ constraintp = mConstraints.getNextData())
+ {
+ deactivateConstraint(constraintp);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setStopTime()
+//-----------------------------------------------------------------------------
+// time is in seconds since character creation
+void LLKeyframeMotion::setStopTime(F32 time)
+{
+ LLMotion::setStopTime(time);
+
+ if (mJointMotionList->mLoop && mJointMotionList->mLoopOutPoint != mJointMotionList->mDuration)
+ {
+ F32 start_loop_time = mActivationTimestamp + mJointMotionList->mLoopInPoint;
+ F32 loop_fraction_time;
+ if (mJointMotionList->mLoopOutPoint == mJointMotionList->mLoopInPoint)
+ {
+ loop_fraction_time = 0.f;
+ }
+ else
+ {
+ loop_fraction_time = fmod(time - start_loop_time,
+ mJointMotionList->mLoopOutPoint - mJointMotionList->mLoopInPoint);
+ }
+ mStopTimestamp = llmax(time,
+ (time - loop_fraction_time) + (mJointMotionList->mDuration - mJointMotionList->mLoopInPoint) - getEaseOutDuration());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// initializeConstraint()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::initializeConstraint(JointConstraint* constraint)
+{
+ JointConstraintSharedData *shared_data = constraint->mSharedData;
+
+ S32 joint_num;
+ LLVector3 source_pos = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset);
+ LLJoint* cur_joint = mJointStates[shared_data->mJointStateIndices[0]].getJoint();
+
+ F32 source_pos_offset = dist_vec(source_pos, cur_joint->getWorldPosition());
+
+ constraint->mTotalLength = constraint->mJointLengths[0] = dist_vec(cur_joint->getParent()->getWorldPosition(), source_pos);
+
+ // grab joint lengths
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ cur_joint = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint();
+ if (!cur_joint)
+ {
+ return;
+ }
+ constraint->mJointLengths[joint_num] = dist_vec(cur_joint->getWorldPosition(), cur_joint->getParent()->getWorldPosition());
+ constraint->mTotalLength += constraint->mJointLengths[joint_num];
+ }
+
+ // store fraction of total chain length so we know how to shear the entire chain towards the goal position
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ constraint->mJointLengthFractions[joint_num] = constraint->mJointLengths[joint_num] / constraint->mTotalLength;
+ }
+
+ // add last step in chain, from final joint to constraint position
+ constraint->mTotalLength += source_pos_offset;
+
+ constraint->mSourceVolume = mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume);
+ constraint->mTargetVolume = mCharacter->findCollisionVolume(shared_data->mTargetConstraintVolume);
+}
+
+//-----------------------------------------------------------------------------
+// activateConstraint()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::activateConstraint(JointConstraint* constraint)
+{
+ JointConstraintSharedData *shared_data = constraint->mSharedData;
+ constraint->mActive = TRUE;
+ S32 joint_num;
+
+ // grab ground position if we need to
+ if (shared_data->mConstraintTargetType == TYPE_GROUND)
+ {
+ LLVector3 source_pos = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset);
+ LLVector3 ground_pos_agent;
+ mCharacter->getGround(source_pos, ground_pos_agent, constraint->mGroundNorm);
+ constraint->mGroundPos = mCharacter->getPosGlobalFromAgent(ground_pos_agent + shared_data->mTargetConstraintOffset);
+ }
+
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ LLJoint* cur_joint = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint();
+ constraint->mPositions[joint_num] = (cur_joint->getWorldPosition() - mPelvisp->getWorldPosition()) * ~mPelvisp->getWorldRotation();
+ }
+
+ constraint->mWeight = 1.f;
+}
+
+//-----------------------------------------------------------------------------
+// deactivateConstraint()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::deactivateConstraint(JointConstraint *constraintp)
+{
+ if (constraintp->mSourceVolume)
+ {
+ constraintp->mSourceVolume->mUpdateXform = FALSE;
+ }
+
+ if (!constraintp->mSharedData->mConstraintTargetType == TYPE_GROUND)
+ {
+ if (constraintp->mTargetVolume)
+ {
+ constraintp->mTargetVolume->mUpdateXform = FALSE;
+ }
+ }
+ constraintp->mActive = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// applyConstraint()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8* joint_mask)
+{
+ JointConstraintSharedData *shared_data = constraint->mSharedData;
+ if (!shared_data) return;
+
+ LLVector3 positions[MAX_CHAIN_LENGTH];
+ const F32* joint_lengths = constraint->mJointLengths;
+ LLVector3 velocities[MAX_CHAIN_LENGTH - 1];
+ LLQuaternion old_rots[MAX_CHAIN_LENGTH];
+ S32 joint_num;
+ LLJoint* cur_joint;
+
+ if (time < shared_data->mEaseInStartTime)
+ {
+ return;
+ }
+
+ if (time > shared_data->mEaseOutStopTime)
+ {
+ if (constraint->mActive)
+ {
+ deactivateConstraint(constraint);
+ }
+ return;
+ }
+
+ if (!constraint->mActive || time < shared_data->mEaseInStopTime)
+ {
+ activateConstraint(constraint);
+ }
+
+ LLJoint* root_joint = mJointStates[shared_data->mJointStateIndices[shared_data->mChainLength]].getJoint();
+ LLVector3 root_pos = root_joint->getWorldPosition();
+// LLQuaternion root_rot =
+ root_joint->getParent()->getWorldRotation();
+// LLQuaternion inv_root_rot = ~root_rot;
+
+// LLVector3 current_source_pos = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset);
+
+ //apply underlying keyframe animation to get nominal "kinematic" joint positions
+ for (joint_num = 0; joint_num <= shared_data->mChainLength; joint_num++)
+ {
+ cur_joint = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint();
+ if (joint_mask[cur_joint->getJointNum()] >= (0xff >> (7 - getPriority())))
+ {
+ // skip constraint
+ return;
+ }
+ old_rots[joint_num] = cur_joint->getRotation();
+ cur_joint->setRotation(mJointStates[shared_data->mJointStateIndices[joint_num]].getRotation());
+ }
+
+
+ LLVector3 keyframe_source_pos = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset);
+ LLVector3 target_pos;
+
+ switch(shared_data->mConstraintTargetType)
+ {
+ case TYPE_GROUND:
+ target_pos = mCharacter->getPosAgentFromGlobal(constraint->mGroundPos);
+// llinfos << "Target Pos " << constraint->mGroundPos << " on " << mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume)->getName() << llendl;
+ break;
+ case TYPE_BODY:
+ target_pos = mCharacter->getVolumePos(shared_data->mTargetConstraintVolume, shared_data->mTargetConstraintOffset);
+ break;
+ default:
+ break;
+ }
+
+ LLVector3 norm;
+ LLJoint *source_jointp = NULL;
+ LLJoint *target_jointp = NULL;
+
+ if (shared_data->mConstraintType == TYPE_PLANE)
+ {
+ switch(shared_data->mConstraintTargetType)
+ {
+ case TYPE_GROUND:
+ norm = constraint->mGroundNorm;
+ break;
+ case TYPE_BODY:
+ target_jointp = mCharacter->findCollisionVolume(shared_data->mTargetConstraintVolume);
+ if (target_jointp)
+ {
+ // FIXME: do proper normal calculation for stretched spheres (inverse transpose)
+ norm = target_pos - target_jointp->getWorldPosition();
+ }
+
+ if (norm.isExactlyZero())
+ {
+ source_jointp = mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume);
+ norm = -1.f * shared_data->mSourceConstraintOffset;
+ if (source_jointp)
+ {
+ norm = norm * source_jointp->getWorldRotation();
+ }
+ }
+ norm.normVec();
+ break;
+ default:
+ norm.clearVec();
+ break;
+ }
+
+ target_pos = keyframe_source_pos + (norm * ((target_pos - keyframe_source_pos) * norm));
+ }
+
+ if (constraint->mSharedData->mChainLength != 0 &&
+ dist_vec_squared(root_pos, target_pos) * 0.95f > constraint->mTotalLength * constraint->mTotalLength)
+ {
+ constraint->mWeight = lerp(constraint->mWeight, 0.f, LLCriticalDamp::getInterpolant(0.1f));
+ }
+ else
+ {
+ constraint->mWeight = lerp(constraint->mWeight, 1.f, LLCriticalDamp::getInterpolant(0.3f));
+ }
+
+ F32 weight = constraint->mWeight * ((shared_data->mEaseOutStopTime == 0.f) ? 1.f :
+ llmin(clamp_rescale(time, shared_data->mEaseInStartTime, shared_data->mEaseInStopTime, 0.f, 1.f),
+ clamp_rescale(time, shared_data->mEaseOutStartTime, shared_data->mEaseOutStopTime, 1.f, 0.f)));
+
+ LLVector3 source_to_target = target_pos - keyframe_source_pos;
+
+ S32 max_iteration_count = llround(clamp_rescale(
+ mCharacter->getPixelArea(),
+ MAX_PIXEL_AREA_CONSTRAINTS,
+ MIN_PIXEL_AREA_CONSTRAINTS,
+ (F32)MAX_ITERATIONS,
+ (F32)MIN_ITERATIONS));
+
+ if (shared_data->mChainLength)
+ {
+ LLQuaternion end_rot = mJointStates[shared_data->mJointStateIndices[0]].getJoint()->getWorldRotation();
+
+ // slam start and end of chain to the proper positions (rest of chain stays put)
+ positions[0] = lerp(keyframe_source_pos, target_pos, weight);
+ positions[shared_data->mChainLength] = root_pos;
+
+ // grab keyframe-specified positions of joints
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ LLVector3 kinematic_position = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint()->getWorldPosition() +
+ (source_to_target * constraint->mJointLengthFractions[joint_num]);
+
+ // convert intermediate joint positions to world coordinates
+ positions[joint_num] = ( constraint->mPositions[joint_num] * mPelvisp->getWorldRotation()) + mPelvisp->getWorldPosition();
+ F32 time_constant = 1.f / clamp_rescale(constraint->mFixupDistanceRMS, 0.f, 0.5f, 0.2f, 8.f);
+// llinfos << "Interpolant " << LLCriticalDamp::getInterpolant(time_constant, FALSE) << " and fixup distance " << constraint->mFixupDistanceRMS << " on " << mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume)->getName() << llendl;
+ positions[joint_num] = lerp(positions[joint_num], kinematic_position,
+ LLCriticalDamp::getInterpolant(time_constant, FALSE));
+ }
+
+ S32 iteration_count;
+ for (iteration_count = 0; iteration_count < max_iteration_count; iteration_count++)
+ {
+ S32 num_joints_finished = 0;
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ // constraint to child
+ LLVector3 acceleration = (positions[joint_num - 1] - positions[joint_num]) *
+ (dist_vec(positions[joint_num], positions[joint_num - 1]) - joint_lengths[joint_num - 1]) * JOINT_LENGTH_K;
+ // constraint to parent
+ acceleration += (positions[joint_num + 1] - positions[joint_num]) *
+ (dist_vec(positions[joint_num + 1], positions[joint_num]) - joint_lengths[joint_num]) * JOINT_LENGTH_K;
+
+ if (acceleration.magVecSquared() < MIN_ACCELERATION_SQUARED)
+ {
+ num_joints_finished++;
+ }
+
+ velocities[joint_num - 1] = velocities[joint_num - 1] * 0.7f;
+ positions[joint_num] += velocities[joint_num - 1] + (acceleration * 0.5f);
+ velocities[joint_num - 1] += acceleration;
+ }
+
+ if ((iteration_count >= MIN_ITERATION_COUNT) &&
+ (num_joints_finished == shared_data->mChainLength - 1))
+ {
+// llinfos << iteration_count << " iterations on " <<
+// mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume)->getName() << llendl;
+ break;
+ }
+ }
+
+ for (joint_num = shared_data->mChainLength; joint_num > 0; joint_num--)
+ {
+ LLQuaternion parent_rot = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint()->getParent()->getWorldRotation();
+ cur_joint = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint();
+ LLJoint* child_joint = mJointStates[shared_data->mJointStateIndices[joint_num - 1]].getJoint();
+
+ LLQuaternion cur_rot = cur_joint->getWorldRotation();
+ LLQuaternion fixup_rot;
+
+ LLVector3 target_at = positions[joint_num - 1] - positions[joint_num];
+ LLVector3 current_at;
+
+ // at bottom of chain, use point on collision volume, not joint position
+ if (joint_num == 1)
+ {
+ current_at = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset) -
+ cur_joint->getWorldPosition();
+ }
+ else
+ {
+ current_at = child_joint->getPosition() * cur_rot;
+ }
+ fixup_rot.shortestArc(current_at, target_at);
+
+ LLQuaternion target_rot = cur_rot * fixup_rot;
+ target_rot = target_rot * ~parent_rot;
+
+ if (weight != 1.f)
+ {
+ LLQuaternion cur_rot = mJointStates[shared_data->mJointStateIndices[joint_num]].getRotation();
+ target_rot = nlerp(weight, cur_rot, target_rot);
+ }
+
+ mJointStates[shared_data->mJointStateIndices[joint_num]].setRotation(target_rot);
+ cur_joint->setRotation(target_rot);
+ }
+
+ LLJoint* end_joint = mJointStates[shared_data->mJointStateIndices[0]].getJoint();
+ LLQuaternion end_local_rot = end_rot * ~end_joint->getParent()->getWorldRotation();
+
+ if (weight == 1.f)
+ {
+ mJointStates[shared_data->mJointStateIndices[0]].setRotation(end_local_rot);
+ }
+ else
+ {
+ LLQuaternion cur_rot = mJointStates[shared_data->mJointStateIndices[0]].getRotation();
+ mJointStates[shared_data->mJointStateIndices[0]].setRotation(nlerp(weight, cur_rot, end_local_rot));
+ }
+
+ // save simulated positions in pelvis-space and calculate total fixup distance
+ constraint->mFixupDistanceRMS = 0.f;
+ F32 delta_time = llmax(0.02f, llabs(time - mLastUpdateTime));
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ LLVector3 new_pos = (positions[joint_num] - mPelvisp->getWorldPosition()) * ~mPelvisp->getWorldRotation();
+ constraint->mFixupDistanceRMS += dist_vec_squared(new_pos, constraint->mPositions[joint_num]) / delta_time;
+ constraint->mPositions[joint_num] = new_pos;
+ }
+ constraint->mFixupDistanceRMS *= 1.f / (constraint->mTotalLength * (F32)(shared_data->mChainLength - 1));
+ constraint->mFixupDistanceRMS = fsqrtf(constraint->mFixupDistanceRMS);
+
+ //reset old joint rots
+ for (joint_num = 0; joint_num <= shared_data->mChainLength; joint_num++)
+ {
+ mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint()->setRotation(old_rots[joint_num]);
+ }
+ }
+ // simple positional constraint (pelvis only)
+ else if (mJointStates[shared_data->mJointStateIndices[0]].getUsage() & LLJointState::POS)
+ {
+ LLVector3 delta = source_to_target * weight;
+ LLJointState* current_joint_statep = &mJointStates[shared_data->mJointStateIndices[0]];
+ LLQuaternion parent_rot = current_joint_statep->getJoint()->getParent()->getWorldRotation();
+ delta = delta * ~parent_rot;
+ current_joint_statep->setPosition(current_joint_statep->getJoint()->getPosition() + delta);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// deserialize()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
+{
+ BOOL old_version = FALSE;
+ mJointMotionList = new LLKeyframeMotion::JointMotionList;
+ mJointMotionList->mNumJointMotions = 0;
+
+ //-------------------------------------------------------------------------
+ // get base priority
+ //-------------------------------------------------------------------------
+ S32 temp_priority;
+ U16 version;
+ U16 sub_version;
+
+ if (!dp.unpackU16(version, "version"))
+ {
+ llwarns << "can't read version number" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackU16(sub_version, "sub_version"))
+ {
+ llwarns << "can't read sub version number" << llendl;
+ return FALSE;
+ }
+
+ if (version == 0 && sub_version == 1)
+ {
+ old_version = TRUE;
+ }
+ else if (version != KEYFRAME_MOTION_VERSION || sub_version != KEYFRAME_MOTION_SUBVERSION)
+ {
+#if LL_RELEASE
+ llwarns << "Bad animation version " << version << "." << sub_version << llendl;
+ return FALSE;
+#else
+ llerrs << "Bad animation version " << version << "." << sub_version << llendl;
+#endif
+ }
+
+ if (!dp.unpackS32(temp_priority, "base_priority"))
+ {
+ llwarns << "can't read priority" << llendl;
+ return FALSE;
+ }
+ mJointMotionList->mBasePriority = (LLJoint::JointPriority) temp_priority;
+
+ if (mJointMotionList->mBasePriority >= LLJoint::ADDITIVE_PRIORITY)
+ {
+ mJointMotionList->mBasePriority = (LLJoint::JointPriority)((int)LLJoint::ADDITIVE_PRIORITY-1);
+ mJointMotionList->mMaxPriority = mJointMotionList->mBasePriority;
+ }
+
+ //-------------------------------------------------------------------------
+ // get duration
+ //-------------------------------------------------------------------------
+ if (!dp.unpackF32(mJointMotionList->mDuration, "duration"))
+ {
+ llwarns << "can't read duration" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // get emote (optional)
+ //-------------------------------------------------------------------------
+ char read_string[128];
+ if (!dp.unpackString(read_string, "emote_name"))
+ {
+ llwarns << "can't read optional_emote_animation" << llendl;
+ return FALSE;
+ }
+
+ mEmoteName.assign( read_string );
+
+ //-------------------------------------------------------------------------
+ // get loop
+ //-------------------------------------------------------------------------
+ if (!dp.unpackF32(mJointMotionList->mLoopInPoint, "loop_in_point"))
+ {
+ llwarns << "can't read loop point" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(mJointMotionList->mLoopOutPoint, "loop_out_point"))
+ {
+ llwarns << "can't read loop point" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackS32(mJointMotionList->mLoop, "loop"))
+ {
+ llwarns << "can't read loop" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // get easeIn and easeOut
+ //-------------------------------------------------------------------------
+ if (!dp.unpackF32(mJointMotionList->mEaseInDuration, "ease_in_duration"))
+ {
+ llwarns << "can't read easeIn" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(mJointMotionList->mEaseOutDuration, "ease_out_duration"))
+ {
+ llwarns << "can't read easeOut" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // get hand pose
+ //-------------------------------------------------------------------------
+ U32 word;
+ if (!dp.unpackU32(word, "hand_pose"))
+ {
+ llwarns << "can't read hand pose" << llendl;
+ return FALSE;
+ }
+ mJointMotionList->mHandPose = (LLHandMotion::eHandPose)word;
+
+ //-------------------------------------------------------------------------
+ // get number of joint motions
+ //-------------------------------------------------------------------------
+ if (!dp.unpackU32(mJointMotionList->mNumJointMotions, "num_joints"))
+ {
+ llwarns << "can't read number of joints" << llendl;
+ return FALSE;
+ }
+
+ if (mJointMotionList->mNumJointMotions == 0)
+ {
+ llwarns << "no joints in animation" << llendl;
+ return FALSE;
+ }
+ else if (mJointMotionList->mNumJointMotions > LL_CHARACTER_MAX_JOINTS)
+ {
+ llwarns << "too many joints in animation" << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray = new JointMotion[mJointMotionList->mNumJointMotions];
+ mJointStates = new LLJointState[mJointMotionList->mNumJointMotions];
+
+ if (!mJointMotionList->mJointMotionArray)
+ {
+ mJointMotionList->mDuration = 0.0f;
+ mJointMotionList->mEaseInDuration = 0.0f;
+ mJointMotionList->mEaseOutDuration = 0.0f;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize joint motions
+ //-------------------------------------------------------------------------
+ S32 k;
+ for(U32 i=0; i<mJointMotionList->mNumJointMotions; ++i)
+ {
+ if (!dp.unpackString(read_string, "joint_name"))
+ {
+ llwarns << "can't read joint name" << llendl;
+ return FALSE;
+ }
+
+ //---------------------------------------------------------------------
+ // find the corresponding joint
+ //---------------------------------------------------------------------
+ LLJoint *joint = mCharacter->getJoint( read_string );
+ if (joint)
+ {
+// llinfos << " joint: " << read_string << llendl;
+ }
+ else
+ {
+ llwarns << "joint not found: " << read_string << llendl;
+ //return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray[i].mJointName = read_string;
+ mJointStates[i].setJoint( joint );
+ mJointStates[i].setUsage( 0 );
+
+ //---------------------------------------------------------------------
+ // get joint priority
+ //---------------------------------------------------------------------
+ S32 joint_priority;
+ if (!dp.unpackS32(joint_priority, "joint_priority"))
+ {
+ llwarns << "can't read joint priority." << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray[i].mPriority = (LLJoint::JointPriority)joint_priority;
+ if (joint_priority != LLJoint::USE_MOTION_PRIORITY &&
+ joint_priority > mJointMotionList->mMaxPriority)
+ {
+ mJointMotionList->mMaxPriority = (LLJoint::JointPriority)joint_priority;
+ }
+
+ mJointStates[i].setPriority((LLJoint::JointPriority)joint_priority);
+
+ //---------------------------------------------------------------------
+ // scan rotation curve header
+ //---------------------------------------------------------------------
+ if (!dp.unpackS32(mJointMotionList->mJointMotionArray[i].mRotationCurve.mNumKeys, "num_rot_keys"))
+ {
+ llwarns << "can't read number of rotation keys" << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray[i].mRotationCurve.mInterpolationType = IT_LINEAR;
+ if (mJointMotionList->mJointMotionArray[i].mRotationCurve.mNumKeys != 0)
+ {
+ mJointStates[i].setUsage(mJointStates[i].getUsage() | LLJointState::ROT );
+ }
+
+ //---------------------------------------------------------------------
+ // scan rotation curve keys
+ //---------------------------------------------------------------------
+ RotationCurve *rCurve = &mJointMotionList->mJointMotionArray[i].mRotationCurve;
+
+ for (k = 0; k < mJointMotionList->mJointMotionArray[i].mRotationCurve.mNumKeys; k++)
+ {
+ F32 time;
+ U16 time_short;
+
+ if (old_version)
+ {
+ if (!dp.unpackF32(time, "time"))
+ {
+ llwarns << "can't read rotation key (" << k << ")" << llendl;
+ return FALSE;
+ }
+
+ }
+ else
+ {
+ if (!dp.unpackU16(time_short, "time"))
+ {
+ llwarns << "can't read rotation key (" << k << ")" << llendl;
+ return FALSE;
+ }
+
+ time = U16_to_F32(time_short, 0.f, mJointMotionList->mDuration);
+ }
+
+ RotationKey *rot_key = new RotationKey;
+ rot_key->mTime = time;
+ LLVector3 rot_angles;
+ U16 x, y, z;
+
+ BOOL success = TRUE;
+
+ if (old_version)
+ {
+ success = dp.unpackVector3(rot_angles, "rot_angles");
+
+ LLQuaternion::Order ro = StringToOrder("ZYX");
+ rot_key->mRotation = mayaQ(rot_angles.mV[VX], rot_angles.mV[VY], rot_angles.mV[VZ], ro);
+ }
+ else
+ {
+ success &= dp.unpackU16(x, "rot_angle_x");
+ success &= dp.unpackU16(y, "rot_angle_y");
+ success &= dp.unpackU16(z, "rot_angle_z");
+
+ LLVector3 rot_vec;
+ rot_vec.mV[VX] = U16_to_F32(x, -1.f, 1.f);
+ rot_vec.mV[VY] = U16_to_F32(y, -1.f, 1.f);
+ rot_vec.mV[VZ] = U16_to_F32(z, -1.f, 1.f);
+ rot_key->mRotation.unpackFromVector3(rot_vec);
+ }
+
+ if (!success)
+ {
+ llwarns << "can't read rotation key (" << k << ")" << llendl;
+ return FALSE;
+ }
+
+ rCurve->mKeys[time] = rot_key;
+ }
+
+ //---------------------------------------------------------------------
+ // scan position curve header
+ //---------------------------------------------------------------------
+ if (!dp.unpackS32(mJointMotionList->mJointMotionArray[i].mPositionCurve.mNumKeys, "num_pos_keys"))
+ {
+ llwarns << "can't read number of position keys" << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray[i].mPositionCurve.mInterpolationType = IT_LINEAR;
+ if (mJointMotionList->mJointMotionArray[i].mPositionCurve.mNumKeys != 0)
+ {
+ mJointStates[i].setUsage(mJointStates[i].getUsage() | LLJointState::POS );
+ }
+
+ //---------------------------------------------------------------------
+ // scan position curve keys
+ //---------------------------------------------------------------------
+ PositionCurve *pCurve = &mJointMotionList->mJointMotionArray[i].mPositionCurve;
+ BOOL is_pelvis = mJointMotionList->mJointMotionArray[i].mJointName == "mPelvis";
+ for (k = 0; k < mJointMotionList->mJointMotionArray[i].mPositionCurve.mNumKeys; k++)
+ {
+ U16 time_short;
+ PositionKey* pos_key = new PositionKey;
+
+ if (old_version)
+ {
+ if (!dp.unpackF32(pos_key->mTime, "time"))
+ {
+ llwarns << "can't read position key (" << k << ")" << llendl;
+ delete pos_key;
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!dp.unpackU16(time_short, "time"))
+ {
+ llwarns << "can't read position key (" << k << ")" << llendl;
+ delete pos_key;
+ return FALSE;
+ }
+
+ pos_key->mTime = U16_to_F32(time_short, 0.f, mJointMotionList->mDuration);
+ }
+
+ BOOL success = TRUE;
+
+ if (old_version)
+ {
+ success = dp.unpackVector3(pos_key->mPosition, "pos");
+ }
+ else
+ {
+ U16 x, y, z;
+
+ success &= dp.unpackU16(x, "pos_x");
+ success &= dp.unpackU16(y, "pos_y");
+ success &= dp.unpackU16(z, "pos_z");
+
+ pos_key->mPosition.mV[VX] = U16_to_F32(x, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ pos_key->mPosition.mV[VY] = U16_to_F32(y, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ pos_key->mPosition.mV[VZ] = U16_to_F32(z, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ }
+
+ if (!success)
+ {
+ llwarns << "can't read position key (" << k << ")" << llendl;
+ delete pos_key;
+ return FALSE;
+ }
+
+ pCurve->mKeys[pos_key->mTime] = pos_key;
+
+ if (is_pelvis)
+ {
+ mJointMotionList->mPelvisBBox.addPoint(pos_key->mPosition);
+ }
+ }
+
+ mJointMotionList->mJointMotionArray[i].mUsage = mJointStates[i].getUsage();
+ }
+
+ //-------------------------------------------------------------------------
+ // get number of constraints
+ //-------------------------------------------------------------------------
+ S32 num_constraints = 0;
+ if (!dp.unpackS32(num_constraints, "num_constraints"))
+ {
+ llwarns << "can't read number of constraints" << llendl;
+ return FALSE;
+ }
+
+ if (num_constraints > MAX_CONSTRAINTS)
+ {
+ llwarns << "Too many constraints...ignoring" << llendl;
+ }
+ else
+ {
+ //-------------------------------------------------------------------------
+ // get constraints
+ //-------------------------------------------------------------------------
+ std::string str;
+ for(S32 i = 0; i < num_constraints; ++i)
+ {
+ // read in constraint data
+ JointConstraintSharedData* constraintp = new JointConstraintSharedData;
+ U8 byte = 0;
+
+ if (!dp.unpackU8(byte, "chain_length"))
+ {
+ llwarns << "can't read constraint chain length" << llendl;
+ return FALSE;
+ }
+ constraintp->mChainLength = (S32) byte;
+
+ if (!dp.unpackU8(byte, "constraint_type"))
+ {
+ llwarns << "can't read constraint type" << llendl;
+ return FALSE;
+ }
+ constraintp->mConstraintType = (EConstraintType)byte;
+
+ if (!dp.unpackBinaryDataFixed((unsigned char*)read_string, 16, "source_volume"))
+ {
+ llwarns << "can't read source volume name" << llendl;
+ return FALSE;
+ }
+
+ str.assign(read_string);
+ constraintp->mSourceConstraintVolume = mCharacter->getCollisionVolumeID(str);
+
+ if (!dp.unpackVector3(constraintp->mSourceConstraintOffset, "source_offset"))
+ {
+ llwarns << "can't read constraint source offset" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackBinaryDataFixed((unsigned char*)read_string, 16, "target_volume"))
+ {
+ llwarns << "can't read target volume name" << llendl;
+ return FALSE;
+ }
+
+ str.assign(read_string);
+ if (str == "GROUND")
+ {
+ // constrain to ground
+ constraintp->mConstraintTargetType = TYPE_GROUND;
+ }
+ else
+ {
+ constraintp->mConstraintTargetType = TYPE_BODY;
+ constraintp->mTargetConstraintVolume = mCharacter->getCollisionVolumeID(str);
+ }
+
+ if (!dp.unpackVector3(constraintp->mTargetConstraintOffset, "target_offset"))
+ {
+ llwarns << "can't read constraint target offset" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackVector3(constraintp->mTargetConstraintDir, "target_dir"))
+ {
+ llwarns << "can't read constraint target direction" << llendl;
+ return FALSE;
+ }
+
+ if (!constraintp->mTargetConstraintDir.isExactlyZero())
+ {
+ constraintp->mUseTargetOffset = TRUE;
+ // constraintp->mTargetConstraintDir *= constraintp->mSourceConstraintOffset.magVec();
+ }
+
+ if (!dp.unpackF32(constraintp->mEaseInStartTime, "ease_in_start"))
+ {
+ llwarns << "can't read constraint ease in start time" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(constraintp->mEaseInStopTime, "ease_in_stop"))
+ {
+ llwarns << "can't read constraint ease in stop time" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(constraintp->mEaseOutStartTime, "ease_out_start"))
+ {
+ llwarns << "can't read constraint ease out start time" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(constraintp->mEaseOutStopTime, "ease_out_stop"))
+ {
+ llwarns << "can't read constraint ease out stop time" << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mConstraints.addData(constraintp);
+
+ constraintp->mJointStateIndices = new S32[constraintp->mChainLength + 1];
+
+ LLJoint* joint = mCharacter->findCollisionVolume(constraintp->mSourceConstraintVolume);
+ if (!joint)
+ {
+ return FALSE;
+ }
+
+ // get joint to which this collision volume is attached
+ joint = joint->getParent();
+
+ for (S32 i = 0; i < constraintp->mChainLength + 1; i++)
+ {
+ constraintp->mJointStateIndices[i] = -1;
+ for (U32 j = 0; j < mJointMotionList->mNumJointMotions; j++)
+ {
+ if(mJointStates[j].getJoint() == joint)
+ {
+ constraintp->mJointStateIndices[i] = (S32)j;
+ break;
+ }
+ }
+ joint = joint->getParent();
+ }
+
+ }
+ }
+
+ // FIXME: support cleanup of old keyframe data
+ LLKeyframeDataCache::addKeyframeData(getID(), mJointMotionList);
+ mAssetStatus = ASSET_LOADED;
+
+ setupPose();
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// serialize()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const
+{
+ BOOL success = TRUE;
+
+ success &= dp.packU16(KEYFRAME_MOTION_VERSION, "version");
+ success &= dp.packU16(KEYFRAME_MOTION_SUBVERSION, "sub_version");
+ success &= dp.packS32(mJointMotionList->mBasePriority, "base_priority");
+ success &= dp.packF32(mJointMotionList->mDuration, "duration");
+ success &= dp.packString(mEmoteName.c_str(), "emote_name");
+ success &= dp.packF32(mJointMotionList->mLoopInPoint, "loop_in_point");
+ success &= dp.packF32(mJointMotionList->mLoopOutPoint, "loop_out_point");
+ success &= dp.packS32(mJointMotionList->mLoop, "loop");
+ success &= dp.packF32(mJointMotionList->mEaseInDuration, "ease_in_duration");
+ success &= dp.packF32(mJointMotionList->mEaseOutDuration, "ease_out_duration");
+ success &= dp.packU32(mJointMotionList->mHandPose, "hand_pose");
+ success &= dp.packU32(mJointMotionList->mNumJointMotions, "num_joints");
+
+ for (U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ JointMotion* joint_motionp = &mJointMotionList->mJointMotionArray[i];
+ success &= dp.packString(joint_motionp->mJointName.c_str(), "joint_name");
+ success &= dp.packS32(joint_motionp->mPriority, "joint_priority");
+ success &= dp.packS32(joint_motionp->mRotationCurve.mNumKeys, "num_rot_keys");
+
+ for (RotationKey* rot_keyp = joint_motionp->mRotationCurve.mKeys.getFirstData();
+ rot_keyp;
+ rot_keyp = joint_motionp->mRotationCurve.mKeys.getNextData())
+ {
+ U16 time_short = F32_to_U16(rot_keyp->mTime, 0.f, mJointMotionList->mDuration);
+ success &= dp.packU16(time_short, "time");
+
+ LLVector3 rot_angles = rot_keyp->mRotation.packToVector3();
+
+ U16 x, y, z;
+ rot_angles.quantize16(-1.f, 1.f, -1.f, 1.f);
+ x = F32_to_U16(rot_angles.mV[VX], -1.f, 1.f);
+ y = F32_to_U16(rot_angles.mV[VY], -1.f, 1.f);
+ z = F32_to_U16(rot_angles.mV[VZ], -1.f, 1.f);
+ success &= dp.packU16(x, "rot_angle_x");
+ success &= dp.packU16(y, "rot_angle_y");
+ success &= dp.packU16(z, "rot_angle_z");
+ }
+
+ success &= dp.packS32(joint_motionp->mPositionCurve.mNumKeys, "num_pos_keys");
+ for (PositionKey* pos_keyp = joint_motionp->mPositionCurve.mKeys.getFirstData();
+ pos_keyp;
+ pos_keyp = joint_motionp->mPositionCurve.mKeys.getNextData())
+ {
+ U16 time_short = F32_to_U16(pos_keyp->mTime, 0.f, mJointMotionList->mDuration);
+ success &= dp.packU16(time_short, "time");
+
+ U16 x, y, z;
+ pos_keyp->mPosition.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ x = F32_to_U16(pos_keyp->mPosition.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ y = F32_to_U16(pos_keyp->mPosition.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ z = F32_to_U16(pos_keyp->mPosition.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ success &= dp.packU16(x, "pos_x");
+ success &= dp.packU16(y, "pos_y");
+ success &= dp.packU16(z, "pos_z");
+ }
+ }
+
+ success &= dp.packS32(mJointMotionList->mConstraints.getLength(), "num_constraints");
+ for (JointConstraintSharedData* shared_constraintp = mJointMotionList->mConstraints.getFirstData();
+ shared_constraintp;
+ shared_constraintp = mJointMotionList->mConstraints.getNextData())
+ {
+ success &= dp.packU8(shared_constraintp->mChainLength, "chain_length");
+ success &= dp.packU8(shared_constraintp->mConstraintType, "constraint_type");
+ char volume_name[16];
+ snprintf(volume_name, sizeof(volume_name), "%s",
+ mCharacter->findCollisionVolume(shared_constraintp->mSourceConstraintVolume)->getName().c_str()); /* Flawfinder: ignore */
+ success &= dp.packBinaryDataFixed((U8*)volume_name, 16, "source_volume");
+ success &= dp.packVector3(shared_constraintp->mSourceConstraintOffset, "source_offset");
+ if (shared_constraintp->mConstraintTargetType == TYPE_GROUND)
+ {
+ snprintf(volume_name,sizeof(volume_name), "%s", "GROUND"); /* Flawfinder: ignore */
+ }
+ else
+ {
+ snprintf(volume_name, sizeof(volume_name),"%s",
+ mCharacter->findCollisionVolume(shared_constraintp->mTargetConstraintVolume)->getName().c_str()); /* Flawfinder: ignore */
+ }
+ success &= dp.packBinaryDataFixed((U8*)volume_name, 16, "target_volume");
+ success &= dp.packVector3(shared_constraintp->mTargetConstraintOffset, "target_offset");
+ success &= dp.packVector3(shared_constraintp->mTargetConstraintDir, "target_dir");
+ success &= dp.packF32(shared_constraintp->mEaseInStartTime, "ease_in_start");
+ success &= dp.packF32(shared_constraintp->mEaseInStopTime, "ease_in_stop");
+ success &= dp.packF32(shared_constraintp->mEaseOutStartTime, "ease_out_start");
+ success &= dp.packF32(shared_constraintp->mEaseOutStopTime, "ease_out_stop");
+ }
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// getFileSize()
+//-----------------------------------------------------------------------------
+U32 LLKeyframeMotion::getFileSize()
+{
+ // serialize into a dummy buffer to calculate required size
+ LLDataPackerBinaryBuffer dp;
+ serialize(dp);
+
+ return dp.getCurrentSize();
+}
+
+//-----------------------------------------------------------------------------
+// getPelvisBBox()
+//-----------------------------------------------------------------------------
+const LLBBoxLocal &LLKeyframeMotion::getPelvisBBox()
+{
+ return mJointMotionList->mPelvisBBox;
+}
+
+//-----------------------------------------------------------------------------
+// setPriority()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setPriority(S32 priority)
+{
+ if (mJointMotionList)
+ {
+ S32 priority_delta = priority - mJointMotionList->mBasePriority;
+ mJointMotionList->mBasePriority = (LLJoint::JointPriority)priority;
+ mJointMotionList->mMaxPriority = mJointMotionList->mBasePriority;
+
+ for (U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ mJointMotionList->mJointMotionArray[i].mPriority = (LLJoint::JointPriority)llclamp(
+ (S32)mJointMotionList->mJointMotionArray[i].mPriority + priority_delta,
+ (S32)LLJoint::LOW_PRIORITY,
+ (S32)LLJoint::HIGHEST_PRIORITY);
+ mJointStates[i].setPriority(mJointMotionList->mJointMotionArray[i].mPriority);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setEmote()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setEmote(const LLUUID& emote_id)
+{
+ const char* emote_name = gAnimLibrary.animStateToString(emote_id);
+ if (emote_name)
+ {
+ mEmoteName = emote_name;
+ }
+ else
+ {
+ mEmoteName = "";
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setEaseIn()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setEaseIn(F32 ease_in)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mEaseInDuration = llmax(ease_in, 0.f);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setEaseOut()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setEaseOut(F32 ease_in)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mEaseOutDuration = llmax(ease_in, 0.f);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// flushKeyframeCache()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::flushKeyframeCache()
+{
+ LLKeyframeDataCache::clear();
+}
+
+//-----------------------------------------------------------------------------
+// setLoop()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setLoop(BOOL loop)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mLoop = loop;
+ mSendStopTimestamp = F32_MAX;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setLoopIn()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setLoopIn(F32 in_point)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mLoopInPoint = in_point;
+
+ // set up loop keys
+ for (U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ PositionCurve* pos_curve = &mJointMotionList->mJointMotionArray[i].mPositionCurve;
+ RotationCurve* rot_curve = &mJointMotionList->mJointMotionArray[i].mRotationCurve;
+ ScaleCurve* scale_curve = &mJointMotionList->mJointMotionArray[i].mScaleCurve;
+
+ pos_curve->mLoopInKey.mTime = mJointMotionList->mLoopInPoint;
+ rot_curve->mLoopInKey.mTime = mJointMotionList->mLoopInPoint;
+ scale_curve->mLoopInKey.mTime = mJointMotionList->mLoopInPoint;
+
+ pos_curve->mLoopInKey.mPosition = pos_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration);
+ rot_curve->mLoopInKey.mRotation = rot_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration);
+ scale_curve->mLoopInKey.mScale = scale_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setLoopOut()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setLoopOut(F32 out_point)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mLoopOutPoint = out_point;
+
+ // set up loop keys
+ for (U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ PositionCurve* pos_curve = &mJointMotionList->mJointMotionArray[i].mPositionCurve;
+ RotationCurve* rot_curve = &mJointMotionList->mJointMotionArray[i].mRotationCurve;
+ ScaleCurve* scale_curve = &mJointMotionList->mJointMotionArray[i].mScaleCurve;
+
+ pos_curve->mLoopOutKey.mTime = mJointMotionList->mLoopOutPoint;
+ rot_curve->mLoopOutKey.mTime = mJointMotionList->mLoopOutPoint;
+ scale_curve->mLoopOutKey.mTime = mJointMotionList->mLoopOutPoint;
+
+ pos_curve->mLoopOutKey.mPosition = pos_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration);
+ rot_curve->mLoopOutKey.mRotation = rot_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration);
+ scale_curve->mLoopOutKey.mScale = scale_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onLoadComplete()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLUUID* id = (LLUUID*)user_data;
+
+ LLCharacter* character = NULL;
+
+ for(character = LLCharacter::sInstances.getFirstData();
+ character;
+ character = LLCharacter::sInstances.getNextData())
+ {
+ if (character->getID() == *id)
+ {
+ break;
+ }
+ }
+
+ delete id;
+
+ if (!character)
+ {
+ return;
+ }
+
+ // create an instance of this motion (it may or may not already exist)
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)character->createMotion(asset_uuid);
+
+ if (0 == status && motionp)
+ {
+ if (motionp->mAssetStatus == ASSET_LOADED)
+ {
+ // asset already loaded
+ return;
+ }
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+ S32 size = file.getSize();
+
+ U8* buffer = new U8[size];
+ file.read((U8*)buffer, size); /*Flawfinder: ignore*/
+
+ lldebugs << "Loading keyframe data for: " << motionp->getName() << ":" << motionp->getID() << " (" << size << " bytes)" << llendl;
+
+ LLDataPackerBinaryBuffer dp(buffer, size);
+ if (motionp->deserialize(dp))
+ {
+ motionp->mAssetStatus = ASSET_LOADED;
+ }
+ else
+ {
+ llwarns << "Failed to decode asset for animation " << motionp->getName() << ":" << motionp->getID() << llendl;
+ motionp->mAssetStatus = ASSET_FETCH_FAILED;
+ }
+
+ delete []buffer;
+
+ }
+ else
+ {
+ llwarns << "Failed to load asset for animation " << motionp->getName() << ":" << motionp->getID() << llendl;
+ motionp->mAssetStatus = ASSET_FETCH_FAILED;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// writeCAL3D()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::writeCAL3D(apr_file_t* fp)
+{
+// <ANIMATION VERSION="1000" DURATION="1.03333" NUMTRACKS="58">
+// <TRACK BONEID="0" NUMKEYFRAMES="31">
+// <KEYFRAME TIME="0">
+// <TRANSLATION>0 0 48.8332</TRANSLATION>
+// <ROTATION>0.0512905 0.05657 0.66973 0.738668</ROTATION>
+// </KEYFRAME>
+// </TRACK>
+// </ANIMATION>
+
+ apr_file_printf(fp, "<ANIMATION VERSION=\"1000\" DURATION=\"%.5f\" NUMTRACKS=\"%d\">\n", getDuration(), mJointMotionList->mNumJointMotions);
+ for (U32 joint_index = 0; joint_index < mJointMotionList->mNumJointMotions; joint_index++)
+ {
+ JointMotion* joint_motionp = &mJointMotionList->mJointMotionArray[joint_index];
+ LLJoint* animated_joint = mCharacter->getJoint(joint_motionp->mJointName);
+ S32 joint_num = animated_joint->mJointNum + 1;
+
+ apr_file_printf(fp, " <TRACK BONEID=\"%d\" NUMKEYFRAMES=\"%d\">\n", joint_num, joint_motionp->mRotationCurve.mNumKeys );
+ PositionKey* pos_keyp = joint_motionp->mPositionCurve.mKeys.getFirstData();
+ for (RotationKey* rot_keyp = joint_motionp->mRotationCurve.mKeys.getFirstData();
+ rot_keyp;
+ rot_keyp = joint_motionp->mRotationCurve.mKeys.getNextData())
+ {
+ apr_file_printf(fp, " <KEYFRAME TIME=\"%0.3f\">\n", rot_keyp->mTime);
+ LLVector3 nominal_pos = animated_joint->getPosition();
+ if (animated_joint->getParent())
+ {
+ nominal_pos.scaleVec(animated_joint->getParent()->getScale());
+ }
+ nominal_pos = nominal_pos * 100.f;
+
+ if (joint_motionp->mUsage & LLJointState::POS && pos_keyp)
+ {
+ LLVector3 pos_val = pos_keyp->mPosition;
+ pos_val = pos_val * 100.f;
+ pos_val += nominal_pos;
+ apr_file_printf(fp, " <TRANSLATION>%0.4f %0.4f %0.4f</TRANSLATION>\n", pos_val.mV[VX], pos_val.mV[VY], pos_val.mV[VZ]);
+ pos_keyp = joint_motionp->mPositionCurve.mKeys.getNextData();
+ }
+ else
+ {
+ apr_file_printf(fp, " <TRANSLATION>%0.4f %0.4f %0.4f</TRANSLATION>\n", nominal_pos.mV[VX], nominal_pos.mV[VY], nominal_pos.mV[VZ]);
+ }
+
+ LLQuaternion rot_val = ~rot_keyp->mRotation;
+ apr_file_printf(fp, " <ROTATION>%0.4f %0.4f %0.4f %0.4f</ROTATION>\n", rot_val.mQ[VX], rot_val.mQ[VY], rot_val.mQ[VZ], rot_val.mQ[VW]);
+ apr_file_printf(fp, " </KEYFRAME>\n");
+ }
+ apr_file_printf(fp, " </TRACK>\n");
+ }
+ apr_file_printf(fp, "</ANIMATION>\n");
+}
+
+//--------------------------------------------------------------------
+// LLKeyframeDataCache::dumpDiagInfo()
+//--------------------------------------------------------------------
+void LLKeyframeDataCache::dumpDiagInfo()
+{
+ // keep track of totals
+ U32 total_size = 0;
+
+ char buf[1024]; /* Flawfinder: ignore */
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ llinfos << " Global Motion Table (DEBUG only)" << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+
+ // print each loaded mesh, and it's memory usage
+ LLKeyframeDataMap::iterator map_it;
+ for (map_it = sKeyframeDataMap.begin(); map_it != sKeyframeDataMap.end(); ++map_it)
+ {
+ U32 joint_motion_kb;
+
+ LLKeyframeMotion::JointMotionList *motion_list_p = map_it->second;
+
+ llinfos << "Motion: " << map_it->first << llendl;
+
+ joint_motion_kb = motion_list_p->dumpDiagInfo();
+
+ total_size += joint_motion_kb;
+ }
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ llinfos << "Motions\tTotal Size" << llendl;
+ snprintf(buf, sizeof(buf), "%d\t\t%d bytes", (S32)sKeyframeDataMap.size(), total_size ); /* Flawfinder: ignore */
+ llinfos << buf << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+}
+
+
+//--------------------------------------------------------------------
+// LLKeyframeDataCache::addKeyframeData()
+//--------------------------------------------------------------------
+void LLKeyframeDataCache::addKeyframeData(const LLUUID& id, LLKeyframeMotion::JointMotionList* joint_motion_listp)
+{
+ sKeyframeDataMap[id] = joint_motion_listp;
+}
+
+//--------------------------------------------------------------------
+// LLKeyframeDataCache::removeKeyframeData()
+//--------------------------------------------------------------------
+void LLKeyframeDataCache::removeKeyframeData(const LLUUID& id)
+{
+ LLKeyframeMotion::JointMotionList* joint_motion_listp = getKeyframeData(id);
+ if (joint_motion_listp)
+ {
+ delete joint_motion_listp;
+ }
+ sKeyframeDataMap.erase(id);
+}
+
+//--------------------------------------------------------------------
+// LLKeyframeDataCache::getKeyframeData()
+//--------------------------------------------------------------------
+LLKeyframeMotion::JointMotionList* LLKeyframeDataCache::getKeyframeData(const LLUUID& id)
+{
+ LLKeyframeDataMap::iterator found_data = sKeyframeDataMap.find(id);
+ if (found_data == sKeyframeDataMap.end())
+ {
+ return NULL;
+ }
+ return found_data->second;
+}
+
+//--------------------------------------------------------------------
+// ~LLKeyframeDataCache::LLKeyframeDataCache()
+//--------------------------------------------------------------------
+LLKeyframeDataCache::~LLKeyframeDataCache()
+{
+ clear();
+}
+
+//-----------------------------------------------------------------------------
+// clear()
+//-----------------------------------------------------------------------------
+void LLKeyframeDataCache::clear()
+{
+ for_each(sKeyframeDataMap.begin(), sKeyframeDataMap.end(), DeletePairedPointer());
+ sKeyframeDataMap.clear();
+}
+
+//-----------------------------------------------------------------------------
+// JointConstraint()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::JointConstraint::JointConstraint(JointConstraintSharedData* shared_data) : mSharedData(shared_data)
+{
+ mTotalLength = 0.f;
+ mActive = FALSE;
+ mSourceVolume = NULL;
+ mTargetVolume = NULL;
+ mFixupDistanceRMS = 0.f;
+}
+
+//-----------------------------------------------------------------------------
+// ~JointConstraint()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::JointConstraint::~JointConstraint()
+{
+}
+
+// End
diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h
new file mode 100644
index 0000000000..4e82c0672a
--- /dev/null
+++ b/indra/llcharacter/llkeyframemotion.h
@@ -0,0 +1,437 @@
+/**
+ * @file llkeyframemotion.h
+ * @brief Implementation of LLKeframeMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYFRAMEMOTION_H
+#define LL_LLKEYFRAMEMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+
+#include <string>
+
+#include "llassetstorage.h"
+#include "llassoclist.h"
+#include "llbboxlocal.h"
+#include "llhandmotion.h"
+#include "lljointstate.h"
+#include "llmotion.h"
+#include "llptrskipmap.h"
+#include "llquaternion.h"
+#include "v3dmath.h"
+#include "v3math.h"
+#include "llapr.h"
+
+class LLKeyframeDataCache;
+class LLVFS;
+class LLDataPacker;
+
+#define MIN_REQUIRED_PIXEL_AREA_KEYFRAME (40.f)
+#define MAX_CHAIN_LENGTH (4)
+
+const S32 KEYFRAME_MOTION_VERSION = 1;
+const S32 KEYFRAME_MOTION_SUBVERSION = 0;
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeMotion
+//-----------------------------------------------------------------------------
+class LLKeyframeMotion :
+ public LLMotion
+{
+ friend class LLKeyframeDataCache;
+public:
+ // Constructor
+ LLKeyframeMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID& id);
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() {
+ if (mJointMotionList) return mJointMotionList->mLoop;
+ else return FALSE;
+ }
+
+ // motions must report their total duration
+ virtual F32 getDuration() {
+ if (mJointMotionList) return mJointMotionList->mDuration;
+ else return 0.f;
+ }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() {
+ if (mJointMotionList) return mJointMotionList->mEaseInDuration;
+ else return 0.f;
+ }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() {
+ if (mJointMotionList) return mJointMotionList->mEaseOutDuration;
+ else return 0.f;
+ }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() {
+ if (mJointMotionList) return mJointMotionList->mBasePriority;
+ else return LLJoint::LOW_PRIORITY;
+ }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_KEYFRAME; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+ virtual void setStopTime(F32 time);
+
+ static void setVFS(LLVFS* vfs) { sVFS = vfs; }
+
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+public:
+ U32 getFileSize();
+ BOOL serialize(LLDataPacker& dp) const;
+ BOOL deserialize(LLDataPacker& dp);
+ void writeCAL3D(apr_file_t* fp);
+ BOOL isLoaded() { return mJointMotionList != NULL; }
+
+
+ // setters for modifying a keyframe animation
+ void setLoop(BOOL loop);
+
+ F32 getLoopIn() {
+ return (mJointMotionList) ? mJointMotionList->mLoopInPoint : 0.f;
+ }
+
+ F32 getLoopOut() {
+ return (mJointMotionList) ? mJointMotionList->mLoopOutPoint : 0.f;
+ }
+
+ void setLoopIn(F32 in_point);
+
+ void setLoopOut(F32 out_point);
+
+ void setHandPose(LLHandMotion::eHandPose pose) {
+ if (mJointMotionList) mJointMotionList->mHandPose = pose;
+ }
+
+ LLHandMotion::eHandPose getHandPose() {
+ return (mJointMotionList) ? mJointMotionList->mHandPose : LLHandMotion::HAND_POSE_RELAXED;
+ }
+
+ void setPriority(S32 priority);
+
+ void setEmote(const LLUUID& emote_id);
+
+ void setEaseIn(F32 ease_in);
+
+ void setEaseOut(F32 ease_in);
+
+ F32 getLastUpdateTime() { return mLastLoopedTime; }
+
+ const LLBBoxLocal& getPelvisBBox();
+
+ static void flushKeyframeCache();
+
+ typedef enum e_constraint_type
+ {
+ TYPE_POINT,
+ TYPE_PLANE
+ } EConstraintType;
+
+ typedef enum e_constraint_target_type
+ {
+ TYPE_BODY,
+ TYPE_GROUND
+ } EConstraintTargetType;
+
+protected:
+ //-------------------------------------------------------------------------
+ // JointConstraintSharedData
+ //-------------------------------------------------------------------------
+ class JointConstraintSharedData
+ {
+ public:
+ JointConstraintSharedData() :
+ mChainLength(0),
+ mEaseInStartTime(0.f),
+ mEaseInStopTime(0.f),
+ mEaseOutStartTime(0.f),
+ mEaseOutStopTime(0.f),
+ mUseTargetOffset(FALSE),
+ mConstraintType(TYPE_POINT),
+ mConstraintTargetType(TYPE_BODY) {};
+ ~JointConstraintSharedData() { delete [] mJointStateIndices; }
+
+ S32 mSourceConstraintVolume;
+ LLVector3 mSourceConstraintOffset;
+ S32 mTargetConstraintVolume;
+ LLVector3 mTargetConstraintOffset;
+ LLVector3 mTargetConstraintDir;
+ S32 mChainLength;
+ S32* mJointStateIndices;
+ F32 mEaseInStartTime;
+ F32 mEaseInStopTime;
+ F32 mEaseOutStartTime;
+ F32 mEaseOutStopTime;
+ BOOL mUseTargetOffset;
+ EConstraintType mConstraintType;
+ EConstraintTargetType mConstraintTargetType;
+ };
+
+ //-----------------------------------------------------------------------------
+ // JointConstraint()
+ //-----------------------------------------------------------------------------
+ class JointConstraint
+ {
+ public:
+ JointConstraint(JointConstraintSharedData* shared_data);
+ ~JointConstraint();
+
+ JointConstraintSharedData* mSharedData;
+ F32 mWeight;
+ F32 mTotalLength;
+ LLVector3 mPositions[MAX_CHAIN_LENGTH];
+ F32 mJointLengths[MAX_CHAIN_LENGTH];
+ F32 mJointLengthFractions[MAX_CHAIN_LENGTH];
+ BOOL mActive;
+ LLVector3d mGroundPos;
+ LLVector3 mGroundNorm;
+ LLJoint* mSourceVolume;
+ LLJoint* mTargetVolume;
+ F32 mFixupDistanceRMS;
+ };
+
+ void applyKeyframes(F32 time);
+
+ void applyConstraints(F32 time, U8* joint_mask);
+
+ void activateConstraint(JointConstraint* constraintp);
+
+ void initializeConstraint(JointConstraint* constraint);
+
+ void deactivateConstraint(JointConstraint *constraintp);
+
+ void applyConstraint(JointConstraint* constraintp, F32 time, U8* joint_mask);
+
+ BOOL setupPose();
+
+public:
+ enum AssetStatus { ASSET_LOADED, ASSET_FETCHED, ASSET_NEEDS_FETCH, ASSET_FETCH_FAILED, ASSET_UNDEFINED };
+
+ enum InterpolationType { IT_STEP, IT_LINEAR, IT_SPLINE };
+
+ //-------------------------------------------------------------------------
+ // ScaleKey
+ //-------------------------------------------------------------------------
+ class ScaleKey
+ {
+ public:
+ ScaleKey() { mTime = 0.0f; }
+ ScaleKey(F32 time, const LLVector3 &scale) { mTime = time; mScale = scale; }
+
+ F32 mTime;
+ LLVector3 mScale;
+ };
+
+ //-------------------------------------------------------------------------
+ // RotationKey
+ //-------------------------------------------------------------------------
+ class RotationKey
+ {
+ public:
+ RotationKey() { mTime = 0.0f; }
+ RotationKey(F32 time, const LLQuaternion &rotation) { mTime = time; mRotation = rotation; }
+
+ F32 mTime;
+ LLQuaternion mRotation;
+ };
+
+ //-------------------------------------------------------------------------
+ // PositionKey
+ //-------------------------------------------------------------------------
+ class PositionKey
+ {
+ public:
+ PositionKey() { mTime = 0.0f; }
+ PositionKey(F32 time, const LLVector3 &position) { mTime = time; mPosition = position; }
+
+ F32 mTime;
+ LLVector3 mPosition;
+ };
+
+ //-------------------------------------------------------------------------
+ // ScaleCurve
+ //-------------------------------------------------------------------------
+ class ScaleCurve
+ {
+ public:
+ ScaleCurve();
+ ~ScaleCurve();
+ LLVector3 getValue(F32 time, F32 duration);
+ LLVector3 interp(F32 u, ScaleKey& before, ScaleKey& after);
+
+ InterpolationType mInterpolationType;
+ S32 mNumKeys;
+ LLPtrSkipMap<F32, ScaleKey*> mKeys;
+ ScaleKey mLoopInKey;
+ ScaleKey mLoopOutKey;
+ };
+
+ //-------------------------------------------------------------------------
+ // RotationCurve
+ //-------------------------------------------------------------------------
+ class RotationCurve
+ {
+ public:
+ RotationCurve();
+ ~RotationCurve();
+ LLQuaternion getValue(F32 time, F32 duration);
+ LLQuaternion interp(F32 u, RotationKey& before, RotationKey& after);
+
+ InterpolationType mInterpolationType;
+ S32 mNumKeys;
+ LLPtrSkipMap<F32, RotationKey*> mKeys;
+ RotationKey mLoopInKey;
+ RotationKey mLoopOutKey;
+ };
+
+ //-------------------------------------------------------------------------
+ // PositionCurve
+ //-------------------------------------------------------------------------
+ class PositionCurve
+ {
+ public:
+ PositionCurve();
+ ~PositionCurve();
+ LLVector3 getValue(F32 time, F32 duration);
+ LLVector3 interp(F32 u, PositionKey& before, PositionKey& after);
+
+ InterpolationType mInterpolationType;
+ S32 mNumKeys;
+ LLPtrSkipMap<F32, PositionKey*> mKeys;
+ PositionKey mLoopInKey;
+ PositionKey mLoopOutKey;
+ };
+
+ //-------------------------------------------------------------------------
+ // JointMotion
+ //-------------------------------------------------------------------------
+ class JointMotion
+ {
+ public:
+ PositionCurve mPositionCurve;
+ RotationCurve mRotationCurve;
+ ScaleCurve mScaleCurve;
+ std::string mJointName;
+ U32 mUsage;
+ LLJoint::JointPriority mPriority;
+
+ void update(LLJointState *joint_state, F32 time, F32 duration);
+ };
+
+ //-------------------------------------------------------------------------
+ // JointMotionList
+ //-------------------------------------------------------------------------
+ class JointMotionList
+ {
+ public:
+ U32 mNumJointMotions;
+ JointMotion* mJointMotionArray;
+ F32 mDuration;
+ BOOL mLoop;
+ F32 mLoopInPoint;
+ F32 mLoopOutPoint;
+ F32 mEaseInDuration;
+ F32 mEaseOutDuration;
+ LLJoint::JointPriority mBasePriority;
+ LLHandMotion::eHandPose mHandPose;
+ LLJoint::JointPriority mMaxPriority;
+ LLLinkedList<JointConstraintSharedData> mConstraints;
+ LLBBoxLocal mPelvisBBox;
+ public:
+ JointMotionList() : mNumJointMotions(0), mJointMotionArray(NULL) {};
+ ~JointMotionList() { mConstraints.deleteAllData(); delete [] mJointMotionArray; }
+ U32 dumpDiagInfo();
+ };
+
+
+protected:
+ static LLVFS* sVFS;
+
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ JointMotionList* mJointMotionList;
+ LLJointState* mJointStates;
+ LLJoint* mPelvisp;
+ LLCharacter* mCharacter;
+ std::string mEmoteName;
+ LLLinkedList<JointConstraint> mConstraints;
+ U32 mLastSkeletonSerialNum;
+ F32 mLastUpdateTime;
+ F32 mLastLoopedTime;
+ AssetStatus mAssetStatus;
+};
+
+class LLKeyframeDataCache
+{
+public:
+ //FIXME: implement this as an actual singleton member of LLKeyframeMotion
+ LLKeyframeDataCache(){};
+ ~LLKeyframeDataCache();
+
+ typedef std::map<LLUUID, class LLKeyframeMotion::JointMotionList*> LLKeyframeDataMap;
+ static LLKeyframeDataMap sKeyframeDataMap;
+
+ static void addKeyframeData(const LLUUID& id, LLKeyframeMotion::JointMotionList*);
+ static LLKeyframeMotion::JointMotionList* getKeyframeData(const LLUUID& id);
+
+ static void removeKeyframeData(const LLUUID& id);
+
+ //print out diagnostic info
+ static void dumpDiagInfo();
+ static void clear();
+};
+
+#endif // LL_LLKEYFRAMEMOTION_H
+
diff --git a/indra/llcharacter/llkeyframemotionparam.cpp b/indra/llcharacter/llkeyframemotionparam.cpp
new file mode 100644
index 0000000000..c57079fc2b
--- /dev/null
+++ b/indra/llcharacter/llkeyframemotionparam.cpp
@@ -0,0 +1,442 @@
+/**
+ * @file llkeyframemotionparam.cpp
+ * @brief Implementation of LLKeyframeMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llkeyframemotionparam.h"
+#include "llcharacter.h"
+#include "llmath.h"
+#include "m3math.h"
+#include "lldir.h"
+#include "llanimationstates.h"
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// sortFunc()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::sortFunc(ParameterizedMotion *new_motion, ParameterizedMotion *tested_motion)
+{
+ return (new_motion->second < tested_motion->second);
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeMotionParam::LLKeyframeMotionParam( const LLUUID &id) : LLMotion(id)
+{
+ mJointStates = NULL;
+ mDefaultKeyframeMotion = NULL;
+ mCharacter = NULL;
+
+ mEaseInDuration = 0.f;
+ mEaseOutDuration = 0.f;
+ mDuration = 0.f;
+ mPriority = LLJoint::LOW_PRIORITY;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeMotionParam()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeMotionParam::~LLKeyframeMotionParam()
+{
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ delete paramMotion->first;
+ }
+ delete motionList;
+ }
+
+ mParameterizedMotions.removeAll();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeMotionParam::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ if (!loadMotions())
+ {
+ return STATUS_FAILURE;
+ }
+
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ paramMotion->first->onInitialize(character);
+
+ if (paramMotion->first->getDuration() > mEaseInDuration)
+ {
+ mEaseInDuration = paramMotion->first->getEaseInDuration();
+ }
+
+ if (paramMotion->first->getEaseOutDuration() > mEaseOutDuration)
+ {
+ mEaseOutDuration = paramMotion->first->getEaseOutDuration();
+ }
+
+ if (paramMotion->first->getDuration() > mDuration)
+ {
+ mDuration = paramMotion->first->getDuration();
+ }
+
+ if (paramMotion->first->getPriority() > mPriority)
+ {
+ mPriority = paramMotion->first->getPriority();
+ }
+
+ LLPose *pose = paramMotion->first->getPose();
+
+ mPoseBlender.addMotion(paramMotion->first);
+ for (LLJointState *jsp = pose->getFirstJointState(); jsp; jsp = pose->getNextJointState())
+ {
+ LLPose *blendedPose = mPoseBlender.getBlendedPose();
+ blendedPose->addJointState(jsp);
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::onActivate()
+{
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ paramMotion->first->activate();
+ }
+ }
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::onUpdate(F32 time, U8* joint_mask)
+{
+ F32 weightFactor = 1.f / (F32)mParameterizedMotions.length();
+ U32 i;
+
+ // zero out all pose weights
+ for (i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+// llinfos << "Weight for pose " << paramMotion->first->getName() << " is " << paramMotion->first->getPose()->getWeight() << llendl;
+ paramMotion->first->getPose()->setWeight(0.f);
+ }
+ }
+
+
+ for (i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ std::string *paramName = mParameterizedMotions.getIndexAt(i);
+ F32* paramValue = (F32 *)mCharacter->getAnimationData(*paramName);
+ ParameterizedMotion* firstMotion = NULL;
+ ParameterizedMotion* secondMotion = NULL;
+
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ paramMotion->first->onUpdate(time, joint_mask);
+
+ F32 distToParam = paramMotion->second - *paramValue;
+
+ if ( distToParam <= 0.f)
+ {
+ // keep track of the motion closest to the parameter value
+ firstMotion = paramMotion;
+ }
+ else
+ {
+ // we've passed the parameter value
+ // so store the first motion we find as the second one we want to blend...
+ if (firstMotion && !secondMotion )
+ {
+ secondMotion = paramMotion;
+ }
+ //...or, if we've seen no other motion so far, make sure we blend to this only
+ else if (!firstMotion)
+ {
+ firstMotion = paramMotion;
+ secondMotion = paramMotion;
+ }
+ }
+ }
+
+ LLPose *firstPose;
+ LLPose *secondPose;
+
+ if (firstMotion)
+ firstPose = firstMotion->first->getPose();
+ else
+ firstPose = NULL;
+
+ if (secondMotion)
+ secondPose = secondMotion->first->getPose();
+ else
+ secondPose = NULL;
+
+ // now modify weight of the subanim (only if we are blending between two motions)
+ if (firstMotion && secondMotion)
+ {
+ if (firstMotion == secondMotion)
+ {
+ firstPose->setWeight(weightFactor);
+ }
+ else if (firstMotion->second == secondMotion->second)
+ {
+ firstPose->setWeight(0.5f * weightFactor);
+ secondPose->setWeight(0.5f * weightFactor);
+ }
+ else
+ {
+ F32 first_weight = 1.f -
+ ((llclamp(*paramValue - firstMotion->second, 0.f, (secondMotion->second - firstMotion->second))) /
+ (secondMotion->second - firstMotion->second));
+ first_weight = llclamp(first_weight, 0.f, 1.f);
+
+ F32 second_weight = 1.f - first_weight;
+
+ firstPose->setWeight(first_weight * weightFactor);
+ secondPose->setWeight(second_weight * weightFactor);
+
+// llinfos << "Parameter " << *paramName << ": " << *paramValue << llendl;
+// llinfos << "Weights " << firstPose->getWeight() << " " << secondPose->getWeight() << llendl;
+ }
+ }
+ else if (firstMotion && !secondMotion)
+ {
+ firstPose->setWeight(weightFactor);
+ }
+ }
+
+ // blend poses
+ mPoseBlender.blendAndApply();
+
+ llinfos << "Param Motion weight " << mPoseBlender.getBlendedPose()->getWeight() << llendl;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotionParam::onDeactivate()
+{
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ paramMotion->first->onDeactivate();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::addKeyframeMotion()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value)
+{
+ LLMotion *newMotion = mCharacter->createMotion( id );
+
+ if (!newMotion)
+ {
+ return FALSE;
+ }
+
+ newMotion->setName(name);
+
+ // make sure a list of motions exists for this parameter
+ LLLinkedList< ParameterizedMotion > *motionList;
+ if (mParameterizedMotions.getValue(param))
+ {
+ motionList = *mParameterizedMotions.getValue(param);
+ }
+ else
+ {
+ motionList = new LLLinkedList< ParameterizedMotion >;
+ motionList->setInsertBefore(sortFunc);
+ mParameterizedMotions.addToHead(param, motionList);
+ }
+
+ // now add motion to this list
+ ParameterizedMotion *parameterizedMotion = new ParameterizedMotion(newMotion, value);
+
+ motionList->addDataSorted(parameterizedMotion);
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::setDefaultKeyframeMotion()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotionParam::setDefaultKeyframeMotion(char *name)
+{
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ if (paramMotion->first->getName() == name)
+ {
+ mDefaultKeyframeMotion = paramMotion->first;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// loadMotions()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::loadMotions()
+{
+ //-------------------------------------------------------------------------
+ // Load named file by concatenating the character prefix with the motion name.
+ // Load data into a buffer to be parsed.
+ //-------------------------------------------------------------------------
+ char path[LL_MAX_PATH]; /* Flawfinder: ignore */
+ snprintf( path, sizeof(path), "%s_%s.llp",
+ gDirUtilp->getExpandedFilename(LL_PATH_MOTIONS,mCharacter->getAnimationPrefix()).c_str(),
+ getName().c_str() ); /* Flawfinder: ignore */
+
+ //-------------------------------------------------------------------------
+ // open the file
+ //-------------------------------------------------------------------------
+ S32 fileSize = 0;
+ apr_file_t* fp = ll_apr_file_open(path, LL_APR_R, &fileSize);
+ if (!fp || fileSize == 0)
+ {
+ llinfos << "ERROR: can't open: " << path << llendl;
+ return FALSE;
+ }
+
+ // allocate a text buffer
+ char *text = new char[ fileSize+1 ];
+ if ( !text )
+ {
+ llinfos << "ERROR: can't allocated keyframe text buffer." << llendl;
+ apr_file_close(fp);
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // load data from file into buffer
+ //-------------------------------------------------------------------------
+ bool error = false;
+ char *p = text;
+ while ( 1 )
+ {
+ if (apr_file_eof(fp) == APR_EOF)
+ {
+ break;
+ }
+ if (apr_file_gets(p, 1024, fp) != APR_SUCCESS)
+ {
+ error = true;
+ break;
+ }
+ while ( *(++p) )
+ ;
+ }
+
+ //-------------------------------------------------------------------------
+ // close the file
+ //-------------------------------------------------------------------------
+ apr_file_close( fp );
+
+ //-------------------------------------------------------------------------
+ // check for error
+ //-------------------------------------------------------------------------
+ llassert( p <= (text+fileSize) );
+
+ if ( error )
+ {
+ llinfos << "ERROR: error while reading from " << path << llendl;
+ delete [] text;
+ return FALSE;
+ }
+
+ llinfos << "Loading parametric keyframe data for: " << getName() << llendl;
+
+ //-------------------------------------------------------------------------
+ // parse the text and build keyframe data structures
+ //-------------------------------------------------------------------------
+ p = text;
+ S32 num;
+ char strA[80]; /* Flawfinder: ignore */
+ char strB[80]; /* Flawfinder: ignore */
+ F32 floatA = 0.0f;
+
+
+ //-------------------------------------------------------------------------
+ // get priority
+ //-------------------------------------------------------------------------
+ BOOL isFirstMotion = TRUE;
+ num = sscanf(p, "%79s %79s %f", strA, strB, &floatA);
+
+ while(1)
+ {
+ if (num == 0 || num == EOF) break;
+ if ((num != 3))
+ {
+ llinfos << "WARNING: can't read parametric motion" << llendl;
+ delete [] text;
+ return FALSE;
+ }
+
+ addKeyframeMotion(strA, gAnimLibrary.stringToAnimState(strA), strB, floatA);
+ if (isFirstMotion)
+ {
+ isFirstMotion = FALSE;
+ setDefaultKeyframeMotion(strA);
+ }
+
+ p = strstr(p, "\n");
+ if (!p)
+ {
+ break;
+ }
+
+ p++;
+ num = sscanf(p, "%79s %79s %f", strA, strB, &floatA);
+ }
+
+ delete [] text;
+ return TRUE;
+}
+
+// End
diff --git a/indra/llcharacter/llkeyframemotionparam.h b/indra/llcharacter/llkeyframemotionparam.h
new file mode 100644
index 0000000000..6b23100d5b
--- /dev/null
+++ b/indra/llcharacter/llkeyframemotionparam.h
@@ -0,0 +1,138 @@
+/**
+ * @file llkeyframemotionparam.h
+ * @brief Implementation of LLKeframeMotionParam class.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYFRAMEMOTIONPARAM_H
+#define LL_LLKEYFRAMEMOTIONPARAM_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+
+#include <string>
+
+#include "llmotion.h"
+#include "lljointstate.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "linked_lists.h"
+#include "llkeyframemotion.h"
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeMotionParam
+//-----------------------------------------------------------------------------
+class LLKeyframeMotionParam :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLKeyframeMotionParam(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeMotionParam();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLKeyframeMotionParam(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() {
+ return TRUE;
+ }
+
+ // motions must report their total duration
+ virtual F32 getDuration() {
+ return mDuration;
+ }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() {
+ return mEaseInDuration;
+ }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() {
+ return mEaseOutDuration;
+ }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() {
+ return mPriority;
+ }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_KEYFRAME; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+ virtual LLPose* getPose() { return mPoseBlender.getBlendedPose();}
+
+protected:
+ //-------------------------------------------------------------------------
+ // new functions defined by this subclass
+ //-------------------------------------------------------------------------
+ typedef std::pair<LLMotion*, F32> ParameterizedMotion;
+
+ // add a motion and associated parameter triplet
+ BOOL addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value);
+
+ // set default motion for LOD and retrieving blend constants
+ void setDefaultKeyframeMotion(char *);
+
+ static BOOL sortFunc(ParameterizedMotion *new_motion, ParameterizedMotion *tested_motion);
+
+ BOOL loadMotions();
+
+protected:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+
+ typedef LLLinkedList < ParameterizedMotion > motion_list_t;
+ LLAssocList <std::string, motion_list_t* > mParameterizedMotions;
+ LLJointState* mJointStates;
+ LLMotion* mDefaultKeyframeMotion;
+ LLCharacter* mCharacter;
+ LLPoseBlender mPoseBlender;
+
+ F32 mEaseInDuration;
+ F32 mEaseOutDuration;
+ F32 mDuration;
+ LLJoint::JointPriority mPriority;
+
+ LLUUID mTransactionID;
+};
+
+#endif // LL_LLKEYFRAMEMOTIONPARAM_H
diff --git a/indra/llcharacter/llkeyframestandmotion.cpp b/indra/llcharacter/llkeyframestandmotion.cpp
new file mode 100644
index 0000000000..6810fb450f
--- /dev/null
+++ b/indra/llcharacter/llkeyframestandmotion.cpp
@@ -0,0 +1,320 @@
+/**
+ * @file llkeyframestandmotion.cpp
+ * @brief Implementation of LLKeyframeStandMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llkeyframestandmotion.h"
+#include "llcharacter.h"
+
+//-----------------------------------------------------------------------------
+// Macros and consts
+//-----------------------------------------------------------------------------
+#define GO_TO_KEY_POSE 1
+#define MIN_TRACK_SPEED 0.01f
+const F32 ROTATION_THRESHOLD = 0.6f;
+const F32 POSITION_THRESHOLD = 0.1f;
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeStandMotion::LLKeyframeStandMotion(const LLUUID &id) : LLKeyframeMotion(id)
+{
+ mFlipFeet = FALSE;
+ mCharacter = NULL;
+
+ // create kinematic hierarchy
+ mPelvisJoint.addChild( &mHipLeftJoint );
+ mHipLeftJoint.addChild( &mKneeLeftJoint );
+ mKneeLeftJoint.addChild( &mAnkleLeftJoint );
+ mPelvisJoint.addChild( &mHipRightJoint );
+ mHipRightJoint.addChild( &mKneeRightJoint );
+ mKneeRightJoint.addChild( &mAnkleRightJoint );
+
+ mPelvisState = NULL;
+
+ mHipLeftState = NULL;
+ mKneeLeftState = NULL;
+ mAnkleLeftState = NULL;
+
+ mHipRightState = NULL;
+ mKneeRightState = NULL;
+ mAnkleRightState = NULL;
+
+ mTrackAnkles = TRUE;
+
+ mFrameNum = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeStandMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeStandMotion::~LLKeyframeStandMotion()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeStandMotion::onInitialize(LLCharacter *character)
+{
+ // save character pointer for later use
+ mCharacter = character;
+
+ mFlipFeet = FALSE;
+
+ // load keyframe data, setup pose and joint states
+ LLMotion::LLMotionInitStatus status = LLKeyframeMotion::onInitialize(character);
+ if ( status == STATUS_FAILURE )
+ {
+ return status;
+ }
+
+ // find the necessary joint states
+ LLPose *pose = getPose();
+ mPelvisState = pose->findJointState("mPelvis");
+
+ mHipLeftState = pose->findJointState("mHipLeft");
+ mKneeLeftState = pose->findJointState("mKneeLeft");
+ mAnkleLeftState = pose->findJointState("mAnkleLeft");
+
+ mHipRightState = pose->findJointState("mHipRight");
+ mKneeRightState = pose->findJointState("mKneeRight");
+ mAnkleRightState = pose->findJointState("mAnkleRight");
+
+ if ( !mPelvisState ||
+ !mHipLeftState ||
+ !mKneeLeftState ||
+ !mAnkleLeftState ||
+ !mHipRightState ||
+ !mKneeRightState ||
+ !mAnkleRightState )
+ {
+ llinfos << getName() << ": Can't find necessary joint states" << llendl;
+ return STATUS_FAILURE;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeStandMotion::onActivate()
+{
+ //-------------------------------------------------------------------------
+ // setup the IK solvers
+ //-------------------------------------------------------------------------
+ mIKLeft.setPoleVector( LLVector3(1.0f, 0.0f, 0.0f));
+ mIKRight.setPoleVector( LLVector3(1.0f, 0.0f, 0.0f));
+ mIKLeft.setBAxis( LLVector3(0.05f, 1.0f, 0.0f));
+ mIKRight.setBAxis( LLVector3(-0.05f, 1.0f, 0.0f));
+
+ mLastGoodPelvisRotation.loadIdentity();
+ mLastGoodPosition.clearVec();
+
+ mFrameNum = 0;
+
+ return LLKeyframeMotion::onActivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLKeyframeStandMotion::onDeactivate()
+{
+ LLKeyframeMotion::onDeactivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeStandMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ //-------------------------------------------------------------------------
+ // let the base class update the cycle
+ //-------------------------------------------------------------------------
+ BOOL status = LLKeyframeMotion::onUpdate(time, joint_mask);
+ if (!status)
+ {
+ return FALSE;
+ }
+
+ LLVector3 root_world_pos = mPelvisState->getJoint()->getParent()->getWorldPosition();
+
+ // have we received a valid world position for this avatar?
+ if (root_world_pos.isExactlyZero())
+ {
+ return TRUE;
+ }
+
+ //-------------------------------------------------------------------------
+ // Stop tracking (start locking) ankles once ease in is done.
+ // Setting this here ensures we track until we get valid foot position.
+ //-------------------------------------------------------------------------
+ if (dot(mPelvisState->getJoint()->getWorldRotation(), mLastGoodPelvisRotation) < ROTATION_THRESHOLD)
+ {
+ mLastGoodPelvisRotation = mPelvisState->getJoint()->getWorldRotation();
+ mLastGoodPelvisRotation.normQuat();
+ mTrackAnkles = TRUE;
+ }
+ else if ((mCharacter->getCharacterPosition() - mLastGoodPosition).magVecSquared() > POSITION_THRESHOLD)
+ {
+ mLastGoodPosition = mCharacter->getCharacterPosition();
+ mTrackAnkles = TRUE;
+ }
+ else if (mPose.getWeight() < 1.f)
+ {
+ mTrackAnkles = TRUE;
+ }
+
+
+ //-------------------------------------------------------------------------
+ // propagate joint positions to internal versions
+ //-------------------------------------------------------------------------
+ mPelvisJoint.setPosition(
+ root_world_pos +
+ mPelvisState->getPosition() );
+
+ mHipLeftJoint.setPosition( mHipLeftState->getJoint()->getPosition() );
+ mKneeLeftJoint.setPosition( mKneeLeftState->getJoint()->getPosition() );
+ mAnkleLeftJoint.setPosition( mAnkleLeftState->getJoint()->getPosition() );
+
+ mHipLeftJoint.setScale( mHipLeftState->getJoint()->getScale() );
+ mKneeLeftJoint.setScale( mKneeLeftState->getJoint()->getScale() );
+ mAnkleLeftJoint.setScale( mAnkleLeftState->getJoint()->getScale() );
+
+ mHipRightJoint.setPosition( mHipRightState->getJoint()->getPosition() );
+ mKneeRightJoint.setPosition( mKneeRightState->getJoint()->getPosition() );
+ mAnkleRightJoint.setPosition( mAnkleRightState->getJoint()->getPosition() );
+
+ mHipRightJoint.setScale( mHipRightState->getJoint()->getScale() );
+ mKneeRightJoint.setScale( mKneeRightState->getJoint()->getScale() );
+ mAnkleRightJoint.setScale( mAnkleRightState->getJoint()->getScale() );
+ //-------------------------------------------------------------------------
+ // propagate joint rotations to internal versions
+ //-------------------------------------------------------------------------
+ mPelvisJoint.setRotation( mPelvisState->getJoint()->getWorldRotation() );
+
+#if GO_TO_KEY_POSE
+ mHipLeftJoint.setRotation( mHipLeftState->getRotation() );
+ mKneeLeftJoint.setRotation( mKneeLeftState->getRotation() );
+ mAnkleLeftJoint.setRotation( mAnkleLeftState->getRotation() );
+
+ mHipRightJoint.setRotation( mHipRightState->getRotation() );
+ mKneeRightJoint.setRotation( mKneeRightState->getRotation() );
+ mAnkleRightJoint.setRotation( mAnkleRightState->getRotation() );
+#else
+ mHipLeftJoint.setRotation( mHipLeftState->getJoint()->getRotation() );
+ mKneeLeftJoint.setRotation( mKneeLeftState->getJoint()->getRotation() );
+ mAnkleLeftJoint.setRotation( mAnkleLeftState->getJoint()->getRotation() );
+
+ mHipRightJoint.setRotation( mHipRightState->getJoint()->getRotation() );
+ mKneeRightJoint.setRotation( mKneeRightState->getJoint()->getRotation() );
+ mAnkleRightJoint.setRotation( mAnkleRightState->getJoint()->getRotation() );
+#endif
+
+ // need to wait for underlying keyframe motion to affect the skeleton
+ if (mFrameNum == 2)
+ {
+ mIKLeft.setupJoints( &mHipLeftJoint, &mKneeLeftJoint, &mAnkleLeftJoint, &mTargetLeft );
+ mIKRight.setupJoints( &mHipRightJoint, &mKneeRightJoint, &mAnkleRightJoint, &mTargetRight );
+ }
+ else if (mFrameNum < 2)
+ {
+ mFrameNum++;
+ return TRUE;
+ }
+
+ mFrameNum++;
+
+ //-------------------------------------------------------------------------
+ // compute target position by projecting ankles to the ground
+ //-------------------------------------------------------------------------
+ if ( mTrackAnkles )
+ {
+ mCharacter->getGround( mAnkleLeftJoint.getWorldPosition(), mPositionLeft, mNormalLeft);
+ mCharacter->getGround( mAnkleRightJoint.getWorldPosition(), mPositionRight, mNormalRight);
+
+ mTargetLeft.setPosition( mPositionLeft );
+ mTargetRight.setPosition( mPositionRight );
+ }
+
+ //-------------------------------------------------------------------------
+ // update solvers
+ //-------------------------------------------------------------------------
+ mIKLeft.solve();
+ mIKRight.solve();
+
+ //-------------------------------------------------------------------------
+ // make ankle rotation conform to the ground
+ //-------------------------------------------------------------------------
+ if ( mTrackAnkles )
+ {
+ LLVector4 dirLeft4 = mAnkleLeftJoint.getWorldMatrix().getFwdRow4();
+ LLVector4 dirRight4 = mAnkleRightJoint.getWorldMatrix().getFwdRow4();
+ LLVector3 dirLeft = vec4to3( dirLeft4 );
+ LLVector3 dirRight = vec4to3( dirRight4 );
+
+ LLVector3 up;
+ LLVector3 dir;
+ LLVector3 left;
+
+ up = mNormalLeft;
+ up.normVec();
+ if (mFlipFeet)
+ {
+ up *= -1.0f;
+ }
+ dir = dirLeft;
+ dir.normVec();
+ left = up % dir;
+ left.normVec();
+ dir = left % up;
+ mRotationLeft = LLQuaternion( dir, left, up );
+
+ up = mNormalRight;
+ up.normVec();
+ if (mFlipFeet)
+ {
+ up *= -1.0f;
+ }
+ dir = dirRight;
+ dir.normVec();
+ left = up % dir;
+ left.normVec();
+ dir = left % up;
+ mRotationRight = LLQuaternion( dir, left, up );
+ }
+ mAnkleLeftJoint.setWorldRotation( mRotationLeft );
+ mAnkleRightJoint.setWorldRotation( mRotationRight );
+
+ //-------------------------------------------------------------------------
+ // propagate joint rotations to joint states
+ //-------------------------------------------------------------------------
+ mHipLeftState->setRotation( mHipLeftJoint.getRotation() );
+ mKneeLeftState->setRotation( mKneeLeftJoint.getRotation() );
+ mAnkleLeftState->setRotation( mAnkleLeftJoint.getRotation() );
+
+ mHipRightState->setRotation( mHipRightJoint.getRotation() );
+ mKneeRightState->setRotation( mKneeRightJoint.getRotation() );
+ mAnkleRightState->setRotation( mAnkleRightJoint.getRotation() );
+
+ //llinfos << "Stand drift amount " << (mCharacter->getCharacterPosition() - mLastGoodPosition).magVec() << llendl;
+
+// llinfos << "DEBUG: " << speed << " : " << mTrackAnkles << llendl;
+ return TRUE;
+}
+
+// End
diff --git a/indra/llcharacter/llkeyframestandmotion.h b/indra/llcharacter/llkeyframestandmotion.h
new file mode 100644
index 0000000000..a82c92d07f
--- /dev/null
+++ b/indra/llcharacter/llkeyframestandmotion.h
@@ -0,0 +1,98 @@
+/**
+ * @file llkeyframestandmotion.h
+ * @brief Implementation of LLKeyframeStandMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYFRAMESTANDMOTION_H
+#define LL_LLKEYFRAMESTANDMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llkeyframemotion.h"
+#include "lljointsolverrp3.h"
+
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeStandMotion
+//-----------------------------------------------------------------------------
+class LLKeyframeStandMotion :
+ public LLKeyframeMotion
+{
+public:
+ // Constructor
+ LLKeyframeStandMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeStandMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLKeyframeStandMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ void onDeactivate();
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+public:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+
+ BOOL mFlipFeet;
+
+ LLJointState *mPelvisState;
+
+ LLJointState *mHipLeftState;
+ LLJointState *mKneeLeftState;
+ LLJointState *mAnkleLeftState;
+
+ LLJointState *mHipRightState;
+ LLJointState *mKneeRightState;
+ LLJointState *mAnkleRightState;
+
+ LLJoint mPelvisJoint;
+
+ LLJoint mHipLeftJoint;
+ LLJoint mKneeLeftJoint;
+ LLJoint mAnkleLeftJoint;
+ LLJoint mTargetLeft;
+
+ LLJoint mHipRightJoint;
+ LLJoint mKneeRightJoint;
+ LLJoint mAnkleRightJoint;
+ LLJoint mTargetRight;
+
+ LLJointSolverRP3 mIKLeft;
+ LLJointSolverRP3 mIKRight;
+
+ LLVector3 mPositionLeft;
+ LLVector3 mPositionRight;
+ LLVector3 mNormalLeft;
+ LLVector3 mNormalRight;
+ LLQuaternion mRotationLeft;
+ LLQuaternion mRotationRight;
+
+ LLQuaternion mLastGoodPelvisRotation;
+ LLVector3 mLastGoodPosition;
+ BOOL mTrackAnkles;
+
+ S32 mFrameNum;
+};
+
+#endif // LL_LLKEYFRAMESTANDMOTION_H
+
diff --git a/indra/llcharacter/llkeyframewalkmotion.cpp b/indra/llcharacter/llkeyframewalkmotion.cpp
new file mode 100644
index 0000000000..930427aa7a
--- /dev/null
+++ b/indra/llcharacter/llkeyframewalkmotion.cpp
@@ -0,0 +1,379 @@
+/**
+ * @file llkeyframewalkmotion.cpp
+ * @brief Implementation of LLKeyframeWalkMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llkeyframewalkmotion.h"
+#include "llcharacter.h"
+#include "llmath.h"
+#include "m3math.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+const F32 MAX_WALK_PLAYBACK_SPEED = 8.f; // max m/s for which we adjust walk cycle speed
+
+const F32 MIN_WALK_SPEED = 0.1f; // minimum speed at which we use velocity for down foot detection
+const F32 MAX_TIME_DELTA = 2.f; //max two seconds a frame for calculating interpolation
+const F32 SPEED_ADJUST_MAX = 2.5f; // maximum adjustment of walk animation playback speed
+const F32 SPEED_ADJUST_MAX_SEC = 3.f; // maximum adjustment to walk animation playback speed for a second
+const F32 DRIFT_COMP_MAX_TOTAL = 0.07f;//0.55f; // maximum drift compensation overall, in any direction
+const F32 DRIFT_COMP_MAX_SPEED = 4.f; // speed at which drift compensation total maxes out
+const F32 MAX_ROLL = 0.6f;
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeWalkMotion::LLKeyframeWalkMotion(const LLUUID &id) : LLKeyframeMotion(id)
+{
+ mRealTimeLast = 0.0f;
+ mAdjTimeLast = 0.0f;
+ mCharacter = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeWalkMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeWalkMotion::~LLKeyframeWalkMotion()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeWalkMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ return LLKeyframeMotion::onInitialize(character);
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeWalkMotion::onActivate()
+{
+ mRealTimeLast = 0.0f;
+ mAdjTimeLast = 0.0f;
+
+ return LLKeyframeMotion::onActivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLKeyframeWalkMotion::onDeactivate()
+{
+ mCharacter->removeAnimationData("Down Foot");
+ LLKeyframeMotion::onDeactivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeWalkMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ // compute time since last update
+ F32 deltaTime = time - mRealTimeLast;
+
+ void* speed_ptr = mCharacter->getAnimationData("Walk Speed");
+ F32 speed = (speed_ptr) ? *((F32 *)speed_ptr) : 1.f;
+
+ // adjust the passage of time accordingly
+ F32 adjusted_time = mAdjTimeLast + (deltaTime * speed);
+
+ // save time for next update
+ mRealTimeLast = time;
+ mAdjTimeLast = adjusted_time;
+
+ // handle wrap around
+ if (adjusted_time < 0.0f)
+ {
+ adjusted_time = getDuration() + fmod(adjusted_time, getDuration());
+ }
+
+ // let the base class update the cycle
+ return LLKeyframeMotion::onUpdate( adjusted_time, joint_mask );
+}
+
+// End
+
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLWalkAdjustMotion::LLWalkAdjustMotion(const LLUUID &id) : LLMotion(id)
+{
+ mLastTime = 0.f;
+ mName = "walk_adjust";
+}
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLWalkAdjustMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+ mLeftAnkleJoint = mCharacter->getJoint("mAnkleLeft");
+ mRightAnkleJoint = mCharacter->getJoint("mAnkleRight");
+
+ mPelvisJoint = mCharacter->getJoint("mPelvis");
+ mPelvisState.setJoint( mPelvisJoint );
+ if ( !mPelvisJoint )
+ {
+ llwarns << getName() << ": Can't get pelvis joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mPelvisState.setUsage(LLJointState::POS);
+ addJointState( &mPelvisState );
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLWalkAdjustMotion::onActivate()
+{
+ mAvgCorrection = 0.f;
+ mSpeedAdjust = 0.f;
+ mAnimSpeed = 0.f;
+ mAvgSpeed = 0.f;
+ mRelativeDir = 1.f;
+ mPelvisState.setPosition(LLVector3::zero);
+ // store ankle positions for next frame
+ mLastLeftAnklePos = mCharacter->getPosGlobalFromAgent(mLeftAnkleJoint->getWorldPosition());
+ mLastRightAnklePos = mCharacter->getPosGlobalFromAgent(mRightAnkleJoint->getWorldPosition());
+
+ F32 leftAnkleOffset = (mLeftAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec();
+ F32 rightAnkleOffset = (mRightAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec();
+ mAnkleOffset = llmax(leftAnkleOffset, rightAnkleOffset);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ LLVector3 footCorrection;
+ LLVector3 vel = mCharacter->getCharacterVelocity() * mCharacter->getTimeDilation();
+ F32 deltaTime = llclamp(time - mLastTime, 0.f, MAX_TIME_DELTA);
+ mLastTime = time;
+
+ LLQuaternion inv_rotation = ~mPelvisJoint->getWorldRotation();
+
+ // get speed and normalize velocity vector
+ LLVector3 ang_vel = mCharacter->getCharacterAngularVelocity() * mCharacter->getTimeDilation();
+ F32 speed = llmin(vel.normVec(), MAX_WALK_PLAYBACK_SPEED);
+ mAvgSpeed = lerp(mAvgSpeed, speed, LLCriticalDamp::getInterpolant(0.2f));
+
+ // calculate facing vector in pelvis-local space
+ // (either straight forward or back, depending on velocity)
+ LLVector3 localVel = vel * inv_rotation;
+ if (localVel.mV[VX] > 0.f)
+ {
+ mRelativeDir = 1.f;
+ }
+ else if (localVel.mV[VX] < 0.f)
+ {
+ mRelativeDir = -1.f;
+ }
+
+ // calculate world-space foot drift
+ LLVector3 leftFootDelta;
+ LLVector3 leftFootWorldPosition = mLeftAnkleJoint->getWorldPosition();
+ LLVector3d leftFootGlobalPosition = mCharacter->getPosGlobalFromAgent(leftFootWorldPosition);
+ leftFootDelta.setVec(mLastLeftAnklePos - leftFootGlobalPosition);
+ mLastLeftAnklePos = leftFootGlobalPosition;
+
+ LLVector3 rightFootDelta;
+ LLVector3 rightFootWorldPosition = mRightAnkleJoint->getWorldPosition();
+ LLVector3d rightFootGlobalPosition = mCharacter->getPosGlobalFromAgent(rightFootWorldPosition);
+ rightFootDelta.setVec(mLastRightAnklePos - rightFootGlobalPosition);
+ mLastRightAnklePos = rightFootGlobalPosition;
+
+ // find foot drift along velocity vector
+ if (mAvgSpeed > 0.1)
+ {
+ // walking/running
+ F32 leftFootDriftAmt = leftFootDelta * vel;
+ F32 rightFootDriftAmt = rightFootDelta * vel;
+
+ if (rightFootDriftAmt > leftFootDriftAmt)
+ {
+ footCorrection = rightFootDelta;
+ } else
+ {
+ footCorrection = leftFootDelta;
+ }
+ }
+ else
+ {
+ mAvgSpeed = ang_vel.magVec() * mAnkleOffset;
+ mRelativeDir = 1.f;
+
+ // standing/turning
+ // find the lower foot
+ if (leftFootWorldPosition.mV[VZ] < rightFootWorldPosition.mV[VZ])
+ {
+ // pivot on left foot
+ footCorrection = leftFootDelta;
+ }
+ else
+ {
+ // pivot on right foot
+ footCorrection = rightFootDelta;
+ }
+ }
+
+ // rotate into avatar coordinates
+ footCorrection = footCorrection * inv_rotation;
+
+ // calculate ideal pelvis offset so that foot is glued to ground and damp towards it
+ // the amount of foot slippage this frame + the offset applied last frame
+ mPelvisOffset = mPelvisState.getPosition() + lerp(LLVector3::zero, footCorrection, LLCriticalDamp::getInterpolant(0.2f));
+
+ // pelvis drift (along walk direction)
+ mAvgCorrection = lerp(mAvgCorrection, footCorrection.mV[VX] * mRelativeDir, LLCriticalDamp::getInterpolant(0.1f));
+
+ // calculate average velocity of foot slippage
+ F32 footSlipVelocity = (deltaTime != 0.f) ? (-mAvgCorrection / deltaTime) : 0.f;
+
+ F32 newSpeedAdjust = 0.f;
+
+ // modulate speed by dot products of facing and velocity
+ // so that if we are moving sideways, we slow down the animation
+ // and if we're moving backward, we walk backward
+
+ F32 directional_factor = localVel.mV[VX] * mRelativeDir;
+ if (speed > 0.1f)
+ {
+ // calculate ratio of desired foot velocity to detected foot velocity
+ newSpeedAdjust = llclamp(footSlipVelocity - mAvgSpeed * (1.f - directional_factor),
+ -SPEED_ADJUST_MAX, SPEED_ADJUST_MAX);
+ newSpeedAdjust = lerp(mSpeedAdjust, newSpeedAdjust, LLCriticalDamp::getInterpolant(0.2f));
+
+ F32 speedDelta = newSpeedAdjust - mSpeedAdjust;
+ speedDelta = llclamp(speedDelta, -SPEED_ADJUST_MAX_SEC * deltaTime, SPEED_ADJUST_MAX_SEC * deltaTime);
+
+ mSpeedAdjust = mSpeedAdjust + speedDelta;
+ }
+ else
+ {
+ mSpeedAdjust = lerp(mSpeedAdjust, 0.f, LLCriticalDamp::getInterpolant(0.2f));
+ }
+
+ mAnimSpeed = (mAvgSpeed + mSpeedAdjust) * mRelativeDir;
+// char debug_text[64];
+// sprintf(debug_text, "Foot slip vel: %.2f", footSlipVelocity);
+// mCharacter->addDebugText(debug_text);
+// sprintf(debug_text, "Speed: %.2f", mAvgSpeed);
+// mCharacter->addDebugText(debug_text);
+// sprintf(debug_text, "Speed Adjust: %.2f", mSpeedAdjust);
+// mCharacter->addDebugText(debug_text);
+// sprintf(debug_text, "Animation Playback Speed: %.2f", mAnimSpeed);
+// mCharacter->addDebugText(debug_text);
+ mCharacter->setAnimationData("Walk Speed", &mAnimSpeed);
+
+ // clamp pelvis offset to a 90 degree arc behind the nominal position
+ F32 drift_comp_max = llclamp(speed, 0.f, DRIFT_COMP_MAX_SPEED) / DRIFT_COMP_MAX_SPEED;
+ drift_comp_max *= DRIFT_COMP_MAX_TOTAL;
+
+ LLVector3 currentPelvisPos = mPelvisState.getJoint()->getPosition();
+
+ // NB: this is an ADDITIVE amount that is accumulated every frame, so clamping it alone won't do the trick
+ // must clamp with absolute position of pelvis in mind
+ mPelvisOffset.mV[VX] = llclamp( mPelvisOffset.mV[VX], -drift_comp_max - currentPelvisPos.mV[VX], drift_comp_max - currentPelvisPos.mV[VX] );
+ mPelvisOffset.mV[VY] = llclamp( mPelvisOffset.mV[VY], -drift_comp_max - currentPelvisPos.mV[VY], drift_comp_max - currentPelvisPos.mV[VY]);
+ mPelvisOffset.mV[VZ] = 0.f;
+
+ // set position
+ mPelvisState.setPosition(mPelvisOffset);
+
+ mCharacter->setAnimationData("Pelvis Offset", &mPelvisOffset);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLWalkAdjustMotion::onDeactivate()
+{
+ mCharacter->removeAnimationData("Walk Speed");
+}
+
+//-----------------------------------------------------------------------------
+// LLFlyAdjustMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLFlyAdjustMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ LLJoint* pelvisJoint = mCharacter->getJoint("mPelvis");
+ mPelvisState.setJoint( pelvisJoint );
+ if ( !pelvisJoint )
+ {
+ llwarns << getName() << ": Can't get pelvis joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mPelvisState.setUsage(LLJointState::POS | LLJointState::ROT);
+ addJointState( &mPelvisState );
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLFlyAdjustMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLFlyAdjustMotion::onActivate()
+{
+ mPelvisState.setPosition(LLVector3::zero);
+ mPelvisState.setRotation(LLQuaternion::DEFAULT);
+ mRoll = 0.f;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFlyAdjustMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLFlyAdjustMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ LLVector3 ang_vel = mCharacter->getCharacterAngularVelocity() * mCharacter->getTimeDilation();
+ F32 speed = mCharacter->getCharacterVelocity().magVec();
+
+ F32 roll_factor = clamp_rescale(speed, 7.f, 15.f, 0.f, -MAX_ROLL);
+ F32 target_roll = llclamp(ang_vel.mV[VZ], -4.f, 4.f) * roll_factor;
+
+ // roll is critically damped interpolation between current roll and angular velocity-derived target roll
+ mRoll = lerp(mRoll, target_roll, LLCriticalDamp::getInterpolant(0.1f));
+
+// llinfos << mRoll << llendl;
+
+ LLQuaternion roll(mRoll, LLVector3(0.f, 0.f, 1.f));
+ mPelvisState.setRotation(roll);
+
+// F32 lerp_amt = LLCriticalDamp::getInterpolant(0.2f);
+//
+// LLVector3 pelvis_correction = mPelvisState.getPosition() - lerp(LLVector3::zero, mPelvisState.getJoint()->getPosition() + mPelvisState.getPosition(), lerp_amt);
+// mPelvisState.setPosition(pelvis_correction);
+ return TRUE;
+}
diff --git a/indra/llcharacter/llkeyframewalkmotion.h b/indra/llcharacter/llkeyframewalkmotion.h
new file mode 100644
index 0000000000..05286456d3
--- /dev/null
+++ b/indra/llcharacter/llkeyframewalkmotion.h
@@ -0,0 +1,158 @@
+/**
+ * @file llkeyframewalkmotion.h
+ * @brief Implementation of LLKeframeWalkMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYFRAMEWALKMOTION_H
+#define LL_LLKEYFRAMEWALKMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llkeyframemotion.h"
+#include "llcharacter.h"
+#include "v3dmath.h"
+
+#define MIN_REQUIRED_PIXEL_AREA_WALK_ADJUST (20.f)
+#define MIN_REQUIRED_PIXEL_AREA_FLY_ADJUST (20.f)
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeWalkMotion
+//-----------------------------------------------------------------------------
+class LLKeyframeWalkMotion :
+ public LLKeyframeMotion
+{
+ friend class LLWalkAdjustMotion;
+public:
+ // Constructor
+ LLKeyframeWalkMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeWalkMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLKeyframeWalkMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ virtual void onDeactivate();
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+public:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+ F32 mCyclePhase;
+ F32 mRealTimeLast;
+ F32 mAdjTimeLast;
+ S32 mDownFoot;
+};
+
+class LLWalkAdjustMotion : public LLMotion
+{
+public:
+ // Constructor
+ LLWalkAdjustMotion(const LLUUID &id);
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLWalkAdjustMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ virtual void onDeactivate();
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+ virtual LLJoint::JointPriority getPriority(){return LLJoint::HIGH_PRIORITY;}
+ virtual BOOL getLoop() { return TRUE; }
+ virtual F32 getDuration() { return 0.f; }
+ virtual F32 getEaseInDuration() { return 0.f; }
+ virtual F32 getEaseOutDuration() { return 0.f; }
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_WALK_ADJUST; }
+ virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+public:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+ LLJoint* mLeftAnkleJoint;
+ LLJoint* mRightAnkleJoint;
+ LLJointState mPelvisState;
+ LLJoint* mPelvisJoint;
+ LLVector3d mLastLeftAnklePos;
+ LLVector3d mLastRightAnklePos;
+ F32 mLastTime;
+ F32 mAvgCorrection;
+ F32 mSpeedAdjust;
+ F32 mAnimSpeed;
+ F32 mAvgSpeed;
+ F32 mRelativeDir;
+ LLVector3 mPelvisOffset;
+ F32 mAnkleOffset;
+};
+
+class LLFlyAdjustMotion : public LLMotion
+{
+public:
+ // Constructor
+ LLFlyAdjustMotion(const LLUUID &id) : LLMotion(id) {mName = "fly_adjust";}
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLFlyAdjustMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ virtual void onDeactivate() {};
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+ virtual LLJoint::JointPriority getPriority(){return LLJoint::HIGHER_PRIORITY;}
+ virtual BOOL getLoop() { return TRUE; }
+ virtual F32 getDuration() { return 0.f; }
+ virtual F32 getEaseInDuration() { return 0.f; }
+ virtual F32 getEaseOutDuration() { return 0.f; }
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_FLY_ADJUST; }
+ virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+protected:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+ LLJointState mPelvisState;
+ F32 mRoll;
+};
+
+#endif // LL_LLKeyframeWalkMotion_H
+
diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp
new file mode 100644
index 0000000000..a53956223c
--- /dev/null
+++ b/indra/llcharacter/llmotion.cpp
@@ -0,0 +1,131 @@
+/**
+ * @file llmotion.cpp
+ * @brief Implementation of LLMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llmotion.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLMotion class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLMotion::LLMotion( const LLUUID &id )
+{
+ mActivationTimestamp = 0.f;
+ mStopTimestamp = 0.f;
+ mSendStopTimestamp = F32_MAX;
+ mResidualWeight = 0.f;
+ mFadeWeight = 1.f;
+ mStopped = TRUE;
+ mActive = FALSE;
+ mDeactivateCallback = NULL;
+
+ memset(&mJointSignature[0][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+ memset(&mJointSignature[1][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+ memset(&mJointSignature[2][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+
+ mID = id;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLMotion::~LLMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// fadeOut()
+//-----------------------------------------------------------------------------
+void LLMotion::fadeOut()
+{
+ if (mFadeWeight > 0.01f)
+ {
+ mFadeWeight = lerp(mFadeWeight, 0.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+ else
+ {
+ mFadeWeight = 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// fadeIn()
+//-----------------------------------------------------------------------------
+void LLMotion::fadeIn()
+{
+ if (mFadeWeight < 0.99f)
+ {
+ mFadeWeight = lerp(mFadeWeight, 1.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+ else
+ {
+ mFadeWeight = 1.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// addJointState()
+//-----------------------------------------------------------------------------
+void LLMotion::addJointState(LLJointState* jointState)
+{
+ mPose.addJointState(jointState);
+ S32 priority = jointState->getPriority();
+ if (priority == LLJoint::USE_MOTION_PRIORITY)
+ {
+ priority = getPriority();
+ }
+
+ U32 usage = jointState->getUsage();
+
+ // for now, usage is everything
+ mJointSignature[0][jointState->getJoint()->getJointNum()] = (usage & LLJointState::POS) ? (0xff >> (7 - priority)) : 0;
+ mJointSignature[1][jointState->getJoint()->getJointNum()] = (usage & LLJointState::ROT) ? (0xff >> (7 - priority)) : 0;
+ mJointSignature[2][jointState->getJoint()->getJointNum()] = (usage & LLJointState::SCALE) ? (0xff >> (7 - priority)) : 0;
+}
+
+void LLMotion::setDeactivateCallback( void (*cb)(void *), void* userdata )
+{
+ mDeactivateCallback = cb;
+ mDeactivateCallbackUserData = userdata;
+}
+
+//-----------------------------------------------------------------------------
+// activate()
+//-----------------------------------------------------------------------------
+void LLMotion::activate()
+{
+ mStopped = FALSE;
+ mActive = TRUE;
+ onActivate();
+}
+
+//-----------------------------------------------------------------------------
+// deactivate()
+//-----------------------------------------------------------------------------
+void LLMotion::deactivate()
+{
+ mActive = FALSE;
+
+ if (mDeactivateCallback) (*mDeactivateCallback)(mDeactivateCallbackUserData);
+
+ onDeactivate();
+}
+
+// End
diff --git a/indra/llcharacter/llmotion.h b/indra/llcharacter/llmotion.h
new file mode 100644
index 0000000000..66882a2c11
--- /dev/null
+++ b/indra/llcharacter/llmotion.h
@@ -0,0 +1,237 @@
+/**
+ * @file llmotion.h
+ * @brief Implementation of LLMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMOTION_H
+#define LL_LLMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include <string>
+
+#include "llerror.h"
+#include "llpose.h"
+#include "lluuid.h"
+
+class LLCharacter;
+
+//-----------------------------------------------------------------------------
+// class LLMotion
+//-----------------------------------------------------------------------------
+class LLMotion
+{
+public:
+ enum LLMotionBlendType
+ {
+ NORMAL_BLEND,
+ ADDITIVE_BLEND
+ };
+
+ enum LLMotionInitStatus
+ {
+ STATUS_FAILURE,
+ STATUS_SUCCESS,
+ STATUS_HOLD
+ };
+
+ // Constructor
+ LLMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return NULL; }
+
+ // get the name of this instance
+ const std::string &getName() const { return mName; }
+
+ // set the name of this instance
+ void setName(const std::string &name) { mName = name; }
+
+ const LLUUID& getID() const { return mID; }
+
+ // returns the pose associated with the current state of this motion
+ virtual LLPose* getPose() { return &mPose;}
+
+ void fadeOut();
+
+ void fadeIn();
+
+ F32 getFadeWeight() const { return mFadeWeight; }
+
+ F32 getStopTime() const { return mStopTimestamp; }
+
+ virtual void setStopTime(F32 time) { mStopTimestamp = time; mStopped = TRUE; }
+
+ BOOL isStopped() const { return mStopped; }
+
+ void setStopped(BOOL stopped) { mStopped = stopped; }
+
+ void activate();
+
+ void deactivate();
+
+ BOOL isActive() { return mActive; }
+
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() = 0;
+
+ // motions must report their total duration
+ virtual F32 getDuration() = 0;
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() = 0;
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() = 0;
+
+ // motions must report their priority level
+ virtual LLJoint::JointPriority getPriority() = 0;
+
+ // motions must report their blend type
+ virtual LLMotionBlendType getBlendType() = 0;
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() = 0;
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character) = 0;
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 activeTime, U8* joint_mask) = 0;
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() = 0;
+
+ // optional callback routine called when animation deactivated.
+ void setDeactivateCallback( void (*cb)(void *), void* userdata );
+
+protected:
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate() = 0;
+
+ void addJointState(LLJointState* jointState);
+
+protected:
+ LLPose mPose;
+ BOOL mStopped; // motion has been stopped;
+ BOOL mActive; // motion is on active list (can be stopped or not stopped)
+
+public:
+ //-------------------------------------------------------------------------
+ // these are set implicitly by the motion controller and
+ // may be referenced (read only) in the above handlers.
+ //-------------------------------------------------------------------------
+ std::string mName; // instance name assigned by motion controller
+ LLUUID mID;
+
+ F32 mActivationTimestamp; // time when motion was activated
+ F32 mStopTimestamp; // time when motion was told to stop
+ F32 mSendStopTimestamp; // time when simulator should be told to stop this motion
+ F32 mResidualWeight; // blend weight at beginning of stop motion phase
+ F32 mFadeWeight; // for fading in and out based on LOD
+ U8 mJointSignature[3][LL_CHARACTER_MAX_JOINTS]; // signature of which joints are animated at what priority
+ void (*mDeactivateCallback)(void* data);
+ void* mDeactivateCallbackUserData;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLTestMotion
+//-----------------------------------------------------------------------------
+class LLTestMotion : public LLMotion
+{
+public:
+ LLTestMotion(const LLUUID &id) : LLMotion(id){}
+ ~LLTestMotion() {}
+ static LLMotion *create(const LLUUID& id) { return new LLTestMotion(id); }
+ BOOL getLoop() { return FALSE; }
+ F32 getDuration() { return 0.0f; }
+ F32 getEaseInDuration() { return 0.0f; }
+ F32 getEaseOutDuration() { return 0.0f; }
+ LLJoint::JointPriority getPriority() { return LLJoint::HIGH_PRIORITY; }
+ LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+ F32 getMinPixelArea() { return 0.f; }
+
+ LLMotionInitStatus onInitialize(LLCharacter*) { llinfos << "LLTestMotion::onInitialize()" << llendl; return STATUS_SUCCESS; }
+ BOOL onActivate() { llinfos << "LLTestMotion::onActivate()" << llendl; return TRUE; }
+ BOOL onUpdate(F32 time, U8* joint_mask) { llinfos << "LLTestMotion::onUpdate(" << time << ")" << llendl; return TRUE; }
+ void onDeactivate() { llinfos << "LLTestMotion::onDeactivate()" << llendl; }
+};
+
+
+//-----------------------------------------------------------------------------
+// LLNullMotion
+//-----------------------------------------------------------------------------
+class LLNullMotion : public LLMotion
+{
+public:
+ LLNullMotion(const LLUUID &id) : LLMotion(id) {}
+ ~LLNullMotion() {}
+ static LLMotion *create(const LLUUID &id) { return new LLNullMotion(id); }
+
+ // motions must specify whether or not they loop
+ /*virtual*/ BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ /*virtual*/ F32 getDuration() { return 1.f; }
+
+ // motions must report their "ease in" duration
+ /*virtual*/ F32 getEaseInDuration() { return 0.f; }
+
+ // motions must report their "ease out" duration.
+ /*virtual*/ F32 getEaseOutDuration() { return 0.f; }
+
+ // motions must report their priority level
+ /*virtual*/ LLJoint::JointPriority getPriority() { return LLJoint::HIGH_PRIORITY; }
+
+ // motions must report their blend type
+ /*virtual*/ LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ /*virtual*/ F32 getMinPixelArea() { return 0.f; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ /*virtual*/ LLMotionInitStatus onInitialize(LLCharacter *character) { return STATUS_SUCCESS; }
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ /*virtual*/ BOOL onActivate() { return TRUE; }
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ /*virtual*/ BOOL onUpdate(F32 activeTime, U8* joint_mask) { return TRUE; }
+
+ // called when a motion is deactivated
+ /*virtual*/ void onDeactivate() {}
+};
+#endif // LL_LLMOTION_H
+
diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp
new file mode 100644
index 0000000000..7ec67b5fd4
--- /dev/null
+++ b/indra/llcharacter/llmotioncontroller.cpp
@@ -0,0 +1,927 @@
+/**
+ * @file llmotioncontroller.cpp
+ * @brief Implementation of LLMotionController class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llmotioncontroller.h"
+#include "llkeyframemotion.h"
+#include "llmath.h"
+#include "lltimer.h"
+#include "llanimationstates.h"
+#include "llstl.h"
+
+const S32 NUM_JOINT_SIGNATURE_STRIDES = LL_CHARACTER_MAX_JOINTS / 4;
+const U32 MAX_MOTION_INSTANCES = 32;
+
+//-----------------------------------------------------------------------------
+// Constants and statics
+//-----------------------------------------------------------------------------
+LLMotionRegistry LLMotionController::sRegistry;
+
+//-----------------------------------------------------------------------------
+// LLMotionTableEntry()
+//-----------------------------------------------------------------------------
+LLMotionTableEntry::LLMotionTableEntry()
+{
+ mConstructor = NULL;
+ mID.setNull();
+}
+
+LLMotionTableEntry::LLMotionTableEntry(LLMotionConstructor constructor, const LLUUID& id)
+ : mConstructor(constructor), mID(id)
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// create()
+//-----------------------------------------------------------------------------
+LLMotion* LLMotionTableEntry::create(const LLUUID &id)
+{
+ LLMotion* motionp = mConstructor(id);
+
+ return motionp;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLMotionRegistry class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLMotionRegistry()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLMotionRegistry::LLMotionRegistry() : mMotionTable(LLMotionTableEntry::uuidEq, LLMotionTableEntry())
+{
+
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLMotionRegistry()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLMotionRegistry::~LLMotionRegistry()
+{
+ mMotionTable.removeAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// addMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionRegistry::addMotion( const LLUUID& id, LLMotionConstructor constructor )
+{
+// llinfos << "Registering motion: " << name << llendl;
+ if (!mMotionTable.check(id))
+ {
+ mMotionTable.set(id, LLMotionTableEntry(constructor, id));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// markBad()
+//-----------------------------------------------------------------------------
+void LLMotionRegistry::markBad( const LLUUID& id )
+{
+ mMotionTable.set(id, LLMotionTableEntry());
+}
+
+//-----------------------------------------------------------------------------
+// createMotion()
+//-----------------------------------------------------------------------------
+LLMotion *LLMotionRegistry::createMotion( const LLUUID &id )
+{
+ LLMotionTableEntry motion_entry = mMotionTable.get(id);
+ LLMotion* motion = NULL;
+
+ if ( motion_entry.getID().isNull() )
+ {
+ //FIXME - RN: need to replace with a better default scheme
+ motion = LLKeyframeMotion::create(id);
+ }
+ else
+ {
+ motion = motion_entry.create(id);
+ }
+
+ return motion;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLMotionController class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLMotionController()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLMotionController::LLMotionController( )
+{
+ mTime = 0.f;
+ mTimeOffset = 0.f;
+ mLastTime = 0.0f;
+ mHasRunOnce = FALSE;
+ mPaused = FALSE;
+ mPauseTime = 0.f;
+ mTimeStep = 0.f;
+ mTimeStepCount = 0;
+ mLastInterp = 0.f;
+ mTimeFactor = 1.f;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLMotionController()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLMotionController::~LLMotionController()
+{
+ deleteAllMotions();
+}
+
+//-----------------------------------------------------------------------------
+// deleteAllMotions()
+//-----------------------------------------------------------------------------
+void LLMotionController::deleteAllMotions()
+{
+ mLoadingMotions.removeAllNodes();
+ mLoadedMotions.clear();
+ mActiveMotions.removeAllNodes();
+
+ for_each(mAllMotions.begin(), mAllMotions.end(), DeletePairedPointer());
+ mAllMotions.clear();
+}
+
+//-----------------------------------------------------------------------------
+// addLoadedMotion()
+//-----------------------------------------------------------------------------
+void LLMotionController::addLoadedMotion(LLMotion* motionp)
+{
+ if (mLoadedMotions.size() > MAX_MOTION_INSTANCES)
+ {
+ // too many motions active this frame, kill all blenders
+ mPoseBlender.clearBlenders();
+
+ for (U32 i = 0; i < mLoadedMotions.size(); i++)
+ {
+ LLMotion* cur_motionp = mLoadedMotions.front();
+ mLoadedMotions.pop_front();
+
+ // motion isn't playing, delete it
+ if (!isMotionActive(cur_motionp))
+ {
+ mCharacter->requestStopMotion(cur_motionp);
+ mAllMotions.erase(cur_motionp->getID());
+ delete cur_motionp;
+ if (mLoadedMotions.size() <= MAX_MOTION_INSTANCES)
+ {
+ break;
+ }
+ }
+ else
+ {
+ // put active motion on back
+ mLoadedMotions.push_back(cur_motionp);
+ }
+ }
+ }
+ mLoadedMotions.push_back(motionp);
+}
+
+//-----------------------------------------------------------------------------
+// setTimeStep()
+//-----------------------------------------------------------------------------
+void LLMotionController::setTimeStep(F32 step)
+{
+ mTimeStep = step;
+
+ if (step != 0.f)
+ {
+ // make sure timestamps conform to new quantum
+ for( LLMotion* motionp = mActiveMotions.getFirstData();
+ motionp != NULL;
+ motionp = mActiveMotions.getNextData() )
+ {
+ motionp->mActivationTimestamp = (F32)llfloor(motionp->mActivationTimestamp / step) * step;
+ BOOL stopped = motionp->isStopped();
+ motionp->setStopTime((F32)llfloor(motionp->getStopTime() / step) * step);
+ motionp->setStopped(stopped);
+ motionp->mSendStopTimestamp = (F32)llfloor(motionp->mSendStopTimestamp / step) * step;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setTimeFactor()
+//-----------------------------------------------------------------------------
+void LLMotionController::setTimeFactor(F32 time_factor)
+{
+ mTimeOffset += mTimer.getElapsedTimeAndResetF32() * mTimeFactor;
+ mTimeFactor = time_factor;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstActiveMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLMotionController::getFirstActiveMotion()
+{
+ return mActiveMotions.getFirstData();
+}
+
+//-----------------------------------------------------------------------------
+// getNextActiveMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLMotionController::getNextActiveMotion()
+{
+ return mActiveMotions.getNextData();
+}
+
+
+//-----------------------------------------------------------------------------
+// setCharacter()
+//-----------------------------------------------------------------------------
+void LLMotionController::setCharacter(LLCharacter *character)
+{
+ mCharacter = character;
+}
+
+
+//-----------------------------------------------------------------------------
+// addMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::addMotion( const LLUUID& id, LLMotionConstructor constructor )
+{
+ return sRegistry.addMotion(id, constructor);
+}
+
+//-----------------------------------------------------------------------------
+// removeMotion()
+//-----------------------------------------------------------------------------
+void LLMotionController::removeMotion( const LLUUID& id)
+{
+ LLMotion* motionp = findMotion(id);
+ if (motionp)
+ {
+ stopMotionLocally(id, TRUE);
+
+ mLoadingMotions.deleteData(motionp);
+ std::deque<LLMotion*>::iterator motion_it;
+ for (motion_it = mLoadedMotions.begin(); motion_it != mLoadedMotions.end(); ++motion_it)
+ {
+ if(*motion_it == motionp)
+ {
+ mLoadedMotions.erase(motion_it);
+ break;
+ }
+ }
+ mActiveMotions.deleteData(motionp);
+ mAllMotions.erase(id);
+ delete motionp;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// createMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLMotionController::createMotion( const LLUUID &id )
+{
+ // do we have an instance of this motion for this character?
+ LLMotion *motion = findMotion(id);
+
+ // if not, we need to create one
+ if (!motion)
+ {
+ // look up constructor and create it
+ motion = sRegistry.createMotion(id);
+ if (!motion)
+ {
+ return NULL;
+ }
+
+ // look up name for default motions
+ const char* motion_name = gAnimLibrary.animStateToString(id);
+ if (motion_name)
+ {
+ motion->setName(motion_name);
+ }
+
+ // initialize the new instance
+ LLMotion::LLMotionInitStatus stat = motion->onInitialize(mCharacter);
+ switch(stat)
+ {
+ case LLMotion::STATUS_FAILURE:
+ llinfos << "Motion " << id << " init failed." << llendl;
+ sRegistry.markBad(id);
+ delete motion;
+ return NULL;
+ case LLMotion::STATUS_HOLD:
+ mLoadingMotions.addData(motion);
+ break;
+ case LLMotion::STATUS_SUCCESS:
+ // add motion to our list
+ addLoadedMotion(motion);
+ break;
+ default:
+ llerrs << "Invalid initialization status" << llendl;
+ break;
+ }
+
+ mAllMotions[id] = motion;
+ }
+ return motion;
+}
+
+//-----------------------------------------------------------------------------
+// startMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset)
+{
+ // look for motion in our list of created motions
+ LLMotion *motion = createMotion(id);
+
+ if (!motion)
+ {
+ return FALSE;
+ }
+ //if the motion is already active, then we're done
+ else if (isMotionActive(motion)) // motion is playing and...
+ {
+ if (motion->isStopped()) // motion has been stopped
+ {
+ deactivateMotion(motion);
+ }
+ else if (mTime < motion->mSendStopTimestamp) // motion is still active
+ {
+ return TRUE;
+ }
+ }
+
+// llinfos << "Starting motion " << name << llendl;
+ return activateMotion(motion, mTime - start_offset);
+}
+
+
+//-----------------------------------------------------------------------------
+// stopMotionLocally()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::stopMotionLocally(const LLUUID &id, BOOL stop_immediate)
+{
+ // if already inactive, return false
+ LLMotion *motion = findMotion(id);
+ if (!motion)
+ {
+ return FALSE;
+ }
+
+ // If on active list, stop it
+ if (isMotionActive(motion) && !motion->isStopped())
+ {
+ // when using timesteps, set stop time to last frame's time, otherwise grab current timer value
+ // FIXME: should investigate this inconsistency...hints of obscure bugs
+
+ F32 stop_time = (mTimeStep != 0.f || mPaused) ? (mTime) : mTimeOffset + (mTimer.getElapsedTimeF32() * mTimeFactor);
+ motion->setStopTime(stop_time);
+
+ if (stop_immediate)
+ {
+ deactivateMotion(motion);
+ }
+ return TRUE;
+ }
+ else if (isMotionLoading(motion))
+ {
+ motion->setStopped(TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateRegularMotions()
+//-----------------------------------------------------------------------------
+void LLMotionController::updateRegularMotions()
+{
+ updateMotionsByType(LLMotion::NORMAL_BLEND);
+}
+
+//-----------------------------------------------------------------------------
+// updateAdditiveMotions()
+//-----------------------------------------------------------------------------
+void LLMotionController::updateAdditiveMotions()
+{
+ updateMotionsByType(LLMotion::ADDITIVE_BLEND);
+}
+
+//-----------------------------------------------------------------------------
+// resetJointSignatures()
+//-----------------------------------------------------------------------------
+void LLMotionController::resetJointSignatures()
+{
+ memset(&mJointSignature[0][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+ memset(&mJointSignature[1][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+}
+
+//-----------------------------------------------------------------------------
+// updateMotionsByType()
+//-----------------------------------------------------------------------------
+void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_type)
+{
+ BOOL update_result = TRUE;
+ U8 last_joint_signature[LL_CHARACTER_MAX_JOINTS];
+
+ memset(&last_joint_signature, 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+
+ // iterate through active motions in chronological order
+ for(LLMotion* motionp = mActiveMotions.getFirstData();
+ motionp != NULL;
+ motionp = mActiveMotions.getNextData())
+ {
+ if (motionp->getBlendType() != anim_type)
+ {
+ continue;
+ }
+
+ BOOL update_motion = FALSE;
+
+ if (motionp->getPose()->getWeight() < 1.f)
+ {
+ update_motion = TRUE;
+ }
+ else
+ {
+ S32 i;
+ // NUM_JOINT_SIGNATURE_STRIDES should be multiple of 4
+ for (i = 0; i < NUM_JOINT_SIGNATURE_STRIDES; i++)
+ {
+ U32 *current_signature = (U32*)&(mJointSignature[0][i * 4]);
+ U32 test_signature = *(U32*)&(motionp->mJointSignature[0][i * 4]);
+
+ if ((*current_signature | test_signature) > (*current_signature))
+ {
+ *current_signature |= test_signature;
+ update_motion = TRUE;
+ }
+
+ *((U32*)&last_joint_signature[i * 4]) = *(U32*)&(mJointSignature[1][i * 4]);
+ current_signature = (U32*)&(mJointSignature[1][i * 4]);
+ test_signature = *(U32*)&(motionp->mJointSignature[1][i * 4]);
+
+ if ((*current_signature | test_signature) > (*current_signature))
+ {
+ *current_signature |= test_signature;
+ update_motion = TRUE;
+ }
+ }
+ }
+
+ if (!update_motion)
+ {
+ if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
+ {
+ deactivateMotion(motionp);
+ }
+ else if (motionp->isStopped() && mTime > motionp->getStopTime())
+ {
+ // is this the first iteration in the ease out phase?
+ if (mLastTime <= motionp->getStopTime())
+ {
+ // store residual weight for this motion
+ motionp->mResidualWeight = motionp->getPose()->getWeight();
+ }
+ }
+ else if (mTime > motionp->mSendStopTimestamp)
+ {
+ // notify character of timed stop event on first iteration past sendstoptimestamp
+ // this will only be called when an animation stops itself (runs out of time)
+ if (mLastTime <= motionp->mSendStopTimestamp)
+ {
+ mCharacter->requestStopMotion( motionp );
+ stopMotionLocally(motionp->getID(), FALSE);
+ }
+ }
+ else if (mTime >= motionp->mActivationTimestamp)
+ {
+ if (mLastTime < motionp->mActivationTimestamp)
+ {
+ motionp->mResidualWeight = motionp->getPose()->getWeight();
+ }
+ }
+ continue;
+ }
+
+ LLPose *posep = motionp->getPose();
+
+ // only filter by LOD after running every animation at least once (to prime the avatar state)
+ if (mHasRunOnce && motionp->getMinPixelArea() > mCharacter->getPixelArea())
+ {
+ motionp->fadeOut();
+
+ //should we notify the simulator that this motion should be stopped (check even if skipped by LOD logic)
+ if (mTime > motionp->mSendStopTimestamp)
+ {
+ // notify character of timed stop event on first iteration past sendstoptimestamp
+ // this will only be called when an animation stops itself (runs out of time)
+ if (mLastTime <= motionp->mSendStopTimestamp)
+ {
+ mCharacter->requestStopMotion( motionp );
+ stopMotionLocally(motionp->getID(), FALSE);
+ }
+ }
+
+ if (motionp->getFadeWeight() < 0.01f)
+ {
+ if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
+ {
+ posep->setWeight(0.f);
+ deactivateMotion(motionp);
+ }
+ continue;
+ }
+ }
+ else
+ {
+ motionp->fadeIn();
+ }
+
+ //**********************
+ // MOTION INACTIVE
+ //**********************
+ if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
+ {
+ // this motion has gone on too long, deactivate it
+ // did we have a chance to stop it?
+ if (mLastTime <= motionp->getStopTime())
+ {
+ // if not, let's stop it this time through and deactivate it the next
+
+ posep->setWeight(motionp->getFadeWeight());
+ motionp->onUpdate(motionp->getStopTime() - motionp->mActivationTimestamp, last_joint_signature);
+ }
+ else
+ {
+ posep->setWeight(0.f);
+ deactivateMotion(motionp);
+ continue;
+ }
+ }
+
+ //**********************
+ // MOTION EASE OUT
+ //**********************
+ else if (motionp->isStopped() && mTime > motionp->getStopTime())
+ {
+ // is this the first iteration in the ease out phase?
+ if (mLastTime <= motionp->getStopTime())
+ {
+ // store residual weight for this motion
+ motionp->mResidualWeight = motionp->getPose()->getWeight();
+ }
+
+ if (motionp->getEaseOutDuration() == 0.f)
+ {
+ posep->setWeight(0.f);
+ }
+ else
+ {
+ posep->setWeight(motionp->getFadeWeight() * motionp->mResidualWeight * cubic_step(1.f - ((mTime - motionp->getStopTime()) / motionp->getEaseOutDuration())));
+ }
+
+ // perform motion update
+ update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature);
+ }
+
+ //**********************
+ // MOTION ACTIVE
+ //**********************
+ else if (mTime > motionp->mActivationTimestamp + motionp->getEaseInDuration())
+ {
+ posep->setWeight(motionp->getFadeWeight());
+
+ //should we notify the simulator that this motion should be stopped?
+ if (mTime > motionp->mSendStopTimestamp)
+ {
+ // notify character of timed stop event on first iteration past sendstoptimestamp
+ // this will only be called when an animation stops itself (runs out of time)
+ if (mLastTime <= motionp->mSendStopTimestamp)
+ {
+ mCharacter->requestStopMotion( motionp );
+ stopMotionLocally(motionp->getID(), FALSE);
+ }
+ }
+
+ // perform motion update
+ update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature);
+ }
+
+ //**********************
+ // MOTION EASE IN
+ //**********************
+ else if (mTime >= motionp->mActivationTimestamp)
+ {
+ if (mLastTime < motionp->mActivationTimestamp)
+ {
+ motionp->mResidualWeight = motionp->getPose()->getWeight();
+ }
+ if (motionp->getEaseInDuration() == 0.f)
+ {
+ posep->setWeight(motionp->getFadeWeight());
+ }
+ else
+ {
+ // perform motion update
+ posep->setWeight(motionp->getFadeWeight() * motionp->mResidualWeight + (1.f - motionp->mResidualWeight) * cubic_step((mTime - motionp->mActivationTimestamp) / motionp->getEaseInDuration()));
+ }
+ // perform motion update
+ update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature);
+ }
+ else
+ {
+ posep->setWeight(0.f);
+ update_result = motionp->onUpdate(0.f, last_joint_signature);
+ }
+
+ // allow motions to deactivate themselves
+ if (!update_result)
+ {
+ if (!motionp->isStopped() || motionp->getStopTime() > mTime)
+ {
+ // animation has stopped itself due to internal logic
+ // propagate this to the network
+ // as not all viewers are guaranteed to have access to the same logic
+ mCharacter->requestStopMotion( motionp );
+ stopMotionLocally(motionp->getID(), FALSE);
+ }
+
+ }
+
+ // even if onupdate returns FALSE, add this motion in to the blend one last time
+ mPoseBlender.addMotion(motionp);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// updateMotion()
+//-----------------------------------------------------------------------------
+void LLMotionController::updateMotion()
+{
+ BOOL use_quantum = (mTimeStep != 0.f);
+
+ // Update timing info for this time step.
+ if (!mPaused)
+ {
+ F32 update_time = mTimeOffset + (mTimer.getElapsedTimeF32() * mTimeFactor);
+ if (use_quantum)
+ {
+ F32 time_interval = fmodf(update_time, mTimeStep);
+
+ // always animate *ahead* of actual time
+ S32 quantum_count = llmax(0, llfloor((update_time - time_interval) / mTimeStep)) + 1;
+ if (quantum_count == mTimeStepCount)
+ {
+ // we're still in same time quantum as before, so just interpolate and exit
+ if (!mPaused)
+ {
+ F32 interp = time_interval / mTimeStep;
+ mPoseBlender.interpolate(interp - mLastInterp);
+ mLastInterp = interp;
+ }
+
+ return;
+ }
+
+ // is calculating a new keyframe pose, make sure the last one gets applied
+ mPoseBlender.interpolate(1.f);
+ mPoseBlender.clearBlenders();
+
+ mTimeStepCount = quantum_count;
+ mLastTime = mTime;
+ mTime = (F32)quantum_count * mTimeStep;
+ mLastInterp = 0.f;
+ }
+ else
+ {
+ mLastTime = mTime;
+ mTime = update_time;
+ }
+ }
+
+ // query pending motions for completion
+ LLMotion* motionp;
+
+ for ( motionp = mLoadingMotions.getFirstData();
+ motionp != NULL;
+ motionp = mLoadingMotions.getNextData() )
+ {
+ LLMotion::LLMotionInitStatus status = motionp->onInitialize(mCharacter);
+ if (status == LLMotion::STATUS_SUCCESS)
+ {
+ mLoadingMotions.removeCurrentData();
+ // add motion to our loaded motion list
+ addLoadedMotion(motionp);
+ // this motion should be playing
+ if (!motionp->isStopped())
+ {
+ activateMotion(motionp, mTime);
+ }
+ }
+ else if (status == LLMotion::STATUS_FAILURE)
+ {
+ llinfos << "Motion " << motionp->getID() << " init failed." << llendl;
+ sRegistry.markBad(motionp->getID());
+ mLoadingMotions.removeCurrentData();
+ mAllMotions.erase(motionp->getID());
+ delete motionp;
+ }
+ }
+
+ resetJointSignatures();
+
+ if (!mPaused)
+ {
+ // update additive motions
+ updateAdditiveMotions();
+ resetJointSignatures();
+
+ // update all regular motions
+ updateRegularMotions();
+
+ if (use_quantum)
+ {
+ mPoseBlender.blendAndCache(TRUE);
+ }
+ else
+ {
+ mPoseBlender.blendAndApply();
+ }
+ }
+
+ mHasRunOnce = TRUE;
+// llinfos << "Motion controller time " << motionTimer.getElapsedTimeF32() << llendl;
+}
+
+
+//-----------------------------------------------------------------------------
+// activateMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::activateMotion(LLMotion *motion, F32 time)
+{
+ if (mLoadingMotions.checkData(motion))
+ {
+ // we want to start this motion, but we can't yet, so flag it as started
+ motion->setStopped(FALSE);
+ // report pending animations as activated
+ return TRUE;
+ }
+
+ motion->mResidualWeight = motion->getPose()->getWeight();
+ motion->mActivationTimestamp = time;
+
+ // set stop time based on given duration and ease out time
+ if (motion->getDuration() != 0.f && !motion->getLoop())
+ {
+ F32 ease_out_time;
+ F32 motion_duration;
+
+ // should we stop at the end of motion duration, or a bit earlier
+ // to allow it to ease out while moving?
+ ease_out_time = motion->getEaseOutDuration();
+
+ // is the clock running when the motion is easing in?
+ // if not (POSTURE_EASE) then we need to wait that much longer before triggering the stop
+ motion_duration = llmax(motion->getDuration() - ease_out_time, 0.f);
+ motion->mSendStopTimestamp = time + motion_duration;
+ }
+ else
+ {
+ motion->mSendStopTimestamp = F32_MAX;
+ }
+
+ mActiveMotions.addData(motion);
+
+ motion->activate();
+ motion->onUpdate(0.f, mJointSignature[1]);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// deactivateMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::deactivateMotion(LLMotion *motion)
+{
+ motion->deactivate();
+ mActiveMotions.removeData(motion);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// isMotionActive()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::isMotionActive(LLMotion *motion)
+{
+ if (motion && motion->isActive())
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// isMotionLoading()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::isMotionLoading(LLMotion* motion)
+{
+ return mLoadingMotions.checkData(motion);
+}
+
+
+//-----------------------------------------------------------------------------
+// findMotion()
+//-----------------------------------------------------------------------------
+LLMotion *LLMotionController::findMotion(const LLUUID& id)
+{
+ return mAllMotions[id];
+}
+
+
+//-----------------------------------------------------------------------------
+// flushAllMotions()
+//-----------------------------------------------------------------------------
+void LLMotionController::flushAllMotions()
+{
+ LLDynamicArray<LLUUID> active_motions;
+ LLDynamicArray<F32> active_motion_times;
+
+ for (LLMotion* motionp = mActiveMotions.getFirstData();
+ motionp;
+ motionp = mActiveMotions.getNextData())
+ {
+ active_motions.put(motionp->getID());
+ active_motion_times.put(mTime - motionp->mActivationTimestamp);
+ motionp->deactivate();
+ }
+
+ // delete all motion instances
+ deleteAllMotions();
+
+ // kill current hand pose that was previously called out by
+ // keyframe motion
+ mCharacter->removeAnimationData("Hand Pose");
+
+ // restart motions
+ for (S32 i = 0; i < active_motions.count(); i++)
+ {
+ startMotion(active_motions[i], active_motion_times[i]);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// pause()
+//-----------------------------------------------------------------------------
+void LLMotionController::pause()
+{
+ if (!mPaused)
+ {
+ //llinfos << "Pausing animations..." << llendl;
+ mPauseTime = mTimer.getElapsedTimeF32();
+ mPaused = TRUE;
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// unpause()
+//-----------------------------------------------------------------------------
+void LLMotionController::unpause()
+{
+ if (mPaused)
+ {
+ //llinfos << "Unpausing animations..." << llendl;
+ mTimer.reset();
+ mTimer.setAge(mPauseTime);
+ mPaused = FALSE;
+ }
+}
+// End
diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h
new file mode 100644
index 0000000000..d43d6d9a8f
--- /dev/null
+++ b/indra/llcharacter/llmotioncontroller.h
@@ -0,0 +1,207 @@
+/**
+ * @file llmotioncontroller.h
+ * @brief Implementation of LLMotionController class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMOTIONCONTROLLER_H
+#define LL_LLMOTIONCONTROLLER_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include <string>
+#include <map>
+#include <deque>
+
+#include "linked_lists.h"
+#include "lluuidhashmap.h"
+#include "llmotion.h"
+#include "llpose.h"
+#include "llframetimer.h"
+#include "llstatemachine.h"
+#include "llstring.h"
+
+//-----------------------------------------------------------------------------
+// Class predeclaration
+// This is necessary because llcharacter.h includes this file.
+//-----------------------------------------------------------------------------
+class LLCharacter;
+
+//-----------------------------------------------------------------------------
+// LLMotionRegistry
+//-----------------------------------------------------------------------------
+typedef LLMotion*(*LLMotionConstructor)(const LLUUID &id);
+
+class LLMotionTableEntry
+{
+public:
+ LLMotionTableEntry();
+ LLMotionTableEntry(LLMotionConstructor constructor, const LLUUID& id);
+ ~LLMotionTableEntry(){};
+
+ LLMotion* create(const LLUUID& id);
+ static BOOL uuidEq(const LLUUID &uuid, const LLMotionTableEntry &id_pair)
+ {
+ if (uuid == id_pair.mID)
+ {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ const LLUUID& getID() { return mID; }
+
+protected:
+ LLMotionConstructor mConstructor;
+ LLUUID mID;
+};
+
+class LLMotionRegistry
+{
+public:
+ // Constructor
+ LLMotionRegistry();
+
+ // Destructor
+ ~LLMotionRegistry();
+
+ // adds motion classes to the registry
+ // returns true if successfull
+ BOOL addMotion( const LLUUID& id, LLMotionConstructor create);
+
+ // creates a new instance of a named motion
+ // returns NULL motion is not registered
+ LLMotion *createMotion( const LLUUID &id );
+
+ // initialization of motion failed, don't try to create this motion again
+ void markBad( const LLUUID& id );
+
+
+protected:
+ LLUUIDHashMap<LLMotionTableEntry, 32> mMotionTable;
+};
+
+//-----------------------------------------------------------------------------
+// class LLMotionController
+//-----------------------------------------------------------------------------
+class LLMotionController
+{
+public:
+ // Constructor
+ LLMotionController();
+
+ // Destructor
+ virtual ~LLMotionController();
+
+ // set associated character
+ // this must be called exactly once by the containing character class.
+ // this is generally done in the Character constructor
+ void setCharacter( LLCharacter *character );
+
+ // registers a motion with the controller
+ // (actually just forwards call to motion registry)
+ // returns true if successfull
+ BOOL addMotion( const LLUUID& id, LLMotionConstructor create );
+
+ // creates a motion from the registry
+ LLMotion *createMotion( const LLUUID &id );
+
+ // unregisters a motion with the controller
+ // (actually just forwards call to motion registry)
+ // returns true if successfull
+ void removeMotion( const LLUUID& id );
+
+ // start motion
+ // begins playing the specified motion
+ // returns true if successful
+ BOOL startMotion( const LLUUID &id, F32 start_offset );
+
+ // stop motion
+ // stops a playing motion
+ // in reality, it begins the ease out transition phase
+ // returns true if successful
+ BOOL stopMotionLocally( const LLUUID &id, BOOL stop_immediate );
+
+ // update motions
+ // invokes the update handlers for each active motion
+ // activates sequenced motions
+ // deactivates terminated motions`
+ void updateMotion();
+
+ // flush motions
+ // releases all motion instances
+ void flushAllMotions();
+
+ // pause and continue all motions
+ void pause();
+ void unpause();
+ BOOL isPaused() { return mPaused; }
+
+ void setTimeStep(F32 step);
+
+ void setTimeFactor(F32 time_factor);
+ F32 getTimeFactor() { return mTimeFactor; }
+
+ LLMotion* getFirstActiveMotion();
+ LLMotion* getNextActiveMotion();
+
+//protected:
+ BOOL isMotionActive( LLMotion *motion );
+ BOOL isMotionLoading( LLMotion *motion );
+ LLMotion *findMotion( const LLUUID& id );
+
+protected:
+ void deleteAllMotions();
+ void addLoadedMotion(LLMotion *motion);
+ BOOL activateMotion(LLMotion *motion, F32 time);
+ BOOL deactivateMotion(LLMotion *motion);
+ void updateRegularMotions();
+ void updateAdditiveMotions();
+ void resetJointSignatures();
+ void updateMotionsByType(LLMotion::LLMotionBlendType motion_type);
+protected:
+
+ F32 mTimeFactor;
+ static LLMotionRegistry sRegistry;
+ LLPoseBlender mPoseBlender;
+
+ LLCharacter *mCharacter;
+
+// Life cycle of an animation:
+//
+// Animations are instantiated and immediately put in the mAllMotions map for their entire lifetime.
+// If the animations depend on any asset data, the appropriate data is fetched from the data server,
+// and the animation is put on the mLoadingMotions list.
+// Once an animations is loaded, it will be initialized and put on the mLoadedMotions deque.
+// Any animation that is currently playing also sits in the mActiveMotions list.
+
+ std::map<LLUUID, LLMotion*> mAllMotions;
+
+ LLLinkedList<LLMotion> mLoadingMotions;
+ std::deque<LLMotion*> mLoadedMotions;
+ LLLinkedList<LLMotion> mActiveMotions;
+
+ LLFrameTimer mTimer;
+ F32 mTime;
+ F32 mTimeOffset;
+ F32 mLastTime;
+ BOOL mHasRunOnce;
+ BOOL mPaused;
+ F32 mTimeStep;
+ S32 mTimeStepCount;
+ F32 mLastInterp;
+ F32 mPauseTime;
+
+ U8 mJointSignature[2][LL_CHARACTER_MAX_JOINTS];
+};
+
+//-----------------------------------------------------------------------------
+// Class declaractions
+//-----------------------------------------------------------------------------
+#include "llcharacter.h"
+
+#endif // LL_LLMOTIONCONTROLLER_H
+
diff --git a/indra/llcharacter/llmultigesture.cpp b/indra/llcharacter/llmultigesture.cpp
new file mode 100644
index 0000000000..1e42352d74
--- /dev/null
+++ b/indra/llcharacter/llmultigesture.cpp
@@ -0,0 +1,478 @@
+/**
+ * @file llmultigesture.cpp
+ * @brief Gestures that are asset-based and can have multiple steps.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <algorithm>
+
+#include "stdio.h"
+
+#include "llmultigesture.h"
+
+#include "llerror.h"
+#include "lldatapacker.h"
+#include "llstl.h"
+
+const S32 GESTURE_VERSION = 2;
+
+//---------------------------------------------------------------------------
+// LLMultiGesture
+//---------------------------------------------------------------------------
+LLMultiGesture::LLMultiGesture()
+: mKey(),
+ mMask(),
+ mTrigger(),
+ mReplaceText(),
+ mSteps(),
+ mPlaying(FALSE),
+ mCurrentStep(0),
+ mDoneCallback(NULL),
+ mCallbackData(NULL)
+{
+ reset();
+}
+
+LLMultiGesture::~LLMultiGesture()
+{
+ std::for_each(mSteps.begin(), mSteps.end(), DeletePointer());
+}
+
+void LLMultiGesture::reset()
+{
+ mPlaying = FALSE;
+ mCurrentStep = 0;
+ mWaitTimer.reset();
+ mWaitingTimer = FALSE;
+ mWaitingAnimations = FALSE;
+ mWaitingAtEnd = FALSE;
+ mRequestedAnimIDs.clear();
+ mPlayingAnimIDs.clear();
+}
+
+S32 LLMultiGesture::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+
+ // ascii format, being very conservative about possible
+ // label lengths.
+ max_size += 64; // version S32
+ max_size += 64; // key U8
+ max_size += 64; // mask U32
+ max_size += 256; // trigger string
+ max_size += 256; // replace string
+
+ max_size += 64; // step count S32
+
+ std::vector<LLGestureStep*>::const_iterator it;
+ for (it = mSteps.begin(); it != mSteps.end(); ++it)
+ {
+ LLGestureStep* step = *it;
+ max_size += 64; // type S32
+ max_size += step->getMaxSerialSize();
+ }
+
+ /* binary format
+ max_size += sizeof(S32); // version
+ max_size += sizeof(mKey);
+ max_size += sizeof(mMask);
+ max_size += mTrigger.length() + 1; // for null
+
+ max_size += sizeof(S32); // step count
+
+ std::vector<LLGestureStep*>::const_iterator it;
+ for (it = mSteps.begin(); it != mSteps.end(); ++it)
+ {
+ LLGestureStep* step = *it;
+ max_size += sizeof(S32); // type
+ max_size += step->getMaxSerialSize();
+ }
+ */
+
+ return max_size;
+}
+
+BOOL LLMultiGesture::serialize(LLDataPacker& dp) const
+{
+ dp.packS32(GESTURE_VERSION, "version");
+ dp.packU8(mKey, "key");
+ dp.packU32(mMask, "mask");
+ dp.packString(mTrigger.c_str(), "trigger");
+ dp.packString(mReplaceText.c_str(), "replace");
+
+ S32 count = (S32)mSteps.size();
+ dp.packS32(count, "step_count");
+ S32 i;
+ for (i = 0; i < count; ++i)
+ {
+ LLGestureStep* step = mSteps[i];
+
+ dp.packS32(step->getType(), "step_type");
+ BOOL ok = step->serialize(dp);
+ if (!ok)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLMultiGesture::deserialize(LLDataPacker& dp)
+{
+ S32 version;
+ dp.unpackS32(version, "version");
+ if (version != GESTURE_VERSION)
+ {
+ llwarns << "Bad LLMultiGesture version " << version
+ << " should be " << GESTURE_VERSION
+ << llendl;
+ return FALSE;
+ }
+
+ dp.unpackU8(mKey, "key");
+ dp.unpackU32(mMask, "mask");
+
+ char buffer[256]; /* Flawfinder: ignore */
+ dp.unpackString(buffer, "trigger");
+ mTrigger = buffer;
+
+ dp.unpackString(buffer, "replace");
+ mReplaceText = buffer;
+
+ S32 count;
+ dp.unpackS32(count, "step_count");
+ if (count < 0)
+ {
+ llwarns << "Bad LLMultiGesture step count " << count << llendl;
+ return FALSE;
+ }
+
+ S32 i;
+ for (i = 0; i < count; ++i)
+ {
+ S32 type;
+ dp.unpackS32(type, "step_type");
+
+ EStepType step_type = (EStepType)type;
+ switch(step_type)
+ {
+ case STEP_ANIMATION:
+ {
+ LLGestureStepAnimation* step = new LLGestureStepAnimation();
+ BOOL ok = step->deserialize(dp);
+ if (!ok) return FALSE;
+ mSteps.push_back(step);
+ break;
+ }
+ case STEP_SOUND:
+ {
+ LLGestureStepSound* step = new LLGestureStepSound();
+ BOOL ok = step->deserialize(dp);
+ if (!ok) return FALSE;
+ mSteps.push_back(step);
+ break;
+ }
+ case STEP_CHAT:
+ {
+ LLGestureStepChat* step = new LLGestureStepChat();
+ BOOL ok = step->deserialize(dp);
+ if (!ok) return FALSE;
+ mSteps.push_back(step);
+ break;
+ }
+ case STEP_WAIT:
+ {
+ LLGestureStepWait* step = new LLGestureStepWait();
+ BOOL ok = step->deserialize(dp);
+ if (!ok) return FALSE;
+ mSteps.push_back(step);
+ break;
+ }
+ default:
+ {
+ llwarns << "Bad LLMultiGesture step type " << type << llendl;
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+void LLMultiGesture::dump()
+{
+ llinfos << "key " << S32(mKey) << " mask " << U32(mMask)
+ << " trigger " << mTrigger
+ << " replace " << mReplaceText
+ << llendl;
+ U32 i;
+ for (i = 0; i < mSteps.size(); ++i)
+ {
+ LLGestureStep* step = mSteps[i];
+ step->dump();
+ }
+}
+
+//---------------------------------------------------------------------------
+// LLGestureStepAnimation
+//---------------------------------------------------------------------------
+LLGestureStepAnimation::LLGestureStepAnimation()
+: LLGestureStep(),
+ mAnimName("None"),
+ mAnimAssetID(),
+ mFlags(0x0)
+{ }
+
+LLGestureStepAnimation::~LLGestureStepAnimation()
+{ }
+
+S32 LLGestureStepAnimation::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+
+ // ascii
+ max_size += 256; // anim name
+ max_size += 64; // anim asset id
+ max_size += 64; // flags
+
+ /* binary
+ max_size += mAnimName.length() + 1;
+ max_size += sizeof(mAnimAssetID);
+ max_size += sizeof(mFlags);
+ */
+ return max_size;
+}
+
+BOOL LLGestureStepAnimation::serialize(LLDataPacker& dp) const
+{
+ dp.packString(mAnimName.c_str(), "anim_name");
+ dp.packUUID(mAnimAssetID, "asset_id");
+ dp.packU32(mFlags, "flags");
+ return TRUE;
+}
+
+BOOL LLGestureStepAnimation::deserialize(LLDataPacker& dp)
+{
+ char buffer[256]; /* Flawfinder: ignore */
+ dp.unpackString(buffer, "anim_name");
+ mAnimName = buffer;
+
+ // Apparently an earlier version of the gesture code added \r to the end
+ // of the animation names. Get rid of it. JC
+ if (mAnimName[mAnimName.length() - 1] == '\r')
+ {
+ // chop the last character
+ mAnimName.resize(mAnimName.length() - 1);
+ }
+
+ dp.unpackUUID(mAnimAssetID, "asset_id");
+ dp.unpackU32(mFlags, "flags");
+ return TRUE;
+}
+
+std::string LLGestureStepAnimation::getLabel() const
+{
+ std::string label;
+ if (mFlags & ANIM_FLAG_STOP)
+ {
+ label = "Stop Animation: ";
+ }
+ else
+ {
+ label = "Start Animation: ";
+ }
+ label += mAnimName;
+ return label;
+}
+
+void LLGestureStepAnimation::dump()
+{
+ llinfos << "step animation " << mAnimName
+ << " id " << mAnimAssetID
+ << " flags " << mFlags
+ << llendl;
+}
+
+//---------------------------------------------------------------------------
+// LLGestureStepSound
+//---------------------------------------------------------------------------
+LLGestureStepSound::LLGestureStepSound()
+: LLGestureStep(),
+ mSoundName("None"),
+ mSoundAssetID(),
+ mFlags(0x0)
+{ }
+
+LLGestureStepSound::~LLGestureStepSound()
+{ }
+
+S32 LLGestureStepSound::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+ max_size += 256; // sound name
+ max_size += 64; // sound asset id
+ max_size += 64; // flags
+ /* binary
+ max_size += mSoundName.length() + 1;
+ max_size += sizeof(mSoundAssetID);
+ max_size += sizeof(mFlags);
+ */
+ return max_size;
+}
+
+BOOL LLGestureStepSound::serialize(LLDataPacker& dp) const
+{
+ dp.packString(mSoundName.c_str(), "sound_name");
+ dp.packUUID(mSoundAssetID, "asset_id");
+ dp.packU32(mFlags, "flags");
+ return TRUE;
+}
+
+BOOL LLGestureStepSound::deserialize(LLDataPacker& dp)
+{
+ char buffer[256]; /* Flawfinder: ignore */
+ dp.unpackString(buffer, "sound_name");
+ mSoundName = buffer;
+
+ dp.unpackUUID(mSoundAssetID, "asset_id");
+ dp.unpackU32(mFlags, "flags");
+ return TRUE;
+}
+
+std::string LLGestureStepSound::getLabel() const
+{
+ std::string label("Sound: ");
+ label += mSoundName;
+ return label;
+}
+
+void LLGestureStepSound::dump()
+{
+ llinfos << "step sound " << mSoundName
+ << " id " << mSoundAssetID
+ << " flags " << mFlags
+ << llendl;
+}
+
+
+//---------------------------------------------------------------------------
+// LLGestureStepChat
+//---------------------------------------------------------------------------
+LLGestureStepChat::LLGestureStepChat()
+: LLGestureStep(),
+ mChatText(),
+ mFlags(0x0)
+{ }
+
+LLGestureStepChat::~LLGestureStepChat()
+{ }
+
+S32 LLGestureStepChat::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+ max_size += 256; // chat text
+ max_size += 64; // flags
+ /* binary
+ max_size += mChatText.length() + 1;
+ max_size += sizeof(mFlags);
+ */
+ return max_size;
+}
+
+BOOL LLGestureStepChat::serialize(LLDataPacker& dp) const
+{
+ dp.packString(mChatText.c_str(), "chat_text");
+ dp.packU32(mFlags, "flags");
+ return TRUE;
+}
+
+BOOL LLGestureStepChat::deserialize(LLDataPacker& dp)
+{
+ char buffer[256]; /* Flawfinder: ignore */
+ dp.unpackString(buffer, "chat_text");
+ mChatText = buffer;
+
+ dp.unpackU32(mFlags, "flags");
+ return TRUE;
+}
+
+std::string LLGestureStepChat::getLabel() const
+{
+ std::string label("Chat: ");
+ label += mChatText;
+ return label;
+}
+
+void LLGestureStepChat::dump()
+{
+ llinfos << "step chat " << mChatText
+ << " flags " << mFlags
+ << llendl;
+}
+
+
+//---------------------------------------------------------------------------
+// LLGestureStepWait
+//---------------------------------------------------------------------------
+LLGestureStepWait::LLGestureStepWait()
+: LLGestureStep(),
+ mWaitSeconds(0.f),
+ mFlags(0x0)
+{ }
+
+LLGestureStepWait::~LLGestureStepWait()
+{ }
+
+S32 LLGestureStepWait::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+ max_size += 64; // wait seconds
+ max_size += 64; // flags
+ /* binary
+ max_size += sizeof(mWaitSeconds);
+ max_size += sizeof(mFlags);
+ */
+ return max_size;
+}
+
+BOOL LLGestureStepWait::serialize(LLDataPacker& dp) const
+{
+ dp.packF32(mWaitSeconds, "wait_seconds");
+ dp.packU32(mFlags, "flags");
+ return TRUE;
+}
+
+BOOL LLGestureStepWait::deserialize(LLDataPacker& dp)
+{
+ dp.unpackF32(mWaitSeconds, "wait_seconds");
+ dp.unpackU32(mFlags, "flags");
+ return TRUE;
+}
+
+std::string LLGestureStepWait::getLabel() const
+{
+ std::string label("--- Wait: ");
+ if (mFlags & WAIT_FLAG_TIME)
+ {
+ char buffer[64]; /* Flawfinder: ignore */
+ snprintf(buffer, sizeof(buffer), "%.1f seconds", (double)mWaitSeconds); /* Flawfinder: ignore */
+ label += buffer;
+ }
+ else if (mFlags & WAIT_FLAG_ALL_ANIM)
+ {
+ label += "until animations are done";
+ }
+
+ return label;
+}
+
+
+void LLGestureStepWait::dump()
+{
+ llinfos << "step wait " << mWaitSeconds
+ << " flags " << mFlags
+ << llendl;
+}
diff --git a/indra/llcharacter/llmultigesture.h b/indra/llcharacter/llmultigesture.h
new file mode 100644
index 0000000000..39ee30568b
--- /dev/null
+++ b/indra/llcharacter/llmultigesture.h
@@ -0,0 +1,213 @@
+/**
+ * @file llmultigesture.h
+ * @brief Gestures that are asset-based and can have multiple steps.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMULTIGESTURE_H
+#define LL_LLMULTIGESTURE_H
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "lluuid.h"
+#include "llframetimer.h"
+
+class LLDataPacker;
+class LLGestureStep;
+
+class LLMultiGesture
+{
+public:
+ LLMultiGesture();
+ virtual ~LLMultiGesture();
+
+ // Maximum number of bytes this could hold once serialized.
+ S32 getMaxSerialSize() const;
+
+ BOOL serialize(LLDataPacker& dp) const;
+ BOOL deserialize(LLDataPacker& dp);
+
+ void dump();
+
+ void reset();
+
+ const std::string& getTrigger() const { return mTrigger; }
+protected:
+ LLMultiGesture(const LLMultiGesture& gest);
+ const LLMultiGesture& operator=(const LLMultiGesture& rhs);
+
+public:
+ // name is stored at asset level
+ // desc is stored at asset level
+ KEY mKey;
+ MASK mMask;
+
+ // String, like "/foo" or "hello" that makes it play
+ std::string mTrigger;
+
+ // Replaces the trigger substring with this text
+ std::string mReplaceText;
+
+ std::vector<LLGestureStep*> mSteps;
+
+ // Is the gesture currently playing?
+ BOOL mPlaying;
+
+ // "instruction pointer" for steps
+ S32 mCurrentStep;
+
+ // We're waiting for triggered animations to stop playing
+ BOOL mWaitingAnimations;
+
+ // We're waiting a fixed amount of time
+ BOOL mWaitingTimer;
+
+ // Waiting after the last step played for all animations to complete
+ BOOL mWaitingAtEnd;
+
+ // Timer for waiting
+ LLFrameTimer mWaitTimer;
+
+ void (*mDoneCallback)(LLMultiGesture* gesture, void* data);
+ void* mCallbackData;
+
+ // Animations that we requested to start
+ std::set<LLUUID> mRequestedAnimIDs;
+
+ // Once the animation starts playing (sim says to start playing)
+ // the ID is moved from mRequestedAnimIDs to here.
+ std::set<LLUUID> mPlayingAnimIDs;
+};
+
+
+enum EStepType
+{
+ STEP_ANIMATION = 0,
+ STEP_SOUND = 1,
+ STEP_CHAT = 2,
+ STEP_WAIT = 3,
+
+ STEP_EOF = 4
+};
+
+
+class LLGestureStep
+{
+public:
+ LLGestureStep() {}
+ virtual ~LLGestureStep() {}
+
+ virtual EStepType getType() = 0;
+
+ // Return a user-readable label for this step
+ virtual std::string getLabel() const = 0;
+
+ virtual S32 getMaxSerialSize() const = 0;
+ virtual BOOL serialize(LLDataPacker& dp) const = 0;
+ virtual BOOL deserialize(LLDataPacker& dp) = 0;
+
+ virtual void dump() = 0;
+};
+
+
+// By default, animation steps start animations.
+// If the least significant bit is 1, it will stop animations.
+const U32 ANIM_FLAG_STOP = 0x01;
+
+class LLGestureStepAnimation : public LLGestureStep
+{
+public:
+ LLGestureStepAnimation();
+ virtual ~LLGestureStepAnimation();
+
+ virtual EStepType getType() { return STEP_ANIMATION; }
+
+ virtual std::string getLabel() const;
+
+ virtual S32 getMaxSerialSize() const;
+ virtual BOOL serialize(LLDataPacker& dp) const;
+ virtual BOOL deserialize(LLDataPacker& dp);
+
+ virtual void dump();
+
+public:
+ std::string mAnimName;
+ LLUUID mAnimAssetID;
+ U32 mFlags;
+};
+
+
+class LLGestureStepSound : public LLGestureStep
+{
+public:
+ LLGestureStepSound();
+ virtual ~LLGestureStepSound();
+
+ virtual EStepType getType() { return STEP_SOUND; }
+
+ virtual std::string getLabel() const;
+
+ virtual S32 getMaxSerialSize() const;
+ virtual BOOL serialize(LLDataPacker& dp) const;
+ virtual BOOL deserialize(LLDataPacker& dp);
+
+ virtual void dump();
+
+public:
+ std::string mSoundName;
+ LLUUID mSoundAssetID;
+ U32 mFlags;
+};
+
+
+class LLGestureStepChat : public LLGestureStep
+{
+public:
+ LLGestureStepChat();
+ virtual ~LLGestureStepChat();
+
+ virtual EStepType getType() { return STEP_CHAT; }
+
+ virtual std::string getLabel() const;
+
+ virtual S32 getMaxSerialSize() const;
+ virtual BOOL serialize(LLDataPacker& dp) const;
+ virtual BOOL deserialize(LLDataPacker& dp);
+
+ virtual void dump();
+
+public:
+ std::string mChatText;
+ U32 mFlags;
+};
+
+
+const U32 WAIT_FLAG_TIME = 0x01;
+const U32 WAIT_FLAG_ALL_ANIM = 0x02;
+
+class LLGestureStepWait : public LLGestureStep
+{
+public:
+ LLGestureStepWait();
+ virtual ~LLGestureStepWait();
+
+ virtual EStepType getType() { return STEP_WAIT; }
+
+ virtual std::string getLabel() const;
+
+ virtual S32 getMaxSerialSize() const;
+ virtual BOOL serialize(LLDataPacker& dp) const;
+ virtual BOOL deserialize(LLDataPacker& dp);
+
+ virtual void dump();
+
+public:
+ F32 mWaitSeconds;
+ U32 mFlags;
+};
+
+#endif
diff --git a/indra/llcharacter/llpose.cpp b/indra/llcharacter/llpose.cpp
new file mode 100644
index 0000000000..ff56dba585
--- /dev/null
+++ b/indra/llcharacter/llpose.cpp
@@ -0,0 +1,544 @@
+/**
+ * @file llpose.cpp
+ * @brief Implementation of LLPose class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llpose.h"
+
+#include "llmotion.h"
+#include "llmath.h"
+
+//-----------------------------------------------------------------------------
+// Static
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLPose
+//-----------------------------------------------------------------------------
+LLPose::~LLPose()
+{
+}
+
+//-----------------------------------------------------------------------------
+// getFirstJointState()
+//-----------------------------------------------------------------------------
+LLJointState *LLPose::getFirstJointState()
+{
+ mListIter = mJointMap.begin();
+ if (mListIter == mJointMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return mListIter->second;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getNextJointState()
+//-----------------------------------------------------------------------------
+LLJointState *LLPose::getNextJointState()
+{
+ mListIter++;
+ if (mListIter == mJointMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return mListIter->second;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// addJointState()
+//-----------------------------------------------------------------------------
+BOOL LLPose::addJointState(LLJointState *jointState)
+{
+ if (mJointMap.find(jointState->getJoint()->getName()) == mJointMap.end())
+ {
+ mJointMap[jointState->getJoint()->getName()] = jointState;
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// removeJointState()
+//-----------------------------------------------------------------------------
+BOOL LLPose::removeJointState(LLJointState *jointState)
+{
+ mJointMap.erase(jointState->getJoint()->getName());
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// removeAllJointStates()
+//-----------------------------------------------------------------------------
+BOOL LLPose::removeAllJointStates()
+{
+ mJointMap.clear();
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// findJointState()
+//-----------------------------------------------------------------------------
+LLJointState* LLPose::findJointState(LLJoint *joint)
+{
+ joint_map_iterator iter = mJointMap.find(joint->getName());
+
+ if (iter == mJointMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return iter->second;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// findJointState()
+//-----------------------------------------------------------------------------
+LLJointState* LLPose::findJointState(const std::string &name)
+{
+ joint_map_iterator iter = mJointMap.find(name);
+
+ if (iter == mJointMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return iter->second;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setWeight()
+//-----------------------------------------------------------------------------
+void LLPose::setWeight(F32 weight)
+{
+ joint_map_iterator iter;
+ for(iter = mJointMap.begin();
+ iter != mJointMap.end();
+ ++iter)
+ {
+ iter->second->setWeight(weight);
+ }
+ mWeight = weight;
+}
+
+//-----------------------------------------------------------------------------
+// getWeight()
+//-----------------------------------------------------------------------------
+F32 LLPose::getWeight() const
+{
+ return mWeight;
+}
+
+//-----------------------------------------------------------------------------
+// getNumJointStates()
+//-----------------------------------------------------------------------------
+S32 LLPose::getNumJointStates() const
+{
+ return (S32)mJointMap.size();
+}
+
+//-----------------------------------------------------------------------------
+// LLJointStateBlender
+//-----------------------------------------------------------------------------
+
+LLJointStateBlender::LLJointStateBlender()
+{
+ for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
+ {
+ mJointStates[i] = NULL;
+ mPriorities[i] = S32_MIN;
+ }
+}
+
+LLJointStateBlender::~LLJointStateBlender()
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// addJointState()
+//-----------------------------------------------------------------------------
+BOOL LLJointStateBlender::addJointState(LLJointState *joint_state, S32 priority, BOOL additive_blend)
+{
+ llassert(joint_state);
+
+ if (!joint_state->getJoint())
+ // this joint state doesn't point to an actual joint, so we don't care about applying it
+ return FALSE;
+
+ for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
+ {
+ if (NULL == mJointStates[i])
+ {
+ mJointStates[i] = joint_state;
+ mPriorities[i] = priority;
+ mAdditiveBlends[i] = additive_blend;
+ return TRUE;
+ }
+ else if (priority > mPriorities[i])
+ {
+ // we're at a higher priority than the current joint state in this slot
+ // so shift everyone over
+ // previous joint states (newer motions) with same priority should stay in place
+ for (S32 j = JSB_NUM_JOINT_STATES - 1; j > i; j--)
+ {
+ mJointStates[j] = mJointStates[j - 1];
+ mPriorities[j] = mPriorities[j - 1];
+ mAdditiveBlends[j] = mAdditiveBlends[j - 1];
+ }
+ // now store ourselves in this slot
+ mJointStates[i] = joint_state;
+ mPriorities[i] = priority;
+ mAdditiveBlends[i] = additive_blend;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// blendJointStates()
+//-----------------------------------------------------------------------------
+void LLJointStateBlender::blendJointStates(BOOL apply_now)
+{
+ // we need at least one joint to blend
+ // if there is one, it will be in slot zero according to insertion logic
+ // instead of resetting joint state to default, just leave it unchanged from last frame
+ if (NULL == mJointStates[0])
+ {
+ return;
+ }
+
+ LLJoint* target_joint = apply_now ? mJointStates[0]->getJoint() : &mJointCache;
+
+ const S32 POS_WEIGHT = 0;
+ const S32 ROT_WEIGHT = 1;
+ const S32 SCALE_WEIGHT = 2;
+
+ F32 sum_weights[3];
+ U32 sum_usage = 0;
+
+ LLVector3 blended_pos = target_joint->getPosition();
+ LLQuaternion blended_rot = target_joint->getRotation();
+ LLVector3 blended_scale = target_joint->getScale();
+
+ LLVector3 added_pos;
+ LLQuaternion added_rot;
+ LLVector3 added_scale;
+
+ //S32 joint_state_index;
+
+ sum_weights[POS_WEIGHT] = 0.f;
+ sum_weights[ROT_WEIGHT] = 0.f;
+ sum_weights[SCALE_WEIGHT] = 0.f;
+
+ for(S32 joint_state_index = 0;
+ joint_state_index < JSB_NUM_JOINT_STATES && mJointStates[joint_state_index] != NULL;
+ joint_state_index++)
+ {
+ U32 current_usage = mJointStates[joint_state_index]->getUsage();
+ F32 current_weight = mJointStates[joint_state_index]->getWeight();
+ LLJointState* jsp = mJointStates[joint_state_index];
+
+ if (current_weight == 0.f)
+ {
+ continue;
+ }
+
+ if (mAdditiveBlends[joint_state_index])
+ {
+ if(current_usage & LLJointState::POS)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[POS_WEIGHT]);
+
+ // add in pos for this jointstate modulated by weight
+ added_pos += jsp->getPosition() * (new_weight_sum - sum_weights[POS_WEIGHT]);
+ //sum_weights[POS_WEIGHT] = new_weight_sum;
+ }
+
+ // now do scale
+ if(current_usage & LLJointState::SCALE)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[SCALE_WEIGHT]);
+
+ // add in scale for this jointstate modulated by weight
+ added_scale += jsp->getScale() * (new_weight_sum - sum_weights[SCALE_WEIGHT]);
+ //sum_weights[SCALE_WEIGHT] = new_weight_sum;
+ }
+
+ if (current_usage & LLJointState::ROT)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[ROT_WEIGHT]);
+
+ // add in rotation for this jointstate modulated by weight
+ added_rot = nlerp((new_weight_sum - sum_weights[ROT_WEIGHT]), added_rot, jsp->getRotation()) * added_rot;
+ //sum_weights[ROT_WEIGHT] = new_weight_sum;
+ }
+ }
+ else
+ {
+ // blend two jointstates together
+
+ // blend position
+ if(current_usage & LLJointState::POS)
+ {
+ if(sum_usage & LLJointState::POS)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[POS_WEIGHT]);
+
+ // blend positions from both
+ blended_pos = lerp(mJointStates[joint_state_index]->getPosition(), blended_pos, sum_weights[POS_WEIGHT] / new_weight_sum);
+ sum_weights[POS_WEIGHT] = new_weight_sum;
+ }
+ else
+ {
+ // copy position from current
+ blended_pos = mJointStates[joint_state_index]->getPosition();
+ sum_weights[POS_WEIGHT] = current_weight;
+ }
+ }
+
+ // now do scale
+ if(current_usage & LLJointState::SCALE)
+ {
+ if(sum_usage & LLJointState::SCALE)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[SCALE_WEIGHT]);
+
+ // blend scales from both
+ blended_scale = lerp(mJointStates[joint_state_index]->getScale(), blended_scale, sum_weights[SCALE_WEIGHT] / new_weight_sum);
+ sum_weights[SCALE_WEIGHT] = new_weight_sum;
+ }
+ else
+ {
+ // copy scale from current
+ blended_scale = mJointStates[joint_state_index]->getScale();
+ sum_weights[SCALE_WEIGHT] = current_weight;
+ }
+ }
+
+ // rotation
+ if (current_usage & LLJointState::ROT)
+ {
+ if(sum_usage & LLJointState::ROT)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[ROT_WEIGHT]);
+
+ // blend rotations from both
+ blended_rot = nlerp(sum_weights[ROT_WEIGHT] / new_weight_sum, mJointStates[joint_state_index]->getRotation(), blended_rot);
+ sum_weights[ROT_WEIGHT] = new_weight_sum;
+ }
+ else
+ {
+ // copy rotation from current
+ blended_rot = mJointStates[joint_state_index]->getRotation();
+ sum_weights[ROT_WEIGHT] = current_weight;
+ }
+ }
+
+ // update resulting usage mask
+ sum_usage = sum_usage | current_usage;
+ }
+ }
+
+ // apply blended transforms
+ target_joint->setPosition(blended_pos);
+ target_joint->setScale(blended_scale);
+ target_joint->setRotation(blended_rot);
+
+ // apply additive transforms
+ target_joint->setPosition(target_joint->getPosition() + added_pos);
+ target_joint->setScale(target_joint->getScale() + added_scale);
+ target_joint->setRotation(added_rot * target_joint->getRotation());
+
+ if (apply_now)
+ {
+ // now clear joint states
+ for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
+ {
+ mJointStates[i] = NULL;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// interpolate()
+//-----------------------------------------------------------------------------
+void LLJointStateBlender::interpolate(F32 u)
+{
+ // only interpolate if we have a joint state
+ if (!mJointStates[0])
+ {
+ return;
+ }
+ LLJoint* target_joint = mJointStates[0]->getJoint();
+
+ if (!target_joint)
+ {
+ return;
+ }
+
+ target_joint->setPosition(lerp(target_joint->getPosition(), mJointCache.getPosition(), u));
+ target_joint->setScale(lerp(target_joint->getScale(), mJointCache.getScale(), u));
+ target_joint->setRotation(nlerp(u, target_joint->getRotation(), mJointCache.getRotation()));
+}
+
+//-----------------------------------------------------------------------------
+// clear()
+//-----------------------------------------------------------------------------
+void LLJointStateBlender::clear()
+{
+ // now clear joint states
+ for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
+ {
+ mJointStates[i] = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// resetCachedJoint()
+//-----------------------------------------------------------------------------
+void LLJointStateBlender::resetCachedJoint()
+{
+ if (!mJointStates[0])
+ {
+ return;
+ }
+ LLJoint* source_joint = mJointStates[0]->getJoint();
+ mJointCache.setPosition(source_joint->getPosition());
+ mJointCache.setScale(source_joint->getScale());
+ mJointCache.setRotation(source_joint->getRotation());
+}
+
+//-----------------------------------------------------------------------------
+// LLPoseBlender
+//-----------------------------------------------------------------------------
+
+LLPoseBlender::LLPoseBlender()
+{
+}
+
+LLPoseBlender::~LLPoseBlender()
+{
+ mJointStateBlenderPool.deleteAllData();
+}
+
+//-----------------------------------------------------------------------------
+// addMotion()
+//-----------------------------------------------------------------------------
+BOOL LLPoseBlender::addMotion(LLMotion* motion)
+{
+ LLPose* pose = motion->getPose();
+
+ for(LLJointState *jsp = pose->getFirstJointState(); jsp; jsp = pose->getNextJointState())
+ {
+ LLJoint *jointp = jsp->getJoint();
+ LLJointStateBlender* joint_blender;
+ if (!mJointStateBlenderPool.checkData(jointp))
+ {
+ // this is the first time we are animating this joint
+ // so create new jointblender and add it to our pool
+ joint_blender = new LLJointStateBlender();
+ mJointStateBlenderPool.addData(jointp, joint_blender);
+ } else
+ {
+ joint_blender = mJointStateBlenderPool.getData(jointp);
+ }
+
+ if (jsp->getPriority() == LLJoint::USE_MOTION_PRIORITY)
+ {
+ joint_blender->addJointState(jsp, motion->getPriority(), motion->getBlendType() == LLMotion::ADDITIVE_BLEND);
+ }
+ else
+ {
+ joint_blender->addJointState(jsp, jsp->getPriority(), motion->getBlendType() == LLMotion::ADDITIVE_BLEND);
+ }
+
+ // add it to our list of active blenders
+ if(!mActiveBlenders.checkData(joint_blender))
+ {
+ mActiveBlenders.addData(joint_blender);
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// blendAndApply()
+//-----------------------------------------------------------------------------
+void LLPoseBlender::blendAndApply()
+{
+ for (LLJointStateBlender* jsbp = mActiveBlenders.getFirstData();
+ jsbp;
+ jsbp = mActiveBlenders.getNextData())
+ {
+ jsbp->blendJointStates();
+ }
+
+ // we're done now so there are no more active blenders for this frame
+ mActiveBlenders.removeAllNodes();
+}
+
+//-----------------------------------------------------------------------------
+// blendAndCache()
+//-----------------------------------------------------------------------------
+void LLPoseBlender::blendAndCache(BOOL reset_cached_joints)
+{
+ for (LLJointStateBlender* jsbp = mActiveBlenders.getFirstData();
+ jsbp;
+ jsbp = mActiveBlenders.getNextData())
+ {
+ if (reset_cached_joints)
+ {
+ jsbp->resetCachedJoint();
+ }
+ jsbp->blendJointStates(FALSE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// interpolate()
+//-----------------------------------------------------------------------------
+void LLPoseBlender::interpolate(F32 u)
+{
+ for (LLJointStateBlender* jsbp = mActiveBlenders.getFirstData();
+ jsbp;
+ jsbp = mActiveBlenders.getNextData())
+ {
+ jsbp->interpolate(u);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearBlenders()
+//-----------------------------------------------------------------------------
+void LLPoseBlender::clearBlenders()
+{
+ for (LLJointStateBlender* jsbp = mActiveBlenders.getFirstData();
+ jsbp;
+ jsbp = mActiveBlenders.getNextData())
+ {
+ jsbp->clear();
+ }
+
+ mActiveBlenders.removeAllNodes();
+}
+
diff --git a/indra/llcharacter/llpose.h b/indra/llcharacter/llpose.h
new file mode 100644
index 0000000000..e286c14e21
--- /dev/null
+++ b/indra/llcharacter/llpose.h
@@ -0,0 +1,120 @@
+/**
+ * @file llpose.h
+ * @brief Implementation of LLPose class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPOSE_H
+#define LL_LLPOSE_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include <string>
+
+#include "linked_lists.h"
+#include "llmap.h"
+#include "lljointstate.h"
+#include "llassoclist.h"
+#include "lljoint.h"
+#include <map>
+
+
+//-----------------------------------------------------------------------------
+// class LLPose
+//-----------------------------------------------------------------------------
+class LLPose
+{
+ friend class LLPoseBlender;
+protected:
+ typedef std::map<std::string, LLJointState*> joint_map;
+ typedef joint_map::iterator joint_map_iterator;
+ typedef joint_map::value_type joint_map_value_type;
+
+ joint_map mJointMap;
+ F32 mWeight;
+ joint_map_iterator mListIter;
+public:
+ // Iterate through jointStates
+ LLJointState *getFirstJointState();
+ LLJointState *getNextJointState();
+ LLJointState *findJointState(LLJoint *joint);
+ LLJointState *findJointState(const std::string &name);
+public:
+ // Constructor
+ LLPose() : mWeight(0.f) {}
+ // Destructor
+ ~LLPose();
+ // add a joint state in this pose
+ BOOL addJointState(LLJointState *jointState);
+ // remove a joint state from this pose
+ BOOL removeJointState(LLJointState *jointState);
+ // removes all joint states from this pose
+ BOOL removeAllJointStates();
+ // set weight for all joint states in this pose
+ void setWeight(F32 weight);
+ // get weight for this pose
+ F32 getWeight() const;
+ // returns number of joint states stored in this pose
+ S32 getNumJointStates() const;
+};
+
+const S32 JSB_NUM_JOINT_STATES = 4;
+
+class LLJointStateBlender
+{
+protected:
+ LLJointState* mJointStates[JSB_NUM_JOINT_STATES];
+ S32 mPriorities[JSB_NUM_JOINT_STATES];
+ BOOL mAdditiveBlends[JSB_NUM_JOINT_STATES];
+public:
+ LLJointStateBlender();
+ ~LLJointStateBlender();
+ void blendJointStates(BOOL apply_now = TRUE);
+ BOOL addJointState(LLJointState *joint_state, S32 priority, BOOL additive_blend);
+ void interpolate(F32 u);
+ void clear();
+ void resetCachedJoint();
+
+public:
+ LLJoint mJointCache;
+};
+
+class LLMotion;
+
+class LLPoseBlender
+{
+protected:
+ LLMap<LLJoint*,LLJointStateBlender*> mJointStateBlenderPool;
+ LLLinkedList<LLJointStateBlender> mActiveBlenders;
+
+ S32 mNextPoseSlot;
+ LLPose mBlendedPose;
+public:
+ // Constructor
+ LLPoseBlender();
+ // Destructor
+ ~LLPoseBlender();
+
+ // request motion joint states to be added to pose blender joint state records
+ BOOL addMotion(LLMotion* motion);
+
+ // blend all joint states and apply to skeleton
+ void blendAndApply();
+
+ // removes all joint state blenders from last time
+ void clearBlenders();
+
+ // blend all joint states and cache results
+ void blendAndCache(BOOL reset_cached_joints);
+
+ // interpolate all joints towards cached values
+ void interpolate(F32 u);
+
+ LLPose* getBlendedPose() { return &mBlendedPose; }
+};
+
+#endif // LL_LLPOSE_H
+
diff --git a/indra/llcharacter/llstatemachine.cpp b/indra/llcharacter/llstatemachine.cpp
new file mode 100644
index 0000000000..d51b2f75aa
--- /dev/null
+++ b/indra/llcharacter/llstatemachine.cpp
@@ -0,0 +1,378 @@
+/**
+ * @file llstatemachine.cpp
+ * @brief LLStateMachine implementation file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llstatemachine.h"
+#include "llapr.h"
+
+#define FSM_PRINT_STATE_TRANSITIONS (0)
+
+U32 LLUniqueID::sNextID = 0;
+
+bool operator==(const LLUniqueID &a, const LLUniqueID &b)
+{
+ return (a.mId == b.mId);
+}
+
+bool operator!=(const LLUniqueID &a, const LLUniqueID &b)
+{
+ return (a.mId != b.mId);
+}
+
+//-----------------------------------------------------------------------------
+// LLStateDiagram
+//-----------------------------------------------------------------------------
+LLStateDiagram::LLStateDiagram()
+{
+ mUseDefaultState = FALSE;
+}
+
+LLStateDiagram::~LLStateDiagram()
+{
+
+}
+
+// add a state to the state graph
+BOOL LLStateDiagram::addState(LLFSMState *state)
+{
+ mStates[state] = Transitions();
+ return TRUE;
+}
+
+// add a directed transition between 2 states
+BOOL LLStateDiagram::addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
+{
+ StateMap::iterator state_it;
+ state_it = mStates.find(&start_state);
+ Transitions* state_transitions = NULL;
+ if (state_it == mStates.end() )
+ {
+ addState(&start_state);
+ state_transitions = &mStates[&start_state];
+ }
+ else
+ {
+ state_transitions = &state_it->second;
+ }
+ state_it = mStates.find(&end_state);
+ if (state_it == mStates.end() )
+ {
+ addState(&end_state);
+ }
+
+ Transitions::iterator transition_it = state_transitions->find(&transition);
+ if (transition_it != state_transitions->end())
+ {
+ llerrs << "LLStateTable::addDirectedTransition() : transition already exists" << llendl;
+ return FALSE; // transition already exists
+ }
+
+ (*state_transitions)[&transition] = &end_state;
+ return TRUE;
+}
+
+// add an undirected transition between 2 states
+BOOL LLStateDiagram::addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
+{
+ BOOL result;
+ result = addTransition(start_state, end_state, transition);
+ if (result)
+ {
+ result = addTransition(end_state, start_state, transition);
+ }
+ return result;
+}
+
+// add a transition that exists for every state
+void LLStateDiagram::addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition)
+{
+ mDefaultTransitions[&transition] = &end_state;
+}
+
+// process a possible transition, and get the resulting state
+LLFSMState* LLStateDiagram::processTransition(LLFSMState& start_state, LLFSMTransition& transition)
+{
+ // look up transition
+ //LLFSMState** dest_state = (mStates.getValue(&start_state))->getValue(&transition);
+ LLFSMState* dest_state = NULL;
+ StateMap::iterator state_it = mStates.find(&start_state);
+ if (state_it == mStates.end())
+ {
+ return NULL;
+ }
+ Transitions::iterator transition_it = state_it->second.find(&transition);
+
+ // try default transitions if state-specific transition not found
+ if (transition_it == state_it->second.end())
+ {
+ dest_state = mDefaultTransitions[&transition];
+ }
+ else
+ {
+ dest_state = transition_it->second;
+ }
+
+ // if we have a destination state...
+ if (NULL != dest_state)
+ {
+ // ...return it...
+ return dest_state;
+ }
+ // ... otherwise ...
+ else
+ {
+ // ...look for default state...
+ if (mUseDefaultState)
+ {
+ // ...return it if we have it...
+ return mDefaultState;
+ }
+ else
+ {
+ // ...or else we're still in the same state.
+ return &start_state;
+ }
+ }
+}
+
+void LLStateDiagram::setDefaultState(LLFSMState& default_state)
+{
+ mUseDefaultState = TRUE;
+ mDefaultState = &default_state;
+}
+
+S32 LLStateDiagram::numDeadendStates()
+{
+ S32 numDeadends = 0;
+ StateMap::iterator state_it;
+ for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
+ {
+ if (state_it->second.size() == 0)
+ {
+ numDeadends++;
+ }
+ }
+ return numDeadends;
+}
+
+BOOL LLStateDiagram::stateIsValid(LLFSMState& state)
+{
+ if (mStates.find(&state) != mStates.end())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+LLFSMState* LLStateDiagram::getState(U32 state_id)
+{
+ StateMap::iterator state_it;
+ for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
+ {
+ if (state_it->first->getID() == state_id)
+ {
+ return state_it->first;
+ }
+ }
+ return NULL;
+}
+
+BOOL LLStateDiagram::saveDotFile(const char* filename)
+{
+ apr_file_t* dot_file = ll_apr_file_open(filename, LL_APR_W);
+
+ if (!dot_file)
+ {
+ llwarns << "LLStateDiagram::saveDotFile() : Couldn't open " << filename << " to save state diagram." << llendl;
+ return FALSE;
+ }
+ apr_file_printf(dot_file, "digraph StateMachine {\n\tsize=\"100,100\";\n\tfontsize=40;\n\tlabel=\"Finite State Machine\";\n\torientation=landscape\n\tratio=.77\n");
+
+ StateMap::iterator state_it;
+ for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
+ {
+ apr_file_printf(dot_file, "\t\"%s\" [fontsize=28,shape=box]\n", state_it->first->getName().c_str());
+ }
+ apr_file_printf(dot_file, "\t\"All States\" [fontsize=30,style=bold,shape=box]\n");
+
+ Transitions::iterator transitions_it;
+ for(transitions_it = mDefaultTransitions.begin(); transitions_it != mDefaultTransitions.end(); ++transitions_it)
+ {
+ apr_file_printf(dot_file, "\t\"All States\" -> \"%s\" [label = \"%s\",fontsize=24];\n", transitions_it->second->getName().c_str(),
+ transitions_it->second->getName().c_str());
+ }
+
+ if (mDefaultState)
+ {
+ apr_file_printf(dot_file, "\t\"All States\" -> \"%s\";\n", mDefaultState->getName().c_str());
+ }
+
+
+ for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
+ {
+ LLFSMState *state = state_it->first;
+
+ Transitions::iterator transitions_it;
+ for(transitions_it = state_it->second.begin();
+ transitions_it != state_it->second.end();
+ ++transitions_it)
+ {
+ std::string state_name = state->getName();
+ std::string target_name = transitions_it->second->getName();
+ std::string transition_name = transitions_it->first->getName();
+ apr_file_printf(dot_file, "\t\"%s\" -> \"%s\" [label = \"%s\",fontsize=24];\n", state->getName().c_str(),
+ target_name.c_str(),
+ transition_name.c_str());
+ }
+ }
+
+ apr_file_printf(dot_file, "}\n");
+
+ apr_file_close(dot_file);
+
+ return TRUE;
+}
+
+std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM)
+{
+ if (FSM.mDefaultState)
+ {
+ s << "Default State: " << FSM.mDefaultState->getName() << "\n";
+ }
+
+ LLStateDiagram::Transitions::iterator transitions_it;
+ for(transitions_it = FSM.mDefaultTransitions.begin();
+ transitions_it != FSM.mDefaultTransitions.end();
+ ++transitions_it)
+ {
+ s << "Any State -- " << transitions_it->first->getName()
+ << " --> " << transitions_it->second->getName() << "\n";
+ }
+
+ LLStateDiagram::StateMap::iterator state_it;
+ for(state_it = FSM.mStates.begin(); state_it != FSM.mStates.end(); ++state_it)
+ {
+ LLStateDiagram::Transitions::iterator transitions_it;
+ for(transitions_it = state_it->second.begin();
+ transitions_it != state_it->second.end();
+ ++transitions_it)
+ {
+ s << state_it->first->getName() << " -- " << transitions_it->first->getName()
+ << " --> " << transitions_it->second->getName() << "\n";
+ }
+ s << "\n";
+ }
+
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+// LLStateMachine
+//-----------------------------------------------------------------------------
+
+LLStateMachine::LLStateMachine()
+{
+ // we haven't received a starting state yet
+ mCurrentState = NULL;
+ mLastState = NULL;
+ mStateDiagram = NULL;
+}
+
+LLStateMachine::~LLStateMachine()
+{
+
+}
+
+// returns current state
+LLFSMState* LLStateMachine::getCurrentState() const
+{
+ return mCurrentState;
+}
+
+// executes current state
+void LLStateMachine::runCurrentState(void *data)
+{
+ mCurrentState->execute(data);
+}
+
+// set current state
+BOOL LLStateMachine::setCurrentState(LLFSMState *initial_state, void* user_data, BOOL skip_entry)
+{
+ llassert(mStateDiagram);
+
+ if (mStateDiagram->stateIsValid(*initial_state))
+ {
+ mLastState = mCurrentState = initial_state;
+ if (!skip_entry)
+ {
+ initial_state->onEntry(user_data);
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLStateMachine::setCurrentState(U32 state_id, void* user_data, BOOL skip_entry)
+{
+ llassert(mStateDiagram);
+
+ LLFSMState* state = mStateDiagram->getState(state_id);
+
+ if (state)
+ {
+ mLastState = mCurrentState = state;
+ if (!skip_entry)
+ {
+ state->onEntry(user_data);
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLStateMachine::processTransition(LLFSMTransition& transition, void* user_data)
+{
+ llassert(mStateDiagram);
+
+ LLFSMState* new_state = mStateDiagram->processTransition(*mCurrentState, transition);
+
+ mLastTransition = &transition;
+ mLastState = mCurrentState;
+
+ if (*mCurrentState != *new_state)
+ {
+ if (mCurrentState && new_state && *mCurrentState != *new_state)
+ {
+ mCurrentState->onExit(user_data);
+ }
+ if (new_state)
+ {
+ if (!mCurrentState || *mCurrentState != *new_state)
+ {
+ mCurrentState = new_state;
+ if (mCurrentState)
+ {
+ mCurrentState->onEntry(user_data);
+ }
+ }
+ }
+#if FSM_PRINT_STATE_TRANSITIONS
+ llinfos << "Entering state " << mCurrentState->getName() <<
+ " on transition " << transition.getName() << " from state " <<
+ mLastState->getName() << llendl;
+#endif
+ }
+}
+
+void LLStateMachine::setStateDiagram(LLStateDiagram* diagram)
+{
+ mStateDiagram = diagram;
+}
diff --git a/indra/llcharacter/llstatemachine.h b/indra/llcharacter/llstatemachine.h
new file mode 100644
index 0000000000..837fc57b9b
--- /dev/null
+++ b/indra/llcharacter/llstatemachine.h
@@ -0,0 +1,130 @@
+/**
+ * @file llstatemachine.h
+ * @brief LLStateMachine class header file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTATEMACHINE_H
+#define LL_LLSTATEMACHINE_H
+
+#include <string>
+
+#include "llassoclist.h"
+#include "llerror.h"
+#include <map>
+
+class LLUniqueID
+{
+ friend bool operator==(const LLUniqueID &a, const LLUniqueID &b);
+ friend bool operator!=(const LLUniqueID &a, const LLUniqueID &b);
+protected:
+ static U32 sNextID;
+ U32 mId;
+public:
+ LLUniqueID(){mId = sNextID++;}
+ virtual ~LLUniqueID(){}
+ U32 getID() {return mId;}
+};
+
+class LLFSMTransition : public LLUniqueID
+{
+public:
+ LLFSMTransition() : LLUniqueID(){};
+ virtual std::string getName(){ return "unnamed"; }
+};
+
+class LLFSMState : public LLUniqueID
+{
+public:
+ LLFSMState() : LLUniqueID(){};
+ virtual void onEntry(void *){};
+ virtual void onExit(void *){};
+ virtual void execute(void *){};
+ virtual std::string getName(){ return "unnamed"; }
+};
+
+class LLStateDiagram
+{
+typedef std::map<LLFSMTransition*, LLFSMState*> Transitions;
+
+friend std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM);
+friend class LLStateMachine;
+
+protected:
+ typedef std::map<LLFSMState*, Transitions> StateMap;
+ StateMap mStates;
+ Transitions mDefaultTransitions;
+ LLFSMState* mDefaultState;
+ BOOL mUseDefaultState;
+
+public:
+ LLStateDiagram();
+ virtual ~LLStateDiagram();
+
+protected:
+ // add a state to the state graph, executed implicitly when adding transitions
+ BOOL addState(LLFSMState *state);
+
+ // add a directed transition between 2 states
+ BOOL addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition);
+
+ // add an undirected transition between 2 states
+ BOOL addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition);
+
+ // add a transition that is taken if none other exist
+ void addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition);
+
+ // process a possible transition, and get the resulting state
+ LLFSMState* processTransition(LLFSMState& start_state, LLFSMTransition& transition);
+
+ // add a transition that exists for every state
+ void setDefaultState(LLFSMState& default_state);
+
+ // return total number of states with no outgoing transitions
+ S32 numDeadendStates();
+
+ // does this state exist in the state diagram?
+ BOOL stateIsValid(LLFSMState& state);
+
+ // get a state pointer by ID
+ LLFSMState* getState(U32 state_id);
+
+public:
+ // save the graph in a DOT file for rendering and visualization
+ BOOL saveDotFile(const char* filename);
+};
+
+class LLStateMachine
+{
+protected:
+ LLFSMState* mCurrentState;
+ LLFSMState* mLastState;
+ LLFSMTransition* mLastTransition;
+ LLStateDiagram* mStateDiagram;
+
+public:
+ LLStateMachine();
+ virtual ~LLStateMachine();
+
+ // set state diagram
+ void setStateDiagram(LLStateDiagram* diagram);
+
+ // process this transition
+ void processTransition(LLFSMTransition &transition, void* user_data);
+
+ // returns current state
+ LLFSMState* getCurrentState() const;
+
+ // execute current state
+ void runCurrentState(void *data);
+
+ // set state by state pointer
+ BOOL setCurrentState(LLFSMState *initial_state, void* user_data, BOOL skip_entry = TRUE);
+
+ // set state by unique ID
+ BOOL setCurrentState(U32 state_id, void* user_data, BOOL skip_entry = TRUE);
+};
+
+#endif //_LL_LLSTATEMACHINE_H
diff --git a/indra/llcharacter/lltargetingmotion.cpp b/indra/llcharacter/lltargetingmotion.cpp
new file mode 100644
index 0000000000..0f3eaa855b
--- /dev/null
+++ b/indra/llcharacter/lltargetingmotion.cpp
@@ -0,0 +1,151 @@
+/**
+ * @file lltargetingmotion.cpp
+ * @brief Implementation of LLTargetingMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "lltargetingmotion.h"
+#include "llcharacter.h"
+#include "v3dmath.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+const F32 TORSO_TARGET_HALF_LIFE = 0.25f;
+const F32 MAX_TIME_DELTA = 2.f; //max two seconds a frame for calculating interpolation
+const F32 TARGET_PLANE_THRESHOLD_DOT = 0.6f;
+const F32 TORSO_ROT_FRACTION = 0.5f;
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLTargetingMotion::LLTargetingMotion(const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+ mName = "targeting";
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLTargetingMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLTargetingMotion::~LLTargetingMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLTargetingMotion::onInitialize(LLCharacter *character)
+{
+ // save character for future use
+ mCharacter = character;
+
+ mPelvisJoint = mCharacter->getJoint("mPelvis");
+ mTorsoJoint = mCharacter->getJoint("mTorso");
+ mRightHandJoint = mCharacter->getJoint("mWristRight");
+
+ // make sure character skeleton is copacetic
+ if (!mPelvisJoint ||
+ !mTorsoJoint ||
+ !mRightHandJoint)
+ {
+ llwarns << "Invalid skeleton for targeting motion!" << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mTorsoState.setJoint( mTorsoJoint );
+
+ // add joint states to the pose
+ mTorsoState.setUsage(LLJointState::ROT);
+ addJointState( &mTorsoState );
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLTargetingMotion::onActivate()
+{
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLTargetingMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ F32 slerp_amt = LLCriticalDamp::getInterpolant(TORSO_TARGET_HALF_LIFE);
+
+ LLVector3 target;
+ LLVector3* lookAtPoint = (LLVector3*)mCharacter->getAnimationData("LookAtPoint");
+
+ BOOL result = TRUE;
+
+ if (!lookAtPoint)
+ {
+ return TRUE;
+ }
+ else
+ {
+ target = *lookAtPoint;
+ target.normVec();
+ }
+
+ //LLVector3 target_plane_normal = LLVector3(1.f, 0.f, 0.f) * mPelvisJoint->getWorldRotation();
+ //LLVector3 torso_dir = LLVector3(1.f, 0.f, 0.f) * (mTorsoJoint->getWorldRotation() * mTorsoState.getRotation());
+
+ LLVector3 skyward(0.f, 0.f, 1.f);
+ LLVector3 left(skyward % target);
+ left.normVec();
+ LLVector3 up(target % left);
+ up.normVec();
+ LLQuaternion target_aim_rot(target, left, up);
+
+ LLQuaternion cur_torso_rot = mTorsoJoint->getWorldRotation();
+
+ LLVector3 right_hand_at = LLVector3(0.f, -1.f, 0.f) * mRightHandJoint->getWorldRotation();
+ left.setVec(skyward % right_hand_at);
+ left.normVec();
+ up.setVec(right_hand_at % left);
+ up.normVec();
+ LLQuaternion right_hand_rot(right_hand_at, left, up);
+
+ LLQuaternion new_torso_rot = (cur_torso_rot * ~right_hand_rot) * target_aim_rot;
+
+ // find ideal additive rotation to make torso point in correct direction
+ new_torso_rot = new_torso_rot * ~cur_torso_rot;
+
+ // slerp from current additive rotation to ideal additive rotation
+ new_torso_rot = nlerp(slerp_amt, mTorsoState.getRotation(), new_torso_rot);
+
+ // constraint overall torso rotation
+ LLQuaternion total_rot = new_torso_rot * mTorsoJoint->getRotation();
+ total_rot.constrain(F_PI_BY_TWO * 0.8f);
+ new_torso_rot = total_rot * ~mTorsoJoint->getRotation();
+
+ mTorsoState.setRotation(new_torso_rot);
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLTargetingMotion::onDeactivate()
+{
+}
+
+
+// End
diff --git a/indra/llcharacter/lltargetingmotion.h b/indra/llcharacter/lltargetingmotion.h
new file mode 100644
index 0000000000..708dd374a2
--- /dev/null
+++ b/indra/llcharacter/lltargetingmotion.h
@@ -0,0 +1,98 @@
+/**
+ * @file lltargetingmotion.h
+ * @brief Implementation of LLTargetingMotion class.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTARGETINGMOTION_H
+#define LL_LLTARGETINGMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+
+#define TARGETING_EASEIN_DURATION 0.3f
+#define TARGETING_EASEOUT_DURATION 0.5f
+#define TARGETING_PRIORITY LLJoint::HIGH_PRIORITY
+#define MIN_REQUIRED_PIXEL_AREA_TARGETING 1000.f;
+
+
+//-----------------------------------------------------------------------------
+// class LLTargetingMotion
+//-----------------------------------------------------------------------------
+class LLTargetingMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLTargetingMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLTargetingMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLTargetingMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return TARGETING_EASEIN_DURATION; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return TARGETING_EASEOUT_DURATION; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return TARGETING_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_TARGETING; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+public:
+
+ LLCharacter *mCharacter;
+ LLJointState mTorsoState;
+ LLJoint* mPelvisJoint;
+ LLJoint* mTorsoJoint;
+ LLJoint* mRightHandJoint;
+};
+
+#endif // LL_LLTARGETINGMOTION_H
+
diff --git a/indra/llcharacter/llvisualparam.cpp b/indra/llcharacter/llvisualparam.cpp
new file mode 100644
index 0000000000..a6fd9b974f
--- /dev/null
+++ b/indra/llcharacter/llvisualparam.cpp
@@ -0,0 +1,266 @@
+/**
+ * @file llvisualparam.cpp
+ * @brief Implementation of LLPolyMesh class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llvisualparam.h"
+
+//-----------------------------------------------------------------------------
+// LLVisualParamInfo()
+//-----------------------------------------------------------------------------
+LLVisualParamInfo::LLVisualParamInfo()
+ :
+ mID( -1 ),
+ mGroup( VISUAL_PARAM_GROUP_TWEAKABLE ),
+ mMinWeight( 0.f ),
+ mMaxWeight( 1.f ),
+ mDefaultWeight( 0.f ),
+ mSex( SEX_BOTH )
+{
+}
+
+//-----------------------------------------------------------------------------
+// parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLVisualParamInfo::parseXml(LLXmlTreeNode *node)
+{
+ // attribute: id
+ static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
+ node->getFastAttributeS32( id_string, mID );
+
+ // attribute: group
+ U32 group = 0;
+ static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
+ if( node->getFastAttributeU32( group_string, group ) )
+ {
+ if( group < NUM_VISUAL_PARAM_GROUPS )
+ {
+ mGroup = (EVisualParamGroup)group;
+ }
+ }
+
+ // attribute: value_min, value_max
+ static LLStdStringHandle value_min_string = LLXmlTree::addAttributeString("value_min");
+ static LLStdStringHandle value_max_string = LLXmlTree::addAttributeString("value_max");
+ node->getFastAttributeF32( value_min_string, mMinWeight );
+ node->getFastAttributeF32( value_max_string, mMaxWeight );
+
+ // attribute: value_default
+ F32 default_weight = 0;
+ static LLStdStringHandle value_default_string = LLXmlTree::addAttributeString("value_default");
+ if( node->getFastAttributeF32( value_default_string, default_weight ) )
+ {
+ mDefaultWeight = llclamp( default_weight, mMinWeight, mMaxWeight );
+ if( default_weight != mDefaultWeight )
+ {
+ llwarns << "value_default attribute is out of range in node " << mName << " " << default_weight << llendl;
+ }
+ }
+
+ // attribute: sex
+ LLString sex = "both";
+ static LLStdStringHandle sex_string = LLXmlTree::addAttributeString("sex");
+ node->getFastAttributeString( sex_string, sex ); // optional
+ if( sex == "both" )
+ {
+ mSex = SEX_BOTH;
+ }
+ else if( sex == "male" )
+ {
+ mSex = SEX_MALE;
+ }
+ else if( sex == "female" )
+ {
+ mSex = SEX_FEMALE;
+ }
+ else
+ {
+ llwarns << "Avatar file: <param> has invalid sex attribute: " << sex << llendl;
+ return FALSE;
+ }
+
+ // attribute: name
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mName ) )
+ {
+ llwarns << "Avatar file: <param> is missing name attribute" << llendl;
+ return FALSE;
+ }
+
+ // attribute: label
+ static LLStdStringHandle label_string = LLXmlTree::addAttributeString("label");
+ if( !node->getFastAttributeString( label_string, mDisplayName ) )
+ {
+ mDisplayName = mName;
+ }
+
+ // JC - make sure the display name includes the capitalization in the XML file,
+ // not the lowercased version.
+ LLString::toLower(mName);
+
+ // attribute: label_min
+ static LLStdStringHandle label_min_string = LLXmlTree::addAttributeString("label_min");
+ if( !node->getFastAttributeString( label_min_string, mMinName ) )
+ {
+ mMinName = "Less";
+ }
+
+ // attribute: label_max
+ static LLStdStringHandle label_max_string = LLXmlTree::addAttributeString("label_max");
+ if( !node->getFastAttributeString( label_max_string, mMaxName ) )
+ {
+ mMaxName = "More";
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLVisualParam()
+//-----------------------------------------------------------------------------
+LLVisualParam::LLVisualParam()
+ :
+ mCurWeight( 0.f ),
+ mLastWeight( 0.f ),
+ mNext( NULL ),
+ mTargetWeight( 0.f ),
+ mIsAnimating( FALSE ),
+ mID( -1 ),
+ mInfo( 0 )
+{
+}
+
+//-----------------------------------------------------------------------------
+// ~LLVisualParam()
+//-----------------------------------------------------------------------------
+LLVisualParam::~LLVisualParam()
+{
+ delete mNext;
+}
+
+/*
+//=============================================================================
+// These virtual functions should always be overridden,
+// but are included here for use as templates
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+
+BOOL LLVisualParam::setInfo(LLVisualParamInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseData()
+//-----------------------------------------------------------------------------
+BOOL LLVisualParam::parseData(LLXmlTreeNode *node)
+{
+ LLVisualParamInfo *info = new LLVisualParamInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ return FALSE;
+
+ return TRUE;
+}
+*/
+
+//-----------------------------------------------------------------------------
+// setWeight()
+//-----------------------------------------------------------------------------
+void LLVisualParam::setWeight(F32 weight, BOOL set_by_user)
+{
+ if (mIsAnimating)
+ {
+ //RN: allow overshoot
+ mCurWeight = weight;
+ }
+ else if (mInfo)
+ {
+ mCurWeight = llclamp(weight, mInfo->mMinWeight, mInfo->mMaxWeight);
+ }
+ else
+ {
+ mCurWeight = weight;
+ }
+
+ if (mNext)
+ {
+ mNext->setWeight(weight, set_by_user);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setAnimationTarget()
+//-----------------------------------------------------------------------------
+void LLVisualParam::setAnimationTarget(F32 target_value, BOOL set_by_user)
+{
+ if (getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ if (mInfo)
+ {
+ mTargetWeight = llclamp(target_value, mInfo->mMinWeight, mInfo->mMaxWeight);
+ }
+ else
+ {
+ mTargetWeight = target_value;
+ }
+ }
+ mIsAnimating = TRUE;
+
+ if (mNext)
+ {
+ mNext->setAnimationTarget(target_value, set_by_user);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setNextParam()
+//-----------------------------------------------------------------------------
+void LLVisualParam::setNextParam( LLVisualParam *next )
+{
+ llassert(!mNext);
+
+ mNext = next;
+}
+
+//-----------------------------------------------------------------------------
+// animate()
+//-----------------------------------------------------------------------------
+void LLVisualParam::animate( F32 delta, BOOL set_by_user )
+{
+ if (mIsAnimating)
+ {
+ F32 new_weight = ((mTargetWeight - mCurWeight) * delta) + mCurWeight;
+ setWeight(new_weight, set_by_user);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// stopAnimating()
+//-----------------------------------------------------------------------------
+void LLVisualParam::stopAnimating(BOOL set_by_user)
+{
+ if (mIsAnimating && getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ mIsAnimating = FALSE;
+ setWeight(mTargetWeight, set_by_user);
+ }
+}
diff --git a/indra/llcharacter/llvisualparam.h b/indra/llcharacter/llvisualparam.h
new file mode 100644
index 0000000000..2a8d03d431
--- /dev/null
+++ b/indra/llcharacter/llvisualparam.h
@@ -0,0 +1,131 @@
+/**
+ * @file llvisualparam.h
+ * @brief Implementation of LLPolyMesh class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVisualParam_H
+#define LL_LLVisualParam_H
+
+#include "v3math.h"
+#include "llstring.h"
+#include "llxmltree.h"
+
+class LLPolyMesh;
+class LLXmlTreeNode;
+
+enum ESex
+{
+ SEX_FEMALE = 0x01,
+ SEX_MALE = 0x02,
+ SEX_BOTH = 0x03 // values chosen to allow use as a bit field.
+};
+
+enum EVisualParamGroup
+{
+ VISUAL_PARAM_GROUP_TWEAKABLE,
+ VISUAL_PARAM_GROUP_ANIMATABLE,
+ NUM_VISUAL_PARAM_GROUPS
+};
+
+const S32 MAX_TRANSMITTED_VISUAL_PARAMS = 255;
+
+//-----------------------------------------------------------------------------
+// LLVisualParamInfo
+// Contains shared data for VisualParams
+//-----------------------------------------------------------------------------
+class LLVisualParamInfo
+{
+ friend class LLVisualParam;
+public:
+ LLVisualParamInfo();
+ virtual ~LLVisualParamInfo() {};
+
+ virtual BOOL parseXml(LLXmlTreeNode *node);
+
+protected:
+ S32 mID; // ID associated with VisualParam
+
+ LLString mName; // name (for internal purposes)
+ LLString mDisplayName; // name displayed to the user
+ LLString mMinName; // name associated with minimum value
+ LLString mMaxName; // name associated with maximum value
+ EVisualParamGroup mGroup; // morph group for separating UI controls
+ F32 mMinWeight; // minimum weight that can be assigned to this morph target
+ F32 mMaxWeight; // maximum weight that can be assigned to this morph target
+ F32 mDefaultWeight;
+ ESex mSex; // Which gender(s) this param applies to.
+};
+
+//-----------------------------------------------------------------------------
+// LLVisualParam
+// VIRTUAL CLASS
+// An interface class for a generalized parametric modification of the avatar mesh
+// Contains data that is specific to each Avatar
+//-----------------------------------------------------------------------------
+class LLVisualParam
+{
+public:
+ LLVisualParam();
+ virtual ~LLVisualParam();
+
+ // Special: These functions are overridden by child classes
+ // (They can not be virtual because they use specific derived Info classes)
+ LLVisualParamInfo* getInfo() const { return mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLVisualParamInfo *info);
+
+ // Virtual functions
+ // Pure virtuals
+ //virtual BOOL parseData( LLXmlTreeNode *node ) = 0;
+ virtual void apply( ESex avatar_sex ) = 0;
+ // Default functions
+ virtual void setWeight(F32 weight, BOOL set_by_user);
+ virtual void setAnimationTarget( F32 target_value, BOOL set_by_user );
+ virtual void animate(F32 delta, BOOL set_by_user);
+ virtual void stopAnimating(BOOL set_by_user);
+
+ // Interface methods
+ const S32 getID() { return mID; }
+ void setID(S32 id) { llassert(!mInfo); mID = id; }
+
+ const LLString& getName() const { return mInfo->mName; }
+ const LLString& getDisplayName() const { return mInfo->mDisplayName; }
+ const LLString& getMaxDisplayName() const { return mInfo->mMaxName; }
+ const LLString& getMinDisplayName() const { return mInfo->mMinName; }
+
+ void setDisplayName(const char* s) { mInfo->mDisplayName = s; }
+ void setMaxDisplayName(const char* s) { mInfo->mMaxName = s; }
+ void setMinDisplayName(const char* s) { mInfo->mMinName = s; }
+
+ const EVisualParamGroup getGroup() { return mInfo->mGroup; }
+ F32 getMinWeight() { return mInfo->mMinWeight; }
+ F32 getMaxWeight() { return mInfo->mMaxWeight; }
+ F32 getDefaultWeight() { return mInfo->mDefaultWeight; }
+ ESex getSex() { return mInfo->mSex; }
+
+ F32 getWeight() { return mIsAnimating ? mTargetWeight : mCurWeight; }
+ F32 getCurrentWeight() { return mCurWeight; }
+ F32 getLastWeight() { return mLastWeight; }
+ BOOL isAnimating() { return mIsAnimating; }
+
+ LLVisualParam* getNextParam() { return mNext; }
+ void setNextParam( LLVisualParam *next );
+
+ virtual void setAnimating(BOOL is_animating) { mIsAnimating = is_animating; }
+ BOOL getAnimating() { return mIsAnimating; }
+
+protected:
+ F32 mCurWeight; // current weight
+ F32 mLastWeight; // last weight
+ LLVisualParam* mNext; // next param in a shared chain
+ F32 mTargetWeight; // interpolation target
+ BOOL mIsAnimating; // this value has been given an interpolation target
+
+ S32 mID; // id for storing weight/morphtarget compares compactly
+ LLVisualParamInfo *mInfo;
+};
+
+#endif // LL_LLVisualParam_H