aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llviewerobjectlist.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llviewerobjectlist.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/newview/llviewerobjectlist.cpp')
-rw-r--r--linden/indra/newview/llviewerobjectlist.cpp1472
1 files changed, 1472 insertions, 0 deletions
diff --git a/linden/indra/newview/llviewerobjectlist.cpp b/linden/indra/newview/llviewerobjectlist.cpp
new file mode 100644
index 0000000..49f2aae
--- /dev/null
+++ b/linden/indra/newview/llviewerobjectlist.cpp
@@ -0,0 +1,1472 @@
1/**
2 * @file llviewerobjectlist.cpp
3 * @brief Implementation of LLViewerObjectList class.
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "llviewerprecompiledheaders.h"
29
30#include "llviewerobjectlist.h"
31
32#include "message.h"
33#include "timing.h"
34#include "llfasttimer.h"
35
36#include "llviewercontrol.h"
37#include "llface.h"
38#include "llvoavatar.h"
39#include "llviewerobject.h"
40#include "llviewerwindow.h"
41#include "viewer.h"
42#include "llnetmap.h"
43#include "llagent.h"
44#include "pipeline.h"
45#include "llhoverview.h"
46#include "llworld.h"
47#include "llstring.h"
48#include "llhudtext.h"
49#include "lldrawable.h"
50#include "xform.h"
51#include "llsky.h"
52#include "llviewercamera.h"
53#include "llselectmgr.h"
54#include "llresmgr.h"
55#include "llviewerregion.h"
56#include "llviewerstats.h"
57#include "lltoolmgr.h"
58#include "lltoolpie.h"
59#include "llkeyboard.h"
60#include "u64.h"
61#include "llviewerimagelist.h"
62#include "lldatapacker.h"
63#include <zlib/zlib.h>
64#include "object_flags.h"
65
66extern BOOL gVelocityInterpolate;
67extern BOOL gPingInterpolate;
68extern F32 gMinObjectDistance;
69extern U32 gFrameCount;
70extern LLTimer gRenderStartTime;
71extern BOOL gAnimateTextures;
72
73void dialog_refresh_all();
74
75#define CULL_VIS
76//#define ORPHAN_SPAM
77//#define IGNORE_DEAD
78
79// Global lists of objects - should go away soon.
80LLViewerObjectList gObjectList;
81
82extern LLPipeline gPipeline;
83
84// Statics for object lookup tables.
85U32 LLViewerObjectList::sSimulatorMachineIndex = 1; // Not zero deliberately, to speed up index check.
86LLMap<U64, U32> LLViewerObjectList::sIPAndPortToIndex;
87std::map<U64, LLUUID> LLViewerObjectList::sIndexAndLocalIDToUUID;
88
89LLViewerObjectList::LLViewerObjectList()
90{
91 mNumVisCulled = 0;
92 mNumSizeCulled = 0;
93 mCurLazyUpdateIndex = 0;
94 mCurBin = 0;
95 mNumDeadObjects = 0;
96 mNumOrphans = 0;
97 mNumNewObjects = 0;
98 mWasPaused = FALSE;
99 mNumDeadObjectUpdates = 0;
100 mNumUnknownKills = 0;
101 mNumUnknownUpdates = 0;
102}
103
104LLViewerObjectList::~LLViewerObjectList()
105{
106 destroy();
107}
108
109void LLViewerObjectList::destroy()
110{
111 killAllObjects();
112
113 resetObjectBeacons();
114 mActiveObjects.clear();
115 mDeadObjects.clear();
116 mMapObjects.clear();
117 mUUIDObjectMap.clear();
118}
119
120
121void LLViewerObjectList::getUUIDFromLocal(LLUUID &id,
122 const U32 local_id,
123 const U32 ip,
124 const U32 port)
125{
126 U64 ipport = (((U64)ip) << 32) | (U64)port;
127
128 U32 index = sIPAndPortToIndex[ipport];
129
130 if (!index)
131 {
132 index = sSimulatorMachineIndex++;
133 sIPAndPortToIndex[ipport] = index;
134 }
135
136 U64 indexid = (((U64)index) << 32) | (U64)local_id;
137
138 id = get_if_there(sIndexAndLocalIDToUUID, indexid, LLUUID::null);
139}
140
141U64 LLViewerObjectList::getIndex(const U32 local_id,
142 const U32 ip,
143 const U32 port)
144{
145 U64 ipport = (((U64)ip) << 32) | (U64)port;
146
147 U32 index = sIPAndPortToIndex[ipport];
148
149 if (!index)
150 {
151 return 0;
152 }
153
154 return (((U64)index) << 32) | (U64)local_id;
155}
156
157BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object)
158{
159 U32 local_id = object.mLocalID;
160 LLHost region_host = object.getRegion()->getHost();
161 U32 ip = region_host.getAddress();
162 U32 port = region_host.getPort();
163 U64 ipport = (((U64)ip) << 32) | (U64)port;
164 U32 index = sIPAndPortToIndex[ipport];
165
166 U64 indexid = (((U64)index) << 32) | (U64)local_id;
167 return sIndexAndLocalIDToUUID.erase(indexid) > 0 ? TRUE : FALSE;
168}
169
170void LLViewerObjectList::setUUIDAndLocal(const LLUUID &id,
171 const U32 local_id,
172 const U32 ip,
173 const U32 port)
174{
175 U64 ipport = (((U64)ip) << 32) | (U64)port;
176
177 U32 index = sIPAndPortToIndex[ipport];
178
179 if (!index)
180 {
181 index = sSimulatorMachineIndex++;
182 sIPAndPortToIndex[ipport] = index;
183 }
184
185 U64 indexid = (((U64)index) << 32) | (U64)local_id;
186
187 sIndexAndLocalIDToUUID[indexid] = id;
188}
189
190S32 gFullObjectUpdates = 0;
191S32 gTerseObjectUpdates = 0;
192
193void LLViewerObjectList::processUpdateCore(LLViewerObject* objectp,
194 void** user_data,
195 U32 i,
196 const EObjectUpdateType update_type,
197 LLDataPacker* dpp,
198 BOOL just_created)
199{
200 LLMessageSystem* msg = gMessageSystem;
201
202 // ignore returned flags
203 objectp->processUpdateMessage(msg, user_data, i, update_type, dpp);
204
205 if (objectp->isDead())
206 {
207 // The update failed
208 return;
209 }
210 updateActive(objectp);
211
212 // Also sets the approx. pixel area
213 objectp->setPixelAreaAndAngle(gAgent);
214
215 // Update the image levels of textures for this object.
216 objectp->updateTextures(gAgent);
217
218 if (just_created)
219 {
220 gPipeline.addObject(objectp);
221 }
222
223 // RN: this must be called after we have a drawable
224 // (from gPipeline.addObject)
225 // so that the drawable parent is set properly
226 findOrphans(objectp, msg->getSenderIP(), msg->getSenderPort());
227
228 // If we're just wandering around, don't create new objects selected.
229 if (just_created
230 && update_type != OUT_TERSE_IMPROVED
231 && objectp->mCreateSelected)
232 {
233 if ( gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ) != gToolPie )
234 {
235 //llinfos << "DEBUG selecting " << objectp->mID << " "
236 // << objectp->mLocalID << llendl;
237 gSelectMgr->selectObjectAndFamily(objectp);
238 dialog_refresh_all();
239 }
240
241 objectp->mCreateSelected = false;
242 gViewerWindow->getWindow()->decBusyCount();
243 gViewerWindow->getWindow()->setCursor( UI_CURSOR_ARROW );
244 }
245}
246
247void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
248 void **user_data,
249 const EObjectUpdateType update_type,
250 bool cached, bool compressed)
251{
252 LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS);
253
254 LLVector3d camera_global = gAgent.getCameraPositionGlobal();
255 LLViewerObject *objectp;
256 S32 num_objects;
257 U32 local_id;
258 LLPCode pcode = 0;
259 LLUUID fullid;
260 S32 i;
261
262 // figure out which simulator these are from and get it's index
263 // Coordinates in simulators are region-local
264 // Until we get region-locality working on viewer we
265 // have to transform to absolute coordinates.
266 num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData);
267
268 if (!cached && !compressed && update_type != OUT_FULL)
269 {
270 gTerseObjectUpdates += num_objects;
271 S32 size;
272 if (mesgsys->getReceiveCompressedSize())
273 {
274 size = mesgsys->getReceiveCompressedSize();
275 }
276 else
277 {
278 size = mesgsys->getReceiveSize();
279 }
280// llinfos << "Received terse " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
281 }
282 else
283 {
284 S32 size;
285 if (mesgsys->getReceiveCompressedSize())
286 {
287 size = mesgsys->getReceiveCompressedSize();
288 }
289 else
290 {
291 size = mesgsys->getReceiveSize();
292 }
293
294// llinfos << "Received " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
295 gFullObjectUpdates += num_objects;
296 }
297
298 U64 region_handle;
299 mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
300 LLViewerRegion *regionp = gWorldPointer->getRegionFromHandle(region_handle);
301
302 if (!regionp)
303 {
304 llwarns << "Object update from unknown region!" << llendl;
305 return;
306 }
307
308 U8 compressed_dpbuffer[2048];
309 LLDataPackerBinaryBuffer compressed_dp(compressed_dpbuffer, 2048);
310 LLDataPacker *cached_dpp = NULL;
311
312 for (i = 0; i < num_objects; i++)
313 {
314 LLTimer update_timer;
315 BOOL justCreated = FALSE;
316
317 if (cached)
318 {
319 U32 id;
320 U32 crc;
321 mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, id, i);
322 mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i);
323
324 // Lookup data packer and add this id to cache miss lists if necessary.
325 cached_dpp = regionp->getDP(id, crc);
326 if (cached_dpp)
327 {
328 cached_dpp->reset();
329 cached_dpp->unpackUUID(fullid, "ID");
330 cached_dpp->unpackU32(local_id, "LocalID");
331 cached_dpp->unpackU8(pcode, "PCode");
332 }
333 else
334 {
335 continue; // no data packer, skip this object
336 }
337 }
338 else if (compressed)
339 {
340 U8 compbuffer[2048];
341 S32 uncompressed_length = 2048;
342 S32 compressed_length;
343
344 compressed_dp.reset();
345
346 U32 flags = 0;
347 if (update_type != OUT_TERSE_IMPROVED)
348 {
349 mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i);
350 }
351
352 if (flags & FLAGS_ZLIB_COMPRESSED)
353 {
354 compressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
355 mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compbuffer, 0, i);
356 uncompressed_length = 2048;
357 uncompress(compressed_dpbuffer, (unsigned long *)&uncompressed_length,
358 compbuffer, compressed_length);
359 compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length);
360 }
361 else
362 {
363 uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
364 mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i);
365 compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length);
366 }
367
368
369 if (update_type != OUT_TERSE_IMPROVED)
370 {
371 compressed_dp.unpackUUID(fullid, "ID");
372 compressed_dp.unpackU32(local_id, "LocalID");
373 compressed_dp.unpackU8(pcode, "PCode");
374 }
375 else
376 {
377 compressed_dp.unpackU32(local_id, "LocalID");
378 getUUIDFromLocal(fullid,
379 local_id,
380 gMessageSystem->getSenderIP(),
381 gMessageSystem->getSenderPort());
382 if (fullid.isNull())
383 {
384 //llwarns << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << llendl;
385 mNumUnknownUpdates++;
386 }
387 }
388 }
389 else if (update_type != OUT_FULL)
390 {
391 mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
392 getUUIDFromLocal(fullid,
393 local_id,
394 gMessageSystem->getSenderIP(),
395 gMessageSystem->getSenderPort());
396 if (fullid.isNull())
397 {
398 //llwarns << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << llendl;
399 mNumUnknownUpdates++;
400 }
401 }
402 else
403 {
404 mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FullID, fullid, i);
405 mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
406 // llinfos << "Full Update, obj " << local_id << ", global ID" << fullid << "from " << mesgsys->getSender() << llendl;
407 }
408 objectp = findObject(fullid);
409
410 // This looks like it will break if the local_id of the object doesn't change
411 // upon boundary crossing, but we check for region id matching later...
412 if (objectp && (objectp->mLocalID != local_id))
413 {
414 removeFromLocalIDTable(*objectp);
415 setUUIDAndLocal(fullid,
416 local_id,
417 gMessageSystem->getSenderIP(),
418 gMessageSystem->getSenderPort());
419 }
420
421 if (!objectp)
422 {
423 if (compressed)
424 {
425 if (update_type == OUT_TERSE_IMPROVED)
426 {
427 // llinfos << "terse update for an unknown object:" << fullid << llendl;
428 continue;
429 }
430 }
431 else if (cached)
432 {
433 }
434 else
435 {
436 if (update_type != OUT_FULL)
437 {
438// llinfos << "terse update for an unknown object:" << fullid << llendl;
439 continue;
440 }
441
442 mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_PCode, pcode, i);
443 }
444#ifdef IGNORE_DEAD
445 if (mDeadObjects.find(fullid) != mDeadObjects.end())
446 {
447 mNumDeadObjectUpdates++;
448 //llinfos << "update for a dead object:" << fullid << llendl;
449 continue;
450 }
451#endif
452
453 objectp = createObject(pcode, regionp, fullid, local_id, gMessageSystem->getSender());
454 if (!objectp)
455 {
456 continue;
457 }
458 justCreated = TRUE;
459 mNumNewObjects++;
460 }
461 else
462 {
463 if (objectp->getRegion() != regionp)
464 {
465 // Object has changed region! Update lookup tables, set region pointer.
466 removeFromLocalIDTable(*objectp);
467 setUUIDAndLocal(fullid,
468 local_id,
469 gMessageSystem->getSenderIP(),
470 gMessageSystem->getSenderPort());
471 objectp->setRegion(regionp);
472 }
473 objectp->updateRegion(regionp); // for LLVOAvatar
474 }
475
476
477 if (objectp->isDead())
478 {
479 llwarns << "Dead object " << objectp->mID << " in UUID map 1!" << llendl;
480 }
481
482 if (compressed)
483 {
484 if (update_type != OUT_TERSE_IMPROVED)
485 {
486 objectp->mLocalID = local_id;
487 }
488 processUpdateCore(objectp, user_data, i, update_type, &compressed_dp, justCreated);
489 if (update_type != OUT_TERSE_IMPROVED)
490 {
491 objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp);
492 }
493 }
494 else if (cached)
495 {
496 objectp->mLocalID = local_id;
497 processUpdateCore(objectp, user_data, i, update_type, cached_dpp, justCreated);
498 }
499 else
500 {
501 if (update_type == OUT_FULL)
502 {
503 objectp->mLocalID = local_id;
504 }
505 processUpdateCore(objectp, user_data, i, update_type, NULL, justCreated);
506 }
507 }
508
509 LLVOAvatar::cullAvatarsByPixelArea();
510}
511
512void LLViewerObjectList::processCompressedObjectUpdate(LLMessageSystem *mesgsys,
513 void **user_data,
514 const EObjectUpdateType update_type)
515{
516 processObjectUpdate(mesgsys, user_data, update_type, false, true);
517}
518
519void LLViewerObjectList::processCachedObjectUpdate(LLMessageSystem *mesgsys,
520 void **user_data,
521 const EObjectUpdateType update_type)
522{
523 processObjectUpdate(mesgsys, user_data, update_type, true, false);
524}
525
526void LLViewerObjectList::relightAllObjects()
527{
528 for (S32 i = 0; i < mObjects.count(); i++)
529 {
530 LLDrawable *drawable = mObjects[i]->mDrawable;
531 if (drawable)
532 {
533 gPipeline.markRelight(drawable);
534 }
535 }
536}
537
538void LLViewerObjectList::dirtyAllObjectInventory()
539{
540 S32 count = mObjects.count();
541 for(S32 i = 0; i < count; ++i)
542 {
543 mObjects[i]->dirtyInventory();
544 }
545}
546
547void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
548{
549 S32 i;
550 S32 num_objects = 0;
551 LLViewerObject *objectp;
552
553 S32 num_updates, max_value;
554 if (NUM_BINS - 1 == mCurBin)
555 {
556 num_updates = mObjects.count() - mCurLazyUpdateIndex;
557 max_value = mObjects.count();
558 gImageList.setUpdateStats(TRUE);
559 }
560 else
561 {
562 num_updates = (mObjects.count() / NUM_BINS) + 1;
563 max_value = llmin(mObjects.count(), mCurLazyUpdateIndex + num_updates);
564 }
565
566
567 if (!gNoRender)
568 {
569 // Slam priorities for textures that we care about (hovered, selected, and focused)
570 // Hovered
571 // Assumes only one level deep of parenting
572 objectp = gHoverView->getLastHoverObject();
573 if (objectp)
574 {
575 objectp->boostTexturePriority();
576 }
577 }
578
579 // Focused
580 objectp = gAgent.getFocusObject();
581 if (objectp)
582 {
583 objectp->boostTexturePriority();
584 }
585
586 // Selected
587 for (objectp = gSelectMgr->getFirstRootObject(); objectp; objectp = gSelectMgr->getNextRootObject())
588 {
589 objectp->boostTexturePriority();
590 }
591
592
593 // Iterate through some of the objects and lazy update their texture priorities
594 for (i = mCurLazyUpdateIndex; i < max_value; i++)
595 {
596 objectp = mObjects[i];
597 if (!objectp->isDead())
598 {
599 num_objects++;
600
601 // Update distance & gpw
602 objectp->setPixelAreaAndAngle(agent); // Also sets the approx. pixel area
603 objectp->updateTextures(agent); // Update the image levels of textures for this object.
604 }
605 }
606
607 mCurLazyUpdateIndex = max_value;
608 if (mCurLazyUpdateIndex == mObjects.count())
609 {
610 mCurLazyUpdateIndex = 0;
611 }
612
613 mCurBin = (++mCurBin) % NUM_BINS;
614
615 LLVOAvatar::cullAvatarsByPixelArea();
616}
617
618
619void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
620{
621 LLMemType mt(LLMemType::MTYPE_OBJECT);
622 // Update globals
623 gVelocityInterpolate = gSavedSettings.getBOOL("VelocityInterpolate");
624 gPingInterpolate = gSavedSettings.getBOOL("PingInterpolate");
625 gAnimateTextures = gSavedSettings.getBOOL("AnimateTextures");
626
627 // update global timer
628 F32 last_time = gFrameTimeSeconds;
629 U64 time = totalTime(); // this will become the new gFrameTime when the update is done
630 // Time _can_ go backwards, for example if the user changes the system clock.
631 // It doesn't cause any fatal problems (just some oddness with stats), so we shouldn't assert here.
632// llassert(time > gFrameTime);
633 F64 time_diff = U64_to_F64(time - gFrameTime)/(F64)SEC_TO_MICROSEC;
634 gFrameTime = time;
635 F64 time_since_start = U64_to_F64(gFrameTime - gStartTime)/(F64)SEC_TO_MICROSEC;
636 gFrameTimeSeconds = (F32)time_since_start;
637
638 gFrameIntervalSeconds = gFrameTimeSeconds - last_time;
639 if (gFrameIntervalSeconds < 0.f)
640 {
641 gFrameIntervalSeconds = 0.f;
642 }
643
644 //clear avatar LOD change counter
645 LLVOAvatar::sNumLODChangesThisFrame = 0;
646
647 const F64 frame_time = LLFrameTimer::getElapsedSeconds();
648
649 std::vector<LLViewerObject*> kill_list;
650 S32 num_active_objects = 0;
651
652 if (gSavedSettings.getBOOL("FreezeTime"))
653 {
654 for (std::set<LLPointer<LLViewerObject> >::iterator iter = mActiveObjects.begin();
655 iter != mActiveObjects.end(); iter++)
656 {
657 LLViewerObject *objectp = *iter;
658 if (objectp->getPCode() == LLViewerObject::LL_VO_CLOUDS ||
659 objectp->isAvatar())
660 {
661 objectp->idleUpdate(agent, world, frame_time);
662 }
663 }
664 }
665 else
666 {
667 for (std::set<LLPointer<LLViewerObject> >::iterator iter = mActiveObjects.begin();
668 iter != mActiveObjects.end(); iter++)
669 {
670 LLViewerObject *objectp = *iter;
671 if (!objectp->idleUpdate(agent, world, frame_time))
672 {
673 // If Idle Update returns false, kill object!
674 kill_list.push_back(objectp);
675 }
676 else
677 {
678 num_active_objects++;
679 }
680 }
681 for (std::vector<LLViewerObject*>::iterator iter = kill_list.begin();
682 iter != kill_list.end(); iter++)
683 {
684 LLViewerObject *objectp = *iter;
685 killObject(objectp);
686 }
687 }
688
689 mNumSizeCulled = 0;
690 mNumVisCulled = 0;
691
692 // compute all sorts of time-based stats
693 // don't factor frames that were paused into the stats
694 if (! mWasPaused)
695 {
696 gViewerStats->updateFrameStats(time_diff);
697 }
698
699 /*
700 // Debugging code for viewing orphans, and orphaned parents
701 LLUUID id;
702 char id_str[UUID_STR_LENGTH + 20];
703 for (i = 0; i < mOrphanParents.count(); i++)
704 {
705 id = sIndexAndLocalIDToUUID[mOrphanParents[i]];
706 LLViewerObject *objectp = findObject(id);
707 if (objectp)
708 {
709 sprintf(id_str, "Par: ");
710 objectp->mID.toString(id_str + 5);
711 addDebugBeacon(objectp->getPositionAgent(),
712 id_str,
713 LLColor4(1.f,0.f,0.f,1.f),
714 LLColor4(1.f,1.f,1.f,1.f));
715 }
716 }
717
718 LLColor4 text_color;
719 for (i = 0; i < mOrphanChildren.count(); i++)
720 {
721 OrphanInfo oi = mOrphanChildren[i];
722 LLViewerObject *objectp = findObject(oi.mChildInfo);
723 if (objectp)
724 {
725 if (objectp->getParent())
726 {
727 sprintf(id_str, "ChP: ");
728 text_color = LLColor4(0.f, 1.f, 0.f, 1.f);
729 }
730 else
731 {
732 sprintf(id_str, "ChNoP: ");
733 text_color = LLColor4(1.f, 0.f, 0.f, 1.f);
734 }
735 id = sIndexAndLocalIDToUUID[oi.mParentInfo];
736 objectp->mID.toString(id_str + 8);
737 addDebugBeacon(objectp->getPositionAgent() + LLVector3(0.f, 0.f, -0.25f),
738 id_str,
739 LLColor4(0.25f,0.25f,0.25f,1.f),
740 text_color);
741 }
742 i++;
743 }
744 */
745
746 mNumObjectsStat.addValue(mObjects.count());
747 mNumActiveObjectsStat.addValue(num_active_objects);
748 mNumSizeCulledStat.addValue(mNumSizeCulled);
749 mNumVisCulledStat.addValue(mNumVisCulled);
750}
751
752void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)
753{
754 LLMemType mt(LLMemType::MTYPE_OBJECT);
755 if (mDeadObjects.count(objectp->mID))
756 {
757 llinfos << "Object " << objectp->mID << " already on dead list, ignoring cleanup!" << llendl;
758 return;
759 }
760
761 mDeadObjects.insert(std::pair<LLUUID, LLPointer<LLViewerObject> >(objectp->mID, objectp));
762
763 // Cleanup any references we have to this object
764 // Remove from object map so noone can look it up.
765
766 mUUIDObjectMap.erase(objectp->mID);
767 removeFromLocalIDTable(*objectp);
768
769 if (objectp->onActiveList())
770 {
771 //llinfos << "Removing " << objectp->mID << " " << objectp->getPCodeString() << " from active list in cleanupReferences." << llendl;
772 objectp->setOnActiveList(FALSE);
773 mActiveObjects.erase(objectp);
774 }
775
776 if (objectp->isOnMap())
777 {
778 mMapObjects.removeObj(objectp);
779 }
780
781 // Don't clean up mObject references, these will be cleaned up more efficiently later!
782 // Also, not cleaned up
783 removeDrawable(objectp->mDrawable);
784
785 mNumDeadObjects++;
786}
787
788void LLViewerObjectList::removeDrawable(LLDrawable* drawablep)
789{
790 if (!drawablep)
791 {
792 return;
793 }
794
795 for (S32 i = 0; i < drawablep->getNumFaces(); i++)
796 {
797 LLViewerObject* objectp = drawablep->getFace(i)->getViewerObject();
798 mSelectPickList.erase(objectp);
799 }
800}
801
802BOOL LLViewerObjectList::killObject(LLViewerObject *objectp)
803{
804 // When we're killing objects, all we do is mark them as dead.
805 // We clean up the dead objects later.
806
807 if (objectp)
808 {
809 if (objectp->isDead())
810 {
811 // This object is already dead. Don't need to do more.
812 return TRUE;
813 }
814 else
815 {
816 objectp->markDead();
817 }
818
819 return TRUE;
820 }
821 return FALSE;
822}
823
824void LLViewerObjectList::killObjects(LLViewerRegion *regionp)
825{
826 LLViewerObject *objectp;
827
828 S32 i;
829 for (i = 0; i < mObjects.count(); i++)
830 {
831 objectp = mObjects[i];
832
833 if (objectp->mRegionp == regionp)
834 {
835 killObject(objectp);
836 }
837 }
838
839 // Have to clean right away because the region is becoming invalid.
840 cleanDeadObjects(FALSE);
841}
842
843void LLViewerObjectList::killAllObjects()
844{
845 // Used only on global destruction.
846 LLViewerObject *objectp;
847
848 for (S32 i = 0; i < mObjects.count(); i++)
849 {
850 objectp = mObjects[i];
851
852 killObject(objectp);
853 llassert(objectp->isDead());
854 }
855
856 cleanDeadObjects(FALSE);
857
858 if(!mObjects.empty())
859 {
860 llwarns << "LLViewerObjectList::killAllObjects still has entries in mObjects: " << mObjects.count() << llendl;
861 mObjects.clear();
862 }
863
864 if (!mActiveObjects.empty())
865 {
866 llwarns << "Some objects still on active object list!" << llendl;
867 mActiveObjects.clear();
868 }
869
870 if (!mMapObjects.empty())
871 {
872 llwarns << "Some objects still on map object list!" << llendl;
873 mActiveObjects.clear();
874 }
875}
876
877void LLViewerObjectList::cleanDeadObjects(BOOL use_timer)
878{
879 if (!mNumDeadObjects)
880 {
881 // No dead objects, don't need to scan object list.
882 return;
883 }
884
885 S32 i = 0;
886 S32 num_removed = 0;
887 LLViewerObject *objectp;
888 while (i < mObjects.count())
889 {
890 // Scan for all of the dead objects and remove any "global" references to them.
891 objectp = mObjects[i];
892 if (objectp->isDead())
893 {
894 mObjects.remove(i);
895 num_removed++;
896
897 if (num_removed == mNumDeadObjects)
898 {
899 // We've cleaned up all of the dead objects.
900 break;
901 }
902 }
903 else
904 {
905 // iterate, this isn't a dead object.
906 i++;
907 }
908 }
909
910 // We've cleaned the global object list, now let's do some paranoia testing on objects
911 // before blowing away the dead list.
912 mDeadObjects.clear();
913 mNumDeadObjects = 0;
914}
915
916void LLViewerObjectList::updateActive(LLViewerObject *objectp)
917{
918 LLMemType mt(LLMemType::MTYPE_OBJECT);
919 if (objectp->isDead())
920 {
921 return; // We don't update dead objects!
922 }
923
924 BOOL active = objectp->isActive();
925 if (active != objectp->onActiveList())
926 {
927 if (active)
928 {
929 //llinfos << "Adding " << objectp->mID << " " << objectp->getPCodeString() << " to active list." << llendl;
930 mActiveObjects.insert(objectp);
931 objectp->setOnActiveList(TRUE);
932 }
933 else
934 {
935 //llinfos << "Removing " << objectp->mID << " " << objectp->getPCodeString() << " from active list." << llendl;
936 mActiveObjects.erase(objectp);
937 objectp->setOnActiveList(FALSE);
938 }
939 }
940}
941
942
943
944void LLViewerObjectList::shiftObjects(const LLVector3 &offset)
945{
946 // This is called when we shift our origin when we cross region boundaries...
947 // We need to update many object caches, I'll document this more as I dig through the code
948 // cleaning things out...
949
950 if (gNoRender || 0 == offset.magVecSquared())
951 {
952 return;
953 }
954
955 LLViewerObject *objectp;
956 S32 i;
957 for (i = 0; i < mObjects.count(); i++)
958 {
959 objectp = getObject(i);
960 // There could be dead objects on the object list, so don't update stuff if the object is dead.
961 if (objectp)
962 {
963 objectp->updatePositionCaches();
964
965 if (objectp->mDrawable.notNull() && !objectp->mDrawable->isDead())
966 {
967 gPipeline.markShift(objectp->mDrawable);
968 }
969 }
970 }
971
972 gPipeline.shiftObjects(offset);
973 gWorldPointer->mPartSim.shift(offset);
974}
975
976void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap)
977{
978 LLColor4 above_water_color = gColors.getColor( "NetMapOtherOwnAboveWater" );
979 LLColor4 below_water_color = gColors.getColor( "NetMapOtherOwnBelowWater" );
980 LLColor4 you_own_above_water_color =
981 gColors.getColor( "NetMapYouOwnAboveWater" );
982 LLColor4 you_own_below_water_color =
983 gColors.getColor( "NetMapYouOwnBelowWater" );
984 LLColor4 group_own_above_water_color =
985 gColors.getColor( "NetMapGroupOwnAboveWater" );
986 LLColor4 group_own_below_water_color =
987 gColors.getColor( "NetMapGroupOwnBelowWater" );
988
989
990 for (S32 i = 0; i < mMapObjects.count(); i++)
991 {
992 LLViewerObject* objectp = mMapObjects[i];
993 if (objectp->isOrphaned() || objectp->isAttachment())
994 {
995 continue;
996 }
997 const LLVector3& scale = objectp->getScale();
998 const LLVector3d pos = objectp->getPositionGlobal();
999 const F64 water_height = F64( objectp->getRegion()->getWaterHeight() );
1000 // gWorldPointer->getWaterHeight();
1001
1002 F32 approx_radius = (scale.mV[VX] + scale.mV[VY]) * 0.5f * 0.5f * 1.3f; // 1.3 is a fudge
1003
1004 LLColor4U color = above_water_color;
1005 if( objectp->permYouOwner() )
1006 {
1007 const F32 MIN_RADIUS_FOR_OWNED_OBJECTS = 2.f;
1008 if( approx_radius < MIN_RADIUS_FOR_OWNED_OBJECTS )
1009 {
1010 approx_radius = MIN_RADIUS_FOR_OWNED_OBJECTS;
1011 }
1012
1013 if( pos.mdV[VZ] >= water_height )
1014 {
1015 if ( objectp->permGroupOwner() )
1016 {
1017 color = group_own_above_water_color;
1018 }
1019 else
1020 {
1021 color = you_own_above_water_color;
1022 }
1023 }
1024 else
1025 {
1026 if ( objectp->permGroupOwner() )
1027 {
1028 color = group_own_below_water_color;
1029 }
1030 else
1031 {
1032 color = you_own_below_water_color;
1033 }
1034 }
1035 }
1036 else
1037 if( pos.mdV[VZ] < water_height )
1038 {
1039 color = below_water_color;
1040 }
1041
1042 netmap.renderScaledPointGlobal(
1043 pos,
1044 color,
1045 approx_radius );
1046 }
1047}
1048
1049void LLViewerObjectList::renderObjectBounds(const LLVector3 &center)
1050{
1051}
1052
1053
1054U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parcel_wall, BOOL keep_pick_list)
1055{
1056 gRenderForSelect = TRUE;
1057
1058 // LLTimer pick_timer;
1059 if (!keep_pick_list)
1060 {
1061 LLViewerObject *objectp;
1062 S32 i;
1063 // Reset all of the GL names to zero.
1064 for (i = 0; i < mObjects.count(); i++)
1065 {
1066 objectp = mObjects[i];
1067 objectp->mGLName = 0;
1068 }
1069
1070 mSelectPickList.clear();
1071
1072 std::vector<LLDrawable*> pick_drawables;
1073 gPipeline.mObjectPartition->cull(camera, &pick_drawables, TRUE);
1074
1075 for (std::vector<LLDrawable*>::iterator iter = pick_drawables.begin();
1076 iter != pick_drawables.end(); iter++)
1077 {
1078 LLDrawable* drawablep = *iter;
1079
1080 LLViewerObject* last_objectp = NULL;
1081 for (S32 face_num = 0; face_num < drawablep->getNumFaces(); face_num++)
1082 {
1083 LLViewerObject* objectp = drawablep->getFace(face_num)->getViewerObject();
1084
1085 if (objectp && objectp != last_objectp)
1086 {
1087 mSelectPickList.insert(objectp);
1088 last_objectp = objectp;
1089 }
1090 }
1091 }
1092
1093 LLHUDText::addPickable(mSelectPickList);
1094
1095 for (objectp = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
1096 objectp;
1097 objectp = (LLVOAvatar*)LLCharacter::sInstances.getNextData())
1098 {
1099 if (!objectp->isDead())
1100 {
1101 if (objectp->mDrawable.notNull() && objectp->mDrawable->isVisible())
1102 {
1103 mSelectPickList.insert(objectp);
1104 }
1105 }
1106 }
1107
1108 // add all hud objects to pick list
1109 LLVOAvatar* avatarp = gAgent.getAvatarObject();
1110 if (avatarp)
1111 {
1112 LLViewerJointAttachment* attachmentp;
1113 for (attachmentp = avatarp->mAttachmentPoints.getFirstData();
1114 attachmentp;
1115 attachmentp = avatarp->mAttachmentPoints.getNextData())
1116 {
1117 if (attachmentp->getIsHUDAttachment())
1118 {
1119 LLViewerObject* objectp = attachmentp->getObject(0);
1120 if (objectp)
1121 {
1122 mSelectPickList.insert(objectp);
1123 for (U32 i = 0; i < objectp->mChildList.size(); i++)
1124 {
1125 LLViewerObject* childp = objectp->mChildList[i];
1126 if (childp)
1127 {
1128 mSelectPickList.insert(childp);
1129 }
1130 }
1131 }
1132 }
1133 }
1134 }
1135
1136 S32 num_pickables = (S32)mSelectPickList.size() + LLHUDIcon::getNumInstances();
1137
1138 S32 step = (0x000fffff - GL_NAME_INDEX_OFFSET) / num_pickables;
1139
1140 std::set<LLViewerObject*>::iterator pick_it;
1141 i = 0;
1142 for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end();)
1143 {
1144 LLViewerObject* objp = (*pick_it);
1145 if (!objp || objp->isDead() || !objp->mbCanSelect)
1146 {
1147 mSelectPickList.erase(pick_it++);
1148 continue;
1149 }
1150
1151 objp->mGLName = (i * step) + GL_NAME_INDEX_OFFSET;
1152 i++;
1153 ++pick_it;
1154 }
1155
1156 LLHUDIcon::generatePickIDs(i * step, step);
1157 }
1158
1159 // At this point, we should only have live drawables/viewer objects
1160 gPipeline.renderForSelect();
1161 //
1162 // Render pass for selected objects
1163 //
1164 gViewerWindow->renderSelections( TRUE, pick_parcel_wall, FALSE );
1165
1166 // render pickable ui elements, like names, etc.
1167 LLHUDObject::renderAllForSelect();
1168
1169 gRenderForSelect = FALSE;
1170
1171 //llinfos << "Rendered " << count << " for select" << llendl;
1172 //llinfos << "Took " << pick_timer.getElapsedTimeF32()*1000.f << "ms to pick" << llendl;
1173 return 0;
1174}
1175
1176LLViewerObject *LLViewerObjectList::getSelectedObject(const U32 object_id)
1177{
1178 std::set<LLViewerObject*>::iterator pick_it;
1179 for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end(); ++pick_it)
1180 {
1181 if ((*pick_it)->mGLName == object_id)
1182 {
1183 return (*pick_it);
1184 }
1185 }
1186 return NULL;
1187}
1188
1189void LLViewerObjectList::addDebugBeacon(const LLVector3 &pos_agent,
1190 const LLString &string,
1191 const LLColor4 &color,
1192 const LLColor4 &text_color,
1193 S32 line_width)
1194{
1195 LLDebugBeacon *beaconp = mDebugBeacons.reserve_block(1);
1196 beaconp->mPositionAgent = pos_agent;
1197 beaconp->mString = string;
1198 beaconp->mColor = color;
1199 beaconp->mTextColor = text_color;
1200 beaconp->mLineWidth = line_width;
1201}
1202
1203void LLViewerObjectList::resetObjectBeacons()
1204{
1205 mDebugBeacons.reset();
1206}
1207
1208LLViewerObject *LLViewerObjectList::createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp)
1209{
1210 LLMemType mt(LLMemType::MTYPE_OBJECT);
1211 LLUUID fullid;
1212 fullid.generate();
1213
1214 LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);
1215 if (!objectp)
1216 {
1217 llwarns << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << llendl;
1218 return NULL;
1219 }
1220
1221 mUUIDObjectMap[fullid] = objectp;
1222
1223 mObjects.put(objectp);
1224
1225 updateActive(objectp);
1226
1227 return objectp;
1228}
1229
1230
1231
1232LLViewerObject *LLViewerObjectList::createObject(const LLPCode pcode, LLViewerRegion *regionp,
1233 const LLUUID &uuid, const U32 local_id, const LLHost &sender)
1234{
1235 LLMemType mt(LLMemType::MTYPE_OBJECT);
1236 LLFastTimer t(LLFastTimer::FTM_CREATE_OBJECT);
1237
1238 LLUUID fullid;
1239 if (uuid == LLUUID::null)
1240 {
1241 fullid.generate();
1242 }
1243 else
1244 {
1245 fullid = uuid;
1246 }
1247
1248 LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);
1249 if (!objectp)
1250 {
1251 llwarns << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << " id:" << fullid << llendl;
1252 return NULL;
1253 }
1254
1255 mUUIDObjectMap[fullid] = objectp;
1256 setUUIDAndLocal(fullid,
1257 local_id,
1258 gMessageSystem->getSenderIP(),
1259 gMessageSystem->getSenderPort());
1260
1261 mObjects.put(objectp);
1262
1263 updateActive(objectp);
1264
1265 return objectp;
1266}
1267
1268LLViewerObject *LLViewerObjectList::replaceObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
1269{
1270 LLViewerObject *old_instance = findObject(id);
1271 if (old_instance)
1272 {
1273 cleanupReferences(old_instance);
1274 old_instance->markDead();
1275
1276 return createObject(pcode, regionp, id, old_instance->getLocalID(), LLHost());
1277 }
1278 return NULL;
1279}
1280
1281S32 LLViewerObjectList::findReferences(LLDrawable *drawablep) const
1282{
1283 LLViewerObject *objectp;
1284 S32 i;
1285 S32 num_refs = 0;
1286 for (i = 0; i < mObjects.count(); i++)
1287 {
1288 objectp = mObjects[i];
1289 if (objectp->mDrawable.notNull())
1290 {
1291 num_refs += objectp->mDrawable->findReferences(drawablep);
1292 }
1293 }
1294 return num_refs;
1295}
1296
1297
1298void LLViewerObjectList::orphanize(LLViewerObject *childp, U32 parent_id, U32 ip, U32 port)
1299{
1300 LLMemType mt(LLMemType::MTYPE_OBJECT);
1301#ifdef ORPHAN_SPAM
1302 llinfos << "Orphaning object " << childp->getID() << " with parent " << parent_id << llendl;
1303#endif
1304
1305 // We're an orphan, flag things appropriately.
1306 childp->mOrphaned = TRUE;
1307 if (childp->mDrawable.notNull())
1308 {
1309 bool make_invisible = true;
1310 LLViewerObject *parentp = (LLViewerObject *)childp->getParent();
1311 if (parentp)
1312 {
1313 if (parentp->getRegion() != childp->getRegion())
1314 {
1315 // This is probably an object flying across a region boundary, the
1316 // object probably ISN'T being reparented, but just got an object
1317 // update out of order (child update before parent).
1318 make_invisible = false;
1319 //llinfos << "Don't make object handoffs invisible!" << llendl;
1320 }
1321 }
1322
1323 if (make_invisible)
1324 {
1325 // Make sure that this object becomes invisible if it's an orphan
1326 childp->mDrawable->setState(LLDrawable::FORCE_INVISIBLE);
1327 }
1328 }
1329
1330 // Unknown parent, add to orpaned child list
1331 U64 parent_info = getIndex(parent_id, ip, port);
1332
1333 if (-1 == mOrphanParents.find(parent_info))
1334 {
1335 mOrphanParents.put(parent_info);
1336 }
1337
1338 LLViewerObjectList::OrphanInfo oi(parent_info, childp->mID);
1339 if (-1 == mOrphanChildren.find(oi))
1340 {
1341 mOrphanChildren.put(oi);
1342 mNumOrphans++;
1343 }
1344}
1345
1346
1347void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port)
1348{
1349 if (gNoRender)
1350 {
1351 return;
1352 }
1353
1354 if (objectp->isDead())
1355 {
1356 llwarns << "Trying to find orphans for dead obj " << objectp->mID
1357 << ":" << objectp->getPCodeString() << llendl;
1358 return;
1359 }
1360
1361 // See if we are a parent of an orphan.
1362 // Note: This code is fairly inefficient but it should happen very rarely.
1363 // It can be sped up if this is somehow a performance issue...
1364 if (0 == mOrphanParents.count())
1365 {
1366 // no known orphan parents
1367 return;
1368 }
1369 if (-1 == mOrphanParents.find(getIndex(objectp->mLocalID, ip, port)))
1370 {
1371 // did not find objectp in OrphanParent list
1372 return;
1373 }
1374
1375 S32 i;
1376 U64 parent_info = getIndex(objectp->mLocalID, ip, port);
1377 BOOL orphans_found = FALSE;
1378 // Iterate through the orphan list, and set parents of matching children.
1379 for (i = 0; i < mOrphanChildren.count(); i++)
1380 {
1381 if (mOrphanChildren[i].mParentInfo != parent_info)
1382 {
1383 continue;
1384 }
1385 LLViewerObject *childp = findObject(mOrphanChildren[i].mChildInfo);
1386 if (childp)
1387 {
1388 if (childp == objectp)
1389 {
1390 llwarns << objectp->mID << " has self as parent, skipping!"
1391 << llendl;
1392 continue;
1393 }
1394
1395#ifdef ORPHAN_SPAM
1396 llinfos << "Reunited parent " << objectp->mID
1397 << " with child " << childp->mID << llendl;
1398 llinfos << "Glob: " << objectp->getPositionGlobal() << llendl;
1399 llinfos << "Agent: " << objectp->getPositionAgent() << llendl;
1400 addDebugBeacon(objectp->getPositionAgent(),"");
1401#endif
1402 gPipeline.markMoved(objectp->mDrawable);
1403 objectp->setChanged(LLXform::MOVED | LLXform::SILHOUETTE);
1404
1405 // Flag the object as no longer orphaned
1406 childp->mOrphaned = FALSE;
1407 if (childp->mDrawable.notNull())
1408 {
1409 // Make the drawable visible again and set the drawable parent
1410 childp->mDrawable->setState(LLDrawable::CLEAR_INVISIBLE);
1411 childp->setDrawableParent(objectp->mDrawable); // LLViewerObjectList::findOrphans()
1412 }
1413 objectp->addChild(childp);
1414 orphans_found = TRUE;
1415 }
1416 else
1417 {
1418 llinfos << "Missing orphan child, removing from list" << llendl;
1419 mOrphanChildren.remove(i);
1420 i--;
1421 }
1422 }
1423
1424 // Remove orphan parent and children from lists now that they've been found
1425 mOrphanParents.remove(mOrphanParents.find(parent_info));
1426
1427 i = 0;
1428 while (i < mOrphanChildren.count())
1429 {
1430 if (mOrphanChildren[i].mParentInfo == parent_info)
1431 {
1432 mOrphanChildren.remove(i);
1433 mNumOrphans--;
1434 }
1435 else
1436 {
1437 i++;
1438 }
1439 }
1440
1441 if (orphans_found && objectp->isSelected())
1442 {
1443 LLSelectNode* nodep = gSelectMgr->findSelectNode(objectp);
1444 if (nodep && !nodep->mIndividualSelection)
1445 {
1446 // rebuild selection with orphans
1447 gSelectMgr->deselectObjectAndFamily(objectp);
1448 gSelectMgr->selectObjectAndFamily(objectp);
1449 }
1450 }
1451}
1452
1453
1454LLViewerObjectList::OrphanInfo::OrphanInfo()
1455{
1456}
1457
1458LLViewerObjectList::OrphanInfo::OrphanInfo(const U64 parent_info, const LLUUID child_info)
1459 : mParentInfo(parent_info), mChildInfo(child_info)
1460{
1461}
1462
1463bool LLViewerObjectList::OrphanInfo::operator==(const OrphanInfo &rhs) const
1464{
1465 return (mParentInfo == rhs.mParentInfo) && (mChildInfo == rhs.mChildInfo);
1466}
1467
1468bool LLViewerObjectList::OrphanInfo::operator!=(const OrphanInfo &rhs) const
1469{
1470 return !operator==(rhs);
1471}
1472