summaryrefslogtreecommitdiff
path: root/indra/newview/llviewerobject.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/llviewerobject.cpp
Print done when done.
Diffstat (limited to 'indra/newview/llviewerobject.cpp')
-rw-r--r--indra/newview/llviewerobject.cpp4683
1 files changed, 4683 insertions, 0 deletions
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
new file mode 100644
index 0000000000..b1e4ee1947
--- /dev/null
+++ b/indra/newview/llviewerobject.cpp
@@ -0,0 +1,4683 @@
+/**
+ * @file llviewerobject.cpp
+ * @brief Base class for viewer objects
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerobject.h"
+
+#include "audioengine.h"
+#include "imageids.h"
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llflexibleobject.h"
+#include "llviewercontrol.h"
+#include "lldatapacker.h"
+#include "llfasttimer.h"
+#include "llfontgl.h"
+#include "llframetimer.h"
+#include "llinventory.h"
+#include "llmaterialtable.h"
+#include "llmutelist.h"
+#include "llnamevalue.h"
+#include "llprimitive.h"
+#include "llquantize.h"
+#include "llregionhandle.h"
+#include "lltree_common.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "object_flags.h"
+#include "timing.h"
+
+#include "llaudiosourcevo.h"
+#include "llagent.h"
+#include "llbbox.h"
+#include "llbox.h"
+#include "llcylinder.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llfloaterproperties.h"
+#include "llfollowcam.h"
+#include "llnetmap.h"
+#include "llselectmgr.h"
+#include "llsphere.h"
+#include "lltooldraganddrop.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewermedialist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerpartsource.h"
+#include "llviewerregion.h"
+#include "llviewertextureanim.h"
+#include "llviewerwindow.h" // For getSpinAxis
+#include "llvoavatar.h"
+#include "llvoclouds.h"
+#include "llvograss.h"
+#include "llvoground.h"
+#include "llvolume.h"
+#include "llvolumemessage.h"
+#include "llvopart.h"
+#include "llvopartgroup.h"
+#include "llvosky.h"
+#include "llvostars.h"
+#include "llvosurfacepatch.h"
+#include "llvotextbubble.h"
+#include "llvotree.h"
+#include "llvotreenew.h"
+#include "llvovolume.h"
+#include "llvowater.h"
+#include "llworld.h"
+#include "llui.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+//#define DEBUG_UPDATE_TYPE
+
+extern BOOL gVelocityInterpolate;
+extern BOOL gPingInterpolate;
+extern U32 gFrameCount;
+
+U32 LLViewerObject::sNumZombieObjects = 0;
+S32 LLViewerObject::sNumObjects = 0;
+BOOL LLViewerObject::sMapDebug = TRUE;
+LLColor4 LLViewerObject::sEditSelectColor( 1.0f, 1.f, 0.f, 0.3f); // Edit OK
+LLColor4 LLViewerObject::sNoEditSelectColor( 1.0f, 0.f, 0.f, 0.3f); // Can't edit
+S32 LLViewerObject::sAxisArrowLength(50);
+BOOL LLViewerObject::sPulseEnabled(FALSE);
+BOOL LLViewerObject::sUseSharedDrawables(FALSE); // TRUE
+
+// static
+LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+{
+ LLViewerObject *res = NULL;
+ LLFastTimer t1(LLFastTimer::FTM_CREATE_OBJECT);
+
+ switch (pcode)
+ {
+ case LL_PCODE_VOLUME:
+ res = new LLVOVolume(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_AVATAR:
+ res = new LLVOAvatar(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_GRASS:
+ res = new LLVOGrass(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_PART_SYS:
+ res = new LLVOPart(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_TREE:
+ res = new LLVOTree(id, pcode, regionp); break;
+ case LL_PCODE_TREE_NEW:
+ llwarns << "Creating new tree!" << llendl;
+// res = new LLVOTree(id, pcode, regionp); break;
+// res = new LLVOTreeNew(id, pcode, regionp); break;
+ res = NULL; break;
+ case LL_PCODE_LEGACY_TEXT_BUBBLE:
+ res = new LLVOTextBubble(id, pcode, regionp); break;
+ case LL_VO_CLOUDS:
+ res = new LLVOClouds(id, pcode, regionp); break;
+ case LL_VO_SURFACE_PATCH:
+ res = new LLVOSurfacePatch(id, pcode, regionp); break;
+ case LL_VO_SKY:
+ res = new LLVOSky(id, pcode, regionp); break;
+ case LL_VO_STARS:
+ res = new LLVOStars(id, pcode, regionp); break;
+ case LL_VO_WATER:
+ res = new LLVOWater(id, pcode, regionp); break;
+ case LL_VO_GROUND:
+ res = new LLVOGround(id, pcode, regionp); break;
+ case LL_VO_PART_GROUP:
+ res = new LLVOPartGroup(id, pcode, regionp); break;
+ default:
+ llwarns << "Unknown object pcode " << (S32)pcode << llendl;
+ res = NULL; break;
+ }
+ return res;
+}
+
+LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLPrimitive(),
+ mChildList(),
+ mID(id),
+ mLocalID(0),
+ mTotalCRC(0),
+ mTEImages(NULL),
+ mGLName(0),
+ mbCanSelect(TRUE),
+ mFlags(0),
+ mDrawable(),
+ mCreateSelected(FALSE),
+ mRenderMedia(FALSE),
+ mBestUpdatePrecision(0),
+ mText(),
+ mLastInterpUpdateSecs(0.f),
+ mLastMessageUpdateSecs(0.f),
+ mData(NULL),
+ mAudioSourcep(NULL),
+ mAppAngle(0.f),
+ mPixelArea(0.f),
+ mInventory(NULL),
+ mInventorySerialNum(0),
+ mRegionp( regionp ),
+ mInventoryPending(FALSE),
+ mInventoryDirty(FALSE),
+ mDead(FALSE),
+ mOrphaned(FALSE),
+ mUserSelected(FALSE),
+ mOnActiveList(FALSE),
+ mOnMap(FALSE),
+ mStatic(FALSE),
+ mFaceIndexOffset(0),
+ mNumFaces(0),
+ mLastUpdateFrame(0),
+ mTimeDilation(1.f),
+ mRotTime(0.f),
+ mJointInfo(NULL),
+ mState(0),
+ mMedia(NULL),
+ mClickAction(0)
+{
+ llassert(mRegionp);
+
+ LLPrimitive::init(pcode);
+
+ // CP: added 12/2/2005 - this was being initialised to 0, not the current frame time
+ mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds();
+
+ mPositionRegion = LLVector3(0.f, 0.f, 0.f);
+ mPositionAgent = mRegionp->getOriginAgent();
+
+ LLViewerObject::sNumObjects++;
+}
+
+LLViewerObject::~LLViewerObject()
+{
+ deleteTEImages();
+
+ if(mInventory)
+ {
+ mInventory->clear(); // will deref and delete entries
+ delete mInventory;
+ mInventory = NULL;
+ }
+
+ if (mJointInfo)
+ {
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ // Delete memory associated with extra parameters.
+ std::map<U16, ExtraParameter*>::iterator iter;
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ delete iter->second->data;
+ delete iter->second;
+ }
+ mExtraParameterList.clear();
+
+ for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ;
+ mNameValuePairs.clear();
+
+ delete[] mData;
+ mData = NULL;
+
+ delete mMedia;
+ mMedia = NULL;
+
+ sNumObjects--;
+ sNumZombieObjects--;
+ llassert(mChildList.size() == 0);
+
+ clearInventoryListeners();
+}
+
+void LLViewerObject::deleteTEImages()
+{
+ delete[] mTEImages;
+ mTEImages = NULL;
+}
+
+void LLViewerObject::markDead()
+{
+ if (!mDead)
+ {
+ //llinfos << "Marking self " << mLocalID << " as dead." << llendl;
+
+ // Root object of this hierarchy unlinks itself.
+ if (getParent())
+ {
+ ((LLViewerObject *)getParent())->removeChild(this);
+ // go ahead and delete any jointinfo's that we find
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ // Mark itself as dead
+ mDead = TRUE;
+ gObjectList.cleanupReferences(this);
+
+ LLViewerObject *childp;
+ while (mChildList.size() > 0)
+ {
+ childp = mChildList[0];
+ if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR)
+ {
+ //llinfos << "Marking child " << childp->getLocalID() << " as dead." << llendl;
+ childp->setParent(NULL); // LLViewerObject::markDead 1
+ childp->markDead();
+ }
+ else
+ {
+ // make sure avatar is no longer parented,
+ // so we can properly set it's position
+ childp->setDrawableParent(NULL);
+ ((LLVOAvatar*)childp)->getOffObject();
+ childp->setParent(NULL); // LLViewerObject::markDead 2
+ }
+ mChildList.erase(mChildList.begin());
+ }
+
+ if (mDrawable.notNull())
+ {
+ // Drawables are reference counted, mark as dead, then nuke the pointer.
+ mDrawable->markDead();
+ mDrawable = NULL;
+ }
+
+ if (mText)
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+
+ if (mIcon)
+ {
+ mIcon->markDead();
+ mIcon = NULL;
+ }
+
+ if (mPartSourcep)
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+
+ if (mAudioSourcep)
+ {
+ // Do some cleanup
+ if (gAudiop)
+ {
+ gAudiop->cleanupAudioSource(mAudioSourcep);
+ }
+ mAudioSourcep = NULL;
+ }
+
+ if (flagAnimSource())
+ {
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (avatarp && !avatarp->isDead())
+ {
+ // stop motions associated with this object
+ avatarp->stopMotionFromSource(mID);
+ }
+ }
+
+ if (flagCameraSource())
+ {
+ LLFollowCamMgr::removeFollowCamParams(mID);
+ }
+
+ sNumZombieObjects++;
+ }
+}
+
+void LLViewerObject::dump() const
+{
+ llinfos << "Type: " << pCodeToString(mPrimitiveCode) << llendl;
+ llinfos << "Drawable: " << (LLDrawable *)mDrawable << llendl;
+ llinfos << "Update Age: " << LLFrameTimer::getElapsedSeconds() - mLastMessageUpdateSecs << llendl;
+
+ llinfos << "Parent: " << getParent() << llendl;
+ llinfos << "ID: " << mID << llendl;
+ llinfos << "LocalID: " << mLocalID << llendl;
+ llinfos << "PositionRegion: " << getPositionRegion() << llendl;
+ llinfos << "PositionAgent: " << getPositionAgent() << llendl;
+ llinfos << "PositionGlobal: " << getPositionGlobal() << llendl;
+ llinfos << "Velocity: " << getVelocity() << llendl;
+ if (mDrawable.notNull() && mDrawable->getNumFaces())
+ {
+ LLDrawPool *poolp = mDrawable->getFace(0)->getPool();
+ llinfos << "Pool: " << poolp << llendl;
+ llinfos << "Pool reference count: " << poolp->mReferences.size() << llendl;
+ llinfos << "Pool vertex count: " << poolp->getVertexCount() << llendl;
+ }
+ //llinfos << "BoxTree Min: " << mDrawable->getBox()->getMin() << llendl;
+ //llinfos << "BoxTree Max: " << mDrawable->getBox()->getMin() << llendl;
+ /*
+ llinfos << "Velocity: " << getVelocity() << llendl;
+ llinfos << "AnyOwner: " << permAnyOwner() << " YouOwner: " << permYouOwner() << " Edit: " << mPermEdit << llendl;
+ llinfos << "UsePhysics: " << usePhysics() << " CanSelect " << mbCanSelect << " UserSelected " << mUserSelected << llendl;
+ llinfos << "AppAngle: " << mAppAngle << llendl;
+ llinfos << "PixelArea: " << mPixelArea << llendl;
+
+ char buffer[1000];
+ char *key;
+ for (key = mNameValuePairs.getFirstKey(); key; key = mNameValuePairs.getNextKey() )
+ {
+ mNameValuePairs[key]->printNameValue(buffer);
+ llinfos << buffer << llendl;
+ }
+
+ S32 i;
+
+ LLViewerObject *child;
+ for (i = 0; i < mChildList.size(); i++)
+ {
+ child = mChildList[i];
+ llinfos << " child " << child->getID() << llendl;
+ }
+ */
+}
+
+void LLViewerObject::printNameValuePairs() const
+{
+ for (name_value_map_t::const_iterator iter = mNameValuePairs.begin();
+ iter != mNameValuePairs.end(); iter++)
+ {
+ LLNameValue* nv = iter->second;
+ llinfos << nv->printNameValue() << llendl;
+ }
+}
+
+void LLViewerObject::initVOClasses()
+{
+ // Initialized shared class stuff first.
+ LLVOAvatar::initClass();
+ LLVOTree::initClass();
+ if (gNoRender)
+ {
+ // Don't init anything else in drone mode
+ return;
+ }
+ llinfos << "Viewer Object size: " << sizeof(LLViewerObject) << llendl;
+ LLVOGrass::initClass();
+ LLVOPart::initClass();
+ LLVOWater::initClass();
+ LLVOSky::initClass();
+ LLVOVolume::initClass();
+}
+
+void LLViewerObject::cleanupVOClasses()
+{
+ LLVOGrass::cleanupClass();
+ LLVOWater::cleanupClass();
+ LLVOTree::cleanupClass();
+ LLVOAvatar::cleanupClass();
+}
+
+// Replaces all name value pairs with data from \n delimited list
+// Does not update server
+void LLViewerObject::setNameValueList(char* name_value_list)
+{
+ // Clear out the old
+ for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ;
+ mNameValuePairs.clear();
+
+ // Bring in the new
+ char* token_start = name_value_list;
+ char* scan = name_value_list;
+
+ if (*scan == '\0') return;
+
+ BOOL done = FALSE;
+ while (!done)
+ {
+ while ( (*scan != '\0') && (*scan != '\n') )
+ {
+ scan++;
+ }
+
+ if (*scan == '\n')
+ {
+ *scan = '\0';
+ addNVPair(token_start);
+ scan++;
+ token_start = scan;
+ }
+ else
+ {
+ addNVPair(token_start);
+ done = TRUE;
+ }
+ }
+}
+
+
+// This method returns true if the object is over land owned by the
+// agent.
+BOOL LLViewerObject::isOverAgentOwnedLand() const
+{
+ return mRegionp
+ && mRegionp->getParcelOverlay()
+ && mRegionp->getParcelOverlay()->isOwnedSelf(getPositionRegion());
+}
+
+// This method returns true if the object is over land owned by the
+// agent.
+BOOL LLViewerObject::isOverGroupOwnedLand() const
+{
+ return mRegionp
+ && mRegionp->getParcelOverlay()
+ && mRegionp->getParcelOverlay()->isOwnedGroup(getPositionRegion());
+}
+
+void LLViewerObject::setParent(LLViewerObject* parent)
+{
+ LLPrimitive::setParent(parent);
+}
+
+void LLViewerObject::addChild(LLViewerObject *childp)
+{
+ BOOL result = TRUE;
+
+ for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i)
+ {
+ if (*i == childp)
+ { //already has child
+ return;
+ }
+ }
+
+ if (!isAvatar())
+ {
+ // propagate selection properties
+ childp->mbCanSelect = mbCanSelect;
+ }
+
+ childp->setParent(this);
+ mChildList.push_back(childp);
+
+ if (!result)
+ {
+ llwarns << "Failed to attach child " << childp->getID() << " to object " << getID() << llendl;
+ removeChild(childp);
+ if (mJointInfo)
+ {
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+ }
+}
+
+void LLViewerObject::removeChild(LLViewerObject *childp)
+{
+ for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i)
+ {
+ if (*i == childp)
+ {
+ if (!childp->isAvatar() && mDrawable.notNull() && mDrawable->isActive() && childp->mDrawable.notNull() && !isAvatar())
+ {
+ gPipeline.markRebuild(childp->mDrawable, LLDrawable::REBUILD_VOLUME);
+ }
+
+ mChildList.erase(i);
+ childp->setParent(NULL);
+ break;
+ }
+ }
+
+ if (childp->isSelected())
+ {
+ gSelectMgr->deselectObjectAndFamily(childp);
+ BOOL add_to_end = TRUE;
+ gSelectMgr->selectObjectAndFamily(childp, add_to_end);
+ }
+}
+
+LLViewerObject::child_list_t& LLViewerObject::getChildren()
+{
+ return mChildList;
+}
+
+void LLViewerObject::addThisAndAllChildren(LLDynamicArray<LLViewerObject*>& objects)
+{
+ objects.put(this);
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ if (!mChildList[i]->isAvatar())
+ {
+ (mChildList[i])->addThisAndAllChildren(objects);
+ }
+ }
+}
+
+void LLViewerObject::addThisAndNonJointChildren(LLDynamicArray<LLViewerObject*>& objects)
+{
+ objects.put(this);
+ // don't add any attachments when temporarily selecting avatar
+ if (isAvatar())
+ {
+ return;
+ }
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ if ( (!mChildList[i]->isAvatar())
+ && (!mChildList[i]->isJointChild()))
+ {
+ (mChildList[i])->addThisAndNonJointChildren(objects);
+ }
+ }
+}
+
+BOOL LLViewerObject::isChild(LLViewerObject *childp) const
+{
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLViewerObject *testChildp = &(*mChildList[i]);
+ if (testChildp == childp) return TRUE;
+ }
+ return FALSE;
+}
+
+
+// returns TRUE if at least one avatar is sitting on this object
+BOOL LLViewerObject::isSeat() const
+{
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLViewerObject *childp = &(*mChildList[i]);
+ if (childp->isAvatar())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+
+}
+
+BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp)
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+
+ mDrawable->mParent = parentp;
+
+ BOOL ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ gPipeline.markMoved(mDrawable, FALSE);
+
+ return ret;
+}
+
+U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ U32 retval = 0x0;
+
+ // Coordinates of objects on simulators are region-local.
+ U64 region_handle;
+ mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
+ mRegionp = gWorldPointer->getRegionFromHandle(region_handle);
+ if (!mRegionp)
+ {
+ U32 x, y;
+ from_region_handle(region_handle, &x, &y);
+
+ llerrs << "Object has invalid region " << x << ":" << y << "!" << llendl;
+ }
+
+ U16 time_dilation16;
+ mesgsys->getU16Fast(_PREHASH_RegionData, _PREHASH_TimeDilation, time_dilation16);
+ F32 time_dilation = ((F32) time_dilation16) / 65535.f;
+ mTimeDilation = time_dilation;
+ mRegionp->setTimeDilation(time_dilation);
+
+ // this will be used to determine if we've really changed position
+ // Use getPosition, not getPositionRegion, since this is what we're comparing directly against.
+ LLVector3 test_pos_parent = getPosition();
+
+ U8 data[60+16]; // This needs to match the largest size below.
+#ifdef LL_BIG_ENDIAN
+ U16 valswizzle[4];
+#endif
+ U16 *val;
+ const F32 size = gWorldPointer->getRegionWidthInMeters();
+ const F32 MAX_HEIGHT = gWorldPointer->getRegionMaxHeight();
+ const F32 MIN_HEIGHT = gWorldPointer->getRegionMinHeight();
+ S32 length;
+ S32 count;
+ S32 this_update_precision = 32; // in bits
+
+ // Temporaries, because we need to compare w/ previous to set dirty flags...
+ LLVector3 new_pos_parent;
+ LLVector3 new_vel;
+ LLVector3 new_acc;
+ LLVector3 new_angv;
+ LLQuaternion new_rot;
+ LLVector3 new_scale;
+
+ U32 parent_id = 0;
+ U8 material = 0;
+ U8 click_action = 0;
+ U32 crc = 0;
+
+ bool old_special_hover_cursor = specialHoverCursor();
+
+ LLViewerObject *cur_parentp = (LLViewerObject *)getParent();
+
+ if (cur_parentp)
+ {
+ parent_id = cur_parentp->mLocalID;
+ }
+
+ if (!dp)
+ {
+ switch(update_type)
+ {
+ case OUT_FULL:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "Full:" << getID() << llendl;
+#endif
+ LLUUID audio_uuid;
+ LLUUID owner_id; // only valid if audio_uuid or particle system is not null
+ F32 gain;
+ U8 sound_flags;
+
+ mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_CRC, crc, block_num);
+ mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_ParentID, parent_id, block_num);
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_Sound, audio_uuid, block_num );
+ // HACK: Owner id only valid if non-null sound id or particle system
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, block_num );
+ mesgsys->getF32Fast( _PREHASH_ObjectData, _PREHASH_Gain, gain, block_num );
+ mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Flags, sound_flags, block_num );
+ mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Material, material, block_num );
+ mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_ClickAction, click_action, block_num);
+ mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_Scale, new_scale, block_num );
+ length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num);
+
+ mTotalCRC = crc;
+
+ // Owner ID used for sound muting or particle system muting
+ setAttachedSound(audio_uuid, owner_id, gain, sound_flags);
+
+ U8 old_material = getMaterial();
+ if (old_material != material)
+ {
+ setMaterial(material);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ setClickAction(click_action);
+
+ count = 0;
+ LLVector4 collision_plane;
+
+ switch(length)
+ {
+ case (60 + 16):
+ // pull out collision normal for avatar
+ htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4));
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ count += sizeof(LLVector4);
+ case 60:
+ this_update_precision = 32;
+ // this is a terse update
+ // pos
+ htonmemcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // vel
+ htonmemcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // acc
+ htonmemcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // theta
+ {
+ LLVector3 vec;
+ htonmemcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ new_rot.unpackFromVector3(vec);
+ }
+ count += sizeof(LLVector3);
+ // omega
+ htonmemcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ if (new_angv.isExactlyZero())
+ {
+ // reset rotation time
+ resetRot();
+ }
+ setAngularVelocity(new_angv);
+#if LL_DARWIN
+ if (length == 76)
+ {
+ setAngularVelocity(LLVector3::zero);
+ }
+#endif
+ break;
+ case(32 + 16):
+ // pull out collision normal for avatar
+ htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4));
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ count += sizeof(LLVector4);
+ case 32:
+ this_update_precision = 16;
+ test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT);
+
+ // This is a terse 16 update, so treat data as an array of U16's.
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ setVelocity(LLVector3(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size)));
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ setAcceleration(LLVector3(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size)));
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Quat, 4);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*4;
+ new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f);
+ new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f);
+ new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f);
+ new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ new_angv.setVec(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size));
+ if (new_angv.isExactlyZero())
+ {
+ // reset rotation time
+ resetRot();
+ }
+ setAngularVelocity(new_angv);
+ break;
+
+ case 16:
+ this_update_precision = 8;
+ test_pos_parent.quantize8(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT);
+ // this is a terse 8 update
+ new_pos_parent.mV[VX] = U8_to_F32(data[0], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VY] = U8_to_F32(data[1], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VZ] = U8_to_F32(data[2], MIN_HEIGHT, MAX_HEIGHT);
+
+ setVelocity(U8_to_F32(data[3], -size, size),
+ U8_to_F32(data[4], -size, size),
+ U8_to_F32(data[5], -size, size) );
+
+ setAcceleration(U8_to_F32(data[6], -size, size),
+ U8_to_F32(data[7], -size, size),
+ U8_to_F32(data[8], -size, size) );
+
+ new_rot.mQ[VX] = U8_to_F32(data[9], -1.f, 1.f);
+ new_rot.mQ[VY] = U8_to_F32(data[10], -1.f, 1.f);
+ new_rot.mQ[VZ] = U8_to_F32(data[11], -1.f, 1.f);
+ new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f);
+
+ new_angv.setVec(U8_to_F32(data[13], -size, size),
+ U8_to_F32(data[14], -size, size),
+ U8_to_F32(data[15], -size, size) );
+ if (new_angv.isExactlyZero())
+ {
+ // reset rotation time
+ resetRot();
+ }
+ setAngularVelocity(new_angv);
+ break;
+ }
+
+ ////////////////////////////////////////////////////
+ //
+ // Here we handle data specific to the full message.
+ //
+
+ U32 flags;
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num);
+ // clear all but local flags
+ mFlags &= FLAGS_LOCAL;
+ mFlags |= flags;
+
+ U8 state;
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num );
+ mState = state;
+
+ // ...new objects that should come in selected need to be added to the selected list
+ mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0);
+
+ // Set the change flags for scale
+ if (new_scale != getScale())
+ {
+ setChanged(SCALED | SILHOUETTE);
+ setScale(new_scale); // Must follow setting permYouOwner()
+ }
+
+ // Set all name value pairs
+ S32 nv_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_NameValue);
+ if (nv_size > 0)
+ {
+ char* name_value_list = new char[nv_size];
+ mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_NameValue, nv_size, name_value_list, block_num);
+
+ setNameValueList(name_value_list);
+
+ delete [] name_value_list;
+ }
+
+ // Clear out any existing generic data
+ if (mData)
+ {
+ delete [] mData;
+ }
+
+ // Check for appended generic data
+ S32 data_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Data);
+ if (data_size == 0)
+ {
+ mData = NULL;
+ }
+ else
+ {
+ // ...has generic data
+ mData = new U8[data_size];
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, mData, data_size, block_num);
+ }
+
+ S32 text_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Text);
+ if (text_size > 1)
+ {
+ // Setup object text
+ if (!mText)
+ {
+ mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mText->setFont(LLFontGL::sSansSerif);
+ mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mText->setMaxLines(-1);
+ mText->setSourceObject(this);
+ mText->setOnHUDAttachment(isHUDAttachment());
+ }
+
+ char temp_string[256]; // not MAX_STRING, must hold 255 chars + \0
+ mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_Text, 256, temp_string, block_num );
+
+ LLColor4U coloru;
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextColor, coloru.mV, 4, block_num);
+
+ // alpha was flipped so that it zero encoded better
+ coloru.mV[3] = 255 - coloru.mV[3];
+ mText->setColor(LLColor4(coloru));
+ mText->setStringUTF8(temp_string);
+
+ if (mDrawable.notNull())
+ {
+ setChanged(MOVED | SILHOUETTE);
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ else if (mText.notNull())
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+
+ char media_url[MAX_STRING+1];
+ mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_MediaURL, MAX_STRING+1, media_url, block_num);
+ //if (media_url[0])
+ //{
+ // llinfos << "WEBONPRIM media_url " << media_url << llendl;
+ //}
+ if (!mMedia && media_url[0] != '\0')
+ {
+ retval |= MEDIA_URL_ADDED;
+ mMedia = new LLViewerObjectMedia;
+ mMedia->mMediaURL = media_url;
+ mMedia->mMediaType = LLViewerObject::MEDIA_TYPE_WEB_PAGE;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ else if (mMedia)
+ {
+ if (media_url[0] == '\0')
+ {
+ retval |= MEDIA_URL_REMOVED;
+ delete mMedia;
+ mMedia = NULL;
+ }
+ else if (mMedia->mMediaURL != media_url)
+ {
+ // We just added or changed a web page.
+ retval |= MEDIA_URL_UPDATED;
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ }
+
+ //
+ // Unpack particle system data
+ //
+ unpackParticleSource(block_num, owner_id);
+
+ // Mark all extra parameters not used
+ std::map<U16, ExtraParameter*>::iterator iter;
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ iter->second->in_use = FALSE;
+ }
+
+ // Unpack extra parameters
+ S32 size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ExtraParams);
+ if (size > 0)
+ {
+ U8 *buffer = new U8[size];
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ExtraParams, buffer, size, block_num);
+ LLDataPackerBinaryBuffer dp(buffer, size);
+
+ U8 num_parameters;
+ dp.unpackU8(num_parameters, "num_params");
+ U8 param_block[MAX_OBJECT_PARAMS_SIZE];
+ for (U8 param=0; param<num_parameters; ++param)
+ {
+ U16 param_type;
+ S32 param_size;
+ dp.unpackU16(param_type, "param_type");
+ dp.unpackBinaryData(param_block, param_size, "param_data");
+ //llinfos << "Param type: " << param_type << ", Size: " << param_size << llendl;
+ LLDataPackerBinaryBuffer dp2(param_block, param_size);
+ unpackParameterEntry(param_type, &dp2);
+ }
+ delete[] buffer;
+ }
+
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ if (!iter->second->in_use)
+ {
+ // Send an update message in case it was formerly in use
+ parameterChanged(iter->first, iter->second->data, FALSE, false);
+ }
+ }
+
+ U8 joint_type = 0;
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_JointType, joint_type, block_num);
+ if (joint_type)
+ {
+ // create new joint info
+ if (!mJointInfo)
+ {
+ mJointInfo = new LLVOJointInfo;
+ }
+ mJointInfo->mJointType = (EHavokJointType) joint_type;
+ mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointPivot, mJointInfo->mPivot, block_num);
+ mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointAxisOrAnchor, mJointInfo->mAxisOrAnchor, block_num);
+ }
+ else if (mJointInfo)
+ {
+ // this joint info is no longer needed
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ break;
+ }
+
+ case OUT_TERSE_IMPROVED:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "TI:" << getID() << llendl;
+#endif
+ length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num);
+ count = 0;
+ LLVector4 collision_plane;
+
+ switch(length)
+ {
+ case(60 + 16):
+ // pull out collision normal for avatar
+ htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4));
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ count += sizeof(LLVector4);
+ case 60:
+ // this is a terse 32 update
+ // pos
+ this_update_precision = 32;
+ htonmemcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // vel
+ htonmemcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // acc
+ htonmemcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ count += sizeof(LLVector3);
+ // theta
+ {
+ LLVector3 vec;
+ htonmemcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ new_rot.unpackFromVector3(vec);
+ }
+ count += sizeof(LLVector3);
+ // omega
+ htonmemcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3));
+ if (new_angv.isExactlyZero())
+ {
+ // reset rotation time
+ resetRot();
+ }
+ setAngularVelocity(new_angv);
+#if LL_DARWIN
+ if (length == 76)
+ {
+ setAngularVelocity(LLVector3::zero);
+ }
+#endif
+ break;
+ case(32 + 16):
+ // pull out collision normal for avatar
+ htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4));
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ count += sizeof(LLVector4);
+ case 32:
+ // this is a terse 16 update
+ this_update_precision = 16;
+ test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ setVelocity(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size));
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*3;
+ setAcceleration(U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size));
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Quat, 8);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ count += sizeof(U16)*4;
+ new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f);
+ new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f);
+ new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f);
+ new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f);
+
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6);
+ val = valswizzle;
+#else
+ val = (U16 *) &data[count];
+#endif
+ setAngularVelocity( U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size));
+ break;
+
+ case 16:
+ // this is a terse 8 update
+ this_update_precision = 8;
+ test_pos_parent.quantize8(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT);
+ new_pos_parent.mV[VX] = U8_to_F32(data[0], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VY] = U8_to_F32(data[1], -0.5f*size, 1.5f*size);
+ new_pos_parent.mV[VZ] = U8_to_F32(data[2], MIN_HEIGHT, MAX_HEIGHT);
+
+ setVelocity(U8_to_F32(data[3], -size, size),
+ U8_to_F32(data[4], -size, size),
+ U8_to_F32(data[5], -size, size) );
+
+ setAcceleration(U8_to_F32(data[6], -size, size),
+ U8_to_F32(data[7], -size, size),
+ U8_to_F32(data[8], -size, size) );
+
+ new_rot.mQ[VX] = U8_to_F32(data[9], -1.f, 1.f);
+ new_rot.mQ[VY] = U8_to_F32(data[10], -1.f, 1.f);
+ new_rot.mQ[VZ] = U8_to_F32(data[11], -1.f, 1.f);
+ new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f);
+
+ setAngularVelocity( U8_to_F32(data[13], -size, size),
+ U8_to_F32(data[14], -size, size),
+ U8_to_F32(data[15], -size, size) );
+ break;
+ }
+
+ U8 state;
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num );
+ mState = state;
+ break;
+ }
+
+ default:
+ break;
+
+ }
+ }
+ else
+ {
+ // handle the compressed case
+ LLUUID sound_uuid;
+ LLUUID owner_id;
+ F32 gain = 0;
+ U8 sound_flags = 0;
+ F32 cutoff = 0;
+
+ U16 val[4];
+
+ U8 state;
+
+ dp->unpackU8(state, "State");
+ mState = state;
+
+ switch(update_type)
+ {
+ case OUT_TERSE_IMPROVED:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "CompTI:" << getID() << llendl;
+#endif
+ U8 value;
+ dp->unpackU8(value, "agent");
+ if (value)
+ {
+ LLVector4 collision_plane;
+ dp->unpackVector4(collision_plane, "Plane");
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ }
+ test_pos_parent = getPosition();
+ dp->unpackVector3(new_pos_parent, "Pos");
+ dp->unpackU16(val[VX], "VelX");
+ dp->unpackU16(val[VY], "VelY");
+ dp->unpackU16(val[VZ], "VelZ");
+ setVelocity(U16_to_F32(val[VX], -128.f, 128.f),
+ U16_to_F32(val[VY], -128.f, 128.f),
+ U16_to_F32(val[VZ], -128.f, 128.f));
+ dp->unpackU16(val[VX], "AccX");
+ dp->unpackU16(val[VY], "AccY");
+ dp->unpackU16(val[VZ], "AccZ");
+ setAcceleration(U16_to_F32(val[VX], -64.f, 64.f),
+ U16_to_F32(val[VY], -64.f, 64.f),
+ U16_to_F32(val[VZ], -64.f, 64.f));
+
+ dp->unpackU16(val[VX], "ThetaX");
+ dp->unpackU16(val[VY], "ThetaY");
+ dp->unpackU16(val[VZ], "ThetaZ");
+ dp->unpackU16(val[VS], "ThetaS");
+ new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f);
+ new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f);
+ new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f);
+ new_rot.mQ[VS] = U16_to_F32(val[VS], -1.f, 1.f);
+ dp->unpackU16(val[VX], "AccX");
+ dp->unpackU16(val[VY], "AccY");
+ dp->unpackU16(val[VZ], "AccZ");
+ setAngularVelocity( U16_to_F32(val[VX], -64.f, 64.f),
+ U16_to_F32(val[VY], -64.f, 64.f),
+ U16_to_F32(val[VZ], -64.f, 64.f));
+ }
+ break;
+ case OUT_FULL_COMPRESSED:
+ case OUT_FULL_CACHED:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "CompFull:" << getID() << llendl;
+#endif
+ dp->unpackU32(crc, "CRC");
+ mTotalCRC = crc;
+ dp->unpackU8(material, "Material");
+ U8 old_material = getMaterial();
+ if (old_material != material)
+ {
+ setMaterial(material);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ dp->unpackU8(click_action, "ClickAction");
+ setClickAction(click_action);
+ dp->unpackVector3(new_scale, "Scale");
+ dp->unpackVector3(new_pos_parent, "Pos");
+ LLVector3 vec;
+ dp->unpackVector3(vec, "Rot");
+ new_rot.unpackFromVector3(vec);
+ setAcceleration(LLVector3::zero);
+
+ U32 value;
+ dp->unpackU32(value, "SpecialCode");
+
+ dp->setPassFlags(value);
+
+
+ if (value & 0x80)
+ {
+ dp->unpackVector3(vec, "Omega");
+ setAngularVelocity(vec);
+ }
+
+ if (value & 0x20)
+ {
+ dp->unpackU32(parent_id, "ParentID");
+ }
+ else
+ {
+ parent_id = 0;
+ }
+
+ S32 sp_size;
+ U32 size;
+ if (value & 0x2)
+ {
+ sp_size = 1;
+ delete [] mData;
+ mData = new U8[1];
+ dp->unpackU8(((U8*)mData)[0], "TreeData");
+ }
+ else if (value & 0x1)
+ {
+ dp->unpackU32(size, "ScratchPadSize");
+ delete [] mData;
+ mData = new U8[size];
+ dp->unpackBinaryData((U8 *)mData, sp_size, "PartData");
+ }
+ else
+ {
+ mData = NULL;
+ }
+
+ // Setup object text
+ if (!mText)
+ {
+ mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mText->setFont(LLFontGL::sSansSerif);
+ mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mText->setMaxLines(-1); // Set to match current agni behavior.
+ mText->setSourceObject(this);
+ mText->setOnHUDAttachment(isHUDAttachment());
+ }
+
+ if (value & 0x4)
+ {
+ char temp_string[256]; // not MAX_STRING, must hold 255 chars + \0
+ dp->unpackString(temp_string, "Text");
+ LLColor4U coloru;
+ dp->unpackBinaryDataFixed(coloru.mV, 4, "Color");
+ coloru.mV[3] = 255 - coloru.mV[3];
+ mText->setColor(LLColor4(coloru));
+ mText->setStringUTF8(temp_string);
+
+ setChanged(TEXTURE);
+ }
+ else
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+
+ if (value & 0x200)
+ {
+ char media_url[MAX_STRING+1];
+ dp->unpackString(media_url, "MediaURL");
+ //if (media_url[0])
+ //{
+ // llinfos << "WEBONPRIM media_url " << media_url << llendl;
+ //}
+ if (!mMedia)
+ {
+ retval |= MEDIA_URL_ADDED;
+ mMedia = new LLViewerObjectMedia;
+ mMedia->mMediaURL = media_url;
+ mMedia->mMediaType = LLViewerObject::MEDIA_TYPE_WEB_PAGE;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ else if (mMedia->mMediaURL != media_url)
+ {
+ retval |= MEDIA_URL_UPDATED;
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ }
+ else if (mMedia)
+ {
+ retval |= MEDIA_URL_REMOVED;
+ delete mMedia;
+ mMedia = NULL;
+ }
+
+ //
+ // Unpack particle system data
+ //
+ if (value & 0x8)
+ unpackParticleSource(*dp, owner_id);
+
+ // Mark all extra parameters not used
+ std::map<U16, ExtraParameter*>::iterator iter;
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ iter->second->in_use = FALSE;
+ }
+
+ // Unpack extra params
+ U8 num_parameters;
+ dp->unpackU8(num_parameters, "num_params");
+ U8 param_block[MAX_OBJECT_PARAMS_SIZE];
+ for (U8 param=0; param<num_parameters; ++param)
+ {
+ U16 param_type;
+ S32 param_size;
+ dp->unpackU16(param_type, "param_type");
+ dp->unpackBinaryData(param_block, param_size, "param_data");
+ //llinfos << "Param type: " << param_type << ", Size: " << param_size << llendl;
+ LLDataPackerBinaryBuffer dp2(param_block, param_size);
+ unpackParameterEntry(param_type, &dp2);
+ }
+
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ if (!iter->second->in_use)
+ {
+ // Send an update message in case it was formerly in use
+ parameterChanged(iter->first, iter->second->data, FALSE, false);
+ }
+ }
+
+ if (value & 0x10)
+ {
+ dp->unpackUUID(sound_uuid, "SoundUUID");
+ dp->unpackUUID(owner_id, "OwnerID");
+ dp->unpackF32(gain, "SoundGain");
+ dp->unpackU8(sound_flags, "SoundFlags");
+ dp->unpackF32(cutoff, "SoundRadius");
+ }
+
+ if (value & 0x100)
+ {
+ char name_value_list[2048];
+ dp->unpackString(name_value_list, "NV");
+
+ setNameValueList(name_value_list);
+ }
+
+ mTotalCRC = crc;
+
+ setAttachedSound(sound_uuid, owner_id, gain, sound_flags);
+
+ // only get these flags on updates from sim, not cached ones
+ // Preload these five flags for every object.
+ // Finer shades require the object to be selected, and the selection manager
+ // stores the extended permission info.
+ U32 flags;
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num);
+ // keep local flags and overwrite remote-controlled flags
+ mFlags = (mFlags & FLAGS_LOCAL) | flags;
+
+ // ...new objects that should come in selected need to be added to the selected list
+ mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0);
+
+ // Set the change flags for scale
+ if (new_scale != getScale())
+ {
+ setChanged(SCALED | SILHOUETTE);
+ setScale(new_scale); // Must follow setting permYouOwner()
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ //
+ // Fix object parenting.
+ //
+ BOOL b_changed_status = FALSE;
+
+ if (OUT_TERSE_IMPROVED != update_type)
+ {
+ // We only need to update parenting on full updates, terse updates
+ // don't send parenting information.
+ if (!cur_parentp)
+ {
+ if (parent_id == 0)
+ {
+ // No parent now, no parent in message -> do nothing
+ }
+ else
+ {
+ // No parent now, new parent in message -> attach to that parent if possible
+ LLUUID parent_uuid;
+ LLViewerObjectList::getUUIDFromLocal(parent_uuid,
+ parent_id,
+ mesgsys->getSenderIP(),
+ mesgsys->getSenderPort());
+
+ LLViewerObject *sent_parentp = gObjectList.findObject(parent_uuid);
+
+ //
+ // Check to see if we have the corresponding viewer object for the parent.
+ //
+ if (sent_parentp && sent_parentp->getParent() == this)
+ {
+ // Try to recover if we attempt to attach a parent to its child
+ llwarns << "Attempt to attach a parent to it's child: " << this->getID() << " to " << sent_parentp->getID() << llendl;
+ this->removeChild(sent_parentp);
+ sent_parentp->setDrawableParent(NULL);
+ }
+
+ if (sent_parentp && (sent_parentp != this) && !sent_parentp->isDead())
+ {
+ //
+ // We have a viewer object for the parent, and it's not dead.
+ // Do the actual reparenting here.
+ //
+
+ // new parent is valid
+ b_changed_status = TRUE;
+ // ...no current parent, so don't try to remove child
+ if (mDrawable.notNull())
+ {
+ if (mDrawable->isDead() || !mDrawable->getVObj())
+ {
+ llwarns << "Drawable is dead or no VObj!" << llendl;
+ sent_parentp->addChild(this);
+ }
+ else
+ {
+ if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 1
+ {
+ // Bad, we got a cycle somehow.
+ // Kill both the parent and the child, and
+ // set cache misses for both of them.
+ llwarns << "Attempting to recover from parenting cycle!" << llendl
+ llwarns << "Killing " << sent_parentp->getID() << " and " << getID() << llendl;
+ llwarns << "Adding to cache miss list" << llendl;
+ setParent(NULL);
+ sent_parentp->setParent(NULL);
+ getRegion()->addCacheMissFull(getLocalID());
+ getRegion()->addCacheMissFull(sent_parentp->getLocalID());
+ gObjectList.killObject(sent_parentp);
+ gObjectList.killObject(this);
+ return retval;
+ }
+ sent_parentp->addChild(this);
+ // make sure this object gets a non-damped update
+ if (sent_parentp->mDrawable.notNull())
+ {
+ gPipeline.markMoved(sent_parentp->mDrawable, FALSE); // undamped
+ }
+ }
+ }
+ else
+ {
+ sent_parentp->addChild(this);
+ }
+
+ setChanged(MOVED | SILHOUETTE);
+ }
+ else
+ {
+ //
+ // No corresponding viewer object for the parent, put the various
+ // pieces on the orphan list.
+ //
+
+ //parent_id
+ U32 ip = mesgsys->getSenderIP();
+ U32 port = mesgsys->getSenderPort();
+
+ gObjectList.orphanize(this, parent_id, ip, port);
+ }
+ }
+ }
+ else
+ {
+ // BUG: this is a bad assumption once border crossing is alowed
+ if ( (parent_id == cur_parentp->mLocalID)
+ &&(update_type == OUT_TERSE_IMPROVED))
+ {
+ // Parent now, same parent in message -> do nothing
+
+ // Debugging for suspected problems with local ids.
+ //LLUUID parent_uuid;
+ //LLViewerObjectList::getUUIDFromLocal(parent_uuid, parent_id, mesgsys->getSenderIP(), mesgsys->getSenderPort() );
+ //if (parent_uuid != cur_parentp->getID() )
+ //{
+ // llerrs << "Local ID match but UUID mismatch of viewer object" << llendl;
+ //}
+ }
+ else
+ {
+ // Parented now, different parent in message
+ LLViewerObject *sent_parentp;
+ if (parent_id == 0)
+ {
+ //
+ // This object is no longer parented, we sent in a zero parent ID.
+ //
+ sent_parentp = NULL;
+ }
+ else
+ {
+ LLUUID parent_uuid;
+ LLViewerObjectList::getUUIDFromLocal(parent_uuid,
+ parent_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ sent_parentp = gObjectList.findObject(parent_uuid);
+
+ if (isAvatar())
+ {
+ // This logic is meant to handle the case where a sitting avatar has reached a new sim
+ // ahead of the object she was sitting on (which is common as objects are transfered through
+ // a slower route than agents)...
+ // In this case, the local id for the object will not be valid, since the viewer has not received
+ // a full update for the object from that sim yet, so we assume that the agent is still sitting
+ // where she was originally. --RN
+ if (!sent_parentp)
+ {
+ sent_parentp = cur_parentp;
+ }
+ }
+ else if (!sent_parentp)
+ {
+ //
+ // Switching parents, but we don't know the new parent.
+ //
+ U32 ip = mesgsys->getSenderIP();
+ U32 port = mesgsys->getSenderPort();
+
+ // We're an orphan, flag things appropriately.
+ gObjectList.orphanize(this, parent_id, ip, port);
+ }
+ }
+
+ // Reattach if possible.
+ if (sent_parentp && sent_parentp != cur_parentp && sent_parentp != this)
+ {
+ // New parent is valid, detach and reattach
+ b_changed_status = TRUE;
+ if (mDrawable.notNull())
+ {
+ if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 2
+ {
+ // Bad, we got a cycle somehow.
+ // Kill both the parent and the child, and
+ // set cache misses for both of them.
+ llwarns << "Attempting to recover from parenting cycle!" << llendl
+ llwarns << "Killing " << sent_parentp->getID() << " and " << getID() << llendl;
+ llwarns << "Adding to cache miss list" << llendl;
+ setParent(NULL);
+ sent_parentp->setParent(NULL);
+ getRegion()->addCacheMissFull(getLocalID());
+ getRegion()->addCacheMissFull(sent_parentp->getLocalID());
+ gObjectList.killObject(sent_parentp);
+ gObjectList.killObject(this);
+ return retval;
+ }
+ // make sure this object gets a non-damped update
+ }
+ cur_parentp->removeChild(this);
+ sent_parentp->addChild(this);
+ setChanged(MOVED | SILHOUETTE);
+ sent_parentp->setChanged(MOVED | SILHOUETTE);
+ if (sent_parentp->mDrawable.notNull())
+ {
+ gPipeline.markMoved(sent_parentp->mDrawable, FALSE); // undamped
+ }
+ }
+ else if (!sent_parentp)
+ {
+ bool remove_parent = true;
+ // No new parent, or the parent that we sent doesn't exist on the viewer.
+ LLViewerObject *parentp = (LLViewerObject *)getParent();
+ if (parentp)
+ {
+ if (parentp->getRegion() != 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).
+ //llinfos << "Don't reparent object handoffs!" << llendl;
+ remove_parent = false;
+ }
+ }
+
+ if (remove_parent)
+ {
+ b_changed_status = TRUE;
+ if (mDrawable.notNull())
+ {
+ // clear parent to removeChild can put the drawable on the damped list
+ setDrawableParent(NULL); // LLViewerObject::processUpdateMessage 3
+ }
+
+ cur_parentp->removeChild(this);
+
+ if (mJointInfo && !parent_id)
+ {
+ // since this object is no longer parent-relative
+ // we make sure we delete any joint info
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ setChanged(MOVED | SILHOUETTE);
+
+ if (mDrawable.notNull())
+ {
+ // make sure this object gets a non-damped update
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ }
+ }
+ }
+ }
+
+ new_rot.normQuat();
+
+ if (gPingInterpolate)
+ {
+ LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mesgsys->getSender());
+ F32 ping_delay = 0.5f * mTimeDilation * ( ((F32)cdp->getPingDelay()) * 0.001f + gFrameDTClamped);
+ LLVector3 diff = getVelocity() * (0.5f*mTimeDilation*(gFrameDTClamped + ((F32)ping_delay)*0.001f));
+ new_pos_parent += diff;
+ }
+
+ //////////////////////////
+ //
+ // Set the generic change flags...
+ //
+ //
+
+ // first, let's see if the new position is actually a change
+
+ //static S32 counter = 0;
+
+ F32 vel_mag_sq = getVelocity().magVecSquared();
+ F32 accel_mag_sq = getAcceleration().magVecSquared();
+
+ if ( ((b_changed_status)||(test_pos_parent != new_pos_parent))
+ ||( (!isSelected())
+ &&( (vel_mag_sq != 0.f)
+ ||(accel_mag_sq != 0.f)
+ ||(this_update_precision > mBestUpdatePrecision))))
+ {
+ mBestUpdatePrecision = this_update_precision;
+ setPositionParent(new_pos_parent);
+
+ if (mParent && ((LLViewerObject*)mParent)->isAvatar())
+ {
+ // we have changed the position of an attachment, so we need to clamp it
+ LLVOAvatar *avatar = (LLVOAvatar*)mParent;
+
+ avatar->clampAttachmentPositions();
+ }
+ }
+
+ if (new_rot != mLastRot)
+ {
+ // if (getAngularVelocity().isExactlyZero() ||
+ // new_angv != getAngularVelocity())
+ {
+ mLastRot = new_rot;
+ setChanged(ROTATED | SILHOUETTE);
+ setRotation(new_rot);
+ resetRot();
+ }
+ }
+
+
+ if ( gShowObjectUpdates )
+ {
+ if (!((mPrimitiveCode == LL_PCODE_LEGACY_AVATAR) && (((LLVOAvatar *) this)->mIsSelf))
+ && mRegionp)
+ {
+ LLViewerObject* object = gObjectList.createObjectViewer(LL_PCODE_LEGACY_TEXT_BUBBLE, mRegionp);
+ LLVOTextBubble* bubble = (LLVOTextBubble*) object;
+
+ if (update_type == OUT_TERSE_IMPROVED)
+ {
+ bubble->mColor.setVec(0.f, 0.f, 1.f, 1.f);
+ }
+ else
+ {
+ bubble->mColor.setVec(1.f, 0.f, 0.f, 1.f);
+ }
+ object->setPositionGlobal(getPositionGlobal());
+ gPipeline.addObject(object);
+ }
+ }
+
+ if ((0.0f == vel_mag_sq) &&
+ (0.0f == accel_mag_sq) &&
+ (0.0f == getAngularVelocity().magVecSquared()))
+ {
+ mStatic = TRUE; // This object doesn't move!
+ }
+ else
+ {
+ mStatic = FALSE;
+ }
+
+// BUG: This code leads to problems during group rotate and any scale operation.
+// Small discepencies between the simulator and viewer representations cause the
+// selection center to creep, leading to objects moving around the wrong center.
+//
+// Removing this, however, means that if someone else drags an object you have
+// selected, your selection center and dialog boxes will be wrong. It also means
+// that higher precision information on selected objects will be ignored.
+//
+// I believe the group rotation problem is fixed. JNC 1.21.2002
+//
+ // Additionally, if any child is selected, need to update the dialogs and selection
+ // center.
+ BOOL needs_refresh = mUserSelected;
+ LLViewerObject *childp;
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ childp = mChildList[i];
+ needs_refresh = needs_refresh || childp->mUserSelected;
+ }
+
+ if (needs_refresh)
+ {
+ gSelectMgr->updateSelectionCenter();
+ dialog_refresh_all();
+ }
+
+
+ // Mark update time as approx. now, with the ping delay.
+ // Ping delay is off because it's not set for velocity interpolation, causing
+ // much jumping and hopping around...
+
+// U32 ping_delay = mesgsys->mCircuitInfo.getPingDelay();
+ mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds();
+ mLastMessageUpdateSecs = LLFrameTimer::getElapsedSeconds();
+ if (mDrawable.notNull())
+ {
+ // Don't clear invisibility flag on update if still orphaned!
+ if (mDrawable->isState(LLDrawable::FORCE_INVISIBLE) && !mOrphaned)
+ {
+// lldebugs << "Clearing force invisible: " << mID << ":" << getPCodeString() << ":" << getPositionAgent() << llendl;
+ mDrawable->setState(LLDrawable::CLEAR_INVISIBLE);
+ }
+ }
+
+ // Update special hover cursor status
+ bool special_hover_cursor = specialHoverCursor();
+ if (old_special_hover_cursor != special_hover_cursor
+ && mDrawable.notNull())
+ {
+ mDrawable->updateSpecialHoverCursor(special_hover_cursor);
+ }
+
+ return retval;
+}
+
+BOOL LLViewerObject::isActive() const
+{
+ return TRUE;
+}
+
+BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mDead)
+ {
+ // It's dead. Don't update it.
+ return TRUE;
+ }
+
+ // CRO - don't velocity interp linked objects!
+ // Leviathan - but DO velocity interp joints
+ if (!mStatic && gVelocityInterpolate && !isSelected())
+ {
+ // calculate dt from last update
+ F32 dt_raw = (F32)(time - mLastInterpUpdateSecs);
+ F32 dt = mTimeDilation * dt_raw;
+
+ if (!mUserSelected && !mJointInfo)
+ {
+ applyAngularVelocity(dt);
+ }
+
+ LLViewerObject *parentp = (LLViewerObject *) getParent();
+ if (mJointInfo)
+ {
+ if (parentp)
+ {
+ // do parent-relative stuff
+ LLVector3 ang_vel = getAngularVelocity();
+ F32 omega = ang_vel.magVecSquared();
+ F32 angle = 0.0f;
+ LLQuaternion dQ;
+ if (omega > 0.00001f)
+ {
+ omega = sqrt(omega);
+ angle = omega * dt;
+ dQ.setQuat(angle, ang_vel);
+ }
+ LLVector3 pos = getPosition();
+
+ if (HJT_HINGE == mJointInfo->mJointType)
+ {
+ // hinge = uniform circular motion
+ LLVector3 parent_pivot = getVelocity();
+ LLVector3 parent_axis = getAcceleration();
+
+ angle = dt * (ang_vel * mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis
+ dQ.setQuat(angle, mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis
+ LLVector3 pivot_offset = pos - mJointInfo->mPivot; // pos in pivot-frame
+ pivot_offset = pivot_offset * dQ; // new rotated pivot-frame pos
+ pos = mJointInfo->mPivot + pivot_offset; // parent-frame
+ LLViewerObject::setPosition(pos);
+ LLQuaternion Q_PC = getRotation();
+ setRotation(Q_PC * dQ);
+ mLastInterpUpdateSecs = time;
+ }
+ else if (HJT_POINT == mJointInfo->mJointType)
+ // || HJT_LPOINT == mJointInfo->mJointType)
+ {
+ // point-to-point = spin about axis and uniform circular motion
+ // of axis about the pivot point
+ //
+ // NOTE: this interpolation scheme is not quite good enough to
+ // reduce the bandwidth -- needs a gravitational correction.
+ // Similarly for hinges with axes that deviate from vertical.
+
+ LLQuaternion Q_PC = getRotation();
+ Q_PC = Q_PC * dQ;
+ setRotation(Q_PC);
+
+ LLVector3 pivot_to_child = - mJointInfo->mAxisOrAnchor; // AxisOrAnchor = anchor
+ pos = mJointInfo->mPivot + pivot_to_child * Q_PC;
+ LLViewerObject::setPosition(pos);
+ mLastInterpUpdateSecs = time;
+ }
+ /* else if (HJT_WHEEL == mJointInfo->mJointInfo)
+ {
+ // wheel = uniform rotation about axis, with linear
+ // velocity interpolation (if any)
+ LLVector3 parent_axis = getAcceleration(); // HACK -- accel stores the parent-axis (parent-frame)
+
+ LLQuaternion Q_PC = getRotation();
+
+ angle = dt * (parent_axis * ang_vel);
+ dQ.setQuat(angle, parent_axis);
+
+ Q_PC = Q_PC * dQ;
+ setRotation(Q_PC);
+
+ pos = getPosition() + dt * getVelocity();
+ LLViewerObject::setPosition(pos);
+ mLastInterpUpdateSecs = time;
+ }*/
+ }
+ }
+ else if (isAttachment())
+ {
+ mLastInterpUpdateSecs = time;
+ return TRUE;
+ }
+ else
+ {
+ // linear motion
+ // HAVOK_TIMESTEP is used below to correct for the fact that the velocity in object
+ // updates represents the average velocity of the last timestep, rather than the final velocity.
+ // the time dilation above should guarrantee that dt is never less than HAVOK_TIMESTEP, theoretically
+ //
+ // There is a problem here if dt is negative. . .
+
+ //FIXME: should also wrap linear accel/velocity in check
+ // to see if object is selected, instead of explicitly
+ // zeroing it out
+ LLVector3 accel = getAcceleration();
+ LLVector3 vel = getVelocity();
+
+ if (!(accel.isExactlyZero() && vel.isExactlyZero()))
+ {
+ LLVector3 pos = (vel + (0.5f * (dt-HAVOK_TIMESTEP)) * accel) * dt;
+
+ // region local
+ setPositionRegion(pos + getPositionRegion());
+ setVelocity(vel + accel*dt);
+
+ // for objects that are spinning but not translating, make sure to flag them as having moved
+ setChanged(MOVED | SILHOUETTE);
+ }
+
+ mLastInterpUpdateSecs = time;
+ }
+ }
+
+ if (gNoRender)
+ {
+ // Skip drawable stuff if not rendering.
+ return TRUE;
+ }
+
+ updateDrawable(FALSE);
+
+ return TRUE;
+}
+
+
+BOOL LLViewerObject::setData(const U8 *datap, const U32 data_size)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ delete [] mData;
+
+ if (datap)
+ {
+ mData = new U8[data_size];
+ if (!mData)
+ {
+ return FALSE;
+ }
+ memcpy(mData, datap, data_size);
+ }
+ return TRUE;
+}
+
+// delete an item in the inventory, but don't tell the server. This is
+// used internally by remove, update, and savescript.
+// This will only delete the first item with an item_id in the list
+void LLViewerObject::deleteInventoryItem(const LLUUID& item_id)
+{
+ if(mInventory)
+ {
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for( ; it != end; ++it )
+ {
+ if((*it)->getUUID() == item_id)
+ {
+ // This is safe only because we return immediatly.
+ mInventory->erase(it); // will deref and delete it
+ return;
+ }
+ }
+ doInventoryCallback();
+ }
+}
+
+void LLViewerObject::doUpdateInventory(
+ LLViewerInventoryItem* item,
+ U8 key,
+ bool is_new)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ LLViewerInventoryItem* old_item = NULL;
+ if(TASK_INVENTORY_ITEM_KEY == key)
+ {
+ old_item = (LLViewerInventoryItem*)getInventoryObject(item->getUUID());
+ }
+ else if(TASK_INVENTORY_ASSET_KEY == key)
+ {
+ old_item = getInventoryItemByAsset(item->getAssetUUID());
+ }
+ LLUUID item_id;
+ LLUUID new_owner;
+ LLUUID new_group;
+ BOOL group_owned = FALSE;
+ if(old_item)
+ {
+ item_id = old_item->getUUID();
+ new_owner = old_item->getPermissions().getOwner();
+ new_group = old_item->getPermissions().getGroup();
+ group_owned = old_item->getPermissions().isGroupOwned();
+ old_item = NULL;
+ }
+ else
+ {
+ item_id = item->getUUID();
+ }
+ if(!is_new && mInventory)
+ {
+ // Attempt to update the local inventory. If we can get the
+ // object perm, we have perfect visibility, so we want the
+ // serial number to match. Otherwise, take our best guess and
+ // make sure that the serial number does not match.
+ deleteInventoryItem(item_id);
+ LLPermissions perm(item->getPermissions());
+ LLPermissions* obj_perm = gSelectMgr->findObjectPermissions(this);
+ bool is_atomic = ((S32)LLAssetType::AT_OBJECT == item->getType()) ? false : true;
+ if(obj_perm)
+ {
+ perm.setOwnerAndGroup(LLUUID::null, obj_perm->getOwner(), obj_perm->getGroup(), is_atomic);
+ }
+ else
+ {
+ if(group_owned)
+ {
+ perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic);
+ }
+ else if(!new_owner.isNull())
+ {
+ // The object used to be in inventory, so we can
+ // assume the owner and group will match what they are
+ // there.
+ perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic);
+ }
+ // *FIX: can make an even better guess by using the mPermGroup flags
+ else if(permYouOwner())
+ {
+ // best guess.
+ perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), item->getPermissions().getGroup(), is_atomic);
+ --mInventorySerialNum;
+ }
+ else
+ {
+ // dummy it up.
+ perm.setOwnerAndGroup(LLUUID::null, LLUUID::null, LLUUID::null, is_atomic);
+ --mInventorySerialNum;
+ }
+ }
+ LLViewerInventoryItem* new_item = new LLViewerInventoryItem(item);
+ new_item->setPermissions(perm);
+ mInventory->push_front(new_item);
+ doInventoryCallback();
+ ++mInventorySerialNum;
+ }
+}
+
+// save a script, which involves removing the old one, and rezzing
+// in the new one. This method should be called with the asset id
+// of the new and old script AFTER the bytecode has been saved.
+void LLViewerObject::saveScript(
+ const LLViewerInventoryItem* item,
+ BOOL active,
+ bool is_new)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ /*
+ * XXXPAM Investigate not making this copy. Seems unecessary, but I'm unsure about the
+ * interaction with doUpdateInventory() called below.
+ */
+ lldebugs << "LLViewerObject::saveScript() " << item->getUUID() << " " << item->getAssetUUID() << llendl;
+ LLPointer<LLViewerInventoryItem> task_item =
+ new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(),
+ item->getAssetUUID(), item->getType(),
+ item->getInventoryType(),
+ item->getName(), item->getDescription(),
+ item->getSaleInfo(), item->getFlags(),
+ item->getCreationDate());
+ task_item->setTransactionID(item->getTransactionID());
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RezScript);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+ msg->nextBlockFast(_PREHASH_UpdateBlock);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, (mLocalID));
+ U8 enabled = active;
+ msg->addBOOLFast(_PREHASH_Enabled, enabled);
+ msg->nextBlockFast(_PREHASH_InventoryBlock);
+ task_item->packMessage(msg);
+ msg->sendReliable(mRegionp->getHost());
+
+ // do the internal logic
+ doUpdateInventory(task_item, TASK_INVENTORY_ITEM_KEY, is_new);
+}
+
+void LLViewerObject::moveInventory(const LLUUID& folder_id,
+ const LLUUID& item_id)
+{
+ lldebugs << "LLViewerObject::moveInventory " << item_id << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoveTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_FolderID, folder_id);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ msg->sendReliable(mRegionp->getHost());
+
+ LLInventoryObject* inv_obj = getInventoryObject(item_id);
+ if(inv_obj)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_obj;
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ deleteInventoryItem(item_id);
+ ++mInventorySerialNum;
+ }
+ }
+}
+
+void LLViewerObject::dirtyInventory()
+{
+ // If there aren't any LLVOInventoryListeners, we won't be
+ // able to update our mInventory when it comes back from the
+ // simulator, so we should not clear the inventory either.
+ if(mInventory && !mInventoryCallbacks.isEmpty())
+ {
+ mInventory->clear(); // will deref and delete entries
+ delete mInventory;
+ mInventory = NULL;
+ mInventoryDirty = TRUE;
+ }
+}
+
+void LLViewerObject::registerInventoryListener(LLVOInventoryListener* listener, void* user_data)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ LLInventoryCallbackInfo* info = new LLInventoryCallbackInfo;
+ info->mListener = listener;
+ info->mInventoryData = user_data;
+ mInventoryCallbacks.addData(info);
+}
+
+void LLViewerObject::removeInventoryListener(LLVOInventoryListener* listener)
+{
+ if (listener == NULL) return;
+ LLInventoryCallbackInfo* info;
+ for (info = mInventoryCallbacks.getFirstData();
+ info;
+ info = mInventoryCallbacks.getNextData() )
+ {
+ if (info->mListener == listener)
+ {
+ mInventoryCallbacks.deleteCurrentData();
+ break;
+ }
+ }
+}
+
+void LLViewerObject::clearInventoryListeners()
+{
+ mInventoryCallbacks.deleteAllData();
+}
+
+void LLViewerObject::requestInventory()
+{
+ mInventoryDirty = FALSE;
+ if(mInventory)
+ {
+ //mInventory->clear() // will deref and delete it
+ //delete mInventory;
+ //mInventory = NULL;
+ doInventoryCallback();
+ }
+ // throw away duplicate requests
+ else if (! mInventoryPending)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RequestTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->sendReliable(mRegionp->getHost());
+
+ // this will get reset by dirtyInventory or doInventoryCallback
+ mInventoryPending = TRUE;
+ }
+}
+
+struct LLFilenameAndTask
+{
+ LLUUID mTaskID;
+ char mFilename[MAX_STRING]; // Just the filename, not the path
+#ifdef _DEBUG
+ static S32 sCount;
+ LLFilenameAndTask()
+ {
+ ++sCount;
+ lldebugs << "Constructing LLFilenameAndTask: " << sCount << llendl;
+ }
+ ~LLFilenameAndTask()
+ {
+ --sCount;
+ lldebugs << "Destroying LLFilenameAndTask: " << sCount << llendl;
+ }
+private:
+ LLFilenameAndTask(const LLFilenameAndTask& rhs);
+ const LLFilenameAndTask& operator=(const LLFilenameAndTask& rhs) const;
+#endif
+};
+
+#ifdef _DEBUG
+S32 LLFilenameAndTask::sCount = 0;
+#endif
+
+// static
+void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ LLUUID task_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_TaskID, task_id);
+ LLViewerObject* object = gObjectList.findObject(task_id);
+ if(object)
+ {
+ msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, object->mInventorySerialNum);
+ LLFilenameAndTask* ft = new LLFilenameAndTask;
+ ft->mTaskID = task_id;
+ msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, MAX_STRING, ft->mFilename);
+ if(!ft->mFilename[0])
+ {
+ lldebugs << "Task has no inventory" << llendl;
+ // mock up some inventory to make a drop target.
+ if(object->mInventory)
+ {
+ object->mInventory->clear(); // will deref and delete it
+ }
+ else
+ {
+ object->mInventory = new InventoryObjectList();
+ }
+ LLPointer<LLInventoryObject> obj;
+ obj = new LLInventoryObject(object->mID, LLUUID::null,
+ LLAssetType::AT_CATEGORY,
+ "Contents");
+ object->mInventory->push_front(obj);
+ object->doInventoryCallback();
+ delete ft;
+ return;
+ }
+ gXferManager->requestFile(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ft->mFilename).c_str(),
+ ft->mFilename, LL_PATH_CACHE,
+ object->mRegionp->getHost(),
+ TRUE,
+ &LLViewerObject::processTaskInvFile,
+ (void**)ft,
+ LLXferManager::HIGH_PRIORITY);
+ }
+}
+
+void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code)
+{
+ LLFilenameAndTask* ft = (LLFilenameAndTask*)user_data;
+ LLViewerObject* object = NULL;
+ if(ft && (0 == error_code) &&
+ (object = gObjectList.findObject(ft->mTaskID)))
+ {
+ object->loadTaskInvFile(ft->mFilename);
+ }
+ else
+ {
+ // This Occurs When to requests were made, and the first one
+ // has already handled it.
+ lldebugs << "Problem loading task inventory. Return code: "
+ << error_code << llendl;
+ }
+ delete ft;
+}
+
+void LLViewerObject::loadTaskInvFile(const char* filename)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ std::string filename_and_local_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, filename);
+ llifstream ifs(filename_and_local_path.c_str());
+ if(ifs.good())
+ {
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ if(mInventory)
+ {
+ mInventory->clear(); // will deref and delete it
+ }
+ else
+ {
+ mInventory = new InventoryObjectList;
+ }
+ while(ifs.good())
+ {
+ ifs.getline(buffer, MAX_STRING);
+ sscanf(buffer, " %s", keyword);
+ if(0 == strcmp("inv_item", keyword))
+ {
+ LLPointer<LLInventoryObject> inv = new LLViewerInventoryItem;
+ inv->importLegacyStream(ifs);
+ mInventory->push_front(inv);
+ }
+ else if(0 == strcmp("inv_object", keyword))
+ {
+ LLPointer<LLInventoryObject> inv = new LLInventoryObject;
+ inv->importLegacyStream(ifs);
+ mInventory->push_front(inv);
+ }
+ else
+ {
+ llwarns << "Unknown token in inventory file '"
+ << keyword << "'" << llendl;
+ }
+ }
+ ifs.close();
+ LLFile::remove(filename_and_local_path.c_str());
+ }
+ else
+ {
+ llwarns << "unable to load task inventory: " << filename_and_local_path
+ << llendl;
+ }
+ doInventoryCallback();
+}
+
+void LLViewerObject::doInventoryCallback()
+{
+ for(LLInventoryCallbackInfo* info = mInventoryCallbacks.getFirstData();
+ info != NULL;
+ info = mInventoryCallbacks.getNextData())
+ {
+ if (info->mListener != NULL)
+ {
+ info->mListener->inventoryChanged(this,
+ mInventory,
+ mInventorySerialNum,
+ info->mInventoryData);
+ }
+ else
+ {
+ llinfos << "LLViewerObject::doInventoryCallback() deleting bad listener entry." << llendl;
+ mInventoryCallbacks.deleteCurrentData();
+ }
+ }
+ mInventoryPending = FALSE;
+}
+
+void LLViewerObject::removeInventory(const LLUUID& item_id)
+{
+ // close any associated floater properties
+ LLFloaterProperties::closeByID(item_id, mID);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ msg->sendReliable(mRegionp->getHost());
+ deleteInventoryItem(item_id);
+ ++mInventorySerialNum;
+
+ // The viewer object should not refresh UI since this is a utility
+ // function. The UI functionality that called this method should
+ // refresh the views if necessary.
+ //gBuildView->refresh();
+}
+
+void LLViewerObject::updateInventory(
+ LLViewerInventoryItem* item,
+ U8 key,
+ bool is_new)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ // This slices the object into what we're concerned about on the
+ // viewer. The simulator will take the permissions and transfer
+ // ownership.
+ LLPointer<LLViewerInventoryItem> task_item =
+ new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(),
+ item->getAssetUUID(), item->getType(),
+ item->getInventoryType(),
+ item->getName(), item->getDescription(),
+ item->getSaleInfo(),
+ item->getFlags(),
+ item->getCreationDate());
+ task_item->setTransactionID(item->getTransactionID());
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UpdateTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_UpdateData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->addU8Fast(_PREHASH_Key, key);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ task_item->packMessage(msg);
+ msg->sendReliable(mRegionp->getHost());
+
+ // do the internal logic
+ doUpdateInventory(task_item, key, is_new);
+}
+
+LLInventoryObject* LLViewerObject::getInventoryObject(const LLUUID& item_id)
+{
+ LLInventoryObject* rv = NULL;
+ if(mInventory)
+ {
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getUUID() == item_id)
+ {
+ rv = *it;
+ break;
+ }
+ }
+ }
+ return rv;
+}
+
+void LLViewerObject::getInventoryContents(InventoryObjectList& objects)
+{
+ if(mInventory)
+ {
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for( ; it != end; ++it)
+ {
+ if ((*it)->getType() != LLAssetType::AT_CATEGORY)
+ {
+ objects.push_back(*it);
+ }
+ }
+ }
+}
+
+LLInventoryObject* LLViewerObject::getInventoryRoot()
+{
+ if (!mInventory || !mInventory->size())
+ {
+ return NULL;
+ }
+ return mInventory->back();
+}
+
+LLViewerInventoryItem* LLViewerObject::getInventoryItemByAsset(const LLUUID& asset_id)
+{
+ LLViewerInventoryItem* rv = NULL;
+ if(mInventory)
+ {
+ LLViewerInventoryItem* item = NULL;
+
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for( ; it != end; ++it)
+ {
+ LLInventoryObject* obj = *it;
+ if(obj->getType() != LLAssetType::AT_CATEGORY)
+ {
+ // *FIX: gank-ass down cast!
+ item = (LLViewerInventoryItem*)obj;
+ if(item->getAssetUUID() == asset_id)
+ {
+ rv = item;
+ break;
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+void LLViewerObject::setPixelAreaAndAngle(LLAgent &agent)
+{
+ if (getVolume())
+ { //volumes calculate pixel area and angle per face
+ return;
+ }
+
+ LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
+ LLVector3 pos_agent = getRenderPosition();
+
+ F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
+ F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
+ F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
+
+ F32 max_scale = getMaxScale();
+ F32 mid_scale = getMidScale();
+ F32 min_scale = getMinScale();
+
+ // IW: esitmate - when close to large objects, computing range based on distance from center is no good
+ // to try to get a min distance from face, subtract min_scale/2 from the range.
+ // This means we'll load too much detail sometimes, but that's better than not enough
+ // I don't think there's a better way to do this without calculating distance per-poly
+ F32 range = sqrt(dx*dx + dy*dy + dz*dz) - min_scale/2;
+
+ if (range < 0.001f || isHUDAttachment()) // range == zero
+ {
+ mAppAngle = 180.f;
+ mPixelArea = (F32)gCamera->getScreenPixelArea();
+ }
+ else
+ {
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+
+ F32 pixels_per_meter = gCamera->getPixelMeterRatio() / range;
+
+ mPixelArea = (pixels_per_meter * max_scale) * (pixels_per_meter * mid_scale);
+ if (mPixelArea > gCamera->getScreenPixelArea())
+ {
+ mAppAngle = 180.f;
+ mPixelArea = (F32)gCamera->getScreenPixelArea();
+ }
+ }
+}
+
+BOOL LLViewerObject::updateLOD()
+{
+ return FALSE;
+}
+
+BOOL LLViewerObject::updateGeometry(LLDrawable *drawable)
+{
+ return TRUE;
+}
+
+LLDrawable* LLViewerObject::createDrawable(LLPipeline *pipeline)
+{
+ return NULL;
+}
+
+void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped)
+{
+ LLPrimitive::setScale(scale);
+ if (mDrawable.notNull())
+ {
+ //encompass completely sheared objects by taking
+ //the most extreme point possible (<1,1,0.5>)
+ mDrawable->setRadius(LLVector3(1,1,0.5f).scaleVec(scale).magVec());
+ updateDrawable(damped);
+ }
+
+ if( (LL_PCODE_VOLUME == getPCode()) && !isDead() )
+ {
+ if (permYouOwner() || (scale.magVecSquared() > (7.5f * 7.5f)) )
+ {
+ if (!mOnMap)
+ {
+ gObjectList.addToMap(this);
+ mOnMap = TRUE;
+ }
+ }
+ else
+ {
+ if (mOnMap)
+ {
+ gObjectList.removeFromMap(this);
+ mOnMap = FALSE;
+ }
+ }
+ }
+}
+
+void LLViewerObject::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
+{
+ LLVector3 center = getRenderPosition();
+ F32 sz = llmin(mDrawable->getRadius(), 256.f);
+ LLVector3 size = LLVector3(sz,sz,sz);
+ newMin.setVec(center-size);
+ newMax.setVec(center+size);
+ mDrawable->setPositionGroup((newMin + newMax) * 0.5f);
+}
+
+F32 LLViewerObject::getMaxScale() const
+{
+ return llmax(getScale().mV[VX],getScale().mV[VY], getScale().mV[VZ]);
+}
+
+F32 LLViewerObject::getMinScale() const
+{
+ return llmin(getScale().mV[0],getScale().mV[1],getScale().mV[2]);
+}
+
+F32 LLViewerObject::getMidScale() const
+{
+ if (getScale().mV[VX] < getScale().mV[VY])
+ {
+ if (getScale().mV[VY] < getScale().mV[VZ])
+ {
+ return getScale().mV[VY];
+ }
+ else if (getScale().mV[VX] < getScale().mV[VZ])
+ {
+ return getScale().mV[VZ];
+ }
+ else
+ {
+ return getScale().mV[VX];
+ }
+ }
+ else if (getScale().mV[VX] < getScale().mV[VZ])
+ {
+ return getScale().mV[VX];
+ }
+ else if (getScale().mV[VY] < getScale().mV[VZ])
+ {
+ return getScale().mV[VZ];
+ }
+ else
+ {
+ return getScale().mV[VY];
+ }
+}
+
+
+void LLViewerObject::updateTextures(LLAgent &agent)
+{
+}
+
+void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */)
+{
+ if (isDead())
+ {
+ return;
+ }
+
+ S32 i;
+ S32 tex_count = getNumTEs();
+ for (i = 0; i < tex_count; i++)
+ {
+ getTEImage(i)->setBoostLevel(LLViewerImage::BOOST_SELECTED);
+ }
+
+ if (boost_children)
+ {
+ S32 num_children = mChildList.size();
+ for (i = 0; i < num_children; i++)
+ {
+ LLViewerObject *childp = mChildList[i];
+ childp->boostTexturePriority();
+ }
+ }
+}
+
+
+void LLViewerObject::setLineWidthForWindowSize(S32 window_width)
+{
+ if (window_width < 700)
+ {
+ LLUI::setLineWidth(2.0f);
+ }
+ else if (window_width < 1100)
+ {
+ LLUI::setLineWidth(3.0f);
+ }
+ else if (window_width < 2000)
+ {
+ LLUI::setLineWidth(4.0f);
+ }
+ else
+ {
+ // _damn_, what a nice monitor!
+ LLUI::setLineWidth(5.0f);
+ }
+}
+
+void LLViewerObject::increaseArrowLength()
+{
+/* ???
+ if (mAxisArrowLength == 50)
+ {
+ mAxisArrowLength = 100;
+ }
+ else
+ {
+ mAxisArrowLength = 150;
+ }
+*/
+}
+
+
+void LLViewerObject::decreaseArrowLength()
+{
+/* ???
+ if (mAxisArrowLength == 150)
+ {
+ mAxisArrowLength = 100;
+ }
+ else
+ {
+ mAxisArrowLength = 50;
+ }
+*/
+}
+
+// Culled from newsim LLTask::addNVPair
+void LLViewerObject::addNVPair(const char* data)
+{
+ // cout << "LLViewerObject::addNVPair() with ---" << data << "---" << endl;
+ LLNameValue *nv = new LLNameValue(data);
+
+// char splat[MAX_STRING];
+// temp->printNameValue(splat);
+// llinfos << "addNVPair " << splat << llendl;
+
+ name_value_map_t::iterator iter = mNameValuePairs.find(nv->mName);
+ if (iter != mNameValuePairs.end())
+ {
+ LLNameValue* foundnv = iter->second;
+ if (foundnv->mClass != NVC_READ_ONLY)
+ {
+ delete foundnv;
+ mNameValuePairs.erase(iter);
+ }
+ else
+ {
+ delete nv;
+// llinfos << "Trying to write to Read Only NVPair " << temp->mName << " in addNVPair()" << llendl;
+ return;
+ }
+ }
+ mNameValuePairs[nv->mName] = nv;
+}
+
+BOOL LLViewerObject::removeNVPair(const char *name)
+{
+ char* canonical_name = gNVNameTable.addString(name);
+
+ lldebugs << "LLViewerObject::removeNVPair(): " << name << llendl;
+
+ name_value_map_t::iterator iter = mNameValuePairs.find(canonical_name);
+ if (iter != mNameValuePairs.end())
+ {
+ if( mRegionp )
+ {
+ LLNameValue* nv = iter->second;
+/*
+ std::string buffer = nv->printNameValue();
+ gMessageSystem->newMessageFast(_PREHASH_RemoveNameValuePair);
+ gMessageSystem->nextBlockFast(_PREHASH_TaskData);
+ gMessageSystem->addUUIDFast(_PREHASH_ID, mID);
+
+ gMessageSystem->nextBlockFast(_PREHASH_NameValueData);
+ gMessageSystem->addStringFast(_PREHASH_NVPair, buffer.c_str());
+
+ gMessageSystem->sendReliable( mRegionp->getHost() );
+*/
+ // Remove the NV pair from the local list.
+ delete nv;
+ mNameValuePairs.erase(iter);
+ return TRUE;
+ }
+ else
+ {
+ lldebugs << "removeNVPair - No region for object" << llendl;
+ }
+ }
+ return FALSE;
+}
+
+
+LLNameValue *LLViewerObject::getNVPair(const char *name) const
+{
+ char *canonical_name;
+
+ canonical_name = gNVNameTable.addString(name);
+
+ // If you access a map with a name that isn't in it, it will add the name and a null pointer.
+ // So first check if the data is in the map.
+ name_value_map_t::const_iterator iter = mNameValuePairs.find(canonical_name);
+ if (iter != mNameValuePairs.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void LLViewerObject::updatePositionCaches() const
+{
+ if (!isRoot())
+ {
+ mPositionRegion = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation();
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+ else
+ {
+ mPositionRegion = getPosition();
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+}
+
+const LLVector3d LLViewerObject::getPositionGlobal() const
+{
+ LLVector3d position_global = mRegionp->getPosGlobalFromRegion(getPositionRegion());;
+
+ if (isAttachment())
+ {
+ position_global = gAgent.getPosGlobalFromAgent(getRenderPosition());
+ }
+
+ return position_global;
+}
+
+const LLVector3 &LLViewerObject::getPositionAgent() const
+{
+ if (mRegionp)
+ {
+ if (mDrawable.notNull() && (!mDrawable->isRoot() && getParent()))
+ {
+ // Don't return cached position if you have a parent, recalc (until all dirtying is done correctly.
+ LLVector3 position_region;
+ position_region = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation();
+ mPositionAgent = mRegionp->getPosAgentFromRegion(position_region);
+ }
+ else
+ {
+ mPositionAgent = mRegionp->getPosAgentFromRegion(getPosition());
+ }
+ }
+ return mPositionAgent;
+}
+
+const LLVector3 &LLViewerObject::getPositionRegion() const
+{
+ if (!isRoot())
+ {
+ LLViewerObject *parent = (LLViewerObject *)getParent();
+ mPositionRegion = parent->getPositionRegion() + (getPosition() * parent->getRotation());
+ }
+ return mPositionRegion;
+}
+
+const LLVector3 LLViewerObject::getPositionEdit() const
+{
+ if (isRootEdit())
+ {
+ return getPosition();
+ }
+ else
+ {
+ LLViewerObject *parent = (LLViewerObject *)getParent();
+ LLVector3 position_edit = parent->getPositionEdit() + getPosition() * parent->getRotationEdit();
+ return position_edit;
+ }
+}
+
+const LLVector3 LLViewerObject::getRenderPosition() const
+{
+ if (mDrawable.isNull() || mDrawable->getGeneration() < 0)
+ {
+ return getPositionAgent();
+ }
+ else
+ {
+ if (isAvatar())
+ {
+ if (isRoot())
+ {
+ return mDrawable->getPositionAgent();
+ }
+ else
+ {
+ return getPosition() * mDrawable->getParent()->getRenderMatrix();
+ }
+ }
+
+ return mDrawable->getPositionAgent();
+ }
+}
+
+const LLVector3 LLViewerObject::getPivotPositionAgent() const
+{
+ return getRenderPosition();
+}
+
+const LLQuaternion LLViewerObject::getRenderRotation() const
+{
+ LLQuaternion ret;
+ if (mDrawable.isNull() || mDrawable->isStatic())
+ {
+ ret = getRotationEdit();
+ }
+ else
+ {
+ if (!mDrawable->isRoot())
+ {
+ ret = getRotation() * LLQuaternion(mDrawable->getParent()->getWorldMatrix());
+ }
+ else
+ {
+ ret = LLQuaternion(mDrawable->getWorldMatrix());
+ }
+ }
+
+ return ret;
+}
+
+const LLMatrix4 LLViewerObject::getRenderMatrix() const
+{
+ return mDrawable->getWorldMatrix();
+}
+
+const LLQuaternion LLViewerObject::getRotationRegion() const
+{
+ LLQuaternion global_rotation = getRotation();
+ if (!((LLXform *)this)->isRoot())
+ {
+ global_rotation = global_rotation * getParent()->getRotation();
+ }
+ return global_rotation;
+}
+
+const LLQuaternion LLViewerObject::getRotationEdit() const
+{
+ LLQuaternion global_rotation = getRotation();
+ if (!((LLXform *)this)->isRootEdit())
+ {
+ global_rotation = global_rotation * getParent()->getRotation();
+ }
+ return global_rotation;
+}
+
+void LLViewerObject::setPositionAbsoluteGlobal( const LLVector3d &pos_global, BOOL damped )
+{
+ if (isAttachment())
+ {
+ LLVector3 new_pos = mRegionp->getPosRegionFromGlobal(pos_global);
+ if (isRootEdit())
+ {
+ new_pos -= mDrawable->mXform.getParent()->getWorldPosition();
+ LLQuaternion world_rotation = mDrawable->mXform.getParent()->getWorldRotation();
+ new_pos = new_pos * ~world_rotation;
+ }
+ else
+ {
+ LLViewerObject* parentp = (LLViewerObject*)getParent();
+ new_pos -= parentp->getPositionAgent();
+ new_pos = new_pos * ~parentp->getRotationRegion();
+ }
+ LLViewerObject::setPosition(new_pos);
+
+ if (mParent && ((LLViewerObject*)mParent)->isAvatar())
+ {
+ // we have changed the position of an attachment, so we need to clamp it
+ LLVOAvatar *avatar = (LLVOAvatar*)mParent;
+
+ avatar->clampAttachmentPositions();
+ }
+ }
+ else
+ {
+ if( isRoot() )
+ {
+ setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global));
+ }
+ else
+ {
+ // the relative position with the parent is not constant
+ LLViewerObject* parent = (LLViewerObject *)getParent();
+ //RN: this assumes we are only calling this function from the edit tools
+ gPipeline.updateMoveNormalAsync(parent->mDrawable);
+
+ LLVector3 pos_local = mRegionp->getPosRegionFromGlobal(pos_global) - parent->getPositionRegion();
+ pos_local = pos_local * ~parent->getRotationRegion();
+ LLViewerObject::setPosition( pos_local );
+ }
+ }
+ //RN: assumes we always want to snap the object when calling this function
+ gPipeline.updateMoveNormalAsync(mDrawable);
+}
+
+void LLViewerObject::setPosition(const LLVector3 &pos, BOOL damped)
+{
+ if (getPosition() != pos)
+ {
+ setChanged(TRANSLATED | SILHOUETTE);
+ }
+
+ LLXform::setPosition(pos);
+ updateDrawable(damped);
+ if (isRoot())
+ {
+ // position caches need to be up to date on root objects
+ updatePositionCaches();
+ }
+}
+
+void LLViewerObject::setPositionGlobal(const LLVector3d &pos_global, BOOL damped)
+{
+ if (isAttachment())
+ {
+ if (isRootEdit())
+ {
+ LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global);
+ newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition();
+
+ LLQuaternion invWorldRotation = mDrawable->mXform.getParent()->getWorldRotation();
+ invWorldRotation.transQuat();
+
+ newPos = newPos * invWorldRotation;
+ LLViewerObject::setPosition(newPos);
+ }
+ else
+ {
+ // assumes parent is root editable (root of attachment)
+ LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global);
+ newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition();
+ LLVector3 delta_pos = newPos - getPosition();
+
+ LLQuaternion invRotation = mDrawable->getRotation();
+ invRotation.transQuat();
+
+ delta_pos = delta_pos * invRotation;
+
+ //FIXME: is this right? Shouldn't we be calling the LLViewerObject version of setPosition?
+ LLVector3 old_pos = mDrawable->mXform.getParent()->getPosition();
+ mDrawable->mXform.getParent()->setPosition(old_pos + delta_pos);
+ setChanged(TRANSLATED | SILHOUETTE);
+ }
+ if (mParent && ((LLViewerObject*)mParent)->isAvatar())
+ {
+ // we have changed the position of an attachment, so we need to clamp it
+ LLVOAvatar *avatar = (LLVOAvatar*)mParent;
+
+ avatar->clampAttachmentPositions();
+ }
+ }
+ else
+ {
+ if (isRoot())
+ {
+ setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global));
+ }
+ else
+ {
+ // the relative position with the parent is constant, but the parent's position needs to be changed
+ LLVector3d position_offset;
+ position_offset.setVec(getPosition()*getParent()->getRotation());
+ LLVector3d new_pos_global = pos_global - position_offset;
+ ((LLViewerObject *)getParent())->setPositionGlobal(new_pos_global);
+ }
+ }
+ updateDrawable(damped);
+}
+
+
+void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, BOOL damped)
+{
+ // Set position relative to parent, if no parent, relative to region
+ if (!isRoot())
+ {
+ LLViewerObject::setPosition(pos_parent);
+ updateDrawable(damped);
+ }
+ else
+ {
+ setPositionRegion(pos_parent, damped);
+ }
+}
+
+void LLViewerObject::setPositionRegion(const LLVector3 &pos_region, BOOL damped)
+{
+ if (!isRootEdit())
+ {
+ LLViewerObject* parent = (LLViewerObject*) getParent();
+ LLViewerObject::setPosition((pos_region-parent->getPositionRegion())*~parent->getRotationRegion());
+ }
+ else
+ {
+ LLViewerObject::setPosition(pos_region);
+ mPositionRegion = pos_region;
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+}
+
+void LLViewerObject::setPositionAgent(const LLVector3 &pos_agent, BOOL damped)
+{
+ LLVector3 pos_region = getRegion()->getPosRegionFromAgent(pos_agent);
+ setPositionRegion(pos_region, damped);
+}
+
+// identical to setPositionRegion() except it checks for child-joints
+// and doesn't also move the joint-parent
+// TODO -- implement similar intelligence for joint-parents toward
+// their joint-children
+void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, BOOL damped)
+{
+ if (!isRootEdit())
+ {
+ // the relative position with the parent is constant, but the parent's position needs to be changed
+ LLVector3 position_offset = getPosition() * getParent()->getRotation();
+
+ ((LLViewerObject *)getParent())->setPositionEdit(pos_edit - position_offset);
+ }
+ else if (isJointChild())
+ {
+ // compute new parent-relative position
+ LLViewerObject *parent = (LLViewerObject *) getParent();
+ LLQuaternion inv_parent_rot = parent->getRotation();
+ inv_parent_rot.transQuat();
+ LLVector3 pos_parent = (pos_edit - parent->getPositionRegion()) * inv_parent_rot;
+ LLViewerObject::setPosition(pos_parent);
+ }
+ else
+ {
+ LLViewerObject::setPosition(pos_edit);
+ mPositionRegion = pos_edit;
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+ updateDrawable(damped);
+}
+
+
+LLViewerObject* LLViewerObject::getRootEdit() const
+{
+ const LLViewerObject* root = this;
+ while (root->mParent
+ && !(root->mJointInfo
+ || ((LLViewerObject*)root->mParent)->isAvatar()) )
+ {
+ root = (LLViewerObject*)root->mParent;
+ }
+ return (LLViewerObject*)root;
+}
+
+U8 LLViewerObject::getMediaType() const
+{
+ if (mMedia)
+ {
+ return mMedia->mMediaType;
+ }
+ else
+ {
+ return LLViewerObject::MEDIA_TYPE_NONE;
+ }
+}
+
+void LLViewerObject::setMediaType(U8 media_type)
+{
+ if (!mMedia)
+ {
+ // JAMESDEBUG TODO what if we don't have a media pointer?
+ }
+ else if (mMedia->mMediaType != media_type)
+ {
+ mMedia->mMediaType = media_type;
+ if (gMediaList)
+ {
+ // we're using web pages on prims
+ gMediaList->updatedMediaURL(this);
+ }
+ if (mDrawable.notNull())
+ {
+ // move this object's faces into LLDrawPoolMedia
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+const LLString& LLViewerObject::getMediaURL() const
+{
+ if (mMedia)
+ {
+ return mMedia->mMediaURL;
+ }
+ else
+ {
+ return LLString::null;
+ }
+}
+
+void LLViewerObject::setMediaURL(const LLString& media_url)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ if (!mMedia)
+ {
+ mMedia = new LLViewerObjectMedia;
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ if (gMediaList)
+ {
+ gMediaList->addedMediaURL(this);
+ }
+ if (mDrawable.notNull())
+ {
+ // move this object's faces into LLDrawPoolMedia
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ else if (mMedia->mMediaURL != media_url)
+ {
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ if (gMediaList)
+ {
+ // we're using web pages on prims
+ gMediaList->updatedMediaURL(this);
+ }
+ if (mDrawable.notNull())
+ {
+ // move this object's faces into LLDrawPoolMedia
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+BOOL LLViewerObject::getMediaPassedWhitelist() const
+{
+ if (mMedia)
+ {
+ return mMedia->mPassedWhitelist;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void LLViewerObject::setMediaPassedWhitelist(BOOL passed)
+{
+ if (mMedia)
+ {
+ mMedia->mPassedWhitelist = passed;
+ }
+}
+
+BOOL LLViewerObject::setMaterial(const U8 material)
+{
+ BOOL res = LLPrimitive::setMaterial(material);
+ if (res)
+ {
+ setChanged(TEXTURE);
+ }
+ return res;
+}
+
+void LLViewerObject::setNumTEs(const U8 num_tes)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ U32 i;
+ if (num_tes != getNumTEs())
+ {
+ if (num_tes)
+ {
+ LLPointer<LLViewerImage> *new_images;
+ new_images = new LLPointer<LLViewerImage>[num_tes];
+ for (i = 0; i < num_tes; i++)
+ {
+ if (i < getNumTEs())
+ {
+ new_images[i] = mTEImages[i];
+ }
+ else if (getNumTEs())
+ {
+ new_images[i] = mTEImages[getNumTEs()-1];
+ }
+ else
+ {
+ new_images[i] = NULL;
+ }
+ }
+
+ deleteTEImages();
+
+ mTEImages = new_images;
+ }
+ else
+ {
+ deleteTEImages();
+ }
+ LLPrimitive::setNumTEs(num_tes);
+ setChanged(TEXTURE);
+
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+void LLViewerObject::sendMaterialUpdate() const
+{
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+ gMessageSystem->newMessageFast(_PREHASH_ObjectMaterial);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ gMessageSystem->addU8Fast(_PREHASH_Material, getMaterial() );
+ gMessageSystem->sendReliable( regionp->getHost() );
+
+}
+
+// formerly send_object_rotation
+void LLViewerObject::sendRotationUpdate() const
+{
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+ gMessageSystem->newMessageFast(_PREHASH_ObjectRotation);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID);
+ gMessageSystem->addQuatFast(_PREHASH_Rotation, getRotationEdit());
+ //llinfos << "Sent rotation " << getRotationEdit() << llendl;
+ gMessageSystem->sendReliable( regionp->getHost() );
+}
+
+// formerly send_object_position_global
+void LLViewerObject::sendPositionUpdate() const
+{
+ gMessageSystem->newMessageFast(_PREHASH_ObjectPosition);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ gMessageSystem->addVector3Fast(_PREHASH_Position, getPositionRegion());
+ LLViewerRegion* regionp = getRegion();
+ gMessageSystem->sendReliable(regionp->getHost());
+}
+
+
+//formerly send_object_scale
+void LLViewerObject::sendScaleUpdate()
+{
+ gMessageSystem->newMessageFast(_PREHASH_ObjectScale);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ gMessageSystem->addVector3Fast(_PREHASH_Scale, (getScale()));
+
+ LLViewerRegion *regionp = getRegion();
+ gMessageSystem->sendReliable(regionp->getHost() );
+}
+
+
+//formerly send_object_shape(LLViewerObject *object)
+void LLViewerObject::sendShapeUpdate()
+{
+ gMessageSystem->newMessageFast(_PREHASH_ObjectShape);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+
+ LLVolumeMessage::packVolumeParams(&getVolume()->getParams(), gMessageSystem);
+
+ LLViewerRegion *regionp = getRegion();
+ gMessageSystem->sendReliable( regionp->getHost() );
+}
+
+
+void LLViewerObject::sendTEUpdate() const
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectImage);
+
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ if (mMedia)
+ {
+ msg->addString("MediaURL", mMedia->mMediaURL);
+ }
+ else
+ {
+ msg->addString("MediaURL", NULL);
+ }
+
+ // JAMESDEBUG TODO send media type
+
+ packTEMessage(msg);
+
+ LLViewerRegion *regionp = getRegion();
+ msg->sendReliable( regionp->getHost() );
+}
+
+void LLViewerObject::setTE(const U8 te, const LLTextureEntry &texture_entry)
+{
+ LLPrimitive::setTE(te, texture_entry);
+// JAMESDEBUG This doesn't work, don't get any textures.
+// if (mDrawable.notNull() && mDrawable->isVisible())
+// {
+ const LLUUID& image_id = getTE(te)->getID();
+ mTEImages[te] = gImageList.getImage(image_id);
+// }
+}
+
+void LLViewerObject::setTEImage(const U8 te, LLViewerImage *imagep)
+{
+ if (mTEImages[te] != imagep)
+ {
+ mTEImages[te] = imagep;
+ LLPrimitive::setTETexture(te, imagep->getID());
+ setChanged(TEXTURE);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+
+S32 LLViewerObject::setTETextureCore(const U8 te, const LLUUID& uuid, LLHost host)
+{
+ S32 retval = 0;
+ if (uuid != getTE(te)->getID() ||
+ uuid == LLUUID::null)
+ {
+ retval = LLPrimitive::setTETexture(te, uuid);
+ mTEImages[te] = gImageList.getImageFromHost(uuid, host);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ return retval;
+}
+
+
+S32 LLViewerObject::setTETexture(const U8 te, const LLUUID& uuid)
+{
+ // Invalid host == get from the agent's sim
+ return setTETextureCore(te, uuid, LLHost::invalid);
+}
+
+
+S32 LLViewerObject::setTEColor(const U8 te, const LLColor4 &color)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (color != tep->getColor())
+ {
+ retval = LLPrimitive::setTEColor(te, color);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ // These should only happen on updates which are not the initial update.
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEBumpmap(const U8 te, const U8 bump)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (bump != tep->getBumpmap())
+ {
+ retval = LLPrimitive::setTEBumpmap(te, bump);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTETexGen(const U8 te, const U8 texgen)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (texgen != tep->getTexGen())
+ {
+ retval = LLPrimitive::setTETexGen(te, texgen);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEShiny(const U8 te, const U8 shiny)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (shiny != tep->getShiny())
+ {
+ retval = LLPrimitive::setTEShiny(te, shiny);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEFullbright(const U8 te, const U8 fullbright)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (fullbright != tep->getFullbright())
+ {
+ retval = LLPrimitive::setTEFullbright(te, fullbright);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ return retval;
+}
+
+
+S32 LLViewerObject::setTEMediaFlags(const U8 te, const U8 media_flags)
+{
+ // JAMESDEBUG this might need work for media type
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (media_flags != tep->getMediaFlags())
+ {
+ retval = LLPrimitive::setTEMediaFlags(te, media_flags);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, TRUE);
+ gPipeline.markTextured(mDrawable);
+ // JC - probably only need this if changes texture coords
+ //gPipeline.markRebuild(mDrawable);
+ }
+ }
+ return retval;
+}
+
+
+S32 LLViewerObject::setTEScale(const U8 te, const F32 s, const F32 t)
+{
+ S32 retval = 0;
+ retval = LLPrimitive::setTEScale(te, s, t);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEScaleS(const U8 te, const F32 s)
+{
+ S32 retval = LLPrimitive::setTEScaleS(te, s);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTEScaleT(const U8 te, const F32 t)
+{
+ S32 retval = LLPrimitive::setTEScaleT(te, t);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTEOffset(const U8 te, const F32 s, const F32 t)
+{
+ S32 retval = LLPrimitive::setTEOffset(te, s, t);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEOffsetS(const U8 te, const F32 s)
+{
+ S32 retval = LLPrimitive::setTEOffsetS(te, s);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTEOffsetT(const U8 te, const F32 t)
+{
+ S32 retval = LLPrimitive::setTEOffsetT(te, t);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTERotation(const U8 te, const F32 r)
+{
+ S32 retval = LLPrimitive::setTERotation(te, r);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+ return retval;
+}
+
+
+LLViewerImage *LLViewerObject::getTEImage(const U8 face) const
+{
+// llassert(mTEImages);
+
+ if (face < getNumTEs())
+ {
+ LLViewerImage* image = mTEImages[face];
+ if (image)
+ {
+ return image;
+ }
+ else
+ {
+ return (LLViewerImage*)((LLImageGL*)LLViewerImage::sDefaultImagep);
+ }
+ }
+
+ llerrs << "Requested invalid face!" << llendl;
+
+ return NULL;
+}
+
+
+void LLViewerObject::fitFaceTexture(const U8 face)
+{
+ llinfos << "fitFaceTexture not implemented" << llendl;
+}
+
+
+LLBBox LLViewerObject::getBoundingBoxAgent() const
+{
+ LLVector3 position_agent;
+ LLQuaternion rot;
+ LLViewerObject* root_edit = (LLViewerObject*)getRootEdit();
+ LLViewerObject* avatar_parent = (LLViewerObject*)root_edit->getParent();
+ if (avatar_parent && avatar_parent->isAvatar() && root_edit->mDrawable.notNull())
+ {
+ LLXform* parent_xform = root_edit->mDrawable->getXform()->getParent();
+ position_agent = (getPositionEdit() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
+ rot = getRotationEdit() * parent_xform->getWorldRotation();
+ }
+ else
+ {
+ position_agent = getPositionAgent();
+ rot = getRotationRegion();
+ }
+
+ return LLBBox( position_agent, rot, getScale() * -0.5f, getScale() * 0.5f );
+}
+
+U32 LLViewerObject::getNumVertices() const
+{
+ U32 num_vertices = 0;
+ if (mDrawable.notNull())
+ {
+ S32 i, num_faces;
+ num_faces = mDrawable->getNumFaces();
+ for (i = 0; i < num_faces; i++)
+ {
+ num_vertices += mDrawable->getFace(i)->getGeomCount();
+ }
+ }
+ return num_vertices;
+}
+
+U32 LLViewerObject::getNumIndices() const
+{
+ U32 num_indices = 0;
+ if (mDrawable.notNull())
+ {
+ S32 i, num_faces;
+ num_faces = mDrawable->getNumFaces();
+ for (i = 0; i < num_faces; i++)
+ {
+ num_indices += mDrawable->getFace(i)->getIndicesCount();
+ }
+ }
+ return num_indices;
+}
+
+// Find the number of instances of this object's inventory that are of the given type
+S32 LLViewerObject::countInventoryContents(LLAssetType::EType type)
+{
+ S32 count = 0;
+ if( mInventory )
+ {
+ InventoryObjectList::const_iterator it = mInventory->begin();
+ InventoryObjectList::const_iterator end = mInventory->end();
+ for( ; it != end ; ++it )
+ {
+ if( (*it)->getType() == type )
+ {
+ ++count;
+ }
+ }
+ }
+ return count;
+}
+
+
+void LLViewerObject::setCanSelect(BOOL canSelect)
+{
+ mbCanSelect = canSelect;
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ mChildList[i]->mbCanSelect = canSelect;
+ }
+}
+
+void LLViewerObject::setDebugText(const std::string &utf8text)
+{
+ if (!mText)
+ {
+ mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mText->setFont(LLFontGL::sSansSerif);
+ mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mText->setMaxLines(-1);
+ mText->setSourceObject(this);
+ mText->setOnHUDAttachment(isHUDAttachment());
+ }
+ mText->setColor(LLColor4::white);
+ mText->setStringUTF8(utf8text);
+ mText->setZCompare(FALSE);
+ mText->setDoFade(FALSE);
+ updateText();
+}
+
+void LLViewerObject::setIcon(LLViewerImage* icon_image)
+{
+ if (!mIcon)
+ {
+ mIcon = (LLHUDIcon *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_ICON);
+ mIcon->setSourceObject(this);
+ mIcon->setImage(icon_image);
+ //FIXME: make this user configurable
+ mIcon->setScale(0.03f);
+ }
+ else
+ {
+ mIcon->restartLifeTimer();
+ }
+}
+
+void LLViewerObject::clearIcon()
+{
+ if (mIcon)
+ {
+ mIcon = NULL;
+ }
+}
+
+LLViewerObject* LLViewerObject::getSubParent()
+{
+ if (isJointChild())
+ {
+ return this;
+ }
+ return (LLViewerObject*) getParent();
+}
+
+const LLViewerObject* LLViewerObject::getSubParent() const
+{
+ if (isJointChild())
+ {
+ return this;
+ }
+ return (const LLViewerObject*) getParent();
+}
+
+BOOL LLViewerObject::isOnMap()
+{
+ return mOnMap;
+}
+
+
+void LLViewerObject::updateText()
+{
+ if (!isDead())
+ {
+ if (mText.notNull())
+ {
+ LLVector3 up_offset(0,0,0);
+ up_offset.mV[2] = getScale().mV[VZ]*0.6f;
+
+ if (mDrawable.notNull())
+ {
+ mText->setPositionAgent(getRenderPosition() + up_offset);
+ }
+ else
+ {
+ mText->setPositionAgent(getPositionAgent() + up_offset);
+ }
+ }
+ }
+}
+
+BOOL LLViewerObject::isParticleSource() const
+{
+ return !mPartSourcep.isNull() && !mPartSourcep->isDead();
+}
+
+void LLViewerObject::unpackParticleSource(const S32 block_num, const LLUUID& owner_id)
+{
+ if (!mPartSourcep.isNull() && mPartSourcep->isDead())
+ {
+ mPartSourcep = NULL;
+ }
+ if (mPartSourcep)
+ {
+ // If we've got one already, just update the existing source (or remove it)
+ if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, block_num))
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+ }
+ else
+ {
+ //If the owner is muted, don't create the system
+ if(gMuteListp->isMuted(owner_id)) return;
+ LLViewerPartSourceScript *pss = LLViewerPartSourceScript::unpackPSS(this, NULL, block_num);
+
+ // We need to be able to deal with a particle source that hasn't changed, but still got an update!
+ if (pss)
+ {
+// llinfos << "Making particle system with owner " << owner_id << llendl;
+ pss->setOwnerUUID(owner_id);
+ mPartSourcep = pss;
+ gWorldPointer->mPartSim.addPartSource(pss);
+ }
+ }
+ if (mPartSourcep)
+ {
+ if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID)
+ {
+ LLViewerImage* image;
+ if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null)
+ {
+ LLUUID id(gViewerArt.getString("pixiesmall.tga"));
+ image = gImageList.getImage(id);
+ }
+ else
+ {
+ image = gImageList.getImage(mPartSourcep->mPartSysData.mPartImageID);
+ }
+ mPartSourcep->setImage(image);
+ }
+ }
+}
+
+void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id)
+{
+ if (!mPartSourcep.isNull() && mPartSourcep->isDead())
+ {
+ mPartSourcep = NULL;
+ }
+ if (mPartSourcep)
+ {
+ // If we've got one already, just update the existing source (or remove it)
+ if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, dp))
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+ }
+ else
+ {
+ //If the owner is muted, don't create the system
+ if(gMuteListp->isMuted(owner_id)) return;
+ LLViewerPartSourceScript *pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp);
+
+ // We need to be able to deal with a particle source that hasn't changed, but still got an update!
+ if (pss)
+ {
+// llinfos << "Making particle system with owner " << owner_id << llendl;
+ pss->setOwnerUUID(owner_id);
+ mPartSourcep = pss;
+ gWorldPointer->mPartSim.addPartSource(pss);
+ }
+ }
+ if (mPartSourcep)
+ {
+ if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID)
+ {
+ LLViewerImage* image;
+ if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null)
+ {
+ LLUUID id(gViewerArt.getString("pixiesmall.tga"));
+ image = gImageList.getImage(id);
+ }
+ else
+ {
+ image = gImageList.getImage(mPartSourcep->mPartSysData.mPartImageID);
+ }
+ mPartSourcep->setImage(image);
+ }
+ }
+}
+
+// virtual
+void LLViewerObject::updateDrawable(BOOL force_damped)
+{
+ if (mDrawable.notNull() &&
+ !mDrawable->isState(LLDrawable::ON_MOVE_LIST) &&
+ isChanged(MOVED) &&
+ !isAvatar())
+ {
+ mLastUpdateFrame = LLFrameTimer::getFrameCount();
+ BOOL damped_motion =
+ !isChanged(SHIFTED) && // not shifted between regions this frame and...
+ (force_damped || // ...forced into damped motion by application logic or...
+ (!isSelected() && // ...not selected and...
+ (mDrawable->isRoot() || // ... is root or ...
+ !((LLViewerObject*)getParent())->isSelected()) && // ... parent is not selected and ...
+ getPCode() == LL_PCODE_VOLUME && // ...is a volume object and...
+ getVelocity().isExactlyZero() && // ...is not moving physically and...
+ mDrawable->getGeneration() != -1) // ...was not created this frame.
+ );
+ gPipeline.markMoved(mDrawable, damped_motion);
+ }
+ clearChanged(SHIFTED);
+}
+
+// virtual, overridden by LLVOVolume
+F32 LLViewerObject::getVObjRadius() const
+{
+ return mDrawable.notNull() ? mDrawable->getRadius() : 0.f;
+}
+
+void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const U8 flags)
+{
+ if (!gAudiop)
+ {
+ return;
+ }
+
+ if (audio_uuid.isNull())
+ {
+ if (mAudioSourcep && mAudioSourcep->isLoop() && !mAudioSourcep->hasPendingPreloads())
+ {
+ // We don't clear the sound if it's a loop, it'll go away on its own.
+ // At least, this appears to be how the scripts work.
+ // The attached sound ID is set to NULL to avoid it playing back when the
+ // object rezzes in on non-looping sounds.
+ //llinfos << "Clearing attached sound " << mAudioSourcep->getCurrentData()->getID() << llendl;
+ gAudiop->cleanupAudioSource(mAudioSourcep);
+ mAudioSourcep = NULL;
+ }
+ else if (mAudioSourcep)
+ {
+ if (mAudioSourcep->isLoop())
+ {
+ // Just shut off the sound
+ mAudioSourcep->play(LLUUID::null);
+ }
+ }
+ return;
+ }
+ if (flags & LL_SOUND_FLAG_LOOP)
+ {
+ if (mAudioSourcep && mAudioSourcep->isLoop() && mAudioSourcep->getCurrentData())
+ {
+ if (mAudioSourcep->getCurrentData()->getID() == audio_uuid)
+ {
+ //llinfos << "Already playing this sound on a loop, ignoring" << llendl;
+ return;
+ }
+ }
+ }
+
+ getAudioSource(owner_id);
+
+ if (mAudioSourcep)
+ {
+ mAudioSourcep->setGain(gain);
+ mAudioSourcep->setLoop((flags & LL_SOUND_FLAG_LOOP) ? TRUE : FALSE);
+ mAudioSourcep->setSyncMaster((flags & LL_SOUND_FLAG_SYNC_MASTER) ? TRUE : FALSE);
+ mAudioSourcep->setSyncSlave((flags & LL_SOUND_FLAG_SYNC_SLAVE) ? TRUE : FALSE);
+ mAudioSourcep->setQueueSounds((flags & LL_SOUND_FLAG_QUEUE) ? TRUE : FALSE);
+ //llinfos << "Playing attached sound " << audio_uuid << llendl;
+ mAudioSourcep->play(audio_uuid);
+ }
+}
+
+LLAudioSource *LLViewerObject::getAudioSource(const LLUUID& owner_id)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ if (!mAudioSourcep)
+ {
+ // Arbitrary low gain for a sound that's not playing.
+ // This is used for sound preloads, for example.
+ LLAudioSourceVO *asvop = new LLAudioSourceVO(mID, owner_id, 0.01f, this);
+
+ mAudioSourcep = asvop;
+ if(gAudiop) gAudiop->addAudioSource(asvop);
+ }
+
+ return mAudioSourcep;
+}
+
+void LLViewerObject::adjustAudioGain(const F32 gain)
+{
+ if (!gAudiop)
+ {
+ return;
+ }
+
+ if (!mAudioSourcep)
+ {
+ return;
+ }
+ mAudioSourcep->setGain(gain);
+}
+
+//----------------------------------------------------------------------------
+
+bool LLViewerObject::unpackParameterEntry(U16 param_type, LLDataPacker *dp)
+{
+ ExtraParameter* param = getExtraParameterEntryCreate(param_type);
+ if (param)
+ {
+ param->data->unpack(*dp);
+ param->in_use = TRUE;
+ parameterChanged(param_type, param->data, TRUE, false);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 param_type)
+{
+ LLNetworkData* new_block = NULL;
+ switch (param_type)
+ {
+ case LLNetworkData::PARAMS_FLEXIBLE:
+ {
+ new_block = new LLFlexibleObjectData();
+ break;
+ }
+ case LLNetworkData::PARAMS_LIGHT:
+ {
+ new_block = new LLLightParams();
+ break;
+ }
+ default:
+ {
+ llinfos << "Unknown param type." << llendl;
+ break;
+ }
+ };
+
+ if (new_block)
+ {
+ ExtraParameter* new_entry = new ExtraParameter;
+ new_entry->data = new_block;
+ new_entry->in_use = false; // not in use yet
+ mExtraParameterList[param_type] = new_entry;
+ return new_entry;
+ }
+ return NULL;
+}
+
+LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntry(U16 param_type) const
+{
+ std::map<U16, ExtraParameter*>::const_iterator itor = mExtraParameterList.find(param_type);
+ if (itor != mExtraParameterList.end())
+ {
+ return itor->second;
+ }
+ return NULL;
+}
+
+LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntryCreate(U16 param_type)
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (!param)
+ {
+ param = createNewParameterEntry(param_type);
+ }
+ return param;
+}
+
+LLNetworkData* LLViewerObject::getParameterEntry(U16 param_type) const
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (param)
+ {
+ return param->data;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+BOOL LLViewerObject::getParameterEntryInUse(U16 param_type) const
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (param)
+ {
+ return param->in_use;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+bool LLViewerObject::setParameterEntry(U16 param_type, const LLNetworkData& new_value, bool local_origin)
+{
+ ExtraParameter* param = getExtraParameterEntryCreate(param_type);
+ if (param)
+ {
+ if (param->in_use && new_value == *(param->data))
+ {
+ return false;
+ }
+ param->in_use = true;
+ param->data->copy(new_value);
+ parameterChanged(param_type, param->data, TRUE, local_origin);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Assumed to be called locally
+// If in_use is TRUE, will crate a new extra parameter if none exists.
+// Should always return true.
+bool LLViewerObject::setParameterEntryInUse(U16 param_type, BOOL in_use, bool local_origin)
+{
+ ExtraParameter* param = getExtraParameterEntryCreate(param_type);
+ if (param->in_use != in_use)
+ {
+ param->in_use = in_use;
+ parameterChanged(param_type, param->data, in_use, local_origin);
+ return true;
+ }
+ return false;
+}
+
+void LLViewerObject::parameterChanged(U16 param_type, bool local_origin)
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (param)
+ {
+ parameterChanged(param_type, param->data, param->in_use, local_origin);
+ }
+}
+
+void LLViewerObject::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin)
+{
+ if (local_origin)
+ {
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+
+ // Change happened on the viewer. Send the change up
+ U8 tmp[MAX_OBJECT_PARAMS_SIZE];
+ LLDataPackerBinaryBuffer dpb(tmp, MAX_OBJECT_PARAMS_SIZE);
+ if (data->pack(dpb))
+ {
+ U32 datasize = (U32)dpb.getCurrentSize();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectExtraParams);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+
+ msg->addU16Fast(_PREHASH_ParamType, param_type);
+ msg->addBOOLFast(_PREHASH_ParamInUse, in_use);
+
+ msg->addU32Fast(_PREHASH_ParamSize, datasize);
+ msg->addBinaryDataFast(_PREHASH_ParamData, tmp, datasize);
+
+ msg->sendReliable( regionp->getHost() );
+ }
+ else
+ {
+ llwarns << "Failed to send object extra parameters: " << param_type << llendl;
+ }
+ }
+}
+
+void LLViewerObject::setDrawableState(U32 state, BOOL recursive)
+{
+ if (mDrawable)
+ {
+ mDrawable->setState(state);
+ }
+ if (recursive)
+ {
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ mChildList[i]->setDrawableState(state, recursive);
+ }
+ }
+}
+
+void LLViewerObject::clearDrawableState(U32 state, BOOL recursive)
+{
+ if (mDrawable)
+ {
+ mDrawable->clearState(state);
+ }
+ if (recursive)
+ {
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ mChildList[i]->clearDrawableState(state, recursive);
+ }
+ }
+}
+
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// RN: these functions assume a 2-level hierarchy
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+// Owned by anyone?
+BOOL LLViewerObject::permAnyOwner() const
+{
+ if (isRootEdit())
+ {
+ return ((mFlags & FLAGS_OBJECT_ANY_OWNER) != 0);
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permAnyOwner();
+ }
+}
+// Owned by this viewer?
+BOOL LLViewerObject::permYouOwner() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_YOU_OWNER) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permYouOwner();
+ }
+}
+
+// Owned by a group?
+BOOL LLViewerObject::permGroupOwner() const
+{
+ if (isRootEdit())
+ {
+ return ((mFlags & FLAGS_OBJECT_GROUP_OWNED) != 0);
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permGroupOwner();
+ }
+}
+
+// Can the owner edit
+BOOL LLViewerObject::permOwnerModify() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_OWNER_MODIFY) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permOwnerModify();
+ }
+}
+
+// Can edit
+BOOL LLViewerObject::permModify() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_MODIFY) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permModify();
+ }
+}
+
+// Can copy
+BOOL LLViewerObject::permCopy() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_COPY) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permCopy();
+ }
+}
+
+// Can move
+BOOL LLViewerObject::permMove() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_MOVE) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permMove();
+ }
+}
+
+// Can be transferred
+BOOL LLViewerObject::permTransfer() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_TRANSFER) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permTransfer();
+ }
+}
+
+// Can only open objects that you own, or that someone has
+// given you modify rights to. JC
+BOOL LLViewerObject::allowOpen() const
+{
+ return !flagInventoryEmpty() && (permYouOwner() || permModify());
+}
+
+LLViewerObject::LLInventoryCallbackInfo::~LLInventoryCallbackInfo()
+{
+ if (mListener)
+ {
+ mListener->clearVOInventoryListener();
+ }
+}
+
+void LLViewerObject::updateVolume(const LLVolumeParams& volume_params)
+{
+ if (setVolume(volume_params, 1)) // FIXME: magic number, ack!
+ {
+ // Transmit the update to the simulator
+ sendShapeUpdate();
+ markForUpdate(TRUE);
+ }
+}
+
+void LLViewerObject::markForUpdate(BOOL priority)
+{
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, priority);
+ }
+}
+
+void LLViewerObject::setRegion(LLViewerRegion *regionp)
+{
+ llassert(regionp);
+ mRegionp = regionp;
+ setChanged(MOVED | SILHOUETTE);
+ updateDrawable(FALSE);
+}
+
+bool LLViewerObject::specialHoverCursor() const
+{
+ return (mFlags & FLAGS_USE_PHYSICS)
+ || (mFlags & FLAGS_HANDLE_TOUCH)
+ || (mClickAction != 0);
+}
+
+void LLViewerObject::updateFlags()
+{
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+ gMessageSystem->newMessage("ObjectFlagUpdate");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, getLocalID() );
+ gMessageSystem->addBOOLFast(_PREHASH_UsePhysics, usePhysics() );
+ gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() );
+ gMessageSystem->addBOOL("IsPhantom", flagPhantom() );
+ gMessageSystem->addBOOL("CastsShadows", flagCastShadows() );
+ gMessageSystem->sendReliable( regionp->getHost() );
+}
+
+BOOL LLViewerObject::setFlags(U32 flags, BOOL state)
+{
+ BOOL setit = FALSE;
+ if (state)
+ {
+ if ((mFlags & flags) != flags)
+ {
+ mFlags |= flags;
+ setit = TRUE;
+ }
+ }
+ else
+ {
+ if ((mFlags & flags) != 0)
+ {
+ mFlags &= ~flags;
+ setit = TRUE;
+ }
+ }
+
+ // BUG: Sometimes viewer physics and simulator physics get
+ // out of sync. To fix this, always send update to simulator.
+// if (setit)
+ {
+ updateFlags();
+ }
+ return setit;
+}
+
+BOOL LLViewerObject::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+{
+ return FALSE;
+}
+
+void LLViewerObject::applyAngularVelocity(F32 dt)
+{
+ //do target omega here
+ mRotTime += dt;
+ LLVector3 ang_vel = getAngularVelocity();
+ F32 omega = ang_vel.magVecSquared();
+ F32 angle = 0.0f;
+ LLQuaternion dQ;
+ if (omega > 0.00001f)
+ {
+ omega = sqrt(omega);
+ angle = omega * dt;
+
+ ang_vel *= 1.f/omega;
+
+ dQ.setQuat(angle, ang_vel);
+
+ setRotation(getRotation()*dQ);
+ setChanged(MOVED | SILHOUETTE);
+ }
+}
+
+void LLViewerObject::resetRot()
+{
+ mRotTime = 0.0f;
+}