Annotation of /trunk/indra/newview/llhudeffectlookat.cpp
Parent Directory
|
Revision Log
Revision 137 - (view) (download)
| 1 : | mjm | 135 | /** |
| 2 : | * @file llhudeffectlookat.cpp | ||
| 3 : | * @brief LLHUDEffectLookAt class implementation | ||
| 4 : | * | ||
| 5 : | * $LicenseInfo:firstyear=2002&license=viewergpl$ | ||
| 6 : | * | ||
| 7 : | mjm | 137 | * Copyright (c) 2002-2010, Linden Research, Inc. |
| 8 : | mjm | 135 | * |
| 9 : | * Second Life Viewer Source Code | ||
| 10 : | * The source code in this file ("Source Code") is provided by Linden Lab | ||
| 11 : | * to you under the terms of the GNU General Public License, version 2.0 | ||
| 12 : | * ("GPL"), unless you have obtained a separate licensing agreement | ||
| 13 : | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
| 14 : | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
| 15 : | * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 | ||
| 16 : | * | ||
| 17 : | * There are special exceptions to the terms and conditions of the GPL as | ||
| 18 : | * it is applied to this Source Code. View the full text of the exception | ||
| 19 : | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
| 20 : | * online at | ||
| 21 : | * http://secondlifegrid.net/programs/open_source/licensing/flossexception | ||
| 22 : | * | ||
| 23 : | * By copying, modifying or distributing this software, you acknowledge | ||
| 24 : | * that you have read and understood your obligations described above, | ||
| 25 : | * and agree to abide by those obligations. | ||
| 26 : | * | ||
| 27 : | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
| 28 : | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
| 29 : | * COMPLETENESS OR PERFORMANCE. | ||
| 30 : | * $/LicenseInfo$ | ||
| 31 : | */ | ||
| 32 : | |||
| 33 : | #include "llviewerprecompiledheaders.h" | ||
| 34 : | |||
| 35 : | #include "llhudeffectlookat.h" | ||
| 36 : | |||
| 37 : | #include "llrender.h" | ||
| 38 : | |||
| 39 : | #include "message.h" | ||
| 40 : | #include "llagent.h" | ||
| 41 : | #include "llvoavatar.h" | ||
| 42 : | #include "lldrawable.h" | ||
| 43 : | #include "llviewerobjectlist.h" | ||
| 44 : | #include "llrendersphere.h" | ||
| 45 : | #include "llselectmgr.h" | ||
| 46 : | #include "llglheaders.h" | ||
| 47 : | |||
| 48 : | |||
| 49 : | #include "llxmltree.h" | ||
| 50 : | |||
| 51 : | |||
| 52 : | BOOL LLHUDEffectLookAt::sDebugLookAt = FALSE; | ||
| 53 : | |||
| 54 : | // packet layout | ||
| 55 : | const S32 SOURCE_AVATAR = 0; | ||
| 56 : | const S32 TARGET_OBJECT = 16; | ||
| 57 : | const S32 TARGET_POS = 32; | ||
| 58 : | const S32 LOOKAT_TYPE = 56; | ||
| 59 : | const S32 PKT_SIZE = 57; | ||
| 60 : | |||
| 61 : | // throttle | ||
| 62 : | const F32 MAX_SENDS_PER_SEC = 4.f; | ||
| 63 : | |||
| 64 : | const F32 MIN_DELTAPOS_FOR_UPDATE = 0.05f; | ||
| 65 : | const F32 MIN_TARGET_OFFSET_SQUARED = 0.0001f; | ||
| 66 : | |||
| 67 : | |||
| 68 : | // can't use actual F32_MAX, because we add this to the current frametime | ||
| 69 : | const F32 MAX_TIMEOUT = F32_MAX / 2.f; | ||
| 70 : | |||
| 71 : | /** | ||
| 72 : | * Simple data class holding values for a particular type of attention. | ||
| 73 : | */ | ||
| 74 : | class LLAttention | ||
| 75 : | { | ||
| 76 : | public: | ||
| 77 : | LLAttention() | ||
| 78 : | : mTimeout(0.f), | ||
| 79 : | mPriority(0.f) | ||
| 80 : | {} | ||
| 81 : | LLAttention(F32 timeout, F32 priority, const std::string& name, LLColor3 color) : | ||
| 82 : | mTimeout(timeout), mPriority(priority), mName(name), mColor(color) | ||
| 83 : | { | ||
| 84 : | } | ||
| 85 : | F32 mTimeout, mPriority; | ||
| 86 : | std::string mName; | ||
| 87 : | LLColor3 mColor; | ||
| 88 : | }; | ||
| 89 : | |||
| 90 : | /** | ||
| 91 : | * Simple data class holding a list of attentions, one for every type. | ||
| 92 : | */ | ||
| 93 : | class LLAttentionSet | ||
| 94 : | { | ||
| 95 : | public: | ||
| 96 : | LLAttentionSet(const LLAttention attentions[]) | ||
| 97 : | { | ||
| 98 : | for(int i=0; i<LOOKAT_NUM_TARGETS; i++) | ||
| 99 : | { | ||
| 100 : | mAttentions[i] = attentions[i]; | ||
| 101 : | } | ||
| 102 : | } | ||
| 103 : | LLAttention mAttentions[LOOKAT_NUM_TARGETS]; | ||
| 104 : | LLAttention& operator[](int idx) { return mAttentions[idx]; } | ||
| 105 : | }; | ||
| 106 : | |||
| 107 : | // Default attribute set data. | ||
| 108 : | // Used to initialize the global attribute set objects, one of which will be | ||
| 109 : | // refered to by the hud object at any given time. | ||
| 110 : | // Note that the values below are only the default values and that any or all of them | ||
| 111 : | // can be overwritten with customizing data from the XML file. The actual values below | ||
| 112 : | // are those that will give exactly the same look-at behavior as before the ability | ||
| 113 : | // to customize was added. - MG | ||
| 114 : | static const | ||
| 115 : | LLAttention | ||
| 116 : | BOY_ATTS[] = { // default set of masculine attentions | ||
| 117 : | LLAttention(MAX_TIMEOUT, 0, "None", LLColor3(0.3f, 0.3f, 0.3f)), // LOOKAT_TARGET_NONE | ||
| 118 : | LLAttention(3.f, 1, "Idle", LLColor3(0.5f, 0.5f, 0.5f)), // LOOKAT_TARGET_IDLE | ||
| 119 : | LLAttention(4.f, 3, "AutoListen", LLColor3(0.5f, 0.5f, 0.5f)), // LOOKAT_TARGET_AUTO_LISTEN | ||
| 120 : | LLAttention(2.f, 2, "FreeLook", LLColor3(0.5f, 0.5f, 0.9f)), // LOOKAT_TARGET_FREELOOK | ||
| 121 : | LLAttention(4.f, 3, "Respond", LLColor3(0.0f, 0.0f, 0.0f)), // LOOKAT_TARGET_RESPOND | ||
| 122 : | LLAttention(1.f, 4, "Hover", LLColor3(0.5f, 0.9f, 0.5f)), // LOOKAT_TARGET_HOVER | ||
| 123 : | LLAttention(MAX_TIMEOUT, 0, "Conversation", LLColor3(0.1f, 0.1f, 0.5f)), // LOOKAT_TARGET_CONVERSATION | ||
| 124 : | LLAttention(MAX_TIMEOUT, 6, "Select", LLColor3(0.9f, 0.5f, 0.5f)), // LOOKAT_TARGET_SELECT | ||
| 125 : | LLAttention(MAX_TIMEOUT, 6, "Focus", LLColor3(0.9f, 0.5f, 0.9f)), // LOOKAT_TARGET_FOCUS | ||
| 126 : | LLAttention(MAX_TIMEOUT, 7, "Mouselook", LLColor3(0.9f, 0.9f, 0.5f)), // LOOKAT_TARGET_MOUSELOOK | ||
| 127 : | LLAttention(0.f, 8, "Clear", LLColor3(1.0f, 1.0f, 1.0f)), // LOOKAT_TARGET_CLEAR | ||
| 128 : | }, | ||
| 129 : | GIRL_ATTS[] = { // default set of feminine attentions | ||
| 130 : | LLAttention(MAX_TIMEOUT, 0, "None", LLColor3(0.3f, 0.3f, 0.3f)), // LOOKAT_TARGET_NONE | ||
| 131 : | LLAttention(3.f, 1, "Idle", LLColor3(0.5f, 0.5f, 0.5f)), // LOOKAT_TARGET_IDLE | ||
| 132 : | LLAttention(4.f, 3, "AutoListen", LLColor3(0.5f, 0.5f, 0.5f)), // LOOKAT_TARGET_AUTO_LISTEN | ||
| 133 : | LLAttention(2.f, 2, "FreeLook", LLColor3(0.5f, 0.5f, 0.9f)), // LOOKAT_TARGET_FREELOOK | ||
| 134 : | LLAttention(4.f, 3, "Respond", LLColor3(0.0f, 0.0f, 0.0f)), // LOOKAT_TARGET_RESPOND | ||
| 135 : | LLAttention(1.f, 4, "Hover", LLColor3(0.5f, 0.9f, 0.5f)), // LOOKAT_TARGET_HOVER | ||
| 136 : | LLAttention(MAX_TIMEOUT, 0, "Conversation", LLColor3(0.1f, 0.1f, 0.5f)), // LOOKAT_TARGET_CONVERSATION | ||
| 137 : | LLAttention(MAX_TIMEOUT, 6, "Select", LLColor3(0.9f, 0.5f, 0.5f)), // LOOKAT_TARGET_SELECT | ||
| 138 : | LLAttention(MAX_TIMEOUT, 6, "Focus", LLColor3(0.9f, 0.5f, 0.9f)), // LOOKAT_TARGET_FOCUS | ||
| 139 : | LLAttention(MAX_TIMEOUT, 7, "Mouselook", LLColor3(0.9f, 0.9f, 0.5f)), // LOOKAT_TARGET_MOUSELOOK | ||
| 140 : | LLAttention(0.f, 8, "Clear", LLColor3(1.0f, 1.0f, 1.0f)), // LOOKAT_TARGET_CLEAR | ||
| 141 : | }; | ||
| 142 : | |||
| 143 : | static LLAttentionSet | ||
| 144 : | gBoyAttentions(BOY_ATTS), | ||
| 145 : | gGirlAttentions(GIRL_ATTS); | ||
| 146 : | |||
| 147 : | |||
| 148 : | static BOOL loadGender(LLXmlTreeNode* gender) | ||
| 149 : | { | ||
| 150 : | if( !gender) | ||
| 151 : | { | ||
| 152 : | return FALSE; | ||
| 153 : | } | ||
| 154 : | std::string str; | ||
| 155 : | gender->getAttributeString("name", str); | ||
| 156 : | LLAttentionSet& attentions = (str.compare("Masculine") == 0) ? gBoyAttentions : gGirlAttentions; | ||
| 157 : | for (LLXmlTreeNode* attention_node = gender->getChildByName( "param" ); | ||
| 158 : | attention_node; | ||
| 159 : | attention_node = gender->getNextNamedChild()) | ||
| 160 : | { | ||
| 161 : | attention_node->getAttributeString("attention", str); | ||
| 162 : | LLAttention* attention; | ||
| 163 : | if (str == "idle") attention = &attentions[LOOKAT_TARGET_IDLE]; | ||
| 164 : | else if(str == "auto_listen") attention = &attentions[LOOKAT_TARGET_AUTO_LISTEN]; | ||
| 165 : | else if(str == "freelook") attention = &attentions[LOOKAT_TARGET_FREELOOK]; | ||
| 166 : | else if(str == "respond") attention = &attentions[LOOKAT_TARGET_RESPOND]; | ||
| 167 : | else if(str == "hover") attention = &attentions[LOOKAT_TARGET_HOVER]; | ||
| 168 : | else if(str == "conversation") attention = &attentions[LOOKAT_TARGET_CONVERSATION]; | ||
| 169 : | else if(str == "select") attention = &attentions[LOOKAT_TARGET_SELECT]; | ||
| 170 : | else if(str == "focus") attention = &attentions[LOOKAT_TARGET_FOCUS]; | ||
| 171 : | else if(str == "mouselook") attention = &attentions[LOOKAT_TARGET_MOUSELOOK]; | ||
| 172 : | else return FALSE; | ||
| 173 : | |||
| 174 : | F32 priority, timeout; | ||
| 175 : | attention_node->getAttributeF32("priority", priority); | ||
| 176 : | attention_node->getAttributeF32("timeout", timeout); | ||
| 177 : | if(timeout < 0) timeout = MAX_TIMEOUT; | ||
| 178 : | attention->mPriority = priority; | ||
| 179 : | attention->mTimeout = timeout; | ||
| 180 : | } | ||
| 181 : | return TRUE; | ||
| 182 : | } | ||
| 183 : | |||
| 184 : | static BOOL loadAttentions() | ||
| 185 : | { | ||
| 186 : | static BOOL first_time = TRUE; | ||
| 187 : | if( ! first_time) | ||
| 188 : | { | ||
| 189 : | return TRUE; // maybe not ideal but otherwise it can continue to fail forever. | ||
| 190 : | } | ||
| 191 : | first_time = FALSE; | ||
| 192 : | |||
| 193 : | std::string filename; | ||
| 194 : | filename = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,"attentions.xml"); | ||
| 195 : | LLXmlTree xml_tree; | ||
| 196 : | BOOL success = xml_tree.parseFile( filename, FALSE ); | ||
| 197 : | if( !success ) | ||
| 198 : | { | ||
| 199 : | return FALSE; | ||
| 200 : | } | ||
| 201 : | LLXmlTreeNode* root = xml_tree.getRoot(); | ||
| 202 : | if( !root ) | ||
| 203 : | { | ||
| 204 : | return FALSE; | ||
| 205 : | } | ||
| 206 : | |||
| 207 : | //------------------------------------------------------------------------- | ||
| 208 : | // <linden_attentions version="1.0"> (root) | ||
| 209 : | //------------------------------------------------------------------------- | ||
| 210 : | if( !root->hasName( "linden_attentions" ) ) | ||
| 211 : | { | ||
| 212 : | llwarns << "Invalid linden_attentions file header: " << filename << llendl; | ||
| 213 : | return FALSE; | ||
| 214 : | } | ||
| 215 : | |||
| 216 : | std::string version; | ||
| 217 : | static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version"); | ||
| 218 : | if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") ) | ||
| 219 : | { | ||
| 220 : | llwarns << "Invalid linden_attentions file version: " << version << llendl; | ||
| 221 : | return FALSE; | ||
| 222 : | } | ||
| 223 : | |||
| 224 : | //------------------------------------------------------------------------- | ||
| 225 : | // <gender> | ||
| 226 : | //------------------------------------------------------------------------- | ||
| 227 : | for (LLXmlTreeNode* child = root->getChildByName( "gender" ); | ||
| 228 : | child; | ||
| 229 : | child = root->getNextNamedChild()) | ||
| 230 : | { | ||
| 231 : | if( !loadGender( child ) ) | ||
| 232 : | { | ||
| 233 : | return FALSE; | ||
| 234 : | } | ||
| 235 : | } | ||
| 236 : | |||
| 237 : | return TRUE; | ||
| 238 : | } | ||
| 239 : | |||
| 240 : | |||
| 241 : | |||
| 242 : | |||
| 243 : | //----------------------------------------------------------------------------- | ||
| 244 : | // LLHUDEffectLookAt() | ||
| 245 : | //----------------------------------------------------------------------------- | ||
| 246 : | LLHUDEffectLookAt::LLHUDEffectLookAt(const U8 type) : | ||
| 247 : | LLHUDEffect(type), | ||
| 248 : | mKillTime(0.f), | ||
| 249 : | mLastSendTime(0.f) | ||
| 250 : | { | ||
| 251 : | clearLookAtTarget(); | ||
| 252 : | // parse the default sets | ||
| 253 : | loadAttentions(); | ||
| 254 : | // initialize current attention set. switches when avatar sex changes. | ||
| 255 : | mAttentions = &gGirlAttentions; | ||
| 256 : | } | ||
| 257 : | |||
| 258 : | //----------------------------------------------------------------------------- | ||
| 259 : | // ~LLHUDEffectLookAt() | ||
| 260 : | //----------------------------------------------------------------------------- | ||
| 261 : | LLHUDEffectLookAt::~LLHUDEffectLookAt() | ||
| 262 : | { | ||
| 263 : | } | ||
| 264 : | |||
| 265 : | //----------------------------------------------------------------------------- | ||
| 266 : | // packData() | ||
| 267 : | //----------------------------------------------------------------------------- | ||
| 268 : | void LLHUDEffectLookAt::packData(LLMessageSystem *mesgsys) | ||
| 269 : | { | ||
| 270 : | // Pack the default data | ||
| 271 : | LLHUDEffect::packData(mesgsys); | ||
| 272 : | |||
| 273 : | // Pack the type-specific data. Uses a fun packed binary format. Whee! | ||
| 274 : | U8 packed_data[PKT_SIZE]; | ||
| 275 : | memset(packed_data, 0, PKT_SIZE); | ||
| 276 : | |||
| 277 : | if (mSourceObject) | ||
| 278 : | { | ||
| 279 : | htonmemcpy(&(packed_data[SOURCE_AVATAR]), mSourceObject->mID.mData, MVT_LLUUID, 16); | ||
| 280 : | } | ||
| 281 : | else | ||
| 282 : | { | ||
| 283 : | htonmemcpy(&(packed_data[SOURCE_AVATAR]), LLUUID::null.mData, MVT_LLUUID, 16); | ||
| 284 : | } | ||
| 285 : | |||
| 286 : | // pack both target object and position | ||
| 287 : | // position interpreted as offset if target object is non-null | ||
| 288 : | if (mTargetObject) | ||
| 289 : | { | ||
| 290 : | htonmemcpy(&(packed_data[TARGET_OBJECT]), mTargetObject->mID.mData, MVT_LLUUID, 16); | ||
| 291 : | } | ||
| 292 : | else | ||
| 293 : | { | ||
| 294 : | htonmemcpy(&(packed_data[TARGET_OBJECT]), LLUUID::null.mData, MVT_LLUUID, 16); | ||
| 295 : | } | ||
| 296 : | |||
| 297 : | htonmemcpy(&(packed_data[TARGET_POS]), mTargetOffsetGlobal.mdV, MVT_LLVector3d, 24); | ||
| 298 : | |||
| 299 : | U8 lookAtTypePacked = (U8)mTargetType; | ||
| 300 : | |||
| 301 : | htonmemcpy(&(packed_data[LOOKAT_TYPE]), &lookAtTypePacked, MVT_U8, 1); | ||
| 302 : | |||
| 303 : | mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, PKT_SIZE); | ||
| 304 : | |||
| 305 : | mLastSendTime = mTimer.getElapsedTimeF32(); | ||
| 306 : | } | ||
| 307 : | |||
| 308 : | //----------------------------------------------------------------------------- | ||
| 309 : | // unpackData() | ||
| 310 : | //----------------------------------------------------------------------------- | ||
| 311 : | void LLHUDEffectLookAt::unpackData(LLMessageSystem *mesgsys, S32 blocknum) | ||
| 312 : | { | ||
| 313 : | LLVector3d new_target; | ||
| 314 : | U8 packed_data[PKT_SIZE]; | ||
| 315 : | |||
| 316 : | LLUUID dataId; | ||
| 317 : | mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, dataId, blocknum); | ||
| 318 : | |||
| 319 : | if (!gAgent.mLookAt.isNull() && dataId == gAgent.mLookAt->getID()) | ||
| 320 : | { | ||
| 321 : | return; | ||
| 322 : | } | ||
| 323 : | |||
| 324 : | LLHUDEffect::unpackData(mesgsys, blocknum); | ||
| 325 : | LLUUID source_id; | ||
| 326 : | LLUUID target_id; | ||
| 327 : | S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData); | ||
| 328 : | if (size != PKT_SIZE) | ||
| 329 : | { | ||
| 330 : | llwarns << "LookAt effect with bad size " << size << llendl; | ||
| 331 : | return; | ||
| 332 : | } | ||
| 333 : | mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, PKT_SIZE, blocknum); | ||
| 334 : | |||
| 335 : | htonmemcpy(source_id.mData, &(packed_data[SOURCE_AVATAR]), MVT_LLUUID, 16); | ||
| 336 : | |||
| 337 : | LLViewerObject *objp = gObjectList.findObject(source_id); | ||
| 338 : | if (objp && objp->isAvatar()) | ||
| 339 : | { | ||
| 340 : | setSourceObject(objp); | ||
| 341 : | } | ||
| 342 : | else | ||
| 343 : | { | ||
| 344 : | //llwarns << "Could not find source avatar for lookat effect" << llendl; | ||
| 345 : | return; | ||
| 346 : | } | ||
| 347 : | |||
| 348 : | htonmemcpy(target_id.mData, &(packed_data[TARGET_OBJECT]), MVT_LLUUID, 16); | ||
| 349 : | |||
| 350 : | objp = gObjectList.findObject(target_id); | ||
| 351 : | |||
| 352 : | htonmemcpy(new_target.mdV, &(packed_data[TARGET_POS]), MVT_LLVector3d, 24); | ||
| 353 : | |||
| 354 : | if (objp) | ||
| 355 : | { | ||
| 356 : | setTargetObjectAndOffset(objp, new_target); | ||
| 357 : | } | ||
| 358 : | else if (target_id.isNull()) | ||
| 359 : | { | ||
| 360 : | setTargetPosGlobal(new_target); | ||
| 361 : | } | ||
| 362 : | else | ||
| 363 : | { | ||
| 364 : | //llwarns << "Could not find target object for lookat effect" << llendl; | ||
| 365 : | } | ||
| 366 : | |||
| 367 : | U8 lookAtTypeUnpacked = 0; | ||
| 368 : | htonmemcpy(&lookAtTypeUnpacked, &(packed_data[LOOKAT_TYPE]), MVT_U8, 1); | ||
| 369 : | mTargetType = (ELookAtType)lookAtTypeUnpacked; | ||
| 370 : | |||
| 371 : | if (mTargetType == LOOKAT_TARGET_NONE) | ||
| 372 : | { | ||
| 373 : | clearLookAtTarget(); | ||
| 374 : | } | ||
| 375 : | } | ||
| 376 : | |||
| 377 : | //----------------------------------------------------------------------------- | ||
| 378 : | // setTargetObjectAndOffset() | ||
| 379 : | //----------------------------------------------------------------------------- | ||
| 380 : | void LLHUDEffectLookAt::setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset) | ||
| 381 : | { | ||
| 382 : | mTargetObject = objp; | ||
| 383 : | mTargetOffsetGlobal = offset; | ||
| 384 : | } | ||
| 385 : | |||
| 386 : | //----------------------------------------------------------------------------- | ||
| 387 : | // setTargetPosGlobal() | ||
| 388 : | //----------------------------------------------------------------------------- | ||
| 389 : | void LLHUDEffectLookAt::setTargetPosGlobal(const LLVector3d &target_pos_global) | ||
| 390 : | { | ||
| 391 : | mTargetObject = NULL; | ||
| 392 : | mTargetOffsetGlobal = target_pos_global; | ||
| 393 : | } | ||
| 394 : | |||
| 395 : | //----------------------------------------------------------------------------- | ||
| 396 : | // setLookAt() | ||
| 397 : | // called by agent logic to set look at behavior locally, and propagate to sim | ||
| 398 : | //----------------------------------------------------------------------------- | ||
| 399 : | BOOL LLHUDEffectLookAt::setLookAt(ELookAtType target_type, LLViewerObject *object, LLVector3 position) | ||
| 400 : | { | ||
| 401 : | if (!mSourceObject) | ||
| 402 : | { | ||
| 403 : | return FALSE; | ||
| 404 : | } | ||
| 405 : | |||
| 406 : | if (target_type >= LOOKAT_NUM_TARGETS) | ||
| 407 : | { | ||
| 408 : | llwarns << "Bad target_type " << (int)target_type << " - ignoring." << llendl; | ||
| 409 : | return FALSE; | ||
| 410 : | } | ||
| 411 : | |||
| 412 : | // must be same or higher priority than existing effect | ||
| 413 : | if ((*mAttentions)[target_type].mPriority < (*mAttentions)[mTargetType].mPriority) | ||
| 414 : | { | ||
| 415 : | return FALSE; | ||
| 416 : | } | ||
| 417 : | |||
| 418 : | F32 current_time = mTimer.getElapsedTimeF32(); | ||
| 419 : | |||
| 420 : | // type of lookat behavior or target object has changed | ||
| 421 : | BOOL lookAtChanged = (target_type != mTargetType) || (object != mTargetObject); | ||
| 422 : | |||
| 423 : | // lookat position has moved a certain amount and we haven't just sent an update | ||
| 424 : | lookAtChanged = lookAtChanged || (dist_vec(position, mLastSentOffsetGlobal) > MIN_DELTAPOS_FOR_UPDATE) && | ||
| 425 : | ((current_time - mLastSendTime) > (1.f / MAX_SENDS_PER_SEC)); | ||
| 426 : | |||
| 427 : | if (lookAtChanged) | ||
| 428 : | { | ||
| 429 : | mLastSentOffsetGlobal = position; | ||
| 430 : | F32 timeout = (*mAttentions)[target_type].mTimeout; | ||
| 431 : | setDuration(timeout); | ||
| 432 : | setNeedsSendToSim(TRUE); | ||
| 433 : | } | ||
| 434 : | |||
| 435 : | if (target_type == LOOKAT_TARGET_CLEAR) | ||
| 436 : | { | ||
| 437 : | clearLookAtTarget(); | ||
| 438 : | } | ||
| 439 : | else | ||
| 440 : | { | ||
| 441 : | mTargetType = target_type; | ||
| 442 : | mTargetObject = object; | ||
| 443 : | if (object) | ||
| 444 : | { | ||
| 445 : | mTargetOffsetGlobal.setVec(position); | ||
| 446 : | } | ||
| 447 : | else | ||
| 448 : | { | ||
| 449 : | mTargetOffsetGlobal = gAgent.getPosGlobalFromAgent(position); | ||
| 450 : | } | ||
| 451 : | mKillTime = mTimer.getElapsedTimeF32() + mDuration; | ||
| 452 : | |||
| 453 : | update(); | ||
| 454 : | } | ||
| 455 : | return TRUE; | ||
| 456 : | } | ||
| 457 : | |||
| 458 : | //----------------------------------------------------------------------------- | ||
| 459 : | // clearLookAtTarget() | ||
| 460 : | //----------------------------------------------------------------------------- | ||
| 461 : | void LLHUDEffectLookAt::clearLookAtTarget() | ||
| 462 : | { | ||
| 463 : | mTargetObject = NULL; | ||
| 464 : | mTargetOffsetGlobal.clearVec(); | ||
| 465 : | mTargetType = LOOKAT_TARGET_NONE; | ||
| 466 : | if (mSourceObject.notNull()) | ||
| 467 : | { | ||
| 468 : | ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->stopMotion(ANIM_AGENT_HEAD_ROT); | ||
| 469 : | } | ||
| 470 : | } | ||
| 471 : | |||
| 472 : | //----------------------------------------------------------------------------- | ||
| 473 : | // markDead() | ||
| 474 : | //----------------------------------------------------------------------------- | ||
| 475 : | void LLHUDEffectLookAt::markDead() | ||
| 476 : | { | ||
| 477 : | if (mSourceObject.notNull()) | ||
| 478 : | { | ||
| 479 : | ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->removeAnimationData("LookAtPoint"); | ||
| 480 : | } | ||
| 481 : | |||
| 482 : | mSourceObject = NULL; | ||
| 483 : | clearLookAtTarget(); | ||
| 484 : | LLHUDEffect::markDead(); | ||
| 485 : | } | ||
| 486 : | |||
| 487 : | void LLHUDEffectLookAt::setSourceObject(LLViewerObject* objectp) | ||
| 488 : | { | ||
| 489 : | // restrict source objects to avatars | ||
| 490 : | if (objectp && objectp->isAvatar()) | ||
| 491 : | { | ||
| 492 : | LLHUDEffect::setSourceObject(objectp); | ||
| 493 : | } | ||
| 494 : | } | ||
| 495 : | |||
| 496 : | //----------------------------------------------------------------------------- | ||
| 497 : | // render() | ||
| 498 : | //----------------------------------------------------------------------------- | ||
| 499 : | void LLHUDEffectLookAt::render() | ||
| 500 : | { | ||
| 501 : | if (sDebugLookAt && mSourceObject.notNull()) | ||
| 502 : | { | ||
| 503 : | gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); | ||
| 504 : | |||
| 505 : | LLVector3 target = mTargetPos + ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->mHeadp->getWorldPosition(); | ||
| 506 : | glMatrixMode(GL_MODELVIEW); | ||
| 507 : | gGL.pushMatrix(); | ||
| 508 : | gGL.translatef(target.mV[VX], target.mV[VY], target.mV[VZ]); | ||
| 509 : | glScalef(0.3f, 0.3f, 0.3f); | ||
| 510 : | gGL.begin(LLRender::LINES); | ||
| 511 : | { | ||
| 512 : | LLColor3 color = (*mAttentions)[mTargetType].mColor; | ||
| 513 : | gGL.color3f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]); | ||
| 514 : | gGL.vertex3f(-1.f, 0.f, 0.f); | ||
| 515 : | gGL.vertex3f(1.f, 0.f, 0.f); | ||
| 516 : | |||
| 517 : | gGL.vertex3f(0.f, -1.f, 0.f); | ||
| 518 : | gGL.vertex3f(0.f, 1.f, 0.f); | ||
| 519 : | |||
| 520 : | gGL.vertex3f(0.f, 0.f, -1.f); | ||
| 521 : | gGL.vertex3f(0.f, 0.f, 1.f); | ||
| 522 : | } gGL.end(); | ||
| 523 : | gGL.popMatrix(); | ||
| 524 : | } | ||
| 525 : | } | ||
| 526 : | |||
| 527 : | //----------------------------------------------------------------------------- | ||
| 528 : | // update() | ||
| 529 : | //----------------------------------------------------------------------------- | ||
| 530 : | void LLHUDEffectLookAt::update() | ||
| 531 : | { | ||
| 532 : | // If the target object is dead, set the target object to NULL | ||
| 533 : | if (!mTargetObject.isNull() && mTargetObject->isDead()) | ||
| 534 : | { | ||
| 535 : | clearLookAtTarget(); | ||
| 536 : | } | ||
| 537 : | |||
| 538 : | // if source avatar is null or dead, mark self as dead and return | ||
| 539 : | if (mSourceObject.isNull() || mSourceObject->isDead()) | ||
| 540 : | { | ||
| 541 : | markDead(); | ||
| 542 : | return; | ||
| 543 : | } | ||
| 544 : | |||
| 545 : | // make sure the proper set of avatar attention are currently being used. | ||
| 546 : | LLVOAvatar* source_avatar = (LLVOAvatar*)(LLViewerObject*)mSourceObject; | ||
| 547 : | // for now the first cut will just switch on sex. future development could adjust | ||
| 548 : | // timeouts according to avatar age and/or other features. | ||
| 549 : | mAttentions = (source_avatar->getSex() == SEX_MALE) ? &gBoyAttentions : &gGirlAttentions; | ||
| 550 : | //printf("updated to %s\n", (source_avatar->getSex() == SEX_MALE) ? "male" : "female"); | ||
| 551 : | |||
| 552 : | F32 time = mTimer.getElapsedTimeF32(); | ||
| 553 : | |||
| 554 : | // clear out the effect if time is up | ||
| 555 : | if (mKillTime != 0.f && time > mKillTime) | ||
| 556 : | { | ||
| 557 : | if (mTargetType != LOOKAT_TARGET_NONE) | ||
| 558 : | { | ||
| 559 : | clearLookAtTarget(); | ||
| 560 : | // look at timed out (only happens on own avatar), so tell everyone | ||
| 561 : | setNeedsSendToSim(TRUE); | ||
| 562 : | } | ||
| 563 : | } | ||
| 564 : | |||
| 565 : | if (mTargetType != LOOKAT_TARGET_NONE) | ||
| 566 : | { | ||
| 567 : | if (calcTargetPosition()) | ||
| 568 : | { | ||
| 569 : | LLMotion* head_motion = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->findMotion(ANIM_AGENT_HEAD_ROT); | ||
| 570 : | if (!head_motion || head_motion->isStopped()) | ||
| 571 : | { | ||
| 572 : | ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->startMotion(ANIM_AGENT_HEAD_ROT); | ||
| 573 : | } | ||
| 574 : | } | ||
| 575 : | } | ||
| 576 : | |||
| 577 : | if (sDebugLookAt) | ||
| 578 : | { | ||
| 579 : | ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->addDebugText((*mAttentions)[mTargetType].mName); | ||
| 580 : | } | ||
| 581 : | } | ||
| 582 : | |||
| 583 : | /** | ||
| 584 : | * Initializes the mTargetPos member from the current mSourceObjec and mTargetObject | ||
| 585 : | * (and possibly mTargetOffsetGlobal). | ||
| 586 : | * When mTargetObject is another avatar, it sets mTargetPos to be their eyes. | ||
| 587 : | * | ||
| 588 : | * Has the side-effect of also calling setAnimationData("LookAtPoint") with the new | ||
| 589 : | * mTargetPos on the source object which is assumed to be an avatar. | ||
| 590 : | * | ||
| 591 : | * Returns whether we successfully calculated a finite target position. | ||
| 592 : | */ | ||
| 593 : | bool LLHUDEffectLookAt::calcTargetPosition() | ||
| 594 : | { | ||
| 595 : | if (gNoRender) | ||
| 596 : | { | ||
| 597 : | return false; | ||
| 598 : | } | ||
| 599 : | |||
| 600 : | LLViewerObject *target_obj = (LLViewerObject *)mTargetObject; | ||
| 601 : | LLVector3 local_offset; | ||
| 602 : | |||
| 603 : | if (target_obj) | ||
| 604 : | { | ||
| 605 : | local_offset.setVec(mTargetOffsetGlobal); | ||
| 606 : | } | ||
| 607 : | else | ||
| 608 : | { | ||
| 609 : | local_offset = gAgent.getPosAgentFromGlobal(mTargetOffsetGlobal); | ||
| 610 : | } | ||
| 611 : | |||
| 612 : | LLVOAvatar* source_avatar = (LLVOAvatar*)(LLViewerObject*)mSourceObject; | ||
| 613 : | |||
| 614 : | if (target_obj && target_obj->mDrawable.notNull()) | ||
| 615 : | { | ||
| 616 : | LLQuaternion target_rot; | ||
| 617 : | if (target_obj->isAvatar()) | ||
| 618 : | { | ||
| 619 : | LLVOAvatar *target_av = (LLVOAvatar *)target_obj; | ||
| 620 : | |||
| 621 : | BOOL looking_at_self = source_avatar->isSelf() && target_av->isSelf(); | ||
| 622 : | |||
| 623 : | // if selecting self, stare forward | ||
| 624 : | if (looking_at_self && mTargetOffsetGlobal.magVecSquared() < MIN_TARGET_OFFSET_SQUARED) | ||
| 625 : | { | ||
| 626 : | //sets the lookat point in front of the avatar | ||
| 627 : | mTargetOffsetGlobal.setVec(5.0, 0.0, 0.0); | ||
| 628 : | local_offset.setVec(mTargetOffsetGlobal); | ||
| 629 : | } | ||
| 630 : | |||
| 631 : | // look the other avatar in the eye. note: what happens if target is self? -MG | ||
| 632 : | mTargetPos = target_av->mHeadp->getWorldPosition(); | ||
| 633 : | if (mTargetType == LOOKAT_TARGET_MOUSELOOK || mTargetType == LOOKAT_TARGET_FREELOOK) | ||
| 634 : | { | ||
| 635 : | // mouselook and freelook target offsets are absolute | ||
| 636 : | target_rot = LLQuaternion::DEFAULT; | ||
| 637 : | } | ||
| 638 : | else if (looking_at_self && gAgent.cameraCustomizeAvatar()) | ||
| 639 : | { | ||
| 640 : | // *NOTE: We have to do this because animation | ||
| 641 : | // overrides do not set lookat behavior. | ||
| 642 : | // *TODO: animation overrides for lookat behavior. | ||
| 643 : | target_rot = target_av->mPelvisp->getWorldRotation(); | ||
| 644 : | } | ||
| 645 : | else | ||
| 646 : | { | ||
| 647 : | target_rot = target_av->mRoot.getWorldRotation(); | ||
| 648 : | } | ||
| 649 : | } | ||
| 650 : | else // target obj is not an avatar | ||
| 651 : | { | ||
| 652 : | if (target_obj->mDrawable->getGeneration() == -1) | ||
| 653 : | { | ||
| 654 : | mTargetPos = target_obj->getPositionAgent(); | ||
| 655 : | target_rot = target_obj->getWorldRotation(); | ||
| 656 : | } | ||
| 657 : | else | ||
| 658 : | { | ||
| 659 : | mTargetPos = target_obj->getRenderPosition(); | ||
| 660 : | target_rot = target_obj->getRenderRotation(); | ||
| 661 : | } | ||
| 662 : | } | ||
| 663 : | |||
| 664 : | mTargetPos += (local_offset * target_rot); | ||
| 665 : | } | ||
| 666 : | else // no target obj or it's not drawable | ||
| 667 : | { | ||
| 668 : | mTargetPos = local_offset; | ||
| 669 : | } | ||
| 670 : | |||
| 671 : | mTargetPos -= source_avatar->mHeadp->getWorldPosition(); | ||
| 672 : | |||
| 673 : | if (!mTargetPos.isFinite()) | ||
| 674 : | return false; | ||
| 675 : | |||
| 676 : | source_avatar->setAnimationData("LookAtPoint", (void *)&mTargetPos); | ||
| 677 : | |||
| 678 : | return true; | ||
| 679 : | } |
| ViewVC Help | |
| Powered by ViewVC 1.0.0 |

