Commits

Vadim Savchuk committed 84846ef Merge

Merge from default branch

Comments (0)

Files changed (15)

indra/newview/llagentwearables.cpp

 									const std::string new_name)
 {
 	LLWearable* old_wearable = getWearable(type, index);
-	if(!old_wearable) return;
-	bool name_changed = !new_name.empty() && (new_name != old_wearable->getName());
-	if (name_changed || old_wearable->isDirty() || old_wearable->isOldVersion())
+	if (old_wearable && (old_wearable->isDirty() || old_wearable->isOldVersion()))
 	{
 		LLUUID old_item_id = old_wearable->getItemID();
 		LLWearable* new_wearable = LLWearableList::instance().createCopy(old_wearable);
 		if (item)
 		{
 			std::string item_name = item->getName();
-			if (name_changed)
+			bool name_changed = false;
+			if (!new_name.empty() && (new_name != item->getName()))
 			{
 				llinfos << "saveWearable changing name from "  << item->getName() << " to " << new_name << llendl;
 				item_name = new_name;
+				name_changed = true;
 			}
 			// Update existing inventory item
 			LLPointer<LLViewerInventoryItem> template_item =
 			LLViewerObject *objectp = (*attachment_iter);
 			if (objectp)
 			{
-				LLUUID object_item_id = objectp->getItemID();
+				LLUUID object_item_id = objectp->getAttachmentItemID();
 				if (requested_item_ids.find(object_item_id) != requested_item_ids.end())
 				{
 					// Object currently worn, was requested.
 		msg->nextBlockFast(_PREHASH_ObjectData );
 		msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
 		msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
-		msg->addU8Fast(_PREHASH_AttachmentPt, 0 );	// Wear at the previous or default attachment point
+		if (gSavedSettings.getBOOL("MultipleAttachments"))
+			msg->addU8Fast(_PREHASH_AttachmentPt, 0 | ATTACHMENT_ADD );
+		else
+			msg->addU8Fast(_PREHASH_AttachmentPt, 0 );	// Wear at the previous or default attachment point
 		pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
 		msg->addStringFast(_PREHASH_Name, item->getName());
 		msg->addStringFast(_PREHASH_Description, item->getDescription());

indra/newview/llagentwearablesfetch.cpp

 				{
 					LLViewerObject* attached_object = (*attachment_iter);
 					if (!attached_object) continue;
-					const LLUUID& item_id = attached_object->getItemID();
+					const LLUUID& item_id = attached_object->getAttachmentItemID();
 					if (item_id.isNull()) continue;
 					ids.push_back(item_id);
 				}

indra/newview/llappearancemgr.cpp

 	}
 }
 
-void LLAppearanceMgr::enforceItemCountLimits()
+S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,
+												 LLAssetType::EType type,
+												 S32 max_items,
+												 LLInventoryModel::item_array_t& items_to_kill)
+{
+	S32 to_kill_count = 0;
+
+	LLInventoryModel::item_array_t items;
+	getDescendentsOfAssetType(cat_id, items, type, false);
+	LLInventoryModel::item_array_t curr_items = items;
+	removeDuplicateItems(items);
+	if (max_items > 0)
+	{
+		filterWearableItems(items, max_items);
+	}
+	LLInventoryModel::item_array_t kill_items;
+	item_array_diff(curr_items,items,kill_items);
+	for (LLInventoryModel::item_array_t::iterator it = kill_items.begin();
+		 it != kill_items.end();
+		 ++it)
+	{
+		items_to_kill.push_back(*it);
+		to_kill_count++;
+	}
+	return to_kill_count;
+}
+	
+												 
+void LLAppearanceMgr::enforceItemRestrictions()
 {
 	S32 purge_count = 0;
-	
-	LLInventoryModel::item_array_t body_items;
-	getDescendentsOfAssetType(getCOF(), body_items, LLAssetType::AT_BODYPART, false);
-	LLInventoryModel::item_array_t curr_body_items = body_items;
-	removeDuplicateItems(body_items);
-	filterWearableItems(body_items, 1);
-	LLInventoryModel::item_array_t kill_body_items;
-	item_array_diff(curr_body_items,body_items,kill_body_items);
-	for (LLInventoryModel::item_array_t::iterator it = kill_body_items.begin();
-		 it != kill_body_items.end();
-		 ++it)
+	LLInventoryModel::item_array_t items_to_kill;
+
+	purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_BODYPART,
+											  1, items_to_kill);
+	purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_CLOTHING,
+											  LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill);
+	purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_OBJECT,
+											  -1, items_to_kill);
+
+	if (items_to_kill.size()>0)
 	{
-		LLViewerInventoryItem *item = *it;
-		llinfos << "purging dup body part " << item->getName() << llendl;
-		gInventory.purgeObject(item->getUUID());
-		purge_count++;
-	}
-	
-	LLInventoryModel::item_array_t wear_items;
-	getDescendentsOfAssetType(getCOF(), wear_items, LLAssetType::AT_CLOTHING, false);
-	LLInventoryModel::item_array_t curr_wear_items = wear_items;
-	removeDuplicateItems(wear_items);
-	filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE);
-	LLInventoryModel::item_array_t kill_wear_items;
-	item_array_diff(curr_wear_items,wear_items,kill_wear_items);
-	for (LLInventoryModel::item_array_t::iterator it = kill_wear_items.begin();
-		 it != kill_wear_items.end();
-		 ++it)
-	{
-		LLViewerInventoryItem *item = *it;
-		llinfos << "purging excess clothing item " << item->getName() << llendl;
-		gInventory.purgeObject(item->getUUID());
-		purge_count++;
-	}
-
-	if (purge_count>0)
-	{
+		for (LLInventoryModel::item_array_t::iterator it = items_to_kill.begin();
+			 it != items_to_kill.end();
+			 ++it)
+		{
+			LLViewerInventoryItem *item = *it;
+			llinfos << "purging duplicate or excess item " << item->getName() << llendl;
+			gInventory.purgeObject(item->getUUID());
+		}
 		gInventory.notifyObservers();
 	}
 }
 
 	// Remove duplicate or excess wearables. Should normally be enforced at the UI level, but
 	// this should catch anything that gets through.
-	enforceItemCountLimits();
+	enforceItemRestrictions();
 	
 	// update dirty flag to see if the state of the COF matches
 	// the saved outfit stored as a folder link
 
 	switch (item_to_remove->getType())
 	{
-	case LLAssetType::AT_CLOTHING:
-		if (get_is_item_worn(id_to_remove))
-		{
-			//*TODO move here the exact removing code from LLWearableBridge::removeItemFromAvatar in the future
-			LLWearableBridge::removeItemFromAvatar(item_to_remove);
-		}
-		break;
-	case LLAssetType::AT_OBJECT:
-		gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
-		gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
-		gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
-		gMessageSystem->addUUIDFast(_PREHASH_ItemID, item_to_remove->getLinkedUUID());
-		gMessageSystem->sendReliable( gAgent.getRegion()->getHost());
-
-		{
-			// this object might have been selected, so let the selection manager know it's gone now
-			LLViewerObject *found_obj = gObjectList.findObject(item_to_remove->getLinkedUUID());
-			if (found_obj)
+		case LLAssetType::AT_CLOTHING:
+			if (get_is_item_worn(id_to_remove))
 			{
-				LLSelectMgr::getInstance()->remove(found_obj);
-			};
-		}
-	default: break;
+				//*TODO move here the exact removing code from LLWearableBridge::removeItemFromAvatar in the future
+				LLWearableBridge::removeItemFromAvatar(item_to_remove);
+			}
+			break;
+		case LLAssetType::AT_OBJECT:
+			LLVOAvatarSelf::detachAttachmentIntoInventory(item_to_remove->getLinkedUUID());
+		default:
+			break;
 	}
 
 	// *HACK: Force to remove garbage from COF.
 	mAttachmentInvLinkEnabled = val;
 }
 
-// BAP TODO - mRegisteredAttachments is currently maintained but not used for anything.  Consider yanking.
 void dumpAttachmentSet(const std::set<LLUUID>& atts, const std::string& msg)
 {
        llinfos << msg << llendl;
 
 void LLAppearanceMgr::registerAttachment(const LLUUID& item_id)
 {
-       mRegisteredAttachments.insert(item_id);
 	   gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
 
 	   if (mAttachmentInvLinkEnabled)
 
 void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id)
 {
-       mRegisteredAttachments.erase(item_id);
 	   gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
 
 	   if (mAttachmentInvLinkEnabled)
 	   }
 }
 
-void LLAppearanceMgr::linkRegisteredAttachments()
-{
-	for (std::set<LLUUID>::iterator it = mRegisteredAttachments.begin();
-		 it != mRegisteredAttachments.end();
-		 ++it)
-	{
-		LLUUID item_id = *it;
-		addCOFItemLink(item_id, false);
-	}
-	mRegisteredAttachments.clear();
-}
-
 BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const
 {
 	return gInventory.isObjectDescendentOf(obj_id, getCOF());
 	 LLInventoryModel::item_array_t items;
 	 LLLinkedItemIDMatches find_links(gInventory.getLinkedItemID(obj_id));
 	 gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(),
-	 cats,
-	 items,
+									 cats,
+									 items,
 	 LLInventoryModel::EXCLUDE_TRASH,
 	 find_links);
 

indra/newview/llappearancemgr.h

 	void renameOutfit(const LLUUID& outfit_id);
 	void takeOffOutfit(const LLUUID& cat_id);
 	void addCategoryToCurrentOutfit(const LLUUID& cat_id);
-	void enforceItemCountLimits();
+	S32 findExcessOrDuplicateItems(const LLUUID& cat_id,
+								   LLAssetType::EType type,
+								   S32 max_items,
+								   LLInventoryModel::item_array_t& items_to_kill);
+	void enforceItemRestrictions();
 
 	// Copy all items and the src category itself.
 	void shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
 	void unregisterAttachment(const LLUUID& item_id);
 	void registerAttachment(const LLUUID& item_id);
 	void setAttachmentInvLinkEnable(bool val);
-	void linkRegisteredAttachments();
 
 	// utility function for bulk linking.
 	void linkAll(const LLUUID& category,
 
 	void setOutfitLocked(bool locked);
 
-	std::set<LLUUID> mRegisteredAttachments;
 	bool mAttachmentInvLinkEnabled;
 	bool mOutfitIsDirty;
 	bool mIsInUpdateAppearanceFromCOF; // to detect recursive calls.

indra/newview/llfloatergroups.cpp

 #include "llbutton.h"
 #include "llgroupactions.h"
 #include "llscrolllistctrl.h"
-#include "llselectmgr.h"
 #include "lltextbox.h"
 #include "lluictrlfactory.h"
 #include "lltrans.h"
 		list_ctrl->setContextMenu(LLScrollListCtrl::MENU_GROUP);
 	}
 	
-	LLSelectMgr::getInstance()->mUpdateSignal.connect(boost::bind(&LLFloaterGroupPicker::onBtnCancel, this));
-
 	childSetAction("OK", onBtnOK, this);
 
 	childSetAction("Cancel", onBtnCancel, this);
 
 	setDefaultBtn("OK");
 
-	getChildView("OK")->setEnabled(TRUE);
+	childEnable("OK");
 
 	return TRUE;
 }
 	{
 		group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
 	}
-	getChild<LLUICtrl>("groupcount")->setTextArg("[COUNT]", llformat("%d",gAgent.mGroups.count()));
-	getChild<LLUICtrl>("groupcount")->setTextArg("[MAX]", llformat("%d",MAX_AGENT_GROUPS));
+	childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
+	childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
 
 	init_group_list(getChild<LLScrollListCtrl>("group list"), gAgent.getGroupID());
 	enableButtons();
 {
 	childSetCommitCallback("group list", onGroupList, this);
 
-	getChild<LLUICtrl>("groupcount")->setTextArg("[COUNT]", llformat("%d",gAgent.mGroups.count()));
-	getChild<LLUICtrl>("groupcount")->setTextArg("[MAX]", llformat("%d",MAX_AGENT_GROUPS));
+	childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
+	childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
 
 	LLScrollListCtrl *list = getChild<LLScrollListCtrl>("group list");
 	if (list)
 
 	if(group_id != gAgent.getGroupID())
 	{
-		getChildView("Activate")->setEnabled(TRUE);
+		childEnable("Activate");
 	}
 	else
 	{
-		getChildView("Activate")->setEnabled(FALSE);
+		childDisable("Activate");
 	}
 	if (group_id.notNull())
 	{
-		getChildView("Info")->setEnabled(TRUE);
-		getChildView("IM")->setEnabled(TRUE);
-		getChildView("Leave")->setEnabled(TRUE);
+		childEnable("Info");
+		childEnable("IM");
+		childEnable("Leave");
 	}
 	else
 	{
-		getChildView("Info")->setEnabled(FALSE);
-		getChildView("IM")->setEnabled(FALSE);
-		getChildView("Leave")->setEnabled(FALSE);
+		childDisable("Info");
+		childDisable("IM");
+		childDisable("Leave");
 	}
-	getChildView("Create")->setEnabled(gAgent.canJoinGroups());
+	childSetEnabled("Create", gAgent.canJoinGroups());
 }
 
 

indra/newview/llinventorybridge.cpp

 		LLInventoryItem* item = gInventory.getItem(mUUID);
 		if(item)
 		{
-			gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
-			gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
-			gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
-			gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
-			gMessageSystem->sendReliable( gAgent.getRegion()->getHost());
-
-			// this object might have been selected, so let the selection manager know it's gone now
-			LLViewerObject *found_obj = gObjectList.findObject(item->getLinkedUUID());
-			if (found_obj)
-			{
-				LLSelectMgr::getInstance()->remove(found_obj);
-			}
+			LLVOAvatarSelf::detachAttachmentIntoInventory(item->getLinkedUUID());
 		}
 	}
 	else LLItemBridge::performAction(model, action);
 
 void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment)
 {
-	LLSD payload;
-	payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link.
+	const LLUUID& item_id = item->getLinkedUUID();
+
+	// Check for duplicate request.
+	if (isAgentAvatarValid() &&
+		(gAgentAvatarp->attachmentWasRequested(item_id) ||
+		 gAgentAvatarp->isWearingAttachment(item_id)))
+	{
+		llwarns << "duplicate attachment request, ignoring" << llendl;
+		return;
+	}
+	gAgentAvatarp->addAttachmentRequest(item_id);
 
 	S32 attach_pt = 0;
 	if (isAgentAvatarValid() && attachment)
 		}
 	}
 
+	LLSD payload;
+	payload["item_id"] = item_id; // Wear the base object in case this is a link.
 	payload["attachment_point"] = attach_pt;
 
 	if (!gSavedSettings.getBOOL("MultipleAttachments") &&
 	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 	if (option == 0/*YES*/)
 	{
-		LLViewerInventoryItem* itemp = gInventory.getItem(notification["payload"]["item_id"].asUUID());
+		LLUUID item_id = notification["payload"]["item_id"].asUUID();
+		LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
 
 		if (itemp)
 		{
 			U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
+			
 			if (gSavedSettings.getBOOL("MultipleAttachments"))
 				attachment_pt |= ATTACHMENT_ADD;
 
 				LLViewerInventoryItem *obj_item = obj_item_array.get(i);
 				if (get_is_item_worn(obj_item->getUUID()))
 				{
-					gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
-					gMessageSystem->nextBlockFast(_PREHASH_ObjectData );
-					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
-					gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item->getLinkedUUID() );
-					
-					gMessageSystem->sendReliable( gAgent.getRegion()->getHost() );
-					
-					// this object might have been selected, so let the selection manager know it's gone now
-					LLViewerObject *found_obj = gObjectList.findObject( obj_item->getLinkedUUID());
-					if (found_obj)
-					{
-						LLSelectMgr::getInstance()->remove(found_obj);
-					}
+					LLVOAvatarSelf::detachAttachmentIntoInventory(obj_item->getLinkedUUID());
 				}
 			}
 		}

indra/newview/llsidepanelappearance.cpp

 			{
 				LLViewerObject* attached_object = (*attachment_iter);
 				if (!attached_object) continue;
-				const LLUUID& item_id = attached_object->getItemID();
+				const LLUUID& item_id = attached_object->getAttachmentItemID();
 				if (item_id.isNull()) continue;
 				ids.push_back(item_id);
 			}
 
 void LLSidepanelAppearance::setWearablesLoading(bool val)
 {
-	getChildView("wearables_loading_indicator")->setVisible( val);
-	getChildView("edit_outfit_btn")->setVisible( !val);
+	childSetVisible("wearables_loading_indicator", val);
+	childSetVisible("edit_outfit_btn", !val);
 
 	if (!val)
 	{

indra/newview/llviewerjointattachment.cpp

 #include "llviewerjointattachment.h"
 
 #include "llagentconstants.h"
-
 #include "llviewercontrol.h"
 #include "lldrawable.h"
 #include "llgl.h"
 #include "llrender.h"
-#include "llvoavatar.h"
+#include "llvoavatarself.h"
 #include "llvolume.h"
 #include "pipeline.h"
 #include "llspatialpartition.h"
 //-----------------------------------------------------------------------------
 BOOL LLViewerJointAttachment::addObject(LLViewerObject* object)
 {
+	object->extractAttachmentItemID();
+
+	// Same object reattached
 	if (isObjectAttached(object))
 	{
 		llinfos << "(same object re-attached)" << llendl;
 		// Pass through anyway to let setupDrawable()
 		// re-connect object to the joint correctly
 	}
+	
+	// Two instances of the same inventory item attached --
+	// Request detach, and kill the object in the meantime.
+	if (getAttachedObject(object->getAttachmentItemID()))
+	{
+		llinfos << "(same object re-attached)" << llendl;
+		object->markDead();
 
-	// Find the inventory item ID of the attached object
-	LLNameValue* item_id_nv = object->getNVPair("AttachItemID");
-	if( item_id_nv )
-	{
-		const char* s = item_id_nv->getString();
-		if( s )
-		{
-			LLUUID item_id;
-			item_id.set(s);
-			object->setItemID(item_id);
-			lldebugs << "getNVPair( AttachItemID ) = " << item_id << llendl;
-		}
+		// If this happens to be attached to self, then detach.
+		LLVOAvatarSelf::detachAttachmentIntoInventory(object->getAttachmentItemID());
+		return FALSE;
 	}
+
 	mAttachedObjects.push_back(object);
 	setupDrawable(object);
 	
 	}
 	calcLOD();
 	mUpdateXform = TRUE;
-
+	
 	return TRUE;
 }
 
 	{
 		mUpdateXform = FALSE;
 	}
-	object->setItemID(LLUUID::null);
+	object->setAttachmentItemID(LLUUID::null);
 }
 
 //-----------------------------------------------------------------------------
 		 ++iter)
 	{
 		const LLViewerObject* attached_object = (*iter);
-		if (attached_object->getItemID() == object_id)
+		if (attached_object->getAttachmentItemID() == object_id)
 		{
 			return attached_object;
 		}
 		 ++iter)
 	{
 		LLViewerObject* attached_object = (*iter);
-		if (attached_object->getItemID() == object_id)
+		if (attached_object->getAttachmentItemID() == object_id)
 		{
 			return attached_object;
 		}

indra/newview/llviewermenu.cpp

 	bool handleEvent(const LLSD& userdata)
 	{
 		gUseWireframe = !(gUseWireframe);
-		LLPipeline::updateRenderDeferred();
-		gPipeline.resetVertexBuffers();
 		return true;
 	}
 };
 };
 
 /////////////////////////////////////
-// Enable Deferred Rendering sub-options
+// Enable Global Illumination 	  ///
 /////////////////////////////////////
-class LLAdvancedEnableRenderDeferredOptions: public view_listener_t
+class LLAdvancedEnableRenderDeferredGI: public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
 	{
 class LLObjectAttachToAvatar : public view_listener_t
 {
 public:
-	LLObjectAttachToAvatar(bool replace) : mReplace(replace) {}
 	static void setObjectSelection(LLObjectSelectionHandle selection) { sObjectSelection = selection; }
 
 private:
 			LLViewerJointAttachment* attachment_point = NULL;
 			if (index > 0)
 				attachment_point = get_if_there(gAgentAvatarp->mAttachmentPoints, index, (LLViewerJointAttachment*)NULL);
-			confirmReplaceAttachment(0, attachment_point);
-		}
-		return true;
-	}
-
-	static void onNearAttachObject(BOOL success, void *user_data);
-	void confirmReplaceAttachment(S32 option, LLViewerJointAttachment* attachment_point);
-
-	struct CallbackData
-	{
-		CallbackData(LLViewerJointAttachment* point, bool replace) : mAttachmentPoint(point), mReplace(replace) {}
-
-		LLViewerJointAttachment*	mAttachmentPoint;
-		bool						mReplace;
-	};
+			confirm_replace_attachment(0, attachment_point);
+		}
+		return true;
+	}
 
 protected:
 	static LLObjectSelectionHandle sObjectSelection;
-	bool mReplace;
 };
 
 LLObjectSelectionHandle LLObjectAttachToAvatar::sObjectSelection;
 
-// static
-void LLObjectAttachToAvatar::onNearAttachObject(BOOL success, void *user_data)
-{
-	if (!user_data) return;
-	CallbackData* cb_data = static_cast<CallbackData*>(user_data);
-
+void near_attach_object(BOOL success, void *user_data)
+{
 	if (success)
 	{
-		const LLViewerJointAttachment *attachment = cb_data->mAttachmentPoint;
+		const LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
 		
 		U8 attachment_id = 0;
 		if (attachment)
 			// interpret 0 as "default location"
 			attachment_id = 0;
 		}
-		LLSelectMgr::getInstance()->sendAttach(attachment_id, cb_data->mReplace);
+		LLSelectMgr::getInstance()->sendAttach(attachment_id);
 	}		
 	LLObjectAttachToAvatar::setObjectSelection(NULL);
-
-	delete cb_data;
-}
-
-// static
-void LLObjectAttachToAvatar::confirmReplaceAttachment(S32 option, LLViewerJointAttachment* attachment_point)
+}
+
+void confirm_replace_attachment(S32 option, void* user_data)
 {
 	if (option == 0/*YES*/)
 	{
 			delta = delta * 0.5f;
 			walkToSpot -= delta;
 
-			// The callback will be called even if avatar fails to get close enough to the object, so we won't get a memory leak.
-			CallbackData* user_data = new CallbackData(attachment_point, mReplace);
-			gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(walkToSpot), "Attach", NULL, onNearAttachObject, user_data, stop_distance);
+			gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(walkToSpot), "Attach", NULL, near_attach_object, user_data, stop_distance);
 			gAgentCamera.clearFocusObject();
 		}
 	}
 				const LLViewerObject* attached_object = (*attachment_iter);
 				if (attached_object)
 				{
-					LLViewerInventoryItem* itemp = gInventory.getItem(attached_object->getItemID());
+					LLViewerInventoryItem* itemp = gInventory.getItem(attached_object->getAttachmentItemID());
 					if (itemp)
 					{
 						label += std::string(" (") + itemp->getName() + std::string(")");
 				{
 					// make sure item is in your inventory (it could be a delayed attach message being sent from the sim)
 					// so check to see if the item is in the inventory already
-					item = gInventory.getItem((*attachment_iter)->getItemID());
+					item = gInventory.getItem((*attachment_iter)->getAttachmentItemID());
 					if (!item)
 					{
 						// Item does not exist, make an observer to enable the pie menu 
 						// when the item finishes fetching worst case scenario 
 						// if a fetch is already out there (being sent from a slow sim)
 						// we refetch and there are 2 fetches
-						LLWornItemFetchedObserver* worn_item_fetched = new LLWornItemFetchedObserver((*attachment_iter)->getItemID());		
+						LLWornItemFetchedObserver* worn_item_fetched = new LLWornItemFetchedObserver((*attachment_iter)->getAttachmentItemID());		
 						worn_item_fetched->startFetch();
 						gInventory.addObserver(worn_item_fetched);
 					}
 							!attached_object->mDrawable->isRenderType(0));
 			LLVector3 pos;
 			if (visible) pos = attached_object->mDrawable->getPosition();
-			llinfos << "ATTACHMENT " << key << ": item_id=" << attached_object->getItemID()
+			llinfos << "ATTACHMENT " << key << ": item_id=" << attached_object->getAttachmentItemID()
 					<< (attached_object ? " present " : " absent ")
 					<< (visible ? "visible " : "invisible ")
 					<<  " at " << pos
 }
 
 
-// these are used in the gl menus to set control values, generically.
+// these are used in the gl menus to set control values.
 class LLToggleControl : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
 		std::string callback_data = userdata.asString();
 		bool new_value = gSavedSettings.getBOOL(callback_data);
 		return new_value;
-	}
-};
-
-// not so generic
-
-class LLAdvancedCheckRenderShadowOption: public view_listener_t
-{
-	bool handleEvent(const LLSD& userdata)
-	{
-		std::string control_name = userdata.asString();
-		S32 current_shadow_level = gSavedSettings.getS32(control_name);
-		if (current_shadow_level == 0) // is off
-		{
-			return false;
-		}
-		else // is on
-		{
-			return true;
-		}
-	}
-};
-
-class LLAdvancedClickRenderShadowOption: public view_listener_t
-{
-	bool handleEvent(const LLSD& userdata)
-	{
-		std::string control_name = userdata.asString();
-		S32 current_shadow_level = gSavedSettings.getS32(control_name);
-		if (current_shadow_level == 0) // upgrade to level 2
-		{
-			gSavedSettings.setS32(control_name, 2);
-		}
-		else // downgrade to level 0
-		{
-			gSavedSettings.setS32(control_name, 0);
-		}
-		return true;
-	}
+}
+
 };
 
 void menu_toggle_attached_lights(void* user_data)
 	// Help menu
 	// most items use the ShowFloater method
 
-	// Advanced menu
+	// Advance menu
 	view_listener_t::addMenu(new LLAdvancedToggleConsole(), "Advanced.ToggleConsole");
 	view_listener_t::addMenu(new LLAdvancedCheckConsole(), "Advanced.CheckConsole");
 	view_listener_t::addMenu(new LLAdvancedDumpInfoToConsole(), "Advanced.DumpInfoToConsole");
 	view_listener_t::addMenu(new LLAdvancedSelectedTextureInfo(), "Advanced.SelectedTextureInfo");
 	view_listener_t::addMenu(new LLAdvancedToggleWireframe(), "Advanced.ToggleWireframe");
 	view_listener_t::addMenu(new LLAdvancedCheckWireframe(), "Advanced.CheckWireframe");
-	// Develop > Render
 	view_listener_t::addMenu(new LLAdvancedToggleTextureAtlas(), "Advanced.ToggleTextureAtlas");
 	view_listener_t::addMenu(new LLAdvancedCheckTextureAtlas(), "Advanced.CheckTextureAtlas");
 	view_listener_t::addMenu(new LLAdvancedEnableObjectObjectOcclusion(), "Advanced.EnableObjectObjectOcclusion");
 	view_listener_t::addMenu(new LLAdvancedEnableRenderFBO(), "Advanced.EnableRenderFBO");
 	view_listener_t::addMenu(new LLAdvancedEnableRenderDeferred(), "Advanced.EnableRenderDeferred");
-	view_listener_t::addMenu(new LLAdvancedEnableRenderDeferredOptions(), "Advanced.EnableRenderDeferredOptions");
+	view_listener_t::addMenu(new LLAdvancedEnableRenderDeferredGI(), "Advanced.EnableRenderDeferredGI");
 	view_listener_t::addMenu(new LLAdvancedToggleRandomizeFramerate(), "Advanced.ToggleRandomizeFramerate");
 	view_listener_t::addMenu(new LLAdvancedCheckRandomizeFramerate(), "Advanced.CheckRandomizeFramerate");
 	view_listener_t::addMenu(new LLAdvancedTogglePeriodicSlowFrame(), "Advanced.TogglePeriodicSlowFrame");
 	view_listener_t::addMenu(new LLAdvancedToggleFrameTest(), "Advanced.ToggleFrameTest");
 	view_listener_t::addMenu(new LLAdvancedCheckFrameTest(), "Advanced.CheckFrameTest");
 	view_listener_t::addMenu(new LLAdvancedHandleAttachedLightParticles(), "Advanced.HandleAttachedLightParticles");
-	view_listener_t::addMenu(new LLAdvancedCheckRenderShadowOption(), "Advanced.CheckRenderShadowOption");
-	view_listener_t::addMenu(new LLAdvancedClickRenderShadowOption(), "Advanced.ClickRenderShadowOption");
 	
 
 	#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
 	commit.add("Object.Touch", boost::bind(&handle_object_touch));
 	commit.add("Object.SitOrStand", boost::bind(&handle_object_sit_or_stand));
 	commit.add("Object.Delete", boost::bind(&handle_object_delete));
-	view_listener_t::addMenu(new LLObjectAttachToAvatar(true), "Object.AttachToAvatar");
-	view_listener_t::addMenu(new LLObjectAttachToAvatar(false), "Object.AttachAddToAvatar");
+	view_listener_t::addMenu(new LLObjectAttachToAvatar(), "Object.AttachToAvatar");
 	view_listener_t::addMenu(new LLObjectReturn(), "Object.Return");
 	view_listener_t::addMenu(new LLObjectReportAbuse(), "Object.ReportAbuse");
 	view_listener_t::addMenu(new LLObjectMute(), "Object.Mute");

indra/newview/llviewerobject.cpp

 	return ;
 }
 
+const LLUUID &LLViewerObject::getAttachmentItemID() const
+{
+	return mAttachmentItemID;
+}
+
+void LLViewerObject::setAttachmentItemID(const LLUUID &id)
+{
+	mAttachmentItemID = id;
+}
+
+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();
+}

indra/newview/llviewerobject.h

 private:	
 	static S32 sNumObjects;
 
+	//--------------------------------------------------------------------
+	// For objects that are attachments
+	//--------------------------------------------------------------------
 public:
-	const LLUUID &getItemID() const { return mAttachmentItemID; }
-	void setItemID(const LLUUID &id) { mAttachmentItemID = id; }
+	const LLUUID &getAttachmentItemID() const;
+	void setAttachmentItemID(const LLUUID &id);
+	const LLUUID &extractAttachmentItemID(); // find&set the inventory item ID of the attached object
 private:
-	LLUUID mAttachmentItemID; // ItemID when item is in user inventory.
+	LLUUID mAttachmentItemID; // ItemID of the associated object is in user inventory.
 };
 
 ///////////////////

indra/newview/llviewertexture.cpp

 LLImageGL* LLViewerTexture::getGLTexture() const
 {
 	llassert(mGLTexturep.notNull()) ;
-	
+
 	return mGLTexturep ;
 }
 
 //virtual
 void LLViewerFetchedTexture::processTextureStats()
 {
+	if(mForceToSaveRawImage && mDesiredSavedRawDiscardLevel >= 0) //force to refetch the texture.
+	{
+		mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S8)mDesiredSavedRawDiscardLevel) ;
+	}
+
 	if(mFullyLoaded)
-	{
+	{		
 		if(mDesiredDiscardLevel <= mMinDesiredDiscardLevel)//already loaded
 		{
 			return ;
 void LLViewerFetchedTexture::unpauseLoadedCallbacks(const LLLoadedCallbackEntry::source_callback_list_t* callback_list)
 {
 	if(!callback_list)
-{
+	{
 		mPauseLoadedCallBacks = FALSE ;
 		return ;
 	}
 void LLViewerFetchedTexture::pauseLoadedCallbacks(const LLLoadedCallbackEntry::source_callback_list_t* callback_list)
 {
 	if(!callback_list)
-{
+	{
 		return ;
 	}
 

indra/newview/llvoavatar.cpp

 		mBakedTextureDatas[i].mTextureIndex = LLVOAvatarDictionary::bakedToLocalTextureIndex((EBakedTextureIndex)i);
 	}
 
-	mDirtyMesh = 2;	// Dirty geometry, need to regenerate.
+	mDirtyMesh = TRUE;	// Dirty geometry, need to regenerate.
 	mMeshTexturesDirty = FALSE;
+	mShadow0Facep = NULL;
+	mShadow1Facep = NULL;
 	mHeadp = NULL;
 
 	mIsBuilt = FALSE;
 
 	mRippleTimeLast = 0.f;
 
+	mShadowImagep = LLViewerTextureManager::getFetchedTextureFromFile("foot_shadow.j2c");
+	
+	// GL NOT ACTIVE HERE
+	//gGL.getTexUnit(0)->bind(mShadowImagep.get());
+	//mShadowImagep->setAddressMode(LLTexUnit::TAM_CLAMP);
+	
 	mInAir = FALSE;
 
 	mStepOnLand = TRUE;
 			}
 			if(num_vertices < 1)//skip empty meshes
 			{
-				continue ;
+				break ;
 			}
 			if(last_v_num > 0)//put the last inserted part into next vertex buffer.
 			{
 			// resize immediately
 			facep->setSize(num_vertices, num_indices);
 
-			bool terse_update = false;
-
 			if(facep->mVertexBuffer.isNull())
 			{
 				facep->mVertexBuffer = new LLVertexBufferAvatar();
 			}
 			else
 			{
-				if (facep->mVertexBuffer->getRequestedIndices() == num_indices &&
-					facep->mVertexBuffer->getRequestedVerts() == num_vertices)
-				{
-					terse_update = true;
-				}
-				else
-				{
 				facep->mVertexBuffer->resizeBuffer(num_vertices, num_indices) ;
 			}
-			}
 		
 			facep->setGeomIndex(0);
 			facep->setIndicesIndex(0);
 
 			for(S32 k = j ; k < part_index ; k++)
 			{
-				mMeshLOD[k]->updateFaceData(facep, mAdjustedPixelArea, k == MESH_ID_HAIR, terse_update);
+				mMeshLOD[k]->updateFaceData(facep, mAdjustedPixelArea, k == MESH_ID_HAIR);
 			}
 
 			stop_glerror();
 	LLJoint::sNumUpdates = 0;
 	LLJoint::sNumTouches = 0;
 
+	// *NOTE: this is necessary for the floating name text above your head.
+	if (mDrawable.notNull())
+	{
+		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_SHADOW, TRUE);
+	}
+
 	BOOL visible = isVisible() || mNeedsAnimUpdate;
 
 	// update attachments positions
 		return num_indices;
 	}
 
-	LLFace* face = mDrawable->getFace(0);
-
-	bool needs_rebuild = !face || face->mVertexBuffer.isNull() || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY);
-
-	if (needs_rebuild || mDirtyMesh)
+	if (mDirtyMesh || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY))
 	{	//LOD changed or new mesh created, allocate new vertex buffer if needed
-		if (needs_rebuild || mDirtyMesh >= 2 || mVisibilityRank <= 4)
-		{
 		updateMeshData();
-			mDirtyMesh = 0;
+		mDirtyMesh = FALSE;
 		mNeedsSkin = TRUE;
 		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
 	}
-	}
 
 	if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) <= 0)
 	{
 	return num_indices;
 }
 
+U32 LLVOAvatar::renderFootShadows()
+{
+	U32 num_indices = 0;
+
+	if (!mIsBuilt)
+	{
+		return 0;
+	}
+
+	if (isSelf() && (!gAgent.needsRenderAvatar() || !gAgent.needsRenderHead()))
+	{
+		return 0;
+	}
+	
+	if (!mIsBuilt)
+	{
+		return 0;
+	}
+	
+	// Don't render foot shadows if your lower body is completely invisible.
+	// (non-humanoid avatars rule!)
+	if (!isTextureVisible(TEX_LOWER_BAKED))
+	{
+		return 0;
+	}
+
+	// Update the shadow, tractor, and text label geometry.
+	if (mDrawable->isState(LLDrawable::REBUILD_SHADOW) && !isImpostor())
+	{
+		updateShadowFaces();
+		mDrawable->clearState(LLDrawable::REBUILD_SHADOW);
+	}
+
+	U32 foot_mask = LLVertexBuffer::MAP_VERTEX |
+					LLVertexBuffer::MAP_TEXCOORD0;
+
+	LLGLDepthTest test(GL_TRUE, GL_FALSE);
+	//render foot shadows
+	LLGLEnable blend(GL_BLEND);
+	gGL.getTexUnit(0)->bind(mShadowImagep, TRUE);
+	glColor4fv(mShadow0Facep->getRenderColor().mV);
+	mShadow0Facep->renderIndexed(foot_mask);
+	glColor4fv(mShadow1Facep->getRenderColor().mV);
+	mShadow1Facep->renderIndexed(foot_mask);
+	
+	return num_indices;
+}
+
 U32 LLVOAvatar::renderImpostor(LLColor4U color, S32 diffuse_channel)
 {
 	if (!mImpostor.isComplete())
 	{
 		setDebugText(llformat("%4.0f:%4.0f", fsqrtf(mMinPixelArea),fsqrtf(mMaxPixelArea)));
 	}	
+	
+	if( render_avatar )
+	{
+		mShadowImagep->addTextureStats(mPixelArea);
+	}
 }
 
 
  		if (res)
 		{
 			sNumLODChangesThisFrame++;
-			dirtyMesh(2);
+			dirtyMesh();
 			return TRUE;
 		}
 	}
 	mDrawable->addFace(poolp, NULL);
 	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_AVATAR);
 	
+	LLFace *facep;
+
+	// Add faces for the foot shadows
+	facep = mDrawable->addFace((LLFacePool*) NULL, mShadowImagep);
+	mShadow0Facep = facep;
+
+	facep = mDrawable->addFace((LLFacePool*) NULL, mShadowImagep);
+	mShadow1Facep = facep;
+
 	mNumInitFaces = mDrawable->getNumFaces() ;
 
-	dirtyMesh(2);
+	dirtyMesh();
 	return mDrawable;
 }
 
 }
 
 //-----------------------------------------------------------------------------
+// updateShadowFaces()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateShadowFaces()
+{
+	LLFace *face0p = mShadow0Facep;
+	LLFace *face1p = mShadow1Facep;
+
+	//
+	// render avatar shadows
+	//
+	if (mInAir || mUpdatePeriod >= IMPOSTOR_PERIOD)
+	{
+		face0p->setSize(0, 0);
+		face1p->setSize(0, 0);
+		return;
+	}
+
+	LLSprite sprite(mShadowImagep.notNull() ? mShadowImagep->getID() : LLUUID::null);
+	sprite.setFollow(FALSE);
+	const F32 cos_angle = gSky.getSunDirection().mV[2];
+	F32 cos_elev = sqrt(1 - cos_angle * cos_angle);
+	if (cos_angle < 0) cos_elev = -cos_elev;
+	sprite.setSize(0.4f + cos_elev * 0.8f, 0.3f);
+	LLVector3 sun_vec = gSky.mVOSkyp ? gSky.mVOSkyp->getToSun() : LLVector3(0.f, 0.f, 0.f);
+
+	if (mShadowImagep->hasGLTexture())
+	{
+		LLVector3 normal;
+		LLVector3d shadow_pos;
+		LLVector3 shadow_pos_agent;
+		F32 foot_height;
+
+		if (mFootLeftp)
+		{
+			LLVector3 joint_world_pos = mFootLeftp->getWorldPosition();
+			// this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now
+			// but we make an explicit ray trace call in expectation of future improvements
+			resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos), 
+									 gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal);
+			shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos);
+			foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ];
+
+			// Pull sprite in direction of surface normal
+			shadow_pos_agent += normal * SHADOW_OFFSET_AMT;
+
+			// Render sprite
+			sprite.setNormal(normal);
+			if (isSelf() && gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK)
+			{
+				sprite.setColor(0.f, 0.f, 0.f, 0.f);
+			}
+			else
+			{
+				sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f));
+			}
+			sprite.setPosition(shadow_pos_agent);
+
+			LLVector3 foot_to_knee = mKneeLeftp->getWorldPosition() - joint_world_pos;
+			//foot_to_knee.normalize();
+			foot_to_knee -= projected_vec(foot_to_knee, sun_vec);
+			sprite.setYaw(azimuth(sun_vec - foot_to_knee));
+		
+			sprite.updateFace(*face0p);
+		}
+
+		if (mFootRightp)
+		{
+			LLVector3 joint_world_pos = mFootRightp->getWorldPosition();
+			// this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now
+			// but we make an explicit ray trace call in expectation of future improvements
+			resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos), 
+									 gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal);
+			shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos);
+			foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ];
+
+			// Pull sprite in direction of surface normal
+			shadow_pos_agent += normal * SHADOW_OFFSET_AMT;
+
+			// Render sprite
+			sprite.setNormal(normal);
+			if (isSelf() && gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK)
+			{
+				sprite.setColor(0.f, 0.f, 0.f, 0.f);
+			}
+			else
+			{
+				sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f));
+			}
+			sprite.setPosition(shadow_pos_agent);
+
+			LLVector3 foot_to_knee = mKneeRightp->getWorldPosition() - joint_world_pos;
+			//foot_to_knee.normalize();
+			foot_to_knee -= projected_vec(foot_to_knee, sun_vec);
+			sprite.setYaw(azimuth(sun_vec - foot_to_knee));
+	
+			sprite.updateFace(*face1p);
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
 // updateSexDependentLayerSets()
 //-----------------------------------------------------------------------------
 void LLVOAvatar::updateSexDependentLayerSets( BOOL upload_bake )
 //-----------------------------------------------------------------------------
 void LLVOAvatar::dirtyMesh()
 {
-	dirtyMesh(1);
-}
-void LLVOAvatar::dirtyMesh(S32 priority)
-{
-	mDirtyMesh = llmax(mDirtyMesh, priority);
-}
+	mDirtyMesh = TRUE;
+}
+
 //-----------------------------------------------------------------------------
 // hideSkirt()
 //-----------------------------------------------------------------------------
 
 void LLVOAvatar::addChild(LLViewerObject *childp)
 {
+	childp->extractAttachmentItemID(); // find the inventory item this object is associated with.
 	LLViewerObject::addChild(childp);
 	if (childp->mDrawable)
 	{
 }
 
 //-----------------------------------------------------------------------------
-// canAttachMoreObjects()
-// Returns true if we can attach <n> more objects.
-//-----------------------------------------------------------------------------
-BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const
-{
-	return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS;
-}
-
-//-----------------------------------------------------------------------------
 // lazyAttach()
 //-----------------------------------------------------------------------------
 void LLVOAvatar::lazyAttach()
 	BOOL res = updateJointLODs();
 
 	LLFace* facep = mDrawable->getFace(0);
-	if (facep->mVertexBuffer.isNull())
-	{
-		dirtyMesh(2);
-	}
-
-	if (mDirtyMesh >= 2 || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY))
+	if (facep->mVertexBuffer.isNull() ||
+		(LLVertexBuffer::sEnableVBOs &&
+		((facep->mVertexBuffer->getUsage() == GL_STATIC_DRAW ? TRUE : FALSE) !=
+		 (facep->getPool()->getVertexShaderLevel() > 0 ? TRUE : FALSE))))
+	{
+		mDirtyMesh = TRUE;
+	}
+
+	if (mDirtyMesh || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY))
 	{	//LOD changed or new mesh created, allocate new vertex buffer if needed
 		updateMeshData();
-		mDirtyMesh = 0;
+		mDirtyMesh = FALSE;
 		mNeedsSkin = TRUE;
 		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
 	}

indra/newview/llvoavatarself.cpp

 }
 
 //-----------------------------------------------------------------------------
+BOOL LLVOAvatarSelf::attachmentWasRequested(const LLUUID& inv_item_id) const
+{
+	const F32 REQUEST_EXPIRATION_SECONDS = 5.0;  // any request older than this is ignored/removed.
+	std::map<LLUUID,LLTimer>::iterator it = mAttachmentRequests.find(inv_item_id);
+	if (it != mAttachmentRequests.end())
+	{
+		const LLTimer& request_time = it->second;
+		F32 request_time_elapsed = request_time.getElapsedTimeF32();
+		if (request_time_elapsed > REQUEST_EXPIRATION_SECONDS)
+		{
+			mAttachmentRequests.erase(it);
+			return FALSE;
+		}
+		else
+		{
+			return TRUE;
+		}
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
+//-----------------------------------------------------------------------------
+void LLVOAvatarSelf::addAttachmentRequest(const LLUUID& inv_item_id)
+{
+	LLTimer current_time;
+	mAttachmentRequests[inv_item_id] = current_time;
+}
+
+//-----------------------------------------------------------------------------
+void LLVOAvatarSelf::removeAttachmentRequest(const LLUUID& inv_item_id)
+{
+	mAttachmentRequests.erase(inv_item_id);
+}
+
+//-----------------------------------------------------------------------------
 // getWornAttachment()
 //-----------------------------------------------------------------------------
 LLViewerObject* LLVOAvatarSelf::getWornAttachment(const LLUUID& inv_item_id)
 	// Should just be the last object added
 	if (attachment->isObjectAttached(viewer_object))
 	{
-		const LLUUID& attachment_id = viewer_object->getItemID();
+		const LLUUID& attachment_id = viewer_object->getAttachmentItemID();
 		LLAppearanceMgr::instance().registerAttachment(attachment_id);
+		// Clear any pending requests once the attachment arrives.
+		removeAttachmentRequest(attachment_id);
 	}
 
 	return attachment;
 //virtual
 BOOL LLVOAvatarSelf::detachObject(LLViewerObject *viewer_object)
 {
-	const LLUUID attachment_id = viewer_object->getItemID();
+	const LLUUID attachment_id = viewer_object->getAttachmentItemID();
 	if (LLVOAvatar::detachObject(viewer_object))
 	{
 		// the simulator should automatically handle permission revocation
 	return FALSE;
 }
 
+// static
+BOOL LLVOAvatarSelf::detachAttachmentIntoInventory(const LLUUID &item_id)
+{
+	LLInventoryItem* item = gInventory.getItem(item_id);
+	if (item)
+	{
+		gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
+		gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+		gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+		gMessageSystem->addUUIDFast(_PREHASH_ItemID, item_id);
+		gMessageSystem->sendReliable(gAgent.getRegion()->getHost());
+		
+		// this object might have been selected, so let the selection manager know it's gone now
+		LLViewerObject *found_obj = gObjectList.findObject(item_id);
+		if (found_obj)
+		{
+			LLSelectMgr::getInstance()->remove(found_obj);
+		}
+		return TRUE;
+	}
+	return FALSE;
+}
+
 U32 LLVOAvatarSelf::getNumWearables(LLVOAvatarDefines::ETextureIndex i) const
 {
 	LLWearableType::EType type = LLVOAvatarDictionary::getInstance()->getTEWearableType(i);
 					if (isSelf())
 					{
 						if (gAgentAvatarp->isUsingBakedTextures())
-					{
-						requestLayerSetUpdate(type);
+						{
+							requestLayerSetUpdate(type);
+						}
+						else
+						{
+							LLVisualParamHint::requestHintUpdates();
+						}
 					}
-						else
-					{
-						LLVisualParamHint::requestHintUpdates();
-					}
-				}
 				}
 				else
 				{					

indra/newview/llvoavatarself.h

 public:
 	void 				updateAttachmentVisibility(U32 camera_mode);
 	BOOL 				isWearingAttachment(const LLUUID& inv_item_id) const;
+	BOOL				attachmentWasRequested(const LLUUID& inv_item_id) const;
+	void				addAttachmentRequest(const LLUUID& inv_item_id);
+	void				removeAttachmentRequest(const LLUUID& inv_item_id);
 	LLViewerObject* 	getWornAttachment(const LLUUID& inv_item_id);
 	const std::string   getAttachedPointName(const LLUUID& inv_item_id) const;
 	/*virtual*/ const LLViewerJointAttachment *attachObject(LLViewerObject *viewer_object);
 	/*virtual*/ BOOL 	detachObject(LLViewerObject *viewer_object);
+	static BOOL			detachAttachmentIntoInventory(const LLUUID& item_id);
+
+private:
+	// Track attachments that have been requested but have not arrived yet.
+	mutable std::map<LLUUID,LLTimer> mAttachmentRequests;
 
 	//--------------------------------------------------------------------
 	// HUDs