00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034 #include "llhudeffectlookat.h"
00035
00036 #include "message.h"
00037 #include "llagent.h"
00038 #include "llvoavatar.h"
00039 #include "lldrawable.h"
00040 #include "llviewerobjectlist.h"
00041 #include "llsphere.h"
00042 #include "llselectmgr.h"
00043 #include "llglheaders.h"
00044
00045
00046 #include "llxmltree.h"
00047
00048
00049 BOOL LLHUDEffectLookAt::sDebugLookAt = FALSE;
00050
00051
00052 const S32 SOURCE_AVATAR = 0;
00053 const S32 TARGET_OBJECT = 16;
00054 const S32 TARGET_POS = 32;
00055 const S32 LOOKAT_TYPE = 56;
00056 const S32 PKT_SIZE = 57;
00057
00058
00059 const F32 MAX_SENDS_PER_SEC = 4.f;
00060
00061 const F32 MIN_DELTAPOS_FOR_UPDATE = 0.05f;
00062 const F32 MIN_TARGET_OFFSET_SQUARED = 0.0001f;
00063
00064
00065
00066 const F32 MAX_TIMEOUT = F32_MAX / 2.f;
00067
00071 class LLAttention
00072 {
00073 public:
00074 LLAttention(){}
00075 LLAttention(F32 timeout, F32 priority, char *name, LLColor3 color) :
00076 mTimeout(timeout), mPriority(priority), mName(name), mColor(color)
00077 {
00078 }
00079 F32 mTimeout, mPriority;
00080 LLString mName;
00081 LLColor3 mColor;
00082 };
00083
00087 class LLAttentionSet
00088 {
00089 public:
00090 LLAttentionSet(const LLAttention attentions[])
00091 {
00092 for(int i=0; i<LOOKAT_NUM_TARGETS; i++)
00093 {
00094 mAttentions[i] = attentions[i];
00095 }
00096 }
00097 LLAttention mAttentions[LOOKAT_NUM_TARGETS];
00098 LLAttention& operator[](int idx) { return mAttentions[idx]; }
00099 };
00100
00101
00102
00103
00104
00105
00106
00107
00108 static const
00109 LLAttention
00110 BOY_ATTS[] = {
00111 LLAttention(MAX_TIMEOUT, 0, "None", LLColor3(0.3f, 0.3f, 0.3f)),
00112 LLAttention(3.f, 1, "Idle", LLColor3(0.5f, 0.5f, 0.5f)),
00113 LLAttention(4.f, 3, "AutoListen", LLColor3(0.5f, 0.5f, 0.5f)),
00114 LLAttention(2.f, 2, "FreeLook", LLColor3(0.5f, 0.5f, 0.9f)),
00115 LLAttention(4.f, 3, "Respond", LLColor3(0.0f, 0.0f, 0.0f)),
00116 LLAttention(1.f, 4, "Hover", LLColor3(0.5f, 0.9f, 0.5f)),
00117 LLAttention(MAX_TIMEOUT, 0, "Conversation", LLColor3(0.1f, 0.1f, 0.5f)),
00118 LLAttention(MAX_TIMEOUT, 6, "Select", LLColor3(0.9f, 0.5f, 0.5f)),
00119 LLAttention(MAX_TIMEOUT, 6, "Focus", LLColor3(0.9f, 0.5f, 0.9f)),
00120 LLAttention(MAX_TIMEOUT, 7, "Mouselook", LLColor3(0.9f, 0.9f, 0.5f)),
00121 LLAttention(0.f, 8, "Clear", LLColor3(1.0f, 1.0f, 1.0f)),
00122 },
00123 GIRL_ATTS[] = {
00124 LLAttention(MAX_TIMEOUT, 0, "None", LLColor3(0.3f, 0.3f, 0.3f)),
00125 LLAttention(3.f, 1, "Idle", LLColor3(0.5f, 0.5f, 0.5f)),
00126 LLAttention(4.f, 3, "AutoListen", LLColor3(0.5f, 0.5f, 0.5f)),
00127 LLAttention(2.f, 2, "FreeLook", LLColor3(0.5f, 0.5f, 0.9f)),
00128 LLAttention(4.f, 3, "Respond", LLColor3(0.0f, 0.0f, 0.0f)),
00129 LLAttention(1.f, 4, "Hover", LLColor3(0.5f, 0.9f, 0.5f)),
00130 LLAttention(MAX_TIMEOUT, 0, "Conversation", LLColor3(0.1f, 0.1f, 0.5f)),
00131 LLAttention(MAX_TIMEOUT, 6, "Select", LLColor3(0.9f, 0.5f, 0.5f)),
00132 LLAttention(MAX_TIMEOUT, 6, "Focus", LLColor3(0.9f, 0.5f, 0.9f)),
00133 LLAttention(MAX_TIMEOUT, 7, "Mouselook", LLColor3(0.9f, 0.9f, 0.5f)),
00134 LLAttention(0.f, 8, "Clear", LLColor3(1.0f, 1.0f, 1.0f)),
00135 };
00136
00137 static LLAttentionSet
00138 gBoyAttentions(BOY_ATTS),
00139 gGirlAttentions(GIRL_ATTS);
00140
00141
00142 static BOOL loadGender(LLXmlTreeNode* gender)
00143 {
00144 if( !gender)
00145 {
00146 return FALSE;
00147 }
00148 LLString str;
00149 gender->getAttributeString("name", str);
00150 LLAttentionSet& attentions = (str.compare("Masculine") == 0) ? gBoyAttentions : gGirlAttentions;
00151 for (LLXmlTreeNode* attention_node = gender->getChildByName( "param" );
00152 attention_node;
00153 attention_node = gender->getNextNamedChild())
00154 {
00155 attention_node->getAttributeString("attention", str);
00156 LLAttention* attention;
00157 if (str == "idle") attention = &attentions[LOOKAT_TARGET_IDLE];
00158 else if(str == "auto_listen") attention = &attentions[LOOKAT_TARGET_AUTO_LISTEN];
00159 else if(str == "freelook") attention = &attentions[LOOKAT_TARGET_FREELOOK];
00160 else if(str == "respond") attention = &attentions[LOOKAT_TARGET_RESPOND];
00161 else if(str == "hover") attention = &attentions[LOOKAT_TARGET_HOVER];
00162 else if(str == "conversation") attention = &attentions[LOOKAT_TARGET_CONVERSATION];
00163 else if(str == "select") attention = &attentions[LOOKAT_TARGET_SELECT];
00164 else if(str == "focus") attention = &attentions[LOOKAT_TARGET_FOCUS];
00165 else if(str == "mouselook") attention = &attentions[LOOKAT_TARGET_MOUSELOOK];
00166 else return FALSE;
00167
00168 F32 priority, timeout;
00169 attention_node->getAttributeF32("priority", priority);
00170 attention_node->getAttributeF32("timeout", timeout);
00171 if(timeout < 0) timeout = MAX_TIMEOUT;
00172 attention->mPriority = priority;
00173 attention->mTimeout = timeout;
00174 }
00175 return TRUE;
00176 }
00177
00178 static BOOL loadAttentions()
00179 {
00180 static BOOL first_time = TRUE;
00181 if( ! first_time)
00182 {
00183 return TRUE;
00184 }
00185 first_time = FALSE;
00186
00187 char filename[MAX_PATH];
00188 strncpy(filename,gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,"attentions.xml").c_str(), sizeof(filename) -1);
00189 filename[sizeof(filename) -1] = '\0';
00190 LLXmlTree xml_tree;
00191 BOOL success = xml_tree.parseFile( filename, FALSE );
00192 if( !success )
00193 {
00194 return FALSE;
00195 }
00196 LLXmlTreeNode* root = xml_tree.getRoot();
00197 if( !root )
00198 {
00199 return FALSE;
00200 }
00201
00202
00203
00204
00205 if( !root->hasName( "linden_attentions" ) )
00206 {
00207 llwarns << "Invalid linden_attentions file header: " << filename << llendl;
00208 return FALSE;
00209 }
00210
00211 LLString version;
00212 static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
00213 if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
00214 {
00215 llwarns << "Invalid linden_attentions file version: " << version << llendl;
00216 return FALSE;
00217 }
00218
00219
00220
00221
00222 for (LLXmlTreeNode* child = root->getChildByName( "gender" );
00223 child;
00224 child = root->getNextNamedChild())
00225 {
00226 if( !loadGender( child ) )
00227 {
00228 return FALSE;
00229 }
00230 }
00231
00232 return TRUE;
00233 }
00234
00235
00236
00237
00238
00239
00240
00241 LLHUDEffectLookAt::LLHUDEffectLookAt(const U8 type) :
00242 LLHUDEffect(type),
00243 mKillTime(0.f),
00244 mLastSendTime(0.f)
00245 {
00246 clearLookAtTarget();
00247
00248 loadAttentions();
00249
00250 mAttentions = &gGirlAttentions;
00251 }
00252
00253
00254
00255
00256 LLHUDEffectLookAt::~LLHUDEffectLookAt()
00257 {
00258 }
00259
00260
00261
00262
00263 void LLHUDEffectLookAt::packData(LLMessageSystem *mesgsys)
00264 {
00265
00266 LLHUDEffect::packData(mesgsys);
00267
00268
00269 U8 packed_data[PKT_SIZE];
00270 memset(packed_data, 0, PKT_SIZE);
00271
00272 if (mSourceObject)
00273 {
00274 htonmemcpy(&(packed_data[SOURCE_AVATAR]), mSourceObject->mID.mData, MVT_LLUUID, 16);
00275 }
00276 else
00277 {
00278 htonmemcpy(&(packed_data[SOURCE_AVATAR]), LLUUID::null.mData, MVT_LLUUID, 16);
00279 }
00280
00281
00282
00283 if (mTargetObject)
00284 {
00285 htonmemcpy(&(packed_data[TARGET_OBJECT]), mTargetObject->mID.mData, MVT_LLUUID, 16);
00286 }
00287 else
00288 {
00289 htonmemcpy(&(packed_data[TARGET_OBJECT]), LLUUID::null.mData, MVT_LLUUID, 16);
00290 }
00291
00292 htonmemcpy(&(packed_data[TARGET_POS]), mTargetOffsetGlobal.mdV, MVT_LLVector3d, 24);
00293
00294 U8 lookAtTypePacked = (U8)mTargetType;
00295
00296 htonmemcpy(&(packed_data[LOOKAT_TYPE]), &lookAtTypePacked, MVT_U8, 1);
00297
00298 mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, PKT_SIZE);
00299
00300 mLastSendTime = mTimer.getElapsedTimeF32();
00301 }
00302
00303
00304
00305
00306 void LLHUDEffectLookAt::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
00307 {
00308 LLVector3d new_target;
00309 U8 packed_data[PKT_SIZE];
00310
00311 LLUUID dataId;
00312 mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, dataId, blocknum);
00313
00314 if (!gAgent.mLookAt.isNull() && dataId == gAgent.mLookAt->getID())
00315 {
00316 return;
00317 }
00318
00319 LLHUDEffect::unpackData(mesgsys, blocknum);
00320 LLUUID source_id;
00321 LLUUID target_id;
00322 S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
00323 if (size != PKT_SIZE)
00324 {
00325 llwarns << "LookAt effect with bad size " << size << llendl;
00326 return;
00327 }
00328 mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, PKT_SIZE, blocknum);
00329
00330 htonmemcpy(source_id.mData, &(packed_data[SOURCE_AVATAR]), MVT_LLUUID, 16);
00331
00332 LLViewerObject *objp = gObjectList.findObject(source_id);
00333 if (objp && objp->isAvatar())
00334 {
00335 setSourceObject(objp);
00336 }
00337 else
00338 {
00339
00340 return;
00341 }
00342
00343 htonmemcpy(target_id.mData, &(packed_data[TARGET_OBJECT]), MVT_LLUUID, 16);
00344
00345 objp = gObjectList.findObject(target_id);
00346
00347 htonmemcpy(new_target.mdV, &(packed_data[TARGET_POS]), MVT_LLVector3d, 24);
00348
00349 if (objp)
00350 {
00351 setTargetObjectAndOffset(objp, new_target);
00352 }
00353 else if (target_id.isNull())
00354 {
00355 setTargetPosGlobal(new_target);
00356 }
00357 else
00358 {
00359
00360 }
00361
00362 U8 lookAtTypeUnpacked = 0;
00363 htonmemcpy(&lookAtTypeUnpacked, &(packed_data[LOOKAT_TYPE]), MVT_U8, 1);
00364 mTargetType = (ELookAtType)lookAtTypeUnpacked;
00365
00366 if (mTargetType == LOOKAT_TARGET_NONE)
00367 {
00368 clearLookAtTarget();
00369 }
00370 }
00371
00372
00373
00374
00375 void LLHUDEffectLookAt::setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset)
00376 {
00377 mTargetObject = objp;
00378 mTargetOffsetGlobal = offset;
00379 }
00380
00381
00382
00383
00384 void LLHUDEffectLookAt::setTargetPosGlobal(const LLVector3d &target_pos_global)
00385 {
00386 mTargetObject = NULL;
00387 mTargetOffsetGlobal = target_pos_global;
00388 }
00389
00390
00391
00392
00393
00394 BOOL LLHUDEffectLookAt::setLookAt(ELookAtType target_type, LLViewerObject *object, LLVector3 position)
00395 {
00396 if (!mSourceObject)
00397 {
00398 return FALSE;
00399 }
00400
00401 llassert(target_type < LOOKAT_NUM_TARGETS);
00402
00403
00404 if ((*mAttentions)[target_type].mPriority < (*mAttentions)[mTargetType].mPriority)
00405 {
00406 return FALSE;
00407 }
00408
00409 F32 current_time = mTimer.getElapsedTimeF32();
00410
00411
00412 BOOL lookAtChanged = (target_type != mTargetType) || (object != mTargetObject);
00413
00414
00415 lookAtChanged = lookAtChanged || (dist_vec(position, mLastSentOffsetGlobal) > MIN_DELTAPOS_FOR_UPDATE) &&
00416 ((current_time - mLastSendTime) > (1.f / MAX_SENDS_PER_SEC));
00417
00418 if (lookAtChanged)
00419 {
00420 mLastSentOffsetGlobal = position;
00421 F32 timeout = (*mAttentions)[target_type].mTimeout;
00422 setDuration(timeout);
00423 setNeedsSendToSim(TRUE);
00424 }
00425
00426 if (target_type == LOOKAT_TARGET_CLEAR)
00427 {
00428 clearLookAtTarget();
00429 }
00430 else
00431 {
00432 mTargetType = target_type;
00433 mTargetObject = object;
00434 if (object)
00435 {
00436 mTargetOffsetGlobal.setVec(position);
00437 }
00438 else
00439 {
00440 mTargetOffsetGlobal = gAgent.getPosGlobalFromAgent(position);
00441 }
00442 mKillTime = mTimer.getElapsedTimeF32() + mDuration;
00443
00444 update();
00445 }
00446 return TRUE;
00447 }
00448
00449
00450
00451
00452 void LLHUDEffectLookAt::clearLookAtTarget()
00453 {
00454 mTargetObject = NULL;
00455 mTargetOffsetGlobal.clearVec();
00456 mTargetType = LOOKAT_TARGET_NONE;
00457 if (mSourceObject.notNull())
00458 {
00459 ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->stopMotion(ANIM_AGENT_HEAD_ROT);
00460 }
00461 }
00462
00463
00464
00465
00466 void LLHUDEffectLookAt::markDead()
00467 {
00468 if (mSourceObject.notNull())
00469 {
00470 ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->removeAnimationData("LookAtPoint");
00471 }
00472
00473 mSourceObject = NULL;
00474 clearLookAtTarget();
00475 LLHUDEffect::markDead();
00476 }
00477
00478 void LLHUDEffectLookAt::setSourceObject(LLViewerObject* objectp)
00479 {
00480
00481 if (objectp && objectp->isAvatar())
00482 {
00483 LLHUDEffect::setSourceObject(objectp);
00484 }
00485 }
00486
00487
00488
00489
00490 void LLHUDEffectLookAt::render()
00491 {
00492 if (sDebugLookAt && mSourceObject.notNull())
00493 {
00494 LLGLSNoTexture gls_no_texture;
00495
00496 LLVector3 target = mTargetPos + ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->mHeadp->getWorldPosition();
00497 glMatrixMode(GL_MODELVIEW);
00498 glPushMatrix();
00499 glTranslatef(target.mV[VX], target.mV[VY], target.mV[VZ]);
00500 glScalef(0.3f, 0.3f, 0.3f);
00501 glBegin(GL_LINES);
00502 {
00503 LLColor3 color = (*mAttentions)[mTargetType].mColor;
00504 glColor3f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]);
00505 glVertex3f(-1.f, 0.f, 0.f);
00506 glVertex3f(1.f, 0.f, 0.f);
00507
00508 glVertex3f(0.f, -1.f, 0.f);
00509 glVertex3f(0.f, 1.f, 0.f);
00510
00511 glVertex3f(0.f, 0.f, -1.f);
00512 glVertex3f(0.f, 0.f, 1.f);
00513 } glEnd();
00514 glPopMatrix();
00515 }
00516 }
00517
00518
00519
00520
00521 void LLHUDEffectLookAt::update()
00522 {
00523
00524 if (!mTargetObject.isNull() && mTargetObject->isDead())
00525 {
00526 clearLookAtTarget();
00527 }
00528
00529
00530 if (mSourceObject.isNull() || mSourceObject->isDead())
00531 {
00532 markDead();
00533 return;
00534 }
00535
00536
00537 LLVOAvatar* source_avatar = (LLVOAvatar*)(LLViewerObject*)mSourceObject;
00538
00539
00540 mAttentions = (source_avatar->getSex() == SEX_MALE) ? &gBoyAttentions : &gGirlAttentions;
00541
00542
00543 F32 time = mTimer.getElapsedTimeF32();
00544
00545
00546 if (mKillTime != 0.f && time > mKillTime)
00547 {
00548 if (mTargetType != LOOKAT_TARGET_NONE)
00549 {
00550 clearLookAtTarget();
00551
00552 setNeedsSendToSim(TRUE);
00553 }
00554 }
00555
00556 if (mTargetType != LOOKAT_TARGET_NONE)
00557 {
00558 calcTargetPosition();
00559
00560 LLMotion* head_motion = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->findMotion(ANIM_AGENT_HEAD_ROT);
00561 if (!head_motion || head_motion->isStopped())
00562 {
00563 ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->startMotion(ANIM_AGENT_HEAD_ROT);
00564 }
00565 }
00566
00567 if (sDebugLookAt)
00568 {
00569 ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->addDebugText((*mAttentions)[mTargetType].mName);
00570 }
00571 }
00572
00581 void LLHUDEffectLookAt::calcTargetPosition()
00582 {
00583 if (gNoRender)
00584 {
00585 return;
00586 }
00587
00588 LLViewerObject *target_obj = (LLViewerObject *)mTargetObject;
00589 LLVector3 local_offset;
00590
00591 if (target_obj)
00592 {
00593 local_offset.setVec(mTargetOffsetGlobal);
00594 }
00595 else
00596 {
00597 local_offset = gAgent.getPosAgentFromGlobal(mTargetOffsetGlobal);
00598 }
00599
00600 LLVOAvatar* source_avatar = (LLVOAvatar*)(LLViewerObject*)mSourceObject;
00601
00602 if (target_obj && target_obj->mDrawable.notNull())
00603 {
00604 LLQuaternion target_rot;
00605 if (target_obj->isAvatar())
00606 {
00607 LLVOAvatar *target_av = (LLVOAvatar *)target_obj;
00608
00609 BOOL looking_at_self = source_avatar->isSelf() && target_av->isSelf();
00610
00611
00612 if (looking_at_self && mTargetOffsetGlobal.magVecSquared() < MIN_TARGET_OFFSET_SQUARED)
00613 {
00614
00615 mTargetOffsetGlobal.setVec(5.0, 0.0, 0.0);
00616 local_offset.setVec(mTargetOffsetGlobal);
00617 }
00618
00619
00620 mTargetPos = target_av->mHeadp->getWorldPosition();
00621 if (mTargetType == LOOKAT_TARGET_MOUSELOOK || mTargetType == LOOKAT_TARGET_FREELOOK)
00622 {
00623
00624 target_rot = LLQuaternion::DEFAULT;
00625 }
00626 else if (looking_at_self && gAgent.cameraCustomizeAvatar())
00627 {
00628
00629
00630
00631 target_rot = target_av->mPelvisp->getWorldRotation();
00632 }
00633 else
00634 {
00635 target_rot = target_av->mRoot.getWorldRotation();
00636 }
00637 }
00638 else
00639 {
00640 if (target_obj->mDrawable->getGeneration() == -1)
00641 {
00642 mTargetPos = target_obj->getPositionAgent();
00643 target_rot = target_obj->getWorldRotation();
00644 }
00645 else
00646 {
00647 mTargetPos = target_obj->getRenderPosition();
00648 target_rot = target_obj->getRenderRotation();
00649 }
00650 }
00651
00652 mTargetPos += (local_offset * target_rot);
00653 }
00654 else
00655 {
00656 mTargetPos = local_offset;
00657 }
00658
00659 mTargetPos -= source_avatar->mHeadp->getWorldPosition();
00660 source_avatar->setAnimationData("LookAtPoint", (void *)&mTargetPos);
00661 }