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