/** * @file llviewerobject.cpp * @brief Base class for viewer objects * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llviewerobject.h" #include "llaudioengine.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 "llinventorydefines.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 "llagentcamera.h" #include "llbbox.h" #include "llbox.h" #include "llcylinder.h" #include "lldrawable.h" #include "llface.h" #include "llfloaterproperties.h" #include "llfloatertools.h" #include "llfollowcam.h" #include "llselectmgr.h" #include "llrendersphere.h" #include "lltooldraganddrop.h" #include "llviewercamera.h" #include "llviewertexturelist.h" #include "llviewerinventory.h" #include "llviewerobjectlist.h" #include "llviewerparceloverlay.h" #include "llviewerpartsource.h" #include "llviewerregion.h" #include "llviewerstats.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 "llvopartgroup.h" #include "llvosky.h" #include "llvosurfacepatch.h" #include "llvotextbubble.h" #include "llvotree.h" #include "llvovolume.h" #include "llvowater.h" #include "llworld.h" #include "llui.h" #include "pipeline.h" #include "llviewernetwork.h" #include "llvowlsky.h" #include "llmanip.h" // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] //#define DEBUG_UPDATE_TYPE BOOL LLViewerObject::sVelocityInterpolate = TRUE; BOOL LLViewerObject::sPingInterpolate = TRUE; 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 // sMaxUpdateInterpolationTime must be greater than sPhaseOutUpdateInterpolationTime F64 LLViewerObject::sMaxUpdateInterpolationTime = 3.0; // For motion interpolation: after X seconds with no updates, don't predict object motion F64 LLViewerObject::sPhaseOutUpdateInterpolationTime = 2.0; // For motion interpolation: after Y seconds with no updates, taper off motion prediction // 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: // llwarns << "Creating old part sys!" << llendl; // res = new LLVOPart(id, pcode, regionp); break; res = NULL; 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 = 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_VOID_WATER: res = new LLVOVoidWater(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; case LL_VO_HUD_PART_GROUP: res = new LLVOHUDPartGroup(id, pcode, regionp); break; case LL_VO_WL_SKY: res = new LLVOWLSky(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, BOOL is_global) : LLPrimitive(), mChildList(), mID(id), mLocalID(0), mTotalCRC(0), mTEImages(NULL), mGLName(0), mbCanSelect(TRUE), mFlags(0), #if MESH_ENABLED mPhysicsShapeType(0), mPhysicsGravity(0), mPhysicsFriction(0), mPhysicsDensity(0), mPhysicsRestitution(0), #endif //MESH_ENABLED mDrawable(), mCreateSelected(FALSE), mRenderMedia(FALSE), mBestUpdatePrecision(0), mIsNameAttachment(false), mText(), mLastInterpUpdateSecs(0.f), mLastMessageUpdateSecs(0.f), mLatestRecvPacketID(0), mData(NULL), mAudioSourcep(NULL), mAudioGain(1.f), mAppAngle(0.f), mPixelArea(1024.f), mInventory(NULL), mInventorySerialNum(0), mRegionp( regionp ), mInventoryPending(FALSE), mInventoryDirty(FALSE), mDead(FALSE), mOrphaned(FALSE), mUserSelected(FALSE), mOnActiveList(FALSE), mOnMap(FALSE), mStatic(FALSE), mNumFaces(0), mTimeDilation(1.f), mRotTime(0.f), mJointInfo(NULL), mState(0), mMedia(NULL), mClickAction(0), #if MESH_ENABLED mObjectCost(0), mLinksetCost(0), mPhysicsCost(0), mLinksetPhysicsCost(0.f), mCostStale(true), mPhysicsShapeUnknown(true), #endif //MESH_ENABLED mAttachmentItemID(LLUUID::null), mLastUpdateType(OUT_UNKNOWN), mLastUpdateCached(FALSE) { if(!is_global) { llassert(mRegionp); } LLPrimitive::init_primitive(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); if (!is_global && mRegionp) { 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; } if (mPartSourcep) { mPartSourcep->setDead(); mPartSourcep = NULL; } // Delete memory associated with extra parameters. std::map::iterator iter; for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) { if(iter->second != NULL) { // // There was a crash here // 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.back(); 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.pop_back(); } 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()) { if (isAgentAvatarValid()) { // stop motions associated with this object gAgent.getAvatarObject()->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()) { LLFacePool *poolp = mDrawable->getFace(0)->getPool(); if (poolp) { llinfos << "Pool: " << poolp << llendl; llinfos << "Pool reference count: " << poolp->mReferences.size() << 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; } for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* child = *iter; 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(); LLVOWater::initClass(); LLVOVolume::initClass(); } void LLViewerObject::cleanupVOClasses() { LLVOGrass::cleanupClass(); LLVOWater::cleanupClass(); LLVOTree::cleanupClass(); LLVOAvatar::cleanupClass(); LLVOVolume::cleanupClass(); } // Replaces all name value pairs with data from \n delimited list // Does not update server void LLViewerObject::setNameValueList(const std::string& name_value_list) { // Clear out the old for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ; mNameValuePairs.clear(); // Bring in the new std::string::size_type length = name_value_list.length(); std::string::size_type start = 0; while (start < length) { std::string::size_type end = name_value_list.find_first_of("\n", start); if (end == std::string::npos) end = length; if (end > start) { std::string tok = name_value_list.substr(start, end - start); addNVPair(tok); } start = end+1; } } // 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()); } BOOL LLViewerObject::setParent(LLViewerObject* parent) { if (mParent != parent) { LLViewerObject* old_parent = (LLViewerObject*)mParent ; BOOL ret = LLPrimitive::setParent(parent); if (ret && old_parent && parent) { old_parent->removeChild(this) ; } return ret ; } return FALSE ; } void LLViewerObject::addChild(LLViewerObject *childp) { 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; } if (childp->setParent(this)) { mChildList.push_back(childp); } } 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); if(childp->getParent() == this) { childp->setParent(NULL); } break; } } if (childp->isSelected()) { LLSelectMgr::getInstance()->deselectObjectAndFamily(childp); BOOL add_to_end = TRUE; LLSelectMgr::getInstance()->selectObjectAndFamily(childp, add_to_end); } } void LLViewerObject::addThisAndAllChildren(std::vector& objects) { objects.push_back(this); for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); ++iter) { LLViewerObject* child = *iter; if (!child->isAvatar()) { child->addThisAndAllChildren(objects); } } } void LLViewerObject::addThisAndNonJointChildren(std::vector& objects) { objects.push_back(this); // don't add any attachments when temporarily selecting avatar if (isAvatar()) { return; } for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); ++iter) { LLViewerObject* child = *iter; if ( (!child->isAvatar()) && (!child->isJointChild())) { child->addThisAndNonJointChildren(objects); } } } BOOL LLViewerObject::isChild(LLViewerObject *childp) const { for (child_list_t::const_iterator iter = mChildList.begin(); iter != mChildList.end(); ++iter) { LLViewerObject* testchild = *iter; if (testchild == childp) return TRUE; } return FALSE; } // returns TRUE if at least one avatar is sitting on this object BOOL LLViewerObject::isSeat() const { for (child_list_t::const_iterator iter = mChildList.begin(); iter != mChildList.end(); ++iter) { LLViewerObject* child = *iter; if (child->isAvatar()) { return TRUE; } } return FALSE; } BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp) { if (mDrawable.isNull()) { return FALSE; } BOOL ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL); if(!ret) { return FALSE ; } LLDrawable* old_parent = mDrawable->mParent; mDrawable->mParent = parentp; gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); if( (old_parent != parentp && old_parent) || (parentp && parentp->isActive())) { // *TODO we should not be relying on setDrawable parent to call markMoved gPipeline.markMoved(mDrawable, FALSE); } else if (!mDrawable->isAvatar()) { mDrawable->updateXform(TRUE); /*if (!mDrawable->getSpatialGroup()) { mDrawable->movePartition(); }*/ } return ret; } // Show or hide particles, icon and HUD void LLViewerObject::hideExtraDisplayItems( BOOL hidden ) { if( mPartSourcep.notNull() ) { LLViewerPartSourceScript *partSourceScript = mPartSourcep.get(); partSourceScript->setSuspended( hidden ); } if( mText.notNull() ) { LLHUDText *hudText = mText.get(); hudText->setHidden( hidden ); } if( mIcon.notNull() ) { LLHUDIcon *hudIcon = mIcon.get(); hudIcon->setHidden( hidden ); } } 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); { LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromHandle(region_handle); if(regionp != mRegionp && regionp && mRegionp)//region cross { //this is the redundant position and region update, but it is necessary in case the viewer misses the following //position and region update messages from sim. //this redundant update should not cause any problems. LLVector3 delta_pos = mRegionp->getOriginAgent() - regionp->getOriginAgent(); setPositionParent(getPosition() + delta_pos); //update to the new region position immediately. setRegion(regionp) ; //change the region. } else { mRegionp = regionp ; } } if (!mRegionp) { U32 x, y; from_region_handle(region_handle, &x, &y); llerrs << "Object has invalid region " << x << ":" << y << "!" << llendl; return retval; } 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 = LLWorld::getInstance()->getRegionWidthInMeters(); const F32 MAX_HEIGHT = LLWorld::getInstance()->getRegionMaxHeight(); const F32 MIN_HEIGHT = LLWorld::getInstance()->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; LLVector3 old_angv = getAngularVelocity(); LLQuaternion new_rot; LLVector3 new_scale = getScale(); 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 #if MESH_ENABLED //clear cost and linkset cost mCostStale = true; if (isSelected()) { gFloaterTools->dirty(); } #endif //MESH_ENABLED 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); // fall through 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); // fall through 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 all name value pairs S32 nv_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_NameValue); if (nv_size > 0) { std::string name_value_list; mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_NameValue, name_value_list, block_num); setNameValueList(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::getFontSansSerif()); mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP); mText->setMaxLines(-1); mText->setSourceObject(this); mText->setOnHUDAttachment(isHUDAttachment()); } std::string temp_string; mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_Text, 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); // [RLVa:KB] - Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f if (rlv_handler_t::isEnabled()) { mText->setObjectText(temp_string); } // [/RLVa:KB] if (mDrawable.notNull()) { setChanged(MOVED | SILHOUETTE); gPipeline.markMoved(mDrawable, FALSE); // undamped } } else if (mText.notNull()) { mText->markDead(); mText = NULL; } std::string media_url; mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_MediaURL, media_url, block_num); //if (!media_url.empty()) //{ // llinfos << "WEBONPRIM media_url " << media_url << llendl; //} if (!mMedia && !media_url.empty()) { 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.empty()) { 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::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; paramsecond->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); // fall through 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); // fall through 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 #if MESH_ENABLED mCostStale = true; if (isSelected()) { gFloaterTools->dirty(); } #endif //MESH_ENABLED 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); dp->unpackUUID(owner_id, "Owner"); 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 && (value & 0x4)) { mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT); mText->setFont(LLFontGL::getFontSansSerif()); mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP); mText->setMaxLines(-1); // Set to match current agni behavior. mText->setSourceObject(this); mText->setOnHUDAttachment(isHUDAttachment()); } if (value & 0x4) { std::string temp_string; 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); // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-09 (RLVa-1.0.0f) | Added: RLVa-1.0.0f if (rlv_handler_t::isEnabled()) { mText->setObjectText(temp_string); } // [/RLVa:KB] setChanged(TEXTURE); } else if(mText.notNull()) { mText->markDead(); mText = NULL; } if (value & 0x200) { std::string media_url; dp->unpackString(media_url, "MediaURL"); 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); } else { deleteParticleSource(); } // Mark all extra parameters not used std::map::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; paramunpackU16(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->unpackF32(gain, "SoundGain"); dp->unpackU8(sound_flags, "SoundFlags"); dp->unpackF32(cutoff, "SoundRadius"); } if (value & 0x100) { std::string name_value_list; 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); } 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; } // [RLVa:KB] - Checked: 2010-03-16 (RLVa-1.1.0k) | Added: RLVa-1.1.0k if ( (rlv_handler_t::isEnabled()) && (sent_parentp->isAvatar()) && (sent_parentp->getID() == gAgent.getID()) ) { // Rezzed object that's being worn as an attachment (we're assuming this will be due to llAttachToAvatar()) S32 idxAttachPt = ATTACHMENT_ID_FROM_STATE(getState()); if (gRlvAttachmentLocks.isLockedAttachmentPoint(idxAttachPt, RLV_LOCK_ADD)) { // If this will end up on an "add locked" attachment point then treat the attach as a user action LLNameValue* nvItem = getNVPair("AttachItemID"); if (nvItem) { LLUUID idItem(nvItem->getString()); // URGENT-RLVa: [RLVa-1.2.0] At the moment llAttachToAvatar always seems to *add* if (idItem.notNull()) RlvAttachmentLockWatchdog::instance().onWearAttachment(idItem, RLV_WEAR_ADD); } } } // [/RLVa:KB] 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); } // Show particles, icon and HUD hideExtraDisplayItems( FALSE ); 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); // Hide particles, icon and HUD hideExtraDisplayItems( TRUE ); } } } 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 (sPingInterpolate) { LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mesgsys->getSender()); if (cdp) { F32 ping_delay = 0.5f * mTimeDilation * ( ((F32)cdp->getPingDelay()) * 0.001f + gFrameDTClamped); LLVector3 diff = getVelocity() * ping_delay; new_pos_parent += diff; } else { llwarns << "findCircuit() returned NULL; skipping interpolation" << llendl; } } ////////////////////////// // // Set the generic change flags... // // U32 packet_id = mesgsys->getCurrentRecvPacketID(); if (packet_id < mLatestRecvPacketID && mLatestRecvPacketID - packet_id < 65536) { //skip application of this message, it's old return retval; } mLatestRecvPacketID = packet_id; // Set the change flags for scale if (new_scale != getScale()) { setChanged(SCALED | SILHOUETTE); setScale(new_scale); // Must follow setting permYouOwner() } // 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; LLVector3 diff = new_pos_parent - test_pos_parent ; F32 mag_sqr = diff.magVecSquared() ; if(llfinite(mag_sqr)) { setPositionParent(new_pos_parent); } else { llwarns << "Can not move the object/avatar to an infinite location!" << llendl ; retval |= INVALID_UPDATE ; } 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 we're snapping the position by more than 0.5m, update LLViewerStats::mAgentPositionSnaps if ( isAvatar() && gAgent.getAvatarObject() == this && (mag_sqr > 0.25f) ) { LLViewerStats::getInstance()->mAgentPositionSnaps.push( diff.length() ); } } if (new_rot != mLastRot || new_angv != old_angv) { if (new_rot != mLastRot) { mLastRot = new_rot; setRotation(new_rot); } setChanged(ROTATED | SILHOUETTE); resetRot(); } if ( gShowObjectUpdates ) { // We want to see updates from our own avatar //if (!((mPrimitiveCode == LL_PCODE_LEGACY_AVATAR) && (((LLVOAvatar *) this)->mIsSelf)) // && mRegionp) if(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; for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* child = *iter; needs_refresh = needs_refresh || child->mUserSelected; } if (needs_refresh) { // if(isChanged(MOVED)) // Update "center" if this or children are selected, // and translate, scale, or rotate occurred on this. // Leave dialog refresh to happen always, as before. // LLSelectMgr::getInstance()->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 = mLastInterpUpdateSecs; 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 && sVelocityInterpolate && !isSelected()) { // calculate dt from last update F32 dt_raw = (F32)(time - mLastInterpUpdateSecs); F32 dt = mTimeDilation * dt_raw; if (!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 { // Move object based on it's velocity and rotation interpolateLinearMotion(time, dt); } } updateDrawable(FALSE); return TRUE; } // Move an object due to idle-time viewer side updates by iterpolating motion void LLViewerObject::interpolateLinearMotion(const F64 & time, const F32 & dt) { // linear motion // PHYSICS_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 guarantee that dt is never less than PHYSICS_TIMESTEP, theoretically // // *TODO: should also wrap linear accel/velocity in check // to see if object is selected, instead of explicitly // zeroing it out F64 time_since_last_update = time - mLastMessageUpdateSecs; if (time_since_last_update <= 0.0 || dt <= 0.f) { return; } LLVector3 accel = getAcceleration(); LLVector3 vel = getVelocity(); if (sMaxUpdateInterpolationTime <= 0.0) { // Old code path ... unbounded, simple interpolation if (!(accel.isExactlyZero() && vel.isExactlyZero())) { LLVector3 pos = (vel + (0.5f * (dt-PHYSICS_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); } } else if (!accel.isExactlyZero() || !vel.isExactlyZero()) // object is moving { // Object is moving, and hasn't been too long since we got an update from the server // Calculate predicted position and velocity LLVector3 new_pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt; LLVector3 new_v = accel * dt; if (time_since_last_update > sPhaseOutUpdateInterpolationTime && sPhaseOutUpdateInterpolationTime > 0.0) { // Haven't seen a viewer update in a while, check to see if the ciruit is still active if (mRegionp) { // The simulator will NOT send updates if the object continues normally on the path // predicted by the velocity and the acceleration (often gravity) sent to the viewer // So check to see if the circuit is blocked, which means the sim is likely in a long lag LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit( mRegionp->getHost() ); if (cdp) { // Find out how many seconds since last packet arrived on the circuit F64 time_since_last_packet = LLMessageSystem::getMessageTimeSeconds() - cdp->getLastPacketInTime(); if (!cdp->isAlive() || // Circuit is dead or blocked cdp->isBlocked() || // or doesn't seem to be getting any packets (time_since_last_packet > sPhaseOutUpdateInterpolationTime)) { // Start to reduce motion interpolation since we haven't seen a server update in a while F64 time_since_last_interpolation = time - mLastInterpUpdateSecs; F64 phase_out = 1.0; if (time_since_last_update > sMaxUpdateInterpolationTime) { // Past the time limit, so stop the object phase_out = 0.0; //llinfos << "Motion phase out to zero" << llendl; // Kill angular motion as well. Note - not adding this due to paranoia // about stopping rotation for llTargetOmega objects and not having it restart // setAngularVelocity(LLVector3::zero); } else if (mLastInterpUpdateSecs - mLastMessageUpdateSecs > sPhaseOutUpdateInterpolationTime) { // Last update was already phased out a bit phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) / (sMaxUpdateInterpolationTime - time_since_last_interpolation); //llinfos << "Continuing motion phase out of " << (F32) phase_out << llendl; } else { // Phase out from full value phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) / (sMaxUpdateInterpolationTime - sPhaseOutUpdateInterpolationTime); //llinfos << "Starting motion phase out of " << (F32) phase_out << llendl; } phase_out = llclamp(phase_out, 0.0, 1.0); new_pos = new_pos * ((F32) phase_out); new_v = new_v * ((F32) phase_out); } } } } new_pos = new_pos + getPositionRegion(); new_v = new_v + vel; // Clamp interpolated position to minimum underground and maximum region height LLVector3d new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos); F32 min_height; if (isAvatar()) { // Make a better guess about AVs not going underground min_height = LLWorld::getInstance()->resolveLandHeightGlobal(new_pos_global); min_height += (0.5f * getScale().mV[VZ]); } else { // This will put the object underground, but we can't tell if it will stop // at ground level or not min_height = LLWorld::getInstance()->getMinAllowedZ(this, new_pos_global); } new_pos.mV[VZ] = llmax(min_height, new_pos.mV[VZ]); new_pos.mV[VZ] = llmin(LLWorld::getInstance()->getRegionMaxHeight(), new_pos.mV[VZ]); // Check to see if it's going off the region LLVector3 temp(new_pos); if (temp.clamp(0.f, mRegionp->getWidth())) { // Going off this region, so see if we might end up on another region LLVector3d old_pos_global = mRegionp->getPosGlobalFromRegion(getPositionRegion()); new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos); // Re-fetch in case it got clipped above // Clip the positions to known regions LLVector3d clip_pos_global = LLWorld::getInstance()->clipToVisibleRegions(old_pos_global, new_pos_global); if (clip_pos_global != new_pos_global) { // Was clipped, so this means we hit a edge where there is no region to enter //llinfos << "Hit empty region edge, clipped predicted position to " << mRegionp->getPosRegionFromGlobal(clip_pos_global) // << " from " << new_pos << llendl; new_pos = mRegionp->getPosRegionFromGlobal(clip_pos_global); // Stop motion and get server update for bouncing on the edge new_v.clear(); setAcceleration(LLVector3::zero); } else { // Let predicted movement cross into another region //llinfos << "Predicting region crossing to " << new_pos << llendl; } } // Set new position and velocity setPositionRegion(new_pos); setVelocity(new_v); // for objects that are spinning but not translating, make sure to flag them as having moved setChanged(MOVED | SILHOUETTE); } // Update the last time we did anything mLastInterpUpdateSecs = time; } 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); /* Flawfinder: ignore */ } 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) { LLInventoryObject::object_list_t::iterator it = mInventory->begin(); LLInventoryObject::object_list_t::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( LLPointer& 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 = LLSelectMgr::getInstance()->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* oldItem = item; LLViewerInventoryItem* new_item = new LLViewerInventoryItem(oldItem); 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 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.empty()) { 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.push_front(info); } void LLViewerObject::removeInventoryListener(LLVOInventoryListener* listener) { if (listener == NULL) return; for (callback_list_t::iterator iter = mInventoryCallbacks.begin(); iter != mInventoryCallbacks.end(); ) { callback_list_t::iterator curiter = iter++; LLInventoryCallbackInfo* info = *curiter; if (info->mListener == listener) { delete info; mInventoryCallbacks.erase(curiter); break; } } } void LLViewerObject::clearInventoryListeners() { for_each(mInventoryCallbacks.begin(), mInventoryCallbacks.end(), DeletePointer()); mInventoryCallbacks.clear(); } void LLViewerObject::requestInventory() { mInventoryDirty = FALSE; if(mInventory) { //mInventory->clear() // will deref and delete it //delete mInventory; //mInventory = NULL; doInventoryCallback(); } // throw away duplicate requests else { fetchInventoryFromServer(); } } void LLViewerObject::fetchInventoryFromServer() { if (!mInventoryPending) { delete mInventory; mInventory = NULL; mInventoryDirty = FALSE; 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; std::string mFilename; #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) { llwarns << "LLViewerObject::processTaskInv object " << task_id << " does not exist." << llendl; return; } msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, object->mInventorySerialNum); LLFilenameAndTask* ft = new LLFilenameAndTask; ft->mTaskID = task_id; std::string unclean_filename; msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, unclean_filename); ft->mFilename = LLDir::getScrubbedFileName(unclean_filename); if(ft->mFilename.empty()) { 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 LLInventoryObject::object_list_t(); } LLPointer obj; obj = new LLInventoryObject(object->mID, LLUUID::null, LLAssetType::AT_CATEGORY, std::string("Contents")); object->mInventory->push_front(obj); object->doInventoryCallback(); delete ft; return; } gXferManager->requestFile(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ft->mFilename), 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, LLExtStat ext_status) { 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 std::string& filename) { LLMemType mt(LLMemType::MTYPE_OBJECT); std::string filename_and_local_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, filename); llifstream ifs(filename_and_local_path); if(ifs.good()) { char buffer[MAX_STRING]; /* Flawfinder: ignore */ // *NOTE: This buffer size is hard coded into scanf() below. char keyword[MAX_STRING]; /* Flawfinder: ignore */ if(mInventory) { mInventory->clear(); // will deref and delete it } else { mInventory = new LLInventoryObject::object_list_t; } while(ifs.good()) { ifs.getline(buffer, MAX_STRING); sscanf(buffer, " %254s", keyword); /* Flawfinder: ignore */ if(0 == strcmp("inv_item", keyword)) { LLPointer inv = new LLViewerInventoryItem; inv->importLegacyStream(ifs); mInventory->push_front(inv); } else if(0 == strcmp("inv_object", keyword)) { LLPointer 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); } else { llwarns << "unable to load task inventory: " << filename_and_local_path << llendl; } doInventoryCallback(); } void LLViewerObject::doInventoryCallback() { for (callback_list_t::iterator iter = mInventoryCallbacks.begin(); iter != mInventoryCallbacks.end(); ) { callback_list_t::iterator curiter = iter++; LLInventoryCallbackInfo* info = *curiter; if (info->mListener != NULL) { info->mListener->inventoryChanged(this, mInventory, mInventorySerialNum, info->mInventoryData); } else { llinfos << "LLViewerObject::doInventoryCallback() deleting bad listener entry." << llendl; delete info; mInventoryCallbacks.erase(curiter); } } 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 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); } void LLViewerObject::updateInventoryLocal(LLInventoryItem* item, U8 key) { LLPointer 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()); // do the internal logic const bool is_new = false; doUpdateInventory(task_item, key, is_new); } LLInventoryObject* LLViewerObject::getInventoryObject(const LLUUID& item_id) { LLInventoryObject* rv = NULL; if(mInventory) { LLInventoryObject::object_list_t::iterator it = mInventory->begin(); LLInventoryObject::object_list_t::iterator end = mInventory->end(); for ( ; it != end; ++it) { if((*it)->getUUID() == item_id) { rv = *it; break; } } } return rv; } void LLViewerObject::getInventoryContents(LLInventoryObject::object_list_t& objects) { if(mInventory) { LLInventoryObject::object_list_t::iterator it = mInventory->begin(); LLInventoryObject::object_list_t::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) { if (mInventoryDirty) llwarns << "Peforming inventory lookup for object " << mID << " that has dirty inventory!" << llendl; LLViewerInventoryItem* rv = NULL; if(mInventory) { LLViewerInventoryItem* item = NULL; LLInventoryObject::object_list_t::iterator it = mInventory->begin(); LLInventoryObject::object_list_t::iterator end = mInventory->end(); for( ; it != end; ++it) { LLInventoryObject* obj = *it; if (obj && obj->getType() != LLAssetType::AT_CATEGORY && obj->getType() != LLAssetType::AT_NONE) { // *FIX: gank-ass down cast! item = (LLViewerInventoryItem*)obj; if (item && item->getAssetUUID() == asset_id) { rv = item; break; } } } } return rv; } void LLViewerObject::updateViewerInventoryAsset( const LLViewerInventoryItem* item, const LLUUID& new_asset) { LLPointer task_item = new LLViewerInventoryItem(item); task_item->setAssetUUID(new_asset); // do the internal logic doUpdateInventory(task_item, TASK_INVENTORY_ITEM_KEY, false); } void LLViewerObject::setPixelAreaAndAngle(LLAgent &agent) { if (getVolume()) { //volumes calculate pixel area and angle per face return; } LLVector3 viewer_pos_agent = gAgentCamera.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: estimate - 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; LLViewerCamera* camera = LLViewerCamera::getInstance(); if (range < 0.001f || isHUDAttachment()) // range == zero { mAppAngle = 180.f; mPixelArea = (F32)camera->getScreenPixelArea(); } else { mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG; F32 pixels_per_meter = camera->getPixelMeterRatio() / range; mPixelArea = (pixels_per_meter * max_scale) * (pixels_per_meter * mid_scale); if (mPixelArea > camera->getScreenPixelArea()) { mAppAngle = 180.f; mPixelArea = (F32)camera->getScreenPixelArea(); } } } BOOL LLViewerObject::updateLOD() { return FALSE; } BOOL LLViewerObject::updateGeometry(LLDrawable *drawable) { return TRUE; } void LLViewerObject::updateGL() { } void LLViewerObject::updateFaceSize(S32 idx) { } 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) { llassert_always(LLWorld::getInstance()->getRegionFromHandle(getRegion()->getHandle())); gObjectList.addToMap(this); mOnMap = TRUE; } } else { if (mOnMap) { gObjectList.removeFromMap(this); mOnMap = FALSE; } } } } #if MESH_ENABLED void LLViewerObject::setObjectCost(F32 cost) { mObjectCost = cost; mCostStale = false; if (isSelected()) { gFloaterTools->dirty(); } } void LLViewerObject::setLinksetCost(F32 cost) { mLinksetCost = cost; mCostStale = false; if (isSelected()) { gFloaterTools->dirty(); } } void LLViewerObject::setPhysicsCost(F32 cost) { mPhysicsCost = cost; mCostStale = false; if (isSelected()) { gFloaterTools->dirty(); } } void LLViewerObject::setLinksetPhysicsCost(F32 cost) { mLinksetPhysicsCost = cost; mCostStale = false; if (isSelected()) { gFloaterTools->dirty(); } } F32 LLViewerObject::getObjectCost() { if (mCostStale) { gObjectList.updateObjectCost(this); } return mObjectCost; } F32 LLViewerObject::getLinksetCost() { if (mCostStale) { gObjectList.updateObjectCost(this); } return mLinksetCost; } F32 LLViewerObject::getPhysicsCost() { if (mCostStale) { gObjectList.updateObjectCost(this); } return mPhysicsCost; } F32 LLViewerObject::getLinksetPhysicsCost() { if (mCostStale) { gObjectList.updateObjectCost(this); } return mLinksetPhysicsCost; } F32 LLViewerObject::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const { return 0.f; } U32 LLViewerObject::getTriangleCount() const { return 0; } U32 LLViewerObject::getHighLODTriangleCount() { return 0; } #endif //MESH_ENABLED void LLViewerObject::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax) { if(mDrawable.isNull()) return; LLVector4a center; center.load3(getRenderPosition().mV); LLVector4a size; size.load3(getScale().mV); newMin.setSub(center, size); newMax.setAdd(center, size); mDrawable->setPositionGroup(center); } F32 LLViewerObject::getBinRadius() { if (mDrawable.notNull()) { const LLVector4a* ext = mDrawable->getSpatialExtents(); LLVector4a diff; diff.setSub(ext[1], ext[0]); return diff.getLength3().getF32(); } return getScale().magVec(); } 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() { } 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(LLViewerTexture::BOOST_SELECTED); } if (isSculpted() #if MESH_ENABLED && !isMesh() #endif //MESH_ENABLED ) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); LLUUID sculpt_id = sculpt_params->getSculptTexture(); LLViewerTextureManager::getFetchedTexture(sculpt_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)->setBoostLevel(LLViewerTexture::BOOST_SELECTED); } if (boost_children) { for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* child = *iter; child->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 std::string& data) { // cout << "LLViewerObject::addNVPair() with ---" << data << "---" << endl; LLNameValue *nv = new LLNameValue(data.c_str()); // 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 std::string& 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); 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 std::string& 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(mRegionp) { 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 { if(mRegionp) { LLVector3d position_global = mRegionp->getPosGlobalFromRegion(getPositionRegion()); if (isAttachment()) { position_global = gAgent.getPosGlobalFromAgent(getRenderPosition()); } return position_global; } else { LLVector3d position_global(getPosition()); 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()); } else { mPositionRegion = getPosition(); } 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 MESH_ENABLED if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED)) { LLVOAvatar* avatar = getAvatar(); if (avatar) { return avatar->getPositionAgent(); } } #endif //MESH_ENABLED if (mDrawable.isNull() || mDrawable->getGeneration() < 0) { return getPositionAgent(); } else { return mDrawable->getPositionAgent(); } } const LLVector3 LLViewerObject::getPivotPositionAgent() const { return getRenderPosition(); } const LLQuaternion LLViewerObject::getRenderRotation() const { LLQuaternion ret; #if MESH_ENABLED if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED)) { return ret; } #endif //MESH_ENABLED 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; // *FIX: 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, damped); //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); updateDrawable(damped); } 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, damped); } else { LLViewerObject::setPosition(pos_edit, damped); mPositionRegion = pos_edit; mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion); } } LLViewerObject* LLViewerObject::getRootEdit() const { const LLViewerObject* root = this; while (root->mParent && !(root->mJointInfo || ((LLViewerObject*)root->mParent)->isAvatar()) ) { root = (LLViewerObject*)root->mParent; } return (LLViewerObject*)root; } BOOL LLViewerObject::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, BOOL pick_transparent, S32* face_hit, LLVector3* intersection, LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal) { return false; } BOOL LLViewerObject::lineSegmentBoundingBox(const LLVector3& start, const LLVector3& end) { if (mDrawable.isNull() || mDrawable->isDead()) { return FALSE; } const LLVector4a* ext = mDrawable->getSpatialExtents(); //VECTORIZE THIS LLVector4a center; center.setAdd(ext[1], ext[0]); center.mul(0.5f); LLVector4a size; size.setSub(ext[1], ext[0]); size.mul(0.5f); LLVector4a starta, enda; starta.load3(start.mV); enda.load3(end.mV); return LLLineSegmentBoxIntersect(starta, enda, center, size); } 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; // TODO: update materials with new image } } std::string LLViewerObject::getMediaURL() const { if (mMedia) { return mMedia->mMediaURL; } else { return std::string(); } } void LLViewerObject::setMediaURL(const std::string& media_url) { LLMemType mt(LLMemType::MTYPE_OBJECT); if (!mMedia) { mMedia = new LLViewerObjectMedia; mMedia->mMediaURL = media_url; mMedia->mPassedWhitelist = FALSE; // TODO: update materials with new image } else if (mMedia->mMediaURL != media_url) { mMedia->mMediaURL = media_url; mMedia->mPassedWhitelist = FALSE; // TODO: update materials with new image } } 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 *new_images; new_images = new LLPointer[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() ); } /* Obsolete, we use MultipleObjectUpdate instead //// 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_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); } // 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); // This doesn't work, don't get any textures. // if (mDrawable.notNull() && mDrawable->isVisible()) // { const LLUUID& image_id = getTE(te)->getID(); mTEImages[te] = LLViewerTextureManager::getFetchedTexture(image_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); // } } void LLViewerObject::setTEImage(const U8 te, LLViewerTexture *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] = LLViewerTextureManager::getFetchedTexture(uuid, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host); setChanged(TEXTURE); if (mDrawable.notNull()) { gPipeline.markTextured(mDrawable); } } return retval; } void LLViewerObject::changeTEImage(S32 index, LLViewerTexture* new_image) { if(index < 0 || index >= getNumTEs()) { return ; } mTEImages[index] = new_image ; } 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 LLColor3& color) { return setTEColor(te, LLColor4(color)); } 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); if (mDrawable.notNull() && retval) { // These should only happen on updates which are not the initial update. dirtyMesh(); } } 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); } return retval; } S32 LLViewerObject::setTEMediaTexGen(const U8 te, const U8 media) { S32 retval = 0; const LLTextureEntry *tep = getTE(te); if (!tep) { llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl; } else if (media != tep->getMediaTexGen()) { retval = LLPrimitive::setTEMediaTexGen(te, media); setChanged(TEXTURE); } 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); } 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) { // 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::setTEGlow(const U8 te, const F32 glow) { S32 retval = 0; const LLTextureEntry *tep = getTE(te); if (!tep) { llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl; } else if (glow != tep->getGlow()) { retval = LLPrimitive::setTEGlow(te, glow); setChanged(TEXTURE); if (mDrawable.notNull() && retval) { gPipeline.markTextured(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; } LLViewerTexture *LLViewerObject::getTEImage(const U8 face) const { // llassert(mTEImages); if (face < getNumTEs()) { LLViewerTexture* image = mTEImages[face]; if (image) { return image; } else { return (LLViewerTexture*)(LLViewerFetchedTexture::sDefaultImagep); } } llerrs << llformat("Requested Image from invalid face: %d/%d",face,getNumTEs()) << llendl; return NULL; } void LLViewerObject::fitFaceTexture(const U8 face) { llinfos << "fitFaceTexture not implemented" << llendl; } LLBBox LLViewerObject::getBoundingBoxAgent() const { LLVector3 position_agent; LLQuaternion rot; LLViewerObject* avatar_parent = NULL; LLViewerObject* root_edit = (LLViewerObject*)getRootEdit(); if (root_edit) { avatar_parent = (LLViewerObject*)root_edit->getParent(); } if (avatar_parent && avatar_parent->isAvatar() && root_edit && root_edit->mDrawable.notNull() && root_edit->mDrawable->getXform()->getParent()) { 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 ) { LLInventoryObject::object_list_t::const_iterator it = mInventory->begin(); LLInventoryObject::object_list_t::const_iterator end = mInventory->end(); for( ; it != end ; ++it ) { if( (*it)->getType() == type ) { ++count; } } } return count; } void LLViewerObject::setCanSelect(BOOL canSelect) { mbCanSelect = canSelect; for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* child = *iter; child->mbCanSelect = canSelect; } } void LLViewerObject::setDebugText(const std::string &utf8text) { if (utf8text.empty() && !mText) { return; } if (!mText) { mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT); mText->setFont(LLFontGL::getFontSansSerif()); 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(); } // std::string LLViewerObject::getDebugText() { if(mText) return mText->getStringUTF8(); return ""; } // void LLViewerObject::setIcon(LLViewerTexture* icon_image) { if (!mIcon) { mIcon = (LLHUDIcon *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_ICON); mIcon->setSourceObject(this); mIcon->setImage(icon_image); // *TODO: 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::setParticleSource(const LLPartSysData& particle_parameters, const LLUUID& owner_id) { if (mPartSourcep) { deleteParticleSource(); } LLPointer pss = LLViewerPartSourceScript::createPSS(this, particle_parameters); mPartSourcep = pss; if (mPartSourcep) { mPartSourcep->setOwnerUUID(owner_id); if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID) { LLViewerTexture* image; if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null) { image = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.tga"); } else { image = LLViewerTextureManager::getFetchedTexture(mPartSourcep->mPartSysData.mPartImageID); } mPartSourcep->setImage(image); } } LLViewerPartSim::getInstance()->addPartSource(pss); } 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 { LLPointer pss = LLViewerPartSourceScript::unpackPSS(this, NULL, block_num); //If the owner is muted, don't create the system if(LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagParticles)) return; // 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; LLViewerPartSim::getInstance()->addPartSource(pss); } } if (mPartSourcep) { if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID) { LLViewerTexture* image; if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null) { image = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.j2c"); } else { image = LLViewerTextureManager::getFetchedTexture(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 { LLPointer pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp); //If the owner is muted, don't create the system if(LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagParticles)) return; // 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; LLViewerPartSim::getInstance()->addPartSource(pss); } } if (mPartSourcep) { if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID) { LLViewerTexture* image; if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null) { image = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.j2c"); } else { image = LLViewerTextureManager::getFetchedTexture(mPartSourcep->mPartSysData.mPartImageID); } mPartSourcep->setImage(image); } } } void LLViewerObject::deleteParticleSource() { if (mPartSourcep.notNull()) { mPartSourcep->setDead(); mPartSourcep = NULL; } } // virtual void LLViewerObject::updateDrawable(BOOL force_damped) { if (mDrawable.notNull() && !mDrawable->isState(LLDrawable::ON_MOVE_LIST) && isChanged(MOVED)) { 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 ... (getParent() && !((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) { return; } if (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 (flags & LL_SOUND_FLAG_STOP) { // Just shut off the sound mAudioSourcep->play(LLUUID::null); } return; } if (flags & LL_SOUND_FLAG_LOOP && mAudioSourcep && mAudioSourcep->isLoop() && mAudioSourcep->getCurrentData() && mAudioSourcep->getCurrentData()->getID() == audio_uuid) { //llinfos << "Already playing this sound on a loop, ignoring" << llendl; return; } // don't clean up before previous sound is done. Solves: SL-33486 if ( mAudioSourcep && mAudioSourcep->isDone() ) { gAudiop->cleanupAudioSource(mAudioSourcep); mAudioSourcep = NULL; } if (mAudioSourcep && mAudioSourcep->isMuted() && mAudioSourcep->getCurrentData() && mAudioSourcep->getCurrentData()->getID() == audio_uuid) { //llinfos << "Already having this sound as muted sound, ignoring" << llendl; return; } getAudioSource(owner_id); if (mAudioSourcep) { BOOL queue = flags & LL_SOUND_FLAG_QUEUE; mAudioGain = gain; mAudioSourcep->setGain(gain); mAudioSourcep->setLoop(flags & LL_SOUND_FLAG_LOOP); mAudioSourcep->setSyncMaster(flags & LL_SOUND_FLAG_SYNC_MASTER); mAudioSourcep->setSyncSlave(flags & LL_SOUND_FLAG_SYNC_SLAVE); mAudioSourcep->setQueueSounds(queue); if(!queue) // stop any current sound first to avoid "farts of doom" (SL-1541) -MG { mAudioSourcep->play(LLUUID::null); } // Play this sound if region maturity permits if( gAgent.canAccessMaturityAtGlobal(this->getPositionGlobal()) ) { //llinfos << "Playing attached sound " << audio_uuid << llendl; mAudioSourcep->play(audio_uuid); } } } LLAudioSource *LLViewerObject::getAudioSource(const LLUUID& owner_id) { 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) { mAudioGain = gain; mAudioSourcep->setGain(mAudioGain); } } //---------------------------------------------------------------------------- bool LLViewerObject::unpackParameterEntry(U16 param_type, LLDataPacker *dp) { #if MESH_ENABLED if (LLNetworkData::PARAMS_MESH == param_type) { param_type = LLNetworkData::PARAMS_SCULPT; } #endif //MESH_ENABLED 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; } case LLNetworkData::PARAMS_SCULPT: { new_block = new LLSculptParams(); break; } case LLNetworkData::PARAMS_LIGHT_IMAGE: { new_block = new LLLightImageParams(); break; } default: { llinfos << "Unknown param type. (" << llformat("0x%2x",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::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 && 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 (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* child = *iter; child->setDrawableState(state, recursive); } } } void LLViewerObject::clearDrawableState(U32 state, BOOL recursive) { if (mDrawable) { mDrawable->clearState(state); } if (recursive) { for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* child = *iter; child->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 (!LLViewerLogin::getInstance()->isInProductionGrid() && (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 (!LLViewerLogin::getInstance()->isInProductionGrid() && (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 (!LLViewerLogin::getInstance()->isInProductionGrid() && (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 (!LLViewerLogin::getInstance()->isInProductionGrid() && (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 (!LLViewerLogin::getInstance()->isInProductionGrid() && (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 (!LLViewerLogin::getInstance()->isInProductionGrid() && (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 { // [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0b) return !flagInventoryEmpty() && (permYouOwner() || permModify()) && (!gRlvHandler.hasBehaviour(RLV_BHVR_EDIT)); // [/RLVa:KB] // return !flagInventoryEmpty() && (permYouOwner() || permModify()); } LLViewerObject::LLInventoryCallbackInfo::~LLInventoryCallbackInfo() { if (mListener) { mListener->clearVOInventoryListener(); } } void LLViewerObject::updateVolume(const LLVolumeParams& volume_params) { if (setVolume(volume_params, 1)) // *FIX: 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); } } bool LLViewerObject::getIncludeInSearch() const { return ((mFlags & FLAGS_INCLUDE_IN_SEARCH) != 0); } void LLViewerObject::setIncludeInSearch(bool include_in_search) { if (include_in_search) { mFlags |= FLAGS_INCLUDE_IN_SEARCH; } else { mFlags &= ~FLAGS_INCLUDE_IN_SEARCH; } } void LLViewerObject::setRegion(LLViewerRegion *regionp) { if (!regionp) { llwarns << "viewer object set region to NULL" << llendl; } mLatestRecvPacketID = 0; mRegionp = regionp; for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) { LLViewerObject* child = *i; child->setRegion(regionp); } setChanged(MOVED | SILHOUETTE); updateDrawable(FALSE); } bool LLViewerObject::specialHoverCursor() const { return (mFlags & FLAGS_USE_PHYSICS) || (mFlags & FLAGS_HANDLE_TOUCH) || (mClickAction != 0); } void LLViewerObject::updateFlags(BOOL physics_changed) { 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() ); #if MESH_ENABLED if (physics_changed) { gMessageSystem->nextBlock("ExtraPhysics"); gMessageSystem->addU8("PhysicsShapeType", getPhysicsShapeType() ); gMessageSystem->addF32("Density", getPhysicsDensity() ); gMessageSystem->addF32("Friction", getPhysicsFriction() ); gMessageSystem->addF32("Restitution", getPhysicsRestitution() ); gMessageSystem->addF32("GravityMultiplier", getPhysicsGravity() ); } #endif //MESH_ENABLED 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; } #if MESH_ENABLED void LLViewerObject::setPhysicsShapeType(U8 type) { mPhysicsShapeUnknown = false; mPhysicsShapeType = type; mCostStale = true; } void LLViewerObject::setPhysicsGravity(F32 gravity) { mPhysicsGravity = gravity; } void LLViewerObject::setPhysicsFriction(F32 friction) { mPhysicsFriction = friction; } void LLViewerObject::setPhysicsDensity(F32 density) { mPhysicsDensity = density; } void LLViewerObject::setPhysicsRestitution(F32 restitution) { mPhysicsRestitution = restitution; } U8 LLViewerObject::getPhysicsShapeType() const { if (mPhysicsShapeUnknown) { mPhysicsShapeUnknown = false; gObjectList.updatePhysicsFlags(this); } return mPhysicsShapeType; } #endif //MESH_ENABLED 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; } U32 LLViewerObject::getPartitionType() const { return LLViewerRegion::PARTITION_NONE; } void LLViewerObject::dirtySpatialGroup(BOOL priority) const { if (mDrawable) { LLSpatialGroup* group = mDrawable->getSpatialGroup(); if (group) { group->dirtyGeom(); gPipeline.markRebuild(group, priority); } } } void LLViewerObject::dirtyMesh() { if (mDrawable) { LLSpatialGroup* group = mDrawable->getSpatialGroup(); if (group) { group->dirtyMesh(); } } } F32 LLAlphaObject::getPartSize(S32 idx) { return 0.f; } // virtual void LLStaticViewerObject::updateDrawable(BOOL force_damped) { // Force an immediate rebuild on any update if (mDrawable.notNull()) { mDrawable->updateXform(TRUE); gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); } clearChanged(SHIFTED); } void LLViewerObject::saveUnselectedChildrenPosition(std::vector& positions) { if(mChildList.empty() || !positions.empty()) { return ; } for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* childp = *iter; if (!childp->isSelected() && childp->mDrawable.notNull()) { positions.push_back(childp->getPositionEdit()); } } return ; } void LLViewerObject::saveUnselectedChildrenRotation(std::vector& rotations) { if(mChildList.empty()) { return ; } for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* childp = *iter; if (!childp->isSelected() && childp->mDrawable.notNull()) { rotations.push_back(childp->getRotationEdit()); } } return ; } //counter-rotation void LLViewerObject::resetChildrenRotationAndPosition(const std::vector& rotations, const std::vector& positions) { if(mChildList.empty()) { return ; } S32 index = 0 ; LLQuaternion inv_rotation = ~getRotationEdit() ; LLVector3 offset = getPositionEdit() ; for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* childp = *iter; if (!childp->isSelected() && childp->mDrawable.notNull()) { if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR) { childp->setRotation(rotations[index] * inv_rotation); childp->setPosition((positions[index] - offset) * inv_rotation); LLManip::rebuild(childp); } else //avatar { LLVector3 reset_pos = (positions[index] - offset) * inv_rotation ; LLQuaternion reset_rot = rotations[index] * inv_rotation ; ((LLVOAvatar*)childp)->mDrawable->mXform.setPosition(reset_pos); ((LLVOAvatar*)childp)->mDrawable->mXform.setRotation(reset_rot) ; ((LLVOAvatar*)childp)->mDrawable->getVObj()->setPosition(reset_pos, TRUE); ((LLVOAvatar*)childp)->mDrawable->getVObj()->setRotation(reset_rot, TRUE) ; LLManip::rebuild(childp); } index++; } } return ; } //counter-translation void LLViewerObject::resetChildrenPosition(const LLVector3& offset, BOOL simplified) { if(mChildList.empty()) { return ; } LLVector3 child_offset; if(simplified) //translation only, rotation matrix does not change { child_offset = offset * ~getRotation(); } else //rotation matrix might change too. { if (isAttachment() && mDrawable.notNull()) { LLXform* attachment_point_xform = mDrawable->getXform()->getParent(); LLQuaternion parent_rotation = getRotation() * attachment_point_xform->getWorldRotation(); child_offset = offset * ~parent_rotation; } else { child_offset = offset * ~getRenderRotation(); } } for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* childp = *iter; if (!childp->isSelected() && childp->mDrawable.notNull()) { if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR) { childp->setPosition(childp->getPosition() + child_offset); LLManip::rebuild(childp); } else //avatar { LLVector3 reset_pos = ((LLVOAvatar*)childp)->mDrawable->mXform.getPosition() + child_offset ; ((LLVOAvatar*)childp)->mDrawable->mXform.setPosition(reset_pos); ((LLVOAvatar*)childp)->mDrawable->getVObj()->setPosition(reset_pos); LLManip::rebuild(childp); } } } return ; } // S32 LLViewerObject::getAttachmentPoint() { return ((S32)((((U8)mState & AGENT_ATTACH_MASK) >> 4) | (((U8)mState & ~AGENT_ATTACH_MASK) << 4))); } std::string LLViewerObject::getAttachmentPointName() { S32 point = getAttachmentPoint(); if((point > 0) && (point < 39)) { return gAgent.getAvatarObject()->mAttachmentPoints[point]->getName(); } return llformat("unsupported point %d", point); } // EObjectUpdateType LLViewerObject::getLastUpdateType() const { return mLastUpdateType; } void LLViewerObject::setLastUpdateType(EObjectUpdateType last_update_type) { mLastUpdateType = last_update_type; } BOOL LLViewerObject::getLastUpdateCached() const { return mLastUpdateCached; } void LLViewerObject::setLastUpdateCached(BOOL last_update_cached) { mLastUpdateCached = last_update_cached; } const LLUUID &LLViewerObject::extractAttachmentItemID() { LLUUID item_id = LLUUID::null; LLNameValue* item_id_nv = getNVPair("AttachItemID"); if (item_id_nv) { const char* s = item_id_nv->getString(); if (s) { item_id.set(s); } } setAttachmentItemID(item_id); return getAttachmentItemID(); } //virtual LLVOAvatar* LLViewerObject::getAvatar() const { if (isAttachment()) { LLViewerObject* vobj = (LLViewerObject*) getParent(); while (vobj && !vobj->isAvatar()) { vobj = (LLViewerObject*) vobj->getParent(); } return (LLVOAvatar*) vobj; } return NULL; } #if MESH_ENABLED class ObjectPhysicsProperties : public LLHTTPNode { public: virtual void post( ResponsePtr responder, const LLSD& context, const LLSD& input) const { LLSD object_data = input["body"]["ObjectData"]; S32 num_entries = object_data.size(); for ( S32 i = 0; i < num_entries; i++ ) { LLSD& curr_object_data = object_data[i]; U32 local_id = curr_object_data["LocalID"].asInteger(); // Iterate through nodes at end, since it can be on both the regular AND hover list struct f : public LLSelectedNodeFunctor { U32 mID; f(const U32& id) : mID(id) {} virtual bool apply(LLSelectNode* node) { return (node->getObject() && node->getObject()->mLocalID == mID ); } } func(local_id); LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(&func); if (node) { // The LLSD message builder doesn't know how to handle U8, so we need to send as S8 and cast U8 type = (U8)curr_object_data["PhysicsShapeType"].asInteger(); F32 density = (F32)curr_object_data["Density"].asReal(); F32 friction = (F32)curr_object_data["Friction"].asReal(); F32 restitution = (F32)curr_object_data["Restitution"].asReal(); F32 gravity = (F32)curr_object_data["GravityMultiplier"].asReal(); node->getObject()->setPhysicsShapeType(type); node->getObject()->setPhysicsGravity(gravity); node->getObject()->setPhysicsFriction(friction); node->getObject()->setPhysicsDensity(density); node->getObject()->setPhysicsRestitution(restitution); } } dialog_refresh_all(); }; }; LLHTTPRegistration gHTTPRegistrationObjectPhysicsProperties("/message/ObjectPhysicsProperties"); #endif //MESH_ENABLED