summaryrefslogtreecommitdiff
path: root/indra/newview/llviewerobjectlist.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llviewerobjectlist.cpp
Print done when done.
Diffstat (limited to 'indra/newview/llviewerobjectlist.cpp')
-rw-r--r--indra/newview/llviewerobjectlist.cpp1484
1 files changed, 1484 insertions, 0 deletions
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
new file mode 100644
index 0000000000..e1fed12563
--- /dev/null
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -0,0 +1,1484 @@
+/**
+ * @file llviewerobjectlist.cpp
+ * @brief Implementation of LLViewerObjectList class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerobjectlist.h"
+
+#include "gpw.h"
+#include "message.h"
+#include "timing.h"
+#include "llfasttimer.h"
+
+#include "llviewercontrol.h"
+#include "llface.h"
+#include "llvoavatar.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llnetmap.h"
+#include "llagent.h"
+#include "pipeline.h"
+#include "llhoverview.h"
+#include "llworld.h"
+#include "llstring.h"
+#include "llhudtext.h"
+#include "lldrawable.h"
+#include "xform.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llselectmgr.h"
+#include "llresmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "llkeyboard.h"
+#include "u64.h"
+#include "llviewerimagelist.h"
+#include "lldatapacker.h"
+#include <zlib/zlib.h>
+#include "object_flags.h"
+#include "llviewermedialist.h"
+
+extern BOOL gVelocityInterpolate;
+extern BOOL gPingInterpolate;
+extern F32 gMinObjectDistance;
+extern U32 gFrameCount;
+extern LLTimer gRenderStartTime;
+extern BOOL gAnimateTextures;
+
+void dialog_refresh_all();
+
+#define CULL_VIS
+//#define ORPHAN_SPAM
+//#define IGNORE_DEAD
+
+// Global lists of objects - should go away soon.
+LLViewerObjectList gObjectList;
+
+extern LLPipeline gPipeline;
+
+// Statics for object lookup tables.
+U32 LLViewerObjectList::sSimulatorMachineIndex = 1; // Not zero deliberately, to speed up index check.
+LLMap<U64, U32> LLViewerObjectList::sIPAndPortToIndex;
+std::map<U64, LLUUID> LLViewerObjectList::sIndexAndLocalIDToUUID;
+
+LLViewerObjectList::LLViewerObjectList()
+{
+ mNumVisCulled = 0;
+ mNumSizeCulled = 0;
+ mCurLazyUpdateIndex = 0;
+ mCurBin = 0;
+ mNumDeadObjects = 0;
+ mNumOrphans = 0;
+ mNumNewObjects = 0;
+ mWasPaused = FALSE;
+ mNumDeadObjectUpdates = 0;
+ mNumUnknownKills = 0;
+ mNumUnknownUpdates = 0;
+}
+
+LLViewerObjectList::~LLViewerObjectList()
+{
+ destroy();
+}
+
+void LLViewerObjectList::destroy()
+{
+ killAllObjects();
+
+ resetObjectBeacons();
+ mActiveObjects.clear();
+ mDeadObjects.clear();
+ mMapObjects.clear();
+ mUUIDObjectMap.clear();
+}
+
+
+void LLViewerObjectList::getUUIDFromLocal(LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port)
+{
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+
+ U32 index = sIPAndPortToIndex[ipport];
+
+ if (!index)
+ {
+ index = sSimulatorMachineIndex++;
+ sIPAndPortToIndex[ipport] = index;
+ }
+
+ U64 indexid = (((U64)index) << 32) | (U64)local_id;
+
+ id = get_if_there(sIndexAndLocalIDToUUID, indexid, LLUUID::null);
+}
+
+U64 LLViewerObjectList::getIndex(const U32 local_id,
+ const U32 ip,
+ const U32 port)
+{
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+
+ U32 index = sIPAndPortToIndex[ipport];
+
+ if (!index)
+ {
+ return 0;
+ }
+
+ return (((U64)index) << 32) | (U64)local_id;
+}
+
+BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object)
+{
+ U32 local_id = object.mLocalID;
+ LLHost region_host = object.getRegion()->getHost();
+ U32 ip = region_host.getAddress();
+ U32 port = region_host.getPort();
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+ U32 index = sIPAndPortToIndex[ipport];
+
+ U64 indexid = (((U64)index) << 32) | (U64)local_id;
+ return sIndexAndLocalIDToUUID.erase(indexid) > 0 ? TRUE : FALSE;
+}
+
+void LLViewerObjectList::setUUIDAndLocal(const LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port)
+{
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+
+ U32 index = sIPAndPortToIndex[ipport];
+
+ if (!index)
+ {
+ index = sSimulatorMachineIndex++;
+ sIPAndPortToIndex[ipport] = index;
+ }
+
+ U64 indexid = (((U64)index) << 32) | (U64)local_id;
+
+ sIndexAndLocalIDToUUID[indexid] = id;
+}
+
+S32 gFullObjectUpdates = 0;
+S32 gTerseObjectUpdates = 0;
+
+void LLViewerObjectList::processUpdateCore(LLViewerObject* objectp,
+ void** user_data,
+ U32 i,
+ const EObjectUpdateType update_type,
+ LLDataPacker* dpp,
+ BOOL justCreated)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ U32 pum_flags = objectp->processUpdateMessage(msg, user_data, i, update_type, dpp);
+ if (objectp->isDead())
+ {
+ // The update failed
+ return;
+ }
+ updateActive(objectp);
+
+ // Also sets the approx. pixel area
+ objectp->setPixelAreaAndAngle(gAgent);
+
+ // Update the image levels of textures for this object.
+ objectp->updateTextures(gAgent);
+
+ if (justCreated)
+ {
+ gPipeline.addObject(objectp);
+ }
+
+ // RN: this must be called after we have a drawable
+ // (from gPipeline.addObject)
+ // so that the drawable parent is set properly
+ findOrphans(objectp, msg->getSenderIP(), msg->getSenderPort());
+
+ // If we're just wandering around, don't create new objects selected.
+ if (justCreated
+ && update_type != OUT_TERSE_IMPROVED
+ && objectp->mCreateSelected)
+ {
+ if ( gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ) != gToolPie )
+ {
+ //llinfos << "DEBUG selecting " << objectp->mID << " "
+ // << objectp->mLocalID << llendl;
+ gSelectMgr->selectObjectAndFamily(objectp);
+ dialog_refresh_all();
+ }
+
+ objectp->mCreateSelected = false;
+ gViewerWindow->getWindow()->decBusyCount();
+ gViewerWindow->getWindow()->setCursor( UI_CURSOR_ARROW );
+ }
+
+ if (gMediaList)
+ {
+ // we're using web pages on prims
+ if (pum_flags & LLViewerObject::MEDIA_URL_ADDED)
+ {
+ //llwarns << "WEBONPRIM media url added " << objectp->getMediaURL() << llendl;
+ gMediaList->addedMediaURL(objectp);
+ }
+
+ if (pum_flags & LLViewerObject::MEDIA_URL_UPDATED)
+ {
+ //llwarns << "WEBONPRIM media url updated " << objectp->getMediaURL() << llendl;
+ gMediaList->updatedMediaURL(objectp);
+ }
+
+ if (pum_flags & LLViewerObject::MEDIA_URL_REMOVED)
+ {
+ //llwarns << "WEBONPRIM media url removed " << objectp->getMediaURL() << llendl;
+ gMediaList->removedMediaURL(objectp);
+ }
+
+ // Make sure we get moved in or out of LLDrawPoolMedia, as needed
+ if (pum_flags & (LLViewerObject::MEDIA_URL_ADDED | LLViewerObject::MEDIA_URL_REMOVED | LLViewerObject::MEDIA_URL_UPDATED))
+ {
+ if (objectp->mDrawable.notNull())
+ {
+ gPipeline.markTextured(objectp->mDrawable);
+ }
+ }
+ }
+}
+
+void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
+ void **user_data,
+ const EObjectUpdateType update_type,
+ bool cached, bool compressed)
+{
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS);
+
+ LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+ LLViewerObject *objectp;
+ S32 num_objects;
+ U32 local_id;
+ LLPCode pcode = 0;
+ LLUUID fullid;
+ S32 i;
+
+ // figure out which simulator these are from and get it's index
+ // Coordinates in simulators are region-local
+ // Until we get region-locality working on viewer we
+ // have to transform to absolute coordinates.
+ num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData);
+
+ if (!cached && !compressed && update_type != OUT_FULL)
+ {
+ gTerseObjectUpdates += num_objects;
+ S32 size;
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ size = mesgsys->getReceiveCompressedSize();
+ }
+ else
+ {
+ size = mesgsys->getReceiveSize();
+ }
+// llinfos << "Received terse " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
+ }
+ else
+ {
+ S32 size;
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ size = mesgsys->getReceiveCompressedSize();
+ }
+ else
+ {
+ size = mesgsys->getReceiveSize();
+ }
+
+// llinfos << "Received " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
+ gFullObjectUpdates += num_objects;
+ }
+
+ U64 region_handle;
+ mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
+ LLViewerRegion *regionp = gWorldPointer->getRegionFromHandle(region_handle);
+
+ if (!regionp)
+ {
+ llwarns << "Object update from unknown region!" << llendl;
+ return;
+ }
+
+ U8 compressed_dpbuffer[2048];
+ LLDataPackerBinaryBuffer compressed_dp(compressed_dpbuffer, 2048);
+ LLDataPacker *cached_dpp = NULL;
+
+ for (i = 0; i < num_objects; i++)
+ {
+ LLTimer update_timer;
+ BOOL justCreated = FALSE;
+
+ if (cached)
+ {
+ U32 id;
+ U32 crc;
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, id, i);
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i);
+
+ // Lookup data packer and add this id to cache miss lists if necessary.
+ cached_dpp = regionp->getDP(id, crc);
+ if (cached_dpp)
+ {
+ cached_dpp->reset();
+ cached_dpp->unpackUUID(fullid, "ID");
+ cached_dpp->unpackU32(local_id, "LocalID");
+ cached_dpp->unpackU8(pcode, "PCode");
+ }
+ else
+ {
+ continue; // no data packer, skip this object
+ }
+ }
+ else if (compressed)
+ {
+ U8 compbuffer[2048];
+ S32 uncompressed_length = 2048;
+ S32 compressed_length;
+
+ compressed_dp.reset();
+
+ U32 flags = 0;
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i);
+ }
+
+ if (flags & FLAGS_ZLIB_COMPRESSED)
+ {
+ compressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compbuffer, 0, i);
+ uncompressed_length = 2048;
+ uncompress(compressed_dpbuffer, (unsigned long *)&uncompressed_length,
+ compbuffer, compressed_length);
+ compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length);
+ }
+ else
+ {
+ uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i);
+ compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length);
+ }
+
+
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ compressed_dp.unpackUUID(fullid, "ID");
+ compressed_dp.unpackU32(local_id, "LocalID");
+ compressed_dp.unpackU8(pcode, "PCode");
+ }
+ else
+ {
+ compressed_dp.unpackU32(local_id, "LocalID");
+ getUUIDFromLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ if (fullid.isNull())
+ {
+ //llwarns << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << llendl;
+ mNumUnknownUpdates++;
+ }
+ }
+ }
+ else if (update_type != OUT_FULL)
+ {
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
+ getUUIDFromLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ if (fullid.isNull())
+ {
+ //llwarns << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << llendl;
+ mNumUnknownUpdates++;
+ }
+ }
+ else
+ {
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FullID, fullid, i);
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
+ // llinfos << "Full Update, obj " << local_id << ", global ID" << fullid << "from " << mesgsys->getSender() << llendl;
+ }
+ objectp = findObject(fullid);
+
+ // This looks like it will break if the local_id of the object doesn't change
+ // upon boundary crossing, but we check for region id matching later...
+ if (objectp && (objectp->mLocalID != local_id))
+ {
+ removeFromLocalIDTable(*objectp);
+ setUUIDAndLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ }
+
+ if (!objectp)
+ {
+ if (compressed)
+ {
+ if (update_type == OUT_TERSE_IMPROVED)
+ {
+ // llinfos << "terse update for an unknown object:" << fullid << llendl;
+ continue;
+ }
+ }
+ else if (cached)
+ {
+ }
+ else
+ {
+ if (update_type != OUT_FULL)
+ {
+// llinfos << "terse update for an unknown object:" << fullid << llendl;
+ continue;
+ }
+
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_PCode, pcode, i);
+ }
+#ifdef IGNORE_DEAD
+ if (mDeadObjects.find(fullid) != mDeadObjects.end())
+ {
+ mNumDeadObjectUpdates++;
+ //llinfos << "update for a dead object:" << fullid << llendl;
+ continue;
+ }
+#endif
+
+ objectp = createObject(pcode, regionp, fullid, local_id, gMessageSystem->getSender());
+ if (!objectp)
+ {
+ continue;
+ }
+ justCreated = TRUE;
+ mNumNewObjects++;
+ }
+ else
+ {
+ if (objectp->getRegion() != regionp)
+ {
+ // Object has changed region! Update lookup tables, set region pointer.
+ removeFromLocalIDTable(*objectp);
+ setUUIDAndLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ objectp->setRegion(regionp);
+ }
+ objectp->updateRegion(regionp); // for LLVOAvatar
+ }
+
+
+ if (objectp->isDead())
+ {
+ llwarns << "Dead object " << objectp->mID << " in UUID map 1!" << llendl;
+ }
+
+ if (compressed)
+ {
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ objectp->mLocalID = local_id;
+ }
+ processUpdateCore(objectp, user_data, i, update_type, &compressed_dp, justCreated);
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp);
+ }
+ }
+ else if (cached)
+ {
+ objectp->mLocalID = local_id;
+ processUpdateCore(objectp, user_data, i, update_type, cached_dpp, justCreated);
+ }
+ else
+ {
+ if (update_type == OUT_FULL)
+ {
+ objectp->mLocalID = local_id;
+ }
+ processUpdateCore(objectp, user_data, i, update_type, NULL, justCreated);
+ }
+ }
+
+ LLVOAvatar::cullAvatarsByPixelArea();
+}
+
+void LLViewerObjectList::processCompressedObjectUpdate(LLMessageSystem *mesgsys,
+ void **user_data,
+ const EObjectUpdateType update_type)
+{
+ processObjectUpdate(mesgsys, user_data, update_type, false, true);
+}
+
+void LLViewerObjectList::processCachedObjectUpdate(LLMessageSystem *mesgsys,
+ void **user_data,
+ const EObjectUpdateType update_type)
+{
+ processObjectUpdate(mesgsys, user_data, update_type, true, false);
+}
+
+void LLViewerObjectList::relightAllObjects()
+{
+ for (S32 i = 0; i < mObjects.count(); i++)
+ {
+ LLDrawable *drawable = mObjects[i]->mDrawable;
+ if (drawable)
+ {
+ gPipeline.markRelight(drawable);
+ }
+ }
+}
+
+void LLViewerObjectList::dirtyAllObjectInventory()
+{
+ S32 count = mObjects.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ mObjects[i]->dirtyInventory();
+ }
+}
+
+void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
+{
+ S32 i;
+ S32 num_objects = 0;
+ LLViewerObject *objectp;
+
+ S32 num_updates, max_value;
+ if (NUM_BINS - 1 == mCurBin)
+ {
+ num_updates = mObjects.count() - mCurLazyUpdateIndex;
+ max_value = mObjects.count();
+ gImageList.setUpdateStats(TRUE);
+ }
+ else
+ {
+ num_updates = (mObjects.count() / NUM_BINS) + 1;
+ max_value = llmin(mObjects.count(), mCurLazyUpdateIndex + num_updates);
+ }
+
+
+ if (!gNoRender)
+ {
+ // Slam priorities for textures that we care about (hovered, selected, and focused)
+ // Hovered
+ // Assumes only one level deep of parenting
+ objectp = gHoverView->getLastHoverObject();
+ if (objectp)
+ {
+ objectp->boostTexturePriority();
+ }
+ }
+
+ // Focused
+ objectp = gAgent.getFocusObject();
+ if (objectp)
+ {
+ objectp->boostTexturePriority();
+ }
+
+ // Selected
+ for (objectp = gSelectMgr->getFirstRootObject(); objectp; objectp = gSelectMgr->getNextRootObject())
+ {
+ objectp->boostTexturePriority();
+ }
+
+
+ // Iterate through some of the objects and lazy update their texture priorities
+ for (i = mCurLazyUpdateIndex; i < max_value; i++)
+ {
+ objectp = mObjects[i];
+ if (!objectp->isDead())
+ {
+ num_objects++;
+
+ // Update distance & gpw
+ objectp->setPixelAreaAndAngle(agent); // Also sets the approx. pixel area
+ objectp->updateTextures(agent); // Update the image levels of textures for this object.
+ }
+ }
+
+ mCurLazyUpdateIndex = max_value;
+ if (mCurLazyUpdateIndex == mObjects.count())
+ {
+ mCurLazyUpdateIndex = 0;
+ }
+
+ mCurBin = (++mCurBin) % NUM_BINS;
+
+ LLVOAvatar::cullAvatarsByPixelArea();
+}
+
+
+void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ // Update globals
+ gVelocityInterpolate = gSavedSettings.getBOOL("VelocityInterpolate");
+ gPingInterpolate = gSavedSettings.getBOOL("PingInterpolate");
+ gAnimateTextures = gSavedSettings.getBOOL("AnimateTextures");
+
+ // update global timer
+ F32 last_time = gFrameTimeSeconds;
+ U64 time = totalTime(); // this will become the new gFrameTime when the update is done
+ // Time _can_ go backwards, for example if the user changes the system clock.
+ // It doesn't cause any fatal problems (just some oddness with stats), so we shouldn't assert here.
+// llassert(time > gFrameTime);
+ F64 time_diff = U64_to_F64(time - gFrameTime)/(F64)SEC_TO_MICROSEC;
+ gFrameTime = time;
+ F64 time_since_start = U64_to_F64(gFrameTime - gStartTime)/(F64)SEC_TO_MICROSEC;
+ gFrameTimeSeconds = (F32)time_since_start;
+
+ gFrameIntervalSeconds = gFrameTimeSeconds - last_time;
+ if (gFrameIntervalSeconds < 0.f)
+ {
+ gFrameIntervalSeconds = 0.f;
+ }
+
+ //clear avatar LOD change counter
+ LLVOAvatar::sNumLODChangesThisFrame = 0;
+
+ const F64 frame_time = LLFrameTimer::getElapsedSeconds();
+
+ std::vector<LLViewerObject*> kill_list;
+ S32 num_active_objects = 0;
+
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mActiveObjects.begin();
+ iter != mActiveObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (objectp->getPCode() == LLViewerObject::LL_VO_CLOUDS ||
+ objectp->isAvatar())
+ {
+ objectp->idleUpdate(agent, world, frame_time);
+ }
+ }
+ }
+ else
+ {
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mActiveObjects.begin();
+ iter != mActiveObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (!objectp->idleUpdate(agent, world, frame_time))
+ {
+ // If Idle Update returns false, kill object!
+ kill_list.push_back(objectp);
+ }
+ else
+ {
+ num_active_objects++;
+ }
+ }
+ for (std::vector<LLViewerObject*>::iterator iter = kill_list.begin();
+ iter != kill_list.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ killObject(objectp);
+ }
+ }
+
+ mNumSizeCulled = 0;
+ mNumVisCulled = 0;
+
+ // compute all sorts of time-based stats
+ // don't factor frames that were paused into the stats
+ if (! mWasPaused)
+ {
+ gViewerStats->updateFrameStats(time_diff);
+ }
+
+ /*
+ // Debugging code for viewing orphans, and orphaned parents
+ LLUUID id;
+ char id_str[UUID_STR_LENGTH + 20];
+ for (i = 0; i < mOrphanParents.count(); i++)
+ {
+ id = sIndexAndLocalIDToUUID[mOrphanParents[i]];
+ LLViewerObject *objectp = findObject(id);
+ if (objectp)
+ {
+ sprintf(id_str, "Par: ");
+ objectp->mID.toString(id_str + 5);
+ addDebugBeacon(objectp->getPositionAgent(),
+ id_str,
+ LLColor4(1.f,0.f,0.f,1.f),
+ LLColor4(1.f,1.f,1.f,1.f));
+ }
+ }
+
+ LLColor4 text_color;
+ for (i = 0; i < mOrphanChildren.count(); i++)
+ {
+ OrphanInfo oi = mOrphanChildren[i];
+ LLViewerObject *objectp = findObject(oi.mChildInfo);
+ if (objectp)
+ {
+ if (objectp->getParent())
+ {
+ sprintf(id_str, "ChP: ");
+ text_color = LLColor4(0.f, 1.f, 0.f, 1.f);
+ }
+ else
+ {
+ sprintf(id_str, "ChNoP: ");
+ text_color = LLColor4(1.f, 0.f, 0.f, 1.f);
+ }
+ id = sIndexAndLocalIDToUUID[oi.mParentInfo];
+ objectp->mID.toString(id_str + 8);
+ addDebugBeacon(objectp->getPositionAgent() + LLVector3(0.f, 0.f, -0.25f),
+ id_str,
+ LLColor4(0.25f,0.25f,0.25f,1.f),
+ text_color);
+ }
+ i++;
+ }
+ */
+
+ mNumObjectsStat.addValue(mObjects.count());
+ mNumActiveObjectsStat.addValue(num_active_objects);
+ mNumSizeCulledStat.addValue(mNumSizeCulled);
+ mNumVisCulledStat.addValue(mNumVisCulled);
+}
+
+void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ if (mDeadObjects.count(objectp->mID))
+ {
+ llinfos << "Object " << objectp->mID << " already on dead list, ignoring cleanup!" << llendl;
+ return;
+ }
+
+ mDeadObjects.insert(std::pair<LLUUID, LLPointer<LLViewerObject> >(objectp->mID, objectp));
+
+ // Cleanup any references we have to this object
+ // Remove from object map so noone can look it up.
+
+ mUUIDObjectMap.erase(objectp->mID);
+ removeFromLocalIDTable(*objectp);
+
+ if (objectp->onActiveList())
+ {
+ //llinfos << "Removing " << objectp->mID << " " << objectp->getPCodeString() << " from active list in cleanupReferences." << llendl;
+ objectp->setOnActiveList(FALSE);
+ mActiveObjects.erase(objectp);
+ }
+
+ if (objectp->isOnMap())
+ {
+ mMapObjects.removeObj(objectp);
+ }
+
+ // Don't clean up mObject references, these will be cleaned up more efficiently later!
+ // Also, not cleaned up
+ removeDrawable(objectp->mDrawable);
+
+ mNumDeadObjects++;
+}
+
+void LLViewerObjectList::removeDrawable(LLDrawable* drawablep)
+{
+ if (!drawablep)
+ {
+ return;
+ }
+
+ for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+ {
+ LLViewerObject* objectp = drawablep->getFace(i)->getViewerObject();
+ mSelectPickList.erase(objectp);
+ }
+}
+
+BOOL LLViewerObjectList::killObject(LLViewerObject *objectp)
+{
+ // When we're killing objects, all we do is mark them as dead.
+ // We clean up the dead objects later.
+
+ if (objectp)
+ {
+ if (objectp->isDead())
+ {
+ // This object is already dead. Don't need to do more.
+ return TRUE;
+ }
+ else
+ {
+ objectp->markDead();
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLViewerObjectList::killObjects(LLViewerRegion *regionp)
+{
+ LLViewerObject *objectp;
+
+ S32 i;
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+
+ if (objectp->mRegionp == regionp)
+ {
+ killObject(objectp);
+ }
+ }
+
+ // Have to clean right away because the region is becoming invalid.
+ cleanDeadObjects(FALSE);
+}
+
+void LLViewerObjectList::killAllObjects()
+{
+ // Used only on global destruction.
+ LLViewerObject *objectp;
+
+ for (S32 i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+
+ killObject(objectp);
+ llassert(objectp->isDead());
+ }
+
+ cleanDeadObjects(FALSE);
+
+ if(!mObjects.empty())
+ {
+ llwarns << "LLViewerObjectList::killAllObjects still has entries in mObjects: " << mObjects.count() << llendl;
+ mObjects.clear();
+ }
+
+ if (!mActiveObjects.empty())
+ {
+ llwarns << "Some objects still on active object list!" << llendl;
+ mActiveObjects.clear();
+ }
+
+ if (!mMapObjects.empty())
+ {
+ llwarns << "Some objects still on map object list!" << llendl;
+ mActiveObjects.clear();
+ }
+}
+
+void LLViewerObjectList::cleanDeadObjects(BOOL use_timer)
+{
+ if (!mNumDeadObjects)
+ {
+ // No dead objects, don't need to scan object list.
+ return;
+ }
+
+ S32 i = 0;
+ S32 num_removed = 0;
+ LLViewerObject *objectp;
+ while (i < mObjects.count())
+ {
+ // Scan for all of the dead objects and remove any "global" references to them.
+ objectp = mObjects[i];
+ if (objectp->isDead())
+ {
+ mObjects.remove(i);
+ num_removed++;
+
+ if (num_removed == mNumDeadObjects)
+ {
+ // We've cleaned up all of the dead objects.
+ break;
+ }
+ }
+ else
+ {
+ // iterate, this isn't a dead object.
+ i++;
+ }
+ }
+
+ // We've cleaned the global object list, now let's do some paranoia testing on objects
+ // before blowing away the dead list.
+ mDeadObjects.clear();
+ mNumDeadObjects = 0;
+}
+
+void LLViewerObjectList::updateActive(LLViewerObject *objectp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ if (objectp->isDead())
+ {
+ return; // We don't update dead objects!
+ }
+
+ BOOL active = objectp->isActive();
+ if (active != objectp->onActiveList())
+ {
+ if (active)
+ {
+ //llinfos << "Adding " << objectp->mID << " " << objectp->getPCodeString() << " to active list." << llendl;
+ mActiveObjects.insert(objectp);
+ objectp->setOnActiveList(TRUE);
+ }
+ else
+ {
+ //llinfos << "Removing " << objectp->mID << " " << objectp->getPCodeString() << " from active list." << llendl;
+ mActiveObjects.erase(objectp);
+ objectp->setOnActiveList(FALSE);
+ }
+ }
+}
+
+
+
+void LLViewerObjectList::shiftObjects(const LLVector3 &offset)
+{
+ // This is called when we shift our origin when we cross region boundaries...
+ // We need to update many object caches, I'll document this more as I dig through the code
+ // cleaning things out...
+
+ if (gNoRender || 0 == offset.magVecSquared())
+ {
+ return;
+ }
+
+ LLViewerObject *objectp;
+ S32 i;
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = getObject(i);
+ // There could be dead objects on the object list, so don't update stuff if the object is dead.
+ if (objectp)
+ {
+ objectp->updatePositionCaches();
+
+ if (objectp->mDrawable.notNull() && !objectp->mDrawable->isDead())
+ {
+ gPipeline.markShift(objectp->mDrawable);
+ }
+ }
+ }
+
+ gPipeline.shiftObjects(offset);
+ gWorldPointer->mPartSim.shift(offset);
+}
+
+void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap)
+{
+ LLColor4 above_water_color = gColors.getColor( "NetMapOtherOwnAboveWater" );
+ LLColor4 below_water_color = gColors.getColor( "NetMapOtherOwnBelowWater" );
+ LLColor4 you_own_above_water_color =
+ gColors.getColor( "NetMapYouOwnAboveWater" );
+ LLColor4 you_own_below_water_color =
+ gColors.getColor( "NetMapYouOwnBelowWater" );
+ LLColor4 group_own_above_water_color =
+ gColors.getColor( "NetMapGroupOwnAboveWater" );
+ LLColor4 group_own_below_water_color =
+ gColors.getColor( "NetMapGroupOwnBelowWater" );
+
+
+ for (S32 i = 0; i < mMapObjects.count(); i++)
+ {
+ LLViewerObject* objectp = mMapObjects[i];
+ if (objectp->isOrphaned() || objectp->isAttachment())
+ {
+ continue;
+ }
+ const LLVector3& scale = objectp->getScale();
+ const LLVector3d pos = objectp->getPositionGlobal();
+ const F64 water_height = F64( objectp->getRegion()->getWaterHeight() );
+ // gWorldPointer->getWaterHeight();
+
+ F32 approx_radius = (scale.mV[VX] + scale.mV[VY]) * 0.5f * 0.5f * 1.3f; // 1.3 is a fudge
+
+ LLColor4U color = above_water_color;
+ if( objectp->permYouOwner() )
+ {
+ const F32 MIN_RADIUS_FOR_OWNED_OBJECTS = 2.f;
+ if( approx_radius < MIN_RADIUS_FOR_OWNED_OBJECTS )
+ {
+ approx_radius = MIN_RADIUS_FOR_OWNED_OBJECTS;
+ }
+
+ if( pos.mdV[VZ] >= water_height )
+ {
+ if ( objectp->permGroupOwner() )
+ {
+ color = group_own_above_water_color;
+ }
+ else
+ {
+ color = you_own_above_water_color;
+ }
+ }
+ else
+ {
+ if ( objectp->permGroupOwner() )
+ {
+ color = group_own_below_water_color;
+ }
+ else
+ {
+ color = you_own_below_water_color;
+ }
+ }
+ }
+ else
+ if( pos.mdV[VZ] < water_height )
+ {
+ color = below_water_color;
+ }
+
+ netmap.renderScaledPointGlobal(
+ pos,
+ color,
+ approx_radius );
+ }
+}
+
+void LLViewerObjectList::renderObjectBounds(const LLVector3 &center)
+{
+}
+
+
+U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parcel_wall, BOOL keep_pick_list)
+{
+ gRenderForSelect = TRUE;
+
+ // LLTimer pick_timer;
+ if (!keep_pick_list)
+ {
+ LLViewerObject *objectp;
+ S32 i;
+ // Reset all of the GL names to zero.
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+ objectp->mGLName = 0;
+ }
+
+ mSelectPickList.clear();
+
+ std::vector<LLDrawable*> pick_drawables;
+ gPipeline.mObjectPartition->cull(camera, &pick_drawables, TRUE);
+
+ for (std::vector<LLDrawable*>::iterator iter = pick_drawables.begin();
+ iter != pick_drawables.end(); iter++)
+ {
+ LLDrawable* drawablep = *iter;
+
+ LLViewerObject* last_objectp = NULL;
+ for (S32 face_num = 0; face_num < drawablep->getNumFaces(); face_num++)
+ {
+ LLViewerObject* objectp = drawablep->getFace(face_num)->getViewerObject();
+
+ if (objectp && objectp != last_objectp)
+ {
+ mSelectPickList.insert(objectp);
+ last_objectp = objectp;
+ }
+ }
+ }
+
+ LLHUDText::addPickable(mSelectPickList);
+
+ for (objectp = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ objectp;
+ objectp = (LLVOAvatar*)LLCharacter::sInstances.getNextData())
+ {
+ if (!objectp->isDead())
+ {
+ if (objectp->mDrawable.notNull() && objectp->mDrawable->isVisible())
+ {
+ mSelectPickList.insert(objectp);
+ }
+ }
+ }
+
+ // add all hud objects to pick list
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (avatarp)
+ {
+ LLViewerJointAttachment* attachmentp;
+ for (attachmentp = avatarp->mAttachmentPoints.getFirstData();
+ attachmentp;
+ attachmentp = avatarp->mAttachmentPoints.getNextData())
+ {
+ if (attachmentp->getIsHUDAttachment())
+ {
+ LLViewerObject* objectp = attachmentp->getObject(0);
+ if (objectp)
+ {
+ mSelectPickList.insert(objectp);
+ for (U32 i = 0; i < objectp->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = objectp->mChildList[i];
+ if (childp)
+ {
+ mSelectPickList.insert(childp);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ S32 num_pickables = (S32)mSelectPickList.size() + LLHUDIcon::getNumInstances();
+
+ S32 step = (0x000fffff - GL_NAME_INDEX_OFFSET) / num_pickables;
+
+ std::set<LLViewerObject*>::iterator pick_it;
+ i = 0;
+ for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end();)
+ {
+ LLViewerObject* objp = (*pick_it);
+ if (!objp || objp->isDead() || !objp->mbCanSelect)
+ {
+ mSelectPickList.erase(pick_it++);
+ continue;
+ }
+
+ objp->mGLName = (i * step) + GL_NAME_INDEX_OFFSET;
+ i++;
+ ++pick_it;
+ }
+
+ LLHUDIcon::generatePickIDs(i * step, step);
+ }
+
+ // At this point, we should only have live drawables/viewer objects
+ gPipeline.renderForSelect();
+ //
+ // Render pass for selected objects
+ //
+ gViewerWindow->renderSelections( TRUE, pick_parcel_wall, FALSE );
+
+ // render pickable ui elements, like names, etc.
+ LLHUDObject::renderAllForSelect();
+
+ gRenderForSelect = FALSE;
+
+ //llinfos << "Rendered " << count << " for select" << llendl;
+ //llinfos << "Took " << pick_timer.getElapsedTimeF32()*1000.f << "ms to pick" << llendl;
+ return 0;
+}
+
+LLViewerObject *LLViewerObjectList::getSelectedObject(const U32 object_id)
+{
+ std::set<LLViewerObject*>::iterator pick_it;
+ for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end(); ++pick_it)
+ {
+ if ((*pick_it)->mGLName == object_id)
+ {
+ return (*pick_it);
+ }
+ }
+ return NULL;
+}
+
+void LLViewerObjectList::addDebugBeacon(const LLVector3 &pos_agent,
+ const LLString &string,
+ const LLColor4 &color,
+ const LLColor4 &text_color,
+ S32 line_width)
+{
+ LLDebugBeacon *beaconp = mDebugBeacons.reserve_block(1);
+ beaconp->mPositionAgent = pos_agent;
+ beaconp->mString = string;
+ beaconp->mColor = color;
+ beaconp->mTextColor = text_color;
+ beaconp->mLineWidth = line_width;
+}
+
+void LLViewerObjectList::resetObjectBeacons()
+{
+ mDebugBeacons.reset();
+}
+
+LLViewerObject *LLViewerObjectList::createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ LLUUID fullid;
+ fullid.generate();
+
+ LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);
+ if (!objectp)
+ {
+ llwarns << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << llendl;
+ return NULL;
+ }
+
+ mUUIDObjectMap[fullid] = objectp;
+
+ mObjects.put(objectp);
+
+ updateActive(objectp);
+
+ return objectp;
+}
+
+
+
+LLViewerObject *LLViewerObjectList::createObject(const LLPCode pcode, LLViewerRegion *regionp,
+ const LLUUID &uuid, const U32 local_id, const LLHost &sender)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ LLFastTimer t(LLFastTimer::FTM_CREATE_OBJECT);
+
+ LLUUID fullid;
+ if (uuid == LLUUID::null)
+ {
+ fullid.generate();
+ }
+ else
+ {
+ fullid = uuid;
+ }
+
+ LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);
+ if (!objectp)
+ {
+ llwarns << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << " id:" << fullid << llendl;
+ return NULL;
+ }
+
+ mUUIDObjectMap[fullid] = objectp;
+ setUUIDAndLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+
+ mObjects.put(objectp);
+
+ updateActive(objectp);
+
+ return objectp;
+}
+
+LLViewerObject *LLViewerObjectList::replaceObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+{
+ LLViewerObject *old_instance = findObject(id);
+ if (old_instance)
+ {
+ cleanupReferences(old_instance);
+ old_instance->markDead();
+
+ return createObject(pcode, regionp, id, old_instance->getLocalID(), LLHost());
+ }
+ return NULL;
+}
+
+S32 LLViewerObjectList::findReferences(LLDrawable *drawablep) const
+{
+ LLViewerObject *objectp;
+ S32 i;
+ S32 num_refs = 0;
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+ if (objectp->mDrawable.notNull())
+ {
+ num_refs += objectp->mDrawable->findReferences(drawablep);
+ }
+ }
+ return num_refs;
+}
+
+
+void LLViewerObjectList::orphanize(LLViewerObject *childp, U32 parent_id, U32 ip, U32 port)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+#ifdef ORPHAN_SPAM
+ llinfos << "Orphaning object " << childp->getID() << " with parent " << parent_id << llendl;
+#endif
+
+ // We're an orphan, flag things appropriately.
+ childp->mOrphaned = TRUE;
+ if (childp->mDrawable.notNull())
+ {
+ bool make_invisible = true;
+ LLViewerObject *parentp = (LLViewerObject *)childp->getParent();
+ if (parentp)
+ {
+ if (parentp->getRegion() != childp->getRegion())
+ {
+ // This is probably an object flying across a region boundary, the
+ // object probably ISN'T being reparented, but just got an object
+ // update out of order (child update before parent).
+ make_invisible = false;
+ //llinfos << "Don't make object handoffs invisible!" << llendl;
+ }
+ }
+
+ if (make_invisible)
+ {
+ // Make sure that this object becomes invisible if it's an orphan
+ childp->mDrawable->setState(LLDrawable::FORCE_INVISIBLE);
+ }
+ }
+
+ // Unknown parent, add to orpaned child list
+ U64 parent_info = getIndex(parent_id, ip, port);
+
+ if (-1 == mOrphanParents.find(parent_info))
+ {
+ mOrphanParents.put(parent_info);
+ }
+
+ LLViewerObjectList::OrphanInfo oi(parent_info, childp->mID);
+ if (-1 == mOrphanChildren.find(oi))
+ {
+ mOrphanChildren.put(oi);
+ mNumOrphans++;
+ }
+}
+
+
+void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if (objectp->isDead())
+ {
+ llwarns << "Trying to find orphans for dead obj " << objectp->mID
+ << ":" << objectp->getPCodeString() << llendl;
+ return;
+ }
+
+ // See if we are a parent of an orphan.
+ // Note: This code is fairly inefficient but it should happen very rarely.
+ // It can be sped up if this is somehow a performance issue...
+ if (0 == mOrphanParents.count())
+ {
+ // no known orphan parents
+ return;
+ }
+ if (-1 == mOrphanParents.find(getIndex(objectp->mLocalID, ip, port)))
+ {
+ // did not find objectp in OrphanParent list
+ return;
+ }
+
+ S32 i;
+ U64 parent_info = getIndex(objectp->mLocalID, ip, port);
+ BOOL orphans_found = FALSE;
+ // Iterate through the orphan list, and set parents of matching children.
+ for (i = 0; i < mOrphanChildren.count(); i++)
+ {
+ if (mOrphanChildren[i].mParentInfo != parent_info)
+ {
+ continue;
+ }
+ LLViewerObject *childp = findObject(mOrphanChildren[i].mChildInfo);
+ if (childp)
+ {
+ if (childp == objectp)
+ {
+ llwarns << objectp->mID << " has self as parent, skipping!"
+ << llendl;
+ continue;
+ }
+
+#ifdef ORPHAN_SPAM
+ llinfos << "Reunited parent " << objectp->mID
+ << " with child " << childp->mID << llendl;
+ llinfos << "Glob: " << objectp->getPositionGlobal() << llendl;
+ llinfos << "Agent: " << objectp->getPositionAgent() << llendl;
+ addDebugBeacon(objectp->getPositionAgent(),"");
+#endif
+ gPipeline.markMoved(objectp->mDrawable);
+ objectp->setChanged(LLXform::MOVED | LLXform::SILHOUETTE);
+
+ // Flag the object as no longer orphaned
+ childp->mOrphaned = FALSE;
+ if (childp->mDrawable.notNull())
+ {
+ // Make the drawable visible again and set the drawable parent
+ childp->mDrawable->setState(LLDrawable::CLEAR_INVISIBLE);
+ childp->setDrawableParent(objectp->mDrawable); // LLViewerObjectList::findOrphans()
+ }
+ objectp->addChild(childp);
+ orphans_found = TRUE;
+ }
+ else
+ {
+ llinfos << "Missing orphan child, removing from list" << llendl;
+ mOrphanChildren.remove(i);
+ i--;
+ }
+ }
+
+ // Remove orphan parent and children from lists now that they've been found
+ mOrphanParents.remove(mOrphanParents.find(parent_info));
+
+ i = 0;
+ while (i < mOrphanChildren.count())
+ {
+ if (mOrphanChildren[i].mParentInfo == parent_info)
+ {
+ mOrphanChildren.remove(i);
+ mNumOrphans--;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ if (orphans_found && objectp->isSelected())
+ {
+ LLSelectNode* nodep = gSelectMgr->findSelectNode(objectp);
+ if (nodep && !nodep->mIndividualSelection)
+ {
+ // rebuild selection with orphans
+ gSelectMgr->deselectObjectAndFamily(objectp);
+ gSelectMgr->selectObjectAndFamily(objectp);
+ }
+ }
+}
+
+
+LLViewerObjectList::OrphanInfo::OrphanInfo()
+{
+}
+
+LLViewerObjectList::OrphanInfo::OrphanInfo(const U64 parent_info, const LLUUID child_info)
+ : mParentInfo(parent_info), mChildInfo(child_info)
+{
+}
+
+bool LLViewerObjectList::OrphanInfo::operator==(const OrphanInfo &rhs) const
+{
+ return (mParentInfo == rhs.mParentInfo) && (mChildInfo == rhs.mChildInfo);
+}
+
+bool LLViewerObjectList::OrphanInfo::operator!=(const OrphanInfo &rhs) const
+{
+ return !operator==(rhs);
+}
+